From patchwork Sat Sep 12 07:57:04 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: leow149 X-Patchwork-Id: 3282 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10]) by mxe881.netcup.net (Postfix) with ESMTPS id AD5461C0807 for ; Sat, 12 Sep 2026 10:06:22 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=lucidmail.xyz; spf=pass (sender IP is 172.234.253.10) smtp.mailfrom=linux-sunxi+bounces-25823-noreply=patchwork.local@lists.linux.dev smtp.helo=sea.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.234.253.10 as permitted sender) client-ip=172.234.253.10; envelope-from=linux-sunxi+bounces-25823-noreply=patchwork.local@lists.linux.dev; helo=sea.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sea.lore.kernel.org (Postfix) with ESMTP id 66F15361F4 for ; Sat, 12 Sep 2026 07:57:41 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id D773F27AC31; Sat, 12 Sep 2026 07:57:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=lucidmail.xyz header.i=@lucidmail.xyz header.b="uHCOTSNd" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail.lucidmail.xyz (mail.lucidmail.xyz [78.46.175.68]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9F8AB340A57 for ; Sat, 12 Sep 2026 07:57:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=78.46.175.68 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789199859; cv=none; b=s+xVWPN+sWTAE9VN+F5C8zWWAOKymP4/fZQOSDzb0a/i8EWC+4S5njy3p+KzDuAS6+KSGIBwRnNgjp78L6X8DqhMdoX8sG68HTzLeG7z/TmRGcqQObMf0tPUkL9uU7ghoBSoWl8eaiIhyvXWUUJFI3L8+FMWtWsKWDyvSMOp/zQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789199859; c=relaxed/simple; bh=ovDOcq2L4lWy7RYArpBGqBMRn94sLXueaI+xW4q3Kng=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=qaTuabnMMUFcJUBCns7LxCplF4xMbs8irPgofbCp4d9Y36EqkctDE9qW0U1ELiyVWYS+RfULmD679JH3wvkiy2JXb6CrEKjqnI2+6XHs78B0dZ+pX53neNFwSgZMmM32d6UDy50UuER4PdhuwY5e0s37YsY++tPK3OQ/GlXzr2o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=lucidmail.xyz; spf=pass smtp.mailfrom=lucidmail.xyz; dkim=pass (2048-bit key) header.d=lucidmail.xyz header.i=@lucidmail.xyz header.b=uHCOTSNd; arc=none smtp.client-ip=78.46.175.68 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=lucidmail.xyz Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=lucidmail.xyz Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 67383177D5B; Sat, 12 Sep 2026 09:57:24 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=lucidmail.xyz; s=dkim; t=1789199848; h=from:subject:date:message-id:to:cc:mime-version:content-type: content-transfer-encoding:in-reply-to:references; bh=odRsyaI7u3OBSAjaQ0oVwNHkIAVVER2DG27ZY48K6aw=; b=uHCOTSNdnkYRvmEfjCk0ljZcV5ar+1A7q6xoM6HZwq9yxu7ewrXURlNs2Aa62JJXByBbZK PJI8E7ov8gwvX7X1lluATjXIAU/gK848v3Nmn6tJzo7Rox17e0Iza2d71lZhRDxLQVBflF JgRX0Z90ffberJ1S2K5fAKMUR/iuDmdjUqT4A2zQynGFrHQePGBw+fLPid2LMmgLGqxTib KZX7vmr6euhPqi3T/uQdSbm9KKgQhRC7cJsieFnSqomSLn8BQ6qjzS/vDKZCEW7Dr+J+dy IPBx5GZvy9wOoZH8zXbpJln83PkT8YIE6O7ft9SOeuDuVWjOg4iyT8sWqTmOtg== From: leow149 To: dri-devel@lists.freedesktop.org Cc: linux-sunxi@lists.linux.dev, linux-arm-kernel@lists.infradead.org, leow149 , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Maxime Ripard , Paul Kocialkowski Subject: [RFC PATCH v2] arm64: allwinner: add TrimUI Smart Pro (A133) board support Date: Sat, 12 Sep 2026 09:57:04 +0200 Message-ID: <20260912075706.86857-1-leow149@lucidmail.xyz> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260912060249.80035-1-leow149@lucidmail.xyz> References: <20260912060249.80035-1-leow149@lucidmail.xyz> Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Last-TLS-Session-Version: TLSv1.3 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= Board bring-up for the TrimUI Smart Pro handheld (Allwinner A133 Plus): - New board DTS (sun50i-a133-trimui-smartpro.dts): display pipeline (mixer/TCON/DSI/D-PHY), regulators, MMC, PWM backlight. - New OTM1289A/ER68576 DSI panel driver (panel-orisetech-otm1289a.c), including real panel timings and DCS init sequence. - New PWM driver for this SoC's newer-generation PWM IP (pwm-sun50i-a133.c), with binding doc. - sun8i_mixer: A133 DE2 mixer0/mixer1 configs. - sun8i_tcon_top: A133 quirks entry, plus two new helpers (sun8i_tcon_top_set_dsi_gate/set_de0_port) needed because this board's TCON quirks table has no .set_mux callback to reach them through the existing paths. - sun4i_tcon: CPU/8080-interface DSI bring-up (sun4i_tcon0_mode_set_cpu register formulas, an hrtimer-driven TRIGGER_START retrigger, an FSYNC_INT storm fix, IRQ deferred-enable ordering) plus TCON-TOP wiring for this board's DSI clock gate and DE0 port routing. - sun6i_mipi_dsi: an additional DSI_START_TRI instruction-jump-table case for continuous per-frame retriggering (distinct from the existing one-shot HS-clock-enable case), a DRQ_SET formula fix, and reordering the HS-clock-enable step to run before the panel's DCS init sequence. - ccu-sun50i-a100: CLK_SET_RATE_PARENT on the tcon-lcd0 clock so its rate request can actually explore parent PLL rates. Sending as RFC rather than waiting for the display to fully work: posting the board/DTS foundation now so it's in front of people who know this hardware, rather than sitting on it indefinitely. Known issue, and the actual reason for the RFC tag: the panel attaches and the backlight lights, but no pixel data reaches it. TCON0_CPU_IF_REG's TRIGGER_START bit (a documented self-clearing one-shot) never self-clears, despite the rest of the pixel pipeline matching a working vendor system register-for-register -- every static register value, D-PHY lane state, control-flow/sequencing cross-referenced against a symbol-resolved decompile of the vendor kernel, and even measured real-time timing all confirmed matching. Full writeup, including what's been ruled out and how: https://github.com/leow149/trimui-a133-mainline-port/blob/main/COMMUNITY_HELP_REQUEST.md If anyone recognizes this symptom on the CPU/8080-interface DSI path, or can spot something the writeup missed, I would very much like to hear about it. Since the RFC went out, an automated review turned up a real bug worth calling out specifically: sun6i_dsi_clear_int_status() was clearing a write-1-to-clear register via regmap_update_bits(), which silently skips the actual write whenever its computed new value equals the last-read one -- and for a W1C acknowledge, writing back what you just read *is* the correct pattern, so this register's status bits were never actually clearing in hardware. This directly breaks the edge-detection the DSI retrigger timer depends on. Switched to regmap_write_bits() (unconditional write) to fix it. Also addressed, same review pass: a timer/IRQ not being torn down on CRTC disable, an IRQF_NO_AUTOEN regression that would have permanently disabled vblank for any other board sharing this TCON code, a spurious-vblank issue from treating a bare FSYNC_INT as a real frame event, a raw clock-gate write getting silently undone by the "disable unused clocks" sweep, a PWM register race across channels, a PWM 100%-duty truncation bug, a divide-by-zero guard, and a DT schema gap. Re-tested all of the above on real hardware. Result: the regmap fix, while real, did NOT resolve the TRIGGER_START symptom -- register peeks (TCON0_CPU_IF_REG still shows TRIGGER_START stuck at 1; DSI_INT_REG's VIDEO_VBLK status never latches) confirm the panel still never receives a completed frame. The known-issue description above still stands. Testing did turn up two more real, hardware-confirmed bugs, since fixed: - TCON0's own interrupt line (GIC SPI 68) storms at tens of thousands of firings/sec with sun4i_tcon_handler() unable to reliably sample FSYNC_INT fast enough, which was tripping genirq's "nobody cared" unhandled-IRQ storm protection and permanently disabling the line 60-100s into boot -- killing the handler's own TRI_FINISH-based backstop retrigger for the rest of the session. In DSI/CPU mode this driver is the only consumer of that GIC line, so there's no legitimate "not for us" case for genirq's heuristic to protect against here; treating every firing in that mode as handled unconditionally stops the storm from disabling it, confirmed via a full boot with zero "nobody cared" events afterward (previously reproduced twice). - sun6i_dsi_encoder_enable() enabled both VIDEO_VBLK and VIDEO_LINE on DSI's own interrupt register; a live vendor register dump on a genuinely working reference system (already in this repo's vendor-notes/, gathered weeks ago but never acted on) shows only VIDEO_VBLK enabled. Matched vendor exactly. VIDEO_LINE fires once per horizontal line rather than once per frame -- a plausible contributor to the IRQ storm above, though removing it alone did not stop the storm (the genirq fix above was still needed). Neither of these, nor the regmap fix, resolves the core bug. They're real, independently-verified improvements to the driver's correctness and robustness, kept because they're correct, not because they fixed the black screen. Cc: Chen-Yu Tsai Cc: Jernej Skrabec Cc: Samuel Holland Cc: Maxime Ripard Cc: Paul Kocialkowski Signed-off-by: leow149 --- Changes since v1: - Fixed all 11 applicable findings from an automated review that replied to v1 (a regmap_update_bits() write-1-to-clear bug, a timer/IRQ not torn down on CRTC disable, an IRQF_NO_AUTOEN regression risk for other boards, a spurious-vblank issue, a raw clock-gate write undone by the "disable unused clocks" sweep, a PWM register race, a PWM 100%-duty truncation bug, a divide-by-zero guard, and a DT schema gap). One finding (the dsi_cpu_mode early-return in sun4i_tcon_enable_vblank()) was reviewed and correctly left as-is: it's a deliberate, hardware-verified design choice, not a bug. - Re-tested all of the above on real hardware. The regmap fix, while real, did NOT resolve the known TRIGGER_START issue below -- confirmed via direct register reads, not just re-running the same tests. - Testing surfaced two more real, hardware-confirmed bugs along the way, fixed and kept: TCON0's own interrupt line was storming (tens of thousands of firings/sec) and getting permanently disabled by the kernel's own unhandled-IRQ storm protection 60-100s into every boot, silently killing this driver's own per-frame retrigger backstop for the rest of the session; and a DSI interrupt enable-mask mismatch against a real working vendor system's own register dump (already in this series' companion repo, gathered weeks earlier, never acted on until now). - None of the above changes the actual known issue: the panel still never receives a completed frame. Still sending as RFC for the same reason as v1. .../allwinner,sun8i-a83t-de2-mixer.yaml | 2 + .../display/allwinner,sun8i-r40-tcon-top.yaml | 43 ++ .../pwm/allwinner,sun50i-a133-pwm.yaml | 67 ++ arch/arm64/boot/dts/allwinner/Makefile | 1 + .../allwinner/sun50i-a133-trimui-smartpro.dts | 691 ++++++++++++++++++ drivers/clk/sunxi-ng/ccu-sun50i-a100.c | 16 +- drivers/gpu/drm/panel/Kconfig | 8 + drivers/gpu/drm/panel/Makefile | 1 + .../gpu/drm/panel/panel-orisetech-otm1289a.c | 424 +++++++++++ drivers/gpu/drm/sun4i/sun4i_tcon.c | 424 ++++++++++- drivers/gpu/drm/sun4i/sun4i_tcon.h | 95 +++ drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 192 ++++- drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h | 12 + drivers/gpu/drm/sun4i/sun8i_mixer.c | 48 ++ drivers/gpu/drm/sun4i/sun8i_tcon_top.c | 119 ++- drivers/gpu/drm/sun4i/sun8i_tcon_top.h | 2 + drivers/pwm/Kconfig | 14 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-sun50i-a133.c | 317 ++++++++ 19 files changed, 2431 insertions(+), 46 deletions(-) create mode 100644 Documentation/devicetree/bindings/pwm/allwinner,sun50i-a133-pwm.yaml create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-a133-trimui-smartpro.dts create mode 100644 drivers/gpu/drm/panel/panel-orisetech-otm1289a.c create mode 100644 drivers/pwm/pwm-sun50i-a133.c diff --git a/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml b/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml index cbd18fd83..ce72c1ba7 100644 --- a/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml +++ b/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-de2-mixer.yaml @@ -23,6 +23,8 @@ properties: - allwinner,sun20i-d1-de2-mixer-1 - allwinner,sun50i-a64-de2-mixer-0 - allwinner,sun50i-a64-de2-mixer-1 + - allwinner,sun50i-a133-de2-mixer-0 + - allwinner,sun50i-a133-de2-mixer-1 - allwinner,sun50i-h6-de3-mixer-0 - allwinner,sun50i-h616-de33-mixer-0 diff --git a/Documentation/devicetree/bindings/display/allwinner,sun8i-r40-tcon-top.yaml b/Documentation/devicetree/bindings/display/allwinner,sun8i-r40-tcon-top.yaml index 7d849c409..2dd2c255e 100644 --- a/Documentation/devicetree/bindings/display/allwinner,sun8i-r40-tcon-top.yaml +++ b/Documentation/devicetree/bindings/display/allwinner,sun8i-r40-tcon-top.yaml @@ -43,6 +43,7 @@ properties: - allwinner,sun8i-r40-tcon-top - allwinner,sun20i-d1-tcon-top - allwinner,sun50i-h6-tcon-top + - allwinner,sun50i-a133-tcon-top reg: maxItems: 1 @@ -201,6 +202,48 @@ allOf: items: - description: TCON TV0 output clock name + - if: + properties: + compatible: + contains: + const: allwinner,sun50i-a133-tcon-top + + then: + properties: + clocks: + items: + - description: The TCON TOP interface clock + - description: > + The TCON TOP TV0 clock. This SoC has no TCON-TV/HDMI output + at all, so this is an otherwise-unused filler clock, only + present because the driver unconditionally registers a TV0 + gate regardless of quirks. + - description: The TCON TOP MIPI DSI clock + + clock-names: + items: + - const: bus + - const: tcon-tv0 + - const: dsi + + clock-output-names: + items: + - description: TCON TV0 output clock name (unused, see above) + - description: DSI output clock name + + # NOTE: this variant has no HDMI mux (no port@4/port@5) and, on at + # least the one board using it so far, only mixer0 (no port@2/port@3) + # -- but properties.ports.required above applies unconditionally to + # every compatible in this file, including this one, and an `if/then` + # can only add to that required list, not relax it. A DTS for this + # compatible that only wires up port@0/port@1 is therefore expected to + # fail `dtbs_check` against this schema as it stands. Properly fixing + # that means restructuring the base schema so port@2-5 are required + # per-variant instead of unconditionally, which would need re-checking + # against R40/D1/H6's actual in-tree DTS files to do safely -- left + # for a maintainer with those boards to verify, rather than guessed at + # here. + examples: - | #include diff --git a/Documentation/devicetree/bindings/pwm/allwinner,sun50i-a133-pwm.yaml b/Documentation/devicetree/bindings/pwm/allwinner,sun50i-a133-pwm.yaml new file mode 100644 index 000000000..59e4fba3b --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/allwinner,sun50i-a133-pwm.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/allwinner,sun50i-a133-pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allwinner A133 PWM Controller + +description: | + No public register documentation for this IP has ever been reused + upstream: it's a newer, richer generation than the existing + allwinner,sun4i-a10-pwm family (16 channels grouped in pairs, per-pair + dead-zone/group-sync control) with no existing mainline driver, and no + A100/A133-specific binding either. Confirmed real from the public A133 + User Manual (chapter 10.11, base 0x0300a000), which documents this + block down to bit level. + +maintainers: + - out-of-tree hobbyist port, not yet upstream + +properties: + compatible: + const: allwinner,sun50i-a133-pwm + + reg: + maxItems: 1 + + clocks: + items: + - description: Bus Clock (register access only — the PWM output + clock source itself is internal to this IP, selected between + OSC24M/APB1 per-pair in its own PCCR01/PCCR23 registers, not + exposed as a separate CCU clock) + + clock-names: + items: + - const: bus + + resets: + maxItems: 1 + + "#pwm-cells": + const: 3 + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - "#pwm-cells" + +additionalProperties: false + +examples: + - | + #include + #include + + pwm: pwm@300a000 { + compatible = "allwinner,sun50i-a133-pwm"; + reg = <0x0300a000 0x400>; + clocks = <&ccu CLK_BUS_PWM>; + clock-names = "bus"; + resets = <&ccu RST_BUS_PWM>; + #pwm-cells = <3>; + }; diff --git a/arch/arm64/boot/dts/allwinner/Makefile b/arch/arm64/boot/dts/allwinner/Makefile index aa21f58a4..6132fda32 100644 --- a/arch/arm64/boot/dts/allwinner/Makefile +++ b/arch/arm64/boot/dts/allwinner/Makefile @@ -26,6 +26,7 @@ dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h64-remix-mini-pc.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a100-allwinner-perf1.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a133-helperboard.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a133-liontron-h-a133l.dtb +dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a133-trimui-smartpro.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h5-bananapi-m2-plus.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h5-bananapi-m2-plus-v1.2.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h5-emlid-neutis-n5-devboard.dtb diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a133-trimui-smartpro.dts b/arch/arm64/boot/dts/allwinner/sun50i-a133-trimui-smartpro.dts new file mode 100644 index 000000000..531be1d90 --- /dev/null +++ b/arch/arm64/boot/dts/allwinner/sun50i-a133-trimui-smartpro.dts @@ -0,0 +1,691 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2026 (out-of-tree hobbyist port, not yet upstream) + * + * Board: TrimUI Smart Pro (Allwinner A133 Plus / sun50iw10p1) + * + * Sourced from: + * - vendor DTB extracted from Knulli image (dtb_86065152.dts, offset + * 86065152 in knulli-a133-trimui-smart-pro-scarab-20260511.img) + * - sun50i-a133-liontron-h-a133l.dts (mainline reference board, same SoC) + * - sun50i-h700-anbernic-rg35xx-2024.dts (mainline handheld reference for + * AXP717 regulator/battery wiring style) + * - LIVE data pulled directly off the running unit over UART (root shell + + * /sys/firmware/fdt, the kernel's own resolved device tree — not a guess): + * real regulator voltages (confirmed via U-Boot's PMIC init console log), + * real DRAM timing table (see trimui-smartpro_defconfig), real panel + * timings (see the lcd0 comment block below). + * + * TODO markers below are places nothing above could resolve: buttons/ + * joystick are NOT GPIO at all (confirmed: read over a UART-attached + * co-microcontroller, see BUTTON_PROBING_PLAN.md — needs a from-scratch + * serdev driver, not a DT gpio-keys node), and the panel's DSI init + * command sequence (register writes for the otm1289a controller) is + * not yet captured. + */ + +/dts-v1/; + +#include "sun50i-a100.dtsi" +#include "sun50i-a100-cpu-opp.dtsi" + +#include +#include +#include +#include + +/ { + model = "TrimUI Smart Pro"; + chassis-type = "handset"; + /* "trimui" is not a registered vendor prefix in dt-bindings yet; + * fine for an out-of-tree hobbyist build, would need registering + * (vendor-prefixes.yaml) before any upstream submission. */ + compatible = "trimui,smart-pro", "allwinner,sun50i-a100"; + + aliases { + serial0 = &uart0; + }; + + chosen { + /* confirmed from the vendor boot.img kernel cmdline */ + stdout-path = "serial0:115200n8"; + }; + + battery: battery { + compatible = "simple-battery"; + /* vendor DTB: pmu_battery_rdc = 0x93, pmu_battery_cap = 0x1388 (5000mAh) */ + voltage-max-design-microvolt = <4200000>; + }; + + reg_vcc5v: regulator-vcc5v { + compatible = "regulator-fixed"; + regulator-name = "vcc-5v"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; + + de: display-engine { + /* + * The fallback compatible string matters: sun4i_drv.c's + * of_match_table has no A133 entry, and matches here are what + * probes the aggregate "sun4i-drm" platform driver that calls + * component_bind_all() to wire the already-individually-probed + * mixer/tcon/dsi devices together into one DRM device. Without + * a match, this node binds no driver at all and the pipeline + * sits in permanent -EPROBE_DEFER. sun4i_drv.c's probe function + * isn't SoC-specific (it just walks the DT ports graph), so + * reusing an existing entry here is safe -- same pattern used + * on tcon_lcd0 below. + */ + compatible = "allwinner,sun50i-a133-display-engine", + "allwinner,sun50i-a64-display-engine"; + allwinner,pipelines = <&mixer0>; + status = "okay"; + }; + + /* + * Real values from the vendor live DTB's power_sply block: + * lcd_pwm_used=1, lcd_pwm_ch=0 (PWM channel 0 — wired above), + * lcd_pwm_freq=0xc350=50000Hz (period_ns = 1e9/50000 = 20000), + * lcd_pwm_pol=0 (normal polarity), lcd_pwm_max_limit=0xc8=200. + * NOT included: a real backlight-enable GPIO. The vendor DTB has + * a separate lcd_bl_en packed property (bank=7/pin=18, decoding to + * PH18 by cross-checking the pinctrl driver's own bank-numbering + * against an already-confirmed-real pin elsewhere in this file) — + * tried driving it directly via a raw register poke on live + * hardware (no kernel involved, just to test the hypothesis) and + * it had no visible effect, so this pin guess is unconfirmed/likely + * wrong (the vendor property has 8 packed fields where a plain + * 6-cell gpio-spec would only have 7 — there's a piece of that + * encoding not accounted for). Left out entirely rather than wire + * up a pin that's an unverified guess. + */ + backlight: backlight { + compatible = "pwm-backlight"; + pwms = <&pwm 0 20000 0>; + default-brightness-level = <200>; + power-supply = <®_cldo4>; + }; + + /* TODO: face buttons (A/B/X/Y, D-pad, L1/L2/R1/R2, start/select, + * joystick clicks). The vendor DTB has NO gpio-keys node and no + * "trimui_keypad"-style driver string was found in the firmware — + * only a 3-key ADC "allwinner,keyboard_1350mv" node exists (that's + * volume/fastboot-combo, not the face buttons). Real GPIO/ADC + * assignments need to come from live probing on the actual unit + * (pinctrl debugfs, evtest against the stock kernel, or a UART + * dmesg capture during boot) — not guessed from another device. + */ +}; + +&cpu0 { + /* TODO: verify against A133 PLUS-rated OPP ceiling, not just A100's */ + cpu-supply = <®_dcdc1>; +}; + +&mmc0 { + /* SD card slot. cd-gpios still unconfirmed — no hotplug detection, but + * that's fine for a card already inserted at boot (proven live: this is + * literally how SPL/BROM read this same card to boot in the first + * place). Pin/clock wiring already provided by sun50i-a100.dtsi. + * vmmc-supply guessed as the same general IO rail as mmc2 — unconfirmed, + * first thing to revisit if this doesn't come up cleanly. */ + bus-width = <4>; + vmmc-supply = <®_cldo3>; + status = "okay"; +}; + +&mmc2 { + /* eMMC — pin group/clocks/etc. already wired by sun50i-a100.dtsi, this + * board file only needs the board-specific bits. bus mode confirmed + * from the live boot log: negotiated DDR52, NOT HS200/HS400 — the + * vendor kernel actively strips those capability flags at runtime + * ("delete mmc-hs200-1_8v from dtb" / "delete mmc-hs400-1_8v from dtb" + * in dmesg), so we don't claim them here either. vmmc-supply is a + * reasonable inference (cldo3/vcc-io, the general 3.3V IO rail) but + * NOT directly confirmed — no capture explicitly tied cldo3 to mmc2. + */ + bus-width = <8>; + non-removable; + cap-mmc-hw-reset; + mmc-ddr-1_8v; + vmmc-supply = <®_cldo3>; /* inferred, not directly confirmed */ + status = "okay"; +}; + +&r_i2c0 { + status = "okay"; + + axp717: pmic@34 { + compatible = "x-powers,axp717"; + reg = <0x34>; + interrupt-controller; + #interrupt-cells = <1>; + interrupt-parent = <&r_intc>; + interrupts = <0 IRQ_TYPE_LEVEL_LOW>; + + vin1-supply = <®_vcc5v>; + vin2-supply = <®_vcc5v>; + vin3-supply = <®_vcc5v>; + vin4-supply = <®_vcc5v>; + + axp_adc: adc { + compatible = "x-powers,axp717-adc"; + #io-channel-cells = <1>; + }; + + battery_power: battery-power { + compatible = "x-powers,axp717-battery-power-supply"; + monitored-battery = <&battery>; + x-powers,no-thermistor; + }; + + usb_power: usb-power { + compatible = "x-powers,axp717-usb-power-supply"; + }; + + /* + * Regulator names carried over from the vendor DTB's "axp2202-*" + * node names (AXP2202 == AXP717, confirmed: Allwinner's BSP driver + * for "axp2202" has been shown driving a chip physically marked + * AXP717). Voltages below are REAL, read directly from U-Boot's own + * PMIC-init console log on the live unit (not decoded/guessed) — + * each line printed "_vol = , onoff=<0|1>" at boot. dcdc2 + * (GPU/sys) wasn't printed at that boot stage (probably brought up + * later by the kernel) — left undocumented rather than guessed. + * dcdc1 (vdd-cpu) was 940mV at that instant but is DVFS-scaled by + * cpufreq, so it's intentionally NOT hardcoded to a fixed value here. + */ + regulators { + reg_dcdc1: dcdc1 { + /* + * regulator-always-on, not regulator-boot-on: + * drivers/regulator/core.c's regulator_late_cleanup() + * (the late-boot "disable unused regulators" sweep) + * checks constraints->always_on, not ->boot_on -- + * boot-on only means "was already on at boot," it + * does nothing to stop this cleanup pass from + * turning it back off. This board has no cpufreq/OPP + * consumer wired up yet, so dcdc1 would otherwise + * have zero regulator-API reference count despite + * being the CPU's own supply -- disabling it hard + * hangs the board. + */ + regulator-always-on; + regulator-name = "vdd-cpu"; + /* boot-time value was 940mV; real range needs the OPP table, not hardcoded here */ + }; + reg_dcdc2: dcdc2 { + /* + * Same reasoning as dcdc1 above: no GPU driver + * claims this rail yet, and nothing rules out + * shared PLL/bias circuitry with the display + * path, so it's kept on rather than left to the + * late-boot unused-regulator cleanup. + */ + regulator-always-on; + regulator-name = "vdd-gpu-sys"; + }; + reg_dcdc3: dcdc3 { + /* confirmed real: matches boot0's "DRAM_VCC set to 1100 mv" exactly */ + regulator-always-on; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + regulator-name = "vdd-dram"; + }; + /* + * aldo1/aldo2/aldo4/bldo2/bldo3/bldo4 all got + * regulator-always-on added after real, reproducible + * hard hangs: fixing vdd-cpu/vdd-gpu-sys (the first two + * rails caught by the kernel's late-boot "disable + * unused regulators" sweep) didn't stop the hang, it + * just moved it — the very next boot hung identically + * but with "aldo4: disabling" this time. Whack-a-mole: + * every rail the vendor bootloader left on (onoff=1 in + * the real U-Boot PMIC log) that we don't have a + * confirmed real consumer/purpose for is exactly this + * same landmine, since nothing in our still-incomplete + * DT claims them through the regulator API. Rather than + * keep discovering these one hang at a time, all of + * them get always-on now — matches the vendor's own + * actual default behavior anyway (leave them on). + * bldo1 is the one confirmed exception: real U-Boot log + * shows onoff=0 at boot (WiFi/BT driver switches it on + * dynamically later), so it's deliberately left off here. + */ + reg_aldo1: aldo1 { + /* real: 1800mV, onoff=1 at boot */ + regulator-always-on; + }; + reg_aldo2: aldo2 { + /* real: 3300mV, onoff=1 */ + regulator-always-on; + }; + reg_aldo3: aldo3 { + /* confirmed: WiFi/BT IO rail (wlan_io_regulator), real: 3300mV */ + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc-wifi-io"; + }; + reg_aldo4: aldo4 { + /* real: 1800mV, onoff=1 */ + regulator-always-on; + }; + reg_bldo1: bldo1 { + /* confirmed: WiFi/BT power rail (wlan_power1), real: 3300mV. + * NOT always-on: U-Boot log shows onoff=0 at boot — the + * wifi/bt driver switches it on dynamically at runtime. */ + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc-wifi"; + }; + reg_bldo2: bldo2 { + /* real: 1800mV, onoff=1 */ + regulator-always-on; + }; + reg_bldo3: bldo3 { + /* real: 3300mV, onoff=1 */ + regulator-always-on; + }; + reg_bldo4: bldo4 { + /* real: 1800mV at boot, onoff=1 */ + regulator-always-on; + }; + reg_cldo1: cldo1 { + /* confirmed: LCD panel power0 (lcd_power0="cldo1"), real: 1800mV */ + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcc-lcd-1"; + }; + reg_cldo2: cldo2 { }; + reg_cldo3: cldo3 { + /* real: 3300mV */ + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc-io"; + }; + reg_cldo4: cldo4 { + /* confirmed: LCD panel power1 (lcd_power1="cldo4"), real: 3300mV */ + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc-lcd-2"; + }; + reg_cpusldo: cpusldo { + /* real: 900mV, onoff=1 */ + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <900000>; + }; + }; + }; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pb_pins>; /* only pin group this SoC's dtsi defines; matches vendor cmdline's ttyS0 */ + status = "okay"; +}; + +&{/soc} { + /* + * A133 (sun50iw10) has no public DE/mixer register documentation + * (A133 User Manual rev 1.1, chapter 4.1 "DE" is 2 pages of feature + * bullets + a block diagram, no register list — unlike every other + * chapter in that manual). Addresses/topology below are cross- + * checked two ways instead: the manual's own top-level physical + * memory map (DE0/DSI0/DPSS_TOP0/TCON_LCD0 base addresses + GIC IRQ + * numbers, chapter 3) and Allwinner's own GPL disp2 driver source + * for this exact SoC (lowlevel_v2x/sun50iw10 tree — public on + * GitHub from several vendor BSP forks, e.g. chainsx/kernel-sun50iw10, + * engSinteck/A133_Image): de_feat.c gives the real channel/layer + * topology (DISP0 = 2 VI + 2 UI channels, DISP1 = 1 VI + 2 UI, all + * scaler-capable) that drives the sun8i_mixer.c cfg structs, and + * de_rtmx_init()'s reg_base + 0x100000 / + 0x200000 offsets for + * mixer0/mixer1 match the addresses used below (and match the + * existing convention already used for every other DE2.0 SoC in + * this driver — A64, H3, R40). + * + * DPSS_TOP0 (0x06510000) is a real hardware crossbar between the + * mixers and TCON_LCD0 -- CCU exposes its own bus clock/reset gate + * (CLK_BUS_DPSS_TOP0/RST_BUS_DPSS_TOP0), and the vendor implementation + * explicitly routes through it. Leaving it out of the ports graph + * entirely is not survivable: mixer0/tcon_lcd0/dsi0 all probe and + * bind fine without it, but every DRM atomic commit then times out + * waiting for vblank -- the pixel path never physically reaches the + * panel, because nothing keeps DPSS_TOP0's own bus clock enabled + * once Linux's "disable unused clocks" cleanup runs. Modeled here by + * reusing mainline's existing sun8i_tcon_top.c driver with an added + * A133 compatible + quirks entry: the vendor's own register layout + * for this block is bit-for-bit identical to what that driver + * already assumes for R40/D1/H6 -- genuinely the same IP block, just + * undocumented for this particular SoC. + */ + dpss_top0: dpss-top@6510000 { + compatible = "allwinner,sun50i-a133-tcon-top"; + reg = <0x06510000 0x1000>; + /* + * "tcon-tv0" is a required clock-name in sun8i_tcon_top_bind() + * regardless of quirks (it unconditionally registers a TV0 + * gate clock) even though this board has no TCON_TV/HDMI + * output at all. Pointing it at CLK_TCON_LCD is a harmless + * filler — that gate output is never consumed by anything in + * this DT, it just needs to exist so probe doesn't fail + * resolving the clock-name. "dsi" is real and load-bearing + * though (has_dsi=true pulls it in): CLK_MIPI_DSI from CCU is + * the actual upstream parent feeding DSI's gated clock inside + * this block. Missed this the first time around — dpss_top0's + * own probe failed outright without it (of_property_match_string + * couldn't find "dsi" in clock-names), which cascaded into + * dsi0's own "mod" clock lookup failing too, since it points + * at dpss_top0's now-never-registered clock provider. + */ + clocks = <&ccu CLK_BUS_DPSS_TOP0>, + <&ccu CLK_TCON_LCD>, + <&ccu CLK_MIPI_DSI>; + clock-names = "bus", "tcon-tv0", "dsi"; + clock-output-names = "tcon-top-tv0", "tcon-top-dsi"; + resets = <&ccu RST_BUS_DPSS_TOP0>; + #clock-cells = <1>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + dpss_top0_mixer0_in: port@0 { + reg = <0>; + + dpss_top0_in_mixer0: endpoint { + remote-endpoint = <&mixer0_out_dpss_top0>; + }; + }; + + dpss_top0_mixer0_out: port@1 { + reg = <1>; + + dpss_top0_out_tcon_lcd0: endpoint { + remote-endpoint = <&tcon_lcd0_in_dpss_top0>; + }; + }; + }; + }; + + mixer0: mixer@6100000 { + compatible = "allwinner,sun50i-a133-de2-mixer-0"; + reg = <0x06100000 0x100000>; + clocks = <&ccu CLK_BUS_DE>, <&ccu CLK_DE>; + clock-names = "bus", "mod"; + resets = <&ccu RST_BUS_DE>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + mixer0_out: port@1 { + reg = <1>; + + mixer0_out_dpss_top0: endpoint { + remote-endpoint = <&dpss_top0_in_mixer0>; + }; + }; + }; + }; + + tcon_lcd0: lcd-controller@6511000 { + /* + * No A100/A133-specific compatible exists upstream (the + * binding's compatible enum only lists SoCs someone has + * already wired up). The generic a83t-tcon-lcd fallback + * matches the driver purely on this string, so it still + * binds correctly even without an A133-specific entry. + */ + compatible = "allwinner,sun8i-a83t-tcon-lcd"; + reg = <0x06511000 0x1000>; + /* + * Was GIC_SPI 101 (copied from a mismatched reference DTS) -- + * the interrupt handler was silently never firing. Confirmed + * correct via the live vendor DT + /proc/interrupts: vendor's + * combined disp@06000000 node's third interrupt cell is raw + * SPI 0x44 = 68, and that is the ONLY display-related + * interrupt actively counting (~60Hz) on real hardware -- + * cross-checked against dma0's SPI 0x2d=45, which matches + * exactly between the vendor DT and its own /proc/interrupts + * with no offset, confirming the raw-cell-value convention. + */ + interrupts = ; + clocks = <&ccu CLK_BUS_TCON_LCD>, <&ccu CLK_TCON_LCD>; + clock-names = "ahb", "tcon-ch0"; + clock-output-names = "tcon-pixel-clock"; + resets = <&ccu RST_BUS_TCON_LCD>; + reset-names = "lcd"; + #clock-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + tcon_lcd0_in: port@0 { + reg = <0>; + + tcon_lcd0_in_dpss_top0: endpoint { + remote-endpoint = <&dpss_top0_out_tcon_lcd0>; + }; + }; + + tcon_lcd0_out: port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + /* + * reg = <1> here (not 0) is load-bearing, not + * cosmetic: sun4i_drv.c's component-matching + * walk (sun4i_drv_traverse_endpoints()) treats + * a channel-0 TCON's port@1 endpoint 0 as "our + * directly-connected panel" and deliberately + * skips adding it to the component match list. + * Confirmed live on real hardware: with this + * endpoint unaddressed (defaulting to reg=0), + * the DSI encoder got silently treated as a + * bare panel and never added to the component + * list, so component_bind_all() never bound + * it, dsi->drm never got set, and the panel's + * mipi_dsi_attach() spun in EPROBE_DEFER + * forever — screen stayed black even though + * mixer0 probed fine standalone. Matches the + * real D1 reference DTS's own convention: + * sunxi-d1s-t113.dtsi's tcon_lcd0_out_dsi is + * also "endpoint@1 { reg = <1>; ... }", same + * as here, not endpoint 0 — this was a real + * detail I should have carried over the first + * time instead of dropping it. + */ + tcon_lcd0_out_dsi0: endpoint@1 { + reg = <1>; + remote-endpoint = <&dsi0_in_tcon_lcd0>; + }; + }; + }; + }; + + dsi0: dsi@6504000 { + compatible = "allwinner,sun50i-a100-mipi-dsi"; + reg = <0x06504000 0x1000>; + interrupts = ; + /* + * REVERTED: tried sourcing "mod" from dpss_top0's gated DSI + * output (matching the vendor's de_lcd.c, which gates + * tcon_clk_gate.bits.dsi_clk_gate as part of enabling DSI, and + * the real D1 reference DTS's identical convention) — but this + * creates a genuine circular dependency, not just an ordering + * race: dpss_top0 only registers its clock-provider inside its + * component .bind() callback, which only runs once every + * matched component (including dsi0 itself) has *already* + * succeeded its own individual .probe(). dsi0's probe() can't + * succeed without this clock, so it can never reach + * component_add(), so dpss_top0's bind() never fires, forever + * — confirmed live: "Couldn't get the DSI mod clock" repeated + * on every deferred-probe retry with no path to resolution. + * Back to sourcing directly from CCU, which is what actually + * got the panel to attach successfully a few iterations ago. + * dpss_top0 stays modeled in the ports graph regardless (for + * its own bus-clock-keepalive purpose, now fully decoupled + * from dsi0's clock lookup) — whether A133 genuinely needs + * this specific gate toggled for DSI to output correctly, as + * opposed to just needing DPSS_TOP0 clocked/reset for its + * routing mux to be live, is still an open question; CCU's + * own CLK_MIPI_DSI already has its own independent gate bit, + * so it's plausible this DPSS_TOP0 gate is a separate/optional + * clock domain rather than something on the primary pixel path. + */ + clocks = <&ccu CLK_BUS_MIPI_DSI>, <&ccu CLK_MIPI_DSI>; + clock-names = "bus", "mod"; + resets = <&ccu RST_BUS_MIPI_DSI>; + phys = <&dphy0>; + phy-names = "dphy"; + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + port { + dsi0_in_tcon_lcd0: endpoint { + remote-endpoint = <&tcon_lcd0_out_dsi0>; + }; + }; + + panel@0 { + /* + * reset-gpios pin is a placeholder (PD0) — the real + * reset line hasn't been probed on hardware yet. + * power-supply maps to the driver's + * devm_regulator_get(dev, "power") lookup. backlight + * maps to drm_panel_of_backlight()'s lookup, wired to + * the pwm-backlight node above (real PWM channel 0 / + * 50kHz values, but backlight-enable GPIO not included + * — see the comment on that node for why). + * + * No ports/endpoint graph needed here: sun6i_dsi_attach() + * (drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c) finds the + * panel via of_drm_find_panel() on this node directly, + * not through of_graph — the DSI parent/child + * relationship (reg = virtual channel) is the only + * link needed. + */ + compatible = "orisetech,otm1289a"; + reg = <0>; + reset-gpios = <&pio 3 0 GPIO_ACTIVE_LOW>; /* TODO: unconfirmed pin, revisit on real hw */ + power-supply = <®_cldo1>; + backlight = <&backlight>; + }; + }; + + dphy0: phy@6505000 { + compatible = "allwinner,sun50i-a100-mipi-dphy"; + reg = <0x06505000 0x1000>; + interrupts = ; + clocks = <&ccu CLK_BUS_MIPI_DSI>, <&ccu CLK_MIPI_DSI>; + clock-names = "bus", "mod"; + resets = <&ccu RST_BUS_MIPI_DSI>; + #phy-cells = <0>; + }; + + /* + * No mainline driver or A100/A133-specific binding exists upstream + * for this IP (see drivers/pwm/pwm-sun50i-a133.c and its binding + * doc) -- it's a newer, richer generation than allwinner,sun4i-a10-pwm. + * Base address and register layout are from the A133 User Manual, + * chapter 10.11 (fully documented down to bit level). Only + * CLK_BUS_PWM (register-access gate) is needed -- the PWM output + * clock source itself is internal to this IP, confirmed by + * CLK_PWM's absence from the A100 CCU headers. + */ + pwm: pwm@300a000 { + compatible = "allwinner,sun50i-a133-pwm"; + reg = <0x0300a000 0x400>; + clocks = <&ccu CLK_BUS_PWM>; + clock-names = "bus"; + resets = <&ccu RST_BUS_PWM>; + pinctrl-names = "default"; + pinctrl-0 = <&pwm0_pins>; + #pwm-cells = <3>; + }; +}; + +&pio { + /* + * PD23/pwm0, muxsel 0x2 — confirmed real from the vendor live DTB's + * lcd_pwm_ch = 0 (PWM channel 0 drives the panel backlight) cross- + * checked against drivers/pinctrl/sunxi/pinctrl-sun50i-a100.c's own + * SUNXI_PIN(D, 23) table, which already lists "pwm0" at the exact + * same function value (0x2) the vendor DTB used — mainline's + * pinctrl driver already fully supports this pin/function, only + * the PWM controller driving it needed writing. + */ + pwm0_pins: pwm0-pins { + pins = "PD23"; + function = "pwm0"; + }; +}; + +/* + * TODO: WiFi/BT (XR829 on SDIO bus 1 / &mmc1, confirmed from strings: + * boot_xr829.bin, fw_xr829.bin, fw_xr829_bt.bin, sdd_xr829.bin — and + * confirmed live via the running kernel's "XRADIO WIFI OPEN" driver log, + * MAC dc:44:60:xx:xx:xx (redacted)). Power rails: bldo1 (3300mV, switched on + * dynamically, not always-on) + aldo3 (3300mV, always-on) — both wired + * above. GPIO wiring is REAL, read directly off the live unit's + * /sys/kernel/debug/gpio (not decoded from packed vendor fields): + * bt_rst = r_pio 0 2 (PL2) output, active low + * bt_hostwake = r_pio 0 3 (PL3) input + * bt_wake = r_pio 0 4 (PL4) output + * wlan_regon = r_pio 0 5 (PL5) output + * wlan_hostwake = r_pio 0 6 (PL6) input + * Mainline equivalent is an mmc1 node + mmc-pwrseq-simple (using + * wlan_regon as the reset-gpios line) + whatever XR829 driver ends up + * used (no mainline driver exists yet — see project notes for the + * out-of-tree candidates). Not wired into an actual mmc1 node yet. + */ + +/* + * Display: mixer0/tcon_lcd0/dsi0/dphy0/panel@0 are all wired above + * (&soc block + dsi0's panel@0 child). Status: + * - Panel driver (panel-orisetech-otm1289a.c) written: real timings + * (720x1280, 69MHz dclk, ht=880/hbp=88/hspw=4, vt=1320/vbp=9/vspw=4) + * plus a DSI init command sequence transcribed from a MediaTek LCM + * driver for the same panel controller IC found via GitHub code + * search (OrangePi4G-iot_kernel's otm1289a_hd720_dsi_vdo_auo.c) — + * NOT from Allwinner's own disp2 tree, which was checked for this + * board's exact panel and doesn't have it (its bundled lcd/ panel + * library has ~90 entries but none named otm1289a; ours is probably + * a TrimUI-added file in a downstream fork that isn't public). The + * reference driver uses 3 DSI lanes; ours is set to the confirmed + * real 4-lane value, so timing-sensitive parts of the init sequence + * may need retuning once there's real hardware feedback. + * - Mixer (sun8i_mixer.c): A133 cfg added, ported from the same vendor + * tree's de_feat.c topology data (see &soc comment above the mixer0 + * node for the full provenance/reasoning). + * - TCON_LCD0/DSI0/D-PHY: real register addresses from the A133 User + * Manual (chapter 6, which — unlike the DE chapter — is fully + * documented down to bit level); DSI host + D-PHY already had + * mainline driver support for A100 before this port touched anything. + * Backlight: working. The `backlight` pwm-backlight node above (PWM + * channel 0, 50kHz) is wired via `backlight = <&backlight>` on the panel + * node and confirmed on real hardware. + * + * The remaining open item is display output itself: the panel attaches + * and the backlight lights, but TCON0's CPU/8080-interface TRIGGER_START + * bit never self-clears, so no pixel data reaches the panel -- see the + * project's investigation log for the current status. reset-gpios is + * still an unverified placeholder, and the DPSS_TOP0 routing-register + * risk noted above the mixer0 node is still unconfirmed. + */ + +/* + * TODO: GPU (img,gpu / PowerVR GE8300) — no mainline binding to reference + * yet. + */ diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a100.c b/drivers/clk/sunxi-ng/ccu-sun50i-a100.c index 1f81c7ac4..9dee187ba 100644 --- a/drivers/clk/sunxi-ng/ccu-sun50i-a100.c +++ b/drivers/clk/sunxi-ng/ccu-sun50i-a100.c @@ -680,13 +680,27 @@ static const char * const tcon_lcd_parents[] = { "pll-video0-4x", "pll-video2-4x", "pll-video3-4x", "pll-periph0-2x" }; +/* + * No CLK_SET_RATE_PARENT here originally, unlike the other PLL_VIDEO + * derivatives it can mux to (pll_video0_4x_clk etc, which do have it). + * Confirmed via debugfs on real hardware: this left mainline's dclk + * request landing on a 400MHz PLL_VIDEO0 parent, unable to explore other + * PLL rates -- vendor's own live system has this same clock at exactly + * 408MHz (= 24MHz x 17, a clean PLL multiple, the actual nearest + * achievable rate to the ~414MHz our A133 board's CPU/8080 DSI panel + * requests). Without this flag, ccu_mp_ops's own rate-rounding can't ask + * its selected parent to change rate at all -- it can only divide down + * from whatever that parent already happens to be at, which produced a + * measurably worse (2%) result than vendor's real driver achieves for + * this exact panel. + */ static SUNXI_CCU_MP_WITH_MUX_GATE(tcon_lcd_clk, "tcon-lcd0", tcon_lcd_parents, 0xb60, 0, 4, /* M */ 8, 2, /* P */ 24, 3, /* mux */ BIT(31), /* gate */ - 0); + CLK_SET_RATE_PARENT); static SUNXI_CCU_GATE(bus_tcon_lcd_clk, "bus-tcon-lcd0", "ahb3", 0xb7c, BIT(0), 0); diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index cbdf7b8f7..fab1ed5f4 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -758,6 +758,14 @@ config DRM_PANEL_ORISETECH_OTM8009A Say Y here if you want to enable support for Orise Technology otm8009a 480x800 dsi 2dl panel. +config DRM_PANEL_ORISETECH_OTM1289A + tristate "Orise Technology otm1289a 720x1280 dsi panel" + depends on OF + depends on DRM_MIPI_DSI + help + Say Y here if you want to enable support for Orise Technology + otm1289a 720x1280 dsi panel, as used on the TrimUI Smart Pro. + config DRM_PANEL_OSD_OSD101T2587_53TS tristate "OSD OSD101T2587-53TS DSI 1920x1200 video mode panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 3b523cf37..d824f0a9b 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -74,6 +74,7 @@ obj-$(CONFIG_DRM_PANEL_MANTIX_MLAF057WE51) += panel-mantix-mlaf057we51.o obj-$(CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO) += panel-olimex-lcd-olinuxino.o obj-$(CONFIG_DRM_PANEL_ORISETECH_OTA5601A) += panel-orisetech-ota5601a.o obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o +obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM1289A) += panel-orisetech-otm1289a.o obj-$(CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS) += panel-osd-osd101t2587-53ts.o obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o obj-$(CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN) += panel-raspberrypi-touchscreen.o diff --git a/drivers/gpu/drm/panel/panel-orisetech-otm1289a.c b/drivers/gpu/drm/panel/panel-orisetech-otm1289a.c new file mode 100644 index 000000000..57de8bf94 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-orisetech-otm1289a.c @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DRM driver for the Orise Tech OTM1289A MIPI-DSI panel, as used on the + * TrimUI Smart Pro (Allwinner A133). + * + * Timings (dot clock, hsync/vsync, panel size) are real, measured values + * pulled directly from the stock vendor firmware's live device tree on + * actual hardware, not datasheet guesses. + * + * The DCS init command sequence below is reverse-engineered directly from + * this device's own real vendor kernel binary (vendor_kernel.bin, Linux + * 4.9.191, extracted from part1_boot.img), not transcribed from an + * unrelated reference driver. Converted the raw Image to a symbolized ELF + * via vmlinux-to-elf (recovers the embedded kallsyms table + a correct + * load-base guess), then disassembled lcd_panel_init(): it does a + * runtime strncmp() of this board's configured panel name against a + * table of known driver names ("gc9702c", "otm1289a", ...), and on a + * match stores a pointer to that driver's own DCS command table -- + * confirmed at VA 0xffffff8008b72190 for the "otm1289a" match on this + * exact device. That table uses fixed 72-byte entries: byte at +0 is the + * DCS command, byte at +4 is the data length (0xff = end-of-table + * marker, 0xfe = delay marker with the ms value at +8), and the data + * payload starts at +8 -- confirmed by reading lcd_panel_init()'s own + * parsing loop, which calls sunxi_lcd_dsi_dcs_write(sel, entry[0], + * &entry[8], entry[4]) for every non-marker entry. This is a real, + * ground-truth match to this exact physical panel -- and differs in + * concrete values (gamma curves, several power/timing registers) from + * the earlier version of this table, which was transcribed from an + * unrelated MediaTek-based reference driver chosen only because its + * resolution happened to match. This board is wired for 4 DSI lanes + * (confirmed from the vendor DTB) -- a host-side PHY configuration + * independent of the panel's own init register values above. + */ + +#include +#include +#include +#include +#include + +#include