Skip to content

Xiaomi Mi MIX 4 (odin, Snapdragon 888+ / SM8350) ​

qine233/UniXNU-odin

Xiaomi Mi MIX 4 (Snapdragon 888+). Inputs and shared-support gaps live in FACTS.md; no hardware value below is measured until the device rows there close.

Source record: porting.toml, 3f8df85bc2670da5619240febb33f47c32ba0bcb.

This page is generated from the device record.

AreaStatus
Configurationplanned
Firmware and bootplanned
CPU and interruptsplanned
Memory and DMAplanned
Storage and roothost-verified
Displayplanned
Inputhost-verified
GPUhost-verified
Networkplanned
Audioplanned
Powerhost-verified
Image assemblyhost-verified
Diagnosticsplanned

Configuration ​

planned

Components: ArmDTPlatform, ArmCpu, SoftwareAES, XnuPrelink, DarwinBoot.

Hardware and dependencies ​

  • Identify the configuration hardware, resources and existing component interfaces for this board.

Configuration ​

  • Record the board.toml fields, DTS properties and defconfig values used for configuration.

Procedure ​

  • Follow the configuration stage in the porting guide and record the actual commands.

Development findings ​

  • Collect the first configuration observation, its cause when established, and the resulting implementation decision.

Completion criteria ​

  • All source pins resolve; every selected component has a record; status shows the intended overrides and patch order.

Current limits ​

  • Hardware inputs and implementation are pending for this example board.

Next action ​

  • Inventory the required configuration inputs before implementation.

Evidence ​

Collect evidence using the next action.

Firmware and boot ​

planned

Hardware and dependencies ​

  • Identify the firmware and boot hardware, resources and existing component interfaces for this board.

Configuration ​

  • Record the board.toml fields, DTS properties and defconfig values used for firmware and boot.

Procedure ​

  • Follow the firmware and boot stage in the porting guide and record the actual commands.

Development findings ​

  • Collect the first firmware and boot observation, its cause when established, and the resulting implementation decision.

Completion criteria ​

  • Firmware image builds, enters UniXNU, publishes the framebuffer and preserves the documented memory reservations.

Current limits ​

  • Hardware inputs and implementation are pending for this example board.

Next action ​

  • Inventory the required firmware and boot inputs before implementation.

Evidence ​

CPU and interrupts ​

planned

Hardware and dependencies ​

  • Identify the cpu and interrupts hardware, resources and existing component interfaces for this board.

Configuration ​

  • Record the board.toml fields, DTS properties and defconfig values used for cpu and interrupts.

Procedure ​

  • Follow the cpu and interrupts stage in the porting guide and record the actual commands.

Development findings ​

  • Collect the first cpu and interrupts observation, its cause when established, and the resulting implementation decision.

Completion criteria ​

  • Every enabled CPU reaches its entry checkpoint and timer/IPI handling works on each enabled CPU.

Current limits ​

  • Hardware inputs and implementation are pending for this example board.

Next action ​

  • Inventory the required cpu and interrupts inputs before implementation.

Evidence ​

Memory and DMA ​

planned

Hardware and dependencies ​

  • Identify the memory and dma hardware, resources and existing component interfaces for this board.

Configuration ​

  • Record the board.toml fields, DTS properties and defconfig values used for memory and dma.

Procedure ​

  • Follow the memory and dma stage in the porting guide and record the actual commands.

Development findings ​

  • Collect the first memory and dma observation, its cause when established, and the resulting implementation decision.

Completion criteria ​

  • All handoff regions are accounted for and each enabled DMA engine accesses its allocated range with the documented cache policy.

Current limits ​

  • Hardware inputs and implementation are pending for this example board.

Next action ​

  • Inventory the required memory and dma inputs before implementation.

Evidence ​

Storage and root ​

host-verified

Hardware and dependencies ​

  • Controller, PHY, clocks, reset and power, SMMU, DMA, handoff, storage layout and OS interface, each with a source and an owner (docs/porting/ufs.md hardware inventory).

Configuration ​

  • board.toml keeps [userdata] commented out on purpose: first_lba and last_lba must come from this unit's own GPT, and userdata.py computes a capacity from them.
  • dts/odin.dts inherits the exported ufshc and ufsphy nodes from sm8350.dtsi; both windows align inside a non-zero 16 MiB arm-io base, so unlike the touch engine the storage driver can live in the kernel.
  • configs/odin_defconfig leaves CONFIG_XNU_NATIVE_UFS unset until a SM8350 storage component exists; native-ufs-root=1 is added with it, never before.

