From patchwork Sat Sep 12 06:02:47 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: leow149 X-Patchwork-Id: 3281 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sto.lore.kernel.org (sto.lore.kernel.org [172.232.135.74]) by mxe881.netcup.net (Postfix) with ESMTPS id 6B9631C1F8D for ; Sat, 12 Sep 2026 08:10:03 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=lucidmail.xyz; spf=pass (sender IP is 172.232.135.74) smtp.mailfrom=linux-sunxi+bounces-25821-noreply=patchwork.local@lists.linux.dev smtp.helo=sto.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.232.135.74 as permitted sender) client-ip=172.232.135.74; envelope-from=linux-sunxi+bounces-25821-noreply=patchwork.local@lists.linux.dev; helo=sto.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sto.lore.kernel.org (Postfix) with ESMTP id AB1AF60954D for ; Sat, 12 Sep 2026 06:09:25 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 903F933F385; Sat, 12 Sep 2026 06:09:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=lucidmail.xyz header.i=@lucidmail.xyz header.b="RR1ZraGR" 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 89B502DC783 for ; Sat, 12 Sep 2026 06:09:18 +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=1789193364; cv=none; b=Utbpkl9QtvXsrySn+/bQt4DsYw6Z8X6H5sPbWGT8aSo6KRpLVynB///YGkPCqOhbmfqqVooZZpAuzpfD2w8PyahdF2rkEcfm/c8kqodjm0PjWgD4IK0ZsHK94WucucJcd4J4IOrGY8SnJ73x2YDFY7mliI7FefSDM9FicmxNry0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789193364; c=relaxed/simple; bh=AQ+H09OfukIEszYGNpSbJsUJkFvmCUfBaifSuV6KdB4=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version:Content-Type; b=EVWT/xYQhjwYfZxcqxp3PxdHs89pX5w8iTTEPGPMM/2B6uiWdiu1GA6Ab9bLYbgpi6oaB0C3JVy8GPAcGWPHE0OhRiZLBDqSvJxoXfZ40uDURixYB+ijl/rgpf3maEF/n7hTIR59YR6vWC5hF7nZ7HSs6Ph5dXoDTv41IY/bG+I= 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=RR1ZraGR; 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 58BEF17819D; Sat, 12 Sep 2026 08:02:58 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=lucidmail.xyz; s=dkim; t=1789192983; h=from:subject:date:message-id:to:cc:mime-version:content-type: content-transfer-encoding; bh=csadQfcj9OHVaI8Q1K5S/o1y1dG5dyfmdUU5+Y1xDA4=; b=RR1ZraGRCOx0RIbGPSuqFU75/o39CJa2Bmo6F+RPZ3hG7/IYAG1zfNSd8B7cx4QK3zv3Ap zZ45hu8Wb6UrkGwvxepT9WR00mltqII8eGdvA5LOuRF8NJzAvXvPjCloV2er6143VQSson rANC7nWs1CwbEAFDW4PsD3/tpZp6Y36RwyKWABbfMcLcewBzLXGInDqL1qbuHjwzma+Zbm emsxpJPaANkrS0PE5rxxlZfyG0XVTn/Vt1m5108eJSzfca3IW/OIZamb33fgjl92fSWJLD 7gfigWQkQ0adGvCOcryEUadIj20vCJ2Nj76JuN+l+crGICQAfToeACm4RhuT9w== 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] arm64: allwinner: add TrimUI Smart Pro (A133) board support Date: Sat, 12 Sep 2026 08:02:47 +0200 Message-ID: <20260912060249.80035-1-leow149@lucidmail.xyz> X-Mailer: git-send-email 2.55.0 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. Cc: Chen-Yu Tsai Cc: Jernej Skrabec Cc: Samuel Holland Cc: Maxime Ripard Cc: Paul Kocialkowski Signed-off-by: leow149 --- .../allwinner,sun8i-a83t-de2-mixer.yaml | 2 + .../display/allwinner,sun8i-r40-tcon-top.yaml | 1 + .../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 | 343 ++++++++- drivers/gpu/drm/sun4i/sun4i_tcon.h | 86 +++ drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 161 +++- 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 | 94 +++ drivers/gpu/drm/sun4i/sun8i_tcon_top.h | 2 + drivers/pwm/Kconfig | 14 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-sun50i-a133.c | 276 +++++++ 19 files changed, 2209 insertions(+), 39 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..c3809902c 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 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