Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c028f63183 | ||
|
|
8a87204d48 | ||
|
|
f3fac3a449 | ||
|
|
14ae05a5c2 | ||
|
|
832651c2ca | ||
|
|
caac069a36 | ||
|
|
187b52aa12 | ||
|
|
c9eadef4d5 | ||
|
|
35df152310 | ||
|
|
988dd10251 |
@@ -0,0 +1 @@
|
|||||||
|
/usr/sbin/dropbear
|
||||||
@@ -2,13 +2,21 @@
|
|||||||
maintainer="snlavaise <snlavaise@gmail.com>"
|
maintainer="snlavaise <snlavaise@gmail.com>"
|
||||||
pkgname=device-motorola-rtwo
|
pkgname=device-motorola-rtwo
|
||||||
pkgver=1
|
pkgver=1
|
||||||
pkgrel=9
|
pkgrel=36
|
||||||
pkgdesc="Motorola Moto Edge 40 Pro"
|
pkgdesc="Motorola Moto Edge 40 Pro"
|
||||||
url="https://postmarketos.org"
|
url="https://postmarketos.org"
|
||||||
arch="aarch64"
|
arch="aarch64"
|
||||||
license="MIT"
|
license="MIT"
|
||||||
depends="
|
depends="
|
||||||
android-tools
|
android-tools
|
||||||
|
busybox-extras
|
||||||
|
dropbear
|
||||||
|
firmware-motorola-rtwo-adsp
|
||||||
|
firmware-motorola-rtwo-cdsp
|
||||||
|
firmware-motorola-rtwo-gpu
|
||||||
|
linux-firmware-ath12k
|
||||||
|
linux-firmware-qcom
|
||||||
|
linux-firmware-rtw88
|
||||||
linux-postmarketos-qcom-sm8550
|
linux-postmarketos-qcom-sm8550
|
||||||
postmarketos-base
|
postmarketos-base
|
||||||
soc-qcom
|
soc-qcom
|
||||||
@@ -17,13 +25,47 @@ makedepends="devicepkg-dev"
|
|||||||
source="
|
source="
|
||||||
deviceinfo
|
deviceinfo
|
||||||
modules-initfs
|
modules-initfs
|
||||||
|
initfs-hook.sh
|
||||||
|
initfs-hook-cleanup.sh
|
||||||
|
30-dropbear.files
|
||||||
|
debug-telnetd.service
|
||||||
|
logind-power-key.conf
|
||||||
|
rtwo-panel-revive.sh
|
||||||
|
rtwo-panel-revive.service
|
||||||
"
|
"
|
||||||
options="!archcheck !check"
|
options="!archcheck !check"
|
||||||
|
|
||||||
# TODO (as hardware comes up):
|
# TODO (as hardware comes up):
|
||||||
# - firmware-motorola-rtwo (WiFi/BT/modem/GPU blobs, generate from the
|
# - WiFi (WCN7850/ath12k via pcie0): links up, enumerates fine, and the real
|
||||||
# stock image with 'pmbootstrap ...' or extract manually)
|
# Motorola calibration (bdwlan.elf.rtwo.epa) is fully extracted and
|
||||||
# - firmware-qcom-adreno-* for the Adreno 740 (GPU / mesa-vulkan-freedreno)
|
# verified byte-perfect -- but it ALWAYS fails at the QMI board-data
|
||||||
|
# transfer itself (qmi failed to load board data file:-110, ETIMEDOUT),
|
||||||
|
# confirmed a dead end after exhaustive investigation (re-verified
|
||||||
|
# 2026-09-22, no format/corruption issue found). Workaround: a generic
|
||||||
|
# Qualcomm board-id=44 blob as the LEGACY board.bin (not board-2.bin) at
|
||||||
|
# ath12k/WCN7850/hw2.0/board.bin gets wlan0 to appear, but hits a
|
||||||
|
# separate, deeper WMI/HTC credit-exhaustion bug in ath12k_mac_start()
|
||||||
|
# (PMF_QOS never gets a second tx credit) -- 7 hypotheses ruled out with
|
||||||
|
# evidence, cross-referenced against a real LKML thread with the exact
|
||||||
|
# same symptom on unrelated hardware. Looks like a genuine unfixed
|
||||||
|
# upstream kernel bug, not something fixable from this device's side.
|
||||||
|
# Draft bug report at ~/pmos/ath12k-bug-report-wmi-credit-exhaustion.md,
|
||||||
|
# not sent (needs explicit go-ahead before any public post).
|
||||||
|
# - USB WiFi dongle (RTL8723DU) as an alternative to onboard WCN7850:
|
||||||
|
# CONFIG_RTW88_8723DU=m already enabled, needs linux-firmware-rtw88
|
||||||
|
# (added below) for rtw88/rtw8723d_fw.bin. Requires &usb_1 in host mode
|
||||||
|
# (now the default, see the .dts) + a self/externally-powered USB-C hub.
|
||||||
|
# - Bluetooth (qcom,wcn7850-bt) not wired in the .dts yet, same PMU node.
|
||||||
|
# - firmware-motorola-rtwo: ADSP/CDSP (remoteproc, pmic-glink battmgr/UCSI
|
||||||
|
# run on the ADSP) and the GPU zap shader are pulled in as dependencies.
|
||||||
|
# -hexagonfs (FastRPC libs for hexagonrpcd, e.g. sensors) is left out
|
||||||
|
# until hexagonrpcd is actually set up. Modem blobs still TODO.
|
||||||
|
# - GPU (Adreno 740): WORKING 2026-09-25 (freedreno FD740, OpenGL 4.6/GLES).
|
||||||
|
# a740_sqe.fw + gmu_gen70200.bin come from linux-firmware-qcom; the zap
|
||||||
|
# shader MUST be Motorola's signed one (firmware-motorola-rtwo-gpu,
|
||||||
|
# qcom/sm8550/motorola/rtwo/a740_zap.mbn) -- the generic
|
||||||
|
# qcom/sm8550/a740_zap.mbn in linux-firmware-qcom is QRD-signed and is
|
||||||
|
# rejected on a retail device.
|
||||||
# - make-dynpart-mappings once super partitions are mapped
|
# - make-dynpart-mappings once super partitions are mapped
|
||||||
# - soc-qcom-modem, qbootctl (A/B slot handling) once booting
|
# - soc-qcom-modem, qbootctl (A/B slot handling) once booting
|
||||||
|
|
||||||
@@ -33,9 +75,55 @@ build() {
|
|||||||
|
|
||||||
package() {
|
package() {
|
||||||
devicepkg_package $startdir $pkgname
|
devicepkg_package $startdir $pkgname
|
||||||
|
|
||||||
|
# devicepkg_package handles initfs-hook.sh natively (installs it to
|
||||||
|
# usr/share/mkinitfs/hooks/00-$pkgname.sh), but has no generic handler
|
||||||
|
# for extra ".files" manifests (unlike modules-initfs) -- install this
|
||||||
|
# one by hand, same mechanism postmarketos-mkinitfs-hook-debug-shell
|
||||||
|
# uses for telnetd's own 20-debug-shell.files.
|
||||||
|
install -Dm644 "$srcdir"/30-dropbear.files \
|
||||||
|
"$pkgdir"/usr/share/mkinitfs/files/30-dropbear.files
|
||||||
|
|
||||||
|
# Mata el dropbear del debug-shell antes de switch_root -- si no, sigue
|
||||||
|
# vivo pegado al puerto 22 y sshd.service de la rootfs real nunca puede
|
||||||
|
# bindear (ver comentario grande en el propio initfs-hook-cleanup.sh).
|
||||||
|
install -Dm755 "$srcdir"/initfs-hook-cleanup.sh \
|
||||||
|
-t "$pkgdir"/usr/share/mkinitfs/hooks-cleanup/
|
||||||
|
|
||||||
|
# Debug telnet shell de la rootfs REAL (no del initramfs -- ver el
|
||||||
|
# comentario grande en el propio .service). Se habilita a mano con el
|
||||||
|
# symlink porque esto no es un paquete "de verdad" con postinstall,
|
||||||
|
# es un hack de bring-up.
|
||||||
|
install -Dm644 "$srcdir"/debug-telnetd.service \
|
||||||
|
"$pkgdir"/usr/lib/systemd/system/debug-telnetd.service
|
||||||
|
install -d "$pkgdir"/usr/lib/systemd/system/sysinit.target.wants
|
||||||
|
ln -s ../debug-telnetd.service \
|
||||||
|
"$pkgdir"/usr/lib/systemd/system/sysinit.target.wants/debug-telnetd.service
|
||||||
|
|
||||||
|
# Boton de encendido: ignorar la pulsacion corta (ver el .conf).
|
||||||
|
install -Dm644 "$srcdir"/logind-power-key.conf \
|
||||||
|
"$pkgdir"/usr/lib/systemd/logind.conf.d/50-rtwo-power-key.conf
|
||||||
|
|
||||||
|
# Panel negro en frio: blank/unblank desde el rootfs si el panel salio
|
||||||
|
# muerto (el initfs-hook no alcanza cuando msm engancha el display
|
||||||
|
# despues del switch_root). Ver rtwo-panel-revive.sh.
|
||||||
|
install -Dm755 "$srcdir"/rtwo-panel-revive.sh \
|
||||||
|
"$pkgdir"/usr/libexec/rtwo-panel-revive
|
||||||
|
install -Dm644 "$srcdir"/rtwo-panel-revive.service \
|
||||||
|
"$pkgdir"/usr/lib/systemd/system/rtwo-panel-revive.service
|
||||||
|
install -d "$pkgdir"/usr/lib/systemd/system/multi-user.target.wants
|
||||||
|
ln -s ../rtwo-panel-revive.service \
|
||||||
|
"$pkgdir"/usr/lib/systemd/system/multi-user.target.wants/rtwo-panel-revive.service
|
||||||
}
|
}
|
||||||
|
|
||||||
sha512sums="
|
sha512sums="
|
||||||
e1edb54b515de674ded1a0443901f41a37655d79da8273a504bca8a23e7c789b13ddad9ddef99e5eed8ce352502339b14e55b20eacfcf734334daf19edc7b876 deviceinfo
|
bc4d018a852d900fd9932bf881e81b1eae49f416dbafe212a4fa6e26c05f812cef64ca19b8adb69225331468ac1535d568f547e2e9f4ea296ed37a8c6340e4ad deviceinfo
|
||||||
c572e21cb0624b554a22a218b0c4faab9b6412654bed6bfef530f3068d076b21c573bd7f4b3ca65f510f8756f636a861191cce925ef49587db7d68d09558826c modules-initfs
|
298e836000c92db16ee4fe3ce4899eb13e9099983ee0fabff1e98c494e8a1883cef327a3ddce5c1ef98e0043b804aae8506438630b3bb144f92ded89c129668b modules-initfs
|
||||||
|
eccadc2bf5713ff9df16234c0d9daf1364bdcd88fb9709df012035719e2d5e44d658ccaf87a9bb1a76d433861c37162cdfef999fbe1093d6d0d32eb849dbdfe8 initfs-hook.sh
|
||||||
|
cb8ea3e0da0219fdf77f6def6d7a77157a4ef152cc434e3d282457647784b05e7152cf1028d00a5534ae721a38d3c24047edd1dfbaa6fd34290c3823d4cf54c0 initfs-hook-cleanup.sh
|
||||||
|
07ceb61f64d99fdbc815795beae9014dc22177d5622d015db79849faed5c14225b280d83dbac174957089344d2e857595b046683cee033d4acd76fdbf7114eab 30-dropbear.files
|
||||||
|
3511918144a3130d9ac64df78cfd9a02e3ed5ee11f45f0b3b872f2e42504434b3c3197b47797fad2510ff1e7ae7822813a8710eb4830f324b5cb101742fde062 debug-telnetd.service
|
||||||
|
bab2fbd6078d9ac342708a1e85b7f5d5cff5e1105b5bd633517bf27f9a61499b9903c9aa17d0142c82f8cd874cd9ac03d1ddb33b8c66e40094a7271dd54c0502 logind-power-key.conf
|
||||||
|
59db456c9ba57352aa8bff0e8ca73bd5b1c5c7f6f9c57e382e5bdd68791c9324d705210e77aa4e945e3869ab11dc1066e1a290c3750a27772c89f83731d28609 rtwo-panel-revive.sh
|
||||||
|
8e374a7304f8648d33d9087149e53efc5b09ffe1f7bc07b8b10521b7b1163e4787b56ecf3d2871629b1cfcc0037c1c15d084b514412e5e0a90ff66fd8e83b088 rtwo-panel-revive.service
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
[Unit]
|
||||||
|
# Bring-up diagnostic (2026-09-18): shell sin ninguna dependencia de
|
||||||
|
# cripto/entropia, para no quedar a ciegas si sshdgenkeys.service (ssh-keygen
|
||||||
|
# -A, bloqueado en getrandom() si el CRNG nunca termina de inicializar) o
|
||||||
|
# cualquier otra cosa de multi-user.target se cuelga antes de que sshd
|
||||||
|
# arranque. DefaultDependencies=no + WantedBy=sysinit.target: arranca lo mas
|
||||||
|
# temprano posible, en paralelo con todo lo demas, sin esperar a basic.target
|
||||||
|
# ni a nada que dependa de el. Misma IP/puerto que el debug-shell del
|
||||||
|
# initramfs (pmos.debug-shell), a proposito, para no tener que recordar dos
|
||||||
|
# direcciones distintas.
|
||||||
|
Description=Debug telnet shell (bring-up, sin dependencias de cripto)
|
||||||
|
DefaultDependencies=no
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/sbin/telnetd -b 172.16.42.1:23 -F -l /bin/sh
|
||||||
|
Restart=always
|
||||||
|
RestartSec=1
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=sysinit.target
|
||||||
@@ -17,30 +17,231 @@ deviceinfo_external_storage="true"
|
|||||||
deviceinfo_screen_width="1080"
|
deviceinfo_screen_width="1080"
|
||||||
deviceinfo_screen_height="2400"
|
deviceinfo_screen_height="2400"
|
||||||
deviceinfo_getty="ttyMSM0;115200"
|
deviceinfo_getty="ttyMSM0;115200"
|
||||||
|
|
||||||
# The DTS ships a simple-framebuffer pointing at the bootloader's continuous
|
|
||||||
# splash buffer, so there should be an early console on the panel. If the
|
|
||||||
# screen stays black, set this to "true" (and fix the framebuffer@ node).
|
|
||||||
# There is no DRM/mdss yet, so no mode setting / backlight control.
|
|
||||||
deviceinfo_no_framebuffer="true"
|
deviceinfo_no_framebuffer="true"
|
||||||
|
|
||||||
# Split the initramfs: a small base archive (fits in init_boot, 8 MB) plus a
|
# UN SOLO initramfs, sin initramfs-extra. Es lo que hace que
|
||||||
# separate initramfs-extra loaded from the rootfs later.
|
# 'pmos.debug-shell' sirva de algo: init_2nd.sh (y con el, la rama
|
||||||
deviceinfo_create_initfs_extra="true"
|
# if [ "$debug_shell" = "y" ]; then debug_shell; fi
|
||||||
|
# ) vive en el paquete de ficheros -extra, o sea DENTRO del rootfs, que en
|
||||||
|
# este equipo no podemos flashear. Con extra separado, jump_init_2nd() del
|
||||||
|
# stage 1 es un no-op, la bandera de la cmdline no la lee nadie y el unico
|
||||||
|
# camino a la shell de depuracion es agotar los 30 s de wait_boot_partition
|
||||||
|
# -> fail_halt_boot. Fusionandolo, init.sh salta a init_2nd.sh de inmediato,
|
||||||
|
# que hace setup_udev + setup_usb_network + start_unudhcpd y ENTONCES entra
|
||||||
|
# en debug_shell: telnet en 172.16.42.1:23 y getty en /dev/ttyGS0 en cuanto
|
||||||
|
# el USB enumera, sin depender de ninguna particion.
|
||||||
|
# El coste (initramfs de 3 MiB -> ~15 MiB) ya no importa: con cabecera v2 el
|
||||||
|
# ramdisk va DENTRO de boot.img, no en init_boot, y boot_a admite 96 MiB.
|
||||||
|
deviceinfo_create_initfs_extra="false"
|
||||||
|
# The stock LineageOS boot chain packs both Android-v4 ramdisks with LZ4
|
||||||
|
# legacy. When the bootloader concatenates our vendor_ramdisk with the
|
||||||
|
# init_boot ramdisk, a gzip stream after an LZ4-legacy one makes the
|
||||||
|
# kernel's unpacker choke ("invalid magic" -> panic unknown-block(0,0)).
|
||||||
|
# Match the stock format.
|
||||||
|
deviceinfo_initfs_compression="lz4:legacy"
|
||||||
|
|
||||||
# Bootloader related
|
# Bootloader: Android 13 GKI. The kernel goes in boot, the DTB + cmdline +
|
||||||
|
# initramfs in vendor_boot. init_boot holds the generic (first-stage)
|
||||||
|
# ramdisk - it MUST be replaced with pmOS's, or the stock AOSP /init runs.
|
||||||
|
# Lessons folded in from device-motorola-dubai (the closest working
|
||||||
|
# Motorola mainline port) and the stock LineageOS images.
|
||||||
deviceinfo_flash_method="fastboot"
|
deviceinfo_flash_method="fastboot"
|
||||||
|
# Keep flashing dtbo, and keep it EMPTY. The ABL overlays whatever sits in
|
||||||
|
# the dtbo partition on top of the DTB it took from vendor_boot, and a stock
|
||||||
|
# overlay applied to a mainline DTB mangles it - the documented advice for
|
||||||
|
# mainline on Qualcomm is to erase the dtbo partition or program NULL to it.
|
||||||
|
# The dtbo.img pmbootstrap generates here is 24 MiB of zeroes (no DTBO magic,
|
||||||
|
# no entries), so flashing it IS that "program NULL" step: it wipes the
|
||||||
|
# LineageOS/stock table (32 entries) that a restore-to-stock puts back.
|
||||||
|
# Do not drop this line expecting the ABL to skip overlays - dropping it
|
||||||
|
# leaves the stock table in place.
|
||||||
deviceinfo_flash_fastboot_partition_dtbo="dtbo"
|
deviceinfo_flash_fastboot_partition_dtbo="dtbo"
|
||||||
deviceinfo_flash_fastboot_partition_vbmeta="vbmeta"
|
deviceinfo_flash_fastboot_partition_vbmeta="vbmeta"
|
||||||
deviceinfo_kernel_cmdline="console=tty0 console=ttyMSM0,115200n8 earlycon clk_ignore_unused pd_ignore_unused ignore_loglevel"
|
# CONSOLA REAL POR UART (2026-09-13, sin framebuffer en el .dts): console=
|
||||||
|
# apunta ahora a ttyMSM0 (uart7, GENI QUP0/SE7), no a tty0. Ya no hace falta
|
||||||
|
# el 'ignore_loglevel'+panic=5-como-sustituto-de-consola: si el UART vive,
|
||||||
|
# vemos todo en tiempo real.
|
||||||
|
#
|
||||||
|
# 'earlycon' AGREGADO DE NUEVO (2026-09-13, segunda pasada). Se habia sacado
|
||||||
|
# a proposito por el riesgo real y documentado de fragilidad en el handover
|
||||||
|
# clk/earlycon (serie LKML "clk: help OF platforms with their stdout
|
||||||
|
# (earlycon) clocks during early boot", aun en v6) combinado con la sospecha
|
||||||
|
# de que DisableHypUartUsageForLogging() (SMC que el ABL llama antes de
|
||||||
|
# saltar al kernel, ver rtwo-abl-vs-abl-de-referencia) deje el UART en un
|
||||||
|
# estado raro para un acceso CRUDO y tempranisimo. Pero: 'console=ttyMSM0'
|
||||||
|
# SOLO (sin earlycon) no muestra nada hasta que el driver real de
|
||||||
|
# qcom_geni_serial prueba, lo cual exige que GCC y el pinctrl ya hayan
|
||||||
|
# probado antes - y CADA cuelgue que vimos en este bring-up paso muchisimo
|
||||||
|
# antes de eso (reset en 1-2s, antes de fbcon). Si el problema sigue siendo
|
||||||
|
# pre-driver, sin earlycon no veriamos nada, igual que en todos los intentos
|
||||||
|
# anteriores con framebuffer. Sin argumento usa /chosen/stdout-path (serial0
|
||||||
|
# -> &uart7, qcom,geni-debug-uart tiene OF_EARLYCON_DECLARE).
|
||||||
|
#
|
||||||
|
# 'panic=5': con consola real ya no es indispensable, pero se deja: si
|
||||||
|
# panickea en vez de colgarse, lo vemos en pantalla Y el registro queda en
|
||||||
|
# pstore/ramoops para la proxima vez.
|
||||||
|
# 'pmos.debug-shell': shell de depuracion por USB (telnet 172.16.42.1) con
|
||||||
|
# dmesg y pstore montados, si se llega al stage 2 del initramfs.
|
||||||
|
# 'allow_mismatched_32bit_el0' AGREGADO (2026-09-14): AYN Thor (mismo SoC,
|
||||||
|
# SD8 Gen 2 = SM8550, arranca ABL-directo sin U-Boot igual que nosotros) lo
|
||||||
|
# lleva en su receta de cmdline. Sin el, en el peor caso el kernel solo
|
||||||
|
# restringe apps de 32 bits en runtime (no bloquea el arranque segun el
|
||||||
|
# codigo fuente) - pero es gratis agregarlo: solo relaja una verificacion,
|
||||||
|
# nunca puede empeorar nada.
|
||||||
|
# 'console=tty0 ignore_loglevel' AGREGADO (2026-09-17): consola visual sobre
|
||||||
|
# el framebuffer@b8000000 reagregado al .dts del kernel, ver ese archivo.
|
||||||
|
# 'console=ttyMSM0,115200n8' SACADO (2026-09-18): no hay UART real
|
||||||
|
# enganchado en este bring-up, la consola que se usa de verdad es la de
|
||||||
|
# framebuffer (tty0, ver foto). Se deja 'earlycon' (bare, sin argumento)
|
||||||
|
# porque igual sale por /chosen/stdout-path sin bloquear nada aunque nadie
|
||||||
|
# lo lea.
|
||||||
|
# 'printk.devkmsg=on' AGREGADO (2026-09-21): con systemd.log_target=kmsg
|
||||||
|
# (ver qcom-phone.env), TODO el log visible en la consola de framebuffer
|
||||||
|
# sale de que systemd escribe activamente a /dev/kmsg -- inicialmente se
|
||||||
|
# penso que el kernel (printk.devkmsg=ratelimit por default) era el que
|
||||||
|
# cortaba esas escrituras con un rate-limit agresivo, disparando "Too many
|
||||||
|
# messages being logged to kmsg, ignoring" a los ~2-3s de systemd. FALSO,
|
||||||
|
# confirmado en hardware: con SOLO este flag el freeze sigue exactamente
|
||||||
|
# igual, misma linea. Se deja de todos modos (no molesta), ver el flag de
|
||||||
|
# abajo para el fix real.
|
||||||
|
# 'systemd.log_ratelimit_kmsg=false' AGREGADO (2026-09-21, segundo
|
||||||
|
# intento): el rate-limit real es del propio systemd, no del kernel --
|
||||||
|
# write_to_kmsg() en systemd tiene su PROPIO limitador interno para
|
||||||
|
# escrituras a /dev/kmsg, totalmente independiente de printk.devkmsg.
|
||||||
|
# Documentado en systemd(1)/kernel-command-line(7) como
|
||||||
|
# $SYSTEMD_LOG_RATELIMIT_KMSG / systemd.log_ratelimit_kmsg= (boolean,
|
||||||
|
# default true). CONFIRMADO EN HARDWARE QUE ESTE FLAG TAMPOCO ALCANZA: el
|
||||||
|
# usuario reportó que después del freeze NO aparece ningún mensaje nuevo,
|
||||||
|
# ni siquiera los timeouts de WMI del kernel que se repiten cada ~13s
|
||||||
|
# (esos SÍ se siguen generando en el ring buffer real, confirmado por
|
||||||
|
# SSH). Descarta cualquier teoría de "se generan menos mensajes" -- el
|
||||||
|
# framebuffer literalmente deja de refrescarse, sin relación con volumen
|
||||||
|
# de logging.
|
||||||
|
# 'cpufreq.default_governor=performance' AGREGADO (2026-09-21, tercer
|
||||||
|
# intento, DIAGNÓSTICO): el .dts del kernel
|
||||||
|
# (sm8550-motorola-rtwo.dts líneas 721-754) ya documenta que dispcc
|
||||||
|
# habilitado por defecto glitchea disp_cc_mdss_mdp_clk_src (el reloj de
|
||||||
|
# píxeles del MDP que el ABL/U-Boot deja corriendo para seguir escaneando
|
||||||
|
# el framebuffer heredado), fix aplicado ahí ('&dispcc { status =
|
||||||
|
# "disabled"; }', confirmado vigente) -- pero el freeze persiste con ese
|
||||||
|
# fix puesto, así que algo MÁS sigue tocando ese mismo reloj heredado por
|
||||||
|
# una vía distinta. Hipótesis: GCC comparte una PLL padre con ese reloj de
|
||||||
|
# píxeles, y el escalado dinámico de cpufreq (que arranca justo cuando el
|
||||||
|
# boot termina y baja la carga -- la misma ventana donde aparece el
|
||||||
|
# freeze) la glitchea indirectamente. Este flag fija el governor a
|
||||||
|
# 'performance' para probar si evita el glitch. CONFIRMADO EN HARDWARE QUE
|
||||||
|
# TAMPOCO ALCANZA: freeze idéntico con el governor fijo -- descarta
|
||||||
|
# cpufreq DVFS.
|
||||||
|
#
|
||||||
|
# Con 4 hipótesis descartadas con evidencia real (rate limit kernel, rate
|
||||||
|
# limit systemd, cpufreq, y el mecanismo de dispcc vía otro controlador --
|
||||||
|
# dmesg | grep WARNING/clk_rcg2/update_config/genpd no muestra nada salvo
|
||||||
|
# "PM: genpd: Not disabling unused power domains", y el usuario confirmó
|
||||||
|
# que el freeze pasaba INCLUSO ANTES de que existieran los nodos de WiFi
|
||||||
|
# en el .dts, descartando PCIe/ath12k), toca dejar de adivinar QUÉ driver
|
||||||
|
# específico es y aislar CUÁNDO pasa.
|
||||||
|
# 'systemd.mask=systemd-udev-trigger.service' PROBADO (2026-09-21, cuarto
|
||||||
|
# intento): CONFIRMADO EN HARDWARE que con esto el freeze DESAPARECE
|
||||||
|
# (pantalla mostró "emergency mode" en vez de congelarse) -- prueba
|
||||||
|
# definitiva de que es un driver/módulo cargado durante el coldplug de
|
||||||
|
# udev, no un timeout de hardware. Pero bloquear el coldplug entero rompe
|
||||||
|
# el boot normal (cae a emergency mode sin red, probablemente porque el
|
||||||
|
# descubrimiento de particiones/interfaz USB de red también depende de
|
||||||
|
# udev). SACADO, reemplazado por el flag de abajo para bisectar sin
|
||||||
|
# romper el boot.
|
||||||
|
# 'module_blacklist=gpucc_sm8550,videocc_sm8550,camcc_sm8550' AGREGADO
|
||||||
|
# (2026-09-21, quinto intento) -- CONFIRMADO EN HARDWARE QUE ES EL FIX:
|
||||||
|
# con estos 3 bloqueados, boot completo normal (SSH/red ok) y la pantalla
|
||||||
|
# llega al login prompt sin congelarse. NO se aisló cuál de los tres en
|
||||||
|
# particular es el culpable real -- el bring-up actual no necesita
|
||||||
|
# GPU/video-codec/cámara, así que no vale la pena bisectar más por ahora.
|
||||||
|
# Si en el futuro hace falta reactivar alguno (ej. GPU para aceleración
|
||||||
|
# gráfica), sacarlo de esta lista de a uno y probar en hardware; si
|
||||||
|
# individualmente reproduce el freeze, deshabilitarlo a nivel .dts
|
||||||
|
# (status = "disabled", mismo patrón que dispcc) en vez de blacklistear
|
||||||
|
# el módulo. FIX VIGENTE.
|
||||||
|
# 'gpucc_sm8550' SACADO de la lista (2026-09-21) para empezar el
|
||||||
|
# bisect de a uno, siguiendo el plan de arriba -- es el primer candidato
|
||||||
|
# porque es el unico de los tres con un caso de uso real a corto plazo
|
||||||
|
# (aceleracion grafica). El .dts no tiene NINGUN nodo de GPU (no hay
|
||||||
|
# Adreno/zap-shader, se verifico: sm8550-motorola-rtwo.dts no menciona
|
||||||
|
# "adreno"/"gpu@" en ningun lado), asi que gpucc queda habilitado por
|
||||||
|
# defecto desde sm8550.dtsi (como dispcc, ver comentario de &dispcc mas
|
||||||
|
# abajo) sin ningun consumidor todavia. Si el freeze vuelve, la sospecha
|
||||||
|
# recae en gpucc_sm8550 y el fix pasa a &gpucc { status = "disabled"; }
|
||||||
|
# en el .dts en vez del blacklist. videocc_sm8550/camcc_sm8550 SIGUEN
|
||||||
|
# blacklisteados -- no bisectar los tres a la vez.
|
||||||
|
# 'videocc_sm8550' SACADO de la lista (2026-09-21, mismo dia, mas tarde):
|
||||||
|
# reset random confirmado en hardware CON gpucc_sm8550 ya reactivado (asi
|
||||||
|
# que gpucc queda descartado como causa unica del reset). Leyendo el
|
||||||
|
# fuente real de gdsc_init() (drivers/clk/qcom/gdsc.c) contra los GDSC de
|
||||||
|
# cada controlador: video_cc_mvs0_gdsc/mvs1_gdsc tienen la flag
|
||||||
|
# HW_CTRL_TRIGGER (control autonomo por hardware del colapso de energia),
|
||||||
|
# la MISMA clase de flag que mdss_gdsc/mdss_int2_gdsc de dispcc (HW_CTRL) --
|
||||||
|
# dispcc tambien cambio de habilitado a disabled en el mismo fix del
|
||||||
|
# freeze, asi que es sospechoso que comparta este patron con videocc.
|
||||||
|
# camcc_sm8550 NO tiene ningun GDSC con HW_CTRL/HW_CTRL_TRIGGER/VOTABLE
|
||||||
|
# (todos "planos", solo POLL_CFG_GDSCR|RETAIN_FF_ENABLE) -- por eso queda
|
||||||
|
# BLACKLISTEADO todavia, es el candidato menos sospechoso de los tres.
|
||||||
|
# CONFIRMADO EN HARDWARE 2026-09-22: el reset random no volvio a pasar con
|
||||||
|
# videocc_sm8550 reactivado (junto con cpuidle.off=1 y CONFIG_GUNYAH_WATCHDOG,
|
||||||
|
# ver comentarios de abajo) -- no se aislo cual de los tres exactamente era
|
||||||
|
# necesario, pero con los tres juntos el reset desaparecio. No bisectar mas
|
||||||
|
# a menos que vuelva a aparecer.
|
||||||
|
# NOTA: esta linea documenta la cmdline "canonica" del dispositivo, pero el
|
||||||
|
# flujo de arranque VIGENTE es el chainload de U-Boot (ver
|
||||||
|
# rtwo-uboot-chainload en las notas del proyecto), que ignora esto y usa en
|
||||||
|
# cambio pmos_bootargs de ~/pmos/uboot-chainload/qcom-phone.env (mantenida
|
||||||
|
# en sincronia a mano con esta linea).
|
||||||
|
# 'ath12k.debug_mask=0x66' QUITADO 2026-09-22 (sincronizado con deviceinfo):
|
||||||
|
# el WiFi ya funciona y ese mask inundaba dmesg con cada header HTC RX y cada
|
||||||
|
# mensaje WMI (bits 0x2|0x4|0x20|0x40, solo presentes con CONFIG_ATH12K_DEBUG=y).
|
||||||
|
# Si vuelve a hacer falta diagnostico, re-agregar exactamente asi: ath12k.debug_mask=0x66
|
||||||
|
deviceinfo_kernel_cmdline="earlycon console=tty0 quiet splash panic=5 allow_mismatched_32bit_el0 random.trust_cpu=on printk.devkmsg=on systemd.log_ratelimit_kmsg=false cpufreq.default_governor=performance cpuidle.off=1 module_blacklist=camcc_sm8550 msm.separate_gpu_kms=1 deferred_probe_timeout=30"
|
||||||
|
# 'cpuidle.off=1' AGREGADO (2026-09-21): prueba para aislar si el reset
|
||||||
|
# random (siempre dentro de los primeros 5 min, nunca mientras corre algo)
|
||||||
|
# es un cuelgue al entrar/resumir de un C-state profundo via PSCI
|
||||||
|
# CPU_SUSPEND -- no hay overrides de idle-states/cpuidle en este .dts,
|
||||||
|
# heredamos lo que trae sm8550.dtsi sin validar en este hardware. Con esto
|
||||||
|
# las CPU solo usan WFI simple, nunca los estados profundos.
|
||||||
|
# CONFIRMADO EN HARDWARE 2026-09-22: el reset random no volvio a aparecer
|
||||||
|
# con esto puesto (junto con videocc_sm8550 reactivado y
|
||||||
|
# CONFIG_GUNYAH_WATCHDOG=y, ver arriba) -- FIX VIGENTE, no sacar. No se
|
||||||
|
# aislo cual de los tres cambios era el necesario especificamente; si algun
|
||||||
|
# dia hace falta reactivar cpuidle (ej. por consumo de bateria), bisectar
|
||||||
|
# de a uno en vez de sacar los tres juntos.
|
||||||
|
# CONFIG_GUNYAH_WATCHDOG=y (drivers/watchdog/gunyah_wdt.c, protocolo SMC con
|
||||||
|
# techo de ~32s) tambien esta activo -- no encajaba bien con la
|
||||||
|
# variabilidad de 0-5min del reset visto originalmente, pero se dejo activo
|
||||||
|
# sin costo real (el driver apaga el watchdog al hacer probe() si nadie
|
||||||
|
# abre /dev/watchdog) y forma parte del combo que resolvio el reset.
|
||||||
|
# CABECERA v2, NO v4 - y esta es la razon de fondo:
|
||||||
|
# DTBImgCheckAndAppendDT() coge el DTB de sitios distintos segun la version.
|
||||||
|
# v3/v4: ImageBuffer = vendor_boot, DtbOffset = dentro de vendor_boot. Si el
|
||||||
|
# match de qcom,msm-id/board-id falla, el fallback de "single appended
|
||||||
|
# DTB" calcula ImageBuffer(=BOOT.IMG) + DtbOffset(=de vendor_boot), o
|
||||||
|
# sea apunta DENTRO DEL KERNEL -> fdt_check_header falla ->
|
||||||
|
# EFI_NOT_FOUND -> LoadImageAndAuth falla -> el ABL de Motorola hace
|
||||||
|
# "Rebooting to retry" -> bootloop de 7 y a fastboot.
|
||||||
|
# v2: ImageBuffer y DtbOffset son AMBOS del boot.img, asi que el fallback
|
||||||
|
# funciona y acepta nuestro DTB SIN comparar ningun ID.
|
||||||
|
# O sea: con v4 dependemos de acertar msm-id/board-id exactos; con v2 no
|
||||||
|
# dependemos de nada. device-motorola-dubai (mismo ABL de Motorola, puerto
|
||||||
|
# mainline que SI arranca) usa v2 por esto mismo.
|
||||||
deviceinfo_generate_bootimg="true"
|
deviceinfo_generate_bootimg="true"
|
||||||
# The Motorola bootloader places the DTB / kernel at the addresses in the
|
deviceinfo_header_version="2"
|
||||||
# vendor_boot header. mkbootimg defaults to base 0x10000000 (dtb ends up at
|
deviceinfo_append_dtb="true"
|
||||||
# 0x11f00000, outside RAM) - stock uses base 0. Force the stock layout.
|
deviceinfo_flash_offset_base="0x00000000"
|
||||||
deviceinfo_bootimg_custom_args="--base 0x00000000 --kernel_offset 0x00008000 --ramdisk_offset 0x01000000 --tags_offset 0x00000100 --dtb_offset 0x01f00000 --os_version 16.0.0 --os_patch_level 2026-08"
|
deviceinfo_flash_offset_kernel="0x00008000"
|
||||||
|
deviceinfo_flash_offset_ramdisk="0x01000000"
|
||||||
|
deviceinfo_flash_offset_second="0x00000000"
|
||||||
|
deviceinfo_flash_offset_tags="0x00000100"
|
||||||
|
deviceinfo_flash_offset_dtb="0x01f00000"
|
||||||
deviceinfo_bootimg_qcdt="false"
|
deviceinfo_bootimg_qcdt="false"
|
||||||
deviceinfo_header_version="4"
|
|
||||||
deviceinfo_flash_pagesize="4096"
|
deviceinfo_flash_pagesize="4096"
|
||||||
|
# Stock uses base 0 (mkbootimg defaults to 0x10000000, dtb ends up outside
|
||||||
|
# RAM). os_* to satisfy anti-rollback on the boot image.
|
||||||
|
deviceinfo_bootimg_custom_args="--base 0x00000000 --kernel_offset 0x00008000 --ramdisk_offset 0x01000000 --tags_offset 0x00000100 --dtb_offset 0x01f00000 --os_version 16.0.0 --os_patch_level 2026-08-01"
|
||||||
deviceinfo_flash_sparse="true"
|
deviceinfo_flash_sparse="true"
|
||||||
|
|
||||||
# UFS storage
|
# UFS storage
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Corre en la etapa /hooks-cleanup de init_2nd.sh, justo antes de
|
||||||
|
# "Switching root" (el ultimo punto de control antes de pasarle el arbol
|
||||||
|
# de procesos al systemd de la rootfs real).
|
||||||
|
#
|
||||||
|
# El dropbear que arranca initfs-hook.sh para el debug-shell (-p 22, en
|
||||||
|
# background con '&') NO muere solo en switch_root -- los procesos
|
||||||
|
# existentes sobreviven el pivote de root, solo cambia el filesystem raiz
|
||||||
|
# para los que arrancan DESPUES. Confirmado en hardware 2026-09-19: quedaba
|
||||||
|
# vivo (PID bajo, ej. 235) pegado al puerto 22 para siempre, y
|
||||||
|
# sshd.service de la rootfs real nunca podia bindear ("Address in use",
|
||||||
|
# reintentaba 5 veces y se rendia con start-limit-hit) -- el sistema
|
||||||
|
# arrancaba perfecto por debajo (Multi-User System, Graphical Interface
|
||||||
|
# alcanzados en ~48s), pero SSH quedaba inutilizable siempre.
|
||||||
|
pkill dropbear 2>/dev/null || true
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Arranca dropbear (SSH) en el debug-shell, ademas del telnetd que ya trae
|
||||||
|
# postmarketos-mkinitfs-hook-debug-shell. Ningun paquete oficial de
|
||||||
|
# postmarketOS declara dropbear para el initramfs (solo para el rootfs real,
|
||||||
|
# via postmarketos-base-downstream, que es otra cosa) -- se agrega aca a
|
||||||
|
# mano porque hace falta poder scriptear el debug-shell (heredocs, comandos
|
||||||
|
# no interactivos por SSH), cosa que telnet no permite bien.
|
||||||
|
#
|
||||||
|
# Corre via run_hooks() en init_2nd.sh, DESPUES de setup_usb_network +
|
||||||
|
# start_unudhcpd (la red ya esta arriba en este punto) y ANTES de
|
||||||
|
# debug_shell(). Ver init_functions.sh:run_hooks (invoca cada hook con
|
||||||
|
# 'sh "$hook"', no hace falta que sea ejecutable).
|
||||||
|
|
||||||
|
# Panel NT37705 (2026-09-25): el ABL deja el subsistema de display encendido
|
||||||
|
# (mdss_gdsc on) y el primer encendido de msm sobre ese estado no logra
|
||||||
|
# arrancar el panel (sin TE, lecturas DCS -61). Probado en hardware: ni un
|
||||||
|
# ciclo de alimentacion del panel ni uno del host DSI/PHY alcanzan; lo unico
|
||||||
|
# que lo arranca es apagar mdss_gdsc (reinicia todo el hardware de display),
|
||||||
|
# y eso pasa con un blank/unblank del framebuffer. Se hace aca, antes del
|
||||||
|
# debug-shell, para que la consola ya se vea. El estado de mdss_gdsc durante
|
||||||
|
# el blank queda en dmesg para confirmar que el dominio se apago.
|
||||||
|
if [ -e /sys/class/graphics/fb0/blank ]; then
|
||||||
|
mount -t debugfs none /sys/kernel/debug 2>/dev/null
|
||||||
|
echo 1 > /sys/class/graphics/fb0/blank
|
||||||
|
sleep 1
|
||||||
|
gdsc=$(grep '^mdss_gdsc ' /sys/kernel/debug/pm_genpd/pm_genpd_summary 2>/dev/null)
|
||||||
|
echo "rtwo-panel: mdss_gdsc durante blank: ${gdsc:-desconocido}" > /dev/kmsg
|
||||||
|
echo 0 > /sys/class/graphics/fb0/blank
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Este initramfs no trae /etc/passwd ni /etc/shadow (confirmado: ni
|
||||||
|
# siquiera una entrada minima de root) -- sin esto dropbear no puede
|
||||||
|
# resolver el home de root para buscar authorized_keys, y 'passwd root'
|
||||||
|
# falla con "unknown uid 0". El shadow con el campo de clave vacio entre
|
||||||
|
# los primeros dos ':' permite login sin contrasena (para cuando -B mas
|
||||||
|
# abajo si esta soportado por este build de dropbear).
|
||||||
|
if [ ! -f /etc/passwd ]; then
|
||||||
|
cat > /etc/passwd <<'PASSWD'
|
||||||
|
root:x:0:0:root:/root:/bin/sh
|
||||||
|
PASSWD
|
||||||
|
fi
|
||||||
|
if [ ! -f /etc/group ]; then
|
||||||
|
cat > /etc/group <<'GROUP'
|
||||||
|
root:x:0:
|
||||||
|
GROUP
|
||||||
|
fi
|
||||||
|
if [ ! -f /etc/shadow ]; then
|
||||||
|
cat > /etc/shadow <<'SHADOW'
|
||||||
|
root::10933:0:99999:7:::
|
||||||
|
SHADOW
|
||||||
|
chmod 600 /etc/shadow
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p /etc/dropbear
|
||||||
|
mkdir -p /root/.ssh
|
||||||
|
cat > /root/.ssh/authorized_keys <<'KEYS'
|
||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPpCHmYjP+h7qod3HLcAU55A05c/ZDSkzKEXhU8OpJZG rtwo-debug-shell
|
||||||
|
KEYS
|
||||||
|
chmod 700 /root/.ssh
|
||||||
|
chmod 600 /root/.ssh/authorized_keys
|
||||||
|
|
||||||
|
# -R: genera las claves de host al vuelo (el initramfs es tmpfs, no
|
||||||
|
# persisten entre arranques, no tiene sentido guardarlas)
|
||||||
|
# -B: tambien permite login con clave en blanco, por si /etc/passwd trae
|
||||||
|
# root sin contrasena (mismo modelo de acceso que telnetd -l
|
||||||
|
# pmos_getty, que da shell sin ninguna autenticacion) -- si el build de
|
||||||
|
# dropbear no tiene compilado el soporte de blank-password esta opcion
|
||||||
|
# simplemente no hace nada, la clave publica de arriba sigue funcionando
|
||||||
|
# -E: loguea a stderr/consola, para poder ver por que no conecta si falla
|
||||||
|
/usr/sbin/dropbear -R -B -E -p 22 &
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
# rtwo (2026-09-25): el boton de encendido (pon_pwrkey, KEY_POWER) se usa para
|
||||||
|
# ocultar el dashboard de la consola y todavia no hay suspension probada en
|
||||||
|
# este equipo. Sin esto, systemd-logind apaga el telefono al tocarlo.
|
||||||
|
[Login]
|
||||||
|
HandlePowerKey=ignore
|
||||||
|
HandlePowerKeyLongPress=poweroff
|
||||||
@@ -7,9 +7,30 @@ spi-geni-qcom
|
|||||||
goodix-berlin-core
|
goodix-berlin-core
|
||||||
goodix-berlin-spi
|
goodix-berlin-spi
|
||||||
|
|
||||||
# USB gadget path (usually built-in, harmless if already so)
|
# USB keyboard (via &usb_1 host mode + a hub). CONFIG_HID/CONFIG_USB_HID
|
||||||
|
# are built-in, but CONFIG_HID_GENERIC=m is not -- without it, usbcore
|
||||||
|
# enumerates the keyboard fine (visible in dmesg) but no input device is
|
||||||
|
# ever created, so no keypress does anything. 2026-09-22: keyboard showed
|
||||||
|
# as "connected" but keys didn't work, this was the missing piece.
|
||||||
|
hid-generic
|
||||||
|
|
||||||
|
# USB gadget path. dwc3 + dwc3-qcom are built-in; the eUSB2 PHY and its
|
||||||
|
# PMIC repeater are modules and MUST be in the initramfs, otherwise
|
||||||
|
# usb@a600000 sits on -EPROBE_DEFER forever and no UDC appears.
|
||||||
|
# NOTE: in kernel 7.2 the SNPS eUSB2 PHY driver is 'phy-snps-eusb2'
|
||||||
|
# (CONFIG_PHY_SNPS_EUSB2), not the old 'phy-qcom-snps-eusb2'.
|
||||||
|
phy-snps-eusb2
|
||||||
phy-qcom-eusb2-repeater
|
phy-qcom-eusb2-repeater
|
||||||
phy-qcom-snps-eusb2
|
|
||||||
|
# Root filesystem lives on UFS (1d84000.ufshc) inside the dynamic 'super'
|
||||||
|
# partition. ufs-qcom and its QMP PHY are modules in this config, so without
|
||||||
|
# them here the initramfs never sees a root device and the kernel panics with
|
||||||
|
# "VFS: Unable to mount root fs on unknown-block(0,0)". qcom_ice is ufs-qcom's
|
||||||
|
# only modules.dep dependency; listed explicitly rather than relying on
|
||||||
|
# mkinitfs to pull it in.
|
||||||
|
ufs-qcom
|
||||||
|
qcom_ice
|
||||||
|
phy-qcom-qmp-ufs
|
||||||
|
|
||||||
# Panel / DRM are not wired up yet:
|
# Panel / DRM are not wired up yet:
|
||||||
# panel driver for Novatek NT37705 (1080x2400 DSC cmd, reset TLMM133)
|
# panel driver for Novatek NT37705 (1080x2400 DSC cmd, reset TLMM133)
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Revive the NT37705 panel if it came up dead from the ABL handoff
|
||||||
|
# Antes de cualquier cliente DRM/fbcon interactivo: con un DRM master activo
|
||||||
|
# el blank de fbdev no hace nada (ver rtwo-panel-revive.sh).
|
||||||
|
Before=rtwo-dashboard.service getty@tty1.service display-manager.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/libexec/rtwo-panel-revive
|
||||||
|
TimeoutStartSec=60
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Panel NT37705: el ABL deja el subsistema de display encendido (mdss_gdsc on)
|
||||||
|
# y a veces el primer encendido de msm sobre ese estado no arranca el panel
|
||||||
|
# (lecturas DCS -61, pantalla negra). Lo unico que lo revive es apagar
|
||||||
|
# mdss_gdsc, cosa que pasa con un blank/unblank del framebuffer.
|
||||||
|
#
|
||||||
|
# El initfs-hook hace lo mismo, pero msm suele enganchar el display recien
|
||||||
|
# despues del switch_root (no hay fb0 en el initramfs), asi que aca se
|
||||||
|
# repite desde el rootfs. Solo actua si el panel esta muerto: con el panel
|
||||||
|
# vivo actual_brightness devuelve el valor; muerto, falla con ENODATA (la
|
||||||
|
# lectura DCS del driver da -61).
|
||||||
|
#
|
||||||
|
# Tiene que correr antes que cualquier cliente DRM (cage): con un DRM
|
||||||
|
# master activo el blank de fbdev no hace nada.
|
||||||
|
|
||||||
|
BL=/sys/class/backlight/ae94000.dsi.0
|
||||||
|
FB=/sys/class/graphics/fb0
|
||||||
|
|
||||||
|
log() {
|
||||||
|
echo "rtwo-panel: $*" > /dev/kmsg
|
||||||
|
}
|
||||||
|
|
||||||
|
panel_alive() {
|
||||||
|
cat "$BL/actual_brightness" > /dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
i=0
|
||||||
|
while [ ! -e "$BL/actual_brightness" ] || [ ! -e "$FB/blank" ]; do
|
||||||
|
i=$((i + 1))
|
||||||
|
if [ "$i" -gt 60 ]; then
|
||||||
|
log "sin backlight/fb0 tras 30 s, nada que hacer"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
sleep 0.5
|
||||||
|
done
|
||||||
|
|
||||||
|
# Dar tiempo al primer prepare/enable del panel.
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
if panel_alive; then
|
||||||
|
log "panel vivo, sin cambios"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
for try in 1 2 3; do
|
||||||
|
log "panel muerto, blank/unblank (intento $try)"
|
||||||
|
echo 1 > "$FB/blank"
|
||||||
|
sleep 1
|
||||||
|
echo 0 > "$FB/blank"
|
||||||
|
sleep 1
|
||||||
|
if panel_alive; then
|
||||||
|
log "panel revivido en el intento $try"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log "el panel sigue muerto tras 3 intentos"
|
||||||
|
exit 1
|
||||||
@@ -5,7 +5,7 @@ maintainer="snlavaise <snlavaise@gmail.com>"
|
|||||||
_flavor="postmarketos-qcom-sm8550"
|
_flavor="postmarketos-qcom-sm8550"
|
||||||
pkgname=linux-$_flavor
|
pkgname=linux-$_flavor
|
||||||
pkgver=7.2
|
pkgver=7.2
|
||||||
pkgrel=6
|
pkgrel=48
|
||||||
pkgdesc="Mainline kernel for Qualcomm SM8550 (Snapdragon 8 Gen 2) + Motorola rtwo"
|
pkgdesc="Mainline kernel for Qualcomm SM8550 (Snapdragon 8 Gen 2) + Motorola rtwo"
|
||||||
arch="aarch64"
|
arch="aarch64"
|
||||||
_carch="arm64"
|
_carch="arm64"
|
||||||
@@ -47,6 +47,16 @@ source="
|
|||||||
config-motorola-rtwo.aarch64
|
config-motorola-rtwo.aarch64
|
||||||
rtwo.config
|
rtwo.config
|
||||||
sm8550-motorola-rtwo.dts
|
sm8550-motorola-rtwo.dts
|
||||||
|
ath12k-fix-regd-update-completion-hang.patch
|
||||||
|
ath12k-fix-regd-range-wideband.patch
|
||||||
|
icc-sm8550-keepalive-mm0-mdp-fetch.patch
|
||||||
|
ath12k-htc-log-every-rx-header.patch
|
||||||
|
ath12k-mhi-keep-awake.patch
|
||||||
|
msm-dsi-keep-panel-dsc-config.patch
|
||||||
|
msm-dp-ignore-invalid-lttpr-caps.patch
|
||||||
|
msm-dp-no-push-idle-when-link-down.patch
|
||||||
|
panel-novatek-nt37705.c
|
||||||
|
typec-mux-ps5169.c
|
||||||
"
|
"
|
||||||
builddir="$srcdir/linux-${pkgver/_/-}"
|
builddir="$srcdir/linux-${pkgver/_/-}"
|
||||||
|
|
||||||
@@ -60,6 +70,55 @@ prepare() {
|
|||||||
sed -i '/sm8550-mtp\.dtb/a dtb-$(CONFIG_ARCH_QCOM) += sm8550-motorola-rtwo.dtb' \
|
sed -i '/sm8550-mtp\.dtb/a dtb-$(CONFIG_ARCH_QCOM) += sm8550-motorola-rtwo.dtb' \
|
||||||
"$_dtsdir/Makefile"
|
"$_dtsdir/Makefile"
|
||||||
fi
|
fi
|
||||||
|
# Not-yet-upstream panel driver (DSI command mode + DSC, Motorola rtwo).
|
||||||
|
cp "$srcdir/panel-novatek-nt37705.c" \
|
||||||
|
drivers/gpu/drm/panel/panel-novatek-nt37705.c
|
||||||
|
if ! grep -q "CONFIG_DRM_PANEL_NOVATEK_NT37705" drivers/gpu/drm/panel/Kconfig; then
|
||||||
|
cat >> drivers/gpu/drm/panel/Kconfig <<'EOK'
|
||||||
|
|
||||||
|
config DRM_PANEL_NOVATEK_NT37705
|
||||||
|
tristate "Novatek NT37705 DSI panel"
|
||||||
|
depends on OF
|
||||||
|
depends on DRM_MIPI_DSI
|
||||||
|
depends on BACKLIGHT_CLASS_DEVICE
|
||||||
|
select DRM_DISPLAY_DSC_HELPER
|
||||||
|
select DRM_DISPLAY_HELPER
|
||||||
|
help
|
||||||
|
Say Y here if you want to enable support for Novatek NT37705 DSI
|
||||||
|
panel module. 1080x2400 command mode DSC panel from CSOT, used on
|
||||||
|
the Motorola rtwo.
|
||||||
|
EOK
|
||||||
|
fi
|
||||||
|
if ! grep -q "panel-novatek-nt37705" drivers/gpu/drm/panel/Makefile; then
|
||||||
|
echo 'obj-$(CONFIG_DRM_PANEL_NOVATEK_NT37705) += panel-novatek-nt37705.o' \
|
||||||
|
>> drivers/gpu/drm/panel/Makefile
|
||||||
|
fi
|
||||||
|
# Not-yet-upstream Parade PS5169 USB-C redriver (DisplayPort alt mode).
|
||||||
|
cp "$srcdir/typec-mux-ps5169.c" drivers/usb/typec/mux/ps5169.c
|
||||||
|
if ! grep -q "CONFIG_TYPEC_MUX_PS5169" drivers/usb/typec/mux/Kconfig; then
|
||||||
|
cat >> drivers/usb/typec/mux/Kconfig <<'EOK'
|
||||||
|
|
||||||
|
config TYPEC_MUX_PS5169
|
||||||
|
tristate "Parade PS5169 Type-C redriver driver"
|
||||||
|
depends on I2C
|
||||||
|
depends on DRM || DRM=n
|
||||||
|
select DRM_AUX_BRIDGE if DRM_BRIDGE && OF
|
||||||
|
select REGMAP_I2C
|
||||||
|
help
|
||||||
|
Say Y or M if your system has a Parade PS5169 USB/DP Type-C
|
||||||
|
redriver, as found on the Motorola rtwo.
|
||||||
|
EOK
|
||||||
|
fi
|
||||||
|
if ! grep -q "ps5169" drivers/usb/typec/mux/Makefile; then
|
||||||
|
echo 'obj-$(CONFIG_TYPEC_MUX_PS5169) += ps5169.o' \
|
||||||
|
>> drivers/usb/typec/mux/Makefile
|
||||||
|
fi
|
||||||
|
# Build this DTB with -@ so it gets a /__symbols__ node. The Motorola
|
||||||
|
# SM8550 ABL runs its dtbo-apply path (libufdt) against the base DTB
|
||||||
|
# and drops to the bootloader menu if the symbols are missing.
|
||||||
|
if ! grep -q "DTC_FLAGS_sm8550-motorola-rtwo" "$_dtsdir/Makefile"; then
|
||||||
|
echo 'DTC_FLAGS_sm8550-motorola-rtwo := -@' >> "$_dtsdir/Makefile"
|
||||||
|
fi
|
||||||
|
|
||||||
# Base config (from linux-postmarketos-mainline) + rtwo bring-up fragment.
|
# Base config (from linux-postmarketos-mainline) + rtwo bring-up fragment.
|
||||||
cp "$srcdir/config-motorola-rtwo.aarch64" .config
|
cp "$srcdir/config-motorola-rtwo.aarch64" .config
|
||||||
@@ -74,9 +133,9 @@ build() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
package() {
|
package() {
|
||||||
# The Motorola bootloader boots a *raw* uncompressed arm64 Image
|
# Raw uncompressed Image for the Android boot.img: the stock rtwo
|
||||||
# (BOARD_KERNEL_IMAGE_NAME := Image); Image.gz / vmlinuz.efi make it
|
# LineageOS boot.img carries a raw 'MZ' Image (BOARD_KERNEL_IMAGE_NAME
|
||||||
# reset in <2s. CONFIG_EFI_ZBOOT is off in rtwo.config.
|
# := Image), and CONFIG_EFI_ZBOOT is off in rtwo.config.
|
||||||
install -Dm644 "$builddir/arch/$_carch/boot/Image" \
|
install -Dm644 "$builddir/arch/$_carch/boot/Image" \
|
||||||
"$pkgdir/boot/vmlinuz"
|
"$pkgdir/boot/vmlinuz"
|
||||||
|
|
||||||
@@ -87,26 +146,12 @@ package() {
|
|||||||
INSTALL_MOD_STRIP=1 \
|
INSTALL_MOD_STRIP=1 \
|
||||||
INSTALL_DTBS_PATH="$pkgdir"/boot/dtbs
|
INSTALL_DTBS_PATH="$pkgdir"/boot/dtbs
|
||||||
|
|
||||||
# Android DT-overlay image with a single no-op overlay. The bootloader
|
# All-zero dtbo image (full partition size), same trick as
|
||||||
# would otherwise apply the stock kalama dtbo overlay onto our mainline
|
# device-motorola-dubai: the bootloader finds no valid DT-overlay
|
||||||
# base DTB - the phandles don't match, the merged tree is corrupt and
|
# table magic and applies nothing, so the stock kalama overlay does
|
||||||
# the kernel panics. A no-op overlay keeps the base DTB intact. (A
|
# not get merged onto our mainline base DTB.
|
||||||
# zero-entry table fails Motorola's pre-flash validation.)
|
|
||||||
mkdir -p "$pkgdir"/boot
|
mkdir -p "$pkgdir"/boot
|
||||||
printf '/dts-v1/;\n/plugin/;\n/ { };\n' > "$srcdir"/empty-overlay.dts
|
dd if=/dev/zero of="$pkgdir/boot/dtbo.img" bs=1M count=24 status=none
|
||||||
scripts/dtc/dtc -@ -O dtb -o "$srcdir"/empty.dtbo "$srcdir"/empty-overlay.dts
|
|
||||||
python3 - "$srcdir/empty.dtbo" "$pkgdir/boot/dtbo.img" <<-'EOF'
|
|
||||||
import struct, sys
|
|
||||||
fdt = open(sys.argv[1], "rb").read()
|
|
||||||
hdr_sz = ent_sz = 32
|
|
||||||
ent_off = hdr_sz + ent_sz
|
|
||||||
hdr = struct.pack(">IIIIIIII", 0xd7b7ab1e, ent_off + len(fdt),
|
|
||||||
hdr_sz, ent_sz, 1, hdr_sz, 2048, 0)
|
|
||||||
ent = struct.pack(">IIIIIIII", len(fdt), ent_off, 0, 0, 0, 0, 0, 0)
|
|
||||||
blob = hdr + ent + fdt
|
|
||||||
blob += b"\x00" * ((4096 - len(blob) % 4096) % 4096)
|
|
||||||
open(sys.argv[2], "wb").write(blob)
|
|
||||||
EOF
|
|
||||||
|
|
||||||
rm -f "$pkgdir"/usr/lib/modules/*/build "$pkgdir"/usr/lib/modules/*/source
|
rm -f "$pkgdir"/usr/lib/modules/*/build "$pkgdir"/usr/lib/modules/*/source
|
||||||
|
|
||||||
@@ -118,7 +163,17 @@ package() {
|
|||||||
# (same tarball as linux-postmarketos-mainline, ~150 MB download).
|
# (same tarball as linux-postmarketos-mainline, ~150 MB download).
|
||||||
sha512sums="
|
sha512sums="
|
||||||
52d5aa7f94f6aa6141debf4ceb4fc7a2e75505a7cd38b8854e501be83c2b57863020f463f2fdb2fc93667e6cfe13274d077e5207a611e4797bd9404ee0e64c58 linux-postmarketos-qcom-sm8550-7.2.tar.gz
|
52d5aa7f94f6aa6141debf4ceb4fc7a2e75505a7cd38b8854e501be83c2b57863020f463f2fdb2fc93667e6cfe13274d077e5207a611e4797bd9404ee0e64c58 linux-postmarketos-qcom-sm8550-7.2.tar.gz
|
||||||
f9ae52489f6b9e6fef64952943cd09732a5bef90a0e8765db03c256483028368687d6d5a1d80dd813a513e7e80d91f8b47f49b91cff67266b76f1d6275a7fea0 config-motorola-rtwo.aarch64
|
0ab844d874289be5529c7f83a1bdd16d867ea6d5f3f873556779cbd27908316fd5076c201d6b6fee40991da648eadc982422cc0136fe2c9034b7b4846e08a028 config-motorola-rtwo.aarch64
|
||||||
53441c23146d76bb0eaa358a25f6cce4f21740cfb65b755df52bbb218daf2cda30b3d80fa6ca4e283ea1afe2b0fa708307b678b421898100710b8fb99b08d6c5 rtwo.config
|
a03843765d996a4555f991b277dd9cc35f1c96da1b740330a3497202d355ec95406f380a0ec6a778d638bf9acfc1f28dbc441e156fb75b1cf00720af136f69b8 rtwo.config
|
||||||
740ac374913a1304d6b52048d89e14321952f0357d5bfd3c1305f76570fda6c1233b963ae77cc9631c60cc590885ed3c0877b19ad4cd65e66c5c441a94cd610b sm8550-motorola-rtwo.dts
|
ff2da1726b87210497d3d3961af5d95b9dfb160d48af90bb604dbb321b89c50689a06722c50db265aaf5c95b0078da278939efda8e1a8f30232827d27da1c8ca sm8550-motorola-rtwo.dts
|
||||||
|
a068e35a031858161e7a5ac00d7d2db9fd0d842fc19d3ebf08b03275ef470eff5462921e36390f535e2556619b6911192b6e2ccf7c313edafaf22d6da44c1d8e ath12k-fix-regd-update-completion-hang.patch
|
||||||
|
891b9b15e9113488667f2dcf9f1502aab24291228cc4096f358acbf4b67d299fb90b2d00b4e103139cd2160339a10833cddb781838d1f03721fef07ea45b3213 ath12k-fix-regd-range-wideband.patch
|
||||||
|
acbe599c08877065ea4419363da7ab75a16a557aad4b13161f22270f03b27efac1be487884896900c3e80d6df8e3545ca6b1df940d24badef1825fe0d27147ac icc-sm8550-keepalive-mm0-mdp-fetch.patch
|
||||||
|
cf4889c4dca8d3bac7544d71284e6b592d6014d9ad94badfc6d2cbe56a8402c61e4c0811c2f48d3352b446de9fbcf36b58226431c9276956d7ff3fe08c9eb589 ath12k-htc-log-every-rx-header.patch
|
||||||
|
6e01370d013278faba203e57ba20700d500639136672c040c0924a600fc3dcd8185a9f558ee7bf8a9ad5412113f6b6ab1901423006bb839e808dbcb151a16383 ath12k-mhi-keep-awake.patch
|
||||||
|
7673bec5661e692721bce41e72a330180e653d2b1b0a5cb3d36bdc869ad4a32ce23a6048d01c9cf2cf5c78c2a7e33899a0a149c65e9754d9d1c2cd71b542b84c msm-dsi-keep-panel-dsc-config.patch
|
||||||
|
3268835bcb539a77d16c888d933c856df6bb591861d30c7c61c6e0ab70751bac6248961ec30fedf9a62bebdcd63113cf1dd14e58a1b53262493d5e0e90dbbe3f msm-dp-ignore-invalid-lttpr-caps.patch
|
||||||
|
4f5ea9bfe01a6df166fdad601f6bd74d4b39049be924e8b4df58a8acbba7e9ebfc568bd81df70354209642f68e113804b9d1b62cbd3c83c319fae5fefcf07595 msm-dp-no-push-idle-when-link-down.patch
|
||||||
|
e8df16de9b413fedd0d36414311216e68e69363e1bfbba8c7b892120f860d217b58ceeffeb92566a5617177b0f190a64e9e2284d6d4285e72db35cf40770e41a panel-novatek-nt37705.c
|
||||||
|
dd95e80f62879d5167e7bfc7d74b4a64669a43dbf5aacb6a13e62d1ed4a88677ef1042f8cecc46e05e3d12ba75f070a5927df308c90fe846af93b78c6b310e80 typec-mux-ps5169.c
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
Fix regulatory range for wideband radios (carried by OpenWrt/Quectel
|
||||||
|
trees for WCN7850 and wideband QCN9274; NOT in v7.2 mainline).
|
||||||
|
|
||||||
|
ath12k_regd_update() assumes that a radio advertising 6 GHz cannot also
|
||||||
|
operate on 5 GHz. For the rtwo the WCN7850 is a single SMM radio whose
|
||||||
|
5 GHz band spans [4900-7125] (hw_mode 5, 2.4+5+6 GHz in one pdev), so
|
||||||
|
this old check picks only the 6 GHz portion of the band and drops the
|
||||||
|
real 5 GHz half from ar->freq_range. ath12k_mac_update_freq_range()
|
||||||
|
unions multiple calls, so the fixed conditions do not overwrite -- they
|
||||||
|
extend the advertised range to cover both portions.
|
||||||
|
|
||||||
|
Diagnostic build 2026-09-22 (same experiment as the wcn7850-pmu pinctrl
|
||||||
|
DT change): determines whether the cropped regulatory range sent to the
|
||||||
|
WCN7850 WMI path is what stalls the firmware at ath12k_mac_start()
|
||||||
|
("wmi command 16387 timeout", -EAGAIN on insufficient HTC credits after
|
||||||
|
the WMI channel list).
|
||||||
|
|
||||||
|
Blame/author: Robert Marko <robert.marko@sartura.hr>
|
||||||
|
From: https://git-03.infra.openwrt.org/openwrt/staging/stintel/plain/package/kernel/mac80211/patches/ath12k/104-wifi-ath12k-fix-regulatory-range-for-wideband-radios.patch
|
||||||
|
--- a/drivers/net/wireless/ath/ath12k/reg.c
|
||||||
|
+++ b/drivers/net/wireless/ath/ath12k/reg.c
|
||||||
|
@@ -310,7 +310,8 @@ int ath12k_regd_update(struct ath12k *ar, bool init)
|
||||||
|
ath12k_mac_update_freq_range(ar, freq_low, freq_high);
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP && !ar->supports_6ghz) {
|
||||||
|
+ if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP &&
|
||||||
|
+ reg_cap->low_5ghz_chan < ATH12K_MIN_6GHZ_FREQ) {
|
||||||
|
if (ab->hw_params->single_pdev_only) {
|
||||||
|
phy_id = ar->pdev->cap.band[WMI_HOST_WLAN_5GHZ_CAP].phy_id;
|
||||||
|
reg_cap = &ab->hal_reg_cap[phy_id];
|
||||||
|
@@ -322,7 +323,8 @@ int ath12k_regd_update(struct ath12k *ar, bool init)
|
||||||
|
ath12k_mac_update_freq_range(ar, freq_low, freq_high);
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP && ar->supports_6ghz) {
|
||||||
|
+ if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP &&
|
||||||
|
+ reg_cap->high_5ghz_chan >= ATH12K_MIN_6GHZ_FREQ) {
|
||||||
|
freq_low = max(reg_cap->low_5ghz_chan, ab->reg_freq_6ghz.start_freq);
|
||||||
|
freq_high = min(reg_cap->high_5ghz_chan, ab->reg_freq_6ghz.end_freq);
|
||||||
|
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
--- a/drivers/net/wireless/ath/ath12k/wmi.c 2026-09-20 02:48:13.191255011 -0300
|
||||||
|
+++ b/drivers/net/wireless/ath/ath12k/wmi.c 2026-09-20 02:48:13.192653745 -0300
|
||||||
|
@@ -6939,6 +6939,15 @@
|
||||||
|
goto mem_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /* Assign pdev_idx as soon as we have a valid reg_info, BEFORE the
|
||||||
|
+ * FALLBACK/DROP paths below, so ar->regd_update_completed still gets
|
||||||
|
+ * signalled even when the event is dropped because the country code
|
||||||
|
+ * already matches the default regd (fixes a permanent hang of any
|
||||||
|
+ * caller waiting on regd_update_completed, e.g. WMI_PDEV_SET_PARAM
|
||||||
|
+ * during mac80211 hw_register on bring-up).
|
||||||
|
+ */
|
||||||
|
+ pdev_idx = reg_info->phy_id;
|
||||||
|
+
|
||||||
|
ret = ath12k_reg_validate_reg_info(ab, reg_info);
|
||||||
|
if (ret == ATH12K_REG_STATUS_FALLBACK) {
|
||||||
|
ath12k_warn(ab, "failed to validate reg info %d\n", ret);
|
||||||
|
@@ -6955,7 +6964,6 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
/* free old reg_info if it exist */
|
||||||
|
- pdev_idx = reg_info->phy_id;
|
||||||
|
if (ab->reg_info[pdev_idx]) {
|
||||||
|
ath12k_reg_reset_reg_info(ab->reg_info[pdev_idx]);
|
||||||
|
kfree(ab->reg_info[pdev_idx]);
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
Log the raw HTC header of every received message.
|
||||||
|
|
||||||
|
Diagnostic-only, for the rtwo WiFi bring-up. ath12k_mac_start() dies on
|
||||||
|
the second WMI command with -EAGAIN out of ath12k_htc_send()'s
|
||||||
|
"insufficient credits" path, and the endpoint's credits are only ever
|
||||||
|
replenished by an ATH12K_HTC_RECORD_CREDITS record carried in the
|
||||||
|
*trailer* of a received HTC message -- there is no dedicated credit
|
||||||
|
event. /proc/interrupts shows ce2 (target->host WMI) taking real
|
||||||
|
interrupts, so messages do arrive; what cannot be seen from outside is
|
||||||
|
whether any of them ever carries a trailer at all.
|
||||||
|
|
||||||
|
The existing "htc rx completion ep %d" debug line does not answer that:
|
||||||
|
it is printed at the very end of the handler, so it misses both the EP0
|
||||||
|
control traffic and the "zero length packet with trailer data, just drop
|
||||||
|
these" early return -- which is exactly the shape a pure credit-report
|
||||||
|
message has. Print the decoded header right where the fields are read
|
||||||
|
instead, before anything can return early.
|
||||||
|
|
||||||
|
Sits under the existing ATH12K_DBG_HTC (0x4) mask, so it costs one
|
||||||
|
branch per RX when the mask is unset and disappears entirely without
|
||||||
|
CONFIG_ATH12K_DEBUG. Drop this patch once WiFi works.
|
||||||
|
|
||||||
|
--- a/drivers/net/wireless/ath/ath12k/htc.c
|
||||||
|
+++ b/drivers/net/wireless/ath/ath12k/htc.c
|
||||||
|
@@ -292,6 +292,14 @@
|
||||||
|
trailer_present = le32_get_bits(hdr->htc_info, HTC_HDR_FLAGS) &
|
||||||
|
ATH12K_HTC_FLAG_TRAILER_PRESENT;
|
||||||
|
|
||||||
|
+ ath12k_dbg(ab, ATH12K_DBG_HTC,
|
||||||
|
+ "htc rx hdr eid %d payload_len %d flags 0x%02x trailer %d trailer_len %d\n",
|
||||||
|
+ eid, payload_len,
|
||||||
|
+ (u8)le32_get_bits(hdr->htc_info, HTC_HDR_FLAGS),
|
||||||
|
+ trailer_present ? 1 : 0,
|
||||||
|
+ trailer_present ?
|
||||||
|
+ (int)le32_get_bits(hdr->ctrl_info, HTC_HDR_CONTROLBYTES0) : 0);
|
||||||
|
+
|
||||||
|
if (trailer_present) {
|
||||||
|
u8 *trailer;
|
||||||
|
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
Add ath12k.mhi_keep_awake to keep the WCN7850 out of MHI M1/M2.
|
||||||
|
|
||||||
|
Diagnostic for the rtwo WiFi bring-up. With CONFIG_ATH12K_DEBUG the log
|
||||||
|
shows that the firmware returns HTC credits for the WMI endpoint
|
||||||
|
normally (trailers on EP0, ~3 ms after each command) until the moment
|
||||||
|
the device reports M1 and the host moves it to M2 (MHI_CB_IDLE, 22 us
|
||||||
|
after the last credit is spent). After that the target sends nothing on
|
||||||
|
any endpoint and there is no RDDM/SYS_ERR, so it is asleep, not crashed.
|
||||||
|
|
||||||
|
WCN7850 uses shadow registers for the CE doorbells (below
|
||||||
|
ACCESS_ALWAYS_OFF), so those writes never go through pci_ops->wakeup().
|
||||||
|
mhi_keep_awake=1 takes an MHI device wake reference right after power
|
||||||
|
up and holds it until mhi_stop, so the device stays in M0. With it
|
||||||
|
off, MHI_CB_IDLE is logged at warn level to see when it happens.
|
||||||
|
|
||||||
|
--- a/drivers/net/wireless/ath/ath12k/pci.h 2026-09-22 20:54:12.225709859 -0300
|
||||||
|
+++ b/drivers/net/wireless/ath/ath12k/pci.h 2026-09-22 20:54:12.243985679 -0300
|
||||||
|
@@ -93,6 +93,7 @@
|
||||||
|
ATH12K_PCI_FLAG_IS_MSI_64,
|
||||||
|
ATH12K_PCI_ASPM_RESTORE,
|
||||||
|
ATH12K_PCI_FLAG_MULTI_MSI_VECTORS,
|
||||||
|
+ ATH12K_PCI_FLAG_MHI_KEEP_AWAKE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ath12k_pci_ops {
|
||||||
|
--- a/drivers/net/wireless/ath/ath12k/mhi.c 2026-09-22 20:54:12.223301456 -0300
|
||||||
|
+++ b/drivers/net/wireless/ath/ath12k/mhi.c 2026-09-22 20:54:12.244083641 -0300
|
||||||
|
@@ -18,6 +18,16 @@
|
||||||
|
#define OTP_VALID_DUALMAC_BOARD_ID_MASK 0x1000
|
||||||
|
#define MHI_CB_INVALID 0xff
|
||||||
|
|
||||||
|
+/* rtwo bring-up: the WMI endpoint runs out of HTC credits at the exact
|
||||||
|
+ * moment the device reports M1 and the host moves it to M2
|
||||||
|
+ * (MHI_CB_IDLE); after that the target never sends anything again.
|
||||||
|
+ * Holding MHI device wake for the whole session keeps it in M0, to
|
||||||
|
+ * tell whether the M1/M2 transition is what loses the WMI doorbells.
|
||||||
|
+ */
|
||||||
|
+static bool ath12k_mhi_keep_awake;
|
||||||
|
+module_param_named(mhi_keep_awake, ath12k_mhi_keep_awake, bool, 0644);
|
||||||
|
+MODULE_PARM_DESC(mhi_keep_awake, "Hold MHI device wake while powered on (never enter M1/M2)");
|
||||||
|
+
|
||||||
|
void ath12k_mhi_set_mhictrl_reset(struct ath12k_base *ab)
|
||||||
|
{
|
||||||
|
u32 val;
|
||||||
|
@@ -148,6 +158,11 @@
|
||||||
|
case MHI_CB_SYS_ERROR:
|
||||||
|
ath12k_warn(ab, "firmware crashed: MHI_CB_SYS_ERROR\n");
|
||||||
|
break;
|
||||||
|
+ case MHI_CB_IDLE:
|
||||||
|
+ /* rtwo: log at warn level so it shows up without debug_mask */
|
||||||
|
+ ath12k_warn(ab, "mhi idle: device moved to M2 (keep_awake %d)\n",
|
||||||
|
+ test_bit(ATH12K_PCI_FLAG_MHI_KEEP_AWAKE, &ab_pci->flags));
|
||||||
|
+ break;
|
||||||
|
case MHI_CB_EE_RDDM:
|
||||||
|
if (ab_pci->mhi_pre_cb == MHI_CB_EE_RDDM) {
|
||||||
|
ath12k_dbg(ab, ATH12K_DBG_BOOT,
|
||||||
|
@@ -493,6 +508,20 @@
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
+ if (ath12k_mhi_keep_awake) {
|
||||||
|
+ ret = mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev);
|
||||||
|
+ if (ret) {
|
||||||
|
+ ath12k_warn(ab_pci->ab,
|
||||||
|
+ "mhi_keep_awake: failed to hold device wake: %d\n",
|
||||||
|
+ ret);
|
||||||
|
+ ret = 0;
|
||||||
|
+ } else {
|
||||||
|
+ set_bit(ATH12K_PCI_FLAG_MHI_KEEP_AWAKE, &ab_pci->flags);
|
||||||
|
+ ath12k_info(ab_pci->ab,
|
||||||
|
+ "mhi_keep_awake: holding device wake, M1/M2 disabled\n");
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
@@ -501,6 +530,9 @@
|
||||||
|
|
||||||
|
void ath12k_mhi_stop(struct ath12k_pci *ab_pci, bool is_suspend)
|
||||||
|
{
|
||||||
|
+ if (test_and_clear_bit(ATH12K_PCI_FLAG_MHI_KEEP_AWAKE, &ab_pci->flags))
|
||||||
|
+ mhi_device_put(ab_pci->mhi_ctrl->mhi_dev);
|
||||||
|
+
|
||||||
|
/* During suspend we need to use mhi_power_down_keep_dev()
|
||||||
|
* workaround, otherwise ath12k_core_resume() will timeout
|
||||||
|
* during resume.
|
||||||
@@ -5464,7 +5464,7 @@ CONFIG_BCM7038_WDT=m
|
|||||||
#
|
#
|
||||||
# CONFIG_USBPCWATCHDOG is not set
|
# CONFIG_USBPCWATCHDOG is not set
|
||||||
# CONFIG_KEEMBAY_WATCHDOG is not set
|
# CONFIG_KEEMBAY_WATCHDOG is not set
|
||||||
# CONFIG_GUNYAH_WATCHDOG is not set
|
CONFIG_GUNYAH_WATCHDOG=y
|
||||||
CONFIG_SSB_POSSIBLE=y
|
CONFIG_SSB_POSSIBLE=y
|
||||||
# CONFIG_SSB is not set
|
# CONFIG_SSB is not set
|
||||||
CONFIG_BCMA_POSSIBLE=y
|
CONFIG_BCMA_POSSIBLE=y
|
||||||
@@ -11732,10 +11732,10 @@ CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
|
|||||||
CONFIG_PSTORE=y
|
CONFIG_PSTORE=y
|
||||||
CONFIG_PSTORE_DEFAULT_KMSG_BYTES=10240
|
CONFIG_PSTORE_DEFAULT_KMSG_BYTES=10240
|
||||||
CONFIG_PSTORE_COMPRESS=y
|
CONFIG_PSTORE_COMPRESS=y
|
||||||
# CONFIG_PSTORE_CONSOLE is not set
|
CONFIG_PSTORE_CONSOLE=y
|
||||||
# CONFIG_PSTORE_PMSG is not set
|
# CONFIG_PSTORE_PMSG is not set
|
||||||
# CONFIG_PSTORE_FTRACE is not set
|
# CONFIG_PSTORE_FTRACE is not set
|
||||||
CONFIG_PSTORE_RAM=m
|
CONFIG_PSTORE_RAM=y
|
||||||
# CONFIG_PSTORE_BLK is not set
|
# CONFIG_PSTORE_BLK is not set
|
||||||
# CONFIG_UFS_FS is not set
|
# CONFIG_UFS_FS is not set
|
||||||
CONFIG_EROFS_FS=m
|
CONFIG_EROFS_FS=m
|
||||||
|
|||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
--- a/drivers/interconnect/qcom/sm8550.c
|
||||||
|
+++ b/drivers/interconnect/qcom/sm8550.c
|
||||||
|
@@ -1136,6 +1136,22 @@ static struct qcom_icc_bcm bcm_mc0 = {
|
||||||
|
|
||||||
|
static struct qcom_icc_bcm bcm_mm0 = {
|
||||||
|
.name = "MM0",
|
||||||
|
+ /*
|
||||||
|
+ * qns_mem_noc_hf is the only node this BCM covers, and it's what
|
||||||
|
+ * qnm_mdp (the MDP's own interconnect master) feeds into. Our
|
||||||
|
+ * bring-up has no MDSS/DPU driver, so nothing in Linux ever calls
|
||||||
|
+ * icc_set_bw() on this path -- but the DPU hardware is still live,
|
||||||
|
+ * inherited from U-Boot, and keeps fetching the framebuffer over
|
||||||
|
+ * this exact bus. Without keepalive, icc_sync_state() (core.c,
|
||||||
|
+ * gated on every icc provider probing -- in practice on pcie0/
|
||||||
|
+ * WiFi finishing last) zeroes the firmware's boot-time vote on
|
||||||
|
+ * this BCM and the display freezes mid-boot, at a random-looking
|
||||||
|
+ * point that always lines up with pcie0 finishing probe. MC0/SH0/
|
||||||
|
+ * CN0/SN0/QUP0-2 already get this same treatment for the same
|
||||||
|
+ * reason (DDR/LLCC/config/system NoC/QUP buses that must never
|
||||||
|
+ * collapse to zero just because no driver claimed them yet).
|
||||||
|
+ */
|
||||||
|
+ .keepalive = true,
|
||||||
|
.num_nodes = 1,
|
||||||
|
.nodes = { &qns_mem_noc_hf },
|
||||||
|
};
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
--- a/drivers/gpu/drm/msm/dp/dp_display.c
|
||||||
|
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
|
||||||
|
@@ -251,16 +251,34 @@
|
||||||
|
int rc, lttpr_count;
|
||||||
|
|
||||||
|
if (drm_dp_read_lttpr_common_caps(dp->aux, dpcd, dp->link->lttpr_common_caps))
|
||||||
|
- return 0;
|
||||||
|
+ goto no_lttpr;
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * Some sinks without LTTPR support (e.g. DP->HDMI converters) mirror
|
||||||
|
+ * their receiver caps at 0xf0000 instead of NAKing the read. Only
|
||||||
|
+ * trust the block if it carries a valid LTTPR structure revision and
|
||||||
|
+ * repeater count: otherwise the caps would clamp the link to whatever
|
||||||
|
+ * lands in the "max lane count"/"max link rate" slots, and writing
|
||||||
|
+ * PHY_REPEATER_MODE would end up in the sink's own DPCD.
|
||||||
|
+ */
|
||||||
|
+ if (dp->link->lttpr_common_caps[0] < 0x14)
|
||||||
|
+ goto no_lttpr;
|
||||||
|
|
||||||
|
lttpr_count = drm_dp_lttpr_count(dp->link->lttpr_common_caps);
|
||||||
|
+ if (lttpr_count < 0)
|
||||||
|
+ goto no_lttpr;
|
||||||
|
+
|
||||||
|
rc = drm_dp_lttpr_init(dp->aux, lttpr_count);
|
||||||
|
if (rc) {
|
||||||
|
DRM_ERROR("failed to set LTTPRs transparency mode, rc=%d\n", rc);
|
||||||
|
- return 0;
|
||||||
|
+ goto no_lttpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lttpr_count;
|
||||||
|
+
|
||||||
|
+no_lttpr:
|
||||||
|
+ memset(dp->link->lttpr_common_caps, 0, sizeof(dp->link->lttpr_common_caps));
|
||||||
|
+ return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
|
||||||
|
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
|
||||||
|
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
|
||||||
|
@@ -101,6 +101,12 @@
|
||||||
|
|
||||||
|
msm_dp_panel->vsc_sdp_supported = drm_dp_vsc_sdp_supported(panel->aux, dpcd);
|
||||||
|
link_info = &msm_dp_panel->link_info;
|
||||||
|
+ /*
|
||||||
|
+ * Everything below is derived from the current sink. Start from
|
||||||
|
+ * scratch so a rate from a previous sink (the fallback path only runs
|
||||||
|
+ * when rate is 0) does not stick across unplug/replug.
|
||||||
|
+ */
|
||||||
|
+ memset(link_info, 0, sizeof(*link_info));
|
||||||
|
link_info->revision = dpcd[DP_DPCD_REV];
|
||||||
|
major = (link_info->revision >> 4) & 0x0f;
|
||||||
|
minor = link_info->revision & 0x0f;
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
--- a/drivers/gpu/drm/msm/dp/dp_display.c
|
||||||
|
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
|
||||||
|
@@ -1434,6 +1434,15 @@
|
||||||
|
|
||||||
|
msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
|
||||||
|
|
||||||
|
+ /*
|
||||||
|
+ * If link training failed in atomic_enable, the mainlink was already
|
||||||
|
+ * deinitialized (link clocks and PHY off) and the stream never came
|
||||||
|
+ * up. Writing DP_STATE_CTRL then hits an unclocked block, which hangs
|
||||||
|
+ * the bus and resets the SoC on unplug or the next modeset.
|
||||||
|
+ */
|
||||||
|
+ if (!dp->power_on)
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
msm_dp_ctrl_push_idle(msm_dp_display->ctrl);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
--- a/drivers/gpu/drm/msm/dsi/dsi_host.c
|
||||||
|
+++ b/drivers/gpu/drm/msm/dsi/dsi_host.c
|
||||||
|
@@ -1870,6 +1870,15 @@
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
+ /*
|
||||||
|
+ * Some panels (e.g. Novatek DDICs with fixed rate control tables in
|
||||||
|
+ * the decoder) need the exact RC configuration the vendor encoder
|
||||||
|
+ * used, not the one derived here from the generic DSC tables. Keep a
|
||||||
|
+ * configuration the panel driver already filled in completely.
|
||||||
|
+ */
|
||||||
|
+ if (dsc->rc_model_size && dsc->slice_chunk_size && dsc->initial_dec_delay)
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
if (dsc->bits_per_pixel & 0xf) {
|
||||||
|
DRM_DEV_ERROR(&msm_host->pdev->dev, "DSI does not support fractional bits_per_pixel\n");
|
||||||
|
return -EINVAL;
|
||||||
@@ -0,0 +1,594 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2026 snlavaise <snlavaise@gmail.com>
|
||||||
|
*
|
||||||
|
* DRM panel driver for the CSOT NT37705 (Novatek NT37705) command mode DSC
|
||||||
|
* panel used on the Motorola rtwo (Moto Edge 40 Pro, SM8550).
|
||||||
|
*
|
||||||
|
* Sequences transcribed byte-by-byte from the vendor device tree node
|
||||||
|
* "qcom,mdss_dsi_mot_csot_nt37705_667_curve_1080x2400_dsc_cmd_v1"
|
||||||
|
* (panel name mipi_mot_cmd_csot_1080p_dsc_667, panel supplier "csot"),
|
||||||
|
* see pmos/panel-nt37705-v1-dump.md. Only the 60 Hz timing (timing@0, 2f01 +
|
||||||
|
* e000) is exposed for the first bring-up. The other rates only differ in
|
||||||
|
* their timing-switch-command: 120 Hz 2f02/e041, 90 Hz 2f04/e000,
|
||||||
|
* 48 Hz 2f05/e000, 165 Hz 2f03/e041 (same porches and DSI clock).
|
||||||
|
*
|
||||||
|
* DSC: besides the Novatek MCS setup in the on command (0x90 enable, 0x91
|
||||||
|
* parameters), the standard PPS and compression-mode packets are sent after
|
||||||
|
* it, same as the mainline NT37801 driver (same Novatek flow) and the
|
||||||
|
* downstream driver, which sends the PPS by itself.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/backlight.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
|
#include <linux/ktime.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/regulator/consumer.h>
|
||||||
|
|
||||||
|
#include <video/mipi_display.h>
|
||||||
|
|
||||||
|
#include <drm/display/drm_dsc.h>
|
||||||
|
#include <drm/display/drm_dsc_helper.h>
|
||||||
|
#include <drm/drm_mipi_dsi.h>
|
||||||
|
#include <drm/drm_modes.h>
|
||||||
|
#include <drm/drm_panel.h>
|
||||||
|
#include <drm/drm_probe_helper.h>
|
||||||
|
|
||||||
|
struct nt37705_csot {
|
||||||
|
struct drm_panel panel;
|
||||||
|
struct mipi_dsi_device *dsi;
|
||||||
|
struct gpio_desc *reset_gpio;
|
||||||
|
struct regulator_bulk_data *supplies;
|
||||||
|
ktime_t off_since;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct regulator_bulk_data nt37705_csot_supplies[] = {
|
||||||
|
{ .supply = "vddio" },
|
||||||
|
{ .supply = "vdd" },
|
||||||
|
{ .supply = "vci" },
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline struct nt37705_csot *to_nt37705_csot(struct drm_panel *panel)
|
||||||
|
{
|
||||||
|
return container_of(panel, struct nt37705_csot, panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Vendor reset-sequence: <0 12 1 1 0 1 1 13> (value -> duration ms).
|
||||||
|
* Those are raw line levels (downstream uses gpio_set_value()), while
|
||||||
|
* reset-gpios is GPIO_ACTIVE_LOW here, so the logical values are inverted:
|
||||||
|
* assert 12 ms, release 1 ms, assert 1 ms, release 13 ms (ends released).
|
||||||
|
*/
|
||||||
|
static void nt37705_csot_reset(struct nt37705_csot *ctx)
|
||||||
|
{
|
||||||
|
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
|
||||||
|
usleep_range(12000, 13000);
|
||||||
|
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
|
||||||
|
usleep_range(1000, 2000);
|
||||||
|
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
|
||||||
|
usleep_range(1000, 2000);
|
||||||
|
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
|
||||||
|
usleep_range(13000, 14000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rate control for DSC 1.1, 10 bpc, 8 bpp as the Motorola/Qualcomm downstream
|
||||||
|
* driver sets it for panels flagged qcom,mdss-dsc-novateck-ic
|
||||||
|
* (sde_dsc_populate_dsc_config_nt(), DSC_V11_10BPC_8BPP entry of the *_nt
|
||||||
|
* tables). The DDIC decoder uses these fixed tables, which differ from the
|
||||||
|
* generic VESA/drm ones in 8 of the 15 ranges, so the host encoder must use
|
||||||
|
* them too; msm keeps a configuration filled in this completely (see
|
||||||
|
* msm-dsi-keep-panel-dsc-config.patch). Derived values reproduce the stock
|
||||||
|
* 0x91 bytes exactly.
|
||||||
|
*/
|
||||||
|
static const u8 nt37705_rc_min_qp[DSC_NUM_BUF_RANGES] = {
|
||||||
|
0, 4, 5, 5, 7, 7, 7, 7, 7, 8, 9, 9, 9, 12, 16
|
||||||
|
};
|
||||||
|
static const u8 nt37705_rc_max_qp[DSC_NUM_BUF_RANGES] = {
|
||||||
|
8, 8, 9, 10, 11, 11, 11, 12, 13, 14, 14, 15, 15, 16, 17
|
||||||
|
};
|
||||||
|
static const s8 nt37705_rc_bpg[DSC_NUM_BUF_RANGES] = {
|
||||||
|
2, 0, 0, -2, -4, -6, -8, -8, -8, -10, -10, -10, -12, -12, -12
|
||||||
|
};
|
||||||
|
static const u8 nt37705_rc_buf_thresh[DSC_NUM_BUF_RANGES - 1] = {
|
||||||
|
0x0e, 0x1c, 0x2a, 0x38, 0x46, 0x54, 0x62,
|
||||||
|
0x69, 0x70, 0x77, 0x79, 0x7b, 0x7d, 0x7e
|
||||||
|
};
|
||||||
|
|
||||||
|
static void nt37705_csot_dsc_config_nt(struct drm_dsc_config *dsc)
|
||||||
|
{
|
||||||
|
int bpp = dsc->bits_per_pixel >> 4;
|
||||||
|
int bpc = dsc->bits_per_component;
|
||||||
|
int groups_per_line, groups_total, slice_bits;
|
||||||
|
int pre_mux_bits, mux_bits, final_scale, i;
|
||||||
|
|
||||||
|
dsc->rc_model_size = DSC_RC_MODEL_SIZE_CONST;
|
||||||
|
dsc->first_line_bpg_offset = 13;
|
||||||
|
dsc->rc_edge_factor = DSC_RC_EDGE_FACTOR_CONST;
|
||||||
|
dsc->rc_tgt_offset_high = DSC_RC_TGT_OFFSET_HI_CONST;
|
||||||
|
dsc->rc_tgt_offset_low = DSC_RC_TGT_OFFSET_LO_CONST;
|
||||||
|
dsc->simple_422 = false;
|
||||||
|
dsc->convert_rgb = true;
|
||||||
|
dsc->vbr_enable = false;
|
||||||
|
|
||||||
|
for (i = 0; i < DSC_NUM_BUF_RANGES - 1; i++)
|
||||||
|
dsc->rc_buf_thresh[i] = nt37705_rc_buf_thresh[i];
|
||||||
|
for (i = 0; i < DSC_NUM_BUF_RANGES; i++) {
|
||||||
|
dsc->rc_range_params[i].range_min_qp = nt37705_rc_min_qp[i];
|
||||||
|
dsc->rc_range_params[i].range_max_qp = nt37705_rc_max_qp[i];
|
||||||
|
dsc->rc_range_params[i].range_bpg_offset =
|
||||||
|
nt37705_rc_bpg[i] & DSC_RANGE_BPG_OFFSET_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsc->rc_quant_incr_limit0 = 15;
|
||||||
|
dsc->rc_quant_incr_limit1 = 15;
|
||||||
|
dsc->initial_offset = 6144;
|
||||||
|
dsc->initial_xmit_delay = 512;
|
||||||
|
dsc->flatness_min_qp = 7;
|
||||||
|
dsc->flatness_max_qp = 16;
|
||||||
|
|
||||||
|
dsc->line_buf_depth = bpc + 1;
|
||||||
|
dsc->mux_word_size = DSC_MUX_WORD_SIZE_8_10_BPC;
|
||||||
|
|
||||||
|
groups_per_line = DIV_ROUND_UP(dsc->slice_width, 3);
|
||||||
|
dsc->slice_chunk_size = DIV_ROUND_UP(dsc->slice_width * bpp, 8);
|
||||||
|
|
||||||
|
dsc->initial_dec_delay = DIV_ROUND_UP(dsc->rc_model_size -
|
||||||
|
dsc->initial_offset +
|
||||||
|
dsc->initial_xmit_delay * bpp +
|
||||||
|
groups_per_line *
|
||||||
|
dsc->first_line_bpg_offset, bpp) -
|
||||||
|
dsc->initial_xmit_delay;
|
||||||
|
dsc->initial_scale_value = 8 * dsc->rc_model_size /
|
||||||
|
(dsc->rc_model_size - dsc->initial_offset);
|
||||||
|
|
||||||
|
slice_bits = 8 * dsc->slice_chunk_size * dsc->slice_height;
|
||||||
|
groups_total = groups_per_line * dsc->slice_height;
|
||||||
|
|
||||||
|
dsc->nfl_bpg_offset = DIV_ROUND_UP(dsc->first_line_bpg_offset * 2048,
|
||||||
|
dsc->slice_height - 1);
|
||||||
|
|
||||||
|
pre_mux_bits = 3 * (dsc->mux_word_size + (4 * bpc + 4) - 2);
|
||||||
|
mux_bits = pre_mux_bits - (dsc->mux_word_size -
|
||||||
|
((slice_bits - pre_mux_bits) % dsc->mux_word_size));
|
||||||
|
|
||||||
|
dsc->slice_bpg_offset = DIV_ROUND_UP(2048 * (dsc->rc_model_size -
|
||||||
|
dsc->initial_offset +
|
||||||
|
mux_bits), groups_total);
|
||||||
|
|
||||||
|
dsc->final_offset = dsc->rc_model_size -
|
||||||
|
dsc->initial_xmit_delay * bpp + mux_bits;
|
||||||
|
final_scale = 8 * dsc->rc_model_size /
|
||||||
|
(dsc->rc_model_size - dsc->final_offset);
|
||||||
|
dsc->scale_increment_interval = (2048 * dsc->final_offset) /
|
||||||
|
((final_scale - 9) * (dsc->nfl_bpg_offset +
|
||||||
|
dsc->slice_bpg_offset));
|
||||||
|
dsc->scale_decrement_interval = groups_per_line /
|
||||||
|
(dsc->initial_scale_value - 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Novatek 0x91: the panel-side DSC decoder parameters. The layout was checked
|
||||||
|
* field by field against the mainline NT37801 driver, whose hardcoded 0x91
|
||||||
|
* matches drm_dsc_compute_rc_parameters() exactly.
|
||||||
|
*
|
||||||
|
* Built from the config the host actually encodes with. With the Novatek
|
||||||
|
* rate control above it yields exactly the stock bytes
|
||||||
|
* (ab 28 00 0c d2 00 02 25 01 14 00 07 09 75 08 7a 10 f0).
|
||||||
|
*/
|
||||||
|
static void nt37705_csot_write_dsc_params(struct mipi_dsi_multi_context *dsi_ctx,
|
||||||
|
const struct drm_dsc_config *dsc)
|
||||||
|
{
|
||||||
|
const u8 buf[] = {
|
||||||
|
0x91,
|
||||||
|
(dsc->bits_per_component << 4) | dsc->line_buf_depth,
|
||||||
|
0x28, /* stock value, same on NT37801 (block pred, 8 bpp) */
|
||||||
|
dsc->slice_height >> 8, dsc->slice_height & 0xff,
|
||||||
|
(dsc->first_line_bpg_offset << 4) | 0x2, 0x00,
|
||||||
|
dsc->initial_dec_delay >> 8, dsc->initial_dec_delay & 0xff,
|
||||||
|
dsc->scale_increment_interval >> 8,
|
||||||
|
dsc->scale_increment_interval & 0xff,
|
||||||
|
dsc->scale_decrement_interval >> 8,
|
||||||
|
dsc->scale_decrement_interval & 0xff,
|
||||||
|
dsc->nfl_bpg_offset >> 8, dsc->nfl_bpg_offset & 0xff,
|
||||||
|
dsc->slice_bpg_offset >> 8, dsc->slice_bpg_offset & 0xff,
|
||||||
|
dsc->final_offset >> 8, dsc->final_offset & 0xff,
|
||||||
|
};
|
||||||
|
|
||||||
|
mipi_dsi_dcs_write_buffer_multi(dsi_ctx, buf, sizeof(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Full 60 Hz on-command from timing@0 of the stock node, ending with
|
||||||
|
* sleep-out (100 ms) and display-on as in the vendor sequence.
|
||||||
|
*/
|
||||||
|
static int nt37705_csot_on(struct nt37705_csot *ctx)
|
||||||
|
{
|
||||||
|
struct mipi_dsi_device *dsi = ctx->dsi;
|
||||||
|
struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
|
||||||
|
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x55, 0xaa, 0x52, 0x08, 0x00);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xba, 0x00, 0x6a, 0x00, 0x10, 0x09, 0xb0, 0x10);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x07);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xba, 0x00, 0x6a, 0x00, 0x10, 0x00, 0x20, 0x10);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x0e);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xba, 0x00, 0x4c, 0x00, 0x10, 0x00, 0x2c, 0x10);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbb, 0x00, 0x6a, 0x00, 0x10, 0x00, 0x20, 0x70);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc1, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0x4c, 0x00, 0x6a, 0x00, 0x6a);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x55, 0xaa, 0x52, 0x08, 0x08);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe0, 0x41, 0x73, 0x73, 0x73, 0x83);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe1, 0x00, 0x5c, 0xc4, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x10, 0x00);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe2, 0x41, 0x8d, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x55, 0xaa, 0x52, 0x08, 0x07);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x37);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb7, 0x08, 0x08, 0x08);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x55, 0xaa, 0x52, 0x08, 0x00);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x01);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc0, 0x20);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x55, 0xaa, 0x52, 0x08, 0x08);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xea, 0x22, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x55, 0xaa, 0x52, 0x08, 0x08);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x21);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0xaa, 0x55, 0xa5, 0x83);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x02);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x03);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x55, 0xaa, 0x52, 0x08, 0x08);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe0, 0x00);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0xaa, 0x55, 0xa5, 0x81);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x1e);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x0f);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x55, 0xaa, 0x52, 0x08, 0x01);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x05);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc5, 0x15, 0x15, 0x15);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0xaa, 0x55, 0xa5, 0x81);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x19);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x00);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0xaa, 0x55, 0xa5, 0x80);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x2d);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfc, 0x44);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0xaa, 0x55, 0xa5, 0x80);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x1a);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf4, 0x55);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0xaa, 0x55, 0xa5, 0x83);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x12);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x41);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0xaa, 0x55, 0xa5, 0x80);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x18);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf4, 0x30);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0xaa, 0x55, 0xa5, 0x80);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x19);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf2, 0x00);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0xaa, 0x55, 0xa5, 0x80);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x31);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf8, 0x01, 0x09);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x15);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf8, 0x01, 0x5e);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x10);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2a, 0x00, 0x00, 0x04, 0x37);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2b, 0x00, 0x00, 0x09, 0x5f);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2f, 0x01);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0x80);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x00);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0x00, 0x00);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x04);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0x00, 0x00);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0x20);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x90, 0x03, 0x03);
|
||||||
|
nt37705_csot_write_dsc_params(&dsi_ctx, dsi->dsc);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x55, 0xaa, 0x52, 0x08, 0x01);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x02);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc7, 0x00, 0x00);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x26, 0x00);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1f, 0x40);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x55, 0xaa, 0x52, 0x08, 0x00);
|
||||||
|
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb2, 0x01, 0x60);
|
||||||
|
mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
|
||||||
|
mipi_dsi_msleep(&dsi_ctx, 100);
|
||||||
|
mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
|
||||||
|
|
||||||
|
return dsi_ctx.accum_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bring-up aid: the stock ESD check reads DCS 0x0a and expects 0x9c (booster
|
||||||
|
* on, sleep out, display on, normal mode). Logs the result either way.
|
||||||
|
*/
|
||||||
|
static void nt37705_csot_log_power_mode(struct nt37705_csot *ctx,
|
||||||
|
const char *when)
|
||||||
|
{
|
||||||
|
struct device *dev = &ctx->dsi->dev;
|
||||||
|
u8 power_mode;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = mipi_dsi_dcs_get_power_mode(ctx->dsi, &power_mode);
|
||||||
|
if (ret < 0)
|
||||||
|
dev_warn(dev, "power mode %s: read failed: %d\n", when, ret);
|
||||||
|
else
|
||||||
|
dev_info(dev, "power mode %s: 0x%02x (stock expects 0x9c)\n",
|
||||||
|
when, power_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nt37705_csot_off(struct nt37705_csot *ctx)
|
||||||
|
{
|
||||||
|
struct mipi_dsi_device *dsi = ctx->dsi;
|
||||||
|
struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
|
||||||
|
|
||||||
|
mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
|
||||||
|
mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
|
||||||
|
mipi_dsi_msleep(&dsi_ctx, 120);
|
||||||
|
|
||||||
|
return dsi_ctx.accum_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Everything is sent from prepare (DSI host already up thanks to
|
||||||
|
* prepare_prev_first), followed by the standard PPS and compression-mode
|
||||||
|
* packets, like the mainline NT37801 driver does with the same Novatek
|
||||||
|
* 0x90/0x91 flow. The downstream Qualcomm driver also sends the PPS on its
|
||||||
|
* own; it is not part of the vendor on-command.
|
||||||
|
*/
|
||||||
|
static int nt37705_csot_prepare(struct drm_panel *panel)
|
||||||
|
{
|
||||||
|
struct nt37705_csot *ctx = to_nt37705_csot(panel);
|
||||||
|
struct device *dev = &ctx->dsi->dev;
|
||||||
|
struct drm_dsc_picture_parameter_set pps;
|
||||||
|
s64 elapsed;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Supplies were cut in probe or unprepare: keep them off >= 100 ms. */
|
||||||
|
elapsed = ktime_ms_delta(ktime_get(), ctx->off_since);
|
||||||
|
if (elapsed < 100)
|
||||||
|
msleep(100 - elapsed);
|
||||||
|
|
||||||
|
ret = regulator_bulk_enable(ARRAY_SIZE(nt37705_csot_supplies), ctx->supplies);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
nt37705_csot_reset(ctx);
|
||||||
|
|
||||||
|
ret = nt37705_csot_on(ctx);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "failed to initialize panel: %d\n", ret);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
drm_dsc_pps_payload_pack(&pps, ctx->dsi->dsc);
|
||||||
|
|
||||||
|
ret = mipi_dsi_picture_parameter_set(ctx->dsi, &pps);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "failed to transmit PPS: %d\n", ret);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = mipi_dsi_compression_mode(ctx->dsi, true);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "failed to enable compression mode: %d\n", ret);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
msleep(28);
|
||||||
|
|
||||||
|
nt37705_csot_log_power_mode(ctx, "after on");
|
||||||
|
msleep(100);
|
||||||
|
nt37705_csot_log_power_mode(ctx, "after on +100ms");
|
||||||
|
msleep(400);
|
||||||
|
nt37705_csot_log_power_mode(ctx, "after on +500ms");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
|
||||||
|
regulator_bulk_disable(ARRAY_SIZE(nt37705_csot_supplies), ctx->supplies);
|
||||||
|
ctx->off_since = ktime_get();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nt37705_csot_unprepare(struct drm_panel *panel)
|
||||||
|
{
|
||||||
|
struct nt37705_csot *ctx = to_nt37705_csot(panel);
|
||||||
|
struct device *dev = &ctx->dsi->dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = nt37705_csot_off(ctx);
|
||||||
|
if (ret < 0)
|
||||||
|
dev_err(dev, "failed to un-initialize panel: %d\n", ret);
|
||||||
|
|
||||||
|
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
|
||||||
|
regulator_bulk_disable(ARRAY_SIZE(nt37705_csot_supplies), ctx->supplies);
|
||||||
|
ctx->off_since = ktime_get();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 60 Hz modeline, geometry from the stock timing@0:
|
||||||
|
* hbp/hfp/hpw = 0x20/0x20/0x10, vbp/vfp/vpw = 0x04/0x0c/0x04 (1080x2400).
|
||||||
|
*/
|
||||||
|
static const struct drm_display_mode nt37705_csot_mode = {
|
||||||
|
.clock = (1080 + 32 + 16 + 32) * (2400 + 4 + 12 + 4) * 60 / 1000,
|
||||||
|
.hdisplay = 1080,
|
||||||
|
.hsync_start = 1080 + 32,
|
||||||
|
.hsync_end = 1080 + 32 + 16,
|
||||||
|
.htotal = 1080 + 32 + 16 + 32,
|
||||||
|
.vdisplay = 2400,
|
||||||
|
.vsync_start = 2400 + 4,
|
||||||
|
.vsync_end = 2400 + 4 + 12,
|
||||||
|
.vtotal = 2400 + 4 + 12 + 4,
|
||||||
|
.width_mm = 69,
|
||||||
|
.height_mm = 154,
|
||||||
|
.type = DRM_MODE_TYPE_DRIVER,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int nt37705_csot_get_modes(struct drm_panel *panel,
|
||||||
|
struct drm_connector *connector)
|
||||||
|
{
|
||||||
|
return drm_connector_helper_get_modes_fixed(connector, &nt37705_csot_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct drm_panel_funcs nt37705_csot_panel_funcs = {
|
||||||
|
.prepare = nt37705_csot_prepare,
|
||||||
|
.unprepare = nt37705_csot_unprepare,
|
||||||
|
.get_modes = nt37705_csot_get_modes,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DBV (0x51) backlight, 12-bit (0..0x3ffc on the stock panel). The vendor
|
||||||
|
* "bl-is-exponent"/"bl-inverted-dbv" curves are not reproduced; a raw value
|
||||||
|
* is written for now so the display has a working brightness control.
|
||||||
|
*/
|
||||||
|
static int nt37705_csot_bl_update_status(struct backlight_device *bl)
|
||||||
|
{
|
||||||
|
struct mipi_dsi_device *dsi = bl_get_data(bl);
|
||||||
|
u16 brightness = backlight_get_brightness(bl);
|
||||||
|
|
||||||
|
return mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nt37705_csot_bl_get_brightness(struct backlight_device *bl)
|
||||||
|
{
|
||||||
|
struct mipi_dsi_device *dsi = bl_get_data(bl);
|
||||||
|
u16 brightness;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return brightness;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct backlight_ops nt37705_csot_bl_ops = {
|
||||||
|
.update_status = nt37705_csot_bl_update_status,
|
||||||
|
.get_brightness = nt37705_csot_bl_get_brightness,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct backlight_device *
|
||||||
|
nt37705_csot_create_backlight(struct mipi_dsi_device *dsi)
|
||||||
|
{
|
||||||
|
struct device *dev = &dsi->dev;
|
||||||
|
const struct backlight_properties props = {
|
||||||
|
.type = BACKLIGHT_RAW,
|
||||||
|
.brightness = 2047,
|
||||||
|
.max_brightness = 16380,
|
||||||
|
};
|
||||||
|
|
||||||
|
return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
|
||||||
|
&nt37705_csot_bl_ops, &props);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nt37705_csot_probe(struct mipi_dsi_device *dsi)
|
||||||
|
{
|
||||||
|
struct device *dev = &dsi->dev;
|
||||||
|
struct nt37705_csot *ctx;
|
||||||
|
struct drm_dsc_config *dsc;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ctx = devm_drm_panel_alloc(dev, struct nt37705_csot, panel,
|
||||||
|
&nt37705_csot_panel_funcs,
|
||||||
|
DRM_MODE_CONNECTOR_DSI);
|
||||||
|
if (IS_ERR(ctx))
|
||||||
|
return PTR_ERR(ctx);
|
||||||
|
|
||||||
|
ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(nt37705_csot_supplies),
|
||||||
|
nt37705_csot_supplies, &ctx->supplies);
|
||||||
|
if (ret < 0)
|
||||||
|
return dev_err_probe(dev, ret, "Failed to get regulators\n");
|
||||||
|
|
||||||
|
ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
|
||||||
|
if (IS_ERR(ctx->reset_gpio))
|
||||||
|
return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
|
||||||
|
"Failed to get reset-gpios\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The ABL leaves the panel powered and running its splash. Resetting
|
||||||
|
* it, or even power-cycling it from prepare (DSI PHY already up, lanes
|
||||||
|
* in LP11), leaves the DDIC dead: no TE, no DCS read response. The same
|
||||||
|
* power cycle done with the DSI link down (fb blank/unblank) brings it
|
||||||
|
* up fine every time, so cut the inherited supplies here, while MDSS
|
||||||
|
* has just been reset and the DSI PHY is still off.
|
||||||
|
*/
|
||||||
|
ret = regulator_bulk_enable(ARRAY_SIZE(nt37705_csot_supplies),
|
||||||
|
ctx->supplies);
|
||||||
|
if (ret < 0)
|
||||||
|
return dev_err_probe(dev, ret, "Failed to enable regulators\n");
|
||||||
|
regulator_bulk_disable(ARRAY_SIZE(nt37705_csot_supplies), ctx->supplies);
|
||||||
|
ctx->off_since = ktime_get();
|
||||||
|
|
||||||
|
/* Stock DSC config: version 1.1, 10 bpc, 8 bpp, novateck-ic flow. */
|
||||||
|
dsc = devm_kzalloc(dev, sizeof(*dsc), GFP_KERNEL);
|
||||||
|
if (!dsc)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
dsc->pic_width = 1080;
|
||||||
|
dsc->pic_height = 2400;
|
||||||
|
dsc->dsc_version_major = 0x1;
|
||||||
|
dsc->dsc_version_minor = 0x1;
|
||||||
|
dsc->slice_height = 12;
|
||||||
|
dsc->slice_width = 540;
|
||||||
|
dsc->slice_count = 2;
|
||||||
|
dsc->bits_per_component = 10;
|
||||||
|
dsc->bits_per_pixel = 8 << 4;
|
||||||
|
dsc->block_pred_enable = true;
|
||||||
|
nt37705_csot_dsc_config_nt(dsc);
|
||||||
|
|
||||||
|
dsi->dsc = dsc;
|
||||||
|
|
||||||
|
ctx->dsi = dsi;
|
||||||
|
mipi_dsi_set_drvdata(dsi, ctx);
|
||||||
|
|
||||||
|
dsi->lanes = 4;
|
||||||
|
dsi->format = MIPI_DSI_FMT_RGB888;
|
||||||
|
dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM;
|
||||||
|
|
||||||
|
ctx->panel.prepare_prev_first = true;
|
||||||
|
|
||||||
|
ctx->panel.backlight = nt37705_csot_create_backlight(dsi);
|
||||||
|
if (IS_ERR(ctx->panel.backlight))
|
||||||
|
return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
|
||||||
|
"Failed to create backlight\n");
|
||||||
|
|
||||||
|
drm_panel_add(&ctx->panel);
|
||||||
|
|
||||||
|
ret = devm_mipi_dsi_attach(dev, dsi);
|
||||||
|
if (ret < 0) {
|
||||||
|
drm_panel_remove(&ctx->panel);
|
||||||
|
return dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nt37705_csot_remove(struct mipi_dsi_device *dsi)
|
||||||
|
{
|
||||||
|
struct nt37705_csot *ctx = mipi_dsi_get_drvdata(dsi);
|
||||||
|
|
||||||
|
drm_panel_remove(&ctx->panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id nt37705_csot_of_match[] = {
|
||||||
|
{ .compatible = "novatek,nt37705" },
|
||||||
|
{ /* sentinel */ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, nt37705_csot_of_match);
|
||||||
|
|
||||||
|
static struct mipi_dsi_driver nt37705_csot_driver = {
|
||||||
|
.probe = nt37705_csot_probe,
|
||||||
|
.remove = nt37705_csot_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "panel-novatek-nt37705",
|
||||||
|
.of_match_table = nt37705_csot_of_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module_mipi_dsi_driver(nt37705_csot_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("snlavaise <snlavaise@gmail.com>");
|
||||||
|
MODULE_DESCRIPTION("DRM driver for the CSOT NT37705 (Novatek) cmd mode DSC panel on Motorola rtwo");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
@@ -1,5 +1,62 @@
|
|||||||
# Motorola rtwo bring-up fragment, appended to config-motorola-rtwo.aarch64.
|
# Motorola rtwo bring-up fragment, appended to config-motorola-rtwo.aarch64.
|
||||||
|
|
||||||
|
# --- hardening OFF (bring-up diagnostic) --------------------------------
|
||||||
|
# config-motorola-rtwo.aarch64 (heredado de linux-postmarketos-mainline,
|
||||||
|
# nunca revisado a proposito para este equipo) trae CFI/BTI/PAC/MTE/SCS
|
||||||
|
# encendidos incluso para el propio codigo del kernel. Todas estas
|
||||||
|
# protecciones exigen que CUALQUIER cosa a la que el kernel salte o
|
||||||
|
# retorne indirectamente (trampolines de PSCI/SMCCC, callbacks del
|
||||||
|
# hipervisor Gunyah - el rtwo corre como VM primaria) respete las mismas
|
||||||
|
# convenciones (firmas CFI, landing pads BTI, x18 reservado para SCS,
|
||||||
|
# punteros autenticados). Si algun trampolin de firmware/hipervisor no
|
||||||
|
# fue compilado con las mismas convenciones, la CPU dispara una excepcion
|
||||||
|
# de hardware inmediata - encaja con un reset rapido y consistente que no
|
||||||
|
# cambio en absoluto al sacar reguladores/USB/UART/ramoops no-map. Apagado
|
||||||
|
# para aislar esto como variable; si el equipo arranca con esto apagado,
|
||||||
|
# reactivar de a una por vez para encontrar cual puntual es la culpable.
|
||||||
|
# CONFIG_CFI is not set
|
||||||
|
# CONFIG_ARM64_BTI_KERNEL is not set
|
||||||
|
# CONFIG_ARM64_PTR_AUTH_KERNEL is not set
|
||||||
|
# CONFIG_ARM64_MTE is not set
|
||||||
|
# CONFIG_SHADOW_CALL_STACK is not set
|
||||||
|
|
||||||
|
# --- kernel lockdown OFF (bring-up diagnostic) --------------------------
|
||||||
|
# config-motorola-rtwo.aarch64 trae CONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY=y
|
||||||
|
# a secas, asi que el kernel arranca en lockdown "integrity" SIEMPRE,
|
||||||
|
# independientemente del estado de Secure Boot que reporte U-Boot vía EFI.
|
||||||
|
# Lockdown activa LOCKDOWN_MODULE_SIGNATURE: cualquier .ko cuya firma no
|
||||||
|
# verifique contra el certs/signing_key.pem de ESTE build puntual se
|
||||||
|
# rechaza de plano ("Lockdown: modprobe: unsigned module loading is
|
||||||
|
# restricted; see man kernel_lockdown.7"), aunque ese mismo modulo haya
|
||||||
|
# cargado bien ayer. Esto nos exploto justo al reconstruir el kernel (para
|
||||||
|
# soporte UFS) mientras dejabamos a proposito el ramdisk hand-patcheado
|
||||||
|
# (dropbear + retry loop del UDC) SIN regenerar: los modulos empaquetados
|
||||||
|
# ahi quedaron firmados con la clave del build ANTERIOR, asi que el kernel
|
||||||
|
# nuevo desconfia de todos por igual -- tanto los de UFS recien habilitados
|
||||||
|
# como los del PHY USB que hasta ese rebuild cargaban sin problema. Apagado
|
||||||
|
# mientras dure el bring-up activo (vamos a seguir reconstruyendo el kernel
|
||||||
|
# sin resincronizar el ramdisk cada vez); nada aca necesita lockdown todavia.
|
||||||
|
CONFIG_LOCK_DOWN_KERNEL_FORCE_NONE=y
|
||||||
|
# CONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY is not set
|
||||||
|
|
||||||
|
# --- VA/PA de 52-bit -> 48-bit (bring-up diagnostic) --------------------
|
||||||
|
# config-motorola-rtwo.aarch64 trae VA_BITS/PA_BITS en 52 (LPA2), heredado
|
||||||
|
# del defconfig generico. device-motorola-dubai (unico port mainline de
|
||||||
|
# Motorola que arranca, mismo ABL/MbmPkg, aunque SIN hipervisor Gunyah de
|
||||||
|
# por medio) usa 48/48. El rtwo SI corre como VM primaria de Gunyah
|
||||||
|
# (confirmado: nodo qcom,gh-watchdog real en el DT stock), y el hipervisor
|
||||||
|
# es quien gestiona el espacio de direcciones intermedio (IPA) del huesped
|
||||||
|
# - si Gunyah en este equipo solo soporta 48-bit para la VM primaria,
|
||||||
|
# forzar 52-bit podria romper el establecimiento de la MMU en el arranque
|
||||||
|
# en ensamblador, ANTES de cualquier codigo en C y antes de parsear el
|
||||||
|
# device tree - coherente con un reset durisimo que no cambio en absoluto
|
||||||
|
# con nada de lo tocado en el .dts hasta ahora.
|
||||||
|
CONFIG_ARM64_VA_BITS_48=y
|
||||||
|
# CONFIG_ARM64_VA_BITS_52 is not set
|
||||||
|
CONFIG_ARM64_PA_BITS_48=y
|
||||||
|
# CONFIG_ARM64_PA_BITS_52 is not set
|
||||||
|
# CONFIG_ARM64_LPA2 is not set
|
||||||
|
|
||||||
# --- pstore / ramoops -------------------------------------------------------
|
# --- pstore / ramoops -------------------------------------------------------
|
||||||
# Capture the kernel console into the reserved ramoops region (0xae000000)
|
# Capture the kernel console into the reserved ramoops region (0xae000000)
|
||||||
# so a failed boot can be read back from a later pmOS boot
|
# so a failed boot can be read back from a later pmOS boot
|
||||||
@@ -14,16 +71,48 @@ CONFIG_PSTORE_COMPRESS=y
|
|||||||
# sm8550.dtsi has the APSS watchdog node; build the driver in so the kernel
|
# sm8550.dtsi has the APSS watchdog node; build the driver in so the kernel
|
||||||
# pets it early (the bootloader may hand off with it armed).
|
# pets it early (the bootloader may hand off with it armed).
|
||||||
CONFIG_QCOM_WDT=y
|
CONFIG_QCOM_WDT=y
|
||||||
|
# CONFIG_QCOM_WDT above is the wrong watchdog for what actually matters here:
|
||||||
|
# it's DT-matched to the APSS timer at 0x17c10000, but rtwo boots as a Gunyah
|
||||||
|
# primary VM (qcom,gh-watchdog node confirmed in the stock DT) and the real
|
||||||
|
# watchdog guarding HLOS handoff is the HYPERVISOR's, reached only via SMCCC
|
||||||
|
# (ARM_SMCCC_OWNER_VENDOR_HYP), never via a DT node - it is registered by
|
||||||
|
# qcom_scm_gunyah_wdt_init() (drivers/firmware/qcom/qcom_scm.c), which
|
||||||
|
# unconditionally runs once CONFIG_QCOM_SCM=y probes AND arm_smccc_hypervisor_
|
||||||
|
# has_uuid() confirms Gunyah - both true for us. That function only skips
|
||||||
|
# registration if it finds an of_device_is_available() node compatible with
|
||||||
|
# "qcom,kpss-wdt" or "arm,sbsa-gwdt" - our stock DT's gh-watchdog node is
|
||||||
|
# neither, so it registers the "gunyah-wdt" platform_device regardless.
|
||||||
|
# Without CONFIG_GUNYAH_WATCHDOG, nothing ever binds to that platform_device,
|
||||||
|
# so if Gunyah/firmware hands off HLOS with the hyp watchdog already armed
|
||||||
|
# (a plausible safety net for a VM that never comes up), nothing ever calls
|
||||||
|
# gunyah_wdt_stop()/pings it, and it fires -> reset with no kernel-visible
|
||||||
|
# fault. gunyah_wdt_probe() calls gunyah_wdt_stop() unconditionally as its
|
||||||
|
# first action after the SMCCC status check succeeds - it does NOT arm or
|
||||||
|
# start the watchdog itself, so turning this on cannot make anything worse,
|
||||||
|
# it can only disarm a pre-armed timer earlier than before.
|
||||||
|
# Depends only on (ARCH_QCOM || COMPILE_TEST) && HAVE_ARM_SMCCC, both already
|
||||||
|
# satisfied; selects WATCHDOG_CORE (already =y via CONFIG_QCOM_WDT above).
|
||||||
|
CONFIG_GUNYAH_WATCHDOG=y
|
||||||
|
|
||||||
# --- framebuffer console ----------------------------------------------
|
# --- framebuffer console ----------------------------------------------
|
||||||
# Reuse the bootloader framebuffer as an early console (simple-framebuffer
|
# Reactivado 2026-09-17 junto con framebuffer@b8000000 en el .dts (ver ese
|
||||||
# node in the .dts). No panel driver needed.
|
# archivo): con el chainload de U-Boot confirmado en hardware, este
|
||||||
CONFIG_FB=y
|
# framebuffer ya esta encendido y con el formato correcto antes de que
|
||||||
CONFIG_FB_SIMPLE=y
|
# arranque nuestro kernel, asi que simpledrm solo tiene que mapearlo.
|
||||||
|
#
|
||||||
|
# CONFIG_FB / CONFIG_SYSFB_SIMPLEFB no hacen falta: DRM_SIMPLEDRM solo
|
||||||
|
# depende de DRM && MMU (ambos ya =y en la config base) y bindea el nodo
|
||||||
|
# "simple-framebuffer" del DT directamente, sin pasar por el fbdev legacy
|
||||||
|
# ni por el sysfb de x86 (SYSFB_SIMPLEFB es para EFI/VESA en x86, no
|
||||||
|
# aplica aca). CONFIG_FRAMEBUFFER_CONSOLE ya viene =y de la config base
|
||||||
|
# (usa el struct fb_info que expone DRM_FBDEV_EMULATION, tambien ya =y).
|
||||||
|
#
|
||||||
|
# CONFIG_FB_SIMPLE se deja APAGADO a proposito (la config base ya lo trae
|
||||||
|
# asi): compite con DRM_SIMPLEDRM por el mismo nodo "simple-framebuffer"
|
||||||
|
# del DT y gana el que bindee primero -- confundio un intento anterior de
|
||||||
|
# este mismo bring-up (pkgrel 22). Con los dos no hace falta, uno alcanza.
|
||||||
CONFIG_DRM_SIMPLEDRM=y
|
CONFIG_DRM_SIMPLEDRM=y
|
||||||
CONFIG_SYSFB_SIMPLEFB=y
|
# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
|
||||||
CONFIG_FRAMEBUFFER_CONSOLE=y
|
|
||||||
CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
|
|
||||||
|
|
||||||
# --- misc bring-up aids ---------------------------------------------
|
# --- misc bring-up aids ---------------------------------------------
|
||||||
CONFIG_MAGIC_SYSRQ=y
|
CONFIG_MAGIC_SYSRQ=y
|
||||||
@@ -81,3 +170,155 @@ CONFIG_DEBUG_INFO_NONE=y
|
|||||||
# CONFIG_ARCH_VISCONTI is not set
|
# CONFIG_ARCH_VISCONTI is not set
|
||||||
# CONFIG_ARCH_XGENE is not set
|
# CONFIG_ARCH_XGENE is not set
|
||||||
# CONFIG_ARCH_ZYNQMP is not set
|
# CONFIG_ARCH_ZYNQMP is not set
|
||||||
|
|
||||||
|
# --- module compression: OFF ------------------------------------------
|
||||||
|
# The base config (from linux-postmarketos-mainline) turns on
|
||||||
|
# CONFIG_MODULE_COMPRESS + CONFIG_MODULE_COMPRESS_ZSTD +
|
||||||
|
# CONFIG_MODULE_COMPRESS_ALL, so 'make modules_install' writes *.ko.zst.
|
||||||
|
# The pmOS initramfs loads modules with busybox's modprobe applet
|
||||||
|
# ('/bin/busybox --install -s' in /init, then 'modprobe -a' from
|
||||||
|
# load_modules): that applet only knows the plain '.ko' suffix and only
|
||||||
|
# decompresses gzip/bzip2/xz/lzma by magic - this busybox has NO zstd
|
||||||
|
# ("no gzip/bzip2/xz magic" is the error it prints). CONFIG_MODULE_DECOMPRESS
|
||||||
|
# does not help either: busybox reads the file itself and calls init_module(),
|
||||||
|
# never finit_module(MODULE_INIT_COMPRESSED_FILE).
|
||||||
|
#
|
||||||
|
# So with zstd every module in initramfs.load silently fails to load:
|
||||||
|
# ufs-qcom/qcom_ice/phy-qcom-qmp-ufs -> no root device -> "VFS: Unable to
|
||||||
|
# mount root fs on unknown-block(0,0)", and phy-snps-eusb2 -> no UDC ->
|
||||||
|
# no usb0, no telnet on 172.16.42.1.
|
||||||
|
#
|
||||||
|
# 7.2 has no MODULE_COMPRESS_NONE symbol (it was replaced by the
|
||||||
|
# MODULE_COMPRESS bool + a type choice), so turn the bool off; olddefconfig
|
||||||
|
# then drops COMPRESS_ZSTD / COMPRESS_ALL / DECOMPRESS with it.
|
||||||
|
# CONFIG_MODULE_COMPRESS is not set
|
||||||
|
|
||||||
|
# --- RETRACTED 2026-09-20: CONFIG_QCOMTEE / CONFIG_ARM_QCOM_CPUFREQ_HW --
|
||||||
|
# were NOT disabled here in the end. Both were disabled for a while
|
||||||
|
# (2026-09-18) after the screen appeared to freeze right after "qcomtee:
|
||||||
|
# QTEE version 5.2.0", then right after "cpu cpu0: EM: created perf
|
||||||
|
# domain" -- diagnosed purely from photos of the framebuffer console, no
|
||||||
|
# serial/SSH/telnet available at the time to confirm the system was
|
||||||
|
# actually stuck.
|
||||||
|
#
|
||||||
|
# That diagnosis didn't survive scrutiny: in every one of those "hung"
|
||||||
|
# attempts the device stayed reachable over ping the whole time. A kernel
|
||||||
|
# truly wedged in a probe function (e.g. an SMC call that never returns)
|
||||||
|
# would eventually stop servicing network softirqs too, at least once
|
||||||
|
# nothing else is left running; ping staying up is much better explained
|
||||||
|
# by the boot actually completing (or getting close) while only the
|
||||||
|
# on-screen console stopped updating -- unreachable over SSH because
|
||||||
|
# sshd/networking-online wasn't there yet at that point in the bring-up,
|
||||||
|
# and unreachable over telnet because no telnet/debug shell was installed
|
||||||
|
# in userspace yet (added later, see device-motorola-rtwo's
|
||||||
|
# debug-telnetd.service / dropbear work).
|
||||||
|
#
|
||||||
|
# Separately and since confirmed on 2026-09-20 (see sm8550-motorola-rtwo.dts,
|
||||||
|
# the &dispcc override): the inherited U-Boot framebuffer genuinely does
|
||||||
|
# freeze on this board, root-caused to dispcc (Display Clock Controller)
|
||||||
|
# touching disp_cc_mdss_mdp_clk_src -- the MDP pixel clock still actively
|
||||||
|
# driving that inherited buffer -- during its own probe. CONFIG_QCOMTEE=m
|
||||||
|
# is a *module*, only autoloaded once udev coldplugs devices in the real
|
||||||
|
# systemd rootfs; CONFIG_ARM_QCOM_CPUFREQ_HW=y is built-in but its probe
|
||||||
|
# depends on OPP/interconnect data that isn't ready until roughly the same
|
||||||
|
# late-boot window (matches the global deferred-probe-timeout retry seen
|
||||||
|
# at ~11s in current dmesg). Both cluster in the same few seconds of boot
|
||||||
|
# as dispcc's own module autoload -- entirely consistent with "whatever
|
||||||
|
# happened to be the last line printed before the screen froze" rather
|
||||||
|
# than either of them being the actual cause of a hang.
|
||||||
|
#
|
||||||
|
# Re-enabled to their config-motorola-rtwo.aarch64 defaults
|
||||||
|
# (CONFIG_QCOMTEE=m, CONFIG_ARM_QCOM_CPUFREQ_HW=y). Re-test now that a
|
||||||
|
# debug telnet/SSH shell exists in userspace, so aliveness can be checked
|
||||||
|
# directly instead of inferred from a photo of a console that we now know
|
||||||
|
# can freeze independently of whether the system is actually up.
|
||||||
|
|
||||||
|
# --- ath12k: debug messages ON (WiFi bring-up diagnostic) ---------------
|
||||||
|
# The whole "WMI credit exhaustion" diagnosis so far was done BLIND: the
|
||||||
|
# only hard evidence was ath12k_wmi_cmd_send() printing "wmi command 16387
|
||||||
|
# timeout" after WMI_SEND_TIMEOUT_HZ, plus -11/-EAGAIN, which in this
|
||||||
|
# driver can ONLY come from ath12k_htc_send()'s "insufficient credits"
|
||||||
|
# path (wmi.c:412 wait_event_timeout -> cmd_send_nowait -> htc_send; no
|
||||||
|
# other error path in that chain returns -EAGAIN). Everything else --
|
||||||
|
# how many credits the endpoint started with, whether a single credit
|
||||||
|
# report ever came back -- was inferred, because every one of those facts
|
||||||
|
# is printed by an ath12k_dbg() call and config-motorola-rtwo.aarch64
|
||||||
|
# (inherited, never reviewed for this device) has
|
||||||
|
# "# CONFIG_ATH12K_DEBUG is not set", which compiles __ath12k_dbg() down
|
||||||
|
# to an empty inline (debug.h). We were reading a silent driver.
|
||||||
|
#
|
||||||
|
# With this on, the messages that answer the open question directly:
|
||||||
|
# htc.c:543 "Target ready! transmit resources: %d size:%d"
|
||||||
|
# -> htc->total_transmit_credits / target_credit_size, the
|
||||||
|
# initial credit budget the firmware advertises.
|
||||||
|
# htc.c:104 "htc ep %d consumed %d credits (total %d)" (every TX)
|
||||||
|
# htc.c:166 "htc ep %d got %d credits (total %d)" (every credit
|
||||||
|
# report parsed out of an RX trailer)
|
||||||
|
# htc.c:96 "htc insufficient credits ep %d required %d available %d"
|
||||||
|
# all under ATH12K_DBG_HTC (0x4). Falsifiable prediction to check against
|
||||||
|
# them: if the firmware never returns a single credit, the number of WMI
|
||||||
|
# commands that succeed before the wall (INIT, SET_HW_MODE, ..., then the
|
||||||
|
# one PMF_QOS in ath12k_mac_start) should come out EXACTLY equal to the
|
||||||
|
# advertised initial budget -- and not one "got %d credits" line should
|
||||||
|
# ever appear. If instead credits do come back and then stop, the failure
|
||||||
|
# is somewhere else entirely and the "firmware never reports" theory dies.
|
||||||
|
#
|
||||||
|
# Enabled separately at runtime via the debug_mask module param (0644, so
|
||||||
|
# also writable through /sys/module/ath12k/parameters/debug_mask, and
|
||||||
|
# settable from the kernel cmdline as ath12k.debug_mask=0x66 even though
|
||||||
|
# ath12k is a module). Bits (debug.h): WMI 0x2 | HTC 0x4 | BOOT 0x20 |
|
||||||
|
# QMI 0x40. Cost when the mask is 0 is one branch per call site, and this
|
||||||
|
# is a bring-up kernel -- performance is not the constraint here.
|
||||||
|
CONFIG_ATH12K_DEBUG=y
|
||||||
|
|
||||||
|
# Firmware coredump on assert. WANT_DEV_COREDUMP/DEV_COREDUMP are already
|
||||||
|
# =y in the base config, so this only adds ath12k's own collector. It
|
||||||
|
# costs nothing while nothing crashes, and it settles a question the
|
||||||
|
# current evidence cannot: whether the firmware silently asserted at some
|
||||||
|
# point during bring-up (a dead firmware explains "credits never come
|
||||||
|
# back" completely). A dump would show up under /sys/class/devcoredump/.
|
||||||
|
CONFIG_ATH12K_COREDUMP=y
|
||||||
|
|
||||||
|
# Native display bring-up (CSOT NT37705 panel, see panel-novatek-nt37705.c).
|
||||||
|
# DRM_MSM is present as =m in the mainline base config; build it in so the
|
||||||
|
# mdss/dsi0 probe runs from the boot (the frame is committed by MSM itself,
|
||||||
|
# no simpledrm handover needed). DRM_DISPLAY_DSC_HELPER is already =y.
|
||||||
|
CONFIG_BACKLIGHT_CLASS_DEVICE=y
|
||||||
|
CONFIG_DRM_MSM=y
|
||||||
|
CONFIG_DRM_MSM_DSI=y
|
||||||
|
CONFIG_DRM_MIPI_DSI=y
|
||||||
|
CONFIG_DRM_PANEL=y
|
||||||
|
CONFIG_DRM_PANEL_NOVATEK_NT37705=y
|
||||||
|
|
||||||
|
# dispcc/gpucc built in (2026-09-25): as =m they only load once the rootfs is
|
||||||
|
# mounted (~17 s), after the 10 s deferred_probe_timeout. Their GDSCs are the
|
||||||
|
# power-domains of mdss and of the GPU SMMU, and genpd returns -ETIMEDOUT once
|
||||||
|
# the timeout expires: seen in r43 as "arm-smmu 3da0000.iommu: probe ... failed
|
||||||
|
# with error -110". Built-in DRM_MSM would hit the same wall on MDSS_GDSC.
|
||||||
|
CONFIG_SM_DISPCC_8550=y
|
||||||
|
CONFIG_SM_GPUCC_8550=y
|
||||||
|
|
||||||
|
# DRM_MSM "depends on QCOM_LLCC || QCOM_LLCC=n" (same for QCOM_OCMEM): with
|
||||||
|
# both =m in the base config, DRM_MSM=y above was silently capped to =m by
|
||||||
|
# olddefconfig (r44: msm.ko in the rootfs, nothing drove the panel from the
|
||||||
|
# initramfs). Build them in so DRM_MSM can really be =y.
|
||||||
|
CONFIG_QCOM_LLCC=y
|
||||||
|
CONFIG_QCOM_OCMEM=y
|
||||||
|
|
||||||
|
# DisplayPort por USB-C (2026-09-25). Cadena: mdss_dp0 -> QMP USB3/DP combo
|
||||||
|
# PHY -> redriver Parade PS5169 (typec-mux-ps5169.c, integrado en este
|
||||||
|
# paquete) -> conector de pmic-glink (altmode + HPD), con el FSA4480 moviendo
|
||||||
|
# AUX por los SBU. Todo =y a proposito: el DP es un componente de msm, y msm
|
||||||
|
# (panel interno incluido) no se enlaza hasta que el DP encuentra su PHY y
|
||||||
|
# su cadena de bridges; pmic_glink_altmode solo registra su bridge HPD
|
||||||
|
# despues de conseguir el FSA4480 y el redriver. Como =m, eso esperaria a
|
||||||
|
# que monte el rootfs (y en la debug shell no llegaria nunca).
|
||||||
|
CONFIG_TYPEC=y
|
||||||
|
CONFIG_PHY_QCOM_QMP=y
|
||||||
|
CONFIG_PHY_QCOM_QMP_COMBO=y
|
||||||
|
CONFIG_DRM_AUX_BRIDGE=y
|
||||||
|
CONFIG_DRM_AUX_HPD_BRIDGE=y
|
||||||
|
CONFIG_QCOM_PMIC_GLINK=y
|
||||||
|
CONFIG_I2C_QCOM_GENI=y
|
||||||
|
CONFIG_TYPEC_MUX_FSA4480=y
|
||||||
|
CONFIG_TYPEC_MUX_PS5169=y
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,417 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* Parade PS5169 USB 3.1 Gen2 / DP 1.4 Type-C linear redriver
|
||||||
|
*
|
||||||
|
* Mainline typec switch + retimer front-end for the redriver sitting
|
||||||
|
* between the SM8550 QMP USB3/DP combo PHY and the USB-C connector of the
|
||||||
|
* Motorola rtwo (Moto Edge 40 Pro). Modelled on nb7vpq904m.c; the register
|
||||||
|
* programming (CONFIG_REG bits, 0xa0/0xa1 lane setup, chip id) comes from
|
||||||
|
* Motorola's downstream drivers/usb/redriver/ps5169.c, and the init table
|
||||||
|
* is the 'config-seq' of the stock rtwo device tree.
|
||||||
|
*/
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/regulator/consumer.h>
|
||||||
|
#include <linux/usb/typec_dp.h>
|
||||||
|
#include <linux/usb/typec_mux.h>
|
||||||
|
#include <linux/usb/typec_retimer.h>
|
||||||
|
#include <drm/bridge/aux-bridge.h>
|
||||||
|
|
||||||
|
#define PS5169_CONFIG_REG 0x40
|
||||||
|
#define PS5169_PD_ACTIVE BIT(7)
|
||||||
|
#define PS5169_USB3_EN BIT(6)
|
||||||
|
#define PS5169_DP_EN BIT(5)
|
||||||
|
#define PS5169_FLIPPED BIT(4)
|
||||||
|
#define PS5169_CONFIG_MASK (PS5169_PD_ACTIVE | PS5169_USB3_EN | \
|
||||||
|
PS5169_DP_EN | PS5169_FLIPPED)
|
||||||
|
|
||||||
|
/* Lane setup written by the downstream driver next to every mode change. */
|
||||||
|
#define PS5169_LANE_CFG0_REG 0xa0
|
||||||
|
#define PS5169_LANE_CFG1_REG 0xa1
|
||||||
|
|
||||||
|
#define PS5169_CHIPID_LO_REG 0xac
|
||||||
|
#define PS5169_CHIPID_HI_REG 0xad
|
||||||
|
#define PS5169_CHIP_ID 0x6987
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stock rtwo 'config-seq' (reg, value, delay in us): soft reset through
|
||||||
|
* 0x9d, then EQ/gain tuning for this board's traces.
|
||||||
|
*/
|
||||||
|
static const struct reg_sequence ps5169_init_seq[] = {
|
||||||
|
{ 0x9d, 0x80, 10000 },
|
||||||
|
{ 0x9d, 0x00 },
|
||||||
|
{ 0x40, 0x80 },
|
||||||
|
{ 0x8d, 0x01 },
|
||||||
|
{ 0x90, 0x01 },
|
||||||
|
{ 0x51, 0x87 },
|
||||||
|
{ 0x50, 0x20 },
|
||||||
|
{ 0x54, 0x11 },
|
||||||
|
{ 0x5d, 0x66 },
|
||||||
|
{ 0x52, 0x50 },
|
||||||
|
{ 0x55, 0x00 },
|
||||||
|
{ 0x56, 0x00 },
|
||||||
|
{ 0x57, 0x00 },
|
||||||
|
{ 0x58, 0x00 },
|
||||||
|
{ 0x59, 0x00 },
|
||||||
|
{ 0x5a, 0x00 },
|
||||||
|
{ 0x5b, 0x00 },
|
||||||
|
{ 0x5e, 0x07 },
|
||||||
|
{ 0x5f, 0x00 },
|
||||||
|
{ 0x60, 0x00 },
|
||||||
|
{ 0x61, 0x03 },
|
||||||
|
{ 0x65, 0x40 },
|
||||||
|
{ 0x66, 0x00 },
|
||||||
|
{ 0x67, 0x03 },
|
||||||
|
{ 0x75, 0x0c },
|
||||||
|
{ 0x77, 0x00 },
|
||||||
|
{ 0x78, 0x7c },
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ps5169 {
|
||||||
|
struct i2c_client *client;
|
||||||
|
struct regmap *regmap;
|
||||||
|
struct gpio_desc *enable_gpio;
|
||||||
|
struct regulator *vcc_supply;
|
||||||
|
|
||||||
|
struct typec_switch_dev *sw;
|
||||||
|
struct typec_retimer *retimer;
|
||||||
|
struct typec_switch *typec_switch;
|
||||||
|
struct typec_mux *typec_mux;
|
||||||
|
|
||||||
|
struct mutex lock; /* protects the fields below */
|
||||||
|
bool powered;
|
||||||
|
unsigned int config; /* CONFIG_REG value last programmed, 0 = none */
|
||||||
|
enum typec_orientation orientation;
|
||||||
|
unsigned long mode;
|
||||||
|
unsigned int svid;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void ps5169_power(struct ps5169 *ps, bool on)
|
||||||
|
{
|
||||||
|
if (ps->powered == on)
|
||||||
|
return;
|
||||||
|
|
||||||
|
gpiod_set_value_cansleep(ps->enable_gpio, on);
|
||||||
|
if (on)
|
||||||
|
usleep_range(10000, 11000);
|
||||||
|
ps->powered = on;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ps5169_set(struct ps5169 *ps)
|
||||||
|
{
|
||||||
|
bool reverse = ps->orientation == TYPEC_ORIENTATION_REVERSE;
|
||||||
|
unsigned int val;
|
||||||
|
u8 cfg0, cfg1;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Only a disconnect (no orientation) powers the redriver down. */
|
||||||
|
if (ps->orientation == TYPEC_ORIENTATION_NONE) {
|
||||||
|
ps5169_power(ps, false);
|
||||||
|
ps->config = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ps->mode) {
|
||||||
|
/*
|
||||||
|
* With a plain USB partner (OTG cable, pendrive) the rtwo ADSP did
|
||||||
|
* not bring the redriver up: no TYPEC_STATE_USB arrived, it stayed
|
||||||
|
* in TYPEC_STATE_SAFE (NO_CONN) with a valid orientation.
|
||||||
|
* Downstream routes USB3 on every connect (notify_connect: op mode
|
||||||
|
* NONE -> USB3), so do the same instead of leaving the chip off,
|
||||||
|
* which kept the SuperSpeed lanes dead.
|
||||||
|
*/
|
||||||
|
case TYPEC_STATE_SAFE:
|
||||||
|
case TYPEC_STATE_USB:
|
||||||
|
val = PS5169_USB3_EN;
|
||||||
|
cfg0 = 0;
|
||||||
|
cfg1 = 0;
|
||||||
|
break;
|
||||||
|
case TYPEC_DP_STATE_C:
|
||||||
|
case TYPEC_DP_STATE_E:
|
||||||
|
if (ps->svid != USB_TYPEC_DP_SID)
|
||||||
|
return -EINVAL;
|
||||||
|
val = PS5169_DP_EN;
|
||||||
|
cfg0 = 0x00;
|
||||||
|
cfg1 = 0x04;
|
||||||
|
break;
|
||||||
|
case TYPEC_DP_STATE_D:
|
||||||
|
case TYPEC_DP_STATE_F:
|
||||||
|
if (ps->svid != USB_TYPEC_DP_SID)
|
||||||
|
return -EINVAL;
|
||||||
|
val = PS5169_USB3_EN | PS5169_DP_EN;
|
||||||
|
cfg0 = 0x00;
|
||||||
|
cfg1 = 0x04;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reverse)
|
||||||
|
val |= PS5169_FLIPPED;
|
||||||
|
val |= PS5169_PD_ACTIVE;
|
||||||
|
|
||||||
|
if (ps->powered && ps->config == val)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The downstream driver re-runs the full init on every mode change,
|
||||||
|
* and power cycles the chip first when it is already running
|
||||||
|
* (ps5169_host_work: enable low for 2 ms, then high again).
|
||||||
|
*/
|
||||||
|
if (ps->powered) {
|
||||||
|
ps5169_power(ps, false);
|
||||||
|
usleep_range(2000, 2500);
|
||||||
|
}
|
||||||
|
ps->config = 0;
|
||||||
|
ps5169_power(ps, true);
|
||||||
|
ret = regmap_multi_reg_write(ps->regmap, ps5169_init_seq,
|
||||||
|
ARRAY_SIZE(ps5169_init_seq));
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
ret = regmap_update_bits(ps->regmap, PS5169_CONFIG_REG,
|
||||||
|
PS5169_CONFIG_MASK, val);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (val & PS5169_DP_EN) {
|
||||||
|
ret = regmap_write(ps->regmap, PS5169_LANE_CFG0_REG, cfg0);
|
||||||
|
if (!ret)
|
||||||
|
ret = regmap_write(ps->regmap, PS5169_LANE_CFG1_REG, cfg1);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ps->config = val;
|
||||||
|
dev_dbg(&ps->client->dev, "mode %lu orientation %d -> config 0x%02x\n",
|
||||||
|
ps->mode, ps->orientation, val);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
dev_err(&ps->client->dev, "failed to program mode %lu: %d\n",
|
||||||
|
ps->mode, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ps5169_sw_set(struct typec_switch_dev *sw,
|
||||||
|
enum typec_orientation orientation)
|
||||||
|
{
|
||||||
|
struct ps5169 *ps = typec_switch_get_drvdata(sw);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = typec_switch_set(ps->typec_switch, orientation);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
dev_dbg(&ps->client->dev, "switch: orientation %d\n", orientation);
|
||||||
|
|
||||||
|
mutex_lock(&ps->lock);
|
||||||
|
if (ps->orientation != orientation) {
|
||||||
|
ps->orientation = orientation;
|
||||||
|
ret = ps5169_set(ps);
|
||||||
|
}
|
||||||
|
mutex_unlock(&ps->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ps5169_retimer_set(struct typec_retimer *retimer,
|
||||||
|
struct typec_retimer_state *state)
|
||||||
|
{
|
||||||
|
struct ps5169 *ps = typec_retimer_get_drvdata(retimer);
|
||||||
|
struct typec_mux_state mux_state;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
dev_dbg(&ps->client->dev, "retimer: mode %lu svid 0x%04x\n",
|
||||||
|
state->mode, state->alt ? state->alt->svid : 0);
|
||||||
|
|
||||||
|
mutex_lock(&ps->lock);
|
||||||
|
if (ps->mode != state->mode) {
|
||||||
|
ps->mode = state->mode;
|
||||||
|
ps->svid = state->alt ? state->alt->svid : 0;
|
||||||
|
ret = ps5169_set(ps);
|
||||||
|
}
|
||||||
|
mutex_unlock(&ps->lock);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
mux_state.alt = state->alt;
|
||||||
|
mux_state.data = state->data;
|
||||||
|
mux_state.mode = state->mode;
|
||||||
|
|
||||||
|
return typec_mux_set(ps->typec_mux, &mux_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct regmap_config ps5169_regmap = {
|
||||||
|
.max_register = 0xff,
|
||||||
|
.reg_bits = 8,
|
||||||
|
.val_bits = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ps5169_probe(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct device *dev = &client->dev;
|
||||||
|
struct typec_switch_desc sw_desc = { };
|
||||||
|
struct typec_retimer_desc retimer_desc = { };
|
||||||
|
unsigned int lo, hi;
|
||||||
|
struct ps5169 *ps;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ps = devm_kzalloc(dev, sizeof(*ps), GFP_KERNEL);
|
||||||
|
if (!ps)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ps->client = client;
|
||||||
|
ps->mode = TYPEC_STATE_SAFE;
|
||||||
|
ps->orientation = TYPEC_ORIENTATION_NONE;
|
||||||
|
mutex_init(&ps->lock);
|
||||||
|
|
||||||
|
ps->regmap = devm_regmap_init_i2c(client, &ps5169_regmap);
|
||||||
|
if (IS_ERR(ps->regmap))
|
||||||
|
return dev_err_probe(dev, PTR_ERR(ps->regmap),
|
||||||
|
"failed to allocate register map\n");
|
||||||
|
|
||||||
|
ps->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
|
||||||
|
if (IS_ERR(ps->enable_gpio))
|
||||||
|
return dev_err_probe(dev, PTR_ERR(ps->enable_gpio),
|
||||||
|
"unable to acquire enable gpio\n");
|
||||||
|
|
||||||
|
ps->vcc_supply = devm_regulator_get(dev, "vcc");
|
||||||
|
if (IS_ERR(ps->vcc_supply))
|
||||||
|
return dev_err_probe(dev, PTR_ERR(ps->vcc_supply),
|
||||||
|
"unable to acquire vcc supply\n");
|
||||||
|
|
||||||
|
ps->typec_switch = fwnode_typec_switch_get(dev->fwnode);
|
||||||
|
if (IS_ERR(ps->typec_switch))
|
||||||
|
return dev_err_probe(dev, PTR_ERR(ps->typec_switch),
|
||||||
|
"failed to acquire orientation-switch\n");
|
||||||
|
|
||||||
|
ps->typec_mux = fwnode_typec_mux_get(dev->fwnode);
|
||||||
|
if (IS_ERR(ps->typec_mux)) {
|
||||||
|
ret = dev_err_probe(dev, PTR_ERR(ps->typec_mux),
|
||||||
|
"failed to acquire mode-switch\n");
|
||||||
|
goto err_switch_put;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = regulator_enable(ps->vcc_supply);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to enable vcc: %d\n", ret);
|
||||||
|
goto err_mux_put;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Identify the chip, then leave it powered down until a partner
|
||||||
|
* shows up (same as downstream). An I2C failure is only reported:
|
||||||
|
* the DRM bridge chain of the DP controller runs through this device,
|
||||||
|
* so failing the probe here would keep the whole MSM display
|
||||||
|
* (internal panel included) from binding.
|
||||||
|
*/
|
||||||
|
ps5169_power(ps, true);
|
||||||
|
ret = regmap_read(ps->regmap, PS5169_CHIPID_LO_REG, &lo);
|
||||||
|
if (!ret)
|
||||||
|
ret = regmap_read(ps->regmap, PS5169_CHIPID_HI_REG, &hi);
|
||||||
|
if (ret)
|
||||||
|
dev_warn(dev, "failed to read chip id: %d\n", ret);
|
||||||
|
else if (((hi << 8) | lo) != PS5169_CHIP_ID)
|
||||||
|
dev_warn(dev, "unexpected chip id 0x%04x\n", (hi << 8) | lo);
|
||||||
|
else
|
||||||
|
dev_info(dev, "PS5169 redriver, chip id 0x%04x\n", PS5169_CHIP_ID);
|
||||||
|
ps5169_power(ps, false);
|
||||||
|
|
||||||
|
ret = drm_aux_bridge_register(dev);
|
||||||
|
if (ret)
|
||||||
|
goto err_disable_vcc;
|
||||||
|
|
||||||
|
sw_desc.drvdata = ps;
|
||||||
|
sw_desc.fwnode = dev->fwnode;
|
||||||
|
sw_desc.set = ps5169_sw_set;
|
||||||
|
|
||||||
|
ps->sw = typec_switch_register(dev, &sw_desc);
|
||||||
|
if (IS_ERR(ps->sw)) {
|
||||||
|
ret = dev_err_probe(dev, PTR_ERR(ps->sw),
|
||||||
|
"failed to register typec switch\n");
|
||||||
|
goto err_disable_vcc;
|
||||||
|
}
|
||||||
|
|
||||||
|
retimer_desc.drvdata = ps;
|
||||||
|
retimer_desc.fwnode = dev->fwnode;
|
||||||
|
retimer_desc.set = ps5169_retimer_set;
|
||||||
|
|
||||||
|
ps->retimer = typec_retimer_register(dev, &retimer_desc);
|
||||||
|
if (IS_ERR(ps->retimer)) {
|
||||||
|
ret = dev_err_probe(dev, PTR_ERR(ps->retimer),
|
||||||
|
"failed to register typec retimer\n");
|
||||||
|
goto err_switch_unregister;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_set_clientdata(client, ps);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_switch_unregister:
|
||||||
|
typec_switch_unregister(ps->sw);
|
||||||
|
err_disable_vcc:
|
||||||
|
regulator_disable(ps->vcc_supply);
|
||||||
|
err_mux_put:
|
||||||
|
typec_mux_put(ps->typec_mux);
|
||||||
|
err_switch_put:
|
||||||
|
typec_switch_put(ps->typec_switch);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ps5169_remove(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct ps5169 *ps = i2c_get_clientdata(client);
|
||||||
|
|
||||||
|
typec_retimer_unregister(ps->retimer);
|
||||||
|
typec_switch_unregister(ps->sw);
|
||||||
|
|
||||||
|
gpiod_set_value_cansleep(ps->enable_gpio, 0);
|
||||||
|
regulator_disable(ps->vcc_supply);
|
||||||
|
|
||||||
|
typec_mux_put(ps->typec_mux);
|
||||||
|
typec_switch_put(ps->typec_switch);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ps5169_shutdown(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct ps5169 *ps = i2c_get_clientdata(client);
|
||||||
|
|
||||||
|
if (ps)
|
||||||
|
gpiod_set_value_cansleep(ps->enable_gpio, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_device_id ps5169_table[] = {
|
||||||
|
{ .name = "ps5169" },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i2c, ps5169_table);
|
||||||
|
|
||||||
|
static const struct of_device_id ps5169_of_table[] = {
|
||||||
|
{ .compatible = "parade,ps5169" },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, ps5169_of_table);
|
||||||
|
|
||||||
|
static struct i2c_driver ps5169_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "ps5169",
|
||||||
|
.of_match_table = ps5169_of_table,
|
||||||
|
},
|
||||||
|
.probe = ps5169_probe,
|
||||||
|
.remove = ps5169_remove,
|
||||||
|
.shutdown = ps5169_shutdown,
|
||||||
|
.id_table = ps5169_table,
|
||||||
|
};
|
||||||
|
module_i2c_driver(ps5169_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("Parade PS5169 Type-C redriver driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
Reference in New Issue
Block a user