Procedure ​

  • Inventory from the vendor tree, then transcribe the reviewed ShengUfs contract (adopt sequence, UTRD and UPIU packing, completion policy, SMMU takeover, write windows) into package/OdinUfs*.{h,c}.
  • Assert every byte of the descriptors and every write order against a scripted controller and a scripted SMMU on the host: sh package/run-tests.sh.
  • On the device, run the read-only milestones in order: registers, DMA, one command, capacity and known blocks, root mount, persistence.

Development findings ​

  • UFS host window 0x1d84000/0x3000 with inline crypto at 0x1d88000/0x8000 and its HWKM at 0x1d90000/0x9000, GIC_SPI 265, two lanes per direction, QMP phy 0x1d87000/0xe10, apps SMMU stream id 0xE0.
  • Resolved: the board's eUFS is clocked by the GCC_UFS_PHY_* family (CFG 0x77010 AXI, 0x77018 AHB, 0x7705c UNIPRO, 0x77064 ICE, 0x770cc aggregate; RCGs 0x77024 with mnd_width 8, 0x7706c and 0x77084 with mnd_width 0; BCR 0x77000, GDSC 0x77004). The 0x75xxx card family belongs to an external slot. og_ufs_phy_enable() programs it and refuses to write M/N/D into an mnd_width=0 RCG.
  • The host window, the GDSC address and the apps SMMU physical base are identical on SM8550 and SM8350, so the UFS controller IP carries over; the divergence is the PHY window (0x1d80000/0x2000 vs 0x1d87000/0xe10), the stream id (0x60 vs 0xE0), the SMMU compatible (arm,mmu-500 vs qcom,qsmmu-v500) and every GCC branch offset. 0x77018 means AXI on SM8550 and AHB on SM8350: an offset imported by analogy would clock the wrong branch.
  • Two refusals in the upstream SMMU planner are dead ends on this generation, and the sibling SM7475 port proved it on hardware: the candidate-bank scan stops after the top eight, and an exclusive firmware mapping of our own stream onto another bank is refused as a conflict. OdinUfsSmmu.c keeps both refusal strings, scans every bank, and adds a takeover mode that resets the firmware's bank to a clean bypass context after proving no other stream points at it.
  • Completion policy is deliberately unforgiving: a timeout or a fatal interrupt bit quarantines the host and the buffer stays allocated, because the controller may still own that memory with the doorbell back at zero. No cancel, no reset, no retry.

Completion criteria ​

  • Host: descriptor bytes, register write order, completion and quarantine policy and the SMMU plan are pinned by tests against the reviewed contract.
  • Device: registers readable and clock/link state matching the chosen path; buffers reachable and one command completing; capacity and known blocks correct; the configured root mounts and launchd starts; writes survive the documented reboot inside the allocated Data region.

Current limits ​

  • No device run: the transport has never touched a real controller, and the firmware's residue on this unit is unmeasured.
  • The write windows cannot be opened at all until this unit's GPT is read, so the geometry commands in FACTS.md are the next device-side step.
  • Upstream ShengUfs is SM8550-scoped (its GccUfs.h hardcodes that generation's offsets and stream 0x60), so it cannot be selected as a component here; a SM8350 fork carrying the two planner changes above is required before the kernel build can enable the path.

Next action ​

  • Read userdata's first and last 4096-byte LBA and the logical block size from this unit, fill [userdata] in board.toml, then fork ShengUfs with odin's stream and GCC offsets and take the two OdinUfsSmmu behaviours across.

Evidence ​

Display ​

planned

Hardware and dependencies ​

  • Controller and panel identity, the DSI windows and splash region, the brightness transport and its encoding, the tear-check wiring and the enable GPIOs, each with a source.

Configuration ​

  • dts/odin.dts keeps the panel block as documentation until the scanout path exists; the geometry already verified there is 1080x2400 DSC 1.1 command mode at 60 Hz with porches 40/20/10 and 2000/2704/2.
  • package/OdinDisplayBoard.h holds the panel and brightness facts with per-value citations; package/OdinDisplayBacklight.h/.c holds the transport rules, and neither bakes in an 8-bit assumption.
  • uefi-odin records the panel values in Include/RAW.inc; the generated panel XML and the vendor display DXE are still on the firmware-binary side of the boundary.

Procedure ​

  • Read the panel description first-hand and quote it line by line.
  • Read the brightness path in the vendor display driver (techpack/display/msm/dsi/dsi_panel.c) and the DRM helper it calls, and implement the encoding and the holds as a transport-agnostic layer.
  • Assert packets, bounds and hold behaviour on the host with a recorded bus.
  • Then establish firmware scanout, then the kernel backend, then brightness against the real panel.

Development findings ​

  • Panel is 1080x2400 DSC 1.1 command mode (slice 1080x12, 2 per packet, bpc 10, bpp 8) at 60 Hz, backlight over DCS with no dedicated IC, TE on gpio82 (mdp_vsync mux 1), reset on gpio24, vddio and vddd enables on gpio42 and gpio83, ESD error interrupt on gpio75, and an avdd L13C supply.
  • Corrected against the vendor driver: brightness is written as the raw level, 16 bits, bounded only at 0xffff (dsi_panel.c:562-588) - it is NOT scaled into 8 bits, and an earlier revision of OdinDisplayBoard.h claimed it was, which would have made levels above 255 unreachable.
  • This panel declares qcom,mdss-dsi-bl-inverted-dbv (panel dtsi:39), and the driver exchanges the two bytes of the level when that flag is set (dsi_panel.c:583-584). With the DRM helper's low-parameter-first payload (drm_mipi_dsi.c:1057-1061) the two rules cancel, so the level appears big-endian on the link - a port that implements either rule alone produces a plausible-looking but wrong packet.
  • Two holds happen before any transport is touched: an unchanged level is recorded and dropped (dsi_panel.c:659-661), and this panel sets qcom,bl-update-flag = delay_until_first_frame (dtsi:40, parsed at dsi_panel.c:2567), so the first change waits for a queued frame. In command mode both are about real packets on a real link, not cosmetics.
  • odin's panel sets no qcom,bl-dsc-cmd-state, so the driver's default leaves the brightness write in high-speed mode; a panel that declared dsi_lp_mode would be sent in escape mode instead, and the layer models that as state rather than a constant.
  • Board range confirmed with real line numbers: bl_ctrl_dcs, min 2, max 4095, brightness-init-level 100 (xiaomi-sm8350-common.dtsi:282-286). The mi,HBM and clone bands (1..2047, threshold 2047, clone 8191, dtsi:684-701) are recorded but unmodelled because their consumers are in the Xiaomi extras, not in dsi_panel.c.
  • Also confirmed on the way through: qcom,mdss-dsi-te-dcs-command = 1 with te-check-enable and te-using-te-pin, tx-eot-append, lp11-init and panel-mode-switch (panel dtsi:30-38), which is the tear-effect configuration the display stage needs.
  • Open: the 90 and 120 Hz porch sets, and the demura/dc-vi follow-up writes the driver performs after a successful brightness update (dsi_panel.c:588-592).

Completion criteria ​

  • A known frame appears with correct geometry and colors, then userland presentation and backlight controls work.

Current limits ​

  • Only the brightness encoding layer is implemented and host-verified; firmware scanout and the XNU display backend are not, so this area stays planned.
  • The DSI controller windows are [R] from the extraction pass and the display DXE is a vendor binary, so no packet has yet been seen on a link.
  • The AOD 0x51 hold (aod_bl_51ctl) is not modelled because this unit's setting for it is unknown.

Next action ​

  • Establish firmware scanout in uefi-odin, then wire odb_set/odb_first_frame to the real DSI transport and measure brightness against the panel.

Evidence ​

Input ​

host-verified

Hardware and dependencies ​

  • Controller identity, bus pins and clocks, the command set and event framing, the bring-up order, the firmware image path and the contact report contract, each with a source.

Configuration ​

  • board.toml selects no input component: the protocol runs in the firmware because the arm-io 16 MiB base rule cannot export SE11, so QcomGeniSpi is a firmware-side dependency of uefi-odin rather than a kernel component here.
  • package/OdinTouchBoard.h holds the bus, pin, clock and axis facts with [V]/[R] marks; package/OdinDisplayBoard.h holds the panel geometry the axis space is divided against; the orientation is passed to the report layer as an argument because the vendor sets no rotation property on this board.
  • configs/odin_defconfig stays as the minimal set: the HID consumer arrives with the firmware producer, not before.

Procedure ​

  • Extract the command set, framing and timings from the vendor driver with per-line citations (FtsProtocol).
  • Establish the bring-up order from the same tree and encode it as one pipeline function per vendor step (OdinTouchPipeline).
  • Assert the whole path on the host with a scripted bus that replays FIFO records by length: raw bytes in, contacts out (sh package/run-tests.sh).
  • On the device, confirm the first working frame, then measure orientation, then verify the report against the panel.

Development findings ​

  • SE11 sits on wrapper qupv3_1 (stream id 0x43) with the bus on gpio48/49/50 and the chip select split out to gpio51 by the odin pinctrl override; touch reset and irq are gpio22 and gpio23 at 8 mA active drive. The per-pin roles of the three bus lines are not stated by the vendor, so they must be read from the first working frame.
  • Consequence of the 16 MiB arm-io base rule: the touch protocol has to run in firmware, which makes QcomGeniSpi reusable and leaves FtsProtocol as the only new code.
  • Bring-up order, read from the vendor tree rather than inferred: interrupts down, reset line low 10 ms, poll the FIFO for the whole-byte ready event 0x03, then read the 208-byte system information block. The chip id is read only on the failed-reset path, where it decides between no panel and panel-without-firmware. Sensing is switched with {0xA0,0x00,0x01} and off with {0xA0,0x00,0x00}.
  • A defect this port found and fixed in its own earlier code: the ready event had been matched as (byte >> 4) == 0x03, which accepts 0x30 (a leave-point event) and rejects the only byte that means ready, so a successful reset looked like a timeout. The high nibble is the dispatch index (fts.c:4849), the id is the whole byte (ftsCore.c:237).
  • Two defects of the same class came out of wiring the pipeline: a bus failure inside the ready poll was reported as a timeout, hiding a broken serial engine behind a slow-looking reset; and sensing could be started on a controller that never announced itself.
  • Axis space is ten times the panel (10800 x 24000) with super resolution enabled, so the report layer divides by ten and clamps; ten contacts is the ceiling and surface ids are monotonic and never reused, which the pipeline test asserts through a full frame and an eleventh contact.

Completion criteria ​

  • Touches map to the display, simultaneous contacts keep stable IDs, release events arrive, and buttons produce expected HID reports.

Current limits ​

  • No device run: the frames, the order and the report are asserted against a scripted controller, not against the IC.
  • The firmware image (.ftb) for the burn path lives only on the vendor firmware partition, so the panel-without-firmware branch can be detected here but not repaired.
  • Orientation is unmeasured, so it is an argument rather than a constant; the sensor matrix behind the fts,* filter arrays is undecoded, which only affects the axis limits.

Next action ​

  • Read the first working frame on the device to settle the three bus lines' per-pin roles and the orientation, then produce the HID producer in uefi-odin on top of otp_poll.

Evidence ​

GPU ​

host-verified

Hardware and dependencies ​

  • The graphics IP identity and its windows, the power domain and clock owner, the CP packet encoding this generation emits, and the GMU boot sequence, each with a source.

Configuration ​

  • dts/sm8350.dtsi exports gfx 0x3d00000/0x40000 plus 0x3d61000 and 0x3de0000, gmu 0x3d6a000 and kgsl-smmu 0x3da0000; the firmware's own ACPI table for the same silicon declares one MON0 device covering 0x3d00000, 0x3d60000, 0x3d90000 and 0x3de0000, so the merged coverage and the ADT tuples agree.
  • The GPU clock controller is at 0x3d90000 (OG_GPU_CC_BASE) with CX/GX domains at 0x3d9106c and 0x3d9100c: they are inside the graphics window, not the GCC's, so they cannot be programmed through a GCC mapping.
  • AdrenoFirmware is the only reusable catalog component here (it is SoC-neutral); Adreno740 and Sm8550Platform are SM8550-scoped and are not applicable to a 660.

Procedure ​

  • Inventory first-hand from the vendor tree (already done: chipid, windows, GMU, PDC, ZAP, power domains).
  • Build the packet layer against the encoding the vendor driver actually emits, asserted on the host, before touching a submission path.
  • Then the GMU boot sequence, then a render, then presentation.

Development findings ​

  • Adreno 660 (A650 family), chipid 0x06060000, with GMU 0x3d6a000, PDC 0x0b290000 and the ZAP blob a660_zap loaded at pil_gpu 0x8e71a000.
  • Correction that saves a whole module: this generation does not emit PM4 type-3 packets. The vendor driver builds every CP command with cp_type7_packet (adreno_pm4types.h:188-203, used at adreno_a6xx.c:1008 CP_ME_INIT, 1120 CP_SET_PSEUDO_REGISTER, 1133 CP_CONTEXT_SWITCH_YIELD, 1255 CP_SET_MARKER), so the existing type-4/type-7 coverage is the right base and a type-3 path would be dead code.
  • Type-7 header encoding is count in bits[14:0] with an odd-parity bit at 15, opcode in bits[22:16] with an odd-parity bit at 23; type-4 adds an 18-bit register index with its own parity. The builder therefore has to compute parity, not copy a literal, and the tests must pin it.
  • Two structural requirements visible in the same code and easy to miss: every 64-bit GPU address is written through cp_gpuaddr as a two-word low/high split, and the CP can be in protected mode - the driver brackets address-writing packets with cp_protected_mode(0) and cp_protected_mode(1). A builder that ignores either produces a packet stream the CP rejects.
  • CP_ME_INIT is emitted as a single packet whose payload size is A6XX_CP_INIT_DWORDS - 1 with an ordinal mask and conditionally present fields, so the field count is derived from the mask rather than fixed.

Completion criteria ​

  • GPU submissions complete, mapping/fence handling is correct, a known image renders, and the selected Metal client presents it.

Current limits ​

  • No implementation yet: the packet builder and the GMU boot path are the next pieces of work, and the GX power domain has never been brought up on this port.
  • The ZAP firmware blob and the GMU/OOBP images are vendor binaries and are not in these repositories, so the GMU path cannot be exercised before they are obtained from the unit.

Next action ​

  • Take the GMU bring-up sequence from a6xx_gmu.c on top of this builder, then the ringbuffer start, then a render.

Evidence ​

Network ​

planned

Hardware and dependencies ​

  • Identify the network hardware, resources and existing component interfaces for this board.

Configuration ​

  • Record the board.toml fields, DTS properties and defconfig values used for network.

Procedure ​

  • Follow the network stage in the porting guide and record the actual commands.

Development findings ​

  • Collect the first network observation, its cause when established, and the resulting implementation decision.

Completion criteria ​

  • Scanning, association, address configuration and sustained receive/transmit traffic pass with the selected firmware.

Current limits ​

  • Hardware inputs and implementation are pending for this example board.

Next action ​

  • Inventory the required network inputs before implementation.

Evidence ​

Collect evidence using the next action.

Audio ​

planned

Hardware and dependencies ​

  • Identify the audio hardware, resources and existing component interfaces for this board.

Configuration ​

  • Record the board.toml fields, DTS properties and defconfig values used for audio.

Procedure ​

  • Follow the audio stage in the porting guide and record the actual commands.

Development findings ​

  • Collect the first audio observation, its cause when established, and the resulting implementation decision.

Completion criteria ​

  • Each declared route carries the selected audio format through playback or capture and handles start/stop correctly.

Current limits ​

  • Hardware inputs and implementation are pending for this example board.

Next action ​

  • Inventory the required audio inputs before implementation.

Evidence ​

Collect evidence using the next action.

Power ​

host-verified

Hardware and dependencies ​

  • Every rail each subsystem is fed from, with its voltage, load, enable GPIO, boot-on state and any parent supply, each quoted from the board device tree.

Configuration ​

  • The rails stay described in the device tree as the vendor writes them; package/OdinPowerBoard.h is where the port keeps the ordering and the gates, because XNU has no regulator framework to read the DT phandles.
  • dts/sm8350.dtsi's ufs node now carries the vcc/vccq/vccq-parent note next to the controller it feeds, so the dependency is visible where the controller is described.
  • No regulator node is added to the board DTS: these are consumer properties of the Linux drivers, and exporting them into the ADT would describe a device the port does not have.

Procedure ​

  • Read the supply properties out of the board and overlay dtsi files with line numbers.
  • Transcribe them as a table, leaving a value at zero where the tree declares none instead of filling it in.
  • Encode the parent-before-child rule and the three subsystem gates, and assert every row against the file it came from.

Development findings ​

  • Storage: vcc = pm8350_l7 at 2504000..2950000 uV, 800000 uA; vccq = pm8350_l9, 900000 uA with no level declared on this board; and vccq's qcom,vccq-parent-supply = pm8350_s12 at 210000 uA (xiaomi-sm8350-common.dtsi:124-138). The parent is the part a port cannot see from the controller node, and enabling l9 without it powers an rail from a source that is not on.
  • Display: dsi_panel_pwr_supply_k8 is an ordered list - vddio then vddd then avdd - with 1800000/1800000/3000000 uV, enable loads 60700/60700/10000 uA, disable loads 80/80/0 and a two-millisecond pre-off and post-on sleep on every entry (display/lahaina-sde-display-common.dtsi); the rails themselves are display_prim_vddi on tlmm 42, display_prim_vddd on tlmm 83, both regulator-boot-on, and L13C (odin-sm8350.dtsi:37-49, 412-414).
  • Input: touch vdd = L2C with no level or load stated on this board, avdd = touch_avddsource_vreg, a fixed regulator on tlmm 132 at 3008000 uV, regulator-boot-on and regulator-always-on (odin-sm8350.dtsi:92-93, 81-85, 55-58).
  • Because two of the fixed rails are boot-on, a warm handoff from vendor firmware has them already enabled; the state model starts from that instead of demanding GPIO writes that have happened, and the gates still refuse anything whose LDO is down.
  • Two rails are deliberately recorded with zeros: vccq's level and L2C's level and load are not declared by this board, so the module carries them as undeclared and no consumer treats zero as a default.

Completion criteria ​

  • Host: the rail table, the parent ordering and the three gates are asserted against the device tree rows they came from.
  • Device: declared power controls and battery readings match observations; each claimed sleep/resume transition restores its dependent peripherals.

Current limits ​

  • No PMIC access path yet: this layer describes which rails, in which order and with what loads, but the writes go through a driver that does not exist for SM8350 here.
  • Battery, charger and gauge are not inventoried at all, and suspend/resume sequencing is untested, so the second half of this area stays open.
  • The loads are the values the board declares, not measurements; a rail shared with a subsystem this port has not examined may carry more than these rows say.

Next action ​

  • Wire opb_enable and the three gates into the firmware's PMIC sequencing before the first storage or display attempt, then inventory the battery and charger nodes.

Evidence ​

Image assembly ​

host-verified

Hardware and dependencies ​

  • Identify the image assembly hardware, resources and existing component interfaces for this board.

Configuration ​

  • Record the board.toml fields, DTS properties and defconfig values used for image assembly.

Procedure ​

  • Follow the image assembly stage in the porting guide and record the actual commands.

Development findings ​

  • Collect the first image assembly observation, its cause when established, and the resulting implementation decision.

Completion criteria ​

  • Kernel and prelink succeed, payload contains all declared inputs, and the boot image matches the bootloader format and size limit.

Current limits ​

  • Verified up to the build boundary only: no kernel, payload, firmware or boot image has been produced, because the device inputs above are still unmeasured and the firmware binaries are not extracted.
  • The probe copy is where the check ran; the committed board still refuses release, deliberately.

Next action ​

  • Inventory the required image assembly inputs before implementation.

Evidence ​

Diagnostics ​

planned

Hardware and dependencies ​

  • Identify the diagnostics hardware, resources and existing component interfaces for this board.

Configuration ​

  • Record the board.toml fields, DTS properties and defconfig values used for diagnostics.

Procedure ​

  • Follow the diagnostics stage in the porting guide and record the actual commands.

Development findings ​

  • Collect the first diagnostics observation, its cause when established, and the resulting implementation decision.

Completion criteria ​

  • Each claimed milestone has a reproducible command, a matching source/image revision and an accessible result.

Current limits ​

  • Hardware inputs and implementation are pending for this example board.

Next action ​

  • Inventory the required diagnostics inputs before implementation.

Evidence ​

Released under the BSD-2-Clause-Patent License.