diff --git a/Documentation/devicetree/bindings/dma/apple,sio.yaml b/Documentation/devicetree/bindings/dma/apple,sio.yaml index dbaaa2d3d1a1a..495adc52c7e6e 100644 --- a/Documentation/devicetree/bindings/dma/apple,sio.yaml +++ b/Documentation/devicetree/bindings/dma/apple,sio.yaml @@ -19,12 +19,15 @@ allOf: properties: compatible: - items: - - enum: - - apple,t6000-sio - - apple,t6030-sio - - apple,t8103-sio - - const: apple,sio + oneOf: + - items: + - enum: + - apple,t6000-sio + - apple,t6030-sio + - apple,t8103-sio + - const: apple,sio + - description: T6041 firmware requires a generation-specific backend. + const: apple,t6041-sio reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/gpu/apple,agx.yaml b/Documentation/devicetree/bindings/gpu/apple,agx.yaml index c0a32c3badd07..f5b1afd7d7dd3 100644 --- a/Documentation/devicetree/bindings/gpu/apple,agx.yaml +++ b/Documentation/devicetree/bindings/gpu/apple,agx.yaml @@ -72,6 +72,22 @@ properties: - enum: [hw-cal-b, fw-text, l2-pagetables] - enum: [globals, fw-data, fw-text] + apple,clpc: + $ref: /schemas/types.yaml#/definitions/phandle + description: | + Host performance controller receiving GPU submission and idle demand. + GFX supplier power cannot represent GPU work on firmware that keeps + its coprocessor domain powered while the cores are idle. + + apple,idle-off-delay-ms: + minimum: 2 + maximum: 1000 + default: 2 + description: + Continuous idle time before GPU power-off, in milliseconds. The G17 + backend extends the firmware's native delay with its host idle-hold + control; an absent property preserves the native policy. + apple,pmp: $ref: /schemas/types.yaml#/definitions/phandle description: PMP firmware provider owning the GPU power vote and telemetry @@ -83,9 +99,9 @@ properties: minItems: 3 maxItems: 3 description: - macOS version (major, minor, patch) whose boot firmware loaded the GPU - firmware, which identifies the firmware build. Filled in by the boot - loader. + Reported macOS version (major, minor, patch) whose boot firmware loaded + the GPU firmware. Filled in by the boot loader. This is a selection + hint; the T6040 driver requires the exact loaded image identity. apple,cs-opp: $ref: /schemas/types.yaml#/definitions/phandle @@ -99,6 +115,75 @@ properties: Operating-point table of the GPU's AFR clock domain, one voltage cell per state. + apple,t6040-handoff-version: + $ref: /schemas/types.yaml#/definitions/uint32 + const: 2 + description: + Written last by the boot loader after it has filled the T6040 operating + points, memory regions and per-chip calibration. Absent in the source DT. + + apple,core-mask: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Enabled cores in the two ten-core T6040 clusters, low cluster first + + apple,max-power-mw: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Per-chip maximum GPU power in milliwatts, calculated by the loader + + apple,perf-base-pstate: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Logical base state of the frequency-relative performance table. T6040 + supports the first active state, also used when its ADT omits the property. + + apple,perf-map-a: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + All 16 A selector register fields, indexing raw ADT operating points. + Eleven populated entries, including off, followed by five zeros. + + apple,perf-map-b: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + All 16 B selector register fields, indexing raw ADT operating points. + Eleven populated entries, including off, followed by five zeros. + + apple,perf-relative-a: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + Per-chip relative power of each populated A selector, in percent, + followed by zeros for the five unused selectors. + + apple,perf-relative-b: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + Relative frequency of the populated A ladder above the base state, + in percent, followed by zeros for the five unused selectors. + + apple,core-leak-coef: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + GPU core leakage, one coefficient per cluster. Per-chip efuse values decoded by the boot loader, + stored as IEEE-754 binary32 bit patterns in big-endian cells. + + apple,sram-leak-coef: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + GPU SRAM leakage, one coefficient per cluster. Per-chip efuse values decoded by the boot loader, + stored as IEEE-754 binary32 bit patterns in big-endian cells. + + apple,cs-leak-coef: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + CS leakage. Per-chip efuse values decoded by the boot loader, + stored as IEEE-754 binary32 bit patterns in big-endian cells. + + apple,afr-leak-coef: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + AFR leakage. Per-chip efuse values decoded by the boot loader, + stored as IEEE-754 binary32 bit patterns in big-endian cells. + apple,firmware-abi: $ref: /schemas/types.yaml#/definitions/uint32-array minItems: 3 @@ -190,6 +275,38 @@ allOf: const: apple,agx-t6040 then: properties: + apple,core-mask: + minimum: 1 + maximum: 1048575 + apple,max-power-mw: + minimum: 1 + maximum: 200000 + apple,perf-base-pstate: + const: 1 + apple,perf-map-a: + minItems: 16 + maxItems: 16 + apple,perf-map-b: + minItems: 16 + maxItems: 16 + apple,perf-relative-a: + minItems: 16 + maxItems: 16 + apple,perf-relative-b: + minItems: 16 + maxItems: 16 + apple,core-leak-coef: + minItems: 2 + maxItems: 2 + apple,sram-leak-coef: + minItems: 2 + maxItems: 2 + apple,cs-leak-coef: + minItems: 1 + maxItems: 1 + apple,afr-leak-coef: + minItems: 1 + maxItems: 1 reg: items: - description: GFX coprocessor control registers diff --git a/Documentation/devicetree/bindings/input/apple,mesa-fingerprint.yaml b/Documentation/devicetree/bindings/input/apple,mesa-fingerprint.yaml index 53b304bcec7ff..631ba47a5a3ca 100644 --- a/Documentation/devicetree/bindings/input/apple,mesa-fingerprint.yaml +++ b/Documentation/devicetree/bindings/input/apple,mesa-fingerprint.yaml @@ -57,11 +57,17 @@ properties: description: Rail selector words, in power-on order. firmware-name: - maxItems: 1 + minItems: 1 + items: + - description: + Calibration blob for this sensor. + - description: + Signed root-key container, for sensors whose enclave session needs + one. description: - Calibration blob for this sensor. It is per-device factory data written - at manufacture, so it is neither redistributable nor derivable from the - compatible; the board description has to name it. + Per-device factory data written at manufacture, so neither + redistributable nor derivable from the compatible; the board + description has to name it. spi-cpha: true diff --git a/Documentation/devicetree/bindings/media/apple,t8140-ave.yaml b/Documentation/devicetree/bindings/media/apple,t8140-ave.yaml index 110ebbc3d09e2..61e10feb5df71 100644 --- a/Documentation/devicetree/bindings/media/apple,t8140-ave.yaml +++ b/Documentation/devicetree/bindings/media/apple,t8140-ave.yaml @@ -4,19 +4,21 @@ $id: http://devicetree.org/schemas/media/apple,t8140-ave.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Apple T8140 video encoder +title: Apple video encoder maintainers: - Miguel Cruz <7362641+Acelogic@users.noreply.github.com> description: | - The T8140 AVE coprocessor encodes video using firmware preloaded by the + The T6041 and T8140 AVE coprocessors encode video using firmware preloaded by the bootloader. Its two DART instances translate the same stream. The bootloader supplies two reserved firmware regions and their IOVA mappings. properties: compatible: - const: apple,t8140-ave + enum: + - apple,t6041-ave + - apple,t8140-ave reg: items: diff --git a/Documentation/devicetree/bindings/net/wireless/brcm,bcm4329-fmac.yaml b/Documentation/devicetree/bindings/net/wireless/brcm,bcm4329-fmac.yaml index 81fd3e37452a6..8f935a2d2b320 100644 --- a/Documentation/devicetree/bindings/net/wireless/brcm,bcm4329-fmac.yaml +++ b/Documentation/devicetree/bindings/net/wireless/brcm,bcm4329-fmac.yaml @@ -46,6 +46,7 @@ properties: - cypress,cyw4373-fmac - cypress,cyw43012-fmac - infineon,cyw43439-fmac + - pci14e4,4434 # BCM4388 - const: brcm,bcm4329-fmac - enum: - brcm,bcm4329-fmac diff --git a/Documentation/devicetree/bindings/npu/apple,ane.yaml b/Documentation/devicetree/bindings/npu/apple,ane.yaml index 79cf942654f7b..feae5b133ac2f 100644 --- a/Documentation/devicetree/bindings/npu/apple,ane.yaml +++ b/Documentation/devicetree/bindings/npu/apple,ane.yaml @@ -15,7 +15,9 @@ description: drives the engine through its register window. On T602x and T8112 a coprocessor runs the ANE firmware and the host talks to it through an ASC mailbox. T8140 has two interrupts and one system power domain; its - coprocessor uses a different firmware interface. + coprocessor uses a different firmware interface. T6041 likewise needs a + generation-specific firmware backend; it has one interrupt and its CPU + power state supplies the engine through the ANE system domain. properties: compatible: @@ -25,6 +27,7 @@ properties: - apple,t6020-ane - apple,t6021-ane - apple,t6022-ane + - apple,t6041-ane - apple,t8112-ane - apple,t8140-ane @@ -87,19 +90,35 @@ required: - power-domains allOf: + - if: + properties: + compatible: + const: apple,t8140-ane + then: + properties: + interrupts: + minItems: 2 + - if: + properties: + compatible: + const: apple,t6041-ane + then: + properties: + interrupts: + maxItems: 1 - if: properties: compatible: contains: - const: apple,t8140-ane + enum: + - apple,t8140-ane + - apple,t6041-ane then: properties: reg: maxItems: 1 reg-names: maxItems: 1 - interrupts: - minItems: 2 interrupt-names: false iommus: minItems: 3 diff --git a/Documentation/devicetree/bindings/sound/apple,macaudio.yaml b/Documentation/devicetree/bindings/sound/apple,macaudio.yaml index fdf3d1883a99d..755c5d195cf0f 100644 --- a/Documentation/devicetree/bindings/sound/apple,macaudio.yaml +++ b/Documentation/devicetree/bindings/sound/apple,macaudio.yaml @@ -36,6 +36,15 @@ properties: Model name for presentation to users $ref: /schemas/types.yaml#/definitions/string + speaker-id-gpios: + minItems: 1 + maxItems: 8 + description: + Inputs encoding the fitted speaker assembly variant, least significant + bit first. The machine driver exposes their logical levels through the + read-only Speaker Variant ID control. GPIO polarity flags define each + bit's logical level. + patternProperties: "^dai-link(@[0-9a-f]+)?$": description: | diff --git a/Documentation/devicetree/bindings/spi/apple,qspi.yaml b/Documentation/devicetree/bindings/spi/apple,qspi.yaml index 39924afc9a8b6..0b98af1ad68d2 100644 --- a/Documentation/devicetree/bindings/spi/apple,qspi.yaml +++ b/Documentation/devicetree/bindings/spi/apple,qspi.yaml @@ -22,6 +22,7 @@ properties: enum: - apple,t8122-qspi - apple,t8132-qspi + - apple,t6041-qspi - apple,t8140-qspi reg: diff --git a/Documentation/devicetree/bindings/spi/apple,spi.yaml b/Documentation/devicetree/bindings/spi/apple,spi.yaml index 4b30265686fa1..be47c5f9ff075 100644 --- a/Documentation/devicetree/bindings/spi/apple,spi.yaml +++ b/Documentation/devicetree/bindings/spi/apple,spi.yaml @@ -18,6 +18,7 @@ properties: - items: - enum: - apple,t6020-spi + - apple,t6041-spi - apple,t8140-spi - const: apple,t8103-spi - items: diff --git a/Documentation/driver-api/media/drivers/apple-isp-j616s.rst b/Documentation/driver-api/media/drivers/apple-isp-j616s.rst new file mode 100644 index 0000000000000..9a2bf93d7b059 --- /dev/null +++ b/Documentation/driver-api/media/drivers/apple-isp-j616s.rst @@ -0,0 +1,322 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +J616s ISP modes and capture protocol +==================================== + +Sources and scope +----------------- + +The source reference is Ryan's m1n1 PR 20, branch ``j616s`` at +``9be380c8a5a94609fdbe62d693c7b2121478ad8f``: + +* ``proxyclient/m1n1/fw/isp/isp_sensor.py`` discovers IMX958 and queries + ``CH_CAMERA_CONFIG_GET`` for every configuration reported by ``CH_INFO_GET``. +* ``isp_capture.py:T6040PreviewConfiguration`` contains the successful + processed capture command sequence. ``isp_pixels.py`` decodes its P010 + output as video-range BT.709. +* ``docs/j616s-bringup.md`` records all 13 configuration queries, completed + 1280x720 P010 captures (including an 80-frame ring-wrap trial) and a later + single completed 1920x1080 P010 frame. Intermittent startup/streaming stalls + remain unresolved. Neither the requested AE limits nor the host capture + rate is a measured sensor FPS. + +Both processed output sizes use firmware configuration **5**, whose reported +input dimensions are 1920x2160. The branch does not establish that this input +height is an independently deliverable 2160-row colour image, nor that config +5 is the highest-resolution configuration the sensor offers. It contains no +completed colour result from another configuration. RAW trials produced +stable pixels after stop but no completed RAW report; combined RAW/YUV was +refused at the allocation limit. No sensor full-resolution claim follows. + +Linux PR 231, branch ``j616s/linux-integration-next`` at +``a4e7ee86bc5eed8ff9dab61ef9656adfe000b207``, has the corresponding +``isp-profile.[ch]`` and IPC/buffer preparation. Its T6040 profile is pinned +to **25G76** and remains gated. It is a protocol reference, not an admission +profile for the current firmware. + +This driver change builds on the working T6040 H17 implementation in Linux +PR 384, now in ``integration/aurora``. That PR reports 26.6.x firmware capture +at 1920x1080 NV12 and use of config-5 1080p/720p presets. P010 and additional +firmware-derived output modes still need capture validation on 26.6.2. +Only our source and synthetic protocol tests are included; no Apple binary, +setfile contents, calibration table or firmware image is added. + +Public source audit +------------------- + +The pinned PR and its discussion were checked again for the RAW layout and +per-configuration timing. The reusable code establishes the processed path; +it does not contain a RAW Bayer packing/CFA contract or the 13 reply records: + +* ``isp_capture.py`` accepts only 1280x720 and 1920x1080 processed geometry. + Its generic pool-3 builder describes a single address, not the pixel packing, + CFA order or RAW receiver command sequence. The qualified preview uses pool 9. +* ``isp_pixels.py`` contains only an MSB-aligned P010 decoder, with no Bayer decoder. +* ``isp_sensor.py`` reads the sensor ID/count and queries an index at runtime. + It has neither saved per-index replies nor a frame-rate decoder. +* ``isp_profile.py`` and ``fixtures/isp_t6040_25g76.json`` describe boot segments, + allocation geometry and channels. The fixture has no camera-info or config replies. +* ``isp_vid.py`` and ``experiments/isp.py`` use the older fixed 720p NV12 receiver. + ``isp_cmd.py`` includes legacy sensor-0x248 examples and a temporary 3.75 FPS + AE request (0x3c0), alongside a comment describing the older 30 FPS request. + These are not IMX958 per-mode timing records. The qualified T6040 helper instead + requests 30/15 FPS AE limits; neither set supplies a measured nominal-rate table. +* ``isp_command.py`` implements ownership, command/reply and deadline handling. + ``src/isp.c`` prepares boot-time power and heap handoff, not a RAW capture client. +* ``docs/j616s-bringup.md`` lines 752-789 explicitly distinguish a stable RAW + snapshot after stop from a completed buffer report and say orchestration remains + private. Commit ``743e4e75`` adds that diagnostic RAW evidence as documentation; + it does not add a RAW receiver implementation. Lines 832-841 qualify one + completed 1080p P010 frame, with measured FPS still unqualified. + +These findings do not deny the recorded RAW snapshot. They mean a V4L2 Bayer +FOURCC, frame-completion contract and per-config rates cannot be derived from +these files without additional runtime replies or capture evidence. In particular, +the old 0x103 SIF example must not be treated as an IMX958 Bayer-format declaration. + +AOP endpoints and separate audio stream +----------------------------------------- + +The modern ``fw/aop/client.py`` map in PR 20 is 0x20 SPUApp, 0x21 wakehint, +0x22 audio, 0x23 voice trigger, 0x24 accelerometer, 0x25 gyroscope, 0x26 lid +angle, 0x27 ALS and four endpoints at 0x28-0x2b with unqualified roles. The +separate ENDPOINTS13 map assigns different roles and is not the T6040 map. + +The documented HQ audio sequence uses the advertised aop-audio service at +endpoint 0x22, EPIC version 2, checked attachment/property replies and owned +DART mappings. It first establishes LP readiness using adpx buffer property 300, +attachments lpai/pdm0/adpx, lpai pw1, lai attachment and lai/lpai runn. HQ then +attaches hpai, requests pw1, prepares stream-10 NS RX0 DMA and starts DMA before +requesting hpai pwrd. Matching reports and two full copies precede RX stop and +return through pw1 to idle. These are audio commands, not camera mode selectors. + +The qualified ISP preview helper contains no AOP command. This camera change +therefore does not replay the AOP audio stream or alter its Linux driver. The +ISP channel and command maps below describe the camera path independently. + +Mode and format selection +------------------------- + +On T6040/IMX958, probe queries every advertised configuration, bounded at 64, +using the 0x120-byte H17 ``CH_CAMERA_CONFIG_GET`` exchange. The complete list +is available through dynamic debug at probe and cached in full for +``VIDIOC_LOG_STATUS`` (``v4l2-ctl --log-status``), without sending firmware +commands. Collect this diagnostic dump before starting a stress run. It includes +input/output dimensions and the sensor clock word; that word is not FPS. + +The board's existing config-5 1920x1080 and 1280x720 presets stay first and +NV12 stays the default. Additional distinct output sizes are appended only +when the reply's 16-bit and repeated 32-bit dimensions agree, the dimensions +are nonzero, output is even and within input, and output is at most 16384 +in either dimension. The full reported output becomes the crop and output +size for its own config index. Input dimensions alone never add a mode. +Thus a larger consistent firmware output can be selected by ``S_FMT`` and +listed by ``ENUM_FRAMESIZES``; no invented full-resolution preset is added. +These consistency checks cannot establish image quality, supported FPS or +whether a configuration needs additional sensor-specific commands. + +``V4L2_PIX_FMT_P010`` is offered only for T6040/IMX958. The config-5 +1280x720 and 1920x1080 processed previews are enabled when +the runtime input geometry matches the source profile. An unspecified P010 +size selects 1280x720 first. Larger P010 modes require a consistent firmware +output reply. P010 uses two little-endian 16-bit component planes with ten useful +MSB-aligned bits, and equal 64-byte-aligned Y/UV strides. UV immediately +follows Y, as required for contiguous P010. Both NV12 and P010 use the +standard single-planar V4L2 API with one memory plane containing both +component planes. The firmware still receives two component addresses. + +At 1920x1080, stride is 3840, Y is 4147200 bytes, UV is 2073600 bytes and +``sizeimage`` is 6220800. At 1280x720, stride is 2560 and ``sizeimage`` is +2764800. The proxy's independently page-aligned plane extents were +0x3f8000/0x1fc000 at 1080p and 0x1c4000/0xe4000 at 720p; inserting those +inter-plane gaps into a V4L2 contiguous P010 buffer would misdescribe it. +Linux passes its actual contiguous extents to the rendered pool instead. + +P010 uses the proxy's eight metadata slots (0x8000 bytes each) and two +rendered slots. The queue limits userspace to two rendered buffers, matching +the pool capacity. Completion matches either both full submitted addresses +or both zero-extended low-32-bit plane addresses, plus plane count, pool 9 +and a fresh 64-bit submission tag; an old tag +cannot release a new lease. Buffer completion is published to videobuf2 +only after the firmware report ACK and doorbell have been published. +NV12 pool sizes and the T8140/Neo default formats, presets and command path +retain their existing behavior. + +The inherited ``multiplanar`` module parameter and its inconsistent queue-type +hooks are removed. All sensors use the shipped single-planar V4L2 API; the +non-contiguous NV12M alias is not advertised. No driver +module/kernel parameter, platform flag or per-platform Kconfig option selects +support. NV12 stays first and is the initial format because browsers/WebRTC +and many applications do not support P010 or simply choose the first format. +Applications can select P010 explicitly with ``VIDIOC_S_FMT``. + +Endpoints +--------- + +These are the seven shared-memory ISP channels, not AOP/RTKit endpoints. +The table records the 25G76 source fixture. The current driver negotiates +channel names, types, sources, counts and 64-bit addresses on each boot; +it does not impose the captured ring addresses or counts on 26.6.2. + +================= ==== ====== ===== ======================================= +Channel Type Source Slots Purpose +================= ==== ====== ===== ======================================= +TERMINAL 2 0 768 Firmware log reports / terminal ACKs +IO 0 1 8 Host commands and in-place replies +DEBUG 0 1 8 Discovered; no capture commands sent +BUF_H2T 0 2 64 Host metadata/output descriptor batches +BUF_T2H 1 3 64 Firmware buffer completion/return reports +SHAREDMALLOC 1 3 8 Firmware allocation/free requests +IO_T2H 1 3 8 Discovered; no capture handler installed +================= ==== ====== ===== ======================================= + +A source word selects a doorbell bit, so IO and DEBUG share bit 1; the +three reply channels share bit 3. Each channel descriptor is 0x100 bytes +with a u64 ring address at 0x50. Each slot is 0x40 bytes / eight u64 words. +Publish the payload before arg0, which transfers slot ownership. The +existing IPC parser bounds rings against the allocation, descriptor table, +boot/command storage and other rings. The threaded IRQ services allocations, +terminal messages and buffer reports while a command waits for its ACK. + +Command stream +-------------- + +IO commands have a zero/status u32 followed by the opcode u32, then the +parameters below. Sizes include that eight-byte header. ``ch`` is zero. + +====== ================================== ===== ======================= +Opcode Command Size Parameters +====== ================================== ===== ======================= +0x0003 CONFIG_GET 0x1c Camera channel count +0x3206 SET_DSID_CLR_MULTI_BC_REG_BASE 0x50 8 slots, range 0x2fc +0x0000 START 0x0c Proxy 0x11, Linux 0 +0x010d CH_INFO_GET 0x190 ch +0x0106 CH_CAMERA_CONFIG_GET 0x120 ch, index (all) +0x0024 FLICKER_SENSOR_SET 0x0c 0 +0x013b CH_SBS_ENABLE 0x10 ch, 1 +0x0107 CH_CAMERA_CONFIG_SELECT 0x10 ch, selected config +0x010e CH_BUFFER_RECYCLE_MODE_SET 0x10 ch, 1 (empty only) +0x010f CH_BUFFER_RECYCLE_START 0x0c ch +0x080c CH_CROP_SCL1_SET 0x1c ch, x, y, width, height +0x0b09 CH_OUTPUT_CONFIG_SCL1_SET 0x38 ch, output geometry +0x0208 CH_AE_FRAME_RATE_MAX_SET 0x10 ch, fps * 256 +0x020a CH_AE_FRAME_RATE_MIN_SET 0x10 ch, min_fps * 256 +0x0117 CH_BUFFER_POOL_CONFIG_SET (meta) 0x9c ch, pool 0, count 8 +0x0117 CH_BUFFER_POOL_CONFIG_SET (output) 0x9c ch, pool 9, count 2 +0x0125 CH_LOCAL_RAW_BUFFER_ENABLE 0x10 ch, 1 +0x0b0d CH_PREVIEW_STREAM_SET 0x10 ch, 1 +0x0138 CH_MASTER_SLAVE_SYNC_MODE_SET 0x10 ch, 0 +0x0100 CH_START 0x0c ch +0x0101 CH_STOP 0x0c ch +0x0104 CH_BUFFER_RETURN 0x0c ch +====== ================================== ===== ======================= + +The DSID bases are 0x2201d4000, 0x2221d4000, 0x2241d4000, 0x2261d4000, +then four U64_MAX sentinels. Linux retains its existing boot-time PMP and +logging commands in addition to discovery; it does not replay the proxy's +boot allocations or private power orchestration. + +For config 5, crop is (0, 0, output width, output height). The output command +parameters after ch are width, height, Rec.709=1, **format=0x12**, Y stride, +UV stride, 0, 0, height, 0, width. NV12 continues to use format 0. +Metadata pool sizes/strides are 0x8000, one data block; the output pool +contains actual Y/UV sizes and strides, two data blocks, no compression. + +Before CH_START, BUF_H2T submits metadata and output buffers as a 16-byte +header (type 1, count) and 64-byte descriptors. Each descriptor contains +four u64 addresses, four u32 flags (0x40000000 for each used plane), +u32 plane count, u32 pool and u64 tag. BUF_T2H returns completion reports; +render status 0x10000000 means a completed frame. Submission status is +0x30000000 and return ACK status is 0x80000000. Requeue only a returned lease. +CH_STOP and CH_BUFFER_RETURN retire the stream; resident firmware remains +running for the existing reopen path. + +The proxy used AE max/min 7680/3840 (30/15 fps). Linux retains its V4L2 rate +and exposure-priority controls: the default holds 30/30, and enabling +``exposure_auto_priority`` allows 30/15. These are requested limits, not FPS +measurements. P010 follows the minimal proxy sequence without adding +setfile-load, CNR, MBNR, SIF-format or temporal-filter commands; NV12 keeps +its existing calibrated path. A previously loaded setfile may still be +resident when switching streams. + +26.6.2 comparison +----------------- + +The 25G76 firmware UUID differs from the 26.6.2 image. Matching struct sizes +is source-level ABI evidence, not a new live qualification of P010. + +* Boot block: both use 0x290 bytes, IPC address at 0x08, shared base/size at + 0x10/0x18, extra heap at 0x20/0x28, platform at 0x30, queue offset+1 at + 0x50, 0x40 at 0x68, camera scheme at 0xd0 and the no-optical-card flag + at 0x120. The current H17 path checks descriptor flag bit 1. +* Memory: 25G76 pinned TEXT/DATA sizes 0xca8000/0x1344000, remap 1<<40, + shared base 0x1fec000, and a 256 MiB shared aperture. Linux uses the + bootloader's current TEXT/DATA/main-memory reservations, backs the missing + main-memory tail, and retains T6040's **0xde600000** shared-size descriptor. + No pinned segment size, heap request, IOVA or firmware hash is ported. +* IPC: 0x100-byte descriptors, 0x40-byte slots, 64-bit addresses and the + seven names agree. Counts and addresses remain negotiated, with the + existing overlap checks retained. The driver uses its three-DART IOMMU + domain and bootloader address-filter setup, not proxy DART table writes. +* Commands: DSID's 0x50-byte eight-slot layout, + CH_INFO_GET's 0x190-byte record, scaler-1 crop/output, 0x9c-byte pools, + local RAW, preview, sync and channel start/stop layouts agree with the + current H17 builders. START is an important parameter difference: the + proxy sends 0x11, while the working Linux H17 boot path sends 0. Linux + retains its current boot command; issuing START a second time after + selecting P010 would violate the proxy's guarded startup lifecycle. The new config query explicitly exchanges 0x120, + rather than the unused legacy 0xdc current-config helper. Header/channel/ + index checks and duplicated geometry checks guard decoding. The H17 + appended bytes remain uninterpreted. The old preview validator also checks + CH_INFO_GET byte 0x138; the current Linux helper retains only the common + 0x118-byte prefix of its 0x190-byte reply and does not make that check. +* Metadata: CH_INFO_GET reports 0x4d00 on both paths. The proxy rounds its + metadata backing/pool to 0x8000; P010 does the same even on a 4 KiB host. + NV12 retains the current driver's 0x4d00 pool and 16-buffer capacity. + Neither path configures a T6040 capture-metadata pool or capture watchdog. +* Startup: Linux retains its once-per-boot resident-firmware admission, + current reset-vector check and H17 handshake; the 25G76 SHA/UUID admission + and recovery model from PR 231 are not substituted. Linux's command + timeout/IRQ service remain in use; the proxy's 30-second inactivity / + 40-second wall startup budget is not copied. + +Validation on hardware must record the current config list first, then +exercise both P010 presets, requeue, stop/reopen and a longer run. If a larger +firmware output appears, test its format and command requirements separately. +Do not label input dimensions, a successful query, or a requested rate as a +completed full-resolution stream or measured FPS. + +Open qualification and performance checks +----------------------------------------- + +``tools/testing/selftests/media/apple-isp-stress.c`` exercises long runs, +repeated start/stop and format/size switches through MMAP. It logs completed +frames, delivered FPS, maximum inter-frame gap, sequence gaps and buffer errors. +Use the established nominal rate as its explicit rate argument. A driver-generated +sequence is not a firmware frame counter, so timing and sequence checks alone +cannot prove the sensor dropped no frames internally. + +The P010 path requeues even the last returned lease into an otherwise empty +queue, publishes completion ACKs before releasing output or metadata leases, +acquires message validity before reading payload and bounds each IRQ ring drain +to one ring traversal so terminal/allocation traffic cannot starve completions. +Command replies must match the requested reply length, zero status and original +header. A timeout or malformed ACK latches failure; no late ACK can authorize +another command. The existing forced coprocessor stop runs before uncertain +leases are released. Resident firmware then needs a reboot before another capture. +These corrections address source-visible stalls/races. They are not evidence +that the intermittent stalls observed by the USB proxy have been reproduced +or cured on the current firmware. + +No verified IMX958 RAW Bayer packing/order, RAW completion path or nominal FPS +for every configuration is present in the public source. The recorded RAW trial +reads stable memory after stop but receives no completed RAW report. A Bayer +FOURCC cannot be chosen from that evidence. The RAW format and an exhaustive +per-config FPS table remain open pending the original capture evidence and +runtime command replies; neither is inferred from the P010 output or input height. +The 30/15 values in the preview are AE requests, not per-mode nominal measurements. + +The T6040 device tree's current fixed TEXT/DATA reservations should eventually +come from a bootloader handoff describing the loaded image and mapping lifetimes. +They are firmware-build-specific. This change does not implement that handoff +or replace those reservations with 25G76 addresses. diff --git a/Documentation/driver-api/media/drivers/index.rst b/Documentation/driver-api/media/drivers/index.rst index 7f6f3dcd5c90c..f1ead3c9e389f 100644 --- a/Documentation/driver-api/media/drivers/index.rst +++ b/Documentation/driver-api/media/drivers/index.rst @@ -12,6 +12,7 @@ Video4Linux (V4L) drivers .. toctree:: :maxdepth: 5 + apple-isp-j616s bttv-devel cx2341x-devel cx88-devel diff --git a/Documentation/gpu/apple-dcp-t6041-panel.rst b/Documentation/gpu/apple-dcp-t6041-panel.rst new file mode 100644 index 0000000000000..bdf15e644d68f --- /dev/null +++ b/Documentation/gpu/apple-dcp-t6041-panel.rst @@ -0,0 +1,182 @@ +.. SPDX-License-Identifier: GPL-2.0-only OR MIT + +T6041 internal panel +=================== + +Desktop 26.6 firmware uses its own IOMFB instance. Panel brightness is in +integer nits from zero to the panel ceiling, with the same 250 ms fade used +by the H17P panel. The backlight interface reports the requested target and +``actual_brightness`` follows accepted, completed presents. A registration +default does not change the brightness inherited from the boot loader. +Until a loader hint or a completed user request supplies a known level, +``actual_brightness`` reports the target. Echoing the unchanged registration +value also leaves the panel alone while its level is unknown. A request for a +different target takes control. Once a hint or completed request establishes +the level, all targets are usable. Framebuffer unblank and core suspend +notifications also preserve an unknown inherited level. +The first explicit change fades from a known inherited level, or from the +reporting target when no hint is available. Firmware reports are observations, +not requests. After takeover they cannot replace the target, restart a fade or +cause a brightness write. Actual readback follows accepted, completed presents. +Ambient-light policy can use the normal backlight interface, just as a slider +does. Requests made while blanked or suspended preserve the restore target. + +Fixed refresh modes keep the timing and color IDs supplied by DCP. The driver +does not synthesize lower refresh modes from a 120 Hz mode. Fractional rates +remain in 16.16 Hz until the DRM pixel clock is calculated. A ProMotion mode +needs desktop presentation deadlines even with adaptive sync disabled. + +The DT panel compatible ``apple,panel-j616`` identifies the ProMotion panel and +enables adaptive sync support without a module parameter. The connector capability +requires the desktop firmware profile, the internal main display, and a valid +adaptive-sync range. Other panel compatibles do not acquire this capability. The +existing internal ProMotion convention supplies 24 Hz to the mode rate when +the firmware omits both range fields or reports both as zero placeholders; +a partial, duplicate or invalid range +leaves the timing usable at fixed refresh. + +A rejected adaptive-sync parameter or mode selection gets one retry with +adaptive sync disabled. An adaptive swap that stops completing invokes the +swap watchdog and re-applies the active CRTC at fixed refresh. Either failure +latches adaptive sync off until reboot. A transport that stops replying cannot +be recovered by issuing another command on its busy channel; it still requires +a reboot. Use the previous kernel entry if a panel fails qualification. + +Panel identity +-------------- + +The desktop internal panel has no external EDID service. Its ProductAttributes +supply the monitor identity, independently of optional colorimetry or timing +fields. The driver publishes an EDID using that product data, the panel dimensions +from the device tree, and a representable DCP timing plus catalog limits. +The EDID clock rounds to its required 10 kHz units. DRM still uses the exact +firmware timing list; the EDID adds no synthetic modes or color capabilities. + +Compact powerlog reports may be bare serialized dictionaries or carry a command +descriptor. The receiver preserves the serialization header before handing the +report to systemep. Only the first valid report before Linux control can seed +takeover; later reports confirm output without changing the requested target. + +Desktop mode changes use the D202 and D454 adapters for this firmware's records. +The callback table also includes physical mapping and the empty resize +notification. An unknown callback during a modeset gets a bounded unsupported +reply. Its enclosing command must ACK before the set fails, so a subsequent +mode change can reuse the drained channel. Accepted atomic modeset errors are +reported through the out-fence. A timed-out command retains its channel until +the firmware replies; another set cannot overwrite its stack. + +Repeated or partial DisplayAttributes retain a validated fixed-panel identity. +They do not emit another identity hotplug event or revoke the EDID. + +Identity accepts both chunked DisplayAttributes and the desktop dictionary +setters. A legacy PNP manufacturer code is decoded when the text code is absent. +Missing optional labels are represented by the firmware vendor/product code and +unspecified numeric serial, without inventing a per-device identity. Partial +updates for the same vendor/product retain known optional identity fields. + +Desktop identity diagnostics and fallback +----------------------------------------- + +Identity inspection uses the existing DRM KMS debug category (``drm.debug`` +bit 2), with no driver-specific switch. When that category is enabled, D552, D561, +D114 and chunked property delivery report the outer key, serialized key names, +value type numbers and lengths. D000 identifies chunked delivery. Type numbers +are dictionary 1, array 2, integer 4, string 9, blob 10 and boolean 11. Values +are never printed. Each property walk stops at depth 4 or 64 objects, and the +DCP stops after 128 diagnostic records per callback class (512 total), with bounded +inspection-status lines in addition. Atomic reservations include framing records. D552, D561, D114 and chunked delivery have +separate budgets so periodic statistics cannot hide the panel setters. Service +dictionary setter D413 is inspected and can supply identity or a raw EDID too. +Scalar setters D102/D104/D414/D415/D563/D565/D567 report keys and types at +ingress when their public wire lengths match. They share the +fourth budget with chunked delivery. Null scalar values report size zero; no +scalar value or service handle is printed, and acknowledgements are unchanged. +When KMS debugging is disabled no inspection budget is consumed. Malformed +serialization only stops inspection. + +A real EDID with a usable identity takes priority. It may arrive as raw bytes, +a serialized blob, or an ``EDID``, ``edid`` or ``IODisplayEDID`` blob nested +in a dictionary. Checksum-valid blocks without a usable manufacturer, model +and serial are rejected so they cannot reintroduce null userspace strings. Without +one, ProductAttributes remain usable, but are no longer a prerequisite for +publishing an identity. The fallback reads the original ADT from m1n1's +reserved-memory ``phram`` node with ``label = "adt"``. The reader accepts at +most 16 MiB, 4096 nodes, 4096 properties per node and 32 levels, and validates +all extents before committing any identity data. + +The supported ADT identity properties are as follows. These are property +mappings, not a claim that a particular firmware supplies every property. + +* ``/panel`` or ``/product/panel`` ``vendor-id`` or ``display-vendor-id`` is a + little-endian 32-bit packed PNP manufacturer code. +* ``product-id``, ``display-product-id`` or ``panel-id`` supplies the low 16 + bits of the EDID product code. +* ``display-serial-number`` supplies a little-endian numeric serial. + ``serial-number`` supplies panel serial bytes, hashed with FNV-1a into the + numeric EDID serial. ``raw-panel-serial-number`` on ``/product`` or a panel + node is handled the same way. Machine serials from other nodes are ignored. +* ``width-mm`` and ``height-mm`` supply bounded physical dimensions. Missing + dimensions retain the OF panel node's ``width-mm`` and ``height-mm`` (or + ``adj-height-mm`` when the notch is hidden). The full height is read first, + so a missing optional adjusted height cannot leave the height at zero. + J616s has a 345x223 mm full panel and a 345x216 mm notch-hidden viewport. + The connector retains these exact millimetres after the EDID update. + +A successfully parsed ADT does not necessarily contain a panel node. In +particular, a machine-level ``/product`` ID and ``/arm-io/disp0`` timing data +are not panel identity properties. They do not override OF panel dimensions +or prevent the generic fallback from being published. + +If the ADT or its identity fields are absent, the desktop J616 fallback is +manufacturer APP, product 0616, name Apple Panel and unspecified numeric +serial zero, represented by the non-empty serial descriptor ``unspecified``. +This generic identity does not claim to identify an individual panel. The +preferred representable timing and range limits come from the retained DCP +TimingElements catalog. Exact mode enumeration continues to use that catalog. +For notch-hidden modes, the synthetic identity DTD retains the clock, active +area and total blanking but bounds the vertical front porch and sync pulse to +EDID's six-bit fields. It is an identity summary, never an enumerated or +programmed mode; the original DCP sync positions and mode IDs are unchanged. +This also lets a 118-line porch and 82-line notch-expanded pulse publish an +identity instead of failing the base EDID size check. +The EDID records model year 2024 (not a manufacture date) and a nominal D65 +reference white point required by EDID parsing. No calibrated primaries, gamma, +new mode or machine serial is claimed. The ADT reader, +EDID fallback and real EDID setters are restricted to desktop 26.6; T8140 +continues to use its existing path. + +Completed desktop brightness changes emit a backlight change uevent and notify +both ``brightness`` and ``actual_brightness``. The work runs outside the receive +path's locks and is drained during shutdown. Takeover sends the same event when +it updates the inherited level. Requested brightness remains the user's target +through the fade; notifications do not replace it with an intermediate level. + +DT capability and boot-loader handoff +----------------------------------- + +The J616 panel's existing DT compatible supplies adaptive-sync capability; +there is no T6041 VRR module parameter. Firmware and mode validation, the main +panel restriction, and the fixed-refresh failure latch still apply. EDID and +backlight notifications have no driver-specific tunables. + +The kernel does not consume ``chosen.asahi,t6041-dcp``. Its early T6041 gate +already follows the handoff published by m1n1: ``apple,t6041-handoff = <1>`` on +DCP, display and PIODMA, firmware compatibility 26.6.2, the expected DART stream +and mailbox relationships, initially disabled nodes, and a running DCP. +The DCP driver separately validates register resources and the firmware memory +handoff. Missing or invalid handoff data leaves the boot framebuffer in place. +These checks must remain when the loader's policy changes. + +The current m1n1 producer uses that chosen property to decide whether to attempt +publication. Removing it cannot be completed by a kernel change: a loader with +no published handoff provides nothing for the kernel to adopt. The producer +needs to select the T6041 handoff from its existing chip/board match and the +supported DCP/display/panel DT compatibles, then run the existing ADT, power, +running-session, firmware-segment, reservation and live DART mapping checks +without a chosen opt-in. Only a successful transaction may publish the existing +markers, memory regions and firmware properties. Keeping nodes disabled until +the kernel accepts that contract preserves compatibility with older kernels. +A failed transaction must publish no partial handoff. Loader host tests should +cover supported DT without the chosen property, an unsupported board/DT, and +each failed validation, as well as unchanged other-SoC policy. No new policy +flag or kernel bypass is needed. This kernel series does not replace Stage 2. diff --git a/GAPS.md b/GAPS.md new file mode 100644 index 0000000000000..c8d2c7c753261 --- /dev/null +++ b/GAPS.md @@ -0,0 +1,1302 @@ +# T6040 GPU parity audit + +Audit baseline: `integration/aurora` at +`9cbdd0734d9e`, with GPU integration #463 (`a0a9485847f8`), the +r5 display series (`905dff774561`), camera #462 (`2114ed0c6245`), and the +two r6 display patches. The reconstructed r6 branch is +`wip/j616s-display-r6` at `af0a664046fb`. + +This series repairs regression coverage and a shared KUNIT=m WERROR build defect, +ports GPU-driven memory/fabric demand and PMP voter cooperation using +T6040 data, honors the existing DT idle-delay policy, and closes a compute +release-witness gap across recovery. GPU voltages, firmware features and the submission algorithm remain +unchanged from the reconstructed base. The major +T6040 calibration and image-admission changes requested by this audit are +already in the baseline. Software parity does not establish all-workload benchmark or CTS +parity. The operator has selected g3 as the measured J616s baseline; g4 +retains it and adds the qualified shared ports and diagnostics below. + +## Gap table + +| Gap | Neo behavior | T6040 before this series | After this series | Evidence / remaining gate | +| --- | --- | --- | --- | --- | +| Per-chip leakage and power ceiling | T8140 has its own platform data | #463 requires a versioned DT handoff, finite positive leakage, a power ceiling and the measured core mask; no single-unit fallback | Same runtime behavior | `g17/calibration.rs`, `g17/initdata/v25g83.rs`; #463 commits `a8fb1380b414`, `53877eaebd4b`, `415fd2e6ee2d` | +| Raw OPP count versus logical states | Firmware consumes 11 logical states | 16 raw GPU OPPs; 11 selectors in each A/B map; both end at raw OPP 15; unused handoff slots must be zero | KUnit now uses valid complete maps and checks the final state's second voltage lane | `g17/calibration.rs`; a count of 15 in an ADT property is not evidence for expanding this firmware layout | +| Full clock eligibility | Platform OPPs feed firmware performance tables | DT supplies both cluster voltages and GPU/CS/AFR frequencies; maps and relative-B are checked against the GPU OPP table | Same runtime behavior | `g17/initdata/v25g83.rs`, `g17/fw/initdata/v25g83.rs`; confirm sustained clocks and limiting reasons on hardware | +| DVFS ramp / effort | Full effort on publication, cleared after 16 ms of continuous idle | Same `Firmware::note_submission`, direct-port accounting and race-checked idle clear | Same runtime behavior | `g17/mod.rs`, `g17/port.rs`, `g17/power.rs`; opaque firmware power words are not a documented ramp control | +| GPU-driven fabric/memory floor | #440 uses an activity-following graphics PMGR input | T6040 CPU-only demand; g3 port sampled the firmware-lifetime GFX supplier | g4 uses DT-selected publication/continuous-idle demand, preserving DCS 5/fabric 2 and 32 ms CLPC hold | `clpc.c`, `g17/power.rs`, `g17/port.rs`, T6040 DT; blank-panel release and bandwidth retention require hardware qualification | +| PMP PMC vote cooperation | PMP/CLPC/AP agents enabled | T6040 static voters omit PMP 0 from pre-working-PMP bring-up | Port 772a9d3104ff adds the existing PMP supplier dependency and voter 0 on T6040 DCS/SOC rails | pmc.c and T6040 DT; live votes, idle release and thermal qualification required | +| Idle / off policy | Shared host hold and firmware device-control path | Same host implementation; source documents a 25G83 effective 2 ms off delay and approximately 1 ms wake cost | J616s requests a bounded 16 ms host hold through existing `apple,idle-off-delay-ms`; absent policy retains 2 ms | `g17/power.rs`, `g17/mod.rs`, J616s DT and binding; compare short/long gaps, depth-one latency and idle power | +| Fast-die / PMP | Platform-specific firmware power graph | 25G83 AON table and separate power-state target/release (101/96 C) are already populated; DT enables the GFX suppliers | Same runtime behavior | `g17/fw/initdata/v25g83.rs::POWER_STATE_WORDS`, `g17/initdata/v25g83.rs`, J616s DT; verify clocks with PMP running | +| Periodic preemption | Context-switch timer mask is zero | Same zero mask; two-cluster operand-state allocation differs by hardware layout | Same runtime behavior | `g17/fw/initdata.rs::CONTEXT_SWITCH_TIMER_MASK`, `g17/fw/compute.rs::OperandState`; no evidence supports changing the mask | +| High-priority queues | HIGH / MEDIUM / LOW firmware scheduling profiles | Same scheduling-policy selector and priority work rings | Same runtime behavior | `g17/mod.rs::scheduling_policy`, `g17/fw/channels.rs`, `g17/qos.rs` | +| Timestamp publication | Retains one CPU mapping for each firmware timestamp buffer | Same shared timestamp buffer and completion publication | Host timestamp suite again compiles and executes the production retained-mapping writer | `timestamp.rs`, `mmu.rs::TimestampVmap`, `tools/testing/selftests/drivers/gpu/asahi-m4/test-timestamps.py` | +| Statistics | Drains firmware statistics; no decoded `agx_stats` snapshot | Same; payloads discarded | g4 preserves full per-type/per-role raw snapshots and static-key report tracing | `g17/reports.rs`, `g17/channel.rs`, `g17/trace.*`; achieved-frequency/power decoding remains gated on firmware semantics | +| Firmware startup / registration | Two instances, message acknowledgement, POWER_ON_DONE and MMIO render-pair registration; no lead commands | One merged 25G83 graph, status acknowledgement, no POWER_ON_DONE, no MMIO pair registration; lead commands required | Same runtime behavior | `g17/fw/variant.rs`, `g17/initdata/v25g83.rs`; startup differences are ABI requirements, while MMIO pair registration on 25G83 remains unqualified | +| Queue pointer / restart layout | Producer at +0x40, recovery target at +0x50 | Producer at +0x50, no recovery-target word | Same runtime behavior | `g17/fw/variant.rs::PointerLayout`, `g17/fw/queue.rs`; do not copy another firmware build's offsets | +| Core topology / mapping ownership | One six-core cluster; two firmware roots and fabric bridge | Qualified two-cluster, 20-core layout; reserved firmware tables, one root and no fabric bridge | Same runtime behavior | `hw/t6040.rs`, `hw/t8140.rs`, `g17/mod.rs`; other fused core counts are still refused, and this audit does not expand that admission | +| Alternate loaded image | T8140 uses fixed firmware selection | Both exact T6040 normalized TEXT digests are admitted in one `Build.images` slice | Same runtime behavior | #453 (`26f5eb68dbce`), #463 (`804bba86ca55`), `hw/t6040.rs`, `g17/fw/variant.rs`, `fwid.rs`; unknown images remain refused | +| Submission/completion improvements | #423 shared G17 runtime | #423 is already an ancestor of the baseline; direct compute/render publication, admission domains and completion collection are shared | Same runtime behavior | #423; `g17/runtime.rs`, `g17/runtime/render/direct.rs`, `g17/port.rs`, `g17/completion.rs` | +| G13/G14 `submit.empty` regressions | Neo uses the G17 runtime | G16S uses that same G17 runtime, not the maintained G13/G14 workqueue | Same runtime behavior | `driver.rs`, `file.rs`, `g17/queue/mod.rs`; user-space empty submits may never reach this ioctl; trace and benchmark the paired Mesa stack | +| G13/G14 idle wake regressions | Firmware-specific cost plus shared host overhead | G16S shares common ioctl/GEM/sync infrastructure, but has different firmware, power handling and queue backend | Same runtime behavior | `driver.rs`, `g17/power.rs`; G13/G14 benchmark ratios cannot be transferred to G16S | +| QID capacity | Firmware floor 0; reserve protects dedicated render pairs | 25G83 reserves QIDs 0..31; public queue limit is reduced accordingly | KUnit checks floor 32 in dynamic growth and full logical queue budget tests; host resource runner follows current reserve helper | `g17/kick.rs`, `g17/runtime.rs`, `tools/asahi/g17-resource-tests.py` | +| KUnit / WERROR config matrix | Shared test modules require KUNIT=y | Four G17 test helpers used the broader CONFIG_KUNIT guard and failed WERROR with the shipping KUNIT=m config | Helpers now use CONFIG_KUNIT = "y", matching the Rust macro; config and warning policy unchanged | `rust/macros/kunit.rs`, `g17/kick.rs`, `g17/preparation.rs`, `g17/queue/admission.rs`, `g17/runtime.rs`; verify both KUNIT=m release and KUNIT=y QEMU builds | +| Compute ownership / retained mappings | Recovery and retirement witnesses protect mapping lifetime | Same production queue ownership, source loans, growth rollback and deferred release | Aged-record fallback now requires the original recovery generation; source loans and host negative controls remain | `tools/asahi/g17-capacity-fault-tests.py`, constructor/growth/pool/release-order fixtures | +| Restart and fault classification | Shared recovery handshake plus validated T8140 per-slot keys | Shared handshake uses 25G83 status offsets; `recovery_slot_keys=None` preserves firmware blame when keys are unknown | Bounded generation/slot/progress diagnostics retain unknown ownership; any missing selected key preserves firmware blame | `hw/t6040.rs`, `g17/initdata.rs`, `g17/recovery/attribution.rs`; successful 25G83 fault/restart qualification is still required | +| DART / UAT | Shared mapping ownership and checked page-table borrowing | Shared MMU, with T6040 reserved tables, PoC cleaning, per-page TLBI and native GPU permission quirks | Same runtime behavior | `hw/t6040.rs`, `mmu.rs`, `pgtable.rs`; do not remove coherency or handoff requirements for a benchmark | +| Memory accounting | Creator-attributed committed/resident GEM accounting and capacity admission | Same `memacct::Client`, `File::memory_stats` and shared capacity machinery | Same runtime behavior | `file.rs`, `gem.rs`, `memacct.rs`, `g17/capacity.rs`; verify multi-client high-water and post-close behavior on hardware | +| MCache / few-primitives / ray traversal | Enabled by the 26A428 variant | Disabled by 25G83 variant; source records hangs for few-primitives and stalled ray traversal, while MCache is unqualified | Same runtime behavior | `g17/fw/variant.rs::V25G83`; enabling these requires independent ABI evidence and hardware tests | +| M5 Pro comparison | No M5 Pro hardware configuration exists in this public baseline | No T605x/G17S KMD path to compare | No claim of M5 parity | Pinned public tree contains T6040 and T8140; a licensed public M5 Pro reference is required | +| CTS and Linux/macOS performance ratio | Hardware results belong to their exact kernel/Mesa pair | No result for this candidate | Pending hardware agent | Run gpubench with matching suite, configuration and conditions; review CTS failures and kernel faults before claiming parity | + +## GPU memory demand and interactive latency + +The GFX PMP report entry and PMC performance votes are separate mechanisms. +The existing T6040 `pmp_gfx` soc-device 13 entry follows the powered GPU domain; +`apple_pmp_report_set_state()` publishes a power-state request bit and waits +for its acknowledgement. It does not encode a DCS/fabric performance request. +The GPU firmware owns the PMP's performance demand. Before this series, PMC's +static voter list omitted PMP agent 0, and CLPC lacked a GPU power-state input. +The two existing ports enable PMP agent 0 on T6040's DCS/SOC rails and make +CLPC request the already qualified DCS 5 / fabric 2 floor. G3 incorrectly +used the T6040 supplier lifetime as activity. G4 replaces that input with +DT-selected publication/continuous-idle demand and preserves the existing +32 ms memory-floor release. Prove live +request, enabled-agent and actual-state agreement; supplier binding alone +is not proof of an honoured vote. + +For dispatch latency, G17 now reads the existing `apple,idle-off-delay-ms` DT +policy, validates the existing 2..1000 ms bounds, and starts the existing host +hold with that policy. J616s selects 16 ms, matching the existing effort-idle +interval; other boards without the property retain the native 2 ms behavior. +This avoids power-off across short interactive gaps and releases after real +idle, without changing firmware layout or adding a parameter/platform flag. +KUnit covers absent/valid/invalid policy and repeated depth-one gaps followed +by idle release. A shorter software delay is not itself a measured latency +win: compare actual power transitions, firmware execution, IRQ/retirement and +fence wakeup boundaries. Keep empty CPU fence transfers separate from GPU +round trips and require low-duty/long-idle power acceptance. + +## Remaining 25G83 facts and recovery qualification + +No firmware capability is inferred from another GPU's successful CTS result. +The following are the exact T6040/25G83 facts needed to open the remaining +paths. A reference capture of achieved frequency establishes a target, not +an offset, voltage, descriptor ABI, or Linux residency measurement. + +| Gated path | Exact facts needed for T6040/25G83 | Current evidence and disposition | +| --- | --- | --- | +| Sustained top clock | DT OPP count, both voltage lanes, hardware A/B selector maps, selected logical-state limit; independently decoded achieved frequency or residency, counter units/timebase/wrap, request versus achieved-state semantics and limiting reason under sustained load | Per-chip DT handoff and maps are already mandatory; both maps select the final raw OPP. `max_frequency_khz` is an advertised maximum, not achieved telemetry. G17 `stats_snapshot()` returns None; g4 raw `firmware_reports` and `asahi_g17_report` preserve payloads but establish no decoded residency. Keep voltages/maps unchanged and require a measured Linux load trace. | +| Raw versus logical state count | Meaning of the ADT count properties for this firmware, both SGX map banks and number of entries actually consumed by the 25G83 power tables | Public `HwData25G83`, calibration validation and loader handoff select eleven logical states from sixteen raw OPPs. Do not resize the firmware structure from the ADT property name or discard its unsorted OPPs. Compare actual top selection and achieved clock. | +| DVFS ramp / idle / fast-die | 25G83 meaning and units of ramp/effort controls, idle/off delays, temperature limits and hysteresis; PMP/AP/CLPC request ownership, achieved floors and release under idle and load | Existing shared submission/idle policy, DT suppliers and T6040 power-state graph are enabled. Power-state presence is not proof of load tracking. Require live requests, actual floor release and thermal traces; opaque power words are not tunable parameters. | +| Recovery slot attribution | Exact 25G83 slot-key storage range, lifetime, cache policy and slot stride; key QID/stamp bit fields; slot-mask meanings, progress monitor semantics and association with an actual started render | `recovery_slot_keys=None`; never copy T8140's physical address. Preserve firmware blame. Progress-view reads no longer depend on knowing the key address; bounded diagnostics report unknown ownership explicitly. | +| Recovery table / reason | 25G83 recovery-info offset/extent, valid/guilty/cancelled bits, stamp sequence encoding; reason-code semantics; ordering and reset of records relative to states 1/2/3/0; interrupted-command drain and visibility guarantees | Existing checked 256-entry table and handshake remain unchanged. Reason 3 is treated as page fault by existing attribution. New generation/slot diagnostics aid qualification; missing key evidence cannot clear blame. An aged compute record may no longer substitute for a stamp across recovery. | +| Compute completion / visibility | Exact compute record identity and timestamp fields, stamp write/cache-maintenance order, ring consumption during restart, late/missing-stamp behavior and a release witness for interrupted work | Same-generation 50 ms fallback retained. Crossing recovery now requires a covering stamp for ordinary retirement; quarantine, kill/drain and processor-stop ownership remain. A completion record plus elapsed time after restart is not independent visibility evidence. | +| MMIO pair registration | 25G83 MMIO port addresses and access-power witness; pair registration write order, ACK and coexistence with lead commands/bootstrap QID reservation | Keep disabled. The 26A428 sequence, POWER_ON_DONE message and recovery-target pointer are distinct ABI facts. | +| MCache | 25G83 table-entry format, sizes/alignment, descriptor pointers and enable bits, ownership/coherency rules, partial-render/restart behavior | Keep disabled until a licensed public implementation or independently supplied facts establish the whole contract and tests validate it. | +| Few-primitives | T6040 enable-bit semantics, required auxiliary buffer sizes/registers and partial-store/load/resume handling for all tiling/fragment paths | Keep disabled; previously observed hangs are not grounds to enable it for performance. | +| Ray traversal / preemption | T6040 register program, scratch sizing/alignment by enabled cores/clusters, saved operand-state layout, resume/checkpoint semantics, kill/restart contract, scheduler timer mask and priority profiles | Existing two-by-ten operand layout and priorities retained; periodic timer mask remains zero on both variants. Native traversal remains disabled pending bounded execution/fault/recovery qualification. | +| TLB range operations | T6040 opcode/operand encoding, VA/ASID granules, ordering, completion and negative-translation invalidation across every mapping alias | Existing page TLBI and PoC table cleaning retained. Copying Neo range encoding cannot close a performance gap safely. | +| Large vertex/barrier and tess/geometry faults | Faulting engine/access/VA/VM, exact bound intervals/backings and mapping lifetime; render/compute descriptor identity, TVB/GTP capacities, pass-start/stamp state and recovery reason before acknowledgment | Isolate exact public CTS names with one process and compare kernels with the same Mesa. Buffer size alone does not establish VM-bind, timeout or TVB causality. Retention fix is independently justified, not a claim to have cured a CTS fault. | + +The compute stamp-wait KUnit suite now checks that an aged exact record and +consumed items cannot retire a pre-recovery kick without its stamp, while an +exact/later stamp, busy-ring control and same-generation fallback retain their +expected behavior. Recovery-source tests cover unknown keys, incomplete reads +and page-fault fallback. Diagnostic counters explicitly identify lifetime +stampless totals; they are not a count of commands retired by one restart. + + +## g5 large-target capacity correction + +Public CTS `vulkan-cts-1.4.6.2` uses a 10240-square framebuffer for +`rasterization.primitive_size.points.point_size_10000`. The existing 25G83 +GTP layout appends a sub-buffer of 64 times the tile map after the TPC. +Applying the inherited 32 MiB cache bound to this combined allocation rejects +that valid target with ERANGE, despite its cache and tile record each fitting +their original limits. This is reproducible in the production layout code +without GPU execution. The normalized hardware summary contains ERANGE and +reason-2 recoveries but cannot establish their order or one-to-one association. + +G5 applies the original TPC_MAX to the aligned cache, preserving the original +tile-record bound before allocation or growth. The tile-map bound also caps +the derived auxiliary extent. It changes no firmware buffer-sizing formula, +numeric limit, VA window, generation/retention rule or allocation accounting. +Neo has no auxiliary tail and keeps the same policy. KUnit covers both utile +shapes at the CTS target size, oversized cache/layered-record rejection and +Neo capacity. Fresh and growing targets use the same preflight. Large targets +need their full required metadata through the existing memory/VA mechanisms; +allocation failure still propagates. Hardware execution/readback and post-close +memory/capacity baselines are mandatory acceptance gates. + +The separate 1 MiB vertex/barrier case uses a 256-square target, not a large +framebuffer. Its point count is actual vertex-buffer bytes divided by stride. +It cannot be explained by the large-target capacity guard. Preserve its first +operation/exception, generated sequence and the halted-state TVB/growth/pass +snapshots before choosing a coherence, VM-bind or firmware-timeout fix. The +point-target correction is not a claim that firmware reason 2 is decoded. + +## g5 recovery evidence and host-hop timing + +The shared recovery service previously captured slot keys and monitors at +halted state 1, then called `render_pass_started()` during classification +after acknowledging state 3 to 0. A resumed render could change that pass +state. G5 computes attribution from the state-1 pass snapshot and carries +only that result through the handshake. KUnit drives the real service and +rejects any pass read after the first acknowledgement. Unknown 25G83 keys +still retain firmware blame. This is an independently demonstrated ordering +fix; it does not establish the cause of either deterministic reason-2 fault. + +G5 adds the existing declared TVB state (inventory, page capacity, producer, +consumer, high-water and reset) to the halted snapshot without resetting any +counter. The blamed pair's first four commands include render geometry and +the computed GTP layout, alongside the existing pass/cursor/stamp records. +Pending/active growth and its reply remain visible. The next isolated run +can distinguish exhausted inventory or delayed growth from a pass that never +started; no buffer enlargement or firmware timeout change is justified yet. + +Static-key `asahi_g17_inputs` records compute input conversion entry/result, +source count and publication waits. `asahi_g17_schedule` records enqueue, +inline/fallback, preparation and run boundaries. Together with the existing +publish/complete, DMA-fence and scheduler tracepoints these split host cost +per hop into input resolution, scheduler setup/wait/dispatch and firmware +execution. They make no timing claim until a matched trace is captured. + +The available macOS reference uses 26A428 on T6040 and reports a software +request P10 while hardware residency is 1578 MHz at raw state 14. Linux runs +the admitted 25G83 image. Thus neither a literal P10 conversion nor copying +another firmware's statistics layout proves achieved MHz. The reference +contains aggregate frequency/residency and public DT/calibration facts, +not a Linux 25G83 tag/field decoder. The inspected licensed public G17 source +at `jgoyvaerts/linux` `cc32473a09dd` also has no decoded statistics adapter. +Required input remains a fresh Linux raw report series under labeled idle, +ALU and fill load, and independently established field/timebase/achieved-state +semantics for that exact firmware. No captured raw payload has been promoted +to public implementation data. Achieved clock/power and the CTS cure remain +open; g3's measured memory/latency policy is preserved. + +## g4 shared runtime ports and observability + +The operator has selected g3 as the hardware baseline. This follow-on retains +its CLPC/PMP ports, DT idle hold and recovery-generation release guard. +Individual hardware records remain in the private qualification workspace; +no raw lab capture is a public implementation source. + +| Gap | Public source behavior | T6040 g4 disposition | Qualification | +| --- | --- | --- | --- | +| #504 fragment notification | `de0b22a8370e` selects a render pair on its fragment notification; final retirement still requires both stored stamps | Included for both firmware graphs; KUnit covers each mask bank and unrelated queues | Render-light latency/IRQ rate, growth/partial renders, recovery and CTS | +| #504 tiling first-kick | `5073eac15192` changes the word only for the 26A428 register program | Included unchanged; 25G83 retains 1 on every kick | No T6040 notification-halving claim; requires 25G83 descriptor/notification facts and controlled qualification | +| #496 compute-fault witness | `9a76756bf2d7` uses event 7 words 3/4 as QID/stamp on Neo, with consumed-ring guard | Imported prerequisite, but the existing firmware graph keeps 25G83 event 7 unknown | Exact 25G83 layout, queue identity, stamp semantics and DMA/ring visibility must be established before it can release memory | +| #505 closed-context teardown | `078d744a260c` kills before freeing executed unstamped render graphs, retains firmware-linked work storage, and reuses killed pairs last with QID capacity | Shared safety/retention paths included; compute-fault-driven kill depends on the graph-qualified #496 witness | Closed mixed compute/render faults, acknowledgement ordering, memory pressure and slot/QID reuse; does not claim to cure a live render's first fault | +| #499 long-job sharing | `3db24da5c830` recognizes another published, unobserved long compute job as sharing, without changing numeric renewal bounds | Included in the shared host path; T6040 bounded AS-build classification can use it, while native traversal remains gated | KUnit verifies renewals stop at the existing bound; no claim that this cures a firmware reason-2 render fault or makes skipped traversal CTS pass | +| First fault | Reason and blame alone cannot identify engine progress | At halted state 1, before acknowledgement, g4 logs the blamed pair's stored stamps, free-list balance and up to four commands' pass/cursor snapshots; borrowed pairs are explicitly unavailable | Correlate exact CTS operation and original error with raw event report and snapshots; do not label reason 2 as page fault without semantics | +| Raw firmware statistics | Complete 18-word statistics records already have checked ring geometry | Read-only `firmware_reports` exposes last full sample, count and host monotonic time for each role/type. Static-key `asahi_g17_report` copies trace/statistics/event words before slot return | Match payloads to independently decoded achieved-clock/power/residency and freshness/units; legacy statistics tags are a different ABI | +| Idle memory floor | Neo graphics PMGR follows activity; T6040 GFX is held for firmware lifetime | g4 replaces T6040 PMGR demand with a DT-linked CLPC request, asserted at publication and released by the existing race-checked 16 ms continuous-idle clear; 32 ms CLPC hold remains | Read-only last vote/reason/time, host request and last submission, safe core power/host hold; require blank-panel quiet release and load/release cycles | + +Remaining exact facts for telemetry are the 25G83/26A428 tag-to-layout map, +field width/signedness/endianness, sample/timebase/wrap and freshness, +power units/averaging interval, logical/raw/cluster pstate selection, requested +versus achieved-state semantics, and limiting/thermal reason meanings. Raw +samples and the DT maximum are not MHz. Reads issue no firmware request and +never touch gated core registers. `agx_stats` remains unsupported for decoded +G17 data until those semantics are established. + +For the two deterministic CTS cases, retain the first failing operation before +cleanup, full unnormalized recovery logs, raw event trace, and pre-ack render +snapshots. The large-buffer and extreme-point-size cases can stress tiling, +but neither case name proves a TLB, timeout or TVB cause. No timeout, voltage, +feature-enable or buffer-size guess is included in g4. Multi-queue sync needs +its publish/GPU-span/event/fence timeline; #504's compute control does not +promise to halve that path's latency. + +### T6040 idle-floor release correction + +The PMGR DT explicitly documents that GFX cannot power down while firmware +runs. This is a source-established difference from the Neo CLPC input, so +using GFX as T6040 work demand can never establish idle release. G4 removes +that input on T6040 and adds `apple,clpc` from the GPU consumer to the existing +host performance controller. This is a provider relationship, not a feature +flag or user knob. A requested but unavailable provider defers GPU probe. + +Publication asserts the independent, refcounted request through a leaf lock; +o sleeping callback or allocation runs at submission. Both mutex-backed and +off-lock direct paths are covered. The existing effort clear checks in-flight +work and 16 ms continuous idle, withdraws its flag, then rechecks the direct +submission counter. A racing publisher reasserts demand. The CLPC vote keeps +its existing 32 ms hold; CPU arbitration and Neo's PMGR input are unchanged. +Firmware stop and request destruction clear demand. Provider removal cannot +free storage retained by a late consumer, and a device link orders dependencies. + +CLPC status now shows the last requested floor, reason bits and monotonic +time, plus the host request's activity/update sequence. Actual PMC readback +and write errors remain distinct. Read-only `asahi-g17/power` reports the +existing always-accessible core power witness, host hold, effort and last +publication time, without waking firmware. Generic runtime PM may remain +unsupported because autonomous core gating is firmware-managed; no fictitious +runtime-suspend implementation is enabled. Raw stats still do not decode MHz. + +The production CLPC host suite tests an always-powered supplier with absent, +busy and ended GPU demand, 32 ms hold, CPU coexistence, reason/time and legacy +Neo behavior; removing demand or idle release fails its negative controls. +C KUnit checks actual request lifetime and a late consumer after provider +removal. Hardware acceptance requires a locked/blank panel with publications +observably quiet, release after the hold, then three load/idle cycles and +retention of g3's benchmark gains. An active compositor alone proves neither +quiescence nor a latch. Keep failed measurements and rollback candidates. + +## Source and validation boundaries + +The implementation references are the licensed public Aurora/Asahi kernel and +loader history. Existing Gravity Linux-derived layouts and attribution remain +unchanged. The public Gravity Linux `gravity-m4` reference inspected at +`4fc326f6c25ccceb7515e2635c22e01fde5d3bac` was checked in `g16.rs`, +`g16_runtime.rs` and `g16_power.rs`. Those files do not supply validated +T6040 recovery slot keys; that G16 runtime explicitly leaves compute +non-preemptible. No private implementation, firmware bytes, voltage table or +lab record is included here. + +The build handoff must identify the exact tested source and loader, verify boot +payload checksums, retain r6g rollback, and distinguish CPU/QEMU tests from GPU +execution. Missing J616s measurements are not passing results. + +## Neo KMD improvements on T6040 + +This ledger audits refinements and handling, not just feature parity. It uses +`integration/aurora` at `9cbdd0734d9e186c5f5f5949a21b909d10c2571b` and +`aurora-wip` at `927a4a4480cf90ce8e3b4ab1d1ce58d84bf32fd4`. The conservative +history window starts 2026-09-26 (Neo platform prerequisites), before the public +T8140 firmware interface of 2026-10-03 and hardware bring-up of 2026-10-06. +Committer dates retain rebased older-author changes. The inventory also includes +the complete 105-commit public #171 history, all 65 commits of #423, and the +related PR versions used to identify original contributions and later ports. + +The path log covers `drivers/gpu/drm/asahi`, Apple PMP/PMGR/PMC/CLPC/MCC, +RTKit/mailbox, CPU-frequency/thermal and DART code, related AIC and DRM +scheduler/fence support, Rust DRM interfaces, and Neo DT dependencies. Broad +path/PR neighbors and imported baseline changes are explicitly marked N/A; +they are not represented as measured Neo optimizations. Old/rebased hashes are +listed as encountered aliases of the same subject/class, not asserted to be +byte-identical. The coverage inventory lists 1,551 physical commits (including +imported neighbors and PR-original versions), with 851 distinct subjects; all +65 #423 commits have separate rows. **Active** describes the surviving mechanism on T6040, including +its 7.3 port/refinement, rather than claiming that every historical SHA is an +ancestor. Multi-purpose firmware-introduction records retain their shared +parts while explicitly identifying dormant MCache/native-feature portions. A commit link is its exact diff; the evidence key states the current +reachability/gate. No per-commit speedup is invented from a stack measurement. + +### Applicable ports found by the commit audit + +| Neo mechanism | T6040 in g1 | g2 port and per-chip data | Required acceptance | +| --- | --- | --- | --- | +| #440 GPU-driven CLPC memory/fabric floor | CPU demand only; `gpu_demand=false`, no GPU power-state phandle | `0d63308820ab`: existing GPU-demand mechanism and DT phandle, T6040 DCS 5/fabric 2 and existing 32 ms release; CPU sustain=3 retained | C/R/H/I/P; actual PMGR tracking, floor release, video/browsing and sustained-load power | +| #219 PMP voter cooperation | Static voters 1/3 omit PMP 0 from pre-working-PMP bring-up | `772a9d3104ff`: existing `apple,pmp` supplier dependency, voters 0/1/3, existing T6040 DCS/SOC rail mask; no display rail or voltage change | P/B; PMP bound/ready, live request/honoured floors, idle and thermal behavior | + +Other audited gated paths are listed explicitly below. The T8140 lazy CPU +verification cost is absent on T6040's already-fast-switch profile. MMIO pair +registration, range-only TLB encoding, native RT/few-primitives and optional DCS +cooling cannot be enabled by copying a different firmware/hardware protocol. +The recovery slot-key attribution gate still needs validated 25G83 identity +facts; shared recovery, deadlines and retirement handling remain enabled. + +### Reported measurements and their scope + +These are public PR results, not new J616s measurements. Private qualification +reports were checked for scope and regressions; raw lab records are not copied +here. The older GPU profiling report's cost estimates are not measurements. + +| Evidence | Relevant commits/mechanism | Reported Neo result | J616s confirmation | +| --- | --- | --- | --- | +| [#171](https://github.com/aurora-silicon/linux/pull/171) | Retention, off-lock cleanup, reclaim quiet window and shared lifetime stack | Cold first-submit median 224 to 194–198 us, p99 338 to 295–305 us; menu-launch stalls below 100 ms versus 260–280 ms before quiet-window work. Stack results, not isolated gains. | B/M/C/R, repeated fresh contexts and foreground launch under bounded pressure | +| [#171](https://github.com/aurora-silicon/linux/pull/171) | Completion/recovery isolation | Earlier mixed render/compute lost its device within 2 s; later fixture ran over 568k verified checks in 60 s. Historical 1 ms stamp fallback was subsequently refined to 50 ms. | F/H/T; exact data and fences, peer liveness and no premature mapping release | +| [#423](https://github.com/aurora-silicon/linux/pull/423) | Cumulative scopes and submission stack | Same-boot submit API elapsed roughly 14 to 10 us; throughput stayed near 20 us. Combined candidate C-d64 19.44 us, R-d32 41.48 us, hop 42.12 us; workload/Mesa dependent. | C/R/H; preserve wall-time versus thread-CPU distinction | +| [#423](https://github.com/aurora-silicon/linux/pull/423) | `9bf3039873a1`, completion identity index | Scanner self samples 4.87% to 2.24% at depth 64, 3.16% to 1.93% on hops. CPU profile result. | O/C/H | +| [#423](https://github.com/aurora-silicon/linux/pull/423) | `26446182da3a`, 128-interval validation working set | C-d64 API elapsed 10.06 to 7.46 us; R-d32 12.29 to 8.16 us. Not a thread-CPU clock measurement. | C/R/D with rotating 64 buffers and remap/permission invalidation | +| [#423](https://github.com/aurora-silicon/linux/pull/423) | `b6248acf900c`, private-fence fast status | Submit API median 7.52 to 5.79 us; frontier samples 1.83% to 0.25%; throughput remained near 19.5 us. | C/H/O; errors, foreign inputs and delayed host publication | +| [#440](https://github.com/aurora-silicon/linux/pull/440) | `d69f14f4e4fb`, GPU fabric floor | C-d1 158.9 to 108.2 us; C-d64 19.3 to 14.7 us; R-d32 42.7 to 29.7 us; hop 42.2 to 31.9 us. Same driver/Mesa. | P/C/R/H, compare g1/g2 and keep heavy/idle checks | +| [#219](https://github.com/aurora-silicon/linux/pull/219) | GFX report/PMP vote and PMC enables | Short manual-bit glmark2 build experiment: 1453 with GFX report off to 2199 with it on; 2218 with all interface enables. Single-run 7–10 s experiment, not an isolated final-kernel A/B. | P/R; distinguish reported request, enabled agent and honoured floor | +| [#443](https://github.com/aurora-silicon/linux/pull/443) | Lazy verification and fast switch | C-d2 58.8–59.7 to 46.4–47.3 us; C-d1 107.8–108.4 to 102.6–102.7 us. T8140-specific spin removed. | C/I observation only: T6040 already lacks that spin | +| [#413](https://github.com/aurora-silicon/linux/pull/413) | Existing optional idle hold | The roughly 1.1 ms wake and 1.2–1.4 ms tiny-render-after-gap observations are **M4 Pro**, not Neo measurements. Default 2 ms adds no extended hold. | I at defaults; optional hold is a separate policy experiment | + +The provided current Neo Linux/macOS report has no completed paired Linux +record, so an overall Neo margin is still undefined. Historical PR ratios do +not establish the requested current all-workload margin. Use matched suite, +Mesa, clock/power conditions and repeats. Run only workloads admitted by the +controller; queue depth is not permission for extra process workers. + +### Reachability and gate evidence keys + +| Key | Current source/gate and interpretation | +| --- | --- | +| SHARED — Shared G17 execution | driver.rs selects g17::Gpu for T6040. g17/queue/mod.rs, runtime.rs, port.rs, runtime/render/direct.rs and runtime/render/submit.rs have no T8140-only gate on these mechanisms. The variant supplies QID floor 32, producer +0x50, two-cluster operands and 25G83 lead commands. | +| DRM — Shared DRM/scheduler/fences | G17 queues use job.rs, fence.rs and the common DRM scheduler/syncobj/GPUVM/shmem interfaces. CPU_FREQ and UCLAMP_TASK are enabled in the release config. Private cumulative scopes are advertised on both G17-backend targets at feature bit 29; foreign fences retain generic handling. | +| MEM — Common memory and lifetime | file.rs::memory_stats, gem.rs, memacct.rs, cleanup.rs, pgtable.rs and mmu.rs serve the T6040 G17 backend. Creator accounting, pins, revocable backing, canonical bindings and deferred destruction are not gated to T8140. T6040 keeps its explicit UAT coherency policy. | +| GMEM — Shared retained GPU resources | g17/freelist.rs, context.rs, object.rs, queue/compute.rs and runtime/render/pools.rs retain and reclaim the same resource owners. Allocations use T6040 core-layout sizes and its reduced QID budget; no Neo-only admission branch disables them. | +| REC — Shared recovery and deadlines | g17/recovery.rs, recovery/safety.rs, timeout.rs and runtime/events.rs serve both firmware variants. T6040 uses its status-B base and one firmware role. Exact retirement, interrupted-work failure, withheld-doorbell replay, progress-aware budgets and sticky VM reporting remain active. Current missing-stamp fallback is 50 ms, not the historical 1 ms. | +| POWER — Shared feed/effort/idle code | g17/feed.rs is instantiated for T6040 and derives CPU floors from each discovered policy maximum. g17/mod.rs::note_submission/idle_check and power.rs share effort 1.0 while work streams and a 16 ms idle clear. The existing optional idle hold is available on T6040 but its default 2 ms adds no extended hold. | +| TIMESTAMP — Shared timestamp publication | timestamp.rs::FirmwareBuffer retains a CPU vmap for the buffer lifetime; g17 completion publishes through it before signalling. T6040 uses this shared firmware-buffer path and its 24 MHz timestamp conversion, not a per-command vmap. | +| PRIORITY — Shared firmware priority profiles | g17/mod.rs::scheduling_policy selects HIGH/MEDIUM/LOW profiles for both targets; file.rs checks CAP_SYS_NICE or DRM master for HIGH and refuses REALTIME. Priority work-ring selection and QoS ownership are shared. | +| FW — Per-chip firmware adaptation | hw/t6040.rs selects V25G83. g17/initdata/v25g83.rs supplies one merged root and status acknowledgement, per-chip OPP/calibration input, separate views and no POWER_ON_DONE. Pointer and operand layouts are selected from Config/Variant rather than copied from the two-instance 26A428 graph. MCache is explicitly gated by V25G83.mcache=false; its firmware table/protocol is not part of admitted T6040 support. | +| PMGR — Shared PMGR state handling | pmdomain/apple/pmgr-pwrstate.c binds T6041 states through the t8103-compatible state protocol. BUSY masking, timeout unwind and firmware-owned/no-auto-PM properties apply through DT; no T8140-only register address is copied. | +| PMP — PMP/report per-chip data | soc/apple/pmp.rs and pmdomain/apple/pmp-report.c already contain T6041 match data: 42-bit DMA, version-5 bootargs checks, CoreAnalytics EP7, T6041 report offsets and three fast-die lanes. T6040 DT supplies the bootargs/report entries and CPU effort phandles. T8132/J713-only telemetry policy is excluded separately. | +| PMC — Shared PMC, T6040 rail mask | pmdomain/apple/pmc.c::apple_pmc_probe/agent_enable use T6040 agent stride, DCS maximum 5 and agent_rails=GENMASK(1,0). CPU/CLPC/AP voting already worked. Port 772a9d3104ff adds apple,pmp and static voter 0 after its supplier binds; display rails remain excluded. | +| CLPC — Shared CLPC, T6040 demand data | soc/apple/clpc.c uses T6040 DCS {1,2,5,5}, fabric {0,1,2,2}, sustain=3, 4 ms sampling and 32 ms hold. CPU demand/lifecycle refinements are shared. GPU demand was missing and is enabled by 0d63308820ab with the existing apple,gpu-power-state phandle. | +| GPUFLOOR — Applicable gate ported | Before g2, apple_clpc_hw_t6040.gpu_demand was false and its CLPC DT node had no apple,gpu-power-state. Port 0d63308820ab enables the same mechanism as #440 with T6040 DCS 5/fabric 2, not Neo 4/3. Hardware gain and idle/PMP cooperation remain unqualified. | +| MCC — MCC translated to 16 planes | soc/apple/mcc.c accepts the T6041/T8140-compatible controller; T6040 DT describes 16 planes and its display boot-DMA dependency. The J616s enables this node. Generic partial-enable unwind, lifetime and SLC allocation rules apply with that data. | +| CPU — Shared CPU frequency/limit handling | T6041 CPU nodes bind the t8112 fallback profile. Generic OPP cleanup, fast-switch and PMP effort QoS use its DT frequencies and three effort lanes. No Neo frequency or thermal curve is copied. | +| LAZY — Different CPU verification protocol | The lazy_verify refinements are T8140-only. T6040 binds soc_t8112_info, whose verify_transition is false; policy->fast_switch_possible is already true. It does not pay the T8140 blocking verification loop that these commits remove. Adding lazy verification would not port a missing gain. | +| DART — Related DARTs; native GPU uses UAT | iommu/apple-dart.c common publication/error/resume/stream logic is used by T6041 DARTs where their DT ownership mode selects it. T6040 display mirroring uses its own compatible/data. Native GFX translations use asahi/mmu.rs UAT, so this is related PMP/display robustness, not a claim that Neo DART addresses fit the G16 GPU. | +| RTKIT — Shared RTKit transport/lifetime | G17 Coprocessor uses the Rust RTKit wrapper over soc/apple/rtkit.c. Shared buffer retention, barriers, crash handling, bounded logs and shutdown ownership apply. T6040 supplies its firmware-data crash buffers and one ASC instance; MTP/J713-only adoption is excluded. | +| MBOX — ASCWrap transport, per-chip registers | soc/apple/mailbox.c and rust/kernel/soc/apple/mailbox.rs provide the common queue, IRQ and receiver ownership rules. T6040 binds its ASCWrap-v6 data; its wrapper/VBAR windows and IRQs come from DT. Neo-specific mailbox addresses are not reused. | +| AIC — Shared effective-affinity reporting | irq-apple-aic.c applies AICv2+ reporting to the T6041 AIC3 compatible too. It reports actual effective routing rather than manufacturing CPU migration. Record the IRQ/worker CPU on J616s; no isolated throughput gain is claimed. | +| DISPLAY — Related shared display hardening | Generic apple/dcp.c, parser and AFK lifecycle/bounds fixes are inherited by the reconstructed display stack where those helpers are used. They protect the display consumer of GPU buffers; they are not standalone compute performance claims. | +| OBS — Observability only when selected | Existing G17 tracepoints/diagnostics and first-failure logging are shared. Trace register sampling is conditional on the tracepoint being enabled; disabled tracing is not a new runtime policy or a measured throughput gain. | +| MMIO — Different registration ABI | runtime/render.rs::install_pair returns before registration power holds when V25G83.mmio_pair_registration=false. 25G83 installs from queue configuration and uses lead commands; 26A428 uses MMIO pair registration. Do not add its two control round trips or register writes to the admitted 25G83 protocol. | +| FEW — 25G83 rejects this fast path | V25G83.few_primitives=false; command.rs and feature reporting enforce it. The supported-source comment records a fragment hang with the flag. It is not an applicable refinement of the admitted 25G83 render ABI without new validated facts. | +| RT — RT specialization not admitted | V25G83.ray_traversal=false; command.rs rejects native scratch/intersection programs and get_params omits the feature. Generic watchdog/recovery infrastructure remains shared, but native RT pools/kill guards/RT-specific long-job use are dormant on the admitted 25G83 path. | +| KEY — Missing validated recovery identities | hw/t6040.rs has recovery_slot_keys=None. recovery/attribution.rs falls back to firmware blame when slot identity evidence is incomplete. Neo slot-key addresses must not be invented for 25G83; this specific attribution refinement remains gated pending validated T6040 facts. | +| UAT — Qualified alternate invalidation policy | hw/t6040.rs keeps reserved_tables, clean_tables and page_tlbi=true. mem.rs::tlbi_range takes the page/ASID alternative before its range encoder. The range-only field fix is dormant; per-SoC coherency/ownership support is active. Removing these working firmware-table requirements is not a justified performance port. | +| DCS — Optional cooling actor, no T6040 curve | PMC cooling/IPA paths require #cooling-cells and power-model DT data. J616s does not select them, and the Neo DCS thermal zone was subsequently removed because it throttled unrelated GPU/CPU heat. No T6040 power curve is invented to re-enable that policy. | +| DCSOFF — Already equivalent inactive zone | Neo removed its oscillating DCS thermal zone. J616s has no such zone to remove. Its PMP CPU/GPU limits remain; this does not justify disabling thermal protection. | +| IRQENTRY — DT-selected report context | IRQ-safe report entries are selected by apple,irq-safe for PMGR-parent use. GFX report consumption does not require copying the external-display parent topology; use the actual T6040 entry/consumer hierarchy. | +| STAT — Not the Neo backend | StatsChannel/GpuManager agx_stats decoding belongs to the maintained backend. g17::Gpu::stats_snapshot returns None on both Neo and T6040. It is not a Neo optimization waiting to be ported to G16S. | +| G15 — Different backend/hardware | T6030/T6031/T8122/G15 and M3-only runtime commits do not execute in T6040 g17::Gpu. Their firmware records, generation-specific power policy and native-G15 admission cannot be copied into G16S/25G83. | +| G16 — Different G16 backend/owner ABI | T8132/J713 uses the separate g16 backend and retained-owner PMP/TVM protocol. T6040 is G16S but dispatches to the g17 backend with V25G83, not that G16G runtime. Match/board gates and firmware ownership differ. | +| LEGACY — Maintained G13/G14 path | The workqueue/manager/versioned AGX firmware path is not T6040's G17 backend. G13/G14 automatic preemption, timestamp-copy and old InitData changes are not missing G16S mechanisms. | +| OTHERCHIP — Other CPU/guest hardware | T8142/T8152/T6050-specific transition, ownership or guest-timer data is not selected by T6041 CPU/GPU match data. No unsupported register/profile change is ported. | +| TUNNEL — Other peripheral topology | M1 tunneled PCIe/Thunderbolt recovery is not a Neo native-GPU improvement. T6040 has its own topology; these records are retained for coverage, not presented as a gated G16S optimization. | +| PERIPH — Not a GPU refinement | This scoped-history/PR neighbor concerns NVMe, HID, audio, SEP, USB, RTC or another peripheral, not a Neo native-GPU performance/handling mechanism. No port is implied. | +| BOARD — Neo-specific board route | The literal J700 topology/display source/domain-cell fix is not T6040 board data. Its independent J616s display/PMP/GPU composition is retained; copying a Neo route or physical address would be wrong. | +| RAWDIAG — Superseded raw diagnostics | The raw T8140 PMP DVFS debugfs dump was later removed. Current reporting uses validated per-SoC fields; no new raw interface or platform knob is added. | +| OLDTHERMAL — Superseded thermal policy | The interim J700 SMC CPU policy was removed/replaced by thermal-framework and PMP handling. It is not the policy selected for T6040 and no Neo temperature/power curve is copied. | +| SUPERSEDED — Superseded inside the series | The unconditional-doorbell/unbound-inline policies were refined by later #423 commits; the early disabled-J616s admission was replaced by calibrated #463 enablement. Current code preserves low-pressure probes and defers/coalesces eligible doorbells with explicit release/stall/recovery handling; do not reapply the obsolete intermediate policy. | +| VERIFY — Build/test/documentation only | This record changes tests, descriptions, bindings, layout/build checks or source style rather than a runtime performance mechanism. Its contract is retained where applicable; there is no isolated hardware speedup or new platform behavior to port. | +| MERGE — Integration record | This merge is provenance/composition, not an isolated performance mechanism. Its parent changes are enumerated below, including variant preservation and subsequent fixes; no separate speedup is assigned to a merge. | + +### Hardware measurements referenced by each commit + +All hardware actions require the controller's explicit J616s identity, policy +approval and ownership/session/resource gates. These are a measurement plan, +not permission to bypass them. Pair g1/g2 with the same loader and Mesa and +preserve failed runs, logs and r6g rollback. + +| Code | Measurement | +| --- | --- | +| C | compute.light d1/d2/d64; submit API wall and thread-CPU time, scheduler hops, faults and CPU-frequency/IRQ context; matching Mesa/suite. | +| R | render.light d1/d2/d32 and 8 queues, first render, heavy fill and tiler growth; API p50/p99, throughput and memory high-water. | +| H | same/cross-queue binary and timeline hops; p50/p95/p99, doorbells per command and 100k order/data checks; include eligible and fallback dependencies. | +| I | idle.wake after 2/20/100 ms, default policy, first-frame latency and clocks; 60/120 Hz frame pacing and idle power. Keep optional hold experiments separate. | +| F | controller-approved bounded stall/fault/long-job stress with innocent peers; no wrong data or false successful fence, recovery phase/time, VM errors and post-close reuse. | +| M | multi-client create/close/rearm and approved bounded pressure; committed/resident bytes, retained pools/QIDs, restoration time and leak slope. | +| P | PMP readiness, PMC per-agent requests/honoured floors and CLPC readbacks during GPU-only, mixed CPU/GPU, 90 s heavy load, idle, video and browsing; verify idle release and no sustained thermal regression. | +| T | timestamp/query correctness, 24 MHz conversion, aligned/unaligned publication where supported, buffer-map reuse and query CPU cost. | +| Q | privileged/unprivileged priority admission and concurrent LOW/MEDIUM/HIGH progress, global-priority transition cases and timeout attribution. | +| D | approved bounded VM_BIND/unmap/remap and DART ownership/resume boundaries; CPU cost, invalidation errors and zero unexpected DART/UAT faults. | +| B | exact boot/kernel/loader/Mesa pins, firmware admission, first queue/install, panel/display regression, supplier readiness and rollback. | +| O | existing tracepoints/counters under approved instrumentation; IRQ and event-worker CPU, pass duration, notifications and failure site. Tracing off remains the reference. | +| V | static/build/contract or capability-refusal check; no isolated hardware improvement claimed. | + +### Every commit in #423 + +All 65 entries are retained, including intermediate policies, tests and the +integration merge. Superseded steps are not mistaken for current behavior. + +| Commit | Author | Improvement / record | T6040 after ports — evidence key and changed source | Measurement | +| --- | --- | --- | --- | --- | +| [ad691cd5e9f7](https://github.com/aurora-silicon/linux/commit/ad691cd5e9f76aa9e486d93b560c58d5c8ff88c4) | DjDeveloperr | drm/asahi: publish bound G17 compute work without the admission passes [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/completion.rs` | C,H | +| [1121ae36b8d4](https://github.com/aurora-silicon/linux/commit/1121ae36b8d426be24c41ae360281b5f0709a69d) | DjDeveloperr | drm/asahi: wake G17 render publication when its pair is returned [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/mod.rs` | R,H | +| [abd85935bd4d](https://github.com/aurora-silicon/linux/commit/abd85935bd4db7db6be23b8194b1173c9a8988c5) | DjDeveloperr | drm/asahi: clear each G17 compute descriptor only once [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/fw/compute.rs` | C,H | +| [3ef3720890f8](https://github.com/aurora-silicon/linux/commit/3ef3720890f8c085f18e4f5e35db6853075b121c) | DjDeveloperr | drm/asahi: retain compute pool backing through pending channel admission [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — GMEM; `drivers/gpu/drm/asahi/g17/capacity.rs` | M,C,R | +| [f35d7ea1b373](https://github.com/aurora-silicon/linux/commit/f35d7ea1b37395a9bc1241d0fafcb698e0c12d46) | DjDeveloperr | dma-buf: stop polling an array after its first pending member [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — DRM; `drivers/dma-buf/dma-fence-array.c` | C,H,M,F | +| [319805fed41b](https://github.com/aurora-silicon/linux/commit/319805fed41bb181db8e90378beaf905f38f6297) | DjDeveloperr | drm/asahi: avoid unbound compute inline attempts on compute-only queues [#423](https://github.com/aurora-silicon/linux/pull/423) | Not applicable — SUPERSEDED; `drivers/gpu/drm/asahi/g17/context.rs` | C,H | +| [5a4822c6fb3d](https://github.com/aurora-silicon/linux/commit/5a4822c6fb3d1fc0a83d9b754b477bf1db35d782) | DjDeveloperr | drm/asahi: preserve low-pressure compute inline probes [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/runtime.rs` | C,H | +| [026065c43526](https://github.com/aurora-silicon/linux/commit/026065c435260844e39d05a182e99b128c5f9b2d) | DjDeveloperr | drm/asahi: submit same-VM compute input waits through firmware [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/capacity.rs` | C,H | +| [89c8873651b7](https://github.com/aurora-silicon/linux/commit/89c8873651b729fdd4e981e87b9d0df0d0ad0017) | DjDeveloperr | drm/asahi: treat a firmware source as complete once its retirement is queued [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/completion.rs` | C,R,H | +| [3ff4f4baa05a](https://github.com/aurora-silicon/linux/commit/3ff4f4baa05a88d5f99d2fee76089cf72b5617a4) | DjDeveloperr | drm/asahi: publish G17 commands from the ioctl at any queue depth [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/mod.rs` | C,R,H | +| [a0dfc3c0b5a4](https://github.com/aurora-silicon/linux/commit/a0dfc3c0b5a4fced66119fded07ed141990a9025) | DjDeveloperr | drm/asahi: map G17 compute descriptors cached for the firmware [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | C,H | +| [a36e352d8564](https://github.com/aurora-silicon/linux/commit/a36e352d8564947071fef7e11dba709f399cd106) | DjDeveloperr | drm/asahi: size the G17 compute QID budget by open logical queues [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/kick.rs` | C,H | +| [b141ca9b16c3](https://github.com/aurora-silicon/linux/commit/b141ca9b16c33abb26238782cd7c0b751b5f52fe) | DjDeveloperr | drm/asahi: order same-VM render attachment overlaps in firmware [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/completion.rs` | R,H | +| [a475f4f9f1f7](https://github.com/aurora-silicon/linux/commit/a475f4f9f1f721b6636a4147dacbcf9f6224763b) | DjDeveloperr | drm/asahi: map G17 compute command records cached for the firmware [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | C,H | +| [ca71e4449b4a](https://github.com/aurora-silicon/linux/commit/ca71e4449b4a23d5f4e0149db76cf5198bfa7e5f) | DjDeveloperr | drm/asahi: stop the G17 compute completion scan once every record is found [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/initdata.rs` | C,H | +| [1cdac9ba0ddd](https://github.com/aurora-silicon/linux/commit/1cdac9ba0dddec96f59eb02a5067ee343aae43f4) | DjDeveloperr | drm/asahi: publish G17 compute into a bound queue without the device mutex [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/context.rs` | C,H | +| [7242bf87efd2](https://github.com/aurora-silicon/linux/commit/7242bf87efd2b3f4f8bf2a31fadb79b3e687f195) | DjDeveloperr | drm/asahi: send a G17 compute queue configuration only when it changes [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | C,H | +| [014605a36795](https://github.com/aurora-silicon/linux/commit/014605a3679581351913c3ab137653297f7a0cf7) | DjDeveloperr | drm/asahi: add G17 resource wait domains and use them for queue creation [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/availability.rs` | C,R,H | +| [64f717dcfd1e](https://github.com/aurora-silicon/linux/commit/64f717dcfd1e4aa5856882181f5c2ac040c5ce4e) | DjDeveloperr | drm/asahi: wait for G17 compute capacity on the release that frees it [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/availability.rs` | C,H | +| [327e50fc45b5](https://github.com/aurora-silicon/linux/commit/327e50fc45b57acf1954e8c2d78977198286feb0) | DjDeveloperr | drm/asahi: wait for a G17 compute source only through its completion record [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/runtime.rs` | C,H | +| [6f4d3bbb47c1](https://github.com/aurora-silicon/linux/commit/6f4d3bbb47c1a7b19abef118b41b131440a8f414) | DjDeveloperr | drm/asahi: add G17 command lifecycle tracepoints [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — OBS; `drivers/gpu/drm/asahi/g17/mod.rs` | O,F | +| [e24e3bad9e7d](https://github.com/aurora-silicon/linux/commit/e24e3bad9e7d3c6cf7079d683c9364d619cd3a55) | DjDeveloperr | drm/asahi: ring the G17 compute pipe doorbell for every publication [#423](https://github.com/aurora-silicon/linux/pull/423) | Not applicable — SUPERSEDED; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | C,H | +| [94c66ecf24e6](https://github.com/aurora-silicon/linux/commit/94c66ecf24e6d931a434914d6a513ee36ff6ffd7) | DjDeveloperr | drm/asahi: map the G17 compute queue graph cached for the firmware [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | C,H | +| [db365ecc299f](https://github.com/aurora-silicon/linux/commit/db365ecc299f0663db8a629e70dfdb4d117eb95e) | DjDeveloperr | drm/asahi: wait for G17 render capacity on the release that frees it [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/context.rs` | R,H | +| [06bc290c266a](https://github.com/aurora-silicon/linux/commit/06bc290c266abcb8b0d45f75c3369e231ae8c4a7) | DjDeveloperr | drm/asahi: give the G17 render scheduler the pair's depth as credits [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/job.rs` | R,H | +| [3d1f2047766c](https://github.com/aurora-silicon/linux/commit/3d1f2047766c2d6e8d165a4af2cfe4adc0edd366) | DjDeveloperr | drm/asahi: keep a G17 render free list registered while the firmware is busy [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/freelist.rs` | R,H | +| [b7fa0d482eea](https://github.com/aurora-silicon/linux/commit/b7fa0d482eea670f9004422eaf57703a7ec8d4e5) | DjDeveloperr | drm/asahi: take the G17 device mutex less often to publish a render [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/admission.rs` | R,H | +| [d272a7060d20](https://github.com/aurora-silicon/linux/commit/d272a7060d20a0beb635ca8426c8822b8e823a43) | DjDeveloperr | drm/asahi: publish a G17 render into its pair without the device mutex [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/initdata.rs` | R,H | +| [6428f7fd42fe](https://github.com/aurora-silicon/linux/commit/6428f7fd42fe075981984da569b0fa076f59a127) | DjDeveloperr | drm/asahi: lend a G17 render pair without the identity check first [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/runtime.rs` | R,H | +| [8cf02f589bce](https://github.com/aurora-silicon/linux/commit/8cf02f589bce34b13002cbdf0f3b0361c4a8e66b) | DjDeveloperr | drm/asahi: wait for G17 render admission before run_job [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/admission.rs` | R,H | +| [550fb355e9a3](https://github.com/aurora-silicon/linux/commit/550fb355e9a30c0db9c59fe35a931f1db9c6b5c4) | DjDeveloperr | drm/asahi: trace the G17 core power state at each work doorbell [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — OBS; `drivers/gpu/drm/asahi/g17/mod.rs` | O,F | +| [f381ac9b71eb](https://github.com/aurora-silicon/linux/commit/f381ac9b71ebcb70eb72497ec4115553856a31fc) | DjDeveloperr | drm/asahi: trace G17 host-to-firmware messages [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — OBS; `drivers/gpu/drm/asahi/g17/channel.rs` | O,F | +| [adcd56e02036](https://github.com/aurora-silicon/linux/commit/adcd56e020362664c7bc3af288e1ad812df7b4c7) | DjDeveloperr | drm/asahi: maintain cumulative completion scopes without repeated fence merges [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — DRM; `drivers/gpu/drm/asahi/g17/fence.rs` | C,H,M,F | +| [bcdfdafd80cf](https://github.com/aurora-silicon/linux/commit/bcdfdafd80cf93fadc79df688dd0109b9c4ae8d2) | DjDeveloperr | drm/asahi: test G17 queue budgets with firmware-reserved identities [#423](https://github.com/aurora-silicon/linux/pull/423) | Not applicable — VERIFY; `drivers/gpu/drm/asahi/g17/kick.rs` | V | +| [971d4316d91c](https://github.com/aurora-silicon/linux/commit/971d4316d91cce97b92db72c4f29e5155b76d626) | DjDeveloperr | drm/asahi: size cached compute records for every native wait [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | C,H | +| [9bf3039873a1](https://github.com/aurora-silicon/linux/commit/9bf3039873a1e241991b5999aceff5610c660ade) | DjDeveloperr | drm/asahi: index pending compute completion identities before reading payloads [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/compute/retirement.rs` | C,H | +| [26446182da3a](https://github.com/aurora-silicon/linux/commit/26446182da3aa8d98ce799478a025117b021eaba) | DjDeveloperr | drm/asahi: retain a deep working set of validated GPU page ranges [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/validation.rs` | C,R,D | +| [b6248acf900c](https://github.com/aurora-silicon/linux/commit/b6248acf900c1cf30deac52791c2c50d10055d7b) | DjDeveloperr | drm/asahi: avoid locking pending private completion fences [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/job.rs` | C,R,H | +| [b08c81722d89](https://github.com/aurora-silicon/linux/commit/b08c81722d896eee8da9771e99e38e6986a2a806) | DjDeveloperr | drm/asahi: give G17 firmware restarts time to drain long work [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — REC; `drivers/gpu/drm/asahi/g17/recovery.rs` | F,Q | +| [f1db16a83586](https://github.com/aurora-silicon/linux/commit/f1db16a83586e4bec596a71e12e0d8b87dd3b16c) | DjDeveloperr | drm/asahi: fail commands a GPU restart interrupted instead of completing them [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — REC; `drivers/gpu/drm/asahi/g17/completion.rs` | F,Q | +| [30f85560fd28](https://github.com/aurora-silicon/linux/commit/30f85560fd28ce05151c16eab7a58433d125a81e) | DjDeveloperr | drm/asahi: stop the G17 device on a watchdog storm only without progress [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — REC; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | F,Q | +| [2c0abe2d090a](https://github.com/aurora-silicon/linux/commit/2c0abe2d090a9701b13b22d5ac56fb24d87cfe12) | DjDeveloperr | drm/asahi: scan only the G17 compute completion records written since the last pass [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/initdata.rs` | C,H | +| [39bdd5b8e2d5](https://github.com/aurora-silicon/linux/commit/39bdd5b8e2d584fff0d342a71a8945e64c8a9673) | DjDeveloperr | drm/asahi: release pruned G17 frontier entries after the ioctl publishes [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/mod.rs` | C,R,H | +| [b41ecb56c077](https://github.com/aurora-silicon/linux/commit/b41ecb56c0775a43b747adaa37661b353651dbff) | DjDeveloperr | drm/asahi: publish both G17 render QoS owners in one pass [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/port.rs` | R,H | +| [b86c445ba282](https://github.com/aurora-silicon/linux/commit/b86c445ba2829f644075ca663217abe65844e8c0) | DjDeveloperr | drm/asahi: stop copying G17 fragment registers to the unused client page [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/render/memory.rs` | C,R,H | +| [5a55068193e2](https://github.com/aurora-silicon/linux/commit/5a55068193e213d7115a63f2222ffc304a4ced98) | DjDeveloperr | drm/asahi: read the G17 render work rings once per direct publication [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/port.rs` | R,H | +| [b67018ed5974](https://github.com/aurora-silicon/linux/commit/b67018ed5974069c13c13249834dbc3b84e806ed) | DjDeveloperr | drm/asahi: request the G17 tiling descriptor slot before preparing a render [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/render.rs` | R,H | +| [4d0362d0c18d](https://github.com/aurora-silicon/linux/commit/4d0362d0c18d288d3bd520fdfb69c2e7624cf6b0) | DjDeveloperr | drm/asahi: store G17 register arrays without an intermediate copy [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/fw/kick.rs` | C,R,H | +| [861b39d948f6](https://github.com/aurora-silicon/linux/commit/861b39d948f6c0588b3646e1246e660b52230e5b) | DjDeveloperr | drm/asahi: also request the G17 queue configuration records early [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/render/memory.rs` | C,R,H | +| [66ef051d8bd4](https://github.com/aurora-silicon/linux/commit/66ef051d8bd4e66bdc145eb7fb7a920235a8e668) | DjDeveloperr | drm/asahi: scan only render pool slots for G17 free-list leases [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — GMEM; `drivers/gpu/drm/asahi/g17/runtime/render/pools.rs` | M,C,R | +| [1aec5be232da](https://github.com/aurora-silicon/linux/commit/1aec5be232da7bacbbe38b7f9b22c27906eac8c6) | DjDeveloperr | drm/asahi: omit G17 render queue configurations that only reload the held completion [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/render.rs` | R,H | +| [d0df244b8f02](https://github.com/aurora-silicon/linux/commit/d0df244b8f023037bb0f9cc9793f1d0f285cfc87) | DjDeveloperr | drm/asahi: keep one CPU mapping per G17 timestamp buffer [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — TIMESTAMP; `drivers/gpu/drm/asahi/g17/completion.rs` | T,C | +| [3ff8c49c5f12](https://github.com/aurora-silicon/linux/commit/3ff8c49c5f12b908a72e6c076ff0d7336ffddcb8) | DjDeveloperr | drm/asahi: withhold G17 compute doorbells behind two unretired commands [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | C,H | +| [b1f5dc6df0ba](https://github.com/aurora-silicon/linux/commit/b1f5dc6df0ba5650c8424027a025db233c6544c4) | DjDeveloperr | drm/asahi: drop G17 compute doorbells that another doorbell announced [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/port.rs` | C,H | +| [d38cce2d8be5](https://github.com/aurora-silicon/linux/commit/d38cce2d8be54bdbc01e90e1a92ef3dd65ac0640) | DjDeveloperr | drm/asahi: announce withheld G17 compute doorbells whenever retirement stalls [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | C,H | +| [2d8286101c86](https://github.com/aurora-silicon/linux/commit/2d8286101c8631939cb13ce1b7b14c9041856ca6) | DjDeveloperr | drm/asahi: build the G17 driver without warnings [#423](https://github.com/aurora-silicon/linux/pull/423) | Not applicable — VERIFY; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | V | +| [9a0ed807e4d3](https://github.com/aurora-silicon/linux/commit/9a0ed807e4d3691a5ea46fce2a2424295f8af6ea) | DjDeveloperr | drm/asahi: renew G17 job deadlines while the GPU keeps retiring work [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — REC; `drivers/gpu/drm/asahi/g17/job.rs` | F,Q | +| [afbfbc0b89ca](https://github.com/aurora-silicon/linux/commit/afbfbc0b89ca4779b13cefff5bed0b4036751b2c) | DjDeveloperr | drm/asahi: kill closed G17 contexts only while no other work runs [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — REC; `drivers/gpu/drm/asahi/g17/runtime/compute_exit.rs` | F,Q | +| [31279abf0648](https://github.com/aurora-silicon/linux/commit/31279abf0648c00e454b181f01d65b277a32fb90) | DjDeveloperr | drm/asahi: fail G17 render groups a recovery dropped out of order at once [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — REC; `drivers/gpu/drm/asahi/g17/queue/render.rs` | F,Q | +| [7012a59ed129](https://github.com/aurora-silicon/linux/commit/7012a59ed129baf969d83a3aa4d90f2a6cb410f2) | DjDeveloperr | drm/asahi: announce withheld G17 compute doorbells after a recovery [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — REC; `drivers/gpu/drm/asahi/g17/recovery.rs` | F,Q | +| [8e257af33a6c](https://github.com/aurora-silicon/linux/commit/8e257af33a6c4036ee56bc8a628e733dba2bd525) | DjDeveloperr | drm/asahi: move the G17 cumulative-signals feature to bit 29 [#423](https://github.com/aurora-silicon/linux/pull/423) | Active — SHARED; `drivers/gpu/drm/asahi/g17/sync_output.rs` | C,R,H | +| [76d4e880149e](https://github.com/aurora-silicon/linux/commit/76d4e880149e5592396001f728f7803efb616377) | DjDeveloperr | drm/asahi: build the G17 compute QID budget test against the variant constructor [#423](https://github.com/aurora-silicon/linux/pull/423) | Not applicable — VERIFY; `drivers/gpu/drm/asahi/g17/runtime.rs` | V | +| [d0da7e79dc9c](https://github.com/aurora-silicon/linux/commit/d0da7e79dc9c99020d109e95d18256cd5b3d6bc2) | DjDeveloperr | drm/asahi: check the G17 QID floor test against the open-queue compute budget [#423](https://github.com/aurora-silicon/linux/pull/423) | Not applicable — VERIFY; `drivers/gpu/drm/asahi/g17/runtime.rs` | V | +| [18b408b7368b](https://github.com/aurora-silicon/linux/commit/18b408b7368b9470c22d92c9afe1991d0fe608ba) | DjDeveloperr | drm/asahi: check the G17 admission churn test against its deadline, not the clock [#423](https://github.com/aurora-silicon/linux/pull/423) | Not applicable — VERIFY; `drivers/gpu/drm/asahi/g17/preparation.rs` | V | +| [de1be1b4b5b1](https://github.com/aurora-silicon/linux/commit/de1be1b4b5b110ae60877f77aa1637a920333579) | DjDeveloperr | Merge integration/aurora into fix/g17-recovery-dropped-groups-20261025 [#423](https://github.com/aurora-silicon/linux/pull/423) | Not applicable — MERGE | V | + +### Other Neo and shared-support commits + +Each listed alias has the stated current mechanism/gate. Device-specific data +uses the T6040 counterpart, not literal Neo register/DT values. + +| Commits / encountered aliases | Author | Improvement / record | T6040 after ports — evidence key and changed source | Measurement | +| --- | --- | --- | --- | --- | +| [6a5d96e61fa5](https://github.com/aurora-silicon/linux/commit/6a5d96e61fa52810e709a0b8bb5e9988df78a7d7) | Sasha Finkelstein | cpufreq: apple-soc: Calculate frequency as a 64-bit value | Active — CPU; `drivers/cpufreq/apple-soc-cpufreq.c` | C,I,P | +| [a861779b92c2](https://github.com/aurora-silicon/linux/commit/a861779b92c259c0a5ba7ec3fd512ad7bda1b8d4) | Asahi Lina | rust: soc: apple: rtkit: Add Apple RTKit abstraction | Active — RTKIT; `drivers/soc/apple/Kconfig` | B,F | +| [4b42187aef53](https://github.com/aurora-silicon/linux/commit/4b42187aef53c266b472e731e4f8e0a1fe0de3a7) | Janne Grunau | rust: soc: apple: rtkit: Add ASC CPU control register | Active — RTKIT; `rust/kernel/soc/apple/rtkit.rs` | B,F | +| [6a2c57b5a719](https://github.com/aurora-silicon/linux/commit/6a2c57b5a719f7c71fe6252865e4f036f6d27a64) | Sasha Finkelstein | soc: apple: rtkit: Add apple_rtkit_has_endpoint() | Active — RTKIT; `drivers/soc/apple/rtkit.c` | B,F | +| [b7fbb2fde4ea](https://github.com/aurora-silicon/linux/commit/b7fbb2fde4eaa0ac53e46dcb76a835fdc9cd9477) | Sasha Finkelstein | rust: soc: apple: rtkit: Add apple_rtkit_has_endpoint | Active — RTKIT; `rust/kernel/soc/apple/rtkit.rs` | B,F | +| [bba00880a59b](https://github.com/aurora-silicon/linux/commit/bba00880a59b144155716928066b12cc5751dd52) | Sasha Finkelstein | rust: soc: apple: Add Apple mailbox abstractions | Active — MBOX; `drivers/soc/apple/Kconfig` | B,O | +| [883657fe64b3](https://github.com/aurora-silicon/linux/commit/883657fe64b3a62fa36c90666b0a00a4e5093166) | Sasha Finkelstein | soc: apple: Add support for the PMP co-processor | Active — PMP; `drivers/soc/apple/Kconfig` | P,B | +| [fccd1dbe8ba8](https://github.com/aurora-silicon/linux/commit/fccd1dbe8ba820d4d9f5d1a624987e2820035204), [04841b120bbf](https://github.com/aurora-silicon/linux/commit/04841b120bbf7a2fb454cbfa984647e867b64848), [da7aa98b1aae](https://github.com/aurora-silicon/linux/commit/da7aa98b1aae834db5f7f4a8396afc39099d1b2f) | Janne Grunau | fixup! soc: apple: Add support for the PMP co-processor | Active — PMP; `drivers/soc/apple/pmp.rs` | P,B | +| [c81971a6c4c7](https://github.com/aurora-silicon/linux/commit/c81971a6c4c706b2f5783653a2df8d79782d4efe) | Asahi Lina | drm/scheduler: Fix UAF in drm_sched_fence_get_timeline_name | Active — DRM; `drivers/gpu/drm/scheduler/sched_entity.c` | C,H,M,F | +| [94bfb56baeeb](https://github.com/aurora-silicon/linux/commit/94bfb56baeeb02bc9c9ce8616aef0bd72a5de3f2) | Asahi Lina | rust: drm: sched: Add GPU scheduler abstraction | Active — DRM; `rust/kernel/drm/mod.rs` | B,M | +| [1340031e447c](https://github.com/aurora-silicon/linux/commit/1340031e447c56ec9d0f8ea7a68436a623fafc91) | Asahi Lina | drm/asahi: Add the Asahi driver for Apple AGX GPUs | Active — MEM; `drivers/gpu/drm/Kconfig` | M,D | +| [af74e2af96bc](https://github.com/aurora-silicon/linux/commit/af74e2af96bc483417f5015f98f4aa54e355851d) | Janne Grunau | drm/asahi: v6.19 probe change | Active — MEM; `drivers/gpu/drm/asahi/driver.rs` | M,D | +| [8c048a0ab849](https://github.com/aurora-silicon/linux/commit/8c048a0ab84971117a5f641ba2185b51e0b12059) | Janne Grunau | drm/asahi: Move unsafe data initialization to driver code | Active — MEM; `drivers/gpu/drm/asahi/driver.rs` | M,D | +| [a95f0620d24c](https://github.com/aurora-silicon/linux/commit/a95f0620d24c456c74d11261e329df054bf70e7b) | Janne Grunau | drm/asahi: RiiR page tables | Active — MEM; `drivers/gpu/drm/asahi/alloc.rs` | M,D | +| [19e9dc7ccb94](https://github.com/aurora-silicon/linux/commit/19e9dc7ccb9499ac3c92e7764edc2312656dc9d5) | Janne Grunau | drm/asahi: Implement ASAHI_BIND_SINGLE_PAGE (mmu/pgtbl) | Active — MEM; `drivers/gpu/drm/asahi/mmu.rs` | M,D | +| [96ce16e432a2](https://github.com/aurora-silicon/linux/commit/96ce16e432a213f7ae185870826697900c9acc60) | Asahi Lina | drm/asahi: pgtable: Add dumper | Active — MEM; `drivers/gpu/drm/asahi/pgtable.rs` | M,D | +| [ad2fb060c5dc](https://github.com/aurora-silicon/linux/commit/ad2fb060c5dcce503bc099062ff54c237b6866d0) | Asahi Lina | drm/asahi: pgtable: Add helpers for decoding PTE perms | Active — MEM; `drivers/gpu/drm/asahi/pgtable.rs` | M,D | +| [7b55670c3a59](https://github.com/aurora-silicon/linux/commit/7b55670c3a59219e37192a1f5771a86217008808) | Janne Grunau | drm/asahi: Copy tyr's mmu/vm/range.rs | Active — MEM; `drivers/gpu/drm/asahi/asahi.rs` | M,D | +| [9d16f582e208](https://github.com/aurora-silicon/linux/commit/9d16f582e2085935f893f99326fbcc0346c998bb) | Janne Grunau | drm/asahi: Switch gpuvm to DRM_GPUVM_IMMEDIATE_MODE | Active — MEM; `drivers/gpu/drm/asahi/file.rs` | M,D | +| [829551e64f1a](https://github.com/aurora-silicon/linux/commit/829551e64f1a350adddb71751be16a0243e1b666) | Janne Grunau | drm/asahi: MemFlag change in v6.19 | Active — MEM; `drivers/gpu/drm/asahi/gpu.rs` | M,D | +| [f5b02e71cd67](https://github.com/aurora-silicon/linux/commit/f5b02e71cd671ac4e3ceff54bbd1b25e7f20ae9d) | Janne Grunau | drm/asahi: rtkit pin adaptation | Active — MEM; `drivers/gpu/drm/asahi/gpu.rs` | M,D | +| [4220285e8415](https://github.com/aurora-silicon/linux/commit/4220285e8415cc772736b57f0dface3dcdbbee4f) | Janne Grunau | drm/asahi: select APPLE_PMP_REPORT | Active — PMP; `drivers/gpu/drm/asahi/Kconfig` | P,B | +| [30d957799282](https://github.com/aurora-silicon/linux/commit/30d9577992828e8d2f8345cc4b34a19098ca068c) | Janne Grunau | drm/asahi: Improve VM bind performance for large BOs | Active — MEM; `drivers/gpu/drm/asahi/mmu.rs` | M,D | +| [22e9a0b69598](https://github.com/aurora-silicon/linux/commit/22e9a0b69598bc23e66d72d1e51bd40396966dc1) | Janne Grunau | drm/asahi: `use` formatting annd rename changes | Active — MEM; `drivers/gpu/drm/asahi/driver.rs` | M,D | +| [f014fcba9d71](https://github.com/aurora-silicon/linux/commit/f014fcba9d71972198f28c3d5315605669073bea) | Janne Grunau | drm/asahi: v7.1 Mmio relaxed changes | Active — MEM; `drivers/gpu/drm/asahi/regs.rs` | M,D | +| [20dec7b1eaa9](https://github.com/aurora-silicon/linux/commit/20dec7b1eaa9a31dc67a8216d338bb10a452659d) | DesktopECHO | drm/asahi: Clean up deferred BOs when dropping a VM | Active — MEM; `drivers/gpu/drm/asahi/file.rs` | M | +| [e07ff81ee812](https://github.com/aurora-silicon/linux/commit/e07ff81ee812fd84603a498beaa1bb25eb629caa) | Janne Grunau | fixup! rust: soc: apple: rtkit: Add Apple RTKit abstraction | Active — RTKIT; `rust/kernel/soc/apple/rtkit.rs` | B,F | +| [44a01c258b11](https://github.com/aurora-silicon/linux/commit/44a01c258b111368f3c75848553ae1a658f46013) | Janne Grunau | drm/asahi: Adjustment for v7.2 + partial drm-rust v7.3 | Active — MEM; `drivers/gpu/drm/asahi/alloc.rs` | M,D | +| [23b564dd6c09](https://github.com/aurora-silicon/linux/commit/23b564dd6c09982106e2b5cc5ce80c25010c354a) | Janne Grunau | drm/asahi: pgtable: Drop more dev coredump related code | Active — MEM; `drivers/gpu/drm/asahi/pgtable.rs` | M,D | +| [b7f0faf4da36](https://github.com/aurora-silicon/linux/commit/b7f0faf4da367be4a29146f5eb82b795dc2f270d), [afffea42ac29](https://github.com/aurora-silicon/linux/commit/afffea42ac29a1f6f8b3ef3819691c1d7b0a259a) | Hojun Cho | pmdomain: apple: Add missing spin_lock_init | Active — PMP; `drivers/pmdomain/apple/pmp-report.c` | P,B | +| [0de0a3740a9a](https://github.com/aurora-silicon/linux/commit/0de0a3740a9a26973e7e49ad1cd87981914c457a), [919b934ff719](https://github.com/aurora-silicon/linux/commit/919b934ff7191fa72fab73ecf4ac785dd570c3c3), [ae512e4edf8b](https://github.com/aurora-silicon/linux/commit/ae512e4edf8b96d5eacd79b11fb190837d311b1b) | Ryan Murray | arm64: dts: apple: t8140-pmgr: Add ANS, SPMI, PMP and SMC I2C domains | Active — PMP | P,B | +| [2740578bc68c](https://github.com/aurora-silicon/linux/commit/2740578bc68c410528172a54f9e2e19d35c255d1), [c96a7ffae393](https://github.com/aurora-silicon/linux/commit/c96a7ffae39306507c405d49a3a19927eab289fe), [5578a4e73d10](https://github.com/aurora-silicon/linux/commit/5578a4e73d10d63615ed1eaaf5a7215310df19b8) | Ryan Murray | arm64: dts: apple: t8140: Add CPU frequency scaling | Active translated data — FW | B,P | +| [e7e641d03183](https://github.com/aurora-silicon/linux/commit/e7e641d03183aa76578a1504a4758da6b71c968f), [b58c399f44d7](https://github.com/aurora-silicon/linux/commit/b58c399f44d7499ed62c907efda7b0017f421baf), [f9ec3820d63d](https://github.com/aurora-silicon/linux/commit/f9ec3820d63d57ddb9790ac168a5642efc11f21c) | Ryan Murray | arm64: dts: apple: t8140: Use the Tahiti CPU compatibles and L1 cache sizes | Active translated data — FW | B,P | +| [c702ccc166bb](https://github.com/aurora-silicon/linux/commit/c702ccc166bbe593718a8b4d8a51063e6c5bc102), [f104bb09ce2a](https://github.com/aurora-silicon/linux/commit/f104bb09ce2abf48baa4cf5028b9224141cfdd00), [118ffba5b7b0](https://github.com/aurora-silicon/linux/commit/118ffba5b7b06b9786e24dfab819caa1fb1a2333) | Ryan Murray | arm64: dts: apple: t8140-j700: Enable ANS, SMC and CPU frequency scaling | Active translated data — FW | B,P | +| [5e5d5289f17e](https://github.com/aurora-silicon/linux/commit/5e5d5289f17ede272d495e7e4db117d47c6523a3), [dd366e9d3a8e](https://github.com/aurora-silicon/linux/commit/dd366e9d3a8e05ab209c7ebe1b28413820f018d4) | Marcelo Alcantara | iommu: apple-dart: raise MAX_DARTS_PER_DEVICE to 8 | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [e1817d4152f5](https://github.com/aurora-silicon/linux/commit/e1817d4152f57baca92148c8bbc48fe9b0a8e9f9), [97cdb08431f3](https://github.com/aurora-silicon/linux/commit/97cdb08431f3eef20c2e24c41166bc5dee9e9976), [162b64627bbc](https://github.com/aurora-silicon/linux/commit/162b64627bbcb2fa0ae83cbb6cbf86825ced3e33), [70dfd8f93d3c](https://github.com/aurora-silicon/linux/commit/70dfd8f93d3cac6d6ac7ad26909ca6378fd6a2b4) | Ryan Murray | iommu/apple-dart: Check devm_memremap() for an error pointer | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [f57d90040433](https://github.com/aurora-silicon/linux/commit/f57d9004043387ed6c437ddf0afcfcc1bb49f2fd), [6fa300933bb1](https://github.com/aurora-silicon/linux/commit/6fa300933bb1f05c9a49bddc05f8ea3b42c47d35), [79379798f12f](https://github.com/aurora-silicon/linux/commit/79379798f12fdb32ba79f74b0524cfc4f0170471) | Ryan Murray | iommu/apple-dart: Support T8140 DARTs and firmware-owned stream roots | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [6ec845b2f381](https://github.com/aurora-silicon/linux/commit/6ec845b2f3812090ed39893f8e26999c757950c9), [3bd878053ca7](https://github.com/aurora-silicon/linux/commit/3bd878053ca7aed1e4af56bc14c339b8bddc6d9c), [6898f34ac7bd](https://github.com/aurora-silicon/linux/commit/6898f34ac7bd349ce29b60fb9785168950c5ed69), [fb7b8edf4ee1](https://github.com/aurora-silicon/linux/commit/fb7b8edf4ee19a35c07c1a2c6aa6b8e65c71777b) | DjDeveloperr | arm64: dts: apple: t8140-pmgr: Add the GPU power domains [#171](https://github.com/aurora-silicon/linux/pull/171) | Active translated data — FW | B,P | +| [cc2d03580b0a](https://github.com/aurora-silicon/linux/commit/cc2d03580b0a68ca0f9779adebd646645813d673), [ea4abd222b2f](https://github.com/aurora-silicon/linux/commit/ea4abd222b2fa2473dba79ca4a91a220e8cb20ba), [52fac306b907](https://github.com/aurora-silicon/linux/commit/52fac306b90783087af17af7ba3e2fc6570122e5), [e77d74a6be76](https://github.com/aurora-silicon/linux/commit/e77d74a6be7645e64b9b50bb51713e72a08c9520) | DjDeveloperr | arm64: dts: apple: t8140: Add the GPU [#171](https://github.com/aurora-silicon/linux/pull/171) | Active translated data — FW | B,P | +| [8284bd4d3914](https://github.com/aurora-silicon/linux/commit/8284bd4d3914ce296a83c38753a69b07e1c10a0e), [29b0b3a54664](https://github.com/aurora-silicon/linux/commit/29b0b3a546641438b14da95054ae136540465de8), [54124e3efacb](https://github.com/aurora-silicon/linux/commit/54124e3efacba7ba530e742cb2f297bbdeb719fa), [f4a46cd97b2a](https://github.com/aurora-silicon/linux/commit/f4a46cd97b2a3e70875d76e3cf2b82f3959fd0f5) | DjDeveloperr | arm64: dts: apple: t8140-j700: Enable the GPU [#171](https://github.com/aurora-silicon/linux/pull/171) | Active translated data — FW | B,P | +| [31ee8e23124d](https://github.com/aurora-silicon/linux/commit/31ee8e23124d77700e47cabc0e8645404187fd74), [5b85b301d4ee](https://github.com/aurora-silicon/linux/commit/5b85b301d4ee38256c757299a0c58c4a79a531a7), [727284277d33](https://github.com/aurora-silicon/linux/commit/727284277d335215edd48b5f74a6f4d4e949ff3a), [9c10d7198c8e](https://github.com/aurora-silicon/linux/commit/9c10d7198c8e56b16da35914ffde0eecb6bfb525) | DjDeveloperr | arm64: dts: apple: t8140: describe the GPU mailboxes as ASCWrap v6 [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — MBOX | B | +| [dbc9f011ec91](https://github.com/aurora-silicon/linux/commit/dbc9f011ec91fb72089a45487891bfd7c5a8939d), [86fba21bf7c9](https://github.com/aurora-silicon/linux/commit/86fba21bf7c926033306cb2dc5b6d5bcfbbe30ce), [018ab982644a](https://github.com/aurora-silicon/linux/commit/018ab982644a6eff94a6020d8910e9c8c187d6a2) | Ryan Murray | arm64: dts: apple: t8140: Add CPU energy model and thermal sensor cells | Active translated data — FW | B,P | +| [b15c2faba082](https://github.com/aurora-silicon/linux/commit/b15c2faba08274f02bf9a11e0e15eef470017dbd), [d693f725d774](https://github.com/aurora-silicon/linux/commit/d693f725d77422774ae683701732562a84789439), [d69c31212d2b](https://github.com/aurora-silicon/linux/commit/d69c31212d2b93b6396775c7890d5734f962c486) | Ryan Murray | arm64: dts: apple: t8140-j700: Add CPU thermal zones | Active translated data — FW | B,P | +| [25b99e9b6a75](https://github.com/aurora-silicon/linux/commit/25b99e9b6a75a9e783d7b5f04ee60b4f97fe24ed), [1ffe37e7af24](https://github.com/aurora-silicon/linux/commit/1ffe37e7af241fb91ecd7a8d17c946fd0a5d8ecc), [64eff8a46206](https://github.com/aurora-silicon/linux/commit/64eff8a46206613ff354cbeaff49d1354e4c1f52) | Ryan Murray | arm64: dts: apple: t8140: Drop the J700 CPU thermal policy property | Active translated data — FW | B,P | +| [db87f30fa4e7](https://github.com/aurora-silicon/linux/commit/db87f30fa4e7a322d655989316aef41a00fc46e5), [cf7dc9d0474c](https://github.com/aurora-silicon/linux/commit/cf7dc9d0474cc53e3716948365b72bacc848d5e9), [8ba3c01c426a](https://github.com/aurora-silicon/linux/commit/8ba3c01c426a8efd643289689493835c51b8937d) | Ryan Murray | arm64: dts: apple: t8140: Add measured CPU capacity values | Active translated data — FW | B,P | +| [6bb7669702ed](https://github.com/aurora-silicon/linux/commit/6bb7669702edf26c05f2be84f303d0fd612487b2), [2623391b533d](https://github.com/aurora-silicon/linux/commit/2623391b533de7f038408e57e3084a75f765b56c), [3dd2554de0c5](https://github.com/aurora-silicon/linux/commit/3dd2554de0c5b6e9d16a3809186872847987de6a) | Ryan Murray | arm64: dts: apple: enable the PMP fast-die limiter on J700 [#179](https://github.com/aurora-silicon/linux/pull/179) | Active — PMP | P,B | +| [47ed624515f4](https://github.com/aurora-silicon/linux/commit/47ed624515f40f37b39230c3b1b20bb6678c788a), [66988937c0d6](https://github.com/aurora-silicon/linux/commit/66988937c0d62aeb1ed828379cb83bbc68581afc), [39a036ead934](https://github.com/aurora-silicon/linux/commit/39a036ead934c701af76e05eebd72b102810861c) | Ryan Murray | arm64: dts: apple: t8140-j700: enable the PMP nodes once | Active — PMP | P,B | +| [a539d62f765f](https://github.com/aurora-silicon/linux/commit/a539d62f765fb0e8153f2be0fa8fb33058f6df9a), [c1af50661b44](https://github.com/aurora-silicon/linux/commit/c1af50661b44940df0c5747ae7dfca25c871bbcb), [d30b0dfcf358](https://github.com/aurora-silicon/linux/commit/d30b0dfcf35813ce52d935e24e7e6d46349ec3cd) | Ryan Murray | arm64: dts: apple: t8140-j700: Keep the idle E cluster out of its lowest OPP | Active translated data — FW | B,P | +| [fb68b124b886](https://github.com/aurora-silicon/linux/commit/fb68b124b8865e39b72b2387e7f739856f72cc26), [76dd6fb9e643](https://github.com/aurora-silicon/linux/commit/76dd6fb9e643747eb61c3b409c93b1ce2975184e), [2d51c6aada40](https://github.com/aurora-silicon/linux/commit/2d51c6aada40a6c97ae81464eea6935ebf70ba95) | Ryan Murray | arm64: dts: apple: t8140: Describe the memory cache controller | Active — MCC | M,P | +| [5811648de37d](https://github.com/aurora-silicon/linux/commit/5811648de37d1bcd66a5294f44191da8880947b2), [840606faf824](https://github.com/aurora-silicon/linux/commit/840606faf824b4f6ea862f8475e7edaff9af4150), [b5b71dfc50b2](https://github.com/aurora-silicon/linux/commit/b5b71dfc50b2bfbf5800a76c540f878924a678cb), [ed31b37fb032](https://github.com/aurora-silicon/linux/commit/ed31b37fb0327ee7c20961fce2785ed3d4ddb154) | DjDeveloperr | arm64: dts: apple: t8140: describe the PMC and report GFX to the PMP [#212](https://github.com/aurora-silicon/linux/pull/212) [#219](https://github.com/aurora-silicon/linux/pull/219) | Active after port — PMC | P,C,R | +| [15f21cbc0d4b](https://github.com/aurora-silicon/linux/commit/15f21cbc0d4bbbc901454d1e69f53084e1c5dc18), [6cab4decfb66](https://github.com/aurora-silicon/linux/commit/6cab4decfb664524936da59558bc39407b633b9a), [0272b63e30f0](https://github.com/aurora-silicon/linux/commit/0272b63e30f07b8c1a496cb01f5aa74e2cc1ff93) | Ryan Murray | arm64: dts: apple: t8140: keep the GPU on its PMGR state [#219](https://github.com/aurora-silicon/linux/pull/219) | Active translated data — FW | B,P | +| [0a44c956dde2](https://github.com/aurora-silicon/linux/commit/0a44c956dde28aad0bd37b66c6ff5d8096e5676d), [8fa836c5de81](https://github.com/aurora-silicon/linux/commit/8fa836c5de8111ab90d8f2a746526d2cd487afb7), [ae8e780a5b7d](https://github.com/aurora-silicon/linux/commit/ae8e780a5b7dedcb4a31adbd106fb9f9dbff8be0) | Ryan Murray | arm64: dts: apple: t8140: describe the PMC's DCS limit [#219](https://github.com/aurora-silicon/linux/pull/219) | Gated — DCS | P | +| [84eb6b53da34](https://github.com/aurora-silicon/linux/commit/84eb6b53da3453d7c2838bd2a41779e0a9bce181), [f01e1cde7d9f](https://github.com/aurora-silicon/linux/commit/f01e1cde7d9f270e156ee77c6819466757b938f6), [a037985ac960](https://github.com/aurora-silicon/linux/commit/a037985ac9605eb76fe51a05b172b022ac1f0005) | Ryan Murray | arm64: dts: apple: t8140-j700: limit the DCS vote when the die runs hot [#219](https://github.com/aurora-silicon/linux/pull/219) | Gated — DCS | P | +| [4f6bac29d934](https://github.com/aurora-silicon/linux/commit/4f6bac29d9349ced88686239f259ab2747207708), [e10ab48a4590](https://github.com/aurora-silicon/linux/commit/e10ab48a4590c23c664546f524650310222b3a35), [c1bae63595e8](https://github.com/aurora-silicon/linux/commit/c1bae63595e8fc7b18e79664ee6aa6b4536f5d95) | Ryan Murray | arm64: dts: apple: t8140: Keep the GPU power state on across sleep | Active translated data — FW | B,P | +| [01d1be596840](https://github.com/aurora-silicon/linux/commit/01d1be596840358a7258d87fc91d3201a3287313), [8b6371b5500d](https://github.com/aurora-silicon/linux/commit/8b6371b5500d062c00f71d46a0bccbcbed111edd) | Ryan Murray | iommu: apple-dart: Program T8140 and T8142 streams individually | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [2398a03f445e](https://github.com/aurora-silicon/linux/commit/2398a03f445e8f9f65fa9f86748a3deeab3e1b1f), [69c39f85461e](https://github.com/aurora-silicon/linux/commit/69c39f85461e8944dff387eab703c58ea6c6ed49), [1d2904fd3301](https://github.com/aurora-silicon/linux/commit/1d2904fd3301c63620f07c81ae79f2486a81a9a6) | Dj | iommu/apple-dart: defer locked root publication until attach | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [ae5849165749](https://github.com/aurora-silicon/linux/commit/ae58491657494b0f56ec0113a3aa43612648e242), [44c7f32baad0](https://github.com/aurora-silicon/linux/commit/44c7f32baad0511e6f3ae5c2ffdd863eec8e5d23), [dc9d13edb0e5](https://github.com/aurora-silicon/linux/commit/dc9d13edb0e5b18c61d25240ed4ba532afb8d11f) | Acelogic | iommu/apple-dart: accept T8142 firmware slots that mirror Linux mappings | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [1917cbce81cb](https://github.com/aurora-silicon/linux/commit/1917cbce81cb806498b0a5a3f5e636c851255408), [61dfeb8f659d](https://github.com/aurora-silicon/linux/commit/61dfeb8f659d36149d3dc6162d304136b108c5b9), [c361702757bf](https://github.com/aurora-silicon/linux/commit/c361702757bfccb9183ed79be0c6200fb3722e6f) | Ryan Murray | iommu/apple-dart: defer publication until locked roots are known | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [c74dc14619ec](https://github.com/aurora-silicon/linux/commit/c74dc14619ec5467789855d89c0ef224a3655093), [35f59a797e91](https://github.com/aurora-silicon/linux/commit/35f59a797e914308f4ae289e87551ad97997a497), [d957b0bd713b](https://github.com/aurora-silicon/linux/commit/d957b0bd713bbf4617f54f5ed0d274964e9af6c9) | Ryan Murray | iommu/apple-dart: validate software mappings against inherited slots | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [f89a3c873999](https://github.com/aurora-silicon/linux/commit/f89a3c87399911e0faa4f66091b2e7dcc1145825), [64013b9557ab](https://github.com/aurora-silicon/linux/commit/64013b9557abadab17883a7e697f32f32e0c4160), [707f28a4c399](https://github.com/aurora-silicon/linux/commit/707f28a4c399092ba01bd281a3f0b6623f7bb223) | Ryan Murray | iommu/apple-dart: import firmware translations on T8140 | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [99633bd5454e](https://github.com/aurora-silicon/linux/commit/99633bd5454eac0e455812358be63b4f08d78a98), [c29b618f6340](https://github.com/aurora-silicon/linux/commit/c29b618f6340b4ede94747a266f86a844344e769), [ebcff4ba64dd](https://github.com/aurora-silicon/linux/commit/ebcff4ba64dd90983cc8150b0f46fda82b5fb9f0) | Ryan Murray | iommu: apple-dart: retain inherited translations before reservation setup | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [8d2d7790d8d5](https://github.com/aurora-silicon/linux/commit/8d2d7790d8d552f01ce2bf10f922aaccbf7918aa), [dbd410951406](https://github.com/aurora-silicon/linux/commit/dbd4109514067a2ed8140d8138bee68453272f02), [8478ae014201](https://github.com/aurora-silicon/linux/commit/8478ae0142014d170a62df6dd94cc66bd989627b) | Chris Kearney | iommu/apple-dart: restore legacy locked display domain handoff | Active — DART; `drivers/iommu/Kconfig` | D,B | +| [0297adcecc72](https://github.com/aurora-silicon/linux/commit/0297adcecc72a563b5cfb3ca3e483f71422fb77c), [b71446526889](https://github.com/aurora-silicon/linux/commit/b71446526889b27c60461685c3c320b7e123155d), [2bad3dba632f](https://github.com/aurora-silicon/linux/commit/2bad3dba632ff85537392b79255fc527baf01746) | Ryan Murray | iommu: apple-dart: keep inherited firmware translations visible before attach | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [80ce713f5251](https://github.com/aurora-silicon/linux/commit/80ce713f52511abe4d35b35bfac0f10f9072d401), [8ece17f482f1](https://github.com/aurora-silicon/linux/commit/8ece17f482f11da6e81915b98fc8b93ed796504a), [a64a4771b276](https://github.com/aurora-silicon/linux/commit/a64a4771b27602bf5a72b413b6bc9a868892992a) | Ryan Murray | iommu: apple-dart: invalidate T8110 ranges by device DVA [#221](https://github.com/aurora-silicon/linux/pull/221) | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [cef9a23aa45e](https://github.com/aurora-silicon/linux/commit/cef9a23aa45e3ac6b352e6c2bd839935374ae351), [253ba9bebf47](https://github.com/aurora-silicon/linux/commit/253ba9bebf470c0d78845103c93b937f2d9cdfb3), [dd5ac6fdffb0](https://github.com/aurora-silicon/linux/commit/dd5ac6fdffb03d048df510198af6e8d53dceb17d) | Ryan Murray | iommu: apple-dart: leave live firmware translations out of invalidates [#221](https://github.com/aurora-silicon/linux/pull/221) | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [0a73b0299574](https://github.com/aurora-silicon/linux/commit/0a73b0299574da4c42659024b4a0e8390c36d2d8), [bc6682c215ad](https://github.com/aurora-silicon/linux/commit/bc6682c215adc6febfb86c9fa2ed0fb69657dcc2), [9c512b25f55e](https://github.com/aurora-silicon/linux/commit/9c512b25f55eaf6332b590e5a205ca7d55de12c8) | Ryan Murray | iommu: apple-dart: invalidate client-ranged T8140 streams again [#232](https://github.com/aurora-silicon/linux/pull/232) | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [ae812d47ea17](https://github.com/aurora-silicon/linux/commit/ae812d47ea176c9bb0b4ddccc17349fee5afbf1a), [fe15676f6e03](https://github.com/aurora-silicon/linux/commit/fe15676f6e03395f4947cbffb2d49fbee17ea5f9), [4ca4ba380552](https://github.com/aurora-silicon/linux/commit/4ca4ba3805528f5370bbe6bab3d8dbb8f7f9226c) | Ryan Murray | iommu: apple-dart: invalidate four-level T8140 streams by DVA range [#237](https://github.com/aurora-silicon/linux/pull/237) | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [a17634424697](https://github.com/aurora-silicon/linux/commit/a17634424697b082221d141ef61d9689460eebf8), [d1a5c65245b8](https://github.com/aurora-silicon/linux/commit/d1a5c65245b87c457a8fc4b4190d440e9bc38b8d) | Sasha Finkelstein | soc: apple: rtkit: Add tracekit endpoint. | Active — RTKIT; `drivers/soc/apple/rtkit.c` | B,F | +| [ff7d81511780](https://github.com/aurora-silicon/linux/commit/ff7d81511780770e944ebe6e66ed56a06b497a31) | Ryan Murray | drm/asahi: Adapt to the Linux 7.3 Rust APIs [#375](https://github.com/aurora-silicon/linux/pull/375) | Active — MEM; `drivers/gpu/drm/asahi/debug.rs` | M,D | +| [588c231eaa54](https://github.com/aurora-silicon/linux/commit/588c231eaa54f4a34872519517fbbcc079b41175) | Ryan Murray | soc: apple: Adapt the Rust SEP, PMP and AOP drivers to Linux 7.3 | Active — PMP; `drivers/soc/apple/aop.rs` | P,B | +| [c025e6cd0615](https://github.com/aurora-silicon/linux/commit/c025e6cd0615e416e0a06ba997e8d9f417a3e185), [af0890cf33cf](https://github.com/aurora-silicon/linux/commit/af0890cf33cf899cf0005daf6138da1dfe573c19) | Ryan Murray | soc: apple: rtkit: Harden syslog buffer handling | Active — RTKIT; `drivers/soc/apple/rtkit.c` | B,F | +| [3a8760c28c33](https://github.com/aurora-silicon/linux/commit/3a8760c28c3341380e105c75bb48e76c895d3f3d), [c01af1b31c52](https://github.com/aurora-silicon/linux/commit/c01af1b31c523b09106c4bed43f8bb7cba65cbfc) | Ryan Murray | soc: apple: rtkit: Keep a live shared buffer on a repeated request | Active — RTKIT; `drivers/soc/apple/rtkit.c` | B,F | +| [495b2f037c28](https://github.com/aurora-silicon/linux/commit/495b2f037c284f4b7b10f54c90a7d34fbf5e99fa), [6b7e37786882](https://github.com/aurora-silicon/linux/commit/6b7e37786882db803e9de1352a2af623044f9237), [1fc9ea993905](https://github.com/aurora-silicon/linux/commit/1fc9ea9939058bc1307565f036506da30dd62908) | Ryan Murray | soc: apple: rtkit: Serialize receiver ownership through teardown | Active — MBOX; `drivers/soc/apple/mailbox.c` | B,O | +| [335eaeb9b9f1](https://github.com/aurora-silicon/linux/commit/335eaeb9b9f147230f464a140287e820b1d78518), [0593ad398a6e](https://github.com/aurora-silicon/linux/commit/0593ad398a6eb83e2e2ce9dd25bf1fd0768e5bcd) | Ryan Murray | soc: apple: rtkit: Add apple_rtkit_free_retaining_buffers() | Active — RTKIT; `drivers/soc/apple/rtkit.c` | B,F | +| [e796f5fbaf06](https://github.com/aurora-silicon/linux/commit/e796f5fbaf0698b478c43ba6eb15e046c9cc6244), [8527354650e8](https://github.com/aurora-silicon/linux/commit/8527354650e810479ceb0a58417e683b2dee852c) | Ryan Murray | soc: apple: rtkit: Allow streaming-mapped shared buffers | Active — RTKIT; `drivers/soc/apple/rtkit.c` | B,F | +| [9191296f1898](https://github.com/aurora-silicon/linux/commit/9191296f1898d1acf2d0cd500230ae80c9e3e0ca), [88e3c5c07943](https://github.com/aurora-silicon/linux/commit/88e3c5c07943fedf73e45f88b270f8550d7691ae) | Ryan Murray | soc: apple: rtkit: Support adopting a running co-processor | Active — RTKIT; `drivers/soc/apple/rtkit-internal.h` | B,F | +| [1932e1e1b184](https://github.com/aurora-silicon/linux/commit/1932e1e1b18437408e77dcc1d23d6d966b0fdb98), [f232f22bef54](https://github.com/aurora-silicon/linux/commit/f232f22bef54eb6fbd848a1374e49fe91035b397) | Ryan Murray | soc: apple: rtkit: Report crashes of an adopted co-processor | Active — RTKIT; `drivers/soc/apple/rtkit-internal.h` | B,F | +| [b38944203ab0](https://github.com/aurora-silicon/linux/commit/b38944203ab0ad8ec01c26bd67df67455fcc98c3), [4f0416f2c43b](https://github.com/aurora-silicon/linux/commit/4f0416f2c43ba895b3c34a263cf5d0e070095a55), [2768025e0f1d](https://github.com/aurora-silicon/linux/commit/2768025e0f1d9792f84e9fbb99f6c0b155c0a22a), [35b2acbf474d](https://github.com/aurora-silicon/linux/commit/35b2acbf474d8505c3edc1a2749a8d6fbfaf7a09) | Ryan Murray | pmdomain: apple: Print the read-back state on a power state timeout | Active — OBS; `drivers/pmdomain/apple/pmgr-pwrstate.c` | O,F | +| [768a665d0ca1](https://github.com/aurora-silicon/linux/commit/768a665d0ca138d0f2c80029fd1d272c11468e2e), [9c5b8fe6d6d6](https://github.com/aurora-silicon/linux/commit/9c5b8fe6d6d6a93f7c4bbbd14026fd93d5f6aa73), [afe19efeff9c](https://github.com/aurora-silicon/linux/commit/afe19efeff9c466486fb723f7066f86b9a3e71d5), [9dc79fb25c92](https://github.com/aurora-silicon/linux/commit/9dc79fb25c9294bd7a8ecb00acc9bcea379c3d1a) | Ryan Murray | pmdomain: apple: Mask BUSY in power state writes and ratelimit errors | Active — PMGR; `drivers/pmdomain/apple/pmgr-pwrstate.c` | B,F | +| [a6aa625a9b35](https://github.com/aurora-silicon/linux/commit/a6aa625a9b35b96ba4ffc0749d55a0f21fe9e426), [03f4792761f4](https://github.com/aurora-silicon/linux/commit/03f4792761f42c3a4581be4b585a479cfc65bfc3), [f82e88f4049e](https://github.com/aurora-silicon/linux/commit/f82e88f4049ed6ea278717be6d922f8e347aab13), [928641d26be3](https://github.com/aurora-silicon/linux/commit/928641d26be3083dc06c0de072ce99537fa059b7) | DjDeveloperr | soc: apple: rtkit: Support co-processors that place their own buffers [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — RTKIT; `drivers/soc/apple/rtkit.c` | B,F | +| [e52b589758bd](https://github.com/aurora-silicon/linux/commit/e52b589758bd9c72db728ef41947aadc2d1eec3f), [a8e468104169](https://github.com/aurora-silicon/linux/commit/a8e46810416977c034c45d805c0b504cfdab9a6a), [c07d044a0311](https://github.com/aurora-silicon/linux/commit/c07d044a03117705721c78500bc36489ea34e6c4) | Ryan Murray | soc: apple: rtkit: recognise an initial crash log in adopted sessions | Active — RTKIT; `drivers/soc/apple/rtkit-crashlog.c` | B,F | +| [8420f4272083](https://github.com/aurora-silicon/linux/commit/8420f42720830b9e5bad37647b6eead5303bc566), [352b68249894](https://github.com/aurora-silicon/linux/commit/352b682498947d0062635916a9d6902165ab38b5), [2f434459eb09](https://github.com/aurora-silicon/linux/commit/2f434459eb094edc046cd2ff71f1901302d7ef3d) | Ryan Murray | soc: apple: rtkit: reject an already owned mailbox receiver | Active — RTKIT; `drivers/soc/apple/rtkit.c` | B,F | +| [791f8c37bad3](https://github.com/aurora-silicon/linux/commit/791f8c37bad39ada4186e7db9efb74472ef1670b), [44a668c87df1](https://github.com/aurora-silicon/linux/commit/44a668c87df18147ca0c9ace22e897e4c65b7620), [41e7892e40eb](https://github.com/aurora-silicon/linux/commit/41e7892e40eb21ddb08b2391a7940ed6499daf8d) | Ryan Murray | soc: apple: pmp: use a 42-bit DMA mask on T8140 [#179](https://github.com/aurora-silicon/linux/pull/179) | Active — PMP; `drivers/soc/apple/pmp.rs` | P,B | +| [7a9913ea25e2](https://github.com/aurora-silicon/linux/commit/7a9913ea25e2d95d3ebe8f0392fec92979d08728), [493080d07b91](https://github.com/aurora-silicon/linux/commit/493080d07b918a2025fb79ff987d0dc9aeb294fe), [f69f1b76fcb0](https://github.com/aurora-silicon/linux/commit/f69f1b76fcb04e6bd7d7d066654e612be9761af0) | Ryan Murray | pmdomain: apple: pmp-report: expose T8140 fast-die effort [#179](https://github.com/aurora-silicon/linux/pull/179) | Active — PMP; `drivers/pmdomain/apple/pmp-report.c` | P,B | +| [1a665f53f540](https://github.com/aurora-silicon/linux/commit/1a665f53f540a523d8df2c11d11a824f933b0d14), [8410c0eac2d9](https://github.com/aurora-silicon/linux/commit/8410c0eac2d9419a33c6a8d4a00bd14059f94610), [4f3e0ed14a9c](https://github.com/aurora-silicon/linux/commit/4f3e0ed14a9c280aafb3642619e7335413592e06) | Ryan Murray | pmdomain: apple: drain PMP report users before teardown | Active — PMP; `drivers/pmdomain/apple/pmp-report.c` | P,B | +| [50b59e9b8f43](https://github.com/aurora-silicon/linux/commit/50b59e9b8f43e49c14a8a5e426f7891ac5f5e8c7), [061cf8fae73f](https://github.com/aurora-silicon/linux/commit/061cf8fae73f12de8d6720602ccac2aec250a20f), [f4ec00ff19db](https://github.com/aurora-silicon/linux/commit/f4ec00ff19dbfdc6d9bdbf03e8d7ec2798502f1a) | Ryan Murray | soc: apple: Enable T8140 system level cache allocation | Active — MCC; `drivers/soc/apple/Kconfig` | M,P | +| [628dbbe941d2](https://github.com/aurora-silicon/linux/commit/628dbbe941d25e5153dc62cb04104f846f3ffc48), [af915d02f5f6](https://github.com/aurora-silicon/linux/commit/af915d02f5f6022114a46e302642e915f9b22a01), [8b7d7f837ff6](https://github.com/aurora-silicon/linux/commit/8b7d7f837ff68f7fa37f6aaae55114bbd15efb43), [132cee4161dc](https://github.com/aurora-silicon/linux/commit/132cee4161dcff4307c55314f2a6189a00e7b0df) | DjDeveloperr | pmdomain: apple: add PMC voter interface driver [#212](https://github.com/aurora-silicon/linux/pull/212) [#219](https://github.com/aurora-silicon/linux/pull/219) | Active after port — PMC; `drivers/pmdomain/apple/Kconfig` | P,C,R | +| [698f429f4256](https://github.com/aurora-silicon/linux/commit/698f429f42566c059e7788e4efd44978a5db33a9), [9e840cf32336](https://github.com/aurora-silicon/linux/commit/9e840cf323360eabdb61223cf73198f4f4f3179d), [6454e02db2a5](https://github.com/aurora-silicon/linux/commit/6454e02db2a562d82adfbc4447ffe093f43dae0a), [5bd867f5c339](https://github.com/aurora-silicon/linux/commit/5bd867f5c33919ed65c81ead1e31595bb6bb91c5) | DjDeveloperr | soc: apple: add host performance controller floors [#212](https://github.com/aurora-silicon/linux/pull/212) [#219](https://github.com/aurora-silicon/linux/pull/219) | Active — CLPC; `drivers/soc/apple/Kconfig` | P,C,R,I | +| [faff1f7acf75](https://github.com/aurora-silicon/linux/commit/faff1f7acf752284972af5d469f91186c1e28552), [1c69f57e2c3e](https://github.com/aurora-silicon/linux/commit/1c69f57e2c3e5ba7cea2db4708242b7b538b15b2), [e736b2a049bf](https://github.com/aurora-silicon/linux/commit/e736b2a049bf708512f845093f712c431c20da3b) | Ryan Murray | pmdomain: apple: pmc: report the honoured floor of each agent [#219](https://github.com/aurora-silicon/linux/pull/219) | Active — PMC; `drivers/pmdomain/apple/pmc.c` | P,C,R | +| [9afc8195e8e2](https://github.com/aurora-silicon/linux/commit/9afc8195e8e2b3b4da25805f0b0c53baac16f1de), [e3f7747b88a3](https://github.com/aurora-silicon/linux/commit/e3f7747b88a38a8f2db51a63b323bb6fe3a25157), [cf8edfc522ce](https://github.com/aurora-silicon/linux/commit/cf8edfc522ce82871acc421119e324b56ab2813f) | Ryan Murray | pmdomain: apple: pmc: limit the DCS rail as a cooling device [#219](https://github.com/aurora-silicon/linux/pull/219) | Gated — DCS; `drivers/pmdomain/apple/pmc.c` | P | +| [c809017f2926](https://github.com/aurora-silicon/linux/commit/c809017f2926613cc9b683189c5e37aa6236c647), [bbbc7f0f2526](https://github.com/aurora-silicon/linux/commit/bbbc7f0f2526501df99e7d40c7a14a5bcb3c168d), [99bf4fc4c39c](https://github.com/aurora-silicon/linux/commit/99bf4fc4c39cbe59493531b7682f0a7eb60ac2e0) | Ryan Murray | pmdomain: apple: build the PMC driver by default [#219](https://github.com/aurora-silicon/linux/pull/219) | Active — PMC; `drivers/pmdomain/apple/Kconfig` | P,C,R | +| [413512c8c6c7](https://github.com/aurora-silicon/linux/commit/413512c8c6c7503e21b2d845b7072f3e67985e80), [afbf48da68ef](https://github.com/aurora-silicon/linux/commit/afbf48da68ef244414db4e174b630cf95ed33bd2), [d0f012d7b92f](https://github.com/aurora-silicon/linux/commit/d0f012d7b92f9967d42c35a70bc09c2b87274c0b) | Ryan Murray | soc: apple: clpc: do not count offline CPUs as busy [#219](https://github.com/aurora-silicon/linux/pull/219) | Active — CLPC; `drivers/soc/apple/clpc.c` | P,C,R,I | +| [a1517450f0d6](https://github.com/aurora-silicon/linux/commit/a1517450f0d6afa99ed5a5f2acbbc46350fdccd5), [a0301cd2e0fb](https://github.com/aurora-silicon/linux/commit/a0301cd2e0fb788836d8ef4d09b397a478891a72), [ddc216cd42cb](https://github.com/aurora-silicon/linux/commit/ddc216cd42cb7c7ebc831d3fafca17df244b280e) | Ryan Murray | soc: apple: clpc: stop waking idle CPUs and release the floor in sleep [#219](https://github.com/aurora-silicon/linux/pull/219) | Active — CLPC; `drivers/soc/apple/clpc.c` | P,C,R,I | +| [17c6097b09dd](https://github.com/aurora-silicon/linux/commit/17c6097b09ddd81a31a618e1d2447c3fdfed87ce), [ebd97e7441ff](https://github.com/aurora-silicon/linux/commit/ebd97e7441ff7ca5f7169f6e2bf550e7fb1438e0), [4f13ab717cb3](https://github.com/aurora-silicon/linux/commit/4f13ab717cb3a5ab19e5c097084ff2c676457f37) | Ryan Murray | pmdomain: apple: pmp-report: keep entry domains bound [#223](https://github.com/aurora-silicon/linux/pull/223) | Active — PMP; `drivers/pmdomain/apple/pmp-report.c` | P,B | +| [bd2a439af1fe](https://github.com/aurora-silicon/linux/commit/bd2a439af1feefbb19cec4af61a28ae81a7fe479), [6733b19981f1](https://github.com/aurora-silicon/linux/commit/6733b19981f1d70f51264f609582025889e083db), [59540c7f72b3](https://github.com/aurora-silicon/linux/commit/59540c7f72b3721d293fb36cc5fd5bf79e1f47bc) | Ryan Murray | pmdomain: apple: Undo a power state transition that times out | Active — PMGR; `drivers/pmdomain/apple/pmgr-pwrstate.c` | B,F | +| [afa52a4aacd2](https://github.com/aurora-silicon/linux/commit/afa52a4aacd24be61e6963bcfa96a673fcd0f7fd), [b1dadbe56c1a](https://github.com/aurora-silicon/linux/commit/b1dadbe56c1ac98c1687aacd6f9a4ffd45d44802) | Acelogic | soc: apple: rtkit: Allow AP readiness before the IOP power acknowledgement | Active — RTKIT; `drivers/soc/apple/rtkit-helper.c` | B,F | +| [80223777e1c0](https://github.com/aurora-silicon/linux/commit/80223777e1c0fcdd18cdf7dbb693389f18bffe66), [7de139272456](https://github.com/aurora-silicon/linux/commit/7de1392724567cdab869ed7102a23642d6e64320), [b5c2960b4b44](https://github.com/aurora-silicon/linux/commit/b5c2960b4b441fa51b7f0ccdbaa17eae8a9de4bc) | Ryan Murray | soc: apple: rtkit: identify failed boot handshake stages | Active — RTKIT; `drivers/soc/apple/rtkit.c` | B,F | +| [e02a7f13d77d](https://github.com/aurora-silicon/linux/commit/e02a7f13d77d0c450e6e663da89160b8e2e6a9de), [3a165a5f418d](https://github.com/aurora-silicon/linux/commit/3a165a5f418df4ca2445c14657bdc6dbe39bb223), [50a711417171](https://github.com/aurora-silicon/linux/commit/50a711417171e48a07340999bf43bb8c281b14d7) | Eryk Wieliczko | pmdomain: apple: leave firmware-owned power domains untouched | Active — PMGR; `drivers/pmdomain/apple/pmgr-pwrstate.c` | B,F | +| [15a254a94f16](https://github.com/aurora-silicon/linux/commit/15a254a94f1685a5550aec6c99a3270bfb389951), [e271de959b7d](https://github.com/aurora-silicon/linux/commit/e271de959b7de2e436f15415329d175e2e158517) | Ryan Murray | soc: apple: rtkit: Recognise opaque OSLog events 3 to 5 | Active — RTKIT; `drivers/soc/apple/rtkit.c` | B,F | +| [faadb28854ca](https://github.com/aurora-silicon/linux/commit/faadb28854caaf534df60f6d629eca87c963344f), [aa486de92319](https://github.com/aurora-silicon/linux/commit/aa486de923198c933849bb2172f5ddad4f4710b6), [6559da7d50ab](https://github.com/aurora-silicon/linux/commit/6559da7d50ab10da722b3719f447836b12427159), [7810f444ae5a](https://github.com/aurora-silicon/linux/commit/7810f444ae5a403773baf8d4f460e29cb2375cf5) | Ryan Murray | cpufreq: apple-soc: Use related_cpus when removing OPP tables at exit | Active — CPU; `drivers/cpufreq/apple-soc-cpufreq.c` | C,I,P | +| [f630f4e6a724](https://github.com/aurora-silicon/linux/commit/f630f4e6a724b62182875eeb75e3559f6304b286), [4df394b4c36b](https://github.com/aurora-silicon/linux/commit/4df394b4c36be04ca63fabae2c630bfdda20188a), [31ce0bec7173](https://github.com/aurora-silicon/linux/commit/31ce0bec7173eb3fbc1067e6dba46c2ca2f4dc8e) | Ryan Murray | cpufreq: apple-soc: Add T8140 support | Active — CPU; `drivers/cpufreq/apple-soc-cpufreq.c` | C,I,P | +| [116ec26bc074](https://github.com/aurora-silicon/linux/commit/116ec26bc074456b670823002c81ccffc5fbbecc), [2e97d372534e](https://github.com/aurora-silicon/linux/commit/2e97d372534e73b72b9b36b9debaa3caa9ae5ab6), [b0a02dcbfded](https://github.com/aurora-silicon/linux/commit/b0a02dcbfded7f1485db7f37694712892025a4d9) | Ryan Murray | cpufreq: apple-soc: enforce PMP fast-die limits through QoS [#179](https://github.com/aurora-silicon/linux/pull/179) | Active — PMP; `drivers/cpufreq/apple-soc-cpufreq.c` | P,B | +| [3a1ff1c99f6a](https://github.com/aurora-silicon/linux/commit/3a1ff1c99f6a35163c77f9c2cc5f6b19d4fe6242), [9d74a86460df](https://github.com/aurora-silicon/linux/commit/9d74a86460df995f3522f3267c3f3fa34d79a47e) | Ryan Murray | cpufreq: apple-soc: keep required fallback limits with an older DT | Active — CPU; `drivers/cpufreq/apple-soc-cpufreq.c` | C,I,P | +| [1248cb920813](https://github.com/aurora-silicon/linux/commit/1248cb9208135326d9724eb1a83e3c67d25c3664), [a40bdafc42e5](https://github.com/aurora-silicon/linux/commit/a40bdafc42e5dcf8c0c4bc728f883c7e3d48bffe), [a61bca6382a7](https://github.com/aurora-silicon/linux/commit/a61bca6382a765f5b74134dea3b094039f2a15bd) | Ryan Murray | cpufreq: apple-soc: tidy includes and the ->exit() callback [#317](https://github.com/aurora-silicon/linux/pull/317) | Active — CPU; `drivers/cpufreq/apple-soc-cpufreq.c` | C,P | +| [c52554b353b4](https://github.com/aurora-silicon/linux/commit/c52554b353b4fcecf684ea648e4b1ab5f896b677), [143dc7bec441](https://github.com/aurora-silicon/linux/commit/143dc7bec44131ac467fa21049f2d63c52783d52), [e7d3d687d207](https://github.com/aurora-silicon/linux/commit/e7d3d687d20745259fcb83af83c9ed377ff6a1f3) | Ryan Murray | cpufreq: apple-soc: describe the unmanaged P-state ceiling as data [#317](https://github.com/aurora-silicon/linux/pull/317) | Active — CPU; `drivers/cpufreq/apple-soc-cpufreq.c` | C,P | +| [e71cc1ccab4e](https://github.com/aurora-silicon/linux/commit/e71cc1ccab4eaefdf63d66104107c9604ab04121), [cd8875cfd1bf](https://github.com/aurora-silicon/linux/commit/cd8875cfd1bfaa80b1b1dbd7e27ea234d129471b), [cfe897d4abac](https://github.com/aurora-silicon/linux/commit/cfe897d4abac70a3d9703ea7f4c082ed89da7a96) | Ryan Murray | rust: soc: apple: rtkit: Add shutdown() and a buffer-retaining drop | Active — RTKIT; `rust/kernel/soc/apple/rtkit.rs` | B,F | +| [10dd5c21ef72](https://github.com/aurora-silicon/linux/commit/10dd5c21ef7211836beaa47f6f3fd3e250d36866), [8779ecb38c89](https://github.com/aurora-silicon/linux/commit/8779ecb38c89c103617c4ea24207fef96472ebc4), [06914e1d4995](https://github.com/aurora-silicon/linux/commit/06914e1d499555a96e0beccccc0b5485051108cd) | Ryan Murray | soc: apple: rtkit: Handle oslog notifications quietly | Active — RTKIT; `drivers/soc/apple/rtkit.c` | B,F | +| [9a4cf171ba9e](https://github.com/aurora-silicon/linux/commit/9a4cf171ba9edabdd0f0f715468d42689791adb7), [aa18e91ac6fc](https://github.com/aurora-silicon/linux/commit/aa18e91ac6fce29dcc8e69aac619fab3fda7ee85), [904945425463](https://github.com/aurora-silicon/linux/commit/904945425463c0450cb19582afeb4b935175ae51), [201afd84ae50](https://github.com/aurora-silicon/linux/commit/201afd84ae50008095068d028af22ecf4c1356c5) | DjDeveloperr | drm/asahi: use const float bit conversion supported by Rust minimum [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — MEM; `drivers/gpu/drm/asahi/float.rs` | M,D | +| [c9be90533ae0](https://github.com/aurora-silicon/linux/commit/c9be90533ae06ac64dfe5e35b121cf30a1cc37f0), [6b610ee95063](https://github.com/aurora-silicon/linux/commit/6b610ee95063a8d047c1ee49c65ee3599ee24a92), [94b86f96bd9e](https://github.com/aurora-silicon/linux/commit/94b86f96bd9ef2275c99ddf5238374e615b2f510), [12f0a07a6166](https://github.com/aurora-silicon/linux/commit/12f0a07a61666305a1efe1a1f4c4c88a013abafb) | DjDeveloperr | rust: drm: sched: Fix job accounting when cancelling jobs [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — DRM; `rust/kernel/drm/sched.rs` | C,H,M,F | +| [7ba04b60d2ca](https://github.com/aurora-silicon/linux/commit/7ba04b60d2caf298bb9119b359629feedfec38c5), [43d53c0d4f56](https://github.com/aurora-silicon/linux/commit/43d53c0d4f5660bc6d300e6626d1f9a05d832c79), [15a7eae7964f](https://github.com/aurora-silicon/linux/commit/15a7eae7964f94a16fd273fc3b25a22ad2c27bab), [f8102be460b6](https://github.com/aurora-silicon/linux/commit/f8102be460b66b12af865d6250e73ffc6f99c73a) | DjDeveloperr | rust: drm: sched: Keep the scheduler alive while jobs reference it [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — DRM; `rust/kernel/drm/sched.rs` | C,H,M,F | +| [d1ebc542fe8e](https://github.com/aurora-silicon/linux/commit/d1ebc542fe8e1082a1c6e311b72d4d6c609c940d), [e4c63b3dbab0](https://github.com/aurora-silicon/linux/commit/e4c63b3dbab04bda2a798b913dd5e46b8799290a), [7d650853ea8e](https://github.com/aurora-silicon/linux/commit/7d650853ea8e5de3f512a36b69f8653014de83b4), [f1f985d57681](https://github.com/aurora-silicon/linux/commit/f1f985d576810c7183ea9bcd1b701cb284927ac6) | DjDeveloperr | rust: drm: sched: Do not stop the scheduler before tearing it down [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — DRM; `rust/kernel/drm/sched.rs` | C,H,M,F | +| [45ade507d593](https://github.com/aurora-silicon/linux/commit/45ade507d593cb69929a4daf5d1d50159d729126), [1bc200c44668](https://github.com/aurora-silicon/linux/commit/1bc200c446687bc2eb2b6abc19f1d9b5a3ad59ee), [9171e855e6d5](https://github.com/aurora-silicon/linux/commit/9171e855e6d58a12059e265a7e3907f44ffa70a9), [14d7b3c379da](https://github.com/aurora-silicon/linux/commit/14d7b3c379da4c764581d4c34aff495d7346af72) | DjDeveloperr | rust: drm: sched: let drivers handle timeouts without a reset [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — DRM; `rust/kernel/drm/sched.rs` | C,H,M,F | +| [996d255ea104](https://github.com/aurora-silicon/linux/commit/996d255ea104c999554b5c5584d2b59c6eed3cd9), [59b1e2a30011](https://github.com/aurora-silicon/linux/commit/59b1e2a300116dec7170b0b4a44e4b94a39c372b), [630c43dac20e](https://github.com/aurora-silicon/linux/commit/630c43dac20e9621d3c2cd687c98ab85d51c8b9e), [e0a5c5ad84cb](https://github.com/aurora-silicon/linux/commit/e0a5c5ad84cbc6e48c0194882b8c1c67d72538a8) | DjDeveloperr | drm/asahi: uapi: Add the G17 additions [#171](https://github.com/aurora-silicon/linux/pull/171) | Active translated data — FW; `drivers/gpu/drm/asahi/file.rs` | B,C,R | +| [2f298100e9d5](https://github.com/aurora-silicon/linux/commit/2f298100e9d59b05f3c71d9e27d8079a63a0b6bc), [4a9cf4453a2d](https://github.com/aurora-silicon/linux/commit/4a9cf4453a2d0349848c89bd2028e8e6757263a7), [e46d8cfd391b](https://github.com/aurora-silicon/linux/commit/e46d8cfd391b00b302d5a9ff4608b82c3714c910), [aae9f7d3525f](https://github.com/aurora-silicon/linux/commit/aae9f7d3525f93a7a5b5ae0529f8e85219735d08) | DjDeveloperr | drm/asahi: separate DRM-facing GPU operations from the firmware manager [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — MEM; `drivers/gpu/drm/asahi/driver.rs` | M,D | +| [3291ed172be2](https://github.com/aurora-silicon/linux/commit/3291ed172be2a63252d34367f8f5d8baebf2e537), [cac96891a49f](https://github.com/aurora-silicon/linux/commit/cac96891a49fd1416cb3bb0f68922cbebb7618c1), [54721b0f0bee](https://github.com/aurora-silicon/linux/commit/54721b0f0bee7be5b3094112ffcfa36e76dec7ed), [7d3ddb6c35e1](https://github.com/aurora-silicon/linux/commit/7d3ddb6c35e1c478f68c78ac8bb8b3ee9d314e58) | DjDeveloperr | drm/asahi: mmu: support per-SoC UAT geometry and user VA range [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — MEM; `drivers/gpu/drm/asahi/file.rs` | M,D | +| [a0ae891ae8a0](https://github.com/aurora-silicon/linux/commit/a0ae891ae8a00e183ddbc753347a7ea2ef26d7dc), [79aa2038d24c](https://github.com/aurora-silicon/linux/commit/79aa2038d24c495775338c6d72b638885e2cd5e1), [49e3d35945c3](https://github.com/aurora-silicon/linux/commit/49e3d35945c397f7af5a6477540e2ee254b3bd76), [5e21536704b5](https://github.com/aurora-silicon/linux/commit/5e21536704b56c38b478516d1f65bdcfef748a17) | DjDeveloperr | drm/asahi: pgtable: track ownership below firmware-shared roots [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — MEM; `drivers/gpu/drm/asahi/mmu.rs` | M,D | +| [524c57538f14](https://github.com/aurora-silicon/linux/commit/524c57538f142ef38c8b081f5148ffb515e96266), [52ef95b9e763](https://github.com/aurora-silicon/linux/commit/52ef95b9e7635d564449ec08b33a62c6ed26dd12), [87cab74a2498](https://github.com/aurora-silicon/linux/commit/87cab74a2498f5df62f7697786a2743e84125b99), [c0e36ff2a57f](https://github.com/aurora-silicon/linux/commit/c0e36ff2a57f2a461a96b80cebb741f5b76de7ad) | DjDeveloperr | drm/asahi: add the T8140 firmware interface [#171](https://github.com/aurora-silicon/linux/pull/171) | Active translated data; MCache gated by V25G83.mcache=false — FW; `drivers/gpu/drm/asahi/g17/buffer.rs` | B,C,R | +| [ed303abfff0f](https://github.com/aurora-silicon/linux/commit/ed303abfff0fd9e29e8094b828cd3054969bcf97), [0386eb2184a2](https://github.com/aurora-silicon/linux/commit/0386eb2184a268ed6178a6a93a12880806a8022f), [d7d77474e459](https://github.com/aurora-silicon/linux/commit/d7d77474e4590fcd9c1d0e30ab16dc9ca0c73346), [bc4c50abf8ed](https://github.com/aurora-silicon/linux/commit/bc4c50abf8ede16fec7f2a79546a310134b208df) | DjDeveloperr | drm/asahi: request temporary CPU capacity for sustained GPU feeds [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — POWER; `drivers/gpu/drm/asahi/g17/completion.rs` | I,C,R,P | +| [4a77604eb003](https://github.com/aurora-silicon/linux/commit/4a77604eb003ed141f28781769b28953fdabfb1f), [9fdd32f35641](https://github.com/aurora-silicon/linux/commit/9fdd32f3564105f1d012ef20e1c64de912cade1d), [15d553b170f1](https://github.com/aurora-silicon/linux/commit/15d553b170f157558da0343d477c39161f9870f1), [e1e0c5c90faf](https://github.com/aurora-silicon/linux/commit/e1e0c5c90faf0307f95374c83efe3009d58f0bdb) | DjDeveloperr | drm/asahi: observe compute completions without a queue mask [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/runtime/events.rs` | C,H | +| [593e44ca35f7](https://github.com/aurora-silicon/linux/commit/593e44ca35f708e48e0d9b9efde35bfe1052ef35), [e32d6efe3891](https://github.com/aurora-silicon/linux/commit/e32d6efe3891821dce2e6c0e27219ed676ea4f2f), [7dcd8d1467e2](https://github.com/aurora-silicon/linux/commit/7dcd8d1467e2bc1e90eeb04489d4ab7b392351e8), [0c69741cdc26](https://github.com/aurora-silicon/linux/commit/0c69741cdc26f2dbb26754e87f1b53db1f4bed38) | DjDeveloperr | drm/asahi: avoid repeated submission bookkeeping [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/mod.rs` | C,R,H | +| [0ee6827563de](https://github.com/aurora-silicon/linux/commit/0ee6827563deb2e2519534dd938334c90606fb33), [ce33535500cc](https://github.com/aurora-silicon/linux/commit/ce33535500cc78ed2f6695050ce21487902806bc), [ffa3234a92d7](https://github.com/aurora-silicon/linux/commit/ffa3234a92d789514f3e6b05213f417454b73639), [e0d2761bd854](https://github.com/aurora-silicon/linux/commit/e0d2761bd854136076d642b614c9b6078699243e) | DjDeveloperr | drm/asahi: settle unpublished jobs discarded by entity teardown [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/job.rs` | C,R,H | +| [7eb278622690](https://github.com/aurora-silicon/linux/commit/7eb27862269055d524de513e380572b59171a390), [aa659e9a3710](https://github.com/aurora-silicon/linux/commit/aa659e9a3710765dc87d06e50227bdd8476cbd72), [0a72b08543fd](https://github.com/aurora-silicon/linux/commit/0a72b08543fdec99aa8117c8b4983eeee9b8c856), [d41b760af0bc](https://github.com/aurora-silicon/linux/commit/d41b760af0bcc10744b4145cfdbc19dab7625e8e) | DjDeveloperr | drm/asahi: reserve completion capacity for global compute replay [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — REC; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | F,Q | +| [06c4e4a3193e](https://github.com/aurora-silicon/linux/commit/06c4e4a3193e5b0630e343d9730cad097471440a), [1a53f784e14f](https://github.com/aurora-silicon/linux/commit/1a53f784e14f219a44f5faa7a33947c3f4dedcb1), [945285b800d3](https://github.com/aurora-silicon/linux/commit/945285b800d30fcc9a60461b70249b96b4c053ad), [868c80c92152](https://github.com/aurora-silicon/linux/commit/868c80c921527154df5e73a271583a4b0b63fdeb) | DjDeveloperr | drm/asahi: filter compute completion identities before payload reads [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/initdata.rs` | C,H | +| [304da56f2b1b](https://github.com/aurora-silicon/linux/commit/304da56f2b1b927ac837671897273835eeccb91b), [dc74efcea722](https://github.com/aurora-silicon/linux/commit/dc74efcea72275420878d60c358b3af182006eef), [513f7568ad7e](https://github.com/aurora-silicon/linux/commit/513f7568ad7eb30b97ac1309f37bbb3a11229409), [f61c86ab2830](https://github.com/aurora-silicon/linux/commit/f61c86ab28302e46cba8fbdbaa8339aaa4e17b40) | DjDeveloperr | drm/asahi: sample opening counters at the polling deadline [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — REC; `drivers/gpu/drm/asahi/g17/mod.rs` | F,Q | +| [d012beaabd09](https://github.com/aurora-silicon/linux/commit/d012beaabd0985acd8fa07a5e605f9d0c5b50590), [ff5769e16bea](https://github.com/aurora-silicon/linux/commit/ff5769e16bea501f52ff724effb309f107da39e9), [26ba1aa06add](https://github.com/aurora-silicon/linux/commit/26ba1aa06add76aa7e97a843d752fe0d08afd617), [99dd8deec12f](https://github.com/aurora-silicon/linux/commit/99dd8deec12fe28abb844854059bce6a87764199) | DjDeveloperr | drm/asahi: commit canonical binding metadata with each user mapping [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — MEM; `drivers/gpu/drm/asahi/file.rs` | M,D | +| [fef7f56d2bb3](https://github.com/aurora-silicon/linux/commit/fef7f56d2bb33e5bb627a6dc8872b271a5c504cd), [6753db1b2896](https://github.com/aurora-silicon/linux/commit/6753db1b289605b5cc007a8dd8912d948897f86b), [906cacc20dfc](https://github.com/aurora-silicon/linux/commit/906cacc20dfcccb95c6ba7809f8d3ccac4624791), [11b50b74597c](https://github.com/aurora-silicon/linux/commit/11b50b74597c2c3e1f5687d5db50ce0eaa103c21) | DjDeveloperr | drm/asahi: order canonical object invalidation with mapping removal [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — MEM; `drivers/gpu/drm/asahi/file.rs` | M,D | +| [31b8abbea796](https://github.com/aurora-silicon/linux/commit/31b8abbea79664748c2e8a17745bda9e82b119d0), [477cea7fca6e](https://github.com/aurora-silicon/linux/commit/477cea7fca6eab5f5460c2c5e2459fa9cf6e1fa4), [eb1d380fc62d](https://github.com/aurora-silicon/linux/commit/eb1d380fc62d3f8febbcf3a19d173eca44e49f19), [f594e2be7f49](https://github.com/aurora-silicon/linux/commit/f594e2be7f492d0cb4fdbdf5197122418f330036) | DjDeveloperr | drm/asahi: advertise G17 queue and input-sync capabilities [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/mod.rs` | C,R,H | +| [ee8060be8a84](https://github.com/aurora-silicon/linux/commit/ee8060be8a84b43d483ac9d4f76d51f1f5a5e35c), [80c1cad754d8](https://github.com/aurora-silicon/linux/commit/80c1cad754d8711b46ece6be4d7e00c6fd27b6df), [4810fcc3be49](https://github.com/aurora-silicon/linux/commit/4810fcc3be491f1eaa0c78a183c800d08fecbc1d), [98783a0fc38d](https://github.com/aurora-silicon/linux/commit/98783a0fc38dbac9eecb4164ed6c090e3adf591d) | DjDeveloperr | drm/asahi: acquire event records after observing their producer [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/channel.rs` | C,R,H | +| [eaebe2b1f691](https://github.com/aurora-silicon/linux/commit/eaebe2b1f691e89c991bafee6013b2e0d0b5070d), [63c2157467ec](https://github.com/aurora-silicon/linux/commit/63c2157467ec2530afd39ac823a75b74c5d210c9), [7703d07ccd6d](https://github.com/aurora-silicon/linux/commit/7703d07ccd6dbb8c50cbd993a325e0d6a61cad33), [267dc9ba9ea8](https://github.com/aurora-silicon/linux/commit/267dc9ba9ea8e068a3d9d0683a58b72a51291ece) | DjDeveloperr | drm/asahi: discard partial G17 mappings when binding fails [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — MEM; `drivers/gpu/drm/asahi/mmu.rs` | M,D | +| [7f498ba60f1a](https://github.com/aurora-silicon/linux/commit/7f498ba60f1a44401e434975d8d9433f57212539), [56d0e6d6c8b3](https://github.com/aurora-silicon/linux/commit/56d0e6d6c8b340a20cab5869ab6c516170614aa4), [bdfbf517e1fb](https://github.com/aurora-silicon/linux/commit/bdfbf517e1fb478c202e1531a8a084dc7557d4c5), [9f204371cd61](https://github.com/aurora-silicon/linux/commit/9f204371cd6118ecaa4635b76a763e6c4d53b728) | DjDeveloperr | drm/asahi: reuse the reserved output fence for submitted work [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/fence.rs` | C,R,H | +| [1c200975b7c1](https://github.com/aurora-silicon/linux/commit/1c200975b7c1ccbe890e6bdeb097fb9c5d59bfac), [7c55851883a6](https://github.com/aurora-silicon/linux/commit/7c55851883a670690b2e354802b0e265c0430930), [c97c4be0ce4e](https://github.com/aurora-silicon/linux/commit/c97c4be0ce4e1a265fa915b9ec4e634a7db2c3af), [fa771fbd7629](https://github.com/aurora-silicon/linux/commit/fa771fbd7629800795268b560e9e2ff1b0cb806c) | DjDeveloperr | drm/asahi: wake render growth when its prerequisites become ready [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/runtime/events.rs` | R,H | +| [3b92fcd8c068](https://github.com/aurora-silicon/linux/commit/3b92fcd8c068445739b69393a29c7ee7de0bc5b4), [7d4176528d08](https://github.com/aurora-silicon/linux/commit/7d4176528d08bba02e84e23f43b006a7fdbda51f), [4fe42ca7a853](https://github.com/aurora-silicon/linux/commit/4fe42ca7a85341ec777ca2f19e5ed0eb8fdafa82), [956a8e2fd2dd](https://github.com/aurora-silicon/linux/commit/956a8e2fd2ddcb7e9dc596796b79f62b4e017c55) | DjDeveloperr | drm/asahi: skip frontier scans without a cross-engine prefix [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/mod.rs` | C,R,H | +| [cc1601720b58](https://github.com/aurora-silicon/linux/commit/cc1601720b58ceedb0a76606922d7a554da13e17), [63a3c2d02dac](https://github.com/aurora-silicon/linux/commit/63a3c2d02dac4acb23d299c6a00d096f04bf5520), [75ea155f8b2e](https://github.com/aurora-silicon/linux/commit/75ea155f8b2ed4da38c39179a9a653fec0eb1c37), [5026a187d811](https://github.com/aurora-silicon/linux/commit/5026a187d811c441374d11c71a3fa0df58e118fd) | DjDeveloperr | drm/asahi: track CPU feed requests through policy lifetime [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — POWER; `drivers/gpu/drm/asahi/g17/feed.rs` | I,C,R,P | +| [848b36596112](https://github.com/aurora-silicon/linux/commit/848b36596112673711945126a51efd79d0cfa625), [547707b268e9](https://github.com/aurora-silicon/linux/commit/547707b268e96ecff8cadbc6f8b78652ab52d0ba), [a23fed471fe1](https://github.com/aurora-silicon/linux/commit/a23fed471fe1d97b7d75c738cb63d045ad434754), [c42fd8c45c3a](https://github.com/aurora-silicon/linux/commit/c42fd8c45c3a20ed2ec94c40bf5fbcf782220e7c) | DjDeveloperr | drm/asahi: bound dependency scans to the ordered command prefix [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/mod.rs` | C,R,H | +| [7a205aa6b8eb](https://github.com/aurora-silicon/linux/commit/7a205aa6b8eb17488b9882f49beece4f24b49030), [b1087fb75ab1](https://github.com/aurora-silicon/linux/commit/b1087fb75ab153f2cb017c3a08922ece0da18ca7), [b42e20b3a8d3](https://github.com/aurora-silicon/linux/commit/b42e20b3a8d3e03bdf184be067a0149ed6fbee79), [e4d41aaa9c0c](https://github.com/aurora-silicon/linux/commit/e4d41aaa9c0cdd145d5d07fe0b3d0eb209bf0879) | DjDeveloperr | drm/asahi: wait for host timestamp writes in compute prefixes [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — TIMESTAMP; `drivers/gpu/drm/asahi/g17/queue/mod.rs` | T,C | +| [23e316ffe7e5](https://github.com/aurora-silicon/linux/commit/23e316ffe7e5a3fa108eb271778222bdc7f21ee0), [2a2aff37cd8e](https://github.com/aurora-silicon/linux/commit/2a2aff37cd8e3771dea46ebdc3e45dc7b4415b4c), [55df4049246b](https://github.com/aurora-silicon/linux/commit/55df4049246b6da9482b15411e173a11cc7f04e8), [81d324022d0f](https://github.com/aurora-silicon/linux/commit/81d324022d0f97346639b06a6abaa8b86d480ead) | DjDeveloperr | drm/asahi: release successful submission work-state ownership [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/completion.rs` | C,R,H | +| [18ef3a813070](https://github.com/aurora-silicon/linux/commit/18ef3a813070620821371e437d1557a1dd1c79cf), [cfe7880a5d16](https://github.com/aurora-silicon/linux/commit/cfe7880a5d167059b871475fbc5abe60248fcf3f), [1660e75c7e3c](https://github.com/aurora-silicon/linux/commit/1660e75c7e3ccb4b906662db275d6c5ccaebd452), [e0f7410fd5b3](https://github.com/aurora-silicon/linux/commit/e0f7410fd5b368d06ab17d802720f9d76e12880c) | DjDeveloperr | drm/asahi: preserve exact historical command prefixes [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/command.rs` | C,R,H | +| [5c02e806d300](https://github.com/aurora-silicon/linux/commit/5c02e806d3004d2bb01706362298d8a6802e7433), [6507ba3ca670](https://github.com/aurora-silicon/linux/commit/6507ba3ca670d34720c6c8bfea44836b8228772b), [c7986b27e066](https://github.com/aurora-silicon/linux/commit/c7986b27e066b3d3b4fd81e95b6e174cd6c21807), [fbfb5d96f25e](https://github.com/aurora-silicon/linux/commit/fbfb5d96f25efbf2b1571ca635b195e0ad4a54be) | DjDeveloperr | drm/asahi: bring the cleanroom driver up on Neo hardware [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/buffer.rs` | C,R,H | +| [d0b66e8a8778](https://github.com/aurora-silicon/linux/commit/d0b66e8a877898097d40a380483c09e6be471532), [5fa0b934070c](https://github.com/aurora-silicon/linux/commit/5fa0b934070c615c3c745f5759d2fb7f90eaee09), [01d0b0bed7a7](https://github.com/aurora-silicon/linux/commit/01d0b0bed7a763fa775654a0a115c01f38b34584), [66af386568f7](https://github.com/aurora-silicon/linux/commit/66af386568f748d2499be3e3587ce1e7ca0ae9cd) | Ryan Murray | drm/asahi: report live GEM object accounting [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — MEM; `drivers/gpu/drm/asahi/asahi.rs` | M,D | +| [722064f1be0d](https://github.com/aurora-silicon/linux/commit/722064f1be0d171bc5d496cca352f2db6285c6c0), [f313fc4bd1a1](https://github.com/aurora-silicon/linux/commit/f313fc4bd1a1c91bad6657f7a136cc7b0f88dbe4), [2c076e0de6fe](https://github.com/aurora-silicon/linux/commit/2c076e0de6fe0a0a38a2041b02d5b9e75347375a), [f7fcc50568b9](https://github.com/aurora-silicon/linux/commit/f7fcc50568b9467f241a425b20b92ff1de10f641) | Ryan Murray | drm/asahi: port j700 queue accounting and compute recovery isolation [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — REC; `drivers/gpu/drm/asahi/g17/context.rs` | F,Q | +| [b1e288fb887d](https://github.com/aurora-silicon/linux/commit/b1e288fb887dc74d1b428d478493c6c568d3b02d), [b3b17a139f25](https://github.com/aurora-silicon/linux/commit/b3b17a139f25e8164dfdfe31d7a0c03fc2a4fa85), [2d84d7470c0f](https://github.com/aurora-silicon/linux/commit/2d84d7470c0f0d60c11c9df9e9665ee6a6a2bf71), [3c65418ee698](https://github.com/aurora-silicon/linux/commit/3c65418ee698735d33f299fe208aa8c22ff310a4) | DjDeveloperr | drm/asahi: account once per compute queue transition [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | C,H | +| [4273fb6241bb](https://github.com/aurora-silicon/linux/commit/4273fb6241bb6f9083dd8feb263fee0924fffa94), [fa2e76747a7f](https://github.com/aurora-silicon/linux/commit/fa2e76747a7f3e264fc9c8dfaaa616627cf4e70f), [2ebb1eb92796](https://github.com/aurora-silicon/linux/commit/2ebb1eb927960a96456df1369294d3d6e392a591), [982df2dcc66e](https://github.com/aurora-silicon/linux/commit/982df2dcc66e2cb2012df476c4325c8fce016dda) | DjDeveloperr | drm: carry shared GPUVM and scheduler interfaces used by the G17 review [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — DRM; `drivers/gpu/drm/drm_gpuvm.c` | C,H,M,F | +| [c163a8df57f9](https://github.com/aurora-silicon/linux/commit/c163a8df57f986f9cb2f3c3275a1dd2b627d7dba), [751ee0b363e7](https://github.com/aurora-silicon/linux/commit/751ee0b363e7d2733cb452989b6c5fcca182f087), [84f4e1fd0eaf](https://github.com/aurora-silicon/linux/commit/84f4e1fd0eaf13c985e6535d2906b68f1c519947), [ca389fba9b6c](https://github.com/aurora-silicon/linux/commit/ca389fba9b6c7c9354c2f765fe81c5ef5f069f3b) | DjDeveloperr | drm/asahi: correct reviewed lifetime, failure and submission paths [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — RTKIT; `drivers/gpu/drm/asahi/g17/channel.rs` | B,F | +| [3a61b700f19d](https://github.com/aurora-silicon/linux/commit/3a61b700f19d1b2a63017c02359a0eb30360c496), [d297d20ba207](https://github.com/aurora-silicon/linux/commit/d297d20ba207abd5b9c4d7b80aa370dd5a379a4f), [ceb8f96ba823](https://github.com/aurora-silicon/linux/commit/ceb8f96ba8232067893d3956bcc4c335bda7e268), [d9b7b82975e2](https://github.com/aurora-silicon/linux/commit/d9b7b82975e2dad4b9a5941ade2f4debadd61c6c) | DjDeveloperr | drm/asahi: order render timestamp publication independently of GPU retirement [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — TIMESTAMP; `drivers/gpu/drm/asahi/g17/completion.rs` | T,C | +| [0f89327272be](https://github.com/aurora-silicon/linux/commit/0f89327272be80ecf44b643c4198614255024367), [212a7b2ccc2d](https://github.com/aurora-silicon/linux/commit/212a7b2ccc2d7a972478f4feed4c19d77cf7e01d), [3bf578fd8958](https://github.com/aurora-silicon/linux/commit/3bf578fd8958a40af5ab8b3ea892749a1dc5b4bf), [e22376a0ca67](https://github.com/aurora-silicon/linux/commit/e22376a0ca679b5359fdef009b2479616619677b) | DjDeveloperr | drm/sched: separate Rust job timeout state from submission state [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — DRM; `drivers/gpu/drm/asahi/g17/job.rs` | C,H,M,F | +| [ca7ec4ac8767](https://github.com/aurora-silicon/linux/commit/ca7ec4ac8767714b3a4c28a8bc4e3121e5a346bf), [3810bcb5e35c](https://github.com/aurora-silicon/linux/commit/3810bcb5e35cb34abd3e80d5998c94ddc747cb34), [20560fe15a23](https://github.com/aurora-silicon/linux/commit/20560fe15a23c2dd8db188a074e245c20bcf9858), [3e464649d372](https://github.com/aurora-silicon/linux/commit/3e464649d37234b0f47dfe3c22d3f73cbb6ef45b) | DjDeveloperr | drm/asahi: observe completed render work before a deferred VM failure [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/runtime/render/submit.rs` | R,H | +| [6a57525c17e8](https://github.com/aurora-silicon/linux/commit/6a57525c17e8cdf0fcb5965e367c8bca29f6a2f4), [7b30ae9dd4eb](https://github.com/aurora-silicon/linux/commit/7b30ae9dd4eb5a5da852deae9709ef4c10ac09c8), [d20cdd0dba5a](https://github.com/aurora-silicon/linux/commit/d20cdd0dba5a51899ea79cb8ac2df9e14cd3cd8a), [83cf9f5ad0ba](https://github.com/aurora-silicon/linux/commit/83cf9f5ad0baf71001cc0aa9c25c83f27f4a219a) | DjDeveloperr | drm/asahi: publish child page tables before linking them [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — MEM; `drivers/gpu/drm/asahi/pgtable.rs` | M,D | +| [c6754b181576](https://github.com/aurora-silicon/linux/commit/c6754b181576b5fb5467f9735e7abe4451271681), [03f169480b45](https://github.com/aurora-silicon/linux/commit/03f169480b452a052c707ddc07b36c2be478d645), [da35a7e8b950](https://github.com/aurora-silicon/linux/commit/da35a7e8b9502f0afb30feac9aa41b03b0b7d080), [b321f0e70539](https://github.com/aurora-silicon/linux/commit/b321f0e7053949d9144028d0a465a4f03eb6cc83) | DjDeveloperr | drm/asahi: retire deferred mapping cleanup without allocating [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — DRM; `drivers/gpu/drm/asahi/mmu.rs` | C,H,M,F | +| [26b6f8056c9a](https://github.com/aurora-silicon/linux/commit/26b6f8056c9a913656c75753e1d719f5771a2496), [85b9304050b9](https://github.com/aurora-silicon/linux/commit/85b9304050b9331ed8206ab69532c1bb42fe9582), [982da05e5f83](https://github.com/aurora-silicon/linux/commit/982da05e5f831a54800a778df91464ab6d6372b2), [7d57556a8962](https://github.com/aurora-silicon/linux/commit/7d57556a8962dd49aadbb24af772ee5240b8c1c1) | DjDeveloperr | drm/asahi: keep compute timestamps outside shared queue aliases [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — TIMESTAMP; `drivers/gpu/drm/asahi/g17/completion.rs` | T,C | +| [a244ed25307c](https://github.com/aurora-silicon/linux/commit/a244ed25307c3d03df0a57def5fffa80d19209fa), [ee1b5a0211c1](https://github.com/aurora-silicon/linux/commit/ee1b5a0211c14b5a799304948bd91fbfebaed35c), [733634bcb0c4](https://github.com/aurora-silicon/linux/commit/733634bcb0c4de6e9885878dc8b1aad744353c83), [e3cfe6674874](https://github.com/aurora-silicon/linux/commit/e3cfe66748744b6aadf6b863b57818adb0721cdd) | DjDeveloperr | drm/shmem-helper: add revocable CPU mappings and idle backing release [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — DRM; `drivers/gpu/drm/asahi/gem.rs` | C,H,M,F | +| [9afbd440e531](https://github.com/aurora-silicon/linux/commit/9afbd440e5316ca0831289cd5dec955003554737), [97bab816b9d2](https://github.com/aurora-silicon/linux/commit/97bab816b9d20dc0b7433baf2077bc2225d9e36a), [449bb6888c6f](https://github.com/aurora-silicon/linux/commit/449bb6888c6feeb79e6390cfe00dd7d4d58b6261), [e2b08ebbcadc](https://github.com/aurora-silicon/linux/commit/e2b08ebbcadc3a9ebe2733140e1a88c62af87ba1) | DjDeveloperr | drm/asahi: reclaim idle private VM backing under memory pressure [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — MEM; `drivers/gpu/drm/asahi/file.rs` | M,D | +| [18e1bce60e1a](https://github.com/aurora-silicon/linux/commit/18e1bce60e1a82f3872012d13fe6f5b51684eb38), [babe2ba0c5a6](https://github.com/aurora-silicon/linux/commit/babe2ba0c5a68f0d3b81085736f52f57f32cbbfe), [717d6fe9e152](https://github.com/aurora-silicon/linux/commit/717d6fe9e152af9bdda4a73a81151145cb2681ca), [e82b27902f91](https://github.com/aurora-silicon/linux/commit/e82b27902f91d8c833395a966366d0ffc30769c3) | DjDeveloperr | drm/asahi: free a closed context's render pool once its release is consumed [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — GMEM; `drivers/gpu/drm/asahi/g17/context.rs` | M,C,R | +| [177e22791d7c](https://github.com/aurora-silicon/linux/commit/177e22791d7cfecb3b6c6b6315adf674ef1c9b30), [431b18cf9072](https://github.com/aurora-silicon/linux/commit/431b18cf9072a8618cbab77cfcbde3952cdb5623), [20a55bacd2fb](https://github.com/aurora-silicon/linux/commit/20a55bacd2fb91e52b6bb5cbb3e88e5218b2f726), [fa210660c43c](https://github.com/aurora-silicon/linux/commit/fa210660c43c1e8ffc4c0705792febb70c29e779) | DjDeveloperr | drm/asahi: hold idle descent for every first render pair install [#171](https://github.com/aurora-silicon/linux/pull/171) | Gated — MMIO; `drivers/gpu/drm/asahi/g17/runtime/render.rs` | B,I | +| [06175c4cc1bc](https://github.com/aurora-silicon/linux/commit/06175c4cc1bccd44765a75a70278fedb27ed9fe5), [2f10cc462301](https://github.com/aurora-silicon/linux/commit/2f10cc462301b9d1a1e64bd31468268d90a5d96d), [e94491e80ac1](https://github.com/aurora-silicon/linux/commit/e94491e80ac12de2fb9b741739b560ab17ecbb34), [d7715ebbd2d3](https://github.com/aurora-silicon/linux/commit/d7715ebbd2d3397958f08442d096a2476d32cb6a) | DjDeveloperr | drm/asahi: add GEM_MADVISE and purge advised objects under memory pressure [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — DRM; `drivers/gpu/drm/asahi/driver.rs` | C,H,M,F | +| [f338bb052975](https://github.com/aurora-silicon/linux/commit/f338bb05297509f7b87714c5727fb99a8fea373a), [c2e8f1ce84f1](https://github.com/aurora-silicon/linux/commit/c2e8f1ce84f1f72a14e6395fa5246a133211432c), [85271d321506](https://github.com/aurora-silicon/linux/commit/85271d321506ec35cc7e6a82665649e7fd2e4f8c), [c8c442ae6a83](https://github.com/aurora-silicon/linux/commit/c8c442ae6a83502a90fca56d1b8e08e669ce03cb) | DjDeveloperr | drm/asahi: drop the sysfs object and queue-slot counters [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — MEM; `drivers/gpu/drm/asahi/g17/context.rs` | M,D | +| [3bb318ea84a1](https://github.com/aurora-silicon/linux/commit/3bb318ea84a1cd2d7661948b347270c8eaf71931), [8b1bcc708f82](https://github.com/aurora-silicon/linux/commit/8b1bcc708f820baa04c2998acf8513a0146d59cf), [ed235501b653](https://github.com/aurora-silicon/linux/commit/ed235501b6531eb218d3b058bab11a807ae17713), [152092e32a7f](https://github.com/aurora-silicon/linux/commit/152092e32a7fd7a7b3a96b8e45be7db78934b7d8) | DjDeveloperr | drm/asahi: drop the stalled scheduler-release warning [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/runtime/teardown.rs` | C,R,H | +| [110dd19ab7e0](https://github.com/aurora-silicon/linux/commit/110dd19ab7e0263538c8301f903471b1700eac12), [815912e331c3](https://github.com/aurora-silicon/linux/commit/815912e331c32ab6468a108ab34c6b26d60faecb), [9d8357e27590](https://github.com/aurora-silicon/linux/commit/9d8357e275907651adbb7022b812f789ece7d040), [24ce1bcdd8eb](https://github.com/aurora-silicon/linux/commit/24ce1bcdd8ebf0f8664c043a639c0201286f94e5) | DjDeveloperr | drm/asahi: build constant firmware objects from word tables [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/fw/initdata.rs` | C,R,H | +| [b5a72b8cb8a5](https://github.com/aurora-silicon/linux/commit/b5a72b8cb8a581d84a4c35c2b4ecc11cd50fabfb), [cea457b72a30](https://github.com/aurora-silicon/linux/commit/cea457b72a308addcffbef0555408e3c2f021c7b), [8e98d0432927](https://github.com/aurora-silicon/linux/commit/8e98d0432927d780061fcc55516769f3d830291f), [7e317cd1218e](https://github.com/aurora-silicon/linux/commit/7e317cd1218e5461124fbfc8db7dd72e71253f8e) | DjDeveloperr | drm/asahi: grow the scheduler-release table instead of failing a close [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/teardown.rs` | C,R,H | +| [8afbe186d27a](https://github.com/aurora-silicon/linux/commit/8afbe186d27aab81bc71008838387799f1d9d4df), [c4f9aff1f720](https://github.com/aurora-silicon/linux/commit/c4f9aff1f720075ed3b059e6d82a5d8be5663768), [be6a3b5d938f](https://github.com/aurora-silicon/linux/commit/be6a3b5d938f2c51ef5b6d60990dcefd64cb677f), [18dbc2de6f21](https://github.com/aurora-silicon/linux/commit/18dbc2de6f21c92c49ffafce39a38c8d6439a5b5) | DjDeveloperr | drm/asahi: add a render flag for passes with at most four known primitives [#171](https://github.com/aurora-silicon/linux/pull/171) | Gated — FEW; `drivers/gpu/drm/asahi/g17/command.rs` | R | +| [7d34f2f65865](https://github.com/aurora-silicon/linux/commit/7d34f2f658658c1fd829700f2e14211a1b150ccd), [b1ec7881bf69](https://github.com/aurora-silicon/linux/commit/b1ec7881bf69ab6b1678761dec5552b28aa414e1), [3318d3e23f8e](https://github.com/aurora-silicon/linux/commit/3318d3e23f8e49a5721f9e2da0eea674eece1d2e), [c00f3a06ed2d](https://github.com/aurora-silicon/linux/commit/c00f3a06ed2d0400a5ec210c856f3a382971f033) | DjDeveloperr | drm/asahi: add a render flag that orders only the fragment stage on barriers [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/command.rs` | R,H | +| [e2b1485ec599](https://github.com/aurora-silicon/linux/commit/e2b1485ec599011654ac1f6ccc4dc78350b50566), [763771c2f3d0](https://github.com/aurora-silicon/linux/commit/763771c2f3d0613ecacaeb20d1cfd21809ead7d5), [0751d1c543c9](https://github.com/aurora-silicon/linux/commit/0751d1c543c933f65ec2ce2606d5effd25206721), [64820e0cb952](https://github.com/aurora-silicon/linux/commit/64820e0cb952592fff5708712a4b4766a8a12f4c) | DjDeveloperr | drm/asahi: give back the USC backing of idle retained compute queues [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — POWER; `drivers/gpu/drm/asahi/g17/freelist.rs` | I,C,R,P | +| [6825b2e8b6f7](https://github.com/aurora-silicon/linux/commit/6825b2e8b6f7d51be5f21a46821541ae8828c6e5), [9e693050e446](https://github.com/aurora-silicon/linux/commit/9e693050e44633bcf0503ec5b864a292d513b563), [ffbf4b465de9](https://github.com/aurora-silicon/linux/commit/ffbf4b465de966c4a5bf3b2e2881fd5678f98bfa), [3db48c0ac600](https://github.com/aurora-silicon/linux/commit/3db48c0ac600b5e919c4d2713dbd84e9b12b4ec4) | DjDeveloperr | drm/asahi: drop an exited owner's context from idle retained compute queues [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — POWER; `drivers/gpu/drm/asahi/g17/context.rs` | I,C,R,P | +| [dbea31c253f3](https://github.com/aurora-silicon/linux/commit/dbea31c253f3d1845a4998abed2db88177e82d54), [e85d3c35771a](https://github.com/aurora-silicon/linux/commit/e85d3c35771a27fc3721f13d572c2538e898837f), [9964d01e50ad](https://github.com/aurora-silicon/linux/commit/9964d01e50add98a41985adc69ace3bc5dc867f1), [5427403e7135](https://github.com/aurora-silicon/linux/commit/5427403e7135dd886ee52a78d73639bd5a2ccb17) | DjDeveloperr | drm/asahi: keep reclaim away from VMs that are in use or were just restored [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — MEM; `drivers/gpu/drm/asahi/mmu/lifetime.rs` | M,D | +| [34de15489113](https://github.com/aurora-silicon/linux/commit/34de15489113f71dd93695681d735afaddef10db), [c5fdbf93b7c8](https://github.com/aurora-silicon/linux/commit/c5fdbf93b7c8280dd1d266df8b4298b6b5c0645c), [5eb07ab2539b](https://github.com/aurora-silicon/linux/commit/5eb07ab2539bf7628366644c96a4742c934b9a78), [1c62cd0853b6](https://github.com/aurora-silicon/linux/commit/1c62cd0853b670104424bcfddeee7f7d9279304e) | DjDeveloperr | drm/asahi: count bindings and VM creation as reclaim activity [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — MEM; `drivers/gpu/drm/asahi/mmu/lifetime.rs` | M,D | +| [9bca711b6542](https://github.com/aurora-silicon/linux/commit/9bca711b65427b7dc0c384e6de2a783cde6e0160), [84e172f23883](https://github.com/aurora-silicon/linux/commit/84e172f23883c27a8eb0cdc85fa81de9c8879eb1), [3202a8b8912f](https://github.com/aurora-silicon/linux/commit/3202a8b8912f39ff7b7c06acc5fd604d5d0c4769), [dec3bc9eb622](https://github.com/aurora-silicon/linux/commit/dec3bc9eb622df05eca30716834c35506ee31f9f) | DjDeveloperr | drm/asahi: set the idle-VM reclaim quiet window to 2000 ms [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — MEM; `drivers/gpu/drm/asahi/mmu/residency.rs` | M,D | +| [f1018934472b](https://github.com/aurora-silicon/linux/commit/f1018934472b655ba44f1fcbfa2f3be2fdedb1fd), [d2046dcecf61](https://github.com/aurora-silicon/linux/commit/d2046dcecf61aceec6af3d013da811e147e3c410), [4963fa334b24](https://github.com/aurora-silicon/linux/commit/4963fa334b2439a0fc831ee3a5a8db008712a307), [1c1abce16316](https://github.com/aurora-silicon/linux/commit/1c1abce16316282e15f5e471b8ef0b5a33acc038) | DjDeveloperr | drm/asahi: release the VM status mirror object when the VM handle closes [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — MEM; `drivers/gpu/drm/asahi/g17/status.rs` | M,D | +| [cc0e08ecbe61](https://github.com/aurora-silicon/linux/commit/cc0e08ecbe61eae09d5be1619a0e6373888f7d02), [503bc0e39d66](https://github.com/aurora-silicon/linux/commit/503bc0e39d665ac97ac53e0b78b2851ca61d2242), [8057982690f1](https://github.com/aurora-silicon/linux/commit/8057982690f1f1954a83f62382873a8baf03bb8e), [6af79e6e57ef](https://github.com/aurora-silicon/linux/commit/6af79e6e57ef80d2b93d4c428956ec6cdfb11a3a) | DjDeveloperr | drm/asahi: unmap detached compute owners and free vacated pools off-lock [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — GMEM; `drivers/gpu/drm/asahi/g17/freelist.rs` | M,C,R | +| [90e691e8fe49](https://github.com/aurora-silicon/linux/commit/90e691e8fe49fa13756b6a64d22074c6e4e4a6cb), [b00ba3dcbfeb](https://github.com/aurora-silicon/linux/commit/b00ba3dcbfebf63d761a8018109bef6067a2cf8b), [6f6bef1b9fd7](https://github.com/aurora-silicon/linux/commit/6f6bef1b9fd7c46f0885eea0ece01a811e08c7d1), [0b4bd1537cc7](https://github.com/aurora-silicon/linux/commit/0b4bd1537cc71a1796b976c05cdab24e76549303) | DjDeveloperr | drm/asahi: retire a compute command on its record when its stamp never follows [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | C,H | +| [f227c7671c0f](https://github.com/aurora-silicon/linux/commit/f227c7671c0fa54ab73b1477aaf6bbc482e29b8b), [2ceb94e6a1a7](https://github.com/aurora-silicon/linux/commit/2ceb94e6a1a7939d520401fcbb0f41ce5ce9f47a), [e978503f0047](https://github.com/aurora-silicon/linux/commit/e978503f00475864733aefc433d758f0254f401f), [f647d69cc504](https://github.com/aurora-silicon/linux/commit/f647d69cc5043bf1d3389c8e250e78935e5afa24) | DjDeveloperr | drm/asahi: log the first GPU work failure reported to each G17 VM [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — OBS; `drivers/gpu/drm/asahi/g17/status.rs` | O,F | +| [0685cf3905b7](https://github.com/aurora-silicon/linux/commit/0685cf3905b73081560c7c1adc28f76efda774fa), [e8e17790b150](https://github.com/aurora-silicon/linux/commit/e8e17790b150da70debb5e9a1c31027883ad90d3), [85ab080b193f](https://github.com/aurora-silicon/linux/commit/85ab080b193fa481276ebcb9393a93b25024384f), [c8ea00a9ed62](https://github.com/aurora-silicon/linux/commit/c8ea00a9ed621e2e5c7c84461091fda3a9b7cb7b) | DjDeveloperr | drm/asahi: name the completion path in the first-failure log line [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — OBS; `drivers/gpu/drm/asahi/g17/completion.rs` | O,F | +| [ef4fa278cd1a](https://github.com/aurora-silicon/linux/commit/ef4fa278cd1abc81d229ec5853175c35537a962c), [bbbbb8aba0a3](https://github.com/aurora-silicon/linux/commit/bbbbb8aba0a3a08da0b31f31dd0fa1655089c2b0), [ec743d4d34f8](https://github.com/aurora-silicon/linux/commit/ec743d4d34f89fa2ea639801062c957f807c54aa), [5d2a444327af](https://github.com/aurora-silicon/linux/commit/5d2a444327afc38543beb78a252e23661e4d4ecc) | DjDeveloperr | drm/asahi: use one monotonic clock helper across the G17 runtime [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/mod.rs` | C,R,H | +| [860e86da2994](https://github.com/aurora-silicon/linux/commit/860e86da2994a4584ba5ce013c5ef5d79cbda111), [bca35f80a993](https://github.com/aurora-silicon/linux/commit/bca35f80a99330132651e21c61f4d0e5da583cd9), [388a083cca9a](https://github.com/aurora-silicon/linux/commit/388a083cca9a1ff7b98f14dc0e598a63494e7d89), [6850c91310a9](https://github.com/aurora-silicon/linux/commit/6850c91310a913b5ff9646261250e62b129f4ce8) | DjDeveloperr | drm/asahi: grow the scheduler-release table silently [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/teardown.rs` | C,R,H | +| [3cd28842a502](https://github.com/aurora-silicon/linux/commit/3cd28842a502864d73cbfeb7018d6f6e2e00dcdc), [033ec9a3b717](https://github.com/aurora-silicon/linux/commit/033ec9a3b71756814007305dfaf437f3e26c1195), [0c8194353943](https://github.com/aurora-silicon/linux/commit/0c8194353943469158f8651c5ec51723e130afa3), [e60f49eede0e](https://github.com/aurora-silicon/linux/commit/e60f49eede0ecf38cb206c4f59ff832b2aacc1d6) | DjDeveloperr | drm/asahi: give back the descriptor ring of vacated compute queues [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — GMEM; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | M,C,R | +| [5a98c966960a](https://github.com/aurora-silicon/linux/commit/5a98c966960a4310f741538fea1fc04724acffdb), [c9c1859181ab](https://github.com/aurora-silicon/linux/commit/c9c1859181ab17b15b76c74dc36ef00ad7cac96e), [75535b36cc0e](https://github.com/aurora-silicon/linux/commit/75535b36cc0ea75d394b20e11b993bf22e1efe20), [5d630414e13f](https://github.com/aurora-silicon/linux/commit/5d630414e13f4e017628430820fff73a86e97de9) | DjDeveloperr | drm/asahi: return killed compute queues to service with fresh backing [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — GMEM; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | M,C,R | +| [6cae1d77a10a](https://github.com/aurora-silicon/linux/commit/6cae1d77a10aee3e2cd7f52c92eb62b957f6e7ff), [b138b1c78a0e](https://github.com/aurora-silicon/linux/commit/b138b1c78a0ebbba6cef6b3976fc39bfd8b1e3d4), [4457298833f1](https://github.com/aurora-silicon/linux/commit/4457298833f1001a90c15d9277dee87ad6e11927), [16822976f9d3](https://github.com/aurora-silicon/linux/commit/16822976f9d375ddbdc788d968077a15660be5d7) | DjDeveloperr | drm/asahi: give back the render pool backing of evicted idle clients [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — MEM; `drivers/gpu/drm/asahi/g17/context.rs` | M,D | +| [8d77b4f20652](https://github.com/aurora-silicon/linux/commit/8d77b4f20652f18c7fb4e6ae9184eb88ab7e779e), [39c1fc51692c](https://github.com/aurora-silicon/linux/commit/39c1fc51692c3bfcb37c274ce84dfe812bceaaae), [7e8fe19766fd](https://github.com/aurora-silicon/linux/commit/7e8fe19766fd1b895120a8e7bd5fe78f7aca2a7c), [bff3853f260b](https://github.com/aurora-silicon/linux/commit/bff3853f260b02bdd9605bc6690905cb70aca29f) | DjDeveloperr | drm/asahi: carry a scheduling profile from DRM queue priority to firmware [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — PRIORITY; `drivers/gpu/drm/asahi/g17/context.rs` | Q | +| [a15128aa41ec](https://github.com/aurora-silicon/linux/commit/a15128aa41ecb760c5669ec03d8ffee55213bd6c), [87744d8c30b2](https://github.com/aurora-silicon/linux/commit/87744d8c30b28d86a4cc2c45b2e5009b730ed6b4), [fe676c6f4915](https://github.com/aurora-silicon/linux/commit/fe676c6f4915ec15bb3ea0880101eb5b29cfc255), [009b0fb93ba3](https://github.com/aurora-silicon/linux/commit/009b0fb93ba35380563055aa8bc725f36c505262) | DjDeveloperr | drm/asahi: drop two defensive clippy allowances in the VM binding paths [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — MEM; `drivers/gpu/drm/asahi/mmu.rs` | M,D | +| [1567322c2517](https://github.com/aurora-silicon/linux/commit/1567322c251710c365592380a195ceb22979d781), [408df60b8f35](https://github.com/aurora-silicon/linux/commit/408df60b8f35a1f510750df51f5e46bfb51d51a4), [3d1be9ce9669](https://github.com/aurora-silicon/linux/commit/3d1be9ce966988190d72dcad1d0fa4dd1b9204c9), [410d8f80e9ab](https://github.com/aurora-silicon/linux/commit/410d8f80e9ab6b405d7f918901fcbe883bb2d9b2) | DjDeveloperr | drm/asahi: build the G17 boot objects region by region [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/initdata.rs` | C,R,H | +| [0e3bd3afd897](https://github.com/aurora-silicon/linux/commit/0e3bd3afd897637d7c42d54c8ca9adc25e5a691c), [2fa38fc95677](https://github.com/aurora-silicon/linux/commit/2fa38fc956777c4e357d44db60d1b7aafaac273f), [456e6601e69d](https://github.com/aurora-silicon/linux/commit/456e6601e69d15067bcccde93498fd58e22977a1), [f5bd61786e3b](https://github.com/aurora-silicon/linux/commit/f5bd61786e3b537fd811f0ea27d2ec33874232b0) | DjDeveloperr | drm/asahi: split the G17 render pair install paths out of build_render_pair [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/runtime/render/prepare.rs` | R,H | +| [fe926de1f191](https://github.com/aurora-silicon/linux/commit/fe926de1f191b798b49cbe57e36dfba0e0cc4ac7), [e89e97a5241c](https://github.com/aurora-silicon/linux/commit/e89e97a5241cd22b95cb60304f1e2426698c45af), [ef1251b62428](https://github.com/aurora-silicon/linux/commit/ef1251b62428a647c7ed4311cc6223ca1c8fc80a), [e0fa28b70ca9](https://github.com/aurora-silicon/linux/commit/e0fa28b70ca92d7942809827a7cff7e230d68826) | DjDeveloperr | drm/asahi: split the G17 render publication into preflight, barriers and announce [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/render.rs` | R,H | +| [311b9ea8bf83](https://github.com/aurora-silicon/linux/commit/311b9ea8bf835bc7a53866f34b8520ffdc17ef01), [1235a9ee13fe](https://github.com/aurora-silicon/linux/commit/1235a9ee13fe9ed0bf846970e5f5cf9a3049e290), [f76a38f8511f](https://github.com/aurora-silicon/linux/commit/f76a38f8511fd9df3d53e30b899a780c17353d28), [74bc2f09ab74](https://github.com/aurora-silicon/linux/commit/74bc2f09ab74832be661557cacf374ee5fff11d7) | DjDeveloperr | drm/asahi: split the G17 compute publication preflight and queue configuration [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | C,H | +| [f22f4cfb5b97](https://github.com/aurora-silicon/linux/commit/f22f4cfb5b97b17cf49e7ca9feb877313156f246), [74e91a7f9ad5](https://github.com/aurora-silicon/linux/commit/74e91a7f9ad5d2f61bd5846713e27838c6f5324a), [49e9337b68a4](https://github.com/aurora-silicon/linux/commit/49e9337b68a41b5f9a3a26d8d72e8b0eecc16844), [5ad7b191f7e6](https://github.com/aurora-silicon/linux/commit/5ad7b191f7e698e3ef140163c6ecf15d3e57ff85) | DjDeveloperr | drm/asahi: size the compute descriptor ring to the kicks that can be in flight [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — GMEM; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | M,C,R | +| [bb5149df6a74](https://github.com/aurora-silicon/linux/commit/bb5149df6a74ead7da49be6953c63f4033229ff3), [9d92af630ad2](https://github.com/aurora-silicon/linux/commit/9d92af630ad2f55fe898d653e29c363c131ee4e3), [6d54c0fd885c](https://github.com/aurora-silicon/linux/commit/6d54c0fd885ceded70867de874f27987fc8f69b4), [5f6490414a68](https://github.com/aurora-silicon/linux/commit/5f6490414a683eef149ba6de38cbc27ba62f9a26) | DjDeveloperr | drm/asahi: pack G17 tiler heap blocks contiguously in their backing [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — GMEM; `drivers/gpu/drm/asahi/g17/buffer.rs` | M,C,R | +| [46c70ecebde4](https://github.com/aurora-silicon/linux/commit/46c70ecebde4be2fbd3e82e8311946a156504bf1), [f024202d33e8](https://github.com/aurora-silicon/linux/commit/f024202d33e8832b01e0cff6006fbb9e2b1178a3), [29ff1f70b488](https://github.com/aurora-silicon/linux/commit/29ff1f70b4880e79a8b980adc4d44d4eacb7c7e1), [d2a2c2fa97b9](https://github.com/aurora-silicon/linux/commit/d2a2c2fa97b994595209843720ad78b99b93f16a) | DjDeveloperr | drm/asahi: allocate only the G17 boot pool blocks that are used [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — GMEM; `drivers/gpu/drm/asahi/g17/initdata.rs` | M,C,R | +| [56b8769a1364](https://github.com/aurora-silicon/linux/commit/56b8769a1364bc6c46ab48463afa203a5aec92bb), [614f11f2eb30](https://github.com/aurora-silicon/linux/commit/614f11f2eb30471e02d42f7eab08e9bf4ec4cd41), [c304acb009f0](https://github.com/aurora-silicon/linux/commit/c304acb009f0264d1485596f651495d66efb0fec), [272955b009fe](https://github.com/aurora-silicon/linux/commit/272955b009fe844d4d06361365f166d75b6b451b) | DjDeveloperr | drm/asahi: give back idle G17 tiler heap growth beyond recent use [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — POWER; `drivers/gpu/drm/asahi/g17/buffer.rs` | I,C,R,P | +| [3434c1aab8a6](https://github.com/aurora-silicon/linux/commit/3434c1aab8a6c5d305198347040b8237c4bdce06), [35fe32db0e80](https://github.com/aurora-silicon/linux/commit/35fe32db0e806643f201fc6871095da0e68cfaf8), [1464c4bb1ade](https://github.com/aurora-silicon/linux/commit/1464c4bb1ade2f3fe0625eb93776b26a7f91db96), [c01af7a9e73c](https://github.com/aurora-silicon/linux/commit/c01af7a9e73c789c6a9f98547585a7048927af30) | DjDeveloperr | drm/asahi: give back warm G17 compute pools after ten idle seconds | Active — POWER; `drivers/gpu/drm/asahi/g17/runtime.rs` | I,C,R,P | +| [43a409830c67](https://github.com/aurora-silicon/linux/commit/43a409830c673f190213d2643d50f7500bbf69ab), [882abd400823](https://github.com/aurora-silicon/linux/commit/882abd400823eedce71e3def3c17a3143cd7318b), [57e6637d9f08](https://github.com/aurora-silicon/linux/commit/57e6637d9f087775ee87dcab22dd6c4bb0d4f685), [2d7de7690cc0](https://github.com/aurora-silicon/linux/commit/2d7de7690cc0f9c8d3a74b08c33999593b720aac) | DjDeveloperr | drm/asahi: keep compaction away from GEM pages held for the GPU | Active — MEM; `drivers/gpu/drm/asahi/gem.rs` | M,D | +| [dd109eec40ac](https://github.com/aurora-silicon/linux/commit/dd109eec40ace8e09c436eed1997e302e2b26842), [8bc791526797](https://github.com/aurora-silicon/linux/commit/8bc791526797872caece1891ed792e69dce48438), [76b1755a0610](https://github.com/aurora-silicon/linux/commit/76b1755a06109a66416af0282ec8310dc8a3292b), [6dd0f73fd3b2](https://github.com/aurora-silicon/linux/commit/6dd0f73fd3b2bade5415b31346aadeb2f38d446e) | DjDeveloperr | drm/asahi: complete page-table stores before GPU TLB invalidation | Active — MEM; `drivers/gpu/drm/asahi/g17/kick.rs` | M,D | +| [bec1201676a6](https://github.com/aurora-silicon/linux/commit/bec1201676a6f3b073e8d93791d8c9c7e6c99d7f), [36b80077e7c4](https://github.com/aurora-silicon/linux/commit/36b80077e7c45c57469f9d8cd6ba3e0aa6007cd6), [91e15ed8e7a6](https://github.com/aurora-silicon/linux/commit/91e15ed8e7a681ca293f94e337f59563cd03879d), [019ad857ddfa](https://github.com/aurora-silicon/linux/commit/019ad857ddfa08ed2473eae34db5b8b8d98b068e) | DjDeveloperr | drm/asahi: remove tiler-shrink diagnostic logging | Active — OBS; `drivers/gpu/drm/asahi/g17/buffer.rs` | O,F | +| [692752fb7bf6](https://github.com/aurora-silicon/linux/commit/692752fb7bf60c9e5f83e80dba56779fa48961c1), [3f6a8c31626f](https://github.com/aurora-silicon/linux/commit/3f6a8c31626fc077dbfcea147cd6e30cb28c5e70), [27178c4e373a](https://github.com/aurora-silicon/linux/commit/27178c4e373a23ba1015647f774a3102265c1b27), [d128b5e48465](https://github.com/aurora-silicon/linux/commit/d128b5e484658066c82504df550f6f13abcbd1cc) | DjDeveloperr | drm/asahi: reuse the render state mapping for auxiliary framebuffers | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/render/memory.rs` | R,H | +| [a2e5af9f5f03](https://github.com/aurora-silicon/linux/commit/a2e5af9f5f0334dbcdd069cc2944f11313f5d67c), [54101717e1de](https://github.com/aurora-silicon/linux/commit/54101717e1dee1b6fd64f32ae8888ec8429b821e), [c0e47197c71c](https://github.com/aurora-silicon/linux/commit/c0e47197c71c80b57974130a06386e101bbdc7ee) | Ryan Murray | drm/asahi: allocate imported and dumb GEM objects as asahi objects | Active — MEM; `drivers/gpu/drm/asahi/gem.rs` | M,D | +| [c29c639e0934](https://github.com/aurora-silicon/linux/commit/c29c639e09349ce24a2bf768c33476e2e27b2512), [c3a21491589b](https://github.com/aurora-silicon/linux/commit/c3a21491589bb62b2279867805b8e87fd2629e4d), [f2760d2f42ec](https://github.com/aurora-silicon/linux/commit/f2760d2f42ec4ede63abb18918ef4abeb4b3de35) | Dj | drm/asahi: preserve TA prefix dependencies across render pairs | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/render.rs` | R,H | +| [7598420667b6](https://github.com/aurora-silicon/linux/commit/7598420667b6be8788d768316e4218ff3c3fc1ff), [d2f283ac0589](https://github.com/aurora-silicon/linux/commit/d2f283ac058952ea84da562a7f6e6a911ebf879a), [1af8de012d22](https://github.com/aurora-silicon/linux/commit/1af8de012d2221144395f900729619b5065b14fd) | Dj | drm/asahi: provision G17P ray traversal pools from Gen1 SCS demand | Gated — RT; `drivers/gpu/drm/asahi/g17/command.rs` | F | +| [e864801fe49c](https://github.com/aurora-silicon/linux/commit/e864801fe49c9dc6d1fd6e4d1b6c2a607e672f71), [a075a1fab446](https://github.com/aurora-silicon/linux/commit/a075a1fab446a49270d387274f7793d3e9ee6560), [6ffb2dc7a658](https://github.com/aurora-silicon/linux/commit/6ffb2dc7a658e2e6bcaf48a75461b8d7e84549bf) | DjDeveloperr | drm/asahi: recycle settled quarantined G17 compute queues [#305](https://github.com/aurora-silicon/linux/pull/305) | Active — REC; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | F,Q | +| [ae76590d6ea0](https://github.com/aurora-silicon/linux/commit/ae76590d6ea0313a4741fdce5025f6c336eaf90c), [b9fd334a9c96](https://github.com/aurora-silicon/linux/commit/b9fd334a9c96f58640e0ef6ce2010749b85e7c4f), [8ad79fc25869](https://github.com/aurora-silicon/linux/commit/8ad79fc25869a9a5cd36262cc8a702b4327cf437) | DjDeveloperr | drm/asahi: g17: replay render groups firmware retired without executing [#307](https://github.com/aurora-silicon/linux/pull/307) | Active — REC; `drivers/gpu/drm/asahi/g17/job.rs` | F,Q | +| [41786756b43d](https://github.com/aurora-silicon/linux/commit/41786756b43da732fa85c6ac3e23a33e3baa9335), [0a2cdd456013](https://github.com/aurora-silicon/linux/commit/0a2cdd456013347efd1bc2a73d79e291d2d77fbc), [167852510065](https://github.com/aurora-silicon/linux/commit/1678525100652694b0aaee977ea2012b487d6a51) | DjDeveloperr | drm/asahi: g17: never kill compute commands with ray traversal scratch [#308](https://github.com/aurora-silicon/linux/pull/308) | Gated — RT; `drivers/gpu/drm/asahi/g17/queue/compute/exit.rs` | F | +| [d3f4f5dd1b59](https://github.com/aurora-silicon/linux/commit/d3f4f5dd1b592ba925f469133ebac876a101ddb7), [ec3aa2ff51b8](https://github.com/aurora-silicon/linux/commit/ec3aa2ff51b8ad6f9f39c70bf563866ee81baecb), [48b2631fdaab](https://github.com/aurora-silicon/linux/commit/48b2631fdaab66dc7bd850b3006c0f85a73aba0e) | DjDeveloperr | drm/asahi: g17: ray queries in render stages [#309](https://github.com/aurora-silicon/linux/pull/309) | Gated — RT; `drivers/gpu/drm/asahi/g17/command.rs` | F | +| [079f81d5a637](https://github.com/aurora-silicon/linux/commit/079f81d5a637e459a33e8f75a97b91f2454f1499), [2d1fcbe1a980](https://github.com/aurora-silicon/linux/commit/2d1fcbe1a9808f52f2427e4c1237b2b0eba7b7fb), [03426fb46c9c](https://github.com/aurora-silicon/linux/commit/03426fb46c9ce29e5b58db52cfefd09fac156fea) | Ryan Murray | soc: apple: mailbox, rtkit: Allow holding received messages | Active — MBOX; `drivers/soc/apple/mailbox.c` | B,O | +| [a98bb5de4b2e](https://github.com/aurora-silicon/linux/commit/a98bb5de4b2e6dd5d54150008bd322f97c8dbd0d), [c623f821fdfa](https://github.com/aurora-silicon/linux/commit/c623f821fdfaada5df34d2a79fcc4586b0d7323b), [23b2b6f4a1c9](https://github.com/aurora-silicon/linux/commit/23b2b6f4a1c93dbc90768e271c92bc914e5d676f) | Ryan Murray | soc: apple: pmp: log tunables the boot loader does not provide at debug level [#317](https://github.com/aurora-silicon/linux/pull/317) | Active — PMP; `drivers/soc/apple/pmp.rs` | P,B | +| [689592f3219f](https://github.com/aurora-silicon/linux/commit/689592f3219f4733c038cdf72e5a911084d22521), [bc1d13129d6d](https://github.com/aurora-silicon/linux/commit/bc1d13129d6d88ee93515eb547967d87170e5da3), [f80758084279](https://github.com/aurora-silicon/linux/commit/f80758084279064eb1c60e6bfa56f7649bb86a4d) | Ryan Murray | iommu: apple-dart: Demote informational probe messages to debug [#314](https://github.com/aurora-silicon/linux/pull/314) | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [e28469b8a38e](https://github.com/aurora-silicon/linux/commit/e28469b8a38e4102fd5c5b877bf51439fc00fca3), [00baae48e464](https://github.com/aurora-silicon/linux/commit/00baae48e464c44cb754d52552d3bd1f431d52f7) | Ryan Murray | iommu: apple-dart: Invalidate every stream in a domain bitmap | Active — DART; `drivers/iommu/Kconfig` | D,B | +| [aade7be0a12a](https://github.com/aurora-silicon/linux/commit/aade7be0a12a38ae8bb276fac80df27eaea97e32), [b13ede3059b0](https://github.com/aurora-silicon/linux/commit/b13ede3059b0bc51bca6033e405606e525ec2259), [4cf2113dc363](https://github.com/aurora-silicon/linux/commit/4cf2113dc36329544edc54b09dc775c5888c3975) | Ryan Murray | soc: apple: mcc: factor out disabling and flushing planes [#317](https://github.com/aurora-silicon/linux/pull/317) | Active — MCC; `drivers/soc/apple/mcc.c` | M,P | +| [ef010a009b3f](https://github.com/aurora-silicon/linux/commit/ef010a009b3ffb15af9794ed37083d1ad2c4facb), [0cba4dd60674](https://github.com/aurora-silicon/linux/commit/0cba4dd60674aa9ded2c27eaae1ff7bf4f1e243e), [8bf158a74ea7](https://github.com/aurora-silicon/linux/commit/8bf158a74ea7e2a5a7e42082fc882d3508119a09) | Ryan Murray | soc: apple: mcc: disable the planes already enabled when enabling fails [#317](https://github.com/aurora-silicon/linux/pull/317) | Active — MCC; `drivers/soc/apple/mcc.c` | M,P | +| [a099186592d6](https://github.com/aurora-silicon/linux/commit/a099186592d6aa010ab7da6d71c7c88d1801345d), [e46e30ff7457](https://github.com/aurora-silicon/linux/commit/e46e30ff7457d01b6e9cb97e8d7a5b0fd8fe20e6), [4653ef5a873f](https://github.com/aurora-silicon/linux/commit/4653ef5a873f16c68a1e9e9080a41395628ca9d4) | Ryan Murray | pmdomain: apple: pmc: undo a partial agent enable [#317](https://github.com/aurora-silicon/linux/pull/317) | Active — PMC; `drivers/pmdomain/apple/pmc.c` | P,C,R | +| [49bb592b9e9f](https://github.com/aurora-silicon/linux/commit/49bb592b9e9f626d06af3296172be8d3aa99a320), [43f4894e7c18](https://github.com/aurora-silicon/linux/commit/43f4894e7c18a46f5e379dd994f0fbd5def67ed7), [a86e8e5637c0](https://github.com/aurora-silicon/linux/commit/a86e8e5637c07504ca09b7bfa5639492af065d8c) | Ryan Murray | pmdomain: apple: pmc: restore the AP's own DCS vote when a limit is lifted [#317](https://github.com/aurora-silicon/linux/pull/317) | Gated — DCS; `drivers/pmdomain/apple/Kconfig` | P | +| [75984fc70c8f](https://github.com/aurora-silicon/linux/commit/75984fc70c8f252d96910dad4cd8635f4977b74f), [6351fe2e5e29](https://github.com/aurora-silicon/linux/commit/6351fe2e5e29f4447d997214d785f10885ef7538), [dba445e67326](https://github.com/aurora-silicon/linux/commit/dba445e6732685313e036e73dc2753c7ed8cbcb6) | Ryan Murray | soc: apple: clpc: remove the debugfs override of the floor [#317](https://github.com/aurora-silicon/linux/pull/317) | Active — CLPC; `drivers/soc/apple/clpc.c` | P,C,R,I | +| [9e4e936ed101](https://github.com/aurora-silicon/linux/commit/9e4e936ed10105188f37f3f8b2c27042e2e2ec58), [c47472a2343b](https://github.com/aurora-silicon/linux/commit/c47472a2343b6c35235e48db178cf76ae94c1f73), [9dfd51203aa8](https://github.com/aurora-silicon/linux/commit/9dfd51203aa80ce8867d98ba03c79fbb283039c2) | Ryan Murray | soc: apple: clpc: make the demand policy per-SoC data, drop the GPU input [#317](https://github.com/aurora-silicon/linux/pull/317) | Active — CLPC; `drivers/soc/apple/clpc.c` | P,C,R,I | +| [64290b216821](https://github.com/aurora-silicon/linux/commit/64290b21682151a3eb4c93d5f666d07f56aae87c), [d19ce09e3508](https://github.com/aurora-silicon/linux/commit/d19ce09e35089ec81bcc82f55198498901df10ac), [10e182579152](https://github.com/aurora-silicon/linux/commit/10e1825791523747c5f0d4ac44570fe4418ff586) | Ryan Murray | pmdomain: apple: pmgr-pwrstate: log always-on domains left off at info level [#317](https://github.com/aurora-silicon/linux/pull/317) | Active — PMGR; `drivers/pmdomain/apple/pmgr-pwrstate.c` | B,F | +| [57dafd4b412f](https://github.com/aurora-silicon/linux/commit/57dafd4b412f8b49be5362a98503be11138cd388), [f86db4089cee](https://github.com/aurora-silicon/linux/commit/f86db4089cee44d2f54de73f60aa32c8a07ffd3a), [1ef9c2113ecd](https://github.com/aurora-silicon/linux/commit/1ef9c2113ecdf2851908e4bd9793876d92152d27) | Ryan Murray | soc: apple: rtkit: Do not warn when buffers are retained on purpose [#314](https://github.com/aurora-silicon/linux/pull/314) | Active — RTKIT; `drivers/soc/apple/rtkit.c` | B,F | +| [5ebe92d17a32](https://github.com/aurora-silicon/linux/commit/5ebe92d17a32112b13e74553bab5b0e0b7c0e147), [7b29e7caccc4](https://github.com/aurora-silicon/linux/commit/7b29e7caccc4896949b0dff4dd8caf7c5fa3f0c0), [9779b2457e8f](https://github.com/aurora-silicon/linux/commit/9779b2457e8f13b0f257964570c9883f079e65f6) | Ryan Murray | pmdomain: apple: pmc: keep the DCS limit state when the rail enable fails [#345](https://github.com/aurora-silicon/linux/pull/345) | Gated — DCS; `drivers/pmdomain/apple/pmc.c` | P | +| [962dacdbb6ad](https://github.com/aurora-silicon/linux/commit/962dacdbb6ad38d9fc723d9f8ea3b3d8dee0e6a9), [4209657d7d94](https://github.com/aurora-silicon/linux/commit/4209657d7d948671c9872e1cbb53f66774793607), [1c797d3eb5cb](https://github.com/aurora-silicon/linux/commit/1c797d3eb5cb79f7e80696b4d7c93800172771c0) | Ryan Murray | cpufreq: apple-soc: say why a cluster with verified transitions fails init [#345](https://github.com/aurora-silicon/linux/pull/345) | Active — CPU; `drivers/cpufreq/apple-soc-cpufreq.c` | C,I,P | +| [854b2e6ec82e](https://github.com/aurora-silicon/linux/commit/854b2e6ec82ea030f3dcfd8ff024393019dd3818), [5a02a0cd2865](https://github.com/aurora-silicon/linux/commit/5a02a0cd28651e4eb3f2eede40af991d8d265a51), [13857139ed46](https://github.com/aurora-silicon/linux/commit/13857139ed4643ef532281808bc8035ad64c9a10) | Ryan Murray | soc: apple: mcc: print the boot DMA device before dropping its node [#345](https://github.com/aurora-silicon/linux/pull/345) | Active — MCC; `drivers/soc/apple/mcc.c` | M,P | +| [e157c990ad98](https://github.com/aurora-silicon/linux/commit/e157c990ad98e3008b552e45d0c85448f95e4266), [ec4fc264985c](https://github.com/aurora-silicon/linux/commit/ec4fc264985c5505be71434f96cc949d0037801f), [0b65493ccd0f](https://github.com/aurora-silicon/linux/commit/0b65493ccd0f4a779e592258032cb40ce03d1a1c) | Ryan Murray | pmdomain: apple: pmgr-pwrstate: tidy the rate-limited error messages [#345](https://github.com/aurora-silicon/linux/pull/345) | Active — PMGR; `drivers/pmdomain/apple/pmgr-pwrstate.c` | B,F | +| [3a9f56606ec3](https://github.com/aurora-silicon/linux/commit/3a9f56606ec3001c7cd7df137cf49a6ae2db0157), [3762bb8f345d](https://github.com/aurora-silicon/linux/commit/3762bb8f345de262f1efca74de864457a3adc9aa), [a516c3f7c3f8](https://github.com/aurora-silicon/linux/commit/a516c3f7c3f85aeb05efe5e3aff5b7890c7d022c) | Ryan Murray | iommu: apple-dart: Invalidate every DART even if a TLB sync step fails [#345](https://github.com/aurora-silicon/linux/pull/345) | Active — DART; `drivers/iommu/apple-dart-test.c` | D,B | +| [8a2875a6789d](https://github.com/aurora-silicon/linux/commit/8a2875a6789d2a4f1452beafd50e4de134b30683), [0f997e1c9354](https://github.com/aurora-silicon/linux/commit/0f997e1c9354b9aa18c7ae13cdf61328b1f50d9d), [df5213734e31](https://github.com/aurora-silicon/linux/commit/df5213734e31c372fc36a2375539e4d69b5f815a) | Ryan Murray | soc: apple: rtkit: Only parse an initial crash log in inherited sessions [#345](https://github.com/aurora-silicon/linux/pull/345) | Active — RTKIT; `drivers/soc/apple/rtkit.c` | B,F | +| [d6caa25b65d2](https://github.com/aurora-silicon/linux/commit/d6caa25b65d2ca7fc7315ee568e0cbd250a9593c), [f501cf5f491d](https://github.com/aurora-silicon/linux/commit/f501cf5f491da7527d71b86cd164e094aac050fd), [82494bf5c6b0](https://github.com/aurora-silicon/linux/commit/82494bf5c6b07739782eeac837b9f6a96c70ca36) | Ryan Murray | iommu/apple-dart: Do not reset a DART that kept its state across sleep [#336](https://github.com/aurora-silicon/linux/pull/336) | Active — DART; `drivers/iommu/apple-dart-test.c` | D,B | +| [91d9f93cf842](https://github.com/aurora-silicon/linux/commit/91d9f93cf842a892913e7ce56218ddc27292a6c1), [dc02d89a1465](https://github.com/aurora-silicon/linux/commit/dc02d89a1465719e6ab3b3a5e31dbea08e1a8f68), [39bc76211b30](https://github.com/aurora-silicon/linux/commit/39bc76211b30ca23b29eb740ffdf0ae8cf2ed5d3) | Ryan Murray | iommu/apple-dart: Keep a retained DART in bypass from being reset [#336](https://github.com/aurora-silicon/linux/pull/336) | Active — DART; `drivers/iommu/apple-dart-test.c` | D,B | +| [b6b28c111cff](https://github.com/aurora-silicon/linux/commit/b6b28c111cff20b5312fee34ca544ce6f7fd00a5), [469fd41db0a3](https://github.com/aurora-silicon/linux/commit/469fd41db0a33df014d0523648c5e1f052895c9a), [d9fc21447419](https://github.com/aurora-silicon/linux/commit/d9fc214474191909db140a5ba565a014819bd6de) | Ryan Murray | iommu/apple-dart: Restore the translations when the reset on resume fails [#336](https://github.com/aurora-silicon/linux/pull/336) | Active — DART; `drivers/iommu/apple-dart-test.c` | D,B | +| [4bfe19d0ad80](https://github.com/aurora-silicon/linux/commit/4bfe19d0ad8042e95b784fe2e439e15c8f869ac5), [cd1ff978d660](https://github.com/aurora-silicon/linux/commit/cd1ff978d6600a354b93fd829d9c9588715c44f1), [43c7fc8f82a6](https://github.com/aurora-silicon/linux/commit/43c7fc8f82a65f2e3807f41f03421df2b27ed3de) | Ryan Murray | irqchip/apple-aic: Allow interrupts to wake the system | Active — AIC; `drivers/irqchip/irq-apple-aic.c` | O,C,I | +| [345f6329c88b](https://github.com/aurora-silicon/linux/commit/345f6329c88b1ea97ecf1c373bc573a9c20947e7), [eee098196369](https://github.com/aurora-silicon/linux/commit/eee098196369f3a2aebe4f99333c53d538caa1c5) | Ryan Murray | drm/asahi: queue background work on system_dfl_wq | Active — SHARED; `drivers/gpu/drm/asahi/g17/mod.rs` | C,R,H | +| [e04141b6136c](https://github.com/aurora-silicon/linux/commit/e04141b6136c63f61679eda781cabe349297d58e), [a741120e3ca0](https://github.com/aurora-silicon/linux/commit/a741120e3ca0d349dffab6cc32086935ecb98d81) | Chris Kearney | soc: apple: validate PMP boot arguments before patching | Active — PMP; `drivers/soc/apple/pmp.rs` | P,B | +| [8a74c2db9581](https://github.com/aurora-silicon/linux/commit/8a74c2db9581fd106dc90be11fb1a1e075bf07fa), [d67dbeedca63](https://github.com/aurora-silicon/linux/commit/d67dbeedca63d1389fb5754d419fffae0c678155) | Chris Kearney | soc: apple: bound PMP arguments by the mapped SRAM size | Active — PMP; `drivers/soc/apple/pmp.rs` | P,B | +| [6c53c0ff9715](https://github.com/aurora-silicon/linux/commit/6c53c0ff9715f055de8af928ed57277f1f0f30a2), [687f1c4f2190](https://github.com/aurora-silicon/linux/commit/687f1c4f21906c698f38c29c467f4aa74425509e) | Eryk Wieliczko | soc: apple: acknowledge serialized PMP device power commands | Active — PMP; `drivers/pmdomain/apple/pmp-report.c` | P,B | +| [5a68774e87a8](https://github.com/aurora-silicon/linux/commit/5a68774e87a8baa711c211757b709966b2193be5), [fefec69b9fca](https://github.com/aurora-silicon/linux/commit/fefec69b9fca69f98a026a4dbdf9bde89b64544b) | Chris Kearney | soc: apple: retry unsent RTKit endpoint starts once | Active — RTKIT; `drivers/soc/apple/rtkit.c` | B,F | +| [dc7d0b49a07d](https://github.com/aurora-silicon/linux/commit/dc7d0b49a07df88aab2ee9cbb3fae17db8694f2b), [5bd0a91f565c](https://github.com/aurora-silicon/linux/commit/5bd0a91f565cf815b9f999b3e704edba2563aac8) | Marius Gehrmann | soc: apple: rtkit: Honour apple,sync-syslog-ack | Active — RTKIT; `drivers/soc/apple/rtkit-internal.h` | B,F | +| [d67d380d1519](https://github.com/aurora-silicon/linux/commit/d67d380d1519babe4b9d080830e31bca56da4d8f), [9d0290148561](https://github.com/aurora-silicon/linux/commit/9d029014856181eb6190324b8300d82590ab2e8b) | Chris Kearney | soc: apple: rtkit: cap the kdebug buffer, and take a late completion | Active — RTKIT; `drivers/soc/apple/rtkit.c` | B,F | +| [1369d18b4ec7](https://github.com/aurora-silicon/linux/commit/1369d18b4ec7bf0a631bc68fbdf7d894f2a4c542), [0d9ac5799dd4](https://github.com/aurora-silicon/linux/commit/0d9ac5799dd4dffe26513b100e93930bb3833a53) | Eryk Wieliczko | soc: apple: acknowledge OSLog progress in negotiated and inherited sessions | Active — RTKIT; `drivers/soc/apple/rtkit-internal.h` | B,F | +| [e47d06ceb56f](https://github.com/aurora-silicon/linux/commit/e47d06ceb56fd73444f3e17954f961dce5edfdcf), [47e137e98667](https://github.com/aurora-silicon/linux/commit/47e137e986675ca44843155e66b731c167932749) | Chris Kearney | iommu/apple-dart: reserve retained firmware tables and root slots | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [e663049674c8](https://github.com/aurora-silicon/linux/commit/e663049674c81f6818a88b594f029d0bf9f45ccc), [058d2b7355cf](https://github.com/aurora-silicon/linux/commit/058d2b7355cffe72dad9053486a26ae2822d51a2) | DjDeveloperr | soc: apple: finish the merge of DART, QSPI and SMC hwmon | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [d97a27e94fd8](https://github.com/aurora-silicon/linux/commit/d97a27e94fd8967ce1c3846dcc71628a8b39db36), [6dbfbcb21eb0](https://github.com/aurora-silicon/linux/commit/6dbfbcb21eb09d07ae59037a1ceca8cc3500841f) | Eryk Wieliczko | drm/asahi: validate GEM size before page rounding | Active — MEM; `drivers/gpu/drm/asahi/gem.rs` | M,D | +| [6d457ae0a70c](https://github.com/aurora-silicon/linux/commit/6d457ae0a70c583dc760d05ac552829d7370060f), [b038d2767d6e](https://github.com/aurora-silicon/linux/commit/b038d2767d6e08c0dae43c87da89dfd77d719f7c) | Chris Kearney | arm64: dts: apple: t8140: drop the t6000 fallback from the GFX report | Active translated data — FW | B,P | +| [496040e469aa](https://github.com/aurora-silicon/linux/commit/496040e469aa1ea3457c89cbd677d9f964614cca), [17bf156987c5](https://github.com/aurora-silicon/linux/commit/17bf156987c5f7be583bd5efc86430b1055efc36) | Chris Kearney | pmdomain: apple: pmp-report: match T8140 report entries by compatible | Active — PMP; `drivers/pmdomain/apple/pmp-report.c` | P,B | +| [e033fe71b24d](https://github.com/aurora-silicon/linux/commit/e033fe71b24da2b2aabc90b9c5faa5bd0f497d9f), [5f7925d83ca6](https://github.com/aurora-silicon/linux/commit/5f7925d83ca680ba7760dbd16b9e0cb61029161a) | Chris Kearney | drm/asahi: keep the range TLBI base address in its field | Gated — UAT; `drivers/gpu/drm/asahi/mem.rs` | D | +| [6582e3d5ef6f](https://github.com/aurora-silicon/linux/commit/6582e3d5ef6f47ff7e0a11d3fffa47f15ef22a44), [f7132eb38e81](https://github.com/aurora-silicon/linux/commit/f7132eb38e81e5af14711b3da87aa6cf96b3ba98) | Ryan Murray | iommu: apple-dart: Factor out the DT DMA window parse | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [fc7981e78871](https://github.com/aurora-silicon/linux/commit/fc7981e78871a2ec91b38835bc84e03611b4e860), [ff11ec1083af](https://github.com/aurora-silicon/linux/commit/ff11ec1083af04c76c1d91fcebc78333681cf519) | Chris Kearney | pmdomain: apple: validate PMP startup tables before seeding | Active — PMP; `drivers/pmdomain/apple/.kunitconfig.apple-pmp` | P,B | +| [4c673adf2c11](https://github.com/aurora-silicon/linux/commit/4c673adf2c110c8264da8b0130eaf181fd6bd560), [782906a65540](https://github.com/aurora-silicon/linux/commit/782906a65540c569f6afa8b49b8c6ac22c9e8dc5) | Ryan Murray | pmdomain: apple: pmp-report: select OF_DYNAMIC | Active — PMP; `drivers/pmdomain/apple/Kconfig` | P,B | +| [f78d7690bfd2](https://github.com/aurora-silicon/linux/commit/f78d7690bfd2d927462c7af34242d6476a0cf59c), [a41d634596e5](https://github.com/aurora-silicon/linux/commit/a41d634596e536031e719b5ea8a3082220e8ec61) | Ryan Murray | drm/asahi: mmu: preallocate enough GPUVAs to unmap across two mappings | Active — MEM; `drivers/gpu/drm/asahi/mmu.rs` | M,D | +| [10fc7af5e0b8](https://github.com/aurora-silicon/linux/commit/10fc7af5e0b897112afaa5fc9ab791f241244969), [755ac9ed1425](https://github.com/aurora-silicon/linux/commit/755ac9ed142576a6e27da54a39b68290e861b34b) | DjDeveloperr | drm/asahi: g17: move the G17P UAPI past the M3 and J613 numbers | Active translated data — FW; `drivers/gpu/drm/asahi/g17/command.rs` | B,C,R | +| [6d5b51d6e818](https://github.com/aurora-silicon/linux/commit/6d5b51d6e818fc0b8915b43a030854d6d96ff330), [62aae2f0f8cb](https://github.com/aurora-silicon/linux/commit/62aae2f0f8cb52a40b2cf4ea21212ad2badd267c) | Ryan Murray | drm/asahi: uapi: use integration/aurora's wording | Active translated data — FW | B,C,R | +| [ff25cb1691ff](https://github.com/aurora-silicon/linux/commit/ff25cb1691ffa6e285dbcb014992e3838f4ad6fe) | Ryan Murray | drm/asahi: Adapt the lifetime and module-ownership changes to Linux 7.3 [#375](https://github.com/aurora-silicon/linux/pull/375) | Active — DRM; `drivers/gpu/drm/asahi/g17/job.rs` | C,H,M,F | +| [2348e8c3acd2](https://github.com/aurora-silicon/linux/commit/2348e8c3acd2920ca583d801645e8387fd80c7f2) | Ryan Murray | drm/asahi: regs: Hold the G17 SGX mapping as a DevresIoMem [#375](https://github.com/aurora-silicon/linux/pull/375) | Active — MEM; `drivers/gpu/drm/asahi/regs.rs` | M,D | +| [d4128b7c28ce](https://github.com/aurora-silicon/linux/commit/d4128b7c28ce177d227ff5488145e8eedd970610) | Ryan Murray | drm/asahi: make SG backing leases revocable [#375](https://github.com/aurora-silicon/linux/pull/375) | Active — DRM; `drivers/gpu/drm/asahi/gem.rs` | C,H,M,F | +| [464cb2e30ae3](https://github.com/aurora-silicon/linux/commit/464cb2e30ae395d13723d161f2aef41066dbb79d) | Ryan Murray | drm/asahi: attribute GPU memory to its creator and limit it [#375](https://github.com/aurora-silicon/linux/pull/375) | Active — MEM; `drivers/gpu/drm/asahi/asahi_core.rs` | M,D | +| [47f408da5996](https://github.com/aurora-silicon/linux/commit/47f408da5996da161de66282da83550bf436450e) | Ryan Murray | drm/asahi: clean up Clippy diagnostics in Aurora additions [#375](https://github.com/aurora-silicon/linux/pull/375) | Active — OBS; `drivers/gpu/drm/asahi/g17/command.rs` | O,F | +| [e44a6b59f5d2](https://github.com/aurora-silicon/linux/commit/e44a6b59f5d248ee1a026b9dbb6dbd244e35a1d7) | Ryan Murray | soc: apple: pmp: use lossless command and bootargs conversions | Active — PMP; `drivers/soc/apple/pmp.rs` | P,B | +| [761626d47af3](https://github.com/aurora-silicon/linux/commit/761626d47af3eb6da3ff8c998373646349aa7169) | Ryan Murray | drm/apple: quiet routine H17P external run-mode syslog records | Active — DISPLAY; `drivers/gpu/drm/apple/Makefile` | B | +| [ac38eb0f57a1](https://github.com/aurora-silicon/linux/commit/ac38eb0f57a17f59eb2ce8158df1fb81aeff2955) | Ryan Murray | drm/asahi: g17: wait for a compute command's stamp before releasing it [#375](https://github.com/aurora-silicon/linux/pull/375) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | C,H | +| [1e3b9caebc7b](https://github.com/aurora-silicon/linux/commit/1e3b9caebc7b1924acc3643222ff95b85e62bdb7) | Ryan Murray | drm/asahi: memacct: Read the module parameters by value on Linux 7.3 [#375](https://github.com/aurora-silicon/linux/pull/375) | Active — MEM; `drivers/gpu/drm/asahi/memacct.rs` | M,D | +| [db2943a27f88](https://github.com/aurora-silicon/linux/commit/db2943a27f88dcd011ae97498cbf2250a4245463) | Jonathan Goyvaerts | soc: apple: rtkit: Log unknown oslog messages at debug level | Active — RTKIT; `drivers/soc/apple/rtkit.c` | B,F | +| [1797c344df7f](https://github.com/aurora-silicon/linux/commit/1797c344df7f7631b2f9be7c9be1a55d8852b164) | Jonathan Goyvaerts | pmdomain: apple: pmp-report: Add T6040 support [#374](https://github.com/aurora-silicon/linux/pull/374) | Active — PMP; `drivers/pmdomain/apple/pmp-report.c` | P,B | +| [927c31856676](https://github.com/aurora-silicon/linux/commit/927c318566760e7b4845d36904022c38271b925d) | Jonathan Goyvaerts | soc: apple: pmp: Add T6040 support [#374](https://github.com/aurora-silicon/linux/pull/374) | Active — PMP; `drivers/soc/apple/pmp.rs` | P,B | +| [7fcc7fd38c43](https://github.com/aurora-silicon/linux/commit/7fcc7fd38c43b6aa64ea2270dcd32cdd57d19f37) | Ryan Murray | drm/asahi: carry G17 admission prerequisites to 7.3 [#377](https://github.com/aurora-silicon/linux/pull/377) | Active — MEM; `drivers/gpu/drm/asahi/g17/freelist.rs` | M,D | +| [c9f72dc51116](https://github.com/aurora-silicon/linux/commit/c9f72dc51116c12b3aa840185eee5a45f4b298b5) | Ryan Murray | drm/asahi: carry G17 pool-release recovery and stamp-wait fixup to 7.3 [#377](https://github.com/aurora-silicon/linux/pull/377) | Active — REC; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | F,Q | +| [10e9da2303fc](https://github.com/aurora-silicon/linux/commit/10e9da2303fc79dc205989075855350020bf115b) | Ryan Murray | drm/asahi: hand abandoned compute growth back to witness settlement [#377](https://github.com/aurora-silicon/linux/pull/377) | Active — SHARED; `drivers/gpu/drm/asahi/g17/freelist.rs` | C,H | +| [e933ad436de0](https://github.com/aurora-silicon/linux/commit/e933ad436de06981d95e9de6ce5626f4ba99e6ff) | Ryan Murray | drm/asahi: borrow the compute pool mutably for header replacement [#377](https://github.com/aurora-silicon/linux/pull/377) | Active — GMEM; `drivers/gpu/drm/asahi/g17/freelist.rs` | M,C,R | +| [04358c1ebe86](https://github.com/aurora-silicon/linux/commit/04358c1ebe86cfbfdc926cfb41ff3e3549248279) | Ryan Murray | drm/asahi: hold failed growth reservations until aliases have gone [#377](https://github.com/aurora-silicon/linux/pull/377) | Active — SHARED; `drivers/gpu/drm/asahi/g17/runtime.rs` | C,R,H | +| [d765e50ada90](https://github.com/aurora-silicon/linux/commit/d765e50ada90e7f00a4ad608c9ff027d0a07a82a) | Ryan Murray | drm/asahi: drop tentative growth aliases before returning a fresh QID [#377](https://github.com/aurora-silicon/linux/pull/377) | Active — SHARED; `drivers/gpu/drm/asahi/g17/runtime.rs` | C,R,H | +| [780956abdb5f](https://github.com/aurora-silicon/linux/commit/780956abdb5fe59c76ca7343bc7f19770df658dd) | Ryan Murray | drm/asahi: compose shared capacity admission with accepted recovery port [#377](https://github.com/aurora-silicon/linux/pull/377) | Active — MEM; `drivers/gpu/drm/asahi/g17/buffer.rs` | M,D | +| [b52bd36805e0](https://github.com/aurora-silicon/linux/commit/b52bd36805e0c35bac5c8255aae8cfad7cce5dd7) | Ryan Murray | drm/asahi: give native ray-query jobs a bounded execution allowance [#377](https://github.com/aurora-silicon/linux/pull/377) | Gated — RT; `drivers/gpu/drm/asahi/g17/job.rs` | F | +| [a5bc25b2ce19](https://github.com/aurora-silicon/linux/commit/a5bc25b2ce1997452676894fc8c9b401c7396e6c) | Ryan Murray | drm/asahi: cover render helpers and AS builds with bounded execution [#377](https://github.com/aurora-silicon/linux/pull/377) | Gated — RT; `drivers/gpu/drm/asahi/g17/command.rs` | F | +| [9827b6bb277a](https://github.com/aurora-silicon/linux/commit/9827b6bb277af5912490229f6fe56d3c575dafe3) | Ryan Murray | drm/asahi: bound repeated timeout recovery and G17 mailbox stalls [#377](https://github.com/aurora-silicon/linux/pull/377) | Active — MBOX; `drivers/gpu/drm/asahi/g17/dependency.rs` | B,O | +| [c314dbb5bd42](https://github.com/aurora-silicon/linux/commit/c314dbb5bd4284697bd065db87eb038021b18125) | Ryan Murray | drm/asahi: expire background pool-release and render-replay polls [#377](https://github.com/aurora-silicon/linux/pull/377) | Active — REC; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | F,Q | +| [6c2d17ff4079](https://github.com/aurora-silicon/linux/commit/6c2d17ff40796899d7190580cabaeea749c73d0a) | Ryan Murray | drm/sched: initialise free_guilty in drm_sched_init() [#381](https://github.com/aurora-silicon/linux/pull/381) | Active — DRM; `drivers/gpu/drm/scheduler/sched_main.c` | C,H,M,F | +| [0d1871c3cfde](https://github.com/aurora-silicon/linux/commit/0d1871c3cfde9cc45b8bc244dde4775dbf4e05c6) | Ryan Murray | rust: drm: sched: zero the scheduler before drm_sched_init() [#381](https://github.com/aurora-silicon/linux/pull/381) | Active — DRM; `rust/kernel/drm/sched.rs` | C,H,M,F | +| [24399672582c](https://github.com/aurora-silicon/linux/commit/24399672582c155df35cadf8cb81a3397958f9e2) | Ryan Murray | drm/asahi: retry G17 retained compute publication pressure [#382](https://github.com/aurora-silicon/linux/pull/382) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | C,H | +| [5ae95851753d](https://github.com/aurora-silicon/linux/commit/5ae95851753d377e13b7e3ac9012452a7fdaccc9) | Jonathan Goyvaerts | pmdomain: apple: pmc: Add the T6040 voter interface [#385](https://github.com/aurora-silicon/linux/pull/385) | Active — PMC; `drivers/pmdomain/apple/pmc-test.c` | P,C,R | +| [7bbd2ad37554](https://github.com/aurora-silicon/linux/commit/7bbd2ad375543683c070aca6487fae75a79fc747) | Jonathan Goyvaerts | soc: apple: clpc: Add the T6040 demand table [#385](https://github.com/aurora-silicon/linux/pull/385) | Active — CLPC; `drivers/soc/apple/clpc.c` | P,C,R,I | +| [b3765bd38ecb](https://github.com/aurora-silicon/linux/commit/b3765bd38ecbfaa5368b760c9d908964abf8c7a5) | Jonathan Goyvaerts | soc: apple: mcc: Allow up to 16 cache planes [#385](https://github.com/aurora-silicon/linux/pull/385) | Active — MCC; `drivers/soc/apple/Kconfig` | M,P | +| [f236c33daca6](https://github.com/aurora-silicon/linux/commit/f236c33daca696a326bd652799bd189ac2b6421f) | Jonathan Goyvaerts | soc: apple: clpc: Request a state only after sustained load [#385](https://github.com/aurora-silicon/linux/pull/385) | Active — CLPC; `drivers/soc/apple/clpc.c` | P,C,R,I | +| [32960bbd0e9c](https://github.com/aurora-silicon/linux/commit/32960bbd0e9cf24931f18d25131eef24f74f3d31) | Jonathan Goyvaerts | iommu/apple-dart: Mirror the firmware's display mappings on T6040 [#386](https://github.com/aurora-silicon/linux/pull/386) | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [833c6ed10ffc](https://github.com/aurora-silicon/linux/commit/833c6ed10ffc2ac2d38b7bbb0d85dcc2b8e171df) | DjDeveloperr | drm/asahi: reduce minimum G17 target record backing [#390](https://github.com/aurora-silicon/linux/pull/390) | Active — MEM; `drivers/gpu/drm/asahi/hw/t8140.rs` | M,D | +| [145361fdc90e](https://github.com/aurora-silicon/linux/commit/145361fdc90e82f4bddf6234ec46d0e023f41f86) | DjDeveloperr | drm/asahi: order G17 queue owner changes after scheduler releases [#390](https://github.com/aurora-silicon/linux/pull/390) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | C,R,H | +| [1d478ee6c06b](https://github.com/aurora-silicon/linux/commit/1d478ee6c06b56b48782d08b693d9aa9e5c43435) | DjDeveloperr | drm/asahi: keep G17 render kick ring storage for the life of the device [#390](https://github.com/aurora-silicon/linux/pull/390) | Active — SHARED; `drivers/gpu/drm/asahi/g17/kick.rs` | R,H | +| [7304ddbf6d4d](https://github.com/aurora-silicon/linux/commit/7304ddbf6d4d47b7976efcd20623524c9c53b0b1) | DjDeveloperr | drm/asahi: keep G17 render capacity waits from stopping the runtime [#390](https://github.com/aurora-silicon/linux/pull/390) | Active — SHARED; `drivers/gpu/drm/asahi/g17/freelist.rs` | R,H | +| [72d57c0b9ac9](https://github.com/aurora-silicon/linux/commit/72d57c0b9ac9b8d5fcd58c59eebfa7f9c605399c) | DjDeveloperr | drm/asahi: wait for G17 contexts of closing queues at queue creation [#390](https://github.com/aurora-silicon/linux/pull/390) | Active — REC; `drivers/gpu/drm/asahi/g17/capacity.rs` | F,Q | +| [a4351b50cd8d](https://github.com/aurora-silicon/linux/commit/a4351b50cd8d6f194bb71252acf9c9e2cf636db4) | DjDeveloperr | drm/asahi: keep G17 publication waits out of the submit ioctl [#390](https://github.com/aurora-silicon/linux/pull/390) | Active — SHARED; `drivers/gpu/drm/asahi/g17/completion.rs` | C,R,H | +| [c556dacd085b](https://github.com/aurora-silicon/linux/commit/c556dacd085b148da86b47792dc34d14f02ace86) | DjDeveloperr | drm/asahi: rerun the G17 event pass for completions missed while a pair is borrowed [#390](https://github.com/aurora-silicon/linux/pull/390) | Active — SHARED; `drivers/gpu/drm/asahi/g17/runtime/render.rs` | C,R,H | +| [8744e9417249](https://github.com/aurora-silicon/linux/commit/8744e9417249175511480765dbcc99f0a09c0fb9) | DjDeveloperr | drm/asahi: reuse G17 compute timestamp pages within a context [#390](https://github.com/aurora-silicon/linux/pull/390) | Active — TIMESTAMP; `drivers/gpu/drm/asahi/g17/completion.rs` | T,C | +| [6f7faeecea0e](https://github.com/aurora-silicon/linux/commit/6f7faeecea0eccc8ee317ba163867df339d54fa5) | Jonathan Goyvaerts | drm/asahi: g17: do not advertise soft faults [#391](https://github.com/aurora-silicon/linux/pull/391) | Active — SHARED; `drivers/gpu/drm/asahi/g17/mod.rs` | C,R,H | +| [b0912f542fd0](https://github.com/aurora-silicon/linux/commit/b0912f542fd0655f10631977a8c63abc51a85ada) | Jonathan Goyvaerts | drm/asahi: g17: reread a torn borrowed render snapshot [#391](https://github.com/aurora-silicon/linux/pull/391) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/render/retirement.rs` | R,H | +| [30ea4b130473](https://github.com/aurora-silicon/linux/commit/30ea4b13047377972cb75b78a4cbe5885cf46d1d) | Jonathan Goyvaerts | drm/asahi: g17: do not fail a blamed render that attribution cleared [#391](https://github.com/aurora-silicon/linux/pull/391) | Gated — KEY; `drivers/gpu/drm/asahi/g17/recovery.rs` | F | +| [e3d8d1b2fc1f](https://github.com/aurora-silicon/linux/commit/e3d8d1b2fc1feada69ec028203f727f642a150ac) | Jonathan Goyvaerts | drm/asahi: g17: skip already-signalled successful input fences [#391](https://github.com/aurora-silicon/linux/pull/391) | Active — SHARED; `drivers/gpu/drm/asahi/g17/queue/mod.rs` | C,R,H | +| [2321a1bc6ea6](https://github.com/aurora-silicon/linux/commit/2321a1bc6ea6c8a62a275685c0542a293c9b5438) | Jonathan Goyvaerts | drm/asahi: allow HIGH queue priority with CAP_SYS_NICE or DRM master [#391](https://github.com/aurora-silicon/linux/pull/391) | Active — DRM; `drivers/gpu/drm/asahi/g17/mod.rs` | B,M | +| [77a2bbe1a951](https://github.com/aurora-silicon/linux/commit/77a2bbe1a9512960e8c9ba17b466b2c9ab51fb5f) | Ryan Murray | soc: apple: rtkit: Keep warning about unknown oslog messages | Active — RTKIT; `drivers/soc/apple/rtkit.c` | B,F | +| [b2adcfc94046](https://github.com/aurora-silicon/linux/commit/b2adcfc94046a10a989c6fd61fdb1bf7131ef78a) | Ryan Murray | drm/asahi: fail large G17 backing population without direct reclaim [#397](https://github.com/aurora-silicon/linux/pull/397) | Active — DRM; `drivers/gpu/drm/asahi/file.rs` | C,H,M,F | +| [632e4676500b](https://github.com/aurora-silicon/linux/commit/632e4676500bab6fd88817b1cf767afb1bf5da52) | Ryan Murray | drm/asahi: expose memory admission capacities in GET_PARAMS [#397](https://github.com/aurora-silicon/linux/pull/397) | Active — MEM; `drivers/gpu/drm/asahi/file.rs` | M,D | +| [ffe8011a479c](https://github.com/aurora-silicon/linux/commit/ffe8011a479ccd74dde747225c9baf67e2a02ba6) | Chris Kearney | drm/asahi: g15: reject errored input dependencies before publication [#395](https://github.com/aurora-silicon/linux/pull/395) | Active — SHARED; `drivers/gpu/drm/asahi/g17/mod.rs` | C,R,H | +| [faef7fd12e22](https://github.com/aurora-silicon/linux/commit/faef7fd12e22a4d56873b91981d412fd981cfd21) | Chris Kearney | drm/asahi: expose the inherited-error policy only for native G15 [#395](https://github.com/aurora-silicon/linux/pull/395) | Active — SHARED; `drivers/gpu/drm/asahi/g17/mod.rs` | C,R,H | +| [8736001f2b13](https://github.com/aurora-silicon/linux/commit/8736001f2b130a9e477fdef7d1db45f31591bfec) | Eryk Wieliczko | pmdomain: apple: pmgr-pwrstate: Honour apple,no-auto-pm [#395](https://github.com/aurora-silicon/linux/pull/395) | Active — PMGR; `drivers/pmdomain/apple/pmgr-pwrstate.c` | B,F | +| [2aff1b95c550](https://github.com/aurora-silicon/linux/commit/2aff1b95c55039846beb6451c737154254aa3ae0) | Eryk Wieliczko | soc: apple: rtkit: describe and adopt retained system buffers | Active — RTKIT; `drivers/soc/apple/rtkit-internal.h` | B,F | +| [71500694f45b](https://github.com/aurora-silicon/linux/commit/71500694f45b1fd27b05c9ed030b2cc803fb0f2f) | Eryk Wieliczko | drm/asahi: share tracked VM lifetime and native query support [#400](https://github.com/aurora-silicon/linux/pull/400) | Active — MEM; `drivers/gpu/drm/asahi/g17/completion.rs` | M,D | +| [444c52644826](https://github.com/aurora-silicon/linux/commit/444c52644826e99eecf65e9f643744c9e380bca9) | Ryan Murray | soc: apple: pmp: Keep power commands after an unhandled message on other boards | Active — PMP; `drivers/soc/apple/pmp.rs` | P,B | +| [76c60f146885](https://github.com/aurora-silicon/linux/commit/76c60f1468853b4d28e3fed6e9c207c8a2f28410) | Ryan Murray | iommu/apple-dart: Map retained DARTs non-posted like every other DART [#400](https://github.com/aurora-silicon/linux/pull/400) | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [d5df65ffcf66](https://github.com/aurora-silicon/linux/commit/d5df65ffcf66e54d9f23de05635512a2cf0ad41d) | Jonathan Goyvaerts | soc: apple: rtkit: add apple_rtkit_resume_idle() | Active — RTKIT; `drivers/soc/apple/rtkit.c` | B,F | +| [f82267ce1172](https://github.com/aurora-silicon/linux/commit/f82267ce117278fb915879abbbd0f355286cecf3) | Jonathan Goyvaerts | soc: apple: rtkit: resume the AP power state before the IOP's | Active — RTKIT; `drivers/soc/apple/rtkit.c` | B,F | +| [e772e7f87659](https://github.com/aurora-silicon/linux/commit/e772e7f87659243ab7b94c2b5bb77ab485dc1128) | DjDeveloperr | iommu/apple-dart: accept mirrored pages in handed-off firmware slots [#404](https://github.com/aurora-silicon/linux/pull/404) | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [ff5c39ae1782](https://github.com/aurora-silicon/linux/commit/ff5c39ae1782cd1d826eddf48e0b08b1b2af61eb) | DjDeveloperr | iommu/apple-dart: keep stream invalidates out of handed-off slots [#404](https://github.com/aurora-silicon/linux/pull/404) | Active — DART; `drivers/iommu/apple-dart.c` | D,B | +| [c2bace5bb285](https://github.com/aurora-silicon/linux/commit/c2bace5bb28512471318d71abd5073b2bc7e2af1) | Ryan Murray | soc: apple: pmp: start endpoint 7 on every T6041 PMP [#402](https://github.com/aurora-silicon/linux/pull/402) | Active — PMP; `drivers/soc/apple/pmp.rs` | P,B | +| [2d8c0fe879e2](https://github.com/aurora-silicon/linux/commit/2d8c0fe879e26583b62655534a3aba86b1dfc80f) | Jonathan Goyvaerts | drm/asahi: mmu: make the secondary root of PublishedRoots optional [#420](https://github.com/aurora-silicon/linux/pull/420) | Active — MEM; `drivers/gpu/drm/asahi/g17/mod.rs` | M,D | +| [40507bad232c](https://github.com/aurora-silicon/linux/commit/40507bad232c7539f3c7c2540bac818a7aa90f26) | Jonathan Goyvaerts | drm/asahi: g17: support a single firmware coprocessor [#420](https://github.com/aurora-silicon/linux/pull/420) | Active — SHARED; `drivers/gpu/drm/asahi/g17/event.rs` | C,R,H | +| [750dd1a8e590](https://github.com/aurora-silicon/linux/commit/750dd1a8e5902e4e7478ce9213278b2131b63266) | Jonathan Goyvaerts | drm/asahi: g17: describe the firmware build in a variant table [#420](https://github.com/aurora-silicon/linux/pull/420) | Active translated data; MCache gated by V25G83.mcache=false — FW; `drivers/gpu/drm/asahi/g17/command.rs` | B,C,R | +| [9038ef8d328c](https://github.com/aurora-silicon/linux/commit/9038ef8d328cef14e038513ccaf55dc03869006b) | Jonathan Goyvaerts | drm/asahi: g17: take the per-SoC tables from Config [#420](https://github.com/aurora-silicon/linux/pull/420) | Active translated data — FW; `drivers/gpu/drm/asahi/g17/initdata.rs` | B,C,R | +| [24d1e5f4d8d3](https://github.com/aurora-silicon/linux/commit/24d1e5f4d8d31dd320048546cd53230a61203a9f) | Jonathan Goyvaerts | drm/asahi: g17: operating points with one voltage per cluster [#420](https://github.com/aurora-silicon/linux/pull/420) | Active — SHARED; `drivers/gpu/drm/asahi/g17/fw/initdata.rs` | C,R,H | +| [1ef423a08b3c](https://github.com/aurora-silicon/linux/commit/1ef423a08b3cbc81856afbe3c5854ac25b0893b6) | Jonathan Goyvaerts | drm/asahi: g17: register windows with several instances [#420](https://github.com/aurora-silicon/linux/pull/420) | Active — SHARED; `drivers/gpu/drm/asahi/g17/fw/initdata.rs` | C,R,H | +| [f23df96e697b](https://github.com/aurora-silicon/linux/commit/f23df96e697bc05a47029efd4fe786f900b08a83) | Jonathan Goyvaerts | drm/asahi: g17: let the variant choose the init acknowledgement [#420](https://github.com/aurora-silicon/linux/pull/420) | Active translated data — FW; `drivers/gpu/drm/asahi/g17/fw/variant.rs` | B,C,R | +| [39e990b44482](https://github.com/aurora-silicon/linux/commit/39e990b44482f46fb6bed6c3b7eb2413e0f0331a) | Jonathan Goyvaerts | drm/asahi: mmu: firmware tables in no-map reserved memory [#420](https://github.com/aurora-silicon/linux/pull/420) | Active — MEM; `drivers/gpu/drm/asahi/g17/mod.rs` | M,D | +| [54eccc50cc77](https://github.com/aurora-silicon/linux/commit/54eccc50cc770bdbe9306be4337c548f399e2eb8) | Jonathan Goyvaerts | drm/asahi: mmu: per-SoC UAT cache, TLB and handoff policy [#420](https://github.com/aurora-silicon/linux/pull/420) | Gated — UAT; `drivers/gpu/drm/asahi/mem.rs` | D | +| [abb36e8aa320](https://github.com/aurora-silicon/linux/commit/abb36e8aa3203f4cb28c1b2498d636617d01111d) | Jonathan Goyvaerts | drm/asahi: g17: 25G83 initialization layout [#420](https://github.com/aurora-silicon/linux/pull/420) | Active translated data — FW; `drivers/gpu/drm/asahi/g17/fw/initdata.rs` | B,C,R | +| [0d9efa515188](https://github.com/aurora-silicon/linux/commit/0d9efa515188d93cd775e240b20bf4ad29a258c8) | Jonathan Goyvaerts | drm/asahi: g17: render lead commands for the 25G83 firmware [#420](https://github.com/aurora-silicon/linux/pull/420) | Active translated data — FW; `drivers/gpu/drm/asahi/g17/buffer.rs` | B,C,R | +| [f8dc19862078](https://github.com/aurora-silicon/linux/commit/f8dc19862078a7ad72cd3e10fe22052a02609e76) | Jonathan Goyvaerts | drm/asahi: g17: 25G83 render and compute commands [#420](https://github.com/aurora-silicon/linux/pull/420) | Active translated data — FW; `drivers/gpu/drm/asahi/g17/buffer/target.rs` | B,C,R | +| [20e51646e036](https://github.com/aurora-silicon/linux/commit/20e51646e036432fa9498a24e5330b8d02068612) | Jonathan Goyvaerts | drm/asahi: g17: add the T6040 (M4 Pro) [#420](https://github.com/aurora-silicon/linux/pull/420) | Active translated data — FW; `drivers/gpu/drm/asahi/g17/fw/variant.rs` | B,C,R | +| [244f347d7dee](https://github.com/aurora-silicon/linux/commit/244f347d7deedb8a67eda1f0175ce001a5e7509c) | Jonathan Goyvaerts | soc: apple: rtkit: drop apple_rtkit_resume_idle() | Active — RTKIT; `drivers/soc/apple/rtkit.c` | B,F | +| [a214e242a034](https://github.com/aurora-silicon/linux/commit/a214e242a03404fc7324ff0efdc595a6b37a53c5) | Jonathan Goyvaerts | drm/asahi: g17: bounded cross-VM grace for render timeouts [#413](https://github.com/aurora-silicon/linux/pull/413) | Active — REC; `drivers/gpu/drm/asahi/g17/job.rs` | F,Q | +| [24349f4a4593](https://github.com/aurora-silicon/linux/commit/24349f4a4593fa4e02ce731f5e73a228aa27aed8) | Jonathan Goyvaerts | drm/asahi: g17: retire abandoned spared renders without failing clients [#413](https://github.com/aurora-silicon/linux/pull/413) | Active — REC; `drivers/gpu/drm/asahi/g17/completion.rs` | F,Q | +| [9af21ada7212](https://github.com/aurora-silicon/linux/commit/9af21ada7212737042705f3081d153332516a182) | Jonathan Goyvaerts | drm/asahi: g17: spare innocent renders once the cross-VM grace runs out [#413](https://github.com/aurora-silicon/linux/pull/413) | Active — SHARED; `drivers/gpu/drm/asahi/g17/runtime/backend.rs` | R,H | +| [29f38d83f6a6](https://github.com/aurora-silicon/linux/commit/29f38d83f6a6208d4ff3c4187042b3b0b476d242) | Jonathan Goyvaerts | drm/asahi: g17: keep a VM usable behind its spared render quarantine [#413](https://github.com/aurora-silicon/linux/pull/413) | Active — REC; `drivers/gpu/drm/asahi/g17/completion.rs` | F,Q | +| [c71f1177e773](https://github.com/aurora-silicon/linux/commit/c71f1177e7730db49e9a7ec9cdf150a0feeb05ab) | Jonathan Goyvaerts | drm/asahi: g17: hold the GPU powered for idle_off_delay_ms after work [#413](https://github.com/aurora-silicon/linux/pull/413) | Active — POWER; `drivers/gpu/drm/asahi/g17/mod.rs` | I,C,R,P | +| [645b4e55cf16](https://github.com/aurora-silicon/linux/commit/645b4e55cf16c1cedffba87258bdaafd5b15dcd8) | Jonathan Goyvaerts | drm/asahi: g17: replay every render group firmware dropped unexecuted [#413](https://github.com/aurora-silicon/linux/pull/413) | Active — REC; `drivers/gpu/drm/asahi/g17/completion.rs` | F,Q | +| [94e719a0cec2](https://github.com/aurora-silicon/linux/commit/94e719a0cec2a65502ce5413d2ee9a105c1cc8d9) | Ryan Murray | drm/asahi: g17: never replay a render group a recovery blamed [#418](https://github.com/aurora-silicon/linux/pull/418) | Active — REC; `drivers/gpu/drm/asahi/g17/completion.rs` | F,Q | +| [1054f6763014](https://github.com/aurora-silicon/linux/commit/1054f6763014fe4b4032a08ae7ccce721be4d385) | Ryan Murray | drm/asahi: g17: fix the render timeout head and replay documentation [#418](https://github.com/aurora-silicon/linux/pull/418) | Active — REC; `drivers/gpu/drm/asahi/g17/queue/render.rs` | F,Q | +| [45ac35fa2796](https://github.com/aurora-silicon/linux/commit/45ac35fa279642f98ea7326144716755fb3a39a2) | Ryan Murray | drm/asahi: g17: move the fw-data crash log mapping to its own file [#420](https://github.com/aurora-silicon/linux/pull/420) | Active — SHARED; `drivers/gpu/drm/asahi/g17/data_segment.rs` | C,R,H | +| [fd21bf6faed6](https://github.com/aurora-silicon/linux/commit/fd21bf6faed6f978223e61444424ee918c199838) | Ryan Murray | drm/asahi: g17: take the core-layout buffers from Config [#420](https://github.com/aurora-silicon/linux/pull/420) | Active translated data — FW; `drivers/gpu/drm/asahi/g17/fw/compute.rs` | B,C,R | +| [c0b9fbfa9fb0](https://github.com/aurora-silicon/linux/commit/c0b9fbfa9fb048d5bc67ffb9e17168b686b1962d) | Ryan Murray | drm/asahi: g17: check the voltage cells of the GPU operating points [#420](https://github.com/aurora-silicon/linux/pull/420) | Active translated data — FW; `drivers/gpu/drm/asahi/g17/initdata.rs` | B,C,R | +| [1c5a2ef5783a](https://github.com/aurora-silicon/linux/commit/1c5a2ef5783a565e6b27da871d30e4e637e1ce69) | Ryan Murray | drm/asahi: g17: accept only the two host words of a pointer block [#420](https://github.com/aurora-silicon/linux/pull/420) | Active translated data — FW; `drivers/gpu/drm/asahi/g17/fw/queue.rs` | B,C,R | +| [df6b70b85cbf](https://github.com/aurora-silicon/linux/commit/df6b70b85cbfada36604f511b9ba8da21b13eb7d) | Ryan Murray | drm/asahi: g17: pass the fragment ID to the barrier unchanged [#420](https://github.com/aurora-silicon/linux/pull/420) | Active — SHARED; `drivers/gpu/drm/asahi/g17/fw/queue.rs` | C,R,H | +| [496a59f89601](https://github.com/aurora-silicon/linux/commit/496a59f8960191dc511b92328b13ccc7e31ad1ff) | Ryan Murray | drm/asahi: g17: do not spare a timeout behind work that already failed [#418](https://github.com/aurora-silicon/linux/pull/418) | Active — REC; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | F,Q | +| [fc1a16b4722c](https://github.com/aurora-silicon/linux/commit/fc1a16b4722ce9a9eb6b77fc0af4163873cacc9e) | Ryan Murray | drm/asahi: g17: name the blocking work in the watchdog lines [#418](https://github.com/aurora-silicon/linux/pull/418) | Active — OBS; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | O,F | +| [894ec76dc892](https://github.com/aurora-silicon/linux/commit/894ec76dc89219caae27f9d6845521337b16e8bf) | Ryan Murray | drm/asahi: g17: treat a torn render sample in the timeout fallback as no proof [#418](https://github.com/aurora-silicon/linux/pull/418) | Active — REC; `drivers/gpu/drm/asahi/g17/queue/render.rs` | F,Q | +| [7d167e382f40](https://github.com/aurora-silicon/linux/commit/7d167e382f40542b25ffceb596d0982ee002b7ca) | Ryan Murray | drm/asahi: g17: fail the dropped successors of an exhausted render replay [#418](https://github.com/aurora-silicon/linux/pull/418) | Active — REC; `drivers/gpu/drm/asahi/g17/queue/render.rs` | F,Q | +| [5ee97891f61f](https://github.com/aurora-silicon/linux/commit/5ee97891f61f552c8c1d677db50725c413fb3ed7) | Ryan Murray | drm/asahi: g17: retire finished renders before attributing a render timeout [#418](https://github.com/aurora-silicon/linux/pull/418) | Active — REC; `drivers/gpu/drm/asahi/g17/runtime/backend.rs` | F,Q | +| [25f4c6c3ab18](https://github.com/aurora-silicon/linux/commit/25f4c6c3ab18f329ba2af41b102c30d53e361ae6) | Ryan Murray | thermal: apple: Describe the PMP policy KUnit test module [#417](https://github.com/aurora-silicon/linux/pull/417) | Active — PMP; `drivers/thermal/apple-pmp-policy-test.c` | P,B | +| [b9a8f3938f3d](https://github.com/aurora-silicon/linux/commit/b9a8f3938f3daf5926b7c86204113f6730aec136) | Ryan Murray | drm/asahi: g17: pass the queue count to Pool::new() in the fault admission test [#420](https://github.com/aurora-silicon/linux/pull/420) | Active — GMEM; `drivers/gpu/drm/asahi/g17/queue/admission.rs` | M,C,R | +| [bec833e0bc58](https://github.com/aurora-silicon/linux/commit/bec833e0bc581f74744265f0613832e8291db0cc) | Jonathan Goyvaerts | drm/asahi: pgtable: check the struct page of a table outside the reserved regions [#427](https://github.com/aurora-silicon/linux/pull/427) | Active — MEM; `drivers/gpu/drm/asahi/mmu.rs` | M,D | +| [a335c77de52a](https://github.com/aurora-silicon/linux/commit/a335c77de52a274029bb4e42f96d6afde5712a2f) | Jonathan Goyvaerts | drm/asahi: identify the T6040 GPU firmware by its loaded text [#428](https://github.com/aurora-silicon/linux/pull/428) | Active translated data — FW; `drivers/gpu/drm/asahi/g17/fw/variant.rs` | B,C,R | +| [d69f14f4e4fb](https://github.com/aurora-silicon/linux/commit/d69f14f4e4fbe9af21b70ee5c1b3ff9125c7a9cf) | DjDeveloperr | soc: apple: clpc: raise the T8140 fabric floor for GPU work [#440](https://github.com/aurora-silicon/linux/pull/440) | Active after port; 0d63308820ab — GPUFLOOR; `drivers/soc/apple/clpc.c` | P,C,R,H,I | +| [d1468a5f9a50](https://github.com/aurora-silicon/linux/commit/d1468a5f9a50a23cf741f56d83e6690441b90482) | Ryan | drm/asahi: gpu: drop the unused module_parameters import [#395](https://github.com/aurora-silicon/linux/pull/395) | Active — MEM; `drivers/gpu/drm/asahi/gpu.rs` | M,D | +| [26f5eb68dbce](https://github.com/aurora-silicon/linux/commit/26f5eb68dbce039de470b789d754d2ec3541c15e) | Ryan Murray | drm/asahi: admit the alternate T6040 loaded 25G83 image [#453](https://github.com/aurora-silicon/linux/pull/453) | Active translated data — FW; `drivers/gpu/drm/asahi/g17/fw/variant.rs` | B,C,R | +| [88aebfe0c656](https://github.com/aurora-silicon/linux/commit/88aebfe0c656676c530faa8f85df8d7b69a7ddb0) | DjDeveloperr | irqchip/apple-aic: Report the effective affinity of AICv2+ IRQs [#447](https://github.com/aurora-silicon/linux/pull/447) | Active — AIC; `drivers/irqchip/irq-apple-aic.c` | O,C,I | +| [eb346c1a25d8](https://github.com/aurora-silicon/linux/commit/eb346c1a25d813a1411a34fafc7d4bdee418e0d4), [42a176b951d9](https://github.com/aurora-silicon/linux/commit/42a176b951d90e746e33d27d6eb06a1773579afe) | Haoxiang Li | cpufreq: apple-soc: Fix OPP table cleanup | Active — CPU; `drivers/cpufreq/apple-soc-cpufreq.c` | C,I,P | +| [ae37f03d4163](https://github.com/aurora-silicon/linux/commit/ae37f03d4163df8143b2144e742916b91bf90654) | Ryan Murray | thermal: apple-smc-cpu: Add the J700 CPU thermal policy [#22](https://github.com/aurora-silicon/linux/pull/22) | Active — CPU; `drivers/cpufreq/apple-soc-cpufreq.c` | C,I,P | +| [583b9fa52fbf](https://github.com/aurora-silicon/linux/commit/583b9fa52fbf6a47f150736ad4a1aaa0d616ac02), [a8872354b695](https://github.com/aurora-silicon/linux/commit/a8872354b695a066ac6411791a691f26fcde55bf) | Oliver Lukschander | pmdomain: apple: pmp-report: initialize the report lock [#56](https://github.com/aurora-silicon/linux/pull/56) | Active — PMP; `drivers/pmdomain/apple/pmp-report.c` | P,B | +| [8ca5289bec3a](https://github.com/aurora-silicon/linux/commit/8ca5289bec3a2b6f3629c9df516b357778273a72) | Joshua Warren | drm/asahi: connect the snapshot pointer to the C sysfs static [#197](https://github.com/aurora-silicon/linux/pull/197) [#380](https://github.com/aurora-silicon/linux/pull/380) [#470](https://github.com/aurora-silicon/linux/pull/470) | Active; carried/refined form — MEM; `drivers/gpu/drm/asahi/gpu.rs` | M,D | +| [628bf1df4312](https://github.com/aurora-silicon/linux/commit/628bf1df43123ead994b028fd7b82a209e67aa30), [983575d7b492](https://github.com/aurora-silicon/linux/commit/983575d7b492f6824401cd21fe02278812211e27) | Ryan Murray | drm/asahi: initialize driver data before claiming destructor ownership [#171](https://github.com/aurora-silicon/linux/pull/171) [#380](https://github.com/aurora-silicon/linux/pull/380) [#470](https://github.com/aurora-silicon/linux/pull/470) | Active; carried/refined form — MEM; `drivers/gpu/drm/asahi/driver.rs` | M,B | +| [b10d60ce3916](https://github.com/aurora-silicon/linux/commit/b10d60ce3916c44dfb19b1f1b197bdca40bbac34) | Ryan Murray | drm/asahi: retain two-phase data ownership with stats registration [#380](https://github.com/aurora-silicon/linux/pull/380) [#470](https://github.com/aurora-silicon/linux/pull/470) | Active; carried/refined form — MEM; `drivers/gpu/drm/asahi/driver.rs` | M,B | +| [9266541b8d35](https://github.com/aurora-silicon/linux/commit/9266541b8d35c1cd7146eec9a9b31b39fb7f29cc) | Ryan Murray | treewide: describe hardware behaviour in comments [#233](https://github.com/aurora-silicon/linux/pull/233) [#380](https://github.com/aurora-silicon/linux/pull/380) [#470](https://github.com/aurora-silicon/linux/pull/470) | Active; carried/refined form — MBOX; `drivers/gpu/drm/asahi/g17/buffer.rs` | B,O | +| [8533f072dc30](https://github.com/aurora-silicon/linux/commit/8533f072dc30e076206631f68f41fb4f8d7512a7) | Janne Grunau | iommu: apple-dart: Support specifying the DMA aperture in the DT | Active; carried/refined form — DART; `drivers/iommu/apple-dart.c` | D,B | +| [a9e24ae824ec](https://github.com/aurora-silicon/linux/commit/a9e24ae824ec205cf90f3545b0125631adf1326a) | Ryan Murray | merge: retain the 7.3 implementations where older ports duplicate them [#470](https://github.com/aurora-silicon/linux/pull/470) | Active; carried/refined form — PMGR; `drivers/bluetooth/hci_bcm4377.c` | B,F | +| [00248157ec1f](https://github.com/aurora-silicon/linux/commit/00248157ec1fc4e6369cd26cb8304bb56df7415d), [b2258cfadd69](https://github.com/aurora-silicon/linux/commit/b2258cfadd697cbe10c1e29b54f512c8f297bbe6) | Joshua Warren | soc: apple: mailbox: Make the send-empty IRQ optional (poll TX) | Active — MBOX; `drivers/soc/apple/mailbox.c` | B,O | +| [00e21c12c05a](https://github.com/aurora-silicon/linux/commit/00e21c12c05af1d540a2dc256a8cea3d17d63edc) | Lyude Paul | rust: drm: gem: Add export() callback | Active — DRM; `drivers/gpu/drm/nova/gem.rs` | C,H,M,F | +| [03329eff23bd](https://github.com/aurora-silicon/linux/commit/03329eff23bd3a72bdcfa09a3b98d0ac15107e6e), [35b057e57222](https://github.com/aurora-silicon/linux/commit/35b057e572224c39f79367df9e7c464f1822d15a), [e68c60f81285](https://github.com/aurora-silicon/linux/commit/e68c60f81285248fb50578f268343b06a6452a67) | DjDeveloperr | rust: drm: gpuvm: add non-blocking private mapping traversal [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — DRM; `rust/kernel/drm/gpuvm.rs` | C,H,M,F | +| [033943b2b6ed](https://github.com/aurora-silicon/linux/commit/033943b2b6edb176aaebffa917e2273c3bf8f625) | Ryan Murray | drm: apple: parser: cap the number of timing modes [#345](https://github.com/aurora-silicon/linux/pull/345) | Active; carried/refined form — DISPLAY; `drivers/gpu/drm/apple/parser.c` | B | +| [08e4dd7596c9](https://github.com/aurora-silicon/linux/commit/08e4dd7596c91cdb5b761a0d272e40d75320306b), [29db1d8ab517](https://github.com/aurora-silicon/linux/commit/29db1d8ab517933c2e0114fb3c6bf9beda5417aa), [437f51c11728](https://github.com/aurora-silicon/linux/commit/437f51c11728f3183b0c17af6264547e44a60c26) | Ryan Murray | soc: apple: synchronize the ASCWrap mailbox provider with j700 [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — MBOX; `drivers/soc/apple/mailbox.c` | B,O | +| [0db024825eb1](https://github.com/aurora-silicon/linux/commit/0db024825eb15d5ffd51ae6bb6b71ea4899ff3c2) | Ryan Murray | rust: drm: update shmem KUnit fixtures for the 7.2 file API | Active — DRM; `rust/kernel/drm/gem/shmem.rs` | C,H,M,F | +| [17f9a8193f55](https://github.com/aurora-silicon/linux/commit/17f9a8193f553f12afb69a14c849e12889842e8b) | Ryan Murray | rust: drm: gem: shmem: leave an imported object's SG table to the attachment | Active — DRM; `rust/kernel/drm/gem/shmem.rs` | C,H,M,F | +| [1f6c1fbb7057](https://github.com/aurora-silicon/linux/commit/1f6c1fbb7057db9206dce7187cfd3ec73233cd67) | Ryan Murray | drm/apple: dcp: Release the fixed display route with the device [#345](https://github.com/aurora-silicon/linux/pull/345) | Active; carried/refined form — DISPLAY; `drivers/gpu/drm/apple/dcp.c` | B | +| [29a2e2acec42](https://github.com/aurora-silicon/linux/commit/29a2e2acec42f8361d72bfd9506e3a0527afaf40) | DjDeveloperr | sched/core: add scoped utilization floors for kernel waiters [#171](https://github.com/aurora-silicon/linux/pull/171) | Active; carried/refined form — DRM | C,H,M,F | +| [29a913a5c222](https://github.com/aurora-silicon/linux/commit/29a913a5c222f2bec2ecae62a4dc4af223e5962d) | Jonathan Goyvaerts | soc: apple: mailbox: support the T6040 ASCWrap v6 [#420](https://github.com/aurora-silicon/linux/pull/420) | Active — MBOX; `drivers/soc/apple/mailbox.c` | B,O | +| [2b59aed7191c](https://github.com/aurora-silicon/linux/commit/2b59aed7191cccf0f6239e50e9a165f49b649529) | Ryan Murray | drm/asahi: avoid fence locks when pruning G17 frontiers [#201](https://github.com/aurora-silicon/linux/pull/201) | Active; carried/refined form — SHARED; `drivers/gpu/drm/asahi/g17/fence.rs` | C,R,H | +| [2dc22a191a5c](https://github.com/aurora-silicon/linux/commit/2dc22a191a5cf3109fb18dcfda0fec94bd4df18b), [a45a44cc6e2b](https://github.com/aurora-silicon/linux/commit/a45a44cc6e2b6bbdb189debda045a2174d07e039) | Ryan Murray | rust: drm: gem: shmem: let drivers allocate the objects the core creates | Active — DRM; `rust/kernel/drm/gem/mod.rs` | C,H,M,F | +| [32418c60d4cd](https://github.com/aurora-silicon/linux/commit/32418c60d4cdd6b37e2e9c7e88c7e299d473cf19) | Ryan Murray | drm/apple: dcp: Do not hold the mailbox of an unbound DCP for sleep [#345](https://github.com/aurora-silicon/linux/pull/345) | Active; carried/refined form — DISPLAY; `drivers/gpu/drm/apple/dcp.c` | B | +| [351fc1af825c](https://github.com/aurora-silicon/linux/commit/351fc1af825c49264de7e171a983d2c0c83f1601) | Ryan Murray | dt-bindings: power: apple,pmc: describe the DCS limit [#219](https://github.com/aurora-silicon/linux/pull/219) | Gated — DCS | P | +| [3b9a0383f589](https://github.com/aurora-silicon/linux/commit/3b9a0383f589ebaaa5bcd5c3242e294d6051f0e0) | Janne Grunau | FIXME: drm/gpuvm: Add DRM_GPUVA_REPEAT flag and logic | Active — DRM; `rust/kernel/drm/gpuvm/sm_ops.rs` | C,H,M,F | +| [3c102dba66df](https://github.com/aurora-silicon/linux/commit/3c102dba66df1119625c11c80dca301fb308563c) | Asahi Lina | rust: drm: mm: Add DRM MM Range Allocator abstraction | Active — DRM; `rust/kernel/drm/mm.rs` | C,H,M,F | +| [3d0c9c889919](https://github.com/aurora-silicon/linux/commit/3d0c9c8899198a8ad6d0d0241283ba04d2286fd7) | Ryan Murray | drm: apple: parser: bound the timing catalog allocation [#345](https://github.com/aurora-silicon/linux/pull/345) | Active; carried/refined form — DISPLAY; `drivers/gpu/drm/apple/parser.c` | B | +| [3d3fe96509d1](https://github.com/aurora-silicon/linux/commit/3d3fe96509d102770f72ce5f7829baa3c0f7d1ce), [df2e3cd3b778](https://github.com/aurora-silicon/linux/commit/df2e3cd3b77882ec523b42184d05596e60094072) | Ryan Murray | rust: soc: apple: mailbox: Fix receiver teardown and expose the device | Active — MBOX; `rust/kernel/soc/apple/mailbox.rs` | B,O | +| [3fd71b22d86c](https://github.com/aurora-silicon/linux/commit/3fd71b22d86c3e2397ed1da8a15123053d46a423), [536484539477](https://github.com/aurora-silicon/linux/commit/53648453947771b7ca24a61a585bffdbe1853242) | Ryan Murray | soc: apple: mailbox: Add T8140 ASC and AOP setup mailboxes | Active — MBOX; `drivers/soc/apple/mailbox.c` | B,O | +| [43fc98be783b](https://github.com/aurora-silicon/linux/commit/43fc98be783b0e9833242c1d8d111b49366b134b), [7c99d8debcc5](https://github.com/aurora-silicon/linux/commit/7c99d8debcc54ebf3f76ede14ced9b845db18215), [b0cc4eeb554a](https://github.com/aurora-silicon/linux/commit/b0cc4eeb554a74c2db220e35ceb14955142a79ed) | DjDeveloperr | drm/syncobj: apply expiring capacity hints during blocking waits [#171](https://github.com/aurora-silicon/linux/pull/171) | Active — DRM; `drivers/gpu/drm/drm_syncobj.c` | C,H,M,F | +| [4a577fcddddb](https://github.com/aurora-silicon/linux/commit/4a577fcddddbefec7d13bfd4ae3503893602d29b) | DjDeveloperr | rust: expose the DRM duplicate-reservation flag used by GPUVM [#171](https://github.com/aurora-silicon/linux/pull/171) | Active; carried/refined form — DRM | B,M | +| [4aa16bb5021e](https://github.com/aurora-silicon/linux/commit/4aa16bb5021e427507e15a9a48848d0484a4f3d6) | Eryk Wieliczko | rust: expose fence and DRM lifecycle helpers for native GPU backends | Active — DRM; `rust/kernel/dma_fence.rs` | C,H,M,F | +| [4f7a3272df96](https://github.com/aurora-silicon/linux/commit/4f7a3272df9627d98bc11a7da0c8189ca4aba196) | DjDeveloperr | drm/shmem-helper: populate revocable CPU mappings one page at a time [#171](https://github.com/aurora-silicon/linux/pull/171) | Active; carried/refined form — DRM; `drivers/gpu/drm/drm_gem_shmem_helper.c` | C,H,M,F | +| [5443fc6aeb7a](https://github.com/aurora-silicon/linux/commit/5443fc6aeb7a0fc574805b8b0b1513bbd02dcab4) | Ryan Murray | soc: apple: mailbox: only include reachable KUnit tests | Active — MBOX; `drivers/soc/apple/mailbox.c` | B,O | +| [5611637f6851](https://github.com/aurora-silicon/linux/commit/5611637f685115048c64231e7ac729f23ab20001), [5eff5323da02](https://github.com/aurora-silicon/linux/commit/5eff5323da0282c8fb9adfc57acdc9217e6d7995) | Ryan Murray | soc: apple: mailbox: drain only a present send-empty IRQ | Active — MBOX; `drivers/soc/apple/mailbox.c` | B,O | +| [56920e3e6752](https://github.com/aurora-silicon/linux/commit/56920e3e675230ced9a2ccab81bb7e9aa9c2ccf0) | Janne Grunau | rust: drm: gpuvm: Add sm_can_merge_flags | Active — DRM; `rust/kernel/drm/gpuvm/mod.rs` | C,H,M,F | +| [6154ed6d0f41](https://github.com/aurora-silicon/linux/commit/6154ed6d0f410dd69cbb3e218981b6dc549ac5df) | Janne Grunau | rust: drm: file: Use Registered device in DriverFile::open trait | Active — DRM; `drivers/gpu/drm/nova/file.rs` | C,H,M,F | +| [63d3b0f41939](https://github.com/aurora-silicon/linux/commit/63d3b0f419399d027c865d4ccffa12e3617a73b8) | Ryan Murray | drm: apple: afk: factor the receive bounds check into a helper [#345](https://github.com/aurora-silicon/linux/pull/345) | Active; carried/refined form — DISPLAY; `drivers/gpu/drm/apple/afk.c` | B | +| [6740c25f03c1](https://github.com/aurora-silicon/linux/commit/6740c25f03c1f552b0bdc0ad5b02926d96dd99ad) | Ryan Murray | drm/asahi: Log G17 firmware recovery causes before acknowledgement [#200](https://github.com/aurora-silicon/linux/pull/200) | Active; carried/refined form — OBS; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | O,F | +| [6803b9328c48](https://github.com/aurora-silicon/linux/commit/6803b9328c483c492e0a26fadf0a7cd03a5fe7cc) | Janne Grunau | rust: drm: gpuvm: GpuVa flags and REPEAT logic | Active — DRM; `rust/kernel/drm/gpuvm/sm_ops.rs` | C,H,M,F | +| [6a127c6ff62c](https://github.com/aurora-silicon/linux/commit/6a127c6ff62cde78267225a7094cda2771504c28) | Janne Grunau | rust: drm: file: Add as_raw() | Active — DRM; `drivers/gpu/drm/nova/file.rs` | C,H,M,F | +| [6db05cf4b231](https://github.com/aurora-silicon/linux/commit/6db05cf4b231f020020f31a479bd45ccff83520f) | DjDeveloperr | drm/shmem-helper: index fault pages by object offset [#171](https://github.com/aurora-silicon/linux/pull/171) | Active; carried/refined form — DRM; `drivers/gpu/drm/drm_gem_shmem_helper.c` | C,H,M,F | +| [70d9b6f64ee7](https://github.com/aurora-silicon/linux/commit/70d9b6f64ee76e9da96b752ee3933e2d0a66af0b) | Ryan Murray | drm: apple: free property dictionaries the connector does not keep [#345](https://github.com/aurora-silicon/linux/pull/345) | Active; carried/refined form — DISPLAY; `drivers/gpu/drm/apple/connector.c` | B | +| [72c42b6ddae6](https://github.com/aurora-silicon/linux/commit/72c42b6ddae68c8c58c5f5d720fda5079ae24302) | Janne Grunau | rust: drm: driver: Add feature flags used by asahi | Active — DRM; `rust/kernel/drm/device.rs` | C,H,M,F | +| [765577e012da](https://github.com/aurora-silicon/linux/commit/765577e012da1ee83e6eb56029bdbb9f1f799dce) | Ryan Murray | drm/apple: dptxep: Remove call counters and tidy tunnel comments [#345](https://github.com/aurora-silicon/linux/pull/345) | Active; carried/refined form — DISPLAY; `drivers/gpu/drm/apple/dptxep.c` | B | +| [77a9ab842a02](https://github.com/aurora-silicon/linux/commit/77a9ab842a02e980873a2298caa4dfe8d56faaff) | Ryan Murray | rust: drm: gpuvm: do not leak the VM when exec_lock() fails | Active — DRM; `rust/kernel/drm/gpuvm/mod.rs` | C,H,M,F | +| [7a155e383d2d](https://github.com/aurora-silicon/linux/commit/7a155e383d2d8e70fd76075ffb7427f4c7a3c326), [c98d2b872a2b](https://github.com/aurora-silicon/linux/commit/c98d2b872a2b02241ce4a34d9fe6841cd32b38c3) | Ryan Murray | soc: apple: mailbox: Keep the send-empty IRQ enable balanced | Active — MBOX; `drivers/soc/apple/mailbox.c` | B,O | +| [7c57598ac57c](https://github.com/aurora-silicon/linux/commit/7c57598ac57cb7804a9f2e89d0975e2005985b96) | Janne Grunau | rust: drm: gem: Support locking gpuva.lock | Active — DRM; `rust/kernel/drm/gem/mod.rs` | C,H,M,F | +| [7dce231ad73d](https://github.com/aurora-silicon/linux/commit/7dce231ad73d98e102b4b9e0b362cbab21750854) | DjDeveloperr | drm/syncobj: skip wait hint lookup for ineligible waits [#171](https://github.com/aurora-silicon/linux/pull/171) | Active; carried/refined form — DRM; `drivers/gpu/drm/drm_syncobj.c` | C,H,M,F | +| [7ed160b9b5f2](https://github.com/aurora-silicon/linux/commit/7ed160b9b5f28df712e98088d4368188b2e28072), [c7df21582810](https://github.com/aurora-silicon/linux/commit/c7df2158281045e6fa9bfa6b7c170910f1089d2e) | Ryan Murray | soc: apple: mailbox: Tidy T8140 comments and resume error logging [#314](https://github.com/aurora-silicon/linux/pull/314) | Active — OBS; `drivers/soc/apple/mailbox.c` | O,F | +| [887533ef3bd8](https://github.com/aurora-silicon/linux/commit/887533ef3bd88de5ec05f93be23d2fefd3570cb5) | Ryan Murray | rust: drm: gem: shmem: wait for backing release instead of failing a lease | Active — DRM; `rust/kernel/drm/gem/shmem.rs` | C,H,M,F | +| [8cdeb29b542a](https://github.com/aurora-silicon/linux/commit/8cdeb29b542af3cd5ffb3b535330ad4fd67b97a7) | Janne Grunau | HACK: rust: drm: gem: shmem: Make VMap.ptr pub | Active — DRM; `rust/kernel/drm/gem/shmem.rs` | C,H,M,F | +| [9142d428cb69](https://github.com/aurora-silicon/linux/commit/9142d428cb6956aeb36ffebabe42e1a5c2396c2c) | Ryan Murray | drm: apple: afk: bound received queue entries without 32-bit wrap [#345](https://github.com/aurora-silicon/linux/pull/345) | Active; carried/refined form — DISPLAY; `drivers/gpu/drm/apple/afk.c` | B | +| [92d02170df1e](https://github.com/aurora-silicon/linux/commit/92d02170df1ecf2273d13ba63c0cedbb891996d3) | Jonathan Goyvaerts | arm64: dts: apple: t6040: Add the GPU [#420](https://github.com/aurora-silicon/linux/pull/420) | Active translated data — FW | B,P | +| [9429b186dc3e](https://github.com/aurora-silicon/linux/commit/9429b186dc3edb6658bcdd02e451a5cb1c5f0844) | Ryan Murray | rust: drm: report client memory in fdinfo | Active — DRM; `rust/kernel/drm/device.rs` | C,H,M,F | +| [9523159a7288](https://github.com/aurora-silicon/linux/commit/9523159a72886c5169e7bfd566de9c0aa545ba85) | Ryan Murray | soc: apple: clpc: drop the syscon select and the GPU from Kconfig [#345](https://github.com/aurora-silicon/linux/pull/345) | Active; carried/refined form — CLPC; `drivers/soc/apple/Kconfig` | P,C,R,I | +| [a3828ea8a63d](https://github.com/aurora-silicon/linux/commit/a3828ea8a63d2488f2080dd1f4f6ce8ebd133623) | Asahi Lina | rust: drm: syncobj: Add DRM Sync Object abstraction | Active — DRM; `rust/kernel/drm/mod.rs` | B,M | +| [a6f8817ebbf6](https://github.com/aurora-silicon/linux/commit/a6f8817ebbf6c420aee66897f717ab4e94a4a717) | Ryan Murray | drm/asahi: Reuse fully retired guilty G17 compute queues [#200](https://github.com/aurora-silicon/linux/pull/200) | Active; carried/refined form — REC; `drivers/gpu/drm/asahi/g17/queue/compute.rs` | F,Q | +| [b147e1f460b5](https://github.com/aurora-silicon/linux/commit/b147e1f460b56e398bc085b2457b1ba163cf4505) | Jonathan Goyvaerts | arm64: dts: apple: t6040-j616s: Add the gpu alias [#420](https://github.com/aurora-silicon/linux/pull/420) | Active translated data — FW | B,P | +| [b1f7d09dcf33](https://github.com/aurora-silicon/linux/commit/b1f7d09dcf33e3bb06e81d846783b698d604be5b) | Ryan Murray | drm/apple: dcp: Initialise the start completion before checking RTKit [#345](https://github.com/aurora-silicon/linux/pull/345) | Active; carried/refined form — DISPLAY; `drivers/gpu/drm/apple/dcp.c` | B | +| [b965be95b07f](https://github.com/aurora-silicon/linux/commit/b965be95b07f46ef0be02f47a3a02017d912e4cc) | Asahi Lina | rust: drm: gpuvm: Add part of the asahi downstream gpuvm abstractions | Active — DRM; `rust/kernel/drm/gpuvm/mod.rs` | B,M | +| [ba69d4dde975](https://github.com/aurora-silicon/linux/commit/ba69d4dde9751976c323055ef683d76febf82a2b) | Ryan Murray | drm: apple: afk: validate the receive write pointer and bound a drain [#345](https://github.com/aurora-silicon/linux/pull/345) | Active; carried/refined form — DISPLAY; `drivers/gpu/drm/apple/afk.c` | B | +| [c0e5730f8058](https://github.com/aurora-silicon/linux/commit/c0e5730f8058f14c8fdeb5cd96213e57822f7bbb) | Ryan Murray | drm/apple: dcp: Tidy RTKit start-up logging [#345](https://github.com/aurora-silicon/linux/pull/345) | Active; carried/refined form — DISPLAY; `drivers/gpu/drm/apple/dcp.c` | B | +| [c4722f38cdcc](https://github.com/aurora-silicon/linux/commit/c4722f38cdccca6dbbbc446dfa2d715ab96e9554) | Ryan Murray | arm64: dts: apple: t6040: Do not claim the GPU's PMP report entry at boot [#402](https://github.com/aurora-silicon/linux/pull/402) | Active — PMP | P,B | +| [cd71c1e7ff90](https://github.com/aurora-silicon/linux/commit/cd71c1e7ff901c3c35492ebe5b8bebb0702e5480) | Jonathan Goyvaerts | arm64: dts: apple: t6040-j616s: describe the GPU firmware text segment [#428](https://github.com/aurora-silicon/linux/pull/428) | Active translated data — FW | B,P | +| [d50642d23a7e](https://github.com/aurora-silicon/linux/commit/d50642d23a7edb9ace6837d6471981e84d0d4ef6) | Ryan Murray | iommu: apple-dart: Fix the KUnit test after deferred root publication [#314](https://github.com/aurora-silicon/linux/pull/314) | Active; carried/refined form — DART; `drivers/iommu/apple-dart-test.c` | D,B | +| [d6799a78db13](https://github.com/aurora-silicon/linux/commit/d6799a78db13ad06a2f26b66aad97c7e7626714d) | Ryan Murray | drm/apple: dcp: Initialise work items at probe [#345](https://github.com/aurora-silicon/linux/pull/345) | Active; carried/refined form — DISPLAY; `drivers/gpu/drm/apple/dcp.c` | B | +| [d8b45282c1e7](https://github.com/aurora-silicon/linux/commit/d8b45282c1e70cb4b947d2aadfb2636fd4ee763a) | Lyude Paul | rust: drm: gem: Add BaseObject::prime_export() | Active — DRM; `rust/kernel/drm/gem/mod.rs` | C,H,M,F | +| [defdcb801774](https://github.com/aurora-silicon/linux/commit/defdcb8017744fe0aa57061ed304e793935b52bc) | Ryan Murray | soc: apple: build the CLPC floors by default [#219](https://github.com/aurora-silicon/linux/pull/219) | Active; carried/refined form — CLPC; `drivers/soc/apple/Kconfig` | P,C,R,I | +| [e1468f83c1da](https://github.com/aurora-silicon/linux/commit/e1468f83c1daf236b69c6868a3a05a9d6f994f9b) | Janne Grunau | rust: drm: gem: Add missing trailing '//' for kernel vertical style | Active — DRM; `rust/kernel/drm/gem/mod.rs` | C,H,M,F | +| [e6d369606edf](https://github.com/aurora-silicon/linux/commit/e6d369606edff3c24dc3b1f5dda036cc77a8887c) | Ryan Murray | drm/shmem: revoke CPU PFNs before releasing purge backing [#171](https://github.com/aurora-silicon/linux/pull/171) | Active; carried/refined form — DRM; `drivers/gpu/drm/drm_gem_shmem_helper.c` | C,H,M,F | +| [f0a8afc04957](https://github.com/aurora-silicon/linux/commit/f0a8afc0495743e502fc860fa2bc0fc96e95b22a) | Sami Tolvanen | rust: drm: add a registration guard for the Normal device context | Active — DRM; `rust/kernel/drm/device.rs` | C,H,M,F | +| [fd2a0321db11](https://github.com/aurora-silicon/linux/commit/fd2a0321db11572ac201663697c83c9330ef65a3) | DjDeveloperr | driver core: track devres node membership for direct removal [#390](https://github.com/aurora-silicon/linux/pull/390) | Active — DRM; `drivers/base/base.h` | B,M | + +### N/A, superseded, other-backend and imported-neighbor records + +These scoped candidates are included to make exclusions reviewable. A shared +path query or an omnibus PR is not evidence of a Neo performance improvement. + +| Commits / encountered aliases | Author | Improvement / record | T6040 after ports — evidence key and changed source | Measurement | +| --- | --- | --- | --- | --- | +| [4f772d2c2d6c](https://github.com/aurora-silicon/linux/commit/4f772d2c2d6cb336d4df08ae6c554b8387510c20) | Janne Grunau | drm/asahi: initdata: Fold GlobalsSub struct into Globals | Not applicable — LEGACY; `drivers/gpu/drm/asahi/fw/initdata.rs` | V | +| [532ac90e08af](https://github.com/aurora-silicon/linux/commit/532ac90e08affc3bbcf59e273e00f1573b7a05d0) | Janne Grunau | drm/asahi: Avoid variable/field ref shadowing in pin-init | Not applicable — LEGACY; `drivers/gpu/drm/asahi/initdata.rs` | V | +| [253d85ab9854](https://github.com/aurora-silicon/linux/commit/253d85ab98543f838ecc6be70edb7a43d966d505) | Sasha Finkelstein | drm/asahi: starlight-debug | Not applicable — LEGACY; `drivers/gpu/drm/asahi/gpu.rs` | V | +| [83526ea43d4f](https://github.com/aurora-silicon/linux/commit/83526ea43d4fffdb10a3dff36a37a587ffaa6c14) | Janne Grunau | drm/asahi: workqueue: Drop completed work items | Not applicable — LEGACY; `drivers/gpu/drm/asahi/workqueue.rs` | V | +| [b1e32124e9e9](https://github.com/aurora-silicon/linux/commit/b1e32124e9e94ebd3e1345eb54f687fcfbfee509) | Janne Grunau | drm/asahi: rustfmt | Not applicable — VERIFY; `drivers/gpu/drm/asahi/mmu.rs` | V | +| [8508e5105997](https://github.com/aurora-silicon/linux/commit/8508e5105997a43be75e8a3ffe0c3d8ddc00155c), [e092d51bc9d7](https://github.com/aurora-silicon/linux/commit/e092d51bc9d773f7058fe578515c5f915c0adf2c) | Yureka Lilian | pmdomain: add pmp-report offsets for t6030,t8122 | Not applicable — G15; `drivers/pmdomain/apple/pmp-report.c` | V | +| [357978884c89](https://github.com/aurora-silicon/linux/commit/357978884c895e5fc35af84fed8adf0583bc3353), [a89f7da611ad](https://github.com/aurora-silicon/linux/commit/a89f7da611ade4b9d5f40dd45fe5b41d059e3399) | Yureka Lilian | arm64: dts: apple: add minimal MacBook Neo device tree | Not applicable — PERIPH | V | +| [dfe1431f40cf](https://github.com/aurora-silicon/linux/commit/dfe1431f40cf15ec4aebd1864b4c69707b3c43c2), [b796a047ec25](https://github.com/aurora-silicon/linux/commit/b796a047ec253074e2cd6b5f38a8f19949b50271), [9359ea48b718](https://github.com/aurora-silicon/linux/commit/9359ea48b7184cdee39b7c76b5f0d4a923807cf3) | Ryan Murray | arm64: dts: apple: Add T8140/J700 device trees with audio and camera | Not applicable — PERIPH | V | +| [0d31fa4418ec](https://github.com/aurora-silicon/linux/commit/0d31fa4418ec7c18ab984d6cfb0c4d10b80a0af1), [bffb1ed450d5](https://github.com/aurora-silicon/linux/commit/bffb1ed450d58d0c19f9e312c8b0c2314b9f1c53), [d575cca893bc](https://github.com/aurora-silicon/linux/commit/d575cca893bce9bedb5375be6bb8c209991faec6) | Ryan Murray | arm64: dts: apple: t8140: Add SPMI controller and PMIC NVMEM cells | Not applicable — PERIPH | V | +| [780ab337fd92](https://github.com/aurora-silicon/linux/commit/780ab337fd92e4511f4e4ee698820ae15d007cbc), [368b5343b9f7](https://github.com/aurora-silicon/linux/commit/368b5343b9f7c69683ee5a0c5239a6f96e8177fd), [c3874394cec6](https://github.com/aurora-silicon/linux/commit/c3874394cec613afc6b5c8615eae5cdd013e558c) | Ryan Murray | arm64: dts: apple: t8140: Add SMC node | Not applicable — PERIPH | V | +| [53296a0f9b6e](https://github.com/aurora-silicon/linux/commit/53296a0f9b6eae07ebce8a55c8b16309d87b2656), [985ee0213ef3](https://github.com/aurora-silicon/linux/commit/985ee0213ef3154c4d8c9763157f9620a7ce0243), [b8850ad18ff8](https://github.com/aurora-silicon/linux/commit/b8850ad18ff8d0a489c810fdafece6b8349f8694) | Ryan Murray | arm64: dts: apple: t8140: Add ANS2 NVMe controller | Not applicable — PERIPH | V | +| [4767bc0b221c](https://github.com/aurora-silicon/linux/commit/4767bc0b221cce5fb0c20dbacffe6f6bca7bc4a0), [5b9145de86c1](https://github.com/aurora-silicon/linux/commit/5b9145de86c13364ed3b9d465334674e7dc87083), [0b9461d4be5a](https://github.com/aurora-silicon/linux/commit/0b9461d4be5a77c0c74138900b8b92a7e193f8bb) | Ryan Murray | arm64: dts: apple: t8140-j700: Name the ALS calibration file | Not applicable — PERIPH | V | +| [2b45c6011879](https://github.com/aurora-silicon/linux/commit/2b45c601187962fca0b9289fdb1408e36ef5f838), [613b821cb4bb](https://github.com/aurora-silicon/linux/commit/613b821cb4bb48ccb999f75c4d5815f094f095be), [aefc56975b57](https://github.com/aurora-silicon/linux/commit/aefc56975b57d0e07a4a3e92d7e7af85f0a45cb4) | Ryan Murray | arm64: dts: apple: t8140-pmgr: Add ATC0 and nub SPMI A0 power domains | Not applicable — PERIPH | V | +| [e5992bd6f02c](https://github.com/aurora-silicon/linux/commit/e5992bd6f02c42e2e7404be54a41d8acaf685bb4), [dc6bc1f1613f](https://github.com/aurora-silicon/linux/commit/dc6bc1f1613f7e836b9514f40c07cb424622fca1), [0e335bcc4a79](https://github.com/aurora-silicon/linux/commit/0e335bcc4a799cc546ff5a6e9d5cdde644a53eea) | Ryan Murray | arm64: dts: apple: t8140: Add the MTP coprocessor and DockChannel HID nodes | Not applicable — PERIPH | V | +| [601fe7359e94](https://github.com/aurora-silicon/linux/commit/601fe7359e94f9f527487f2d1725cbfad8ea1ff2), [2d6ae0d0f507](https://github.com/aurora-silicon/linux/commit/2d6ae0d0f5073b6e29cb970926855de13d24a770), [895980a58c46](https://github.com/aurora-silicon/linux/commit/895980a58c46a6a1ec2a4e2bb2d5b9e815d4b349) | Ryan Murray | arm64: dts: apple: t8140: Add the USB-C controller, ATC PHY and SPMI A0 bus | Not applicable — PERIPH | V | +| [addbb17c2a81](https://github.com/aurora-silicon/linux/commit/addbb17c2a810bcaa1d68f6f9bc8e7fb401a508a), [7d1387799cba](https://github.com/aurora-silicon/linux/commit/7d1387799cba4442ba7ea99a95a962535cf26fc2), [7ded6b918bd3](https://github.com/aurora-silicon/linux/commit/7ded6b918bd3436fa2dc81b8b65f336bbe66ae85) | Ryan Murray | arm64: dts: apple: t8140-j700: Enable the keyboard and trackpad | Not applicable — PERIPH | V | +| [70f40a78d8ea](https://github.com/aurora-silicon/linux/commit/70f40a78d8ea8db8b300d3d084b4228b3e172f19), [c94e793fea2a](https://github.com/aurora-silicon/linux/commit/c94e793fea2ad398c3aade3d3649ebc377189ec2), [b8f170b9347b](https://github.com/aurora-silicon/linux/commit/b8f170b9347ba943a11a30f6642a9532758965e4) | Ryan Murray | arm64: dts: apple: t8140-j700: Enable the USB-C ports | Not applicable — PERIPH | V | +| [ea013658bf93](https://github.com/aurora-silicon/linux/commit/ea013658bf93d944617d01034cd7a5a3ece105a8), [e5a7b83611cc](https://github.com/aurora-silicon/linux/commit/e5a7b83611ccc434923524ea7bfb27f4e9c2a2ec), [09b788437876](https://github.com/aurora-silicon/linux/commit/09b788437876d50bf0d13d0d8cf7bfe9487a934b) | Ryan Murray | arm64: dts: apple: t8140-j700: Enable the USB DARTs | Not applicable — PERIPH | V | +| [a97766963d24](https://github.com/aurora-silicon/linux/commit/a97766963d247d039cd76be83fd896b895e1ad2b), [e499013c56ad](https://github.com/aurora-silicon/linux/commit/e499013c56ad475dd45866c64e28dd33f95235c3), [d880e057bc1e](https://github.com/aurora-silicon/linux/commit/d880e057bc1e250d54a0b7dc2db23928f802a1d0) | Ryan Murray | arm64: dts: apple: t8140: Add the SMC RTC | Not applicable — PERIPH | V | +| [50e24690d43e](https://github.com/aurora-silicon/linux/commit/50e24690d43e525c1b2b217aa828a18e4c6503b9), [21ab6edf3da3](https://github.com/aurora-silicon/linux/commit/21ab6edf3da3111ed771155a9dbd2c83118cd0dc), [e90d32748ea7](https://github.com/aurora-silicon/linux/commit/e90d32748ea7857d242afc714365949e9ad86a3b) | Ryan Murray | arm64: dts: apple: t8140-j700: Describe the remaining SMC temperatures | Not applicable — PERIPH | V | +| [2cb796ddf423](https://github.com/aurora-silicon/linux/commit/2cb796ddf423b6d76e9d2ab1231f0b48ddbc2614), [e8d16b77d690](https://github.com/aurora-silicon/linux/commit/e8d16b77d6901f18a468d3d1b636990aa97f89cd), [23b613dfe8e7](https://github.com/aurora-silicon/linux/commit/23b613dfe8e757e1b599dc7cdfb7f9717687dc21) | Ryan Murray | arm64: dts: apple: t8140-j700: Describe the SMC current, power and voltage rails | Not applicable — PERIPH | V | +| [0cd1a28f2113](https://github.com/aurora-silicon/linux/commit/0cd1a28f2113c72a53052fb2733c2aced2f54476), [c2845ec53a91](https://github.com/aurora-silicon/linux/commit/c2845ec53a915dcadc4e68bbd59f97ca0764ddd6) | DjDeveloperr | iommu: apple-dart: support tunneled PCIe-C DMA aliases | Not applicable — TUNNEL; `drivers/iommu/Kconfig` | V | +| [6ff009379755](https://github.com/aurora-silicon/linux/commit/6ff00937975526780a22aa763211ba612f042997), [ee60f6cdcff8](https://github.com/aurora-silicon/linux/commit/ee60f6cdcff8561bff5ec48b10ba2a536a23ed9f) | DjDeveloperr | iommu: apple-dart: support retained PCIe-C tunnel domains | Not applicable — TUNNEL; `drivers/iommu/apple-dart.c` | V | +| [05ae2b2dfc28](https://github.com/aurora-silicon/linux/commit/05ae2b2dfc2853d7ae0f797d6299661e957116f3), [5987e8a08203](https://github.com/aurora-silicon/linux/commit/5987e8a08203974fe539aed3ce2de8ad5a3990a7) | Ryan Murray | arm64: dts: apple: t8140: Describe observed behaviour, not other implementations | Not applicable — VERIFY | V | +| [61f979fc1825](https://github.com/aurora-silicon/linux/commit/61f979fc18256a03db72933dde7f356a44f31eb2), [bf3cb50f012f](https://github.com/aurora-silicon/linux/commit/bf3cb50f012f9ecaeb465e861d8e896e580a69a0), [4fa6c2b053a6](https://github.com/aurora-silicon/linux/commit/4fa6c2b053a65088ca20641480fd9ed8da55716e) | Ace | arm64: dts: apple: Add diagnostic J700 PCIe root-port topology | Not applicable — PERIPH | V | +| [c309c0ebb058](https://github.com/aurora-silicon/linux/commit/c309c0ebb058bf6cd1df2503d121ace539448ecc), [5a4974fde407](https://github.com/aurora-silicon/linux/commit/5a4974fde40748a151a4c23c0df730f1148bc2df) | Ace | arm64: dts: apple: t8140-j700: enable bounded PCIe endpoint bootstrap and enumeration | Not applicable — PERIPH | V | +| [56e442c7cbbe](https://github.com/aurora-silicon/linux/commit/56e442c7cbbe49c2613a5209fbcf772e90bec10e), [716b60de9620](https://github.com/aurora-silicon/linux/commit/716b60de96207e43d3c06eeb9c424f147a5d16a3), [8a03c5341e27](https://github.com/aurora-silicon/linux/commit/8a03c5341e27e9066e15f924e88aa44f848b5294) | DjDeveloperr | arm64: dts: apple: enable AVD on T8140 | Not applicable — PERIPH | V | +| [2b9ef0954320](https://github.com/aurora-silicon/linux/commit/2b9ef0954320320edbee5b75e742176aef3ee8b3), [6f57627a710e](https://github.com/aurora-silicon/linux/commit/6f57627a710e655f30ec73ece845d18c52663d78), [4088c3b1854c](https://github.com/aurora-silicon/linux/commit/4088c3b1854cdbaf106d8bfe72510551bf9fd43c) | Ryan Murray | arm64: dts: apple: t8140: add disabled display power domains | Not applicable — PERIPH | V | +| [7e14b8e9e9a2](https://github.com/aurora-silicon/linux/commit/7e14b8e9e9a2bcb983725fc9a0cb3c016a5ed12b), [1b05c65c4276](https://github.com/aurora-silicon/linux/commit/1b05c65c42763d955c7ca69ef849d6bf5fac7355), [004200ec1cc8](https://github.com/aurora-silicon/linux/commit/004200ec1cc8005fa692d797d2c5fb009ec9e05a) | Ryan Murray | arm64: dts: apple: t8140: add disabled internal display DARTs | Not applicable — PERIPH | V | +| [d7de8bb51e4f](https://github.com/aurora-silicon/linux/commit/d7de8bb51e4f654b1c1d33c8cabd3290a9e231ea), [0c29d4d4a4ea](https://github.com/aurora-silicon/linux/commit/0c29d4d4a4ea28e024596f605676dc0d1d1e3ba0), [d2f162f6d7c2](https://github.com/aurora-silicon/linux/commit/d2f162f6d7c24ed783657112086f7b56e1fb0cbe) | Ryan Murray | arm64: dts: apple: t8140: add disabled external display DARTs | Not applicable — PERIPH | V | +| [7a28ede30a1f](https://github.com/aurora-silicon/linux/commit/7a28ede30a1ff3894d8f2407ba657d51e8d9bd95), [042b2a224bf6](https://github.com/aurora-silicon/linux/commit/042b2a224bf671bacb56b6e5345b3eb399c7ec02), [aec8cb1999f7](https://github.com/aurora-silicon/linux/commit/aec8cb1999f731bbe1895509dbcc6e2fed977348) | Ryan Murray | arm64: dts: apple: t8140: Add the SEP, its DART and mailbox, and SPI2 | Not applicable — PERIPH | V | +| [07029412ae8d](https://github.com/aurora-silicon/linux/commit/07029412ae8d5832c1323d3eeb8a08b09f7b5be0), [1eb7bfe18b01](https://github.com/aurora-silicon/linux/commit/1eb7bfe18b01c0a088dd8e25a72ee5af3a09531a), [29987787b923](https://github.com/aurora-silicon/linux/commit/29987787b9238b15b41b92da87e5fcbf8a571e8c) | Ryan Murray | arm64: dts: apple: t8140-j700: Enable the SEP and the Touch ID sensor | Not applicable — PERIPH | V | +| [7d84216f74e8](https://github.com/aurora-silicon/linux/commit/7d84216f74e8e9e5827eabc9f173524ce5520c9b), [6049af950206](https://github.com/aurora-silicon/linux/commit/6049af950206644d95b8cd3ca11fe0722ff71257), [52f1ba512244](https://github.com/aurora-silicon/linux/commit/52f1ba51224431d256250c69784e1996f6ee63cc) | Acelogic | arm64: dts: apple: wire the J700 AVE encoder | Not applicable — PERIPH | V | +| [5f1fbb7bd670](https://github.com/aurora-silicon/linux/commit/5f1fbb7bd670ca8a26c303594f9ed54535e51f02), [519558a43644](https://github.com/aurora-silicon/linux/commit/519558a43644d79c2cc15135554748ffe6fadc81), [f898b116beaa](https://github.com/aurora-silicon/linux/commit/f898b116beaaab7add264a0f074b21743875d3ae) | Ryan Murray | arm64: dts: apple: Describe additional T8140 peripheral power states | Not applicable — PERIPH | V | +| [555932c62f03](https://github.com/aurora-silicon/linux/commit/555932c62f03ec2833c5db9b39e2d2dc956501cb), [ed2ef28706b7](https://github.com/aurora-silicon/linux/commit/ed2ef28706b7c2ed13aef2f90eb7191b4e34338e), [f75a1041fca4](https://github.com/aurora-silicon/linux/commit/f75a1041fca487774f29b133f736296f4d73a4e3) | Ryan Murray | arm64: dts: apple: Wire up remaining supported J700 peripherals | Not applicable — PERIPH | V | +| [a3d7465380ff](https://github.com/aurora-silicon/linux/commit/a3d7465380ffbf5f9cb4eddb550fabfdd71d3a17), [49fdcbcee4f4](https://github.com/aurora-silicon/linux/commit/49fdcbcee4f4569e6b65f7912e05502a38299743) | Ryan Murray | arm64: dts: apple: describe the J700 PMU alarm function | Not applicable — PERIPH | V | +| [d810cb82e82d](https://github.com/aurora-silicon/linux/commit/d810cb82e82de69013754b6031d00a83a6d15e41), [4e0543ee6caf](https://github.com/aurora-silicon/linux/commit/4e0543ee6cafb4250aaf332f894773412b86dfe2), [b9a339703e95](https://github.com/aurora-silicon/linux/commit/b9a339703e95a6a65e0e4515caa4c4f6afb0d9ce) | Ryan Murray | arm64: dts: apple: describe the disabled T8140 Neural Engine | Not applicable — PERIPH | V | +| [19f7cbdd84fe](https://github.com/aurora-silicon/linux/commit/19f7cbdd84fef12ca56aad9b654b46465cc2e198), [6b57d5423048](https://github.com/aurora-silicon/linux/commit/6b57d5423048ac4c70cd99d7473494ac3d37b2db), [93bc234385f5](https://github.com/aurora-silicon/linux/commit/93bc234385f5bf51b276e9d2e5abe8280e2ff92c) | Ryan Murray | arm64: dts: apple: t8140: Leave display coprocessor power to firmware | Not applicable — PERIPH | V | +| [0f5548d142b1](https://github.com/aurora-silicon/linux/commit/0f5548d142b1d73c44d270c91d5c359272dae499), [7c3c5ee5a070](https://github.com/aurora-silicon/linux/commit/7c3c5ee5a0702bb5b9f0ae4cde6b837cbafa3485), [9e6acbbd21ba](https://github.com/aurora-silicon/linux/commit/9e6acbbd21ba92409dd4f24074532b0bad997792) | Ryan Murray | arm64: dts: apple: t8140: Describe the internal display coprocessor | Not applicable — PERIPH | V | +| [100ff1ebedd3](https://github.com/aurora-silicon/linux/commit/100ff1ebedd337758a4ff3ba3fd3f2f46a9f5be9), [45ae8211efea](https://github.com/aurora-silicon/linux/commit/45ae8211efeafe3bff64b569370942c5218f1fec), [061370b4c70f](https://github.com/aurora-silicon/linux/commit/061370b4c70fd888ea6dc520a8988208f4aebca8) | Ryan Murray | arm64: dts: apple: t8140: Hand the J700 panel over to the DCP | Not applicable — PERIPH | V | +| [29f8091fe351](https://github.com/aurora-silicon/linux/commit/29f8091fe351a0cdb781cf18ac5748ca15feca42), [4f3906d225f4](https://github.com/aurora-silicon/linux/commit/4f3906d225f44739df672ac0b76715407f52e586), [a7fa3d8fbf56](https://github.com/aurora-silicon/linux/commit/a7fa3d8fbf5690df6553896a4f984b8cdc8069c2) | Ryan Murray | arm64: dts: apple: t8140: describe each power state once | Not applicable — PERIPH | V | +| [89c54797ae77](https://github.com/aurora-silicon/linux/commit/89c54797ae77fea8e8878a68e0b56f382cfb0409), [07969983cb1e](https://github.com/aurora-silicon/linux/commit/07969983cb1edefaec1b4b0aabb2307b0b14dacd), [953220ab6d74](https://github.com/aurora-silicon/linux/commit/953220ab6d748bd05db3854f374b701696f6c795) | Ryan Murray | arm64: dts: apple: t8140: describe the external display DARTs once | Not applicable — PERIPH | V | +| [55a5beb425ae](https://github.com/aurora-silicon/linux/commit/55a5beb425ae7c901851c2185e3219d8c0348fcb), [f0234a79d172](https://github.com/aurora-silicon/linux/commit/f0234a79d172479cb7dbcdb713daa5739ad87f74), [c80113dfaab9](https://github.com/aurora-silicon/linux/commit/c80113dfaab9e985b9f617bfde06c481fd17376c) | Ryan Murray | arm64: dts: apple: t8140-j700: give the two RTCs fixed numbers | Not applicable — PERIPH | V | +| [9e1fb240e1df](https://github.com/aurora-silicon/linux/commit/9e1fb240e1df4064a9232007daa5c8797dfbcda4), [19e2b5adfe69](https://github.com/aurora-silicon/linux/commit/19e2b5adfe6905ab2f3fff6ad1a12801d711c607), [ea044d49c76c](https://github.com/aurora-silicon/linux/commit/ea044d49c76c18079848672c2779c7521a6b8fc3) | Ryan Murray | arm64: dts: apple: t8140: Keep the GPIO power state on for the SEP | Not applicable — PERIPH | V | +| [4334bbd7162a](https://github.com/aurora-silicon/linux/commit/4334bbd7162af085ed1c8c7b63859bed8c6646ef), [f1c076a9bf8a](https://github.com/aurora-silicon/linux/commit/f1c076a9bf8a850bec0140fb3e91a9607aadcf4e), [30812f491575](https://github.com/aurora-silicon/linux/commit/30812f491575ede27e5be059fd6afc4e985166f1) | DjDeveloperr | arm64: dts: apple: t8140-j700: keep the DCS rail out of thermal control | Not applicable — DCSOFF | P,R | +| [baacaf604632](https://github.com/aurora-silicon/linux/commit/baacaf60463212b2270f0ad0b50cdabe4e27ab6d), [071651bd5c30](https://github.com/aurora-silicon/linux/commit/071651bd5c30330ecb9980c3c1f2cc4e2f04d1a6), [40a9c929c55f](https://github.com/aurora-silicon/linux/commit/40a9c929c55f2b13c39e732fc2ae3f10e1506634) | Ryan Murray | arm64: dts: apple: describe disabled J700 external resources | Not applicable — PERIPH | V | +| [13af1cb29b95](https://github.com/aurora-silicon/linux/commit/13af1cb29b95b1fc260870868d3c54a596cc483d), [f284458cdfc3](https://github.com/aurora-silicon/linux/commit/f284458cdfc33d2746bb4ede9174adc3afaa2f7f) | Ryan Murray | arm64: dts: apple: t8140: describe the external binding the display master recognizes | Not applicable — PERIPH | V | +| [a7f9b86ea187](https://github.com/aurora-silicon/linux/commit/a7f9b86ea18778f313b73cd174debe54e4dbc09c), [cffe838aca8e](https://github.com/aurora-silicon/linux/commit/cffe838aca8e275526730bd6f3fee5ebcaec0c47), [205a56d33cb0](https://github.com/aurora-silicon/linux/commit/205a56d33cb07184d7dfe17ab8aba0996cff3efe) | Ryan Murray | arm64: dts: apple: retain the inherited external display supply | Not applicable — PERIPH | V | +| [328dacb9d1ad](https://github.com/aurora-silicon/linux/commit/328dacb9d1adcbc1be84e8dae660c72bc12a3005), [3d16013f9795](https://github.com/aurora-silicon/linux/commit/3d16013f979572f7f7f6d1573f2af739e8ac25b4), [2e6003f6db07](https://github.com/aurora-silicon/linux/commit/2e6003f6db07b0e0a6b2bf2b977753bee47556b0) | Ryan Murray | arm64: dts: apple: add an explicit external display trial tree | Not applicable — PERIPH | V | +| [6b82a41053b8](https://github.com/aurora-silicon/linux/commit/6b82a41053b87c8bd0917f72f71778293aa7f276), [70cce5cd20ef](https://github.com/aurora-silicon/linux/commit/70cce5cd20efb8a71d49563b45481c32e0de23a6), [0a0d25f4a99b](https://github.com/aurora-silicon/linux/commit/0a0d25f4a99b66a08a2ab4a22314782aaad7b3e2) | DjDeveloperr | arm64: dts: apple: t8140-j700: drive the SPI NOR on a single data line | Not applicable — PERIPH | V | +| [d56b7ce044f2](https://github.com/aurora-silicon/linux/commit/d56b7ce044f274b153a3b1f9192d3fd4c7cb2d85), [df94160d7567](https://github.com/aurora-silicon/linux/commit/df94160d756756e14d4bc818fe0652bcdb9adc30) | DjDeveloperr | arm64: dts: apple: t8140-j700: enable the validated external display profile by default | Not applicable — PERIPH | V | +| [f9e24b02bc85](https://github.com/aurora-silicon/linux/commit/f9e24b02bc8547acba1453cf462f0f210e707ba7), [be5f3162c985](https://github.com/aurora-silicon/linux/commit/be5f3162c9854d6b5ea8ca9e8f29f764b2c6251b) | Eryk Wieliczko | arm64: dts: apple: t8140: mark firmware-owned power domains | Not applicable — PERIPH | V | +| [7df682a70733](https://github.com/aurora-silicon/linux/commit/7df682a707337025aad3c093252ea7f8504e8345), [b0714e8783f6](https://github.com/aurora-silicon/linux/commit/b0714e8783f685f213c15890c7942c1c03d6c1e3) | Chris Kearney | PCI: apple: enumerate Thunderbolt PCIe on t8103 | Not applicable — LEGACY; `drivers/iommu/apple-dart.c` | V | +| [4c02ff022799](https://github.com/aurora-silicon/linux/commit/4c02ff022799be8aef47108a61671a6a6cd86038), [b388a175a612](https://github.com/aurora-silicon/linux/commit/b388a175a612be6c5d10056c6a10a7219030272d) | Chris Kearney | PCI: apple: finish tunneled IOMMU commands before the port clock stops | Not applicable — TUNNEL; `drivers/iommu/apple-dart.c` | V | +| [9e8a900fee27](https://github.com/aurora-silicon/linux/commit/9e8a900fee275872aa1019df27a4944b73c8940a), [a2b13c1f0369](https://github.com/aurora-silicon/linux/commit/a2b13c1f03696ae69101c8ed51b8901d32dffcd4), [5a7b76f31c3a](https://github.com/aurora-silicon/linux/commit/5a7b76f31c3a8ccdc48fd586e5b646fa0c49499d) | Chris Kearney | iommu/apple-dart: enforce command gating during tunnel teardown | Not applicable — TUNNEL; `drivers/iommu/apple-dart-test.c` | V | +| [39e90a052954](https://github.com/aurora-silicon/linux/commit/39e90a052954d1305a45ca0065eda42ec4aff639), [d15623fd3261](https://github.com/aurora-silicon/linux/commit/d15623fd32614870a9eb62d25afdd823a39bcde7), [65e3e0ed688a](https://github.com/aurora-silicon/linux/commit/65e3e0ed688a5a933a60d92ca79b6136c357b876) | Chris Kearney | iommu/apple-dart: report tunnel command-resume failures | Not applicable — TUNNEL; `drivers/iommu/apple-dart-test.c` | V | +| [3a94483e259c](https://github.com/aurora-silicon/linux/commit/3a94483e259cbc8d0aab638eaa4af5b72c4db6e6), [6e0c3cdf7855](https://github.com/aurora-silicon/linux/commit/6e0c3cdf78550987513b0d4c15ffec90b442b787), [64dfa24ca989](https://github.com/aurora-silicon/linux/commit/64dfa24ca989fec06367a7199164a70de7d99e35) | Chris Kearney | PCI: apple: describe tunneled PM ordering by Linux dependencies | Not applicable — TUNNEL; `drivers/iommu/apple-dart.c` | V | +| [5d4dd51e2769](https://github.com/aurora-silicon/linux/commit/5d4dd51e27695b5d9570e56b050e3574dfc1fd38), [e7292daedcaa](https://github.com/aurora-silicon/linux/commit/e7292daedcaa2186e5b8b484343dc3ec239b2805), [615be505e22c](https://github.com/aurora-silicon/linux/commit/615be505e22c592c1a87c193b22c58dc4c064840) | Chris Kearney | thunderbolt: restore M1 tunneled PCIe state and recover late link loss | Not applicable — TUNNEL; `drivers/iommu/apple-dart-test.c` | V | +| [95caa2cf7ac7](https://github.com/aurora-silicon/linux/commit/95caa2cf7ac7244080527b90da0d43ec917e1513), [92f33b680514](https://github.com/aurora-silicon/linux/commit/92f33b6805149c955b543a6e2c29f88c42886f6d), [fc559d251fca](https://github.com/aurora-silicon/linux/commit/fc559d251fcac7e904607a36fb4b17770e29f239) | Acelogic | irqchip/apple-aic: use architectural guest timer masks on T8142 | Not applicable — OTHERCHIP; `drivers/irqchip/irq-apple-aic.c` | V | +| [4d1bcf62c1af](https://github.com/aurora-silicon/linux/commit/4d1bcf62c1af6fa29a32e94d821f4174098cbd29), [4ae1f9604a1a](https://github.com/aurora-silicon/linux/commit/4ae1f9604a1aa503dd1b452578bf694dbadaca25), [40bc3b97c1d6](https://github.com/aurora-silicon/linux/commit/40bc3b97c1d6a75ff5f8af9bc75322388a5381e1) | DjDeveloperr | soc: apple: rtkit-helper: adopt a running T8140 MTP session | Not applicable — PERIPH; `drivers/soc/apple/rtkit-helper.c` | V | +| [ad7993e04cd2](https://github.com/aurora-silicon/linux/commit/ad7993e04cd2e39c4fe011740268a537868bb8c0), [58a792863c4f](https://github.com/aurora-silicon/linux/commit/58a792863c4fdd79342e5e83ece8f652993084ab), [80cabfefcdc5](https://github.com/aurora-silicon/linux/commit/80cabfefcdc52036a7902268ff42f2a9370fe5c2) | Acelogic | cpufreq: apple: Add bounded T8152 E and P/M performance domains | Not applicable — OTHERCHIP; `drivers/cpufreq/apple-soc-cpufreq.c` | V | +| [81aefe80b586](https://github.com/aurora-silicon/linux/commit/81aefe80b586fa545b20eb90090202cde51718e7), [48f073156423](https://github.com/aurora-silicon/linux/commit/48f0731564236dc79c43bbcdadf6b05cfd807e8a), [296dbb45adff](https://github.com/aurora-silicon/linux/commit/296dbb45adff8654c6f1f8029e7b6b567f990e0b) | Acelogic | cpufreq: apple: add T8142 command-state transitions | Not applicable — OTHERCHIP; `drivers/cpufreq/apple-soc-cpufreq.c` | V | +| [4c3405d11d29](https://github.com/aurora-silicon/linux/commit/4c3405d11d29ec8a49a14bdc546cef4411153b32), [b08be27ef235](https://github.com/aurora-silicon/linux/commit/b08be27ef235dfedb3d18cf4a91a821be8a7effc) | Ryan Murray | cpufreq: apple: retain the unmanaged P-state ceiling on T8152 | Not applicable — OTHERCHIP; `drivers/cpufreq/apple-soc-cpufreq.c` | V | +| [24f4a32e8ec9](https://github.com/aurora-silicon/linux/commit/24f4a32e8ec9438b198e7ca97658154bcaedc6f7), [129471fc83b8](https://github.com/aurora-silicon/linux/commit/129471fc83b89e08275048a812779583ef197a93), [c0bd8b9fec16](https://github.com/aurora-silicon/linux/commit/c0bd8b9fec163274b9e6056ae94fe54d53b565fb), [b4ee9bd8d923](https://github.com/aurora-silicon/linux/commit/b4ee9bd8d923949b19cc22484262670948cafc57) | DjDeveloperr | drm/asahi: clarify the opening polling budget [#171](https://github.com/aurora-silicon/linux/pull/171) | Not applicable — VERIFY; `drivers/gpu/drm/asahi/g17/mod.rs` | V | +| [52f6e8610625](https://github.com/aurora-silicon/linux/commit/52f6e86106255b26e84671649c5f4acb0f434cde), [840488b0b60d](https://github.com/aurora-silicon/linux/commit/840488b0b60d3162e92f050cc59a2b3967036544), [66631bc63295](https://github.com/aurora-silicon/linux/commit/66631bc632956bb7dfd1b4fb44e48ae90a10140e), [d0aadc1e99e6](https://github.com/aurora-silicon/linux/commit/d0aadc1e99e6f11a54bbca5ee685d7c015866789) | DjDeveloperr | drm/asahi: describe the G17 word tables without their generation history [#171](https://github.com/aurora-silicon/linux/pull/171) | Not applicable — VERIFY; `drivers/gpu/drm/asahi/g17/fw/initdata.rs` | V | +| [1c771d73ac6c](https://github.com/aurora-silicon/linux/commit/1c771d73ac6c5e953be9582f1b74ccb358e6322d), [d24b1a49853f](https://github.com/aurora-silicon/linux/commit/d24b1a49853fe6949b44790761678cd2d38e623e), [a6e06b47b49d](https://github.com/aurora-silicon/linux/commit/a6e06b47b49d2666a53b5bc45ba8e0fa22e74944) | Joshua Warren | drm/asahi: Add stats_export module parameter | Not applicable — STAT; `drivers/gpu/drm/asahi/Makefile` | T | +| [3f096100f658](https://github.com/aurora-silicon/linux/commit/3f096100f658c1563dafaa0f0ca8b77ea2a87fe3), [f436b4da5480](https://github.com/aurora-silicon/linux/commit/f436b4da5480140e5ba95d68e46f1e7dd7615db8) | Ryan Murray | drm/asahi: tie stats registration to a per-device snapshot owner | Not applicable — STAT; `drivers/gpu/drm/asahi/g17/mod.rs` | T | +| [fd13851e08b6](https://github.com/aurora-silicon/linux/commit/fd13851e08b689661332ee823ad65eafd4418c1a), [c91a9274e4b2](https://github.com/aurora-silicon/linux/commit/c91a9274e4b2736d5589826b07c8dc54c0e35055) | Ryan Murray | drm/asahi: describe the G17 tiler-heap shrink policy in hardware terms | Not applicable — VERIFY; `drivers/gpu/drm/asahi/g17/buffer.rs` | V | +| [af5c7b24a0e5](https://github.com/aurora-silicon/linux/commit/af5c7b24a0e5932026c8cbab8633f2563bf761ba), [f0ec35d78e5a](https://github.com/aurora-silicon/linux/commit/f0ec35d78e5adf9988c1bc71418d0a764c8d4ba3), [337b14463d50](https://github.com/aurora-silicon/linux/commit/337b14463d5010296efb0b17dfaa11b8f7601aae) | Ryan Murray | arm64: dts: apple: t8140: Fold the trailing soc override into the soc node [#314](https://github.com/aurora-silicon/linux/pull/314) | Not applicable — PERIPH | V | +| [3cd89c634a60](https://github.com/aurora-silicon/linux/commit/3cd89c634a60071014683a27d497e46b8e2e2fc5), [f5ba7b6ee41c](https://github.com/aurora-silicon/linux/commit/f5ba7b6ee41ca4545745bfcc312fdbc0abf3e491), [6f1b4ab6f2f0](https://github.com/aurora-silicon/linux/commit/6f1b4ab6f2f036dedd104b83df774d6284ecab74) | Ryan Murray | arm64: dts: apple: t8140: Tidy the mailbox and pinctrl comments [#314](https://github.com/aurora-silicon/linux/pull/314) | Not applicable — PERIPH | V | +| [553e961d56c9](https://github.com/aurora-silicon/linux/commit/553e961d56c9ecbf6d58561c98468496955ad1d3), [38dd24cdc5a3](https://github.com/aurora-silicon/linux/commit/38dd24cdc5a3e13555afcac85f28d8ca3fc77a8e), [742366203cc8](https://github.com/aurora-silicon/linux/commit/742366203cc8a1f9356d82a43d52e2cb6fa97621) | Ryan Murray | pmdomain: apple: pmp-report: remove the raw DVFS debugfs dump [#317](https://github.com/aurora-silicon/linux/pull/317) | Not applicable — RAWDIAG; `drivers/pmdomain/apple/pmp-report.c` | O | +| [d04cbb892d26](https://github.com/aurora-silicon/linux/commit/d04cbb892d26dca250de4ff6f8c6cde5446682fb), [34754dd5bf3b](https://github.com/aurora-silicon/linux/commit/34754dd5bf3bd3d21b3409b6768af8b8fa38a045), [21c1d3f21ac4](https://github.com/aurora-silicon/linux/commit/21c1d3f21ac49f4223993ed1beefca1d6d6e89cd) | Ryan Murray | arm64: dts: apple: t8140: state why PMGR domains off at boot are always-on [#317](https://github.com/aurora-silicon/linux/pull/317) | Not applicable — PERIPH | V | +| [c3760b2f1cbd](https://github.com/aurora-silicon/linux/commit/c3760b2f1cbd4e17586ced9ac0d9ae7e8fe7d024), [732facd15036](https://github.com/aurora-silicon/linux/commit/732facd1503659e30fb096e0276e2efcdfa41ed7), [0d1799c7c179](https://github.com/aurora-silicon/linux/commit/0d1799c7c179d3b6e914eeaee59da7529e824565) | Ryan Murray | arm64: dts: apple: t8140-j700: trim measurement notes from comments [#317](https://github.com/aurora-silicon/linux/pull/317) | Not applicable — PERIPH | V | +| [ac9e0f78ef56](https://github.com/aurora-silicon/linux/commit/ac9e0f78ef560ef9d7a5c5655c7253e938b08d71), [f2928aa2ef66](https://github.com/aurora-silicon/linux/commit/f2928aa2ef662ff9045367ee9910a62a0f0b4e4b) | Ryan Murray | pmdomain: apple: pmc: Test partial enables and probe unwind | Not applicable — VERIFY; `drivers/pmdomain/apple/pmc-test.c` | V | +| [e8e0eae211f9](https://github.com/aurora-silicon/linux/commit/e8e0eae211f9d97ad777522ac8bf0c331ebf20f8), [a20499ed1321](https://github.com/aurora-silicon/linux/commit/a20499ed1321d5e5054d0f3dfd3f3a622e2938be) | Ryan Murray | soc: apple: mcc: Test partial allocation enable and cache handoff | Not applicable — VERIFY; `drivers/soc/apple/Kconfig` | V | +| [f2669637e7a6](https://github.com/aurora-silicon/linux/commit/f2669637e7a6c7e24f8eac823097156728ce64a8), [e3d59133d7a7](https://github.com/aurora-silicon/linux/commit/e3d59133d7a793ac15e3fa7328d11dafff8d8c92), [d6094473be4c](https://github.com/aurora-silicon/linux/commit/d6094473be4ceae69e8385ffb03891a22c60c3e4) | Ryan Murray | soc: apple: rtkit-helper: Terminate log messages [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — PERIPH; `drivers/soc/apple/rtkit-helper.c` | V | +| [8a83508a3870](https://github.com/aurora-silicon/linux/commit/8a83508a3870e8dd8d15ade42c759ed1be8fc9c1), [bee2492c2bac](https://github.com/aurora-silicon/linux/commit/bee2492c2bac7c4cf6022cffadd92b1e4fae2f54), [0afefd991028](https://github.com/aurora-silicon/linux/commit/0afefd9910288c1783a2756c4240dfc80ee8470f) | Ryan Murray | iommu: apple-dart: Return IOMEM_ERR_PTR() from the tunneled MMIO helper [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — TUNNEL; `drivers/iommu/apple-dart.c` | V | +| [d970f88dd819](https://github.com/aurora-silicon/linux/commit/d970f88dd819b24905808cac119647625e382303), [6b8a1244cb2c](https://github.com/aurora-silicon/linux/commit/6b8a1244cb2c8e0da6290fe794fb442b5cb9404e), [27da3a5d53ca](https://github.com/aurora-silicon/linux/commit/27da3a5d53ca11f2aeff4916d25f40308c378a07) | Eryk Wieliczko | arm64: dts: apple: t8140-j700: give the boot framebuffer its panel | Not applicable — PERIPH | V | +| [8a9127fdd1f5](https://github.com/aurora-silicon/linux/commit/8a9127fdd1f54bfe2913e7996073a994eb41ff04), [8feaf5d2fa71](https://github.com/aurora-silicon/linux/commit/8feaf5d2fa714a11984b1eeafd5dd2e076ec4af6), [748b2a25f2ca](https://github.com/aurora-silicon/linux/commit/748b2a25f2ca960017eef868e2323a27e07f0fe2) | Ryan Murray | drm/asahi: clean up the statistics Rust style | Not applicable — STAT; `drivers/gpu/drm/asahi/channel.rs` | T | +| [c987a1793e75](https://github.com/aurora-silicon/linux/commit/c987a1793e759da730ff84384a601d9d96d916d3), [37b3b1f7de85](https://github.com/aurora-silicon/linux/commit/37b3b1f7de8547eca29a4414465b5b4d705d413d) | Chris Kearney | drm/apple: wait for T6030 RTKit callbacks before a DCP is freed | Not applicable — G15; `drivers/gpu/drm/apple/dcp.c` | V | +| [9639e3803ffb](https://github.com/aurora-silicon/linux/commit/9639e3803ffb7f73c58dbbacad17a6e40b7596ac), [346fd6083ab2](https://github.com/aurora-silicon/linux/commit/346fd6083ab2d45a079fe512c6b3e68affd4cc6f) | Ryan Murray | drm/asahi: free completed commands on system_dfl_wq | Not applicable — LEGACY; `drivers/gpu/drm/asahi/workqueue.rs` | V | +| [3e6775c52948](https://github.com/aurora-silicon/linux/commit/3e6775c52948793951b618817b3715eec5f91924), [b9527e10b2b6](https://github.com/aurora-silicon/linux/commit/b9527e10b2b683dff7d4b718e9af6f93fb0a005f) | Ryan Murray | arm64: add an isolated T8140 graphics and power profile | Not applicable — PERIPH | V | +| [7f40cd79f058](https://github.com/aurora-silicon/linux/commit/7f40cd79f0582c4ca1abbad36f1e923d8163779f), [58db6bfedcec](https://github.com/aurora-silicon/linux/commit/58db6bfedcec7cb4a2be3843c5d3665b4680553e) | Chris Kearney | soc: apple: test every PMP integer replacement width | Not applicable — VERIFY; `drivers/soc/apple/pmp_bootargs.rs` | V | +| [0a1f130cd042](https://github.com/aurora-silicon/linux/commit/0a1f130cd0427037fc94a4ad39e54a3c66d5dbac), [f11518fc495d](https://github.com/aurora-silicon/linux/commit/f11518fc495d51dc3c5d8f67cc10d7cb02a4d739) | Eryk Wieliczko | pmdomain: apple: add the GFX TVM gate | Not applicable — G16; `drivers/pmdomain/apple/Makefile` | V | +| [4e7c8a74e4a5](https://github.com/aurora-silicon/linux/commit/4e7c8a74e4a5ef03a1d06b5cd73f01c68b75a63d), [fe7d063e4cee](https://github.com/aurora-silicon/linux/commit/fe7d063e4cee437b5196c438f9f19a6ad7e22afc) | Satya Benson | pmdomain: apple: gfx-tvm: accept the T8122 | Not applicable — G15; `drivers/pmdomain/apple/gfx-tvm.c` | V | +| [460c0a77ca86](https://github.com/aurora-silicon/linux/commit/460c0a77ca860aef48d7ed449d65a878cffab9ed), [c8aed729e9bf](https://github.com/aurora-silicon/linux/commit/c8aed729e9bf8d23f0adabff999aa16d34201d76) | Joshua Warren | gpu: let the firmware preempt compute work on G14X | Not applicable — LEGACY; `drivers/gpu/drm/asahi/asahi_core.rs` | V | +| [80797e4f1b2b](https://github.com/aurora-silicon/linux/commit/80797e4f1b2bbde71964ea2253553d2c2dbbe379), [9c7ab8b412a5](https://github.com/aurora-silicon/linux/commit/9c7ab8b412a50123bf7dbc642368aa7f930d2d41) | Ryan Murray | drm/asahi: gate compile-only M3 backends before resource setup | Not applicable — G15; `drivers/gpu/drm/asahi/asahi_core.rs` | V | +| [700bfca794e5](https://github.com/aurora-silicon/linux/commit/700bfca794e50093c6e4d2b4231aca6252451a41), [860d185ccc57](https://github.com/aurora-silicon/linux/commit/860d185ccc575a47df4962cb1cf32da8f27ff35f) | Chris Kearney | drm/asahi: add the M3 (G15S) GPU runtime (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_compute_layout.rs` | V | +| [01680f395e55](https://github.com/aurora-silicon/linux/commit/01680f395e557e93b331ad510fbf255e6622c2a0), [a846942e367d](https://github.com/aurora-silicon/linux/commit/a846942e367dade6002d2d456ae6249c50d04d39) | Eryk Wieliczko | drm/asahi: share M3 M4 host retirement accounting (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/agx_host_progress.rs` | V | +| [9593d5f2498f](https://github.com/aurora-silicon/linux/commit/9593d5f2498f3243100c828cc7bbf7ecdaddb861), [4bcb6f1acab4](https://github.com/aurora-silicon/linux/commit/4bcb6f1acab42a1772bf62aaae00c5fb1f6007cd) | Eryk Wieliczko | drm/asahi: account M3 GEM and coherent firmware backing lifetimes (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/agx_memory_stats.rs` | V | +| [bba3fa8f4562](https://github.com/aurora-silicon/linux/commit/bba3fa8f4562761864f8f57b1a461bcd50ec3d64), [3e963ea31aca](https://github.com/aurora-silicon/linux/commit/3e963ea31acaad3e4a78dc137f12339fa8ebb5ee) | Chris Kearney | drm/asahi: expose bounded native retirement timing snapshots (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/agx_timing_stats.rs` | V | +| [6d24d16ce24c](https://github.com/aurora-silicon/linux/commit/6d24d16ce24c8af1902191781e1255807ccd8aae), [1324a0258bb0](https://github.com/aurora-silicon/linux/commit/1324a0258bb0d0987993a3c34c2a74b0f2093cce) | Chris Kearney | drm/asahi: expose host-tracked pending GPU activity separately from completions (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/agx_host_progress.rs` | V | +| [a4bbcd4baf93](https://github.com/aurora-silicon/linux/commit/a4bbcd4baf933a15e5bc75b2c7c5603d8b1b6998), [8faa4de3948b](https://github.com/aurora-silicon/linux/commit/8faa4de3948b0795c068eeeb0456a6a7dfc45077) | Chris Kearney | drm/asahi: avoid repeated activity fences while GPU work is pending (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/agx_host_progress.rs` | V | +| [2397ac8a0861](https://github.com/aurora-silicon/linux/commit/2397ac8a086119bba90f6fc0d8d430e035d27f30), [b62d349e0fc6](https://github.com/aurora-silicon/linux/commit/b62d349e0fc6e160b9dabd167166a2d87ecfdbce) | Eryk Wieliczko | drm/asahi: remove M3 lifetime submission cap with wrapping wire stamps (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_timeline.rs` | V | +| [ce78a5b0c517](https://github.com/aurora-silicon/linux/commit/ce78a5b0c5173dbb9b5d7d0017488c43a07f222f), [578aa862e357](https://github.com/aurora-silicon/linux/commit/578aa862e357f0f61e18dc8e26181eeb5c0b9d7b) | Eryk Wieliczko | drm/asahi: preserve nonzero M3 stamps and dependencies through rollover (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_timeline.rs` | V | +| [646db65b2128](https://github.com/aurora-silicon/linux/commit/646db65b212826a29bd969332760722077926d61), [1d6a90b4117f](https://github.com/aurora-silicon/linux/commit/1d6a90b4117fe9cb84aa7863f491d18d37fcf0c5) | Chris Kearney | drm/asahi: remove unused backend helpers and narrow layout visibility (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_compute_storage.rs` | V | +| [a91573de916a](https://github.com/aurora-silicon/linux/commit/a91573de916a12ddc980c75d977567e57725e449), [f939d03f703f](https://github.com/aurora-silicon/linux/commit/f939d03f703fe760f413a87d435769b3aa970d21) | Chris Kearney | treewide: apple: reword M3 comments and messages as plain technical statements (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_compute_sequence.rs` | V | +| [997a7dba8158](https://github.com/aurora-silicon/linux/commit/997a7dba81582c94d1bdcc26aaae6031a486390c), [262009a6c7f9](https://github.com/aurora-silicon/linux/commit/262009a6c7f9dc7b6ca4abb158b669870a6180c5) | Chris Kearney | drm/asahi: select M3 runtime configuration per SoC (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_init_storage.rs` | V | +| [a8065eb937ce](https://github.com/aurora-silicon/linux/commit/a8065eb937cee67032a68207827419c515e52e11), [d16a8c32954b](https://github.com/aurora-silicon/linux/commit/d16a8c32954b412bb3bf9f61a7c48a68dc9303b1) | Chris Kearney | drm/asahi: m3: derive runtime IO maps from IO mappings (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_init_storage.rs` | V | +| [c3d978ed0793](https://github.com/aurora-silicon/linux/commit/c3d978ed0793058248d9994e868f5c1043cc18f7), [7f04f81b5a19](https://github.com/aurora-silicon/linux/commit/7f04f81b5a191b8ff9b1a3d454656af3993b47be) | Satya Benson | drm/asahi: m3: size render TPC and preemption storage by cluster count (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_pass_layout.rs` | V | +| [4287d2b5166b](https://github.com/aurora-silicon/linux/commit/4287d2b5166bb92f791ae652dba8361cfc732aeb), [d94dcc88b26c](https://github.com/aurora-silicon/linux/commit/d94dcc88b26ca0e964b5298ee1298ed068cbf6eb) | Satya Benson | drm/asahi: m3: publish queued packets from the completion worker (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_batch_policy.rs` | V | +| [6046b500e687](https://github.com/aurora-silicon/linux/commit/6046b500e6872cfe56d53cb65e9b0619cb1f066c), [6787ae6d5717](https://github.com/aurora-silicon/linux/commit/6787ae6d57170ef75c454f6f744d4c4d557cb9a7) | Satya Benson | drm/asahi: m3: bound overlapping render batches to free pass slots (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_batch_policy.rs` | V | +| [4ea0ea7df543](https://github.com/aurora-silicon/linux/commit/4ea0ea7df5437fad2829ec8eb68be6e9f3544591), [ce9df27fd4b6](https://github.com/aurora-silicon/linux/commit/ce9df27fd4b67bd47194655b57efecdec2321081) | Satya Benson | drm/asahi: m3: budget render coalescing by measured per-VM GPU time (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_batch_policy.rs` | V | +| [b877dfb7e3e8](https://github.com/aurora-silicon/linux/commit/b877dfb7e3e89e31fef97057c68581660b6f256e), [d57591e332b4](https://github.com/aurora-silicon/linux/commit/d57591e332b49cafc12aaf8af77845525dda760b) | Chris Kearney | drm/asahi: m3: compare waiting urgency with the active render batch (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_batch_policy.rs` | V | +| [f9ede2dda5dd](https://github.com/aurora-silicon/linux/commit/f9ede2dda5dd7a0fa8ce0e1143724e7be01b31a0), [7c912ffa2635](https://github.com/aurora-silicon/linux/commit/7c912ffa2635dd08673a43c09442a143a39b624d) | Chris Kearney | drm/asahi: retain full firmware-log payloads and use ring entry sizes (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_init_layout.rs` | V | +| [02e89516c254](https://github.com/aurora-silicon/linux/commit/02e89516c254d55b4ae686ccae460af8ddd30901), [e2bbe50fc3fd](https://github.com/aurora-silicon/linux/commit/e2bbe50fc3fd42e5dfbd74e5aff9181af86bb096) | Chris Kearney | drm/asahi: keep firmware-log payload clear of render counter pages (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_init_storage.rs` | V | +| [eb1dec967643](https://github.com/aurora-silicon/linux/commit/eb1dec967643c6b4fbbd528812befd07434951d8), [11896702843e](https://github.com/aurora-silicon/linux/commit/11896702843eb979e00beb871090800d9c53a4e0) | Chris Kearney | drm/asahi: coalesce eligible T6030 render completion stamp flushes (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_batch_policy.rs` | V | +| [407c4ba9d5c8](https://github.com/aurora-silicon/linux/commit/407c4ba9d5c8f14292a7ba75afdc929475a0e40c), [7feec149fdc7](https://github.com/aurora-silicon/linux/commit/7feec149fdc7795ff82698683c86dc4b3a2a486b) | Chris Kearney | drm/asahi: order opt-in T6030 mixed batches with firmware barriers (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_batch_policy.rs` | V | +| [d8c059ddae69](https://github.com/aurora-silicon/linux/commit/d8c059ddae697f618e0b70b4b2ddee103ba9a1eb), [0377e6c2e9ad](https://github.com/aurora-silicon/linux/commit/0377e6c2e9ad549130015a52de84c0c24e47a343) | Chris Kearney | drm/asahi: make T6030 internal render measurements optional (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_batch_policy.rs` | V | +| [ad54cf1de60e](https://github.com/aurora-silicon/linux/commit/ad54cf1de60ea638b0ab96e80d9bea7d7fc726ea), [cd13ef870e00](https://github.com/aurora-silicon/linux/commit/cd13ef870e00dd86aed1b582b393c6a697bec326) | Chris Kearney | drm/asahi: use stage-specific render sequence length fields (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_command_layout.rs` | V | +| [16fef343da41](https://github.com/aurora-silicon/linux/commit/16fef343da414828fe4098a566951945838bb370), [689764765782](https://github.com/aurora-silicon/linux/commit/68976476578273e77d0d5f05f77f437f610dbee8) | Chris Kearney | drm/asahi: add bounded T6030 active power-state sampling (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_active_power.rs` | V | +| [019434491afe](https://github.com/aurora-silicon/linux/commit/019434491afe2cd57bc001515778d9412c9b0747), [cd09499b2a78](https://github.com/aurora-silicon/linux/commit/cd09499b2a78ce5e0ca1b628af94403c56467256) | Chris Kearney | drm/asahi: add opt-in T6030 consistent performance requests (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_active_power.rs` | V | +| [b09e6d9fca33](https://github.com/aurora-silicon/linux/commit/b09e6d9fca33c554a597774f1a6cd4c4ca54622b), [263fd8538d83](https://github.com/aurora-silicon/linux/commit/263fd8538d83cca3809589da9b78cb1d982c0a21) | Chris Kearney | drm/asahi: refresh an opt-in T6030 busy performance request (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_consistent_perf.rs` | V | +| [9bc784357fe8](https://github.com/aurora-silicon/linux/commit/9bc784357fe8affe0875fb911be88ac953ca30db), [db732ce1f244](https://github.com/aurora-silicon/linux/commit/db732ce1f24488887872abc3b61b45de95285252) | Chris Kearney | drm/asahi: add an opt-in T6030 Globals debug-word clear (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_globals_debug.rs` | V | +| [02bde1b70c31](https://github.com/aurora-silicon/linux/commit/02bde1b70c312f6d4d964c7a2b1737be0de5a909), [651fbc49524d](https://github.com/aurora-silicon/linux/commit/651fbc49524d677ff86766dd2d85bb1bbf6ab3eb) | Chris Kearney | drm/asahi: m3: groundwork for more than two clusters and T6031 tables (M3-only split) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_pass_layout.rs` | V | +| [0f47fbf31934](https://github.com/aurora-silicon/linux/commit/0f47fbf31934d0b1684b5412e235315304687d4a), [783f9478d373](https://github.com/aurora-silicon/linux/commit/783f9478d3737cfae945aad01e02e9dea30c2249) | Chris Kearney | drm/asahi: reject unsupported command fields on the M1/M2 queue path | Not applicable — LEGACY; `drivers/gpu/drm/asahi/queue/mod.rs` | V | +| [1de2dbd89acb](https://github.com/aurora-silicon/linux/commit/1de2dbd89acb80f37aec08fa897374dfebe56b10), [294e9f9ebfb4](https://github.com/aurora-silicon/linux/commit/294e9f9ebfb42dbc77a85f635b5bfabaa9315003) | Chris Kearney | drm/asahi: excise unbound G16 and G17 runtimes (M3-only split: TA/3D command encoders) | Not applicable — G15; `drivers/gpu/drm/asahi/m3/agx_render.rs` | V | +| [fd1a2a3d4e60](https://github.com/aurora-silicon/linux/commit/fd1a2a3d4e60ce3f3965d2a47d1904ce3a6ed158), [e91bc024eef8](https://github.com/aurora-silicon/linux/commit/e91bc024eef8a6fde320f7fa062a034a2fe0bccb) | Eryk Wieliczko | drm/asahi: carry validated M3 depth compression bindings | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_fragment_command.rs` | V | +| [bc0a841680f3](https://github.com/aurora-silicon/linux/commit/bc0a841680f3b36193e83e18c339d269d142de03), [2c301fab2412](https://github.com/aurora-silicon/linux/commit/2c301fab241280c36f20c57d7d240ff0f48ad88f) | garymeyerca | drm/asahi: gate T8122 G15G identity before startup | Not applicable — G15; `drivers/gpu/drm/asahi/m3/mod.rs` | V | +| [e10c013299bf](https://github.com/aurora-silicon/linux/commit/e10c013299bf639ca0c8f08574150970e381f203), [be90c110d9df](https://github.com/aurora-silicon/linux/commit/be90c110d9dfcae3918efad4ba9a3d5015bb5bf4) | Chris Kearney | drm/asahi: select M3 runtime configuration per SoC | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_soc.rs` | V | +| [ed90e7bfde51](https://github.com/aurora-silicon/linux/commit/ed90e7bfde5160c20b1e4165f577e874e60fc102), [a6b472702f24](https://github.com/aurora-silicon/linux/commit/a6b472702f24666ee52febc1c2eb93bf21c8d44a) | Satya Benson | drm/asahi: m3: T8122: the G15G compute register list | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_compute_layout.rs` | V | +| [278d0abfc612](https://github.com/aurora-silicon/linux/commit/278d0abfc6128a83aa00d9181bdf2c716b608c89), [7949c0d6139c](https://github.com/aurora-silicon/linux/commit/7949c0d6139c53f8e016cd773744386407a8c9cf) | Satya Benson | drm/asahi: m3: T8122: the G15G TA and 3D register lists | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_fragment_command.rs` | V | +| [7231c0beb83e](https://github.com/aurora-silicon/linux/commit/7231c0beb83ec1976716d28456e09df3e159f2e5), [bfd3e0bf0bbc](https://github.com/aurora-silicon/linux/commit/bfd3e0bf0bbc64ee99213b4b5b3824b588411bba) | Satya Benson | drm/asahi: m3: T8122: the runtime HwDataB words | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_soc.rs` | V | +| [6ad35705d81d](https://github.com/aurora-silicon/linux/commit/6ad35705d81dd1e65f8ef9e1f4136a0080ead760), [f6e58511c69a](https://github.com/aurora-silicon/linux/commit/f6e58511c69ae5cdef73b3ba0d87de8de9a74e01) | Chris Kearney | drm/asahi: m3: T8122: the remaining runtime InitData words | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_soc.rs` | V | +| [2a8cdd0ce1d4](https://github.com/aurora-silicon/linux/commit/2a8cdd0ce1d4a8ceef673922b9517e4780996a0c), [d109a5c9e597](https://github.com/aurora-silicon/linux/commit/d109a5c9e59768669f2d1750ab1abd4560a42e28) | Chris Kearney | drm/asahi: coalesce eligible T6030 render completion stamp flushes | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_fragment_command.rs` | V | +| [bb1f75a18b32](https://github.com/aurora-silicon/linux/commit/bb1f75a18b3265ab5e4a06e424df43ed0371c002), [d4dba3df31cf](https://github.com/aurora-silicon/linux/commit/d4dba3df31cff8f96ddeafad6a4b2709fe942d62) | Chris Kearney | drm/asahi: use stage-specific render sequence length fields | Not applicable — G15; `drivers/gpu/drm/asahi/m3/m3_fragment_command.rs` | V | +| [1e8f813be7c6](https://github.com/aurora-silicon/linux/commit/1e8f813be7c681b558ffcd501ea278d6848099f4), [3d7945cbbd0f](https://github.com/aurora-silicon/linux/commit/3d7945cbbd0f9dd9d7806c402e70a53815c30738) | Satya Benson | drm/asahi: m3: T8122: write the HwDataA +0x418c block on 14.8.3 | Not applicable — G15; `drivers/gpu/drm/asahi/m3/mod.rs` | V | +| [8730d3dce2b7](https://github.com/aurora-silicon/linux/commit/8730d3dce2b7e8d9f05ad437c4cf89169e0c5b78), [453d66a437b0](https://github.com/aurora-silicon/linux/commit/453d66a437b02027ad0b6fb194242dd29bb0d31d) | Chris Kearney | pmdomain: apple: pmp-report: add the T6031 report | Not applicable — G15; `drivers/pmdomain/apple/pmp-report-test.c` | V | +| [a66386d03830](https://github.com/aurora-silicon/linux/commit/a66386d03830555c976850001c12644921b0a028), [a8e5d3a57a0e](https://github.com/aurora-silicon/linux/commit/a8e5d3a57a0e19360cffa4dc04ae500ba832e0ea) | Marius Gehrmann | pmdomain: apple: pmp-report: Request T8122 devices by soc-device record | Not applicable — G15; `drivers/pmdomain/apple/pmp-report-test.c` | V | +| [3b9bb4563bb8](https://github.com/aurora-silicon/linux/commit/3b9bb4563bb8b9f16761fe5842ff3bb6a09d8661), [c7bbd7faf778](https://github.com/aurora-silicon/linux/commit/c7bbd7faf778be8ab87515e81caf06571e527ad8) | Chris Kearney | pmdomain: apple: pmp-report: start a handed-over T8122 or T6031 PMP | Not applicable — G15; `drivers/pmdomain/apple/pmp-report.c` | V | +| [3157c614a661](https://github.com/aurora-silicon/linux/commit/3157c614a66119b47100a6a8ac1ad9232bacbf81), [2b115cc04222](https://github.com/aurora-silicon/linux/commit/2b115cc04222553f4bcde86a70f5a2f9c8f5d086) | Ryan Murray | pmdomain: apple: pmp-report: document a failed PMP start and its recovery | Not applicable — VERIFY; `drivers/pmdomain/apple/Kconfig` | V | +| [c332c6fbd56a](https://github.com/aurora-silicon/linux/commit/c332c6fbd56adddb1f6d5635b4010a940c780e7e), [54b90aaf82e9](https://github.com/aurora-silicon/linux/commit/54b90aaf82e9aa7241747d747a5e79c46841a2dd) | Ryan Murray | pmdomain: apple: pmp-report: add KUnit fault injection for the PMP start | Not applicable — VERIFY; `drivers/pmdomain/apple/.kunitconfig.apple-pmp-start` | V | +| [7d101f700326](https://github.com/aurora-silicon/linux/commit/7d101f7003268e2e459ed86b07c9bc3ec5a000f2), [ac234af7c851](https://github.com/aurora-silicon/linux/commit/ac234af7c851e08e6da06a4dffeb912786611ed8) | Chris Kearney | drm/asahi: uapi: add the M3 (G15S) runtime numbers | Not applicable — G15 | V | +| [70f4bebd69b0](https://github.com/aurora-silicon/linux/commit/70f4bebd69b070d37022bca55228879bb7ec4e32), [577249de3221](https://github.com/aurora-silicon/linux/commit/577249de322139ceb84aea9daffcf54d437da52b) | Eryk Wieliczko | drm/asahi: uapi: add the J613 25G83 runtime numbers | Not applicable — G15 | V | +| [f3de3ac043ac](https://github.com/aurora-silicon/linux/commit/f3de3ac043acd22bf4ded712f2999b5693d824ae), [744d0129b6c0](https://github.com/aurora-silicon/linux/commit/744d0129b6c008708c2d9d019ca0a95ee1c04874) | Ryan Murray | drm/asahi: reject timestamp copies on M1 and M2 | Not applicable — LEGACY; `drivers/gpu/drm/asahi/queue/mod.rs` | V | +| [d2cdf2ff6b35](https://github.com/aurora-silicon/linux/commit/d2cdf2ff6b353a72f5f33239a4a7f2b7d87e8cf4), [1e52a05edada](https://github.com/aurora-silicon/linux/commit/1e52a05edadad288cfc17120f37c721f506abcd5) | Chris Kearney | gpu: preserve compute overrides and limit automatic preemption to M2 Max | Not applicable — LEGACY; `drivers/gpu/drm/asahi/asahi_core.rs` | V | +| [a22a6bfc2fa0](https://github.com/aurora-silicon/linux/commit/a22a6bfc2fa049f249effd677a512d682870b171) | Ryan Murray | rust: drm: document Aurora ownership and simplify pointer conversions | Not applicable — VERIFY; `rust/kernel/drm/gpuvm/mod.rs` | V | +| [e8f94ff7af1c](https://github.com/aurora-silicon/linux/commit/e8f94ff7af1cd4104091ac851109fdae946aa585) | Ryan Murray | arm64: dts: apple: j700: Use the raw xHCI SuperSpeed port number | Not applicable — PERIPH | V | +| [58d75c388d55](https://github.com/aurora-silicon/linux/commit/58d75c388d55030f432e93f42ff8871ffe9390f3) | Chris Kearney | soc: apple: opt native T6030 GPUs into the admitted PMP supplier [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/pmdomain/apple/pmp-report.c` | V | +| [19538d922267](https://github.com/aurora-silicon/linux/commit/19538d9222679bc5ff3f17da845a10f60591e82c) | Chris Kearney | pmdomain: apple: read readiness from the passive T6030 report [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/pmdomain/apple/pmp-report.c` | V | +| [1633fb96515e](https://github.com/aurora-silicon/linux/commit/1633fb96515e3b733a0e3514e17f841ef344ab4c) | Eryk Wieliczko | drm/asahi: g15: Add the UAPI glue and register the G15 generation [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/adt.rs` | V | +| [4d6d40767ae2](https://github.com/aurora-silicon/linux/commit/4d6d40767ae2a1cdbb697d3471dbb2c4a7ef9f9d) | Naeem Malik | drm/asahi: g15: Add the T6031 (M3 Max, G15C) configuration row [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/adt.rs` | V | +| [99a4031d5828](https://github.com/aurora-silicon/linux/commit/99a4031d58282e9d2d0f9211b3ba50547d4fc643) | Naeem Malik | drm/asahi: g15: Pin the J516C GPU firmware build [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/hw/t6031.rs` | V | +| [4abf7048277d](https://github.com/aurora-silicon/linux/commit/4abf7048277dd6ec8364811002adeafe326046d6) | Naeem Malik | drm/asahi: g15: Build the J516C initdata from its board images [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/board.rs` | V | +| [5fc412af6085](https://github.com/aurora-silicon/linux/commit/5fc412af608586b6beb6a567544f3440f42fe7eb) | Naeem Malik | drm/asahi: g15: Encode compute commands with the G15C register values [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/hw/t6031.rs` | V | +| [377ce9d4f32a](https://github.com/aurora-silicon/linux/commit/377ce9d4f32a42a3b87e0885f48777140c4a2488) | Naeem Malik | drm/asahi: g15: Admit and map UAT regions that are reserved System RAM [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/adt.rs` | V | +| [3a5c855a7219](https://github.com/aurora-silicon/linux/commit/3a5c855a7219aaf45d18a3449bfd4a263ed781c2) | Naeem Malik | drm/asahi: g15: Accept the J516C 14.8 GPU firmware build [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/hw/t6031.rs` | V | +| [f0d7c15b4769](https://github.com/aurora-silicon/linux/commit/f0d7c15b47696e3bbf168f1c8bce9c4817ea28da) | DjDeveloperr | drm/asahi: g15: Reject a scissor base that overflows the address space [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/command.rs` | V | +| [2510cc337f15](https://github.com/aurora-silicon/linux/commit/2510cc337f1551b53e9de38b5c69d6d365e373f3) | DjDeveloperr | drm/asahi: g15: Do not fail a published prefix on a group allocation failure [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/engine.rs` | V | +| [5a592f22b23a](https://github.com/aurora-silicon/linux/commit/5a592f22b23a83ec106b50d5f9ec88f9d885e980) | DjDeveloperr | drm/asahi: g15: Release a closed VM's state when its handle goes [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/mod.rs` | V | +| [e2b962c42e40](https://github.com/aurora-silicon/linux/commit/e2b962c42e40c5ee92b81495436c11b5832d056c) | DjDeveloperr | drm/asahi: g15: Release the power hold and the PMP vote when a start fails early [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/boot.rs` | V | +| [c4d01d363c1d](https://github.com/aurora-silicon/linux/commit/c4d01d363c1d25c33fee168114f369288e24e55d) | DjDeveloperr | drm/asahi: g15: Only mark used pass slots dirty on release [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/slots.rs` | V | +| [35bbcb1bca7a](https://github.com/aurora-silicon/linux/commit/35bbcb1bca7a9dac71f9785aed900d4cf6665d9f) | DjDeveloperr | tools/asahi: Drop the removed T6031 knobs from the M3 runtime tests [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/glue.c` | V | +| [f6da2c8fd38a](https://github.com/aurora-silicon/linux/commit/f6da2c8fd38a75723a7569ff6046d08572900559) | Chris Kearney | drm/asahi-m3: adapt the native G15 driver to the 7.2 driver core and DRM device API [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/adt.rs` | V | +| [42f91c2f9eae](https://github.com/aurora-silicon/linux/commit/42f91c2f9eae615bc12335e37f2016ec09398f63) | Naeem Malik | drm/asahi: g15: Say what to report when the GPU firmware build is unknown [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/identity.rs` | V | +| [f3b867b04c1a](https://github.com/aurora-silicon/linux/commit/f3b867b04c1a50f46d034844754e39f8031ffae3) | Naeem Malik | drm/asahi: g15: Move the board template fill into a core-only submodule [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/board.rs` | V | +| [7375309d764f](https://github.com/aurora-silicon/linux/commit/7375309d764f899d8eb4328e30447ec2959efa02) | Naeem Malik | drm/asahi: g15: t6031: Build Globals and Power from the device tree [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/board.rs` | V | +| [b840b9944ad9](https://github.com/aurora-silicon/linux/commit/b840b9944ad9e171cf5b13b4e0e0fe8d018778c9) | Naeem Malik | drm/asahi: g15: board: Review fixes for the device-tree Globals and Power [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/board/words.rs` | V | +| [3f02f6c358ab](https://github.com/aurora-silicon/linux/commit/3f02f6c358abd1630f1342cbd4dbca755c78023c) | Naeem Malik | drm/asahi: g15: t6031: Build the hardware record from the device tree [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/adt.rs` | V | +| [f1f5a81b756f](https://github.com/aurora-silicon/linux/commit/f1f5a81b756f99b30baefcd967a924167b2a0c12) | Naeem Malik | drm/asahi: t6031: take the idle standby time from the GPU node [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/hw.rs` | V | +| [de646267a725](https://github.com/aurora-silicon/linux/commit/de646267a725f94ea67864c2695eb1082f60dc33) | Chris Kearney | drm/asahi: g15: t6031: refuse board-data values that do not fit in u32 [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/adt.rs` | V | +| [a9a0e4be0546](https://github.com/aurora-silicon/linux/commit/a9a0e4be0546ee7876924cee8e3478727f89cb41) | Chris Kearney | selftests: asahi-g15: run the T6031 board-data and firmware-lookup tests [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/board/record.rs` | V | +| [0b06992d812c](https://github.com/aurora-silicon/linux/commit/0b06992d812ce75667740514a5a472fc8d0e091b) | Chris Kearney | drm/asahi: g15: t6031: test that no GPU node value panics the record builder [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/board/record.rs` | V | +| [68b780fc7ced](https://github.com/aurora-silicon/linux/commit/68b780fc7ced9e39a5d4573993564ebda26f3649) | Naeem Malik | drm/asahi: g15: Read back the GPU's performance state in debugfs [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/board.rs` | V | +| [cd970562fa97](https://github.com/aurora-silicon/linux/commit/cd970562fa979059c6dea0e7f0a7f9df63c3b1c7) | Naeem Malik | drm/asahi: g15: t6031: Leave the 42 record words the firmware does not need zero [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/board/record.rs` | V | +| [2a0319927108](https://github.com/aurora-silicon/linux/commit/2a0319927108ee823a82bb1884d2cc6512b515bb) | Chris Kearney | drm/asahi: g15: m3: complete the stats record loads before consuming them [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/channel.rs` | V | +| [73047c7ee59e](https://github.com/aurora-silicon/linux/commit/73047c7ee59e45b8d16e8ec308fb15683caefc6e) | Naeem Malik | drm/asahi: g15: Seed the compute UMA freelist with every pool [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/fw/graph.rs` | V | +| [345f1d8467a4](https://github.com/aurora-silicon/linux/commit/345f1d8467a46f679eb2f2fc2852adb844911033) | Naeem Malik | drm/asahi: g15: Leave PB growth requests unanswered after two refusals at the maximum [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/params.rs` | V | +| [bd0201fad2e0](https://github.com/aurora-silicon/linux/commit/bd0201fad2e02429de6e265c51a223e4209920fe) | Naeem Malik | drm/asahi: g15: Keep PB blocks above the firmware's growth maximum for partial renders [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/params.rs` | V | +| [ff0d1cd692b8](https://github.com/aurora-silicon/linux/commit/ff0d1cd692b838934972a646fa7d1411c54b4864) | Naeem Malik | drm/asahi: g15: Map a 512 MiB parameter buffer heap [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/fw/buffer.rs` | V | +| [e1cdc12eeb97](https://github.com/aurora-silicon/linux/commit/e1cdc12eeb97fc9f4da88343054e6a964e8d2e40) | Chris Kearney | drm/asahi: g15: m3: keep every PB ledger below the block list's K [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/pools.rs` | V | +| [b1adafb49e1c](https://github.com/aurora-silicon/linux/commit/b1adafb49e1c49b71221f7c069dde6045ccce32c) | Chris Kearney | drm/asahi: g15: m3: say what a PB refusal does and size the heap right [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/fw/buffer.rs` | V | +| [3ea5cb6af82b](https://github.com/aurora-silicon/linux/commit/3ea5cb6af82bda7fca665b0902b22275ae02d996) | Chris Kearney | drm/asahi: g15: m3: check firmware-object backing geometry with checked arithmetic [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/object.rs` | V | +| [1eced02cd6c3](https://github.com/aurora-silicon/linux/commit/1eced02cd6c3efe9c43cfc9cf0993ee427e849b9) | Chris Kearney | drm/asahi: g15: m3: map the PB backing into each VM when it first runs [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/engine.rs` | V | +| [73b9ade12e01](https://github.com/aurora-silicon/linux/commit/73b9ade12e0123ef2168abd7eb4969d6865622a3) | Chris Kearney | drm/asahi: g15: m3: back the PB on demand, before an answer posts it [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/pools.rs` | V | +| [716d2fbe9254](https://github.com/aurora-silicon/linux/commit/716d2fbe9254f5ba13a8bd6a6ac687f5904a2f8a) | Chris Kearney | drm/asahi: adapt retained-G15 hooks to the maintained shared APIs [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/file.rs` | V | +| [a4000925920a](https://github.com/aurora-silicon/linux/commit/a4000925920a0e338e0bfa2b726f3b4de90b8cd7) | Chris Kearney | drm/asahi: port the native G15 adapter onto maintained DRM registration [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/Kconfig` | V | +| [23ae3eaf8377](https://github.com/aurora-silicon/linux/commit/23ae3eaf8377dbbd5c51541d77fb4a542fdb384b) | Chris Kearney | drm/asahi: g15: allocate driver data before touching firmware [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/mod.rs` | V | +| [a8289266db94](https://github.com/aurora-silicon/linux/commit/a8289266db94cf7069ab26420bd9d6b60a92dce6) | Chris Kearney | drm/asahi: g15: defer firmware start until module registration completes [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/asahi_core.rs` | V | +| [bc962412f16e](https://github.com/aurora-silicon/linux/commit/bc962412f16e1ca6d93ababf971c68011294c5e3) | Chris Kearney | drm/asahi: g15: make maintained loader contracts independent of SF profiles [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/adt.rs` | V | +| [6c6f0e62156c](https://github.com/aurora-silicon/linux/commit/6c6f0e62156cd9e35f5db47af226dd9821a26b0f) | Chris Kearney | drm/asahi: use the native private wire in G15 command consumers [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/boot.rs` | V | +| [627c501db65e](https://github.com/aurora-silicon/linux/commit/627c501db65ed9bbe22e853f37f247dc579db4af) | Chris Kearney | drm/asahi: isolate the native G15 command ABI behind a private facade [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/wire.rs` | V | +| [9d5c0ee71512](https://github.com/aurora-silicon/linux/commit/9d5c0ee715129fda8cbb8a1420315cf6a7d25cc4) | Chris Kearney | drm/asahi: g15: bound device-wide live logical queue capacity [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/mod.rs` | V | +| [4f6aba6a3598](https://github.com/aurora-silicon/linux/commit/4f6aba6a35980bf152ce5803d7bb5940a2cb11a2) | Chris Kearney | pmdomain: apple: pmgr-pwrstate: Test apple,no-auto-pm on power on and off [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — VERIFY; `drivers/pmdomain/apple/Kconfig` | V | +| [b9d3cf70f1e8](https://github.com/aurora-silicon/linux/commit/b9d3cf70f1e8b488a9ad032dadad7cbe432e164c) | Chris Kearney | drm/asahi: export a T6030 module alias for the native G15 driver [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/mod.rs` | V | +| [af2c5c09ba9f](https://github.com/aurora-silicon/linux/commit/af2c5c09ba9fae7fbf9f165fc6aad73e9aadce0b) | Chris K | pmdomain: apple: restore gated T6030 managed PMP startup [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/pmdomain/apple/pmp-report.c` | V | +| [2a182741b7f7](https://github.com/aurora-silicon/linux/commit/2a182741b7f7d80a7a743eafbf57bd9dbca3c6b4) | Chris Kearney | test: exercise the actual T6030 halted-PMP consumer graph [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/pmdomain/apple/Kconfig` | V | +| [70f41a7944ee](https://github.com/aurora-silicon/linux/commit/70f41a7944eeaf9afdd63f9246ebc5bc41df23f1) | Chris Kearney | drm/apple: admit T6030 G15 only after native display registration [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/apple/apple_drv.c` | V | +| [9632ccd283f9](https://github.com/aurora-silicon/linux/commit/9632ccd283f96ad98321238b6da4718b62f7671a) | Chris Kearney | drm/asahi: require a callable native display provider for G15 [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/Kconfig` | V | +| [e9d92ce6a5c2](https://github.com/aurora-silicon/linux/commit/e9d92ce6a5c27b36266de99d252db029476cb3f1) | Chris Kearney | drm/asahi: retain live G15 storage after allocation failures [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/boot.rs` | V | +| [fc13d05b1db9](https://github.com/aurora-silicon/linux/commit/fc13d05b1db9515fa9481d1362ff530fbeb01ca4) | DjDeveloperr | drm/asahi: g15: adapt the native G15 backend to the Linux 7.3 Rust APIs [#395](https://github.com/aurora-silicon/linux/pull/395) | Not applicable — G15; `drivers/gpu/drm/asahi/g15/boot.rs` | V | +| [0ffd22188c3d](https://github.com/aurora-silicon/linux/commit/0ffd22188c3dbb2e8bb2df216ee8e03d71e14bfb) | Ryan Murray | drm/asahi: Keep refusing HIGH queue priority on G13 and G14 [#399](https://github.com/aurora-silicon/linux/pull/399) | Not applicable — LEGACY; `drivers/gpu/drm/asahi/file.rs` | V | +| [1fac0605f061](https://github.com/aurora-silicon/linux/commit/1fac0605f061ce757917781940a5e0aec1225019) | Eryk Wieliczko | soc: apple: rtkit-helper: preserve the inherited J713 input session | Not applicable — G16; `drivers/soc/apple/rtkit-helper.c` | V | +| [751b9e08ca3d](https://github.com/aurora-silicon/linux/commit/751b9e08ca3d8fa673927984d45b22697cc26edf) | Eryk Wieliczko | iommu: apple-dart: retain J713 input and radio ownership [#400](https://github.com/aurora-silicon/linux/pull/400) | Not applicable — G16; `drivers/iommu/apple-dart.c` | V | +| [939a993d46dd](https://github.com/aurora-silicon/linux/commit/939a993d46dd36392155cc05f692472d2d1c881e) | Eryk Wieliczko | pmdomain: apple: pmc: support T8132 floor requests [#400](https://github.com/aurora-silicon/linux/pull/400) | Not applicable — G16; `drivers/pmdomain/apple/pmc-test.c` | V | +| [c26dfe75ac9b](https://github.com/aurora-silicon/linux/commit/c26dfe75ac9be0fb75dfe937a15246b4e12bc84a) | Eryk Wieliczko | soc: apple: clpc: retain qualified T8132 memory floors | Not applicable — G16; `drivers/soc/apple/clpc.c` | V | +| [15e0eb00b83b](https://github.com/aurora-silicon/linux/commit/15e0eb00b83b783d7fca9720530942020722cd6a) | Eryk Wieliczko | soc: apple: mcc: support the T8132 memory controller | Not applicable — G16; `drivers/soc/apple/mcc.c` | V | +| [df3e4970e686](https://github.com/aurora-silicon/linux/commit/df3e4970e68677d56eca3e9e9608c338abe52cdd) | Eryk Wieliczko | soc: apple: pmp: publish owner-managed T8132 telemetry [#400](https://github.com/aurora-silicon/linux/pull/400) | Not applicable — G16; `drivers/pmdomain/apple/pmp-report.c` | V | +| [f1b75932e61d](https://github.com/aurora-silicon/linux/commit/f1b75932e61d7d193801a7f22ea628cb94d9dd56) | Eryk Wieliczko | cpufreq: apple: track T8132 thermal ownership per policy | Not applicable — G16; `drivers/cpufreq/apple-soc-cpufreq.c` | V | +| [1e23ae3aee82](https://github.com/aurora-silicon/linux/commit/1e23ae3aee824c65cbb6d3c94780e44a2e3fc6c2) | Eryk Wieliczko | thermal: apple: add the J713 PMP CPU policy [#400](https://github.com/aurora-silicon/linux/pull/400) | Not applicable — G16; `drivers/thermal/Kconfig` | V | +| [332eea1e9005](https://github.com/aurora-silicon/linux/commit/332eea1e9005b0cf731a16695c9c87c34e8f13aa) | Eryk Wieliczko | arm64: apple: activate J713 with its PMGR ownership policy [#400](https://github.com/aurora-silicon/linux/pull/400) | Not applicable — G16; `drivers/pmdomain/apple/pmgr-pwrstate.c` | V | +| [3949e3640d94](https://github.com/aurora-silicon/linux/commit/3949e3640d94d2b5434c84e9e1f6d7b81f650b70) | Eryk Wieliczko | arm64: apple: enable qualified J713 SuperSpeed host support [#400](https://github.com/aurora-silicon/linux/pull/400) | Not applicable — G16; `drivers/phy/apple/atc.c` | V | +| [0877cd766126](https://github.com/aurora-silicon/linux/commit/0877cd766126503ad02cc11565b85be37517a62c) | Eryk Wieliczko | arm64: apple: enable J713 Bluetooth with scoped radio DMA [#400](https://github.com/aurora-silicon/linux/pull/400) | Not applicable — G16; `drivers/bluetooth/hci_bcm4377.c` | V | +| [1ae4d49f3592](https://github.com/aurora-silicon/linux/commit/1ae4d49f35921f356d831da974c43dd5fb67f5d7) | Eryk Wieliczko | soc: apple: expose retained PMP display and GPU ownership [#400](https://github.com/aurora-silicon/linux/pull/400) | Not applicable — G16; `drivers/pmdomain/apple/pmp-report.c` | V | +| [a5f2ee806da2](https://github.com/aurora-silicon/linux/commit/a5f2ee806da2931b10d69044e4a40609d4924201) | Eryk Wieliczko | thermal: apple: integrate J713 measured-rail GPU budgets [#400](https://github.com/aurora-silicon/linux/pull/400) | Not applicable — G16; `drivers/thermal/Kconfig` | V | +| [52d846f6b8d3](https://github.com/aurora-silicon/linux/commit/52d846f6b8d3664ea6135a660c0b3ece39937209) | Eryk Wieliczko | iommu/apple-dart: adopt J713 display streams with retained ownership [#400](https://github.com/aurora-silicon/linux/pull/400) | Not applicable — G16; `drivers/iommu/Kconfig` | V | +| [24f67337c9d5](https://github.com/aurora-silicon/linux/commit/24f67337c9d586100ca4a4f438e83310644c1903) | Eryk Wieliczko | drm/asahi: add the T8132 G16 firmware backend [#400](https://github.com/aurora-silicon/linux/pull/400) | Not applicable — G16; `drivers/gpu/drm/asahi/Kconfig` | V | +| [fb96882f6cb4](https://github.com/aurora-silicon/linux/commit/fb96882f6cb4ce464fcf3f8b3f27f0c3eba891ed) | Ryan Murray | soc: apple: rtkit-helper: Keep serving buffer requests on T8140 adopted sessions | Not applicable — PERIPH; `drivers/soc/apple/rtkit-helper.c` | V | +| [4d88194af46e](https://github.com/aurora-silicon/linux/commit/4d88194af46e4eec12e57c3e7ff8bbcf7ac5b856) | Ryan Murray | pmdomain: apple: pmgr-pwrstate: Honour apple,inherited-on only on T8132 [#400](https://github.com/aurora-silicon/linux/pull/400) | Not applicable — G16; `drivers/pmdomain/apple/pmgr-pwrstate.c` | V | +| [d14b566cc309](https://github.com/aurora-silicon/linux/commit/d14b566cc309cf6840199c6394dc8f5f75f82f54) | Ryan Murray | drm/asahi: g17: describe the 25G83 barrier and parameter block plainly [#420](https://github.com/aurora-silicon/linux/pull/420) | Not applicable — VERIFY; `drivers/gpu/drm/asahi/g17/buffer/target.rs` | V | +| [f0f00dca4b09](https://github.com/aurora-silicon/linux/commit/f0f00dca4b09c9caf6f379928827e0d10451c66b) | Ryan Murray | drm/asahi: mem: document that page-by-page invalidation is global [#420](https://github.com/aurora-silicon/linux/pull/420) | Not applicable — VERIFY; `drivers/gpu/drm/asahi/mem.rs` | V | +| [8eacef90ff89](https://github.com/aurora-silicon/linux/commit/8eacef90ff8952b6fc75a85cb20abce488bcd4ed) | Erik Bourget | drm/asahi: g16: accept the 8-core J713 loaded 25G83 image [#442](https://github.com/aurora-silicon/linux/pull/442) | Not applicable — G16; `drivers/gpu/drm/asahi/g16/hw.rs` | V | +| [da42c7cf4fc8](https://github.com/aurora-silicon/linux/commit/da42c7cf4fc83e68261f1c238b2a3b870f94a128) | Ryan Murray | drm/asahi: test shared firmware identity normalization [#428](https://github.com/aurora-silicon/linux/pull/428) | Not applicable — VERIFY; `drivers/gpu/drm/asahi/fwid.rs` | V | +| [b11a5e9e8cd8](https://github.com/aurora-silicon/linux/commit/b11a5e9e8cd83d4ac3ac40bc620de254b095c75d) | DjDeveloperr | cpufreq: apple: verify T8140 transitions on the next request [#443](https://github.com/aurora-silicon/linux/pull/443) | Not applicable — LAZY; `drivers/cpufreq/apple-soc-cpufreq.c` | C,I | +| [e2a2d8ecbb24](https://github.com/aurora-silicon/linux/commit/e2a2d8ecbb24146ff61e1649ff503f234e846b30) | DjDeveloperr | cpufreq: apple: verify T8140 fast-switch requests from a timer [#443](https://github.com/aurora-silicon/linux/pull/443) | Not applicable — LAZY; `drivers/cpufreq/apple-soc-cpufreq.c` | C,I | +| [b7ea3ca9de07](https://github.com/aurora-silicon/linux/commit/b7ea3ca9de073d66f0e031013b7853c3e5702479) | DjDeveloperr | cpufreq: apple: settle T8140 lazy requests before suspend [#443](https://github.com/aurora-silicon/linux/pull/443) | Not applicable — LAZY; `drivers/cpufreq/apple-soc-cpufreq.c` | C,I | +| [6f5d208b4ce0](https://github.com/aurora-silicon/linux/commit/6f5d208b4ce01ff8cfea04fa21f14ac9a7cb610c) | DjDeveloperr | cpufreq: apple: apply a lowered T8140 fast die limit at once [#443](https://github.com/aurora-silicon/linux/pull/443) | Not applicable — LAZY; `drivers/cpufreq/apple-soc-cpufreq.c` | P | +| [b884471f5b13](https://github.com/aurora-silicon/linux/commit/b884471f5b13e62de9105bb0f06362fe01663a75) | Ryan Murray | thermal: apple-smc-cpu-thermal: walk the OPP table by die temperature | Not applicable — OLDTHERMAL; `drivers/thermal/apple-smc-cpu-thermal.c` | P | +| [ca1dab3dde89](https://github.com/aurora-silicon/linux/commit/ca1dab3dde89c57cc0b111fd096478241e6565e5) | Ryan Murray | cpufreq: apple-soc: Drop the SMC CPU thermal policy hooks | Not applicable — OLDTHERMAL; `drivers/cpufreq/apple-soc-cpufreq.c` | P | +| [52006c8e0bc5](https://github.com/aurora-silicon/linux/commit/52006c8e0bc5750331643c2f36ebad06ef300079) | Ryan Murray | thermal: Remove the Apple SMC CPU thermal policy [#43](https://github.com/aurora-silicon/linux/pull/43) [#380](https://github.com/aurora-silicon/linux/pull/380) [#470](https://github.com/aurora-silicon/linux/pull/470) | Not applicable — OLDTHERMAL; `drivers/thermal/Kconfig` | P | +| [1ce75ee9e797](https://github.com/aurora-silicon/linux/commit/1ce75ee9e797c296f742abf955764fa00b77e03d) | Ryan Murray | treewide: apple: t8140: Describe observed behaviour, not other implementations | Not applicable — VERIFY; `drivers/media/platform/apple/isp/isp-cam.c` | V | +| [3b0af1da96c2](https://github.com/aurora-silicon/linux/commit/3b0af1da96c28777073ebf99d65f1498a5f9fe94) | Ace | PCI: apple: enable bounded J700 endpoint bootstrap and enumeration | Not applicable — PERIPH; `drivers/pci/controller/Kconfig` | V | +| [e179042ff067](https://github.com/aurora-silicon/linux/commit/e179042ff0673c4b5adc749cb0fc3fcb0f909106) | Acelogic | HID: dockchannel: support firmware-managed M5 trackpad power | Not applicable — OTHERCHIP; `drivers/hid/dockchannel-hid/dockchannel-hid.c` | V | +| [f19bcf1a1e37](https://github.com/aurora-silicon/linux/commit/f19bcf1a1e373fb5bae2c9a5a0153d7820b13645) | Joshua Warren | drm/asahi: stats: Add StatsSnapshot and firmware-stats decoder [#197](https://github.com/aurora-silicon/linux/pull/197) [#380](https://github.com/aurora-silicon/linux/pull/380) [#470](https://github.com/aurora-silicon/linux/pull/470) | Not applicable — STAT; `drivers/gpu/drm/asahi/stats.rs` | T | +| [72d21d654534](https://github.com/aurora-silicon/linux/commit/72d21d654534decaecc58af0f8fc813064e6f500) | Joshua Warren | drm/asahi: Wire StatsSnapshot into StatsChannel and GpuManager [#197](https://github.com/aurora-silicon/linux/pull/197) [#380](https://github.com/aurora-silicon/linux/pull/380) [#470](https://github.com/aurora-silicon/linux/pull/470) | Not applicable — STAT; `drivers/gpu/drm/asahi/channel.rs` | T | +| [e8c789f9f77b](https://github.com/aurora-silicon/linux/commit/e8c789f9f77baf73f21857ca29a527930ed12aab) | Joshua Warren | drm/asahi: Add sysfs shim for agx_stats [#197](https://github.com/aurora-silicon/linux/pull/197) [#380](https://github.com/aurora-silicon/linux/pull/380) [#470](https://github.com/aurora-silicon/linux/pull/470) | Not applicable — STAT; `drivers/gpu/drm/asahi/Makefile` | T | +| [1b02b0b40af3](https://github.com/aurora-silicon/linux/commit/1b02b0b40af3822e16b5842e21964be40a42025f) | Joshua Warren | drm/asahi: Register agx_stats sysfs file on probe and unregister on drop [#197](https://github.com/aurora-silicon/linux/pull/197) [#380](https://github.com/aurora-silicon/linux/pull/380) [#470](https://github.com/aurora-silicon/linux/pull/470) | Not applicable — STAT; `drivers/gpu/drm/asahi/driver.rs` | T | +| [742f54c4855d](https://github.com/aurora-silicon/linux/commit/742f54c4855d0e3b4db54da4663d421f63d1165a) | Joshua Warren | drm/asahi: count completed submissions in agx_stats jobs [#197](https://github.com/aurora-silicon/linux/pull/197) [#380](https://github.com/aurora-silicon/linux/pull/380) [#470](https://github.com/aurora-silicon/linux/pull/470) | Not applicable — STAT; `drivers/gpu/drm/asahi/gpu.rs` | T | +| [bbfb96f309c8](https://github.com/aurora-silicon/linux/commit/bbfb96f309c8dd264540281469bba6c4a48ee789) | Joshua Warren | drm/asahi: link the sysfs shim into the driver module correctly [#197](https://github.com/aurora-silicon/linux/pull/197) [#380](https://github.com/aurora-silicon/linux/pull/380) [#470](https://github.com/aurora-silicon/linux/pull/470) | Not applicable — STAT; `drivers/gpu/drm/asahi/Makefile` | T | +| [01d19b8a4f55](https://github.com/aurora-silicon/linux/commit/01d19b8a4f55bb202b5c9d85a36996a90537602c) | Joshua Warren | drm/asahi: propagate the stats snapshot allocation result [#197](https://github.com/aurora-silicon/linux/pull/197) [#380](https://github.com/aurora-silicon/linux/pull/380) [#470](https://github.com/aurora-silicon/linux/pull/470) | Not applicable — STAT; `drivers/gpu/drm/asahi/gpu.rs` | T | +| [91c24dc11b09](https://github.com/aurora-silicon/linux/commit/91c24dc11b093be4868234ac2e00eed670d1e741) | Joshua Warren | drm/asahi: decode the stats messages where StatsMsg is nameable [#197](https://github.com/aurora-silicon/linux/pull/197) [#380](https://github.com/aurora-silicon/linux/pull/380) [#470](https://github.com/aurora-silicon/linux/pull/470) | Not applicable — STAT; `drivers/gpu/drm/asahi/channel.rs` | T | +| [dbc8b48aeb06](https://github.com/aurora-silicon/linux/commit/dbc8b48aeb06f85c9e7f9b63be9db7afef074580) | Joshua Warren | drm/asahi: qualify the versioned StatsMsg variant patterns [#197](https://github.com/aurora-silicon/linux/pull/197) [#380](https://github.com/aurora-silicon/linux/pull/380) [#470](https://github.com/aurora-silicon/linux/pull/470) | Not applicable — STAT; `drivers/gpu/drm/asahi/channel.rs` | T | +| [4189501b643f](https://github.com/aurora-silicon/linux/commit/4189501b643f404faa0ea37fc3bb379226300aa9) | Joshua Warren | drm/asahi: fix the agx_stats readout layout and the off-arm string [#197](https://github.com/aurora-silicon/linux/pull/197) [#380](https://github.com/aurora-silicon/linux/pull/380) [#470](https://github.com/aurora-silicon/linux/pull/470) | Not applicable — STAT; `drivers/gpu/drm/asahi/driver.rs` | T | +| [0b923fb29c84](https://github.com/aurora-silicon/linux/commit/0b923fb29c840b9bb675ba8ad9e81c513903b516) | Joshua Warren | drm/asahi: integrate utilization-weighted time into agx_stats busy_ns [#197](https://github.com/aurora-silicon/linux/pull/197) [#380](https://github.com/aurora-silicon/linux/pull/380) [#470](https://github.com/aurora-silicon/linux/pull/470) | Not applicable — STAT; `drivers/gpu/drm/asahi/channel.rs` | T | +| [9250872b1f33](https://github.com/aurora-silicon/linux/commit/9250872b1f33f357c01ccd3bf79be7835fc5c982) | Joshua Warren | drm/asahi: convert stats timestamps from base-clock ticks to ns [#197](https://github.com/aurora-silicon/linux/pull/197) [#380](https://github.com/aurora-silicon/linux/pull/380) [#470](https://github.com/aurora-silicon/linux/pull/470) | Not applicable — STAT; `drivers/gpu/drm/asahi/channel.rs` | T | +| [993288b37ec4](https://github.com/aurora-silicon/linux/commit/993288b37ec4368bbf2debbbd73124d8a2652410) | Joshua Warren | drm/asahi: split the tick window to avoid 128-bit division [#197](https://github.com/aurora-silicon/linux/pull/197) [#380](https://github.com/aurora-silicon/linux/pull/380) [#470](https://github.com/aurora-silicon/linux/pull/470) | Not applicable — STAT; `drivers/gpu/drm/asahi/channel.rs` | T | +| [9aebc2952fb5](https://github.com/aurora-silicon/linux/commit/9aebc2952fb51f6b0b9c03222109fe7f0ac107c2) | Ryan Murray | arm64: dts: apple: retain the T8140 P-cluster domain cells [#179](https://github.com/aurora-silicon/linux/pull/179) | Not applicable — BOARD | B | +| [e3163ca622d6](https://github.com/aurora-silicon/linux/commit/e3163ca622d6c55979d963929edbdf47f2c4c38c) | Ryan Murray | soc: apple: expose the T8140 PMP DVFS report for diagnosis [#182](https://github.com/aurora-silicon/linux/pull/182) [#380](https://github.com/aurora-silicon/linux/pull/380) [#470](https://github.com/aurora-silicon/linux/pull/470) | Not applicable — RAWDIAG; `drivers/pmdomain/apple/pmp-report.c` | O | +| [14e101ee2b8b](https://github.com/aurora-silicon/linux/commit/14e101ee2b8b599cf627015368e16ff8b3d2b95f) | Ryan Murray | cpufreq: apple: retain the unmanaged T8152 P-state ceiling | Not applicable — OTHERCHIP; `drivers/cpufreq/apple-soc-cpufreq.c` | V | +| [19d6664ea33a](https://github.com/aurora-silicon/linux/commit/19d6664ea33affcceef42d5deef488ce1ba7d50d) | Ryan Murray | arm64: dts: apple: retain a single J700 PMP alias | Not applicable — BOARD | B | +| [98038492dc48](https://github.com/aurora-silicon/linux/commit/98038492dc481d784c76fb2c52f3dd030dfad8f5) | Ryan Murray | drm: apple: recognize the T8140 external binding in the display master | Not applicable — BOARD; `drivers/gpu/drm/apple/apple_drv.c` | B | +| [78fc11047efa](https://github.com/aurora-silicon/linux/commit/78fc11047efa814a238113c46b4bb07aa7d641fb) | Ryan Murray | arm64: dts: apple: give the external J700 trial its firmware DMA window | Not applicable — PERIPH | V | +| [bc3b8aaa560f](https://github.com/aurora-silicon/linux/commit/bc3b8aaa560f23e069a7ea2d774645ea5fec39cc) | Ryan Murray | arm64: dts: apple: select the second external DCP source on J700 | Not applicable — PERIPH | V | +| [d08fd52e1619](https://github.com/aurora-silicon/linux/commit/d08fd52e161953a481a464716dd68aae85338044) | DjDeveloperr | drm/apple: enable the validated J700 external display profile by default | Not applicable — BOARD; `drivers/gpu/drm/apple/dcp-external-bw.h` | B | +| [01c8fe5e98f4](https://github.com/aurora-silicon/linux/commit/01c8fe5e98f4ab8805957ca3206d6d46a65b62e7) | Ryan Murray | soc: apple: aop: Do not overrun unread entries in the transmit ring [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — PERIPH; `drivers/soc/apple/aop.rs` | V | +| [02694d65655b](https://github.com/aurora-silicon/linux/commit/02694d65655baae0536d47beef4f0356145069f4) | Ryan Murray | spi: apple-qspi: Resume the controller even if its reset fails [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — PERIPH; `drivers/spi/spi-apple-qspi.c` | V | +| [033fa5e3c095](https://github.com/aurora-silicon/linux/commit/033fa5e3c0959f772e8464a25b8b93773f39b5b9) | Ryan Murray | soc: apple: aop: Accept a transmit entry at a write pointer of zero [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — PERIPH; `drivers/soc/apple/aop_ring.rs` | V | +| [0790f8a546fe](https://github.com/aurora-silicon/linux/commit/0790f8a546fea7ae46871d857643568c1b3c0c64) | Ryan Murray | hwmon: macsmc: drop the check for the undefined apple,cpu-thermal-policy [#317](https://github.com/aurora-silicon/linux/pull/317) | Not applicable — PERIPH; `drivers/hwmon/macsmc-hwmon.c` | V | +| [0943ae25f9bd](https://github.com/aurora-silicon/linux/commit/0943ae25f9bd82917a7a4813962d624d22056fd3) | Ryan Murray | HID: dockchannel-hid: Reject unaligned packet lengths before checksumming [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — PERIPH; `drivers/hid/dockchannel-hid/dockchannel-hid-parser-test.c` | V | +| [0c55425a8157](https://github.com/aurora-silicon/linux/commit/0c55425a8157326aa0bafd20a1c30a0f34af8650) | Ryan Murray | dt-bindings: gpu: apple,agx: Require three cells in apple,firmware-version [#420](https://github.com/aurora-silicon/linux/pull/420) | Not applicable — VERIFY | V | +| [152cc6b4d737](https://github.com/aurora-silicon/linux/commit/152cc6b4d737c3523b56d1c4b0b4cd3928e514b5) | Ryan Murray | spi: apple-qspi: Do not print a banner on probe [#314](https://github.com/aurora-silicon/linux/pull/314) | Not applicable — PERIPH; `drivers/spi/spi-apple-qspi.c` | V | +| [1baee970e0a9](https://github.com/aurora-silicon/linux/commit/1baee970e0a9d5bf0343f72eed61badfc5918159) | Jonathan Goyvaerts | dt-bindings: mailbox: apple,ascwrap-v6: Add the T6040 [#420](https://github.com/aurora-silicon/linux/pull/420) | Not applicable — VERIFY | V | +| [1df5bd529669](https://github.com/aurora-silicon/linux/commit/1df5bd529669987e8804b6ab38dbd1d5175f508d) | Ryan Murray | HID: dockchannel-hid: Test MTP message bounds with KUnit [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — VERIFY; `drivers/hid/dockchannel-hid/Kconfig` | V | +| [1f7383d5a281](https://github.com/aurora-silicon/linux/commit/1f7383d5a2811b99a9f2950581ec392837bfc469) | DjDeveloperr | rust: check generated layouts and provide corrected private bindgen [#171](https://github.com/aurora-silicon/linux/pull/171) | Not applicable — VERIFY | V | +| [232faf36664c](https://github.com/aurora-silicon/linux/commit/232faf36664c7e23aa9950bc3019dbf6ba534ee8) | Ryan Murray | HID: dockchannel-hid: Keep parser tests in the subsystem menu [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — PERIPH; `drivers/hid/dockchannel-hid/Kconfig` | V | +| [2d5c7b6bc705](https://github.com/aurora-silicon/linux/commit/2d5c7b6bc705f0ba8eef8f3d1ea23a53d4248eba) | Ryan Murray | ASoC: apple: t8140-aop-audio: Refuse card names that do not fit [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — PERIPH | V | +| [3006ca1a3835](https://github.com/aurora-silicon/linux/commit/3006ca1a383525a092ad1cbe9c268786292a32de) | Ryan Murray | mfd: macsmc: Test key and fixed-width readers with KUnit [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — VERIFY; `drivers/mfd/Kconfig` | V | +| [3024185f2efa](https://github.com/aurora-silicon/linux/commit/3024185f2efa34c4ea8ac9eebebf06b921795f0b), [aaea0b3a4fcf](https://github.com/aurora-silicon/linux/commit/aaea0b3a4fcf5cb371b735c6b595a2919a6a4d07) | DjDeveloperr | dt-bindings: power: add Apple PMC voter interface [#212](https://github.com/aurora-silicon/linux/pull/212) [#219](https://github.com/aurora-silicon/linux/pull/219) | Not applicable — VERIFY | V | +| [3132675b256e](https://github.com/aurora-silicon/linux/commit/3132675b256e07ad782280c42c853c63f6d18ef1) | Ryan Murray | selftests: apple-dcp: Run the queue recovery control through the RX drain [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — VERIFY | V | +| [315f6dc9944d](https://github.com/aurora-silicon/linux/commit/315f6dc9944de65990476dae34079d3e1313d605) | Chris Kearney | soc: apple: mailbox: match the T8122 GPU mailbox without the v4 fallback | Not applicable — G15; `drivers/soc/apple/mailbox.c` | V | +| [3407efe83df6](https://github.com/aurora-silicon/linux/commit/3407efe83df680534e10c0e66ffa36b75cae69ba) | Jonathan Goyvaerts | dt-bindings: soc: apple,t8140-mcc: Add the T6040 MCC [#385](https://github.com/aurora-silicon/linux/pull/385) | Not applicable — VERIFY | V | +| [35d6b26e5c9d](https://github.com/aurora-silicon/linux/commit/35d6b26e5c9d8bf22ce6284bf0594084ffd2dcdf) | Ryan Murray | soc: apple: aop: Test that a wrapped entry is refused below the read pointer [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — VERIFY; `drivers/soc/apple/aop_ring.rs` | V | +| [4b8d29e13da6](https://github.com/aurora-silicon/linux/commit/4b8d29e13da638bf7ba7f532844c5e368505bf20) | Jonathan Goyvaerts | dt-bindings: gpu: apple,agx: allow a fw-text region on the T6040 [#428](https://github.com/aurora-silicon/linux/pull/428) | Not applicable — VERIFY | V | +| [4c5aad430f62](https://github.com/aurora-silicon/linux/commit/4c5aad430f6283d1d6b3b961a87d1629b397f22f) | DjDeveloperr | dt-bindings: mailbox: apple,mailbox: Add the T8140 GPU mailboxes [#171](https://github.com/aurora-silicon/linux/pull/171) | Not applicable — VERIFY | V | +| [543ac70dce46](https://github.com/aurora-silicon/linux/commit/543ac70dce46b749f1a446cb80d4e63cf08949cd) | Ryan Murray | drm: apple: parser: add KUnit tests for malformed dictionaries [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — VERIFY; `drivers/gpu/drm/apple/Makefile` | V | +| [5b62d0c81387](https://github.com/aurora-silicon/linux/commit/5b62d0c81387e8d0d68f2a88244eeb7a618f9ec1) | Ryan Murray | mfd: macsmc: Preserve the key output on lookup failure [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — PERIPH; `drivers/mfd/macsmc.c` | V | +| [5e03e15dae17](https://github.com/aurora-silicon/linux/commit/5e03e15dae1740326b41df4777a5ee4b7502c02e), [94f215eaa642](https://github.com/aurora-silicon/linux/commit/94f215eaa6424e8f077f1447e78e35b3ebeb6542) | DjDeveloperr | dt-bindings: power: apple,t8140-pmp-report: describe report entries [#212](https://github.com/aurora-silicon/linux/pull/212) [#219](https://github.com/aurora-silicon/linux/pull/219) | Not applicable — VERIFY | V | +| [66db357bd614](https://github.com/aurora-silicon/linux/commit/66db357bd614c7f8e25dab080574c31dab6c0ed2) | Ryan Murray | tools/asahi: measure CPU cost of small render submissions [#201](https://github.com/aurora-silicon/linux/pull/201) | Not applicable — VERIFY | V | +| [6e2835ae7bd6](https://github.com/aurora-silicon/linux/commit/6e2835ae7bd6d16570464106bdfb740b6fed27c4) | Naeem Malik | soc: apple: mailbox: match the T6031 GPU mailbox without the v4 fallback | Not applicable — G15; `drivers/soc/apple/mailbox.c` | V | +| [712530b9753f](https://github.com/aurora-silicon/linux/commit/712530b9753fd8692b31382b3e9c6aaaa720ffe0) | Ryan Murray | gpio: macsmc: tidy the J700 pcIO helpers [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — PERIPH; `drivers/gpio/gpio-macsmc.c` | V | +| [7a6e327251cc](https://github.com/aurora-silicon/linux/commit/7a6e327251cc51d0a483483f0b81dda99a811791) | Ryan Murray | soc: apple: aop: Validate the receive ring before taking entries [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — PERIPH; `drivers/soc/apple/aop.rs` | V | +| [807cb8976697](https://github.com/aurora-silicon/linux/commit/807cb8976697ff272433278b272444ced7e91d57) | Ryan Murray | hwmon: macsmc: account for temperature channels in one helper [#317](https://github.com/aurora-silicon/linux/pull/317) | Not applicable — PERIPH; `drivers/hwmon/macsmc-hwmon.c` | V | +| [83039e319faf](https://github.com/aurora-silicon/linux/commit/83039e319faf3e49ceabc424c7c69180ac997dfd) | Ryan Murray | dt-bindings: apple: use the canonical SPDX form in PMP, PMC and report bindings [#317](https://github.com/aurora-silicon/linux/pull/317) | Not applicable — VERIFY | V | +| [87314d48cc08](https://github.com/aurora-silicon/linux/commit/87314d48cc080bcf535291dc636f1ec6164a06f9) | Ryan Murray | drm/apple: Test firmware message bounds with KUnit [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — VERIFY; `drivers/gpu/drm/apple/Kconfig` | V | +| [910327707f85](https://github.com/aurora-silicon/linux/commit/910327707f852c060b5ada81e64a2a4563cb64b4) | Ryan Murray | ASoC: apple: Test T8140 AOP producer reports with KUnit [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — VERIFY | V | +| [95b26591a1c4](https://github.com/aurora-silicon/linux/commit/95b26591a1c4b446656c3bc8a257a17792917a78) | Ryan Murray | spi: apple: Reword the hardware CS delay comment [#314](https://github.com/aurora-silicon/linux/pull/314) | Not applicable — VERIFY; `drivers/spi/spi-apple.c` | V | +| [966e777fd568](https://github.com/aurora-silicon/linux/commit/966e777fd568565b88c5f8ba4acb36463ae31e47) | Ryan Murray | MAINTAINERS: keep T8140 PMP coverage under Aurora [#179](https://github.com/aurora-silicon/linux/pull/179) | Not applicable — VERIFY | V | +| [a1f9aabbe6a9](https://github.com/aurora-silicon/linux/commit/a1f9aabbe6a9771a4dbf3259bc808713f0daac46) | Ryan Murray | dt-bindings: apple: describe PMP fast-die reporting on T8140 [#179](https://github.com/aurora-silicon/linux/pull/179) | Not applicable — VERIFY | V | +| [a239464d233d](https://github.com/aurora-silicon/linux/commit/a239464d233d2e94b89dea159f298b70e015b6a6) | Ryan Murray | kunit: Add an Apple firmware parser test configuration [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — VERIFY | V | +| [a27007e6ace3](https://github.com/aurora-silicon/linux/commit/a27007e6ace3815d11c340c25a3ee90cfb2981b0) | Ryan Murray | HID: dockchannel: Log routine interface set-up at debug level [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — PERIPH; `drivers/hid/dockchannel-hid/dockchannel-hid.c` | V | +| [a494d618f5a9](https://github.com/aurora-silicon/linux/commit/a494d618f5a9af40ae466166176ae36476b4638a) | Ryan Murray | mfd: macsmc: Hold the mutex while decoding key metadata [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — PERIPH; `drivers/mfd/macsmc.c` | V | +| [b8f892b79561](https://github.com/aurora-silicon/linux/commit/b8f892b79561ea085c0ab2e44368f873f15b9183) | Ryan Murray | soc: apple: aop: Decode ring entry headers without pointer casts [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — PERIPH; `drivers/soc/apple/aop.rs` | V | +| [ba6eb20e2ed9](https://github.com/aurora-silicon/linux/commit/ba6eb20e2ed9065a2925d0f16ae030acfaeceefe) | Ryan Murray | soc: apple: mailbox: Describe the ASCWrap v6 CPU start and stop in hardware terms | Not applicable — VERIFY; `drivers/soc/apple/mailbox.c` | V | +| [bc7394673cbe](https://github.com/aurora-silicon/linux/commit/bc7394673cbed4ef22971a49926be6070a7312be) | DjDeveloperr | tools: preserve bindgen recipe validation across Python modes [#171](https://github.com/aurora-silicon/linux/pull/171) | Not applicable — VERIFY | V | +| [bd7739d6232e](https://github.com/aurora-silicon/linux/commit/bd7739d6232e818ef7cf30acbcc50d24723e061c) | Ryan Murray | mfd: macsmc: Reject short fixed-width replies [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — PERIPH; `drivers/mfd/macsmc.c` | V | +| [c49ab96deb3c](https://github.com/aurora-silicon/linux/commit/c49ab96deb3c378015d1db27bf2122c837fd3bc0) | DjDeveloperr | dt-bindings: gpu: apple,agx: Add apple,agx-t8140 [#171](https://github.com/aurora-silicon/linux/pull/171) | Not applicable — VERIFY | V | +| [c9c779d6a2ba](https://github.com/aurora-silicon/linux/commit/c9c779d6a2bae406a9d48b7f733b16fd253730c7) | Jonathan Goyvaerts | dt-bindings: soc: apple,pmp: Add the T6040 PMP [#374](https://github.com/aurora-silicon/linux/pull/374) | Not applicable — VERIFY | V | +| [cbd157ef2a12](https://github.com/aurora-silicon/linux/commit/cbd157ef2a12a46e3caf5367de912a525d8d4874) | Ryan Murray | hwmon: macsmc: keep the channel of a temperature sensor that fails to set up [#317](https://github.com/aurora-silicon/linux/pull/317) | Not applicable — PERIPH; `drivers/hwmon/Kconfig` | V | +| [cf4ce5f41f4b](https://github.com/aurora-silicon/linux/commit/cf4ce5f41f4b86c4084b49ec6585377e16fd53b7) | Chris Kearney | thunderbolt, phy, soc: apple: T6030 USB4/Thunderbolt, Type-C and DP tunnel support | Not applicable — G15; `drivers/iommu/of_iommu.c` | V | +| [d36196335065](https://github.com/aurora-silicon/linux/commit/d36196335065346ef57a323e483002151670320c) | Ryan Murray | arm64: dts: apple: t6040-j616s: Keep the GPU disabled [#420](https://github.com/aurora-silicon/linux/pull/420) | Not applicable — SUPERSEDED | B | +| [d717a601430a](https://github.com/aurora-silicon/linux/commit/d717a601430aea6711ffc7486f5edb16ca7e7f66) | Jonathan Goyvaerts | dt-bindings: gpu: apple,agx: Add apple,agx-t6040 [#420](https://github.com/aurora-silicon/linux/pull/420) | Not applicable — VERIFY | V | +| [d90daf04de84](https://github.com/aurora-silicon/linux/commit/d90daf04de8431f70eed8b590a532f38a6249095) | Ryan Murray | media: apple: isp: Do not trust the batch count when a batch fails [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — PERIPH; `drivers/media/platform/apple/isp/isp-v4l2.c` | V | +| [d9aface7ea85](https://github.com/aurora-silicon/linux/commit/d9aface7ea8533965e33476631c8dbc44dfeb439) | Ryan Murray | HID: dockchannel: Build the protocol KUnit test only with the driver [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — PERIPH; `drivers/hid/dockchannel-hid/Kconfig` | V | +| [e65283c42678](https://github.com/aurora-silicon/linux/commit/e65283c426783b12298f72896ef6f0dc33c4222c) | Ryan Murray | ASoC: apple: t8140-aop-audio: Let power request sequences wrap [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — PERIPH | V | +| [e666e50ccfe8](https://github.com/aurora-silicon/linux/commit/e666e50ccfe8c58e8fa72bdf7ef6bedffec6551a) | Ryan Murray | rtc: macsmc: correct the comment on the whole-second offset [#317](https://github.com/aurora-silicon/linux/pull/317) | Not applicable — PERIPH; `drivers/rtc/rtc-macsmc.c` | V | +| [e99a6adbeba7](https://github.com/aurora-silicon/linux/commit/e99a6adbeba77b7ed032ef61f864afb3da8fe3a9) | Ryan Murray | dt-bindings: hwmon: apple,smc-hwmon: drop apple,cpu-thermal-policy [#317](https://github.com/aurora-silicon/linux/pull/317) | Not applicable — VERIFY | V | +| [ec34fb1f2835](https://github.com/aurora-silicon/linux/commit/ec34fb1f2835b291f978bd091ed73347cba7ceae) | Ryan Murray | mfd: macsmc: Reject out-of-range key indices [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — PERIPH; `drivers/mfd/macsmc.c` | V | +| [ed0925cdf091](https://github.com/aurora-silicon/linux/commit/ed0925cdf091083ba1efee97bbb288e9c331f812) | Ryan Murray | spi: apple-qspi: Restore the controller configuration after system sleep [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — PERIPH; `drivers/spi/spi-apple-qspi.c` | V | +| [ee6f48ce60bd](https://github.com/aurora-silicon/linux/commit/ee6f48ce60bdf68d596c4bedd31e35f55cd6bb03) | Ryan Murray | MAINTAINERS: Cover Apple firmware parser tests [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — VERIFY | V | +| [f3324e0de0e2](https://github.com/aurora-silicon/linux/commit/f3324e0de0e26bd43df402001c2e3f0f16821520) | Jonathan Goyvaerts | dt-bindings: power: apple,pmc: Add the T6040 PMC and CLPC [#385](https://github.com/aurora-silicon/linux/pull/385) | Not applicable — VERIFY | V | +| [f52e01d6925d](https://github.com/aurora-silicon/linux/commit/f52e01d6925de5022176c5038ff13cc9ad17559b) | Ryan Murray | dt-bindings: gpu: apple,agx: Match the T8140 register layout [#314](https://github.com/aurora-silicon/linux/pull/314) | Not applicable — VERIFY | V | +| [fdd138bbc722](https://github.com/aurora-silicon/linux/commit/fdd138bbc7228b8b1a232268e2a02cc126b24d95) | Ryan Murray | hwmon: macsmc: move the module help text back to its option [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — PERIPH; `drivers/hwmon/Kconfig` | V | +| [fec5e40ebade](https://github.com/aurora-silicon/linux/commit/fec5e40ebade9f7cf3684acfbb9d603d5b82127a) | Jonathan Goyvaerts | dt-bindings: power: apple,t8140-pmp-report: Add the T6040 aperture [#374](https://github.com/aurora-silicon/linux/pull/374) | Not applicable — VERIFY | V | +| [ff3ebdbad58c](https://github.com/aurora-silicon/linux/commit/ff3ebdbad58c1c50dadbf9cecd7f67aada516dc1) | Ryan Murray | nvme-apple: Reword two post-M4 handoff comments [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — VERIFY; `drivers/nvme/host/apple.c` | V | + +### Merge/provenance records + +Parent mechanisms are listed above; no isolated speedup is assigned here. + +| Commits / encountered aliases | Author | Improvement / record | T6040 after ports — evidence key and changed source | Measurement | +| --- | --- | --- | --- | --- | +| [00463d1d4031](https://github.com/aurora-silicon/linux/commit/00463d1d40317155246a13573aeab60ff773b51b), [f566e6098c67](https://github.com/aurora-silicon/linux/commit/f566e6098c676566b02a191eb58e5783fa7bf0b6) | Janne Grunau | Merge branch 'bits/010-soc' into asahi-wip | Not applicable — MERGE | V | +| [80390ef034fc](https://github.com/aurora-silicon/linux/commit/80390ef034fcfaf212674bc53b0a91e596409bcf), [5e35a79dac7f](https://github.com/aurora-silicon/linux/commit/5e35a79dac7f0958af6bd3ae9ea4517d22769cb2) | Janne Grunau | Merge branch 'bits/020-dart' into asahi-wip | Not applicable — MERGE | V | +| [4eea6c6d47ba](https://github.com/aurora-silicon/linux/commit/4eea6c6d47bab3a334b9c1633279c4392e2a4de7), [57710743dc8c](https://github.com/aurora-silicon/linux/commit/57710743dc8c0e9286115f38ad19a298cd36cc50) | Janne Grunau | Merge branch 'bits/030-misc' into asahi-wip | Not applicable — MERGE | V | +| [59f7e8be4e3b](https://github.com/aurora-silicon/linux/commit/59f7e8be4e3b693a96a1dd3b2355aa65dd784b0e), [466efd223bbf](https://github.com/aurora-silicon/linux/commit/466efd223bbf43f9e0f4f7065c67d6d4e97fc04c) | Janne Grunau | Merge branch 'bits/090-spi-hid' into asahi-wip | Not applicable — MERGE | V | +| [2871ed01752e](https://github.com/aurora-silicon/linux/commit/2871ed01752ef74924608f23b5edd7927ae4b71c), [f55090a2d12a](https://github.com/aurora-silicon/linux/commit/f55090a2d12a6fd3dad485a7621392b8f508f8a8) | Janne Grunau | Merge branch 'bits/190-rust' into asahi-wip | Not applicable — MERGE | V | +| [cd36abb1e1d2](https://github.com/aurora-silicon/linux/commit/cd36abb1e1d2bcead322aedaed3cab7cbbab5d4b), [3f3dfd6ef171](https://github.com/aurora-silicon/linux/commit/3f3dfd6ef171f4c3801e1bd8b7c880774e17f1ad) | Janne Grunau | Merge branch 'bits/210-gpu' into asahi-wip | Not applicable — MERGE | V | +| [d6c7409e354f](https://github.com/aurora-silicon/linux/commit/d6c7409e354fee96c74a0aa68c5cbd0cdc0b8d92), [39b401626267](https://github.com/aurora-silicon/linux/commit/39b4016262672c0f87d7e5af835f42b9632dcc6c) | Janne Grunau | Merge branch 'bits/250-aop' into asahi-wip | Not applicable — MERGE | V | +| [072195f87df8](https://github.com/aurora-silicon/linux/commit/072195f87df8ca94580cf5ca0d9a801e5bd217e4), [11ee430b3cf2](https://github.com/aurora-silicon/linux/commit/11ee430b3cf20a6611bea0462335f0ae975a40d7) | Janne Grunau | Merge branch 'bits/500-backports' into asahi-wip | Not applicable — MERGE | V | +| [fe8324184dd8](https://github.com/aurora-silicon/linux/commit/fe8324184dd8c6d3b09a532a9cb6a533c561a324) | Ryan Murray | Merge tag 'v7.3' into unified-7.3/base | Not applicable — MERGE | V | +| [cb8e5e495b88](https://github.com/aurora-silicon/linux/commit/cb8e5e495b88f638c22782136a8721a4f26f4346) | Ryan Murray | Merge branch 'unified/bits/010-soc' into rebase-wip/7.2.10-unified | Not applicable — MERGE | V | +| [6de20e534e47](https://github.com/aurora-silicon/linux/commit/6de20e534e473dd9efd9e14c727883df6036d60b) | Ryan Murray | Merge branch 'unified/bits/020-dart' into rebase-wip/7.2.10-unified | Not applicable — MERGE | V | +| [15fe1cbe72df](https://github.com/aurora-silicon/linux/commit/15fe1cbe72df8d8eb79b2dec500e58bbc5f57647) | Ryan Murray | Merge branch 'unified/bits/060-power-thermal' into rebase-wip/7.2.10-unified | Not applicable — MERGE | V | +| [939cccf33380](https://github.com/aurora-silicon/linux/commit/939cccf333802c32a319f0ddb4b0d01c85aab990) | Ryan Murray | Merge branch 'unified/bits/070-audio' into rebase-wip/7.2.10-unified | Not applicable — MERGE | V | +| [2409e9da1efb](https://github.com/aurora-silicon/linux/commit/2409e9da1efb9494fc9437ce9f3bd909c0f7dd36) | Ryan Murray | Merge branch 'unified/bits/100-sep' into rebase-wip/7.2.10-unified | Not applicable — MERGE | V | +| [2bc8aa67f178](https://github.com/aurora-silicon/linux/commit/2bc8aa67f17817c22c6bacaecd7ecf4df8e8c63d) | Ryan Murray | Merge branch 'unified/bits/200-dcp' into rebase-wip/7.2.10-unified | Not applicable — MERGE | V | +| [c00586c09e2b](https://github.com/aurora-silicon/linux/commit/c00586c09e2bab26f92ddd5b943cbcc66c047ff5) | Ryan Murray | Merge branch 'unified/bits/210-gpu' into rebase-wip/7.2.10-unified | Not applicable — MERGE | V | +| [14ca49f98175](https://github.com/aurora-silicon/linux/commit/14ca49f98175b54c37fa16af766e11f163d3a80f) | Ryan Murray | Merge branch 'unified-7.3/bits/010-soc' into unified-7.3/canonical | Not applicable — MERGE | V | +| [665a5ca84508](https://github.com/aurora-silicon/linux/commit/665a5ca845081049335f69d0bf27c66c90898b7b) | Ryan Murray | Merge branch 'unified-7.3/bits/020-dart' into unified-7.3/canonical | Not applicable — MERGE | V | +| [c217f4f47bf0](https://github.com/aurora-silicon/linux/commit/c217f4f47bf003c8fb39400d1af8179b552c497e) | Ryan Murray | Merge branch 'unified-7.3/bits/060-power-thermal' into unified-7.3/canonical | Not applicable — MERGE | V | +| [5bdd6c5b2c80](https://github.com/aurora-silicon/linux/commit/5bdd6c5b2c80a6f16c8835a57a5d56d26af67839) | Ryan Murray | Merge branch 'unified-7.3/bits/070-audio' into unified-7.3/canonical | Not applicable — MERGE | V | +| [f3c8cf25dd5d](https://github.com/aurora-silicon/linux/commit/f3c8cf25dd5d01f3871267c1b77df343dbef045d) | Ryan Murray | Merge branch 'unified-7.3/bits/100-sep' into unified-7.3/canonical | Not applicable — MERGE | V | +| [c15790e20e01](https://github.com/aurora-silicon/linux/commit/c15790e20e01e8562e0b54a00bee8a8ef9ded84b) | Ryan Murray | Merge branch 'unified-7.3/bits/200-dcp' into unified-7.3/canonical | Not applicable — MERGE | V | +| [ea36d4240e40](https://github.com/aurora-silicon/linux/commit/ea36d4240e40fc7607d568a8fa56f1fa5cf01c03) | Ryan Murray | Merge branch 'unified-7.3/bits/210-gpu' into unified-7.3/canonical | Not applicable — MERGE | V | +| [94141d397eae](https://github.com/aurora-silicon/linux/commit/94141d397eaede2ba95eab7279f4bc18f75bb9d1) | Ryan | Merge pull request #374 from jgoyvaerts/m4pro/pmp-t6041 | Not applicable — MERGE | V | +| [4e5eea54ce7e](https://github.com/aurora-silicon/linux/commit/4e5eea54ce7e1c9d21867d81c5e5672e1cbd1c34) | Ryan | Merge pull request #368 from jgoyvaerts/m4pro/small-fixes | Not applicable — MERGE | V | +| [815438428a9e](https://github.com/aurora-silicon/linux/commit/815438428a9e45f0facbb10c4f9bd77047ef7282) | Ryan | Merge pull request #385 from jgoyvaerts/m4pro/pmc-clpc-mcc-t6040 | Not applicable — MERGE | V | +| [49273fd6921c](https://github.com/aurora-silicon/linux/commit/49273fd6921c9b39101cd1ee8c8d2f2ca0153440) | Ryan | Merge pull request #386 from jgoyvaerts/m4pro/apple-dart-t6040 | Not applicable — MERGE | V | +| [50a912fe81fc](https://github.com/aurora-silicon/linux/commit/50a912fe81fcdbd770d231db8eda856a1678c194) | Ryan | Merge pull request #391 from jgoyvaerts/m4pro/g17-generic | Not applicable — MERGE | V | +| [311471f73e99](https://github.com/aurora-silicon/linux/commit/311471f73e9919764bfb24acfe7eeb7c5230795a) | Ryan | Merge pull request #402 from aurora-silicon/j616s/pmp-ep7-20261024 | Not applicable — MERGE | V | +| [1aa2857509d5](https://github.com/aurora-silicon/linux/commit/1aa2857509d50f7f787541280aba7b274721919c) | Ryan | Merge pull request #403 from aurora-silicon/j616s/usb-20261023 | Not applicable — MERGE | V | +| [04231230fd76](https://github.com/aurora-silicon/linux/commit/04231230fd76fadd12a8b44707159f37f0e498b1), [e9a9b647d0f3](https://github.com/aurora-silicon/linux/commit/e9a9b647d0f36e222f16de732f78f2d77ea2d661), [b9a73d410ff4](https://github.com/aurora-silicon/linux/commit/b9a73d410ff48c65624c9283c634b4d06f4dfc6a), [4433181e4eb6](https://github.com/aurora-silicon/linux/commit/4433181e4eb649033f07751001fbb72621752647) | Jonathan Goyvaerts, Ryan Murray | Merge integration/aurora into the T6040 GPU series [#420](https://github.com/aurora-silicon/linux/pull/420) | Not applicable — MERGE | V | +| [2a868cf22d90](https://github.com/aurora-silicon/linux/commit/2a868cf22d90f0de72b2ad3c339b93460fdb9927) | Ryan | Merge pull request #413 from jgoyvaerts/m4pro/g17-robustness | Not applicable — MERGE | V | +| [2e9e457c922d](https://github.com/aurora-silicon/linux/commit/2e9e457c922d799dcc27975ff6f5d114d2719f3f) | Ryan | Merge pull request #418 from aurora-silicon/ryan/g17-innocent-clients-413 | Not applicable — MERGE | V | +| [127df1be31af](https://github.com/aurora-silicon/linux/commit/127df1be31af90613079dcedaeaf0a2aa9ae7d10) | Ryan Murray | Merge integration/aurora into aurora-wip-sync-177 | Not applicable — MERGE | V | +| [e5dad6df06f6](https://github.com/aurora-silicon/linux/commit/e5dad6df06f66b944037770e81e6a481a0b8302f), [d1b62dc403c6](https://github.com/aurora-silicon/linux/commit/d1b62dc403c6f9ed534850f204f069befca149f1) | Ryan Murray | Merge integration/aurora into m4pro/g17-t6040-text-identity [#428](https://github.com/aurora-silicon/linux/pull/428) | Not applicable — MERGE | V | +| [a1b269f79e35](https://github.com/aurora-silicon/linux/commit/a1b269f79e35844660892b9779adb2f68abc38b7) | Ryan | Merge pull request #395: sync omacom #177 (G15, T6030 dock, platform fixes) onto 7.3 | Not applicable — MERGE | V | +| [8bc8f12e95ed](https://github.com/aurora-silicon/linux/commit/8bc8f12e95edbbd5b7d5431973eb1f08c872e87c) | Ryan | Merge pull request #427 from jgoyvaerts/m4pro/asahi-pgtable-checked-borrow | Not applicable — MERGE | V | +| [18012befd69c](https://github.com/aurora-silicon/linux/commit/18012befd69ceed9892c28291132ebd4c05a6076) | Ryan | Merge pull request #428 from jgoyvaerts/m4pro/g17-t6040-text-identity | Not applicable — MERGE | V | +| [41ef596ab373](https://github.com/aurora-silicon/linux/commit/41ef596ab3731ef695fcce2fdfb666bc47da21b7) | Dj | Merge pull request #443 from aurora-silicon/fix/t8140-cpufreq-lazy-verify-20261025 | Not applicable — MERGE | V | +| [af80fbe9094b](https://github.com/aurora-silicon/linux/commit/af80fbe9094b631c5d734883bd259ad0925e88ba) | Dj | Merge pull request #447 from aurora-silicon/fix/aic2-effective-affinity-20261025 | Not applicable — MERGE | V | +| [67fdc27bdadb](https://github.com/aurora-silicon/linux/commit/67fdc27bdadb58520839376a2b85d1265c3fa78f), [b4e5e4cff710](https://github.com/aurora-silicon/linux/commit/b4e5e4cff710c9961a05d4ba6c16f0f8c47c3776) | Chris Kearney | Merge current aurora-wip into the combined Thunderbolt series | Not applicable — MERGE | V | +| [99a554596d0a](https://github.com/aurora-silicon/linux/commit/99a554596d0a4960120b78f9a09ab30ffd4d0fc1) | Chris Kearney | Merge aurora-wip 3bb0a6104a11 into tb-dp-tunnel-t8103 | Not applicable — MERGE | V | +| [5b55206ca89b](https://github.com/aurora-silicon/linux/commit/5b55206ca89bbeb8bb54a208a9b9a36b80d6360b) | Acelogic | Merge M5 CPU support while retaining per-chip frequency safeguards | Not applicable — MERGE | V | +| [95f70d759093](https://github.com/aurora-silicon/linux/commit/95f70d7590935fdfb63c889d73598996dbf6c695) | Ryan Murray | Merge pull request #172 from aurora-silicon/j700-dcp-foundation | Not applicable — MERGE | V | +| [b37df4bdd552](https://github.com/aurora-silicon/linux/commit/b37df4bdd552dca6fa180e7826d0ec22b5e264f4) | Ryan Murray | Merge pull request #8 from iconidentify/tb-dp-tunnel-t8103 | Not applicable — MERGE | V | +| [c9a55efdff8f](https://github.com/aurora-silicon/linux/commit/c9a55efdff8f9505114fab2c89e934ce363e9b3b) | Ryan Murray | Merge pull request #42 from aurora-silicon/feat/sep-neo-rebase | Not applicable — MERGE | V | +| [026b4131cf05](https://github.com/aurora-silicon/linux/commit/026b4131cf0506b0974e6a0ad0b13cfa15f80cc6) | Ryan Murray | Merge pull request #43 from aurora-silicon/feat/j700-thermal-framework-v2 | Not applicable — MERGE | V | +| [e9c631c62252](https://github.com/aurora-silicon/linux/commit/e9c631c6225261125aae251b6a0b743e0663ed37) | Ryan Murray | Merge pull request #46 from aurora-silicon/fix/j414s-dual-dock-dp | Not applicable — MERGE | V | +| [b8fef4b5b0b5](https://github.com/aurora-silicon/linux/commit/b8fef4b5b0b5d1ed5401db1249fc230fc9ce3f60) | Ryan Murray | Merge pull request #156 from aurora-silicon/codex/neo-ave-bringup | Not applicable — MERGE | V | +| [527179cf0856](https://github.com/aurora-silicon/linux/commit/527179cf085663164d17e4d8e8abf2c3e33b5070) | Ryan Murray | Merge pull request #168 from joshuaswarren/agent/jw16-agx-stats4 | Not applicable — MERGE | V | +| [918c0ff6270c](https://github.com/aurora-silicon/linux/commit/918c0ff6270c673e67067453a2e727b5f3e26331) | Ryan Murray | Merge pull request #55 from aurora-silicon/t8140-cpu-capacity | Not applicable — MERGE | V | +| [db4752825c93](https://github.com/aurora-silicon/linux/commit/db4752825c935257fb77f6b8aacf3b4ee003e9fa) | Ryan Murray | Merge pull request #174 from aurora-silicon/j700/t8140-dt-complete | Not applicable — MERGE | V | +| [d8ad51f6b8c8](https://github.com/aurora-silicon/linux/commit/d8ad51f6b8c809444fe4e3f1ef0a1ebb3ba6af5e) | Ryan Murray | Merge pull request #179 from aurora-silicon/j700/pmp-mcc-thermal | Not applicable — MERGE | V | +| [5de43d36cd52](https://github.com/aurora-silicon/linux/commit/5de43d36cd520eaf5df6fc16d047ee413ac54968) | Ryan Murray | Merge pull request #181 from aurora-silicon/j700/t8140-ane-topology | Not applicable — MERGE | V | +| [564976d8e4a6](https://github.com/aurora-silicon/linux/commit/564976d8e4a60dd64a4dc77b2b82f2a0f25f3f45) | Ryan Murray | Merge pull request #182 from aurora-silicon/j700/mcc-telemetry | Not applicable — MERGE | V | +| [4f3a64e7baa9](https://github.com/aurora-silicon/linux/commit/4f3a64e7baa91c6616ae955695e8a5c847dff968) | Ryan | Merge pull request #215 from aurora-silicon/j700/t8140-slc-allocation | Not applicable — MERGE | V | +| [ccedac0c6a30](https://github.com/aurora-silicon/linux/commit/ccedac0c6a30b11b9b1926522a98d4079c9acbf1) | Ryan | Merge pull request #221 from aurora-silicon/j700/display-handoff-race | Not applicable — MERGE | V | +| [123c0d909a8a](https://github.com/aurora-silicon/linux/commit/123c0d909a8af07363e3e885691f0e9feb10c9bc) | Ryan | Merge pull request #223 from aurora-silicon/apple/sashiko-pmp-entry-unbind | Not applicable — MERGE | V | +| [1702229de78f](https://github.com/aurora-silicon/linux/commit/1702229de78f1acd92808b228034839fb7e6c0bc) | Ryan | Merge pull request #232 from aurora-silicon/j700/aop-dart-invalidate | Not applicable — MERGE | V | +| [5c06b1998b8e](https://github.com/aurora-silicon/linux/commit/5c06b1998b8eefdf785b5320a6f8cec31f8da29a) | Ryan | Merge pull request #272 from aurora-silicon/j700/pmgr-failed-power-on | Not applicable — MERGE | V | +| [92ad2a772af8](https://github.com/aurora-silicon/linux/commit/92ad2a772af82a81fc3c73c915a48b9b611db55b) | Ryan Murray | Merge public J700 DP Alt qualification stack into current aurora-wip | Not applicable — MERGE | V | +| [f5afac94ca29](https://github.com/aurora-silicon/linux/commit/f5afac94ca2999c858fb61ebab0bf4b4c168fc6d) | Ryan Murray | drm/apple: compose lean J700 display and automatic recovery candidate | Not applicable — MERGE | V | +| [08e0d0a90be3](https://github.com/aurora-silicon/linux/commit/08e0d0a90be3bb6d777cc1a285ba22bfce12a4d9) | Ryan Murray | drm/apple: integrate reviewed fixed-host lifecycle and scoped boot fixes | Not applicable — MERGE | V | +| [47c99edf4878](https://github.com/aurora-silicon/linux/commit/47c99edf48782ec6078624f579108e243f8cbb4c) | Ryan | Merge pull request #342 from aurora-silicon/cleanup/rust-clippy-20261017 | Not applicable — MERGE | V | +| [3abf4a56b59e](https://github.com/aurora-silicon/linux/commit/3abf4a56b59e323026f9a6e2fe9a0d4307c251d5) | Ryan | Merge pull request #345 from aurora-silicon/fix/harden-all-20261017 | Not applicable — MERGE | V | +| [c20b9d96ba77](https://github.com/aurora-silicon/linux/commit/c20b9d96ba77da9a32c751ab0e63f283d10eea4a) | Ryan Murray | Merge branch 'integration/aurora' into aurora-wip | Not applicable — MERGE | V | +| [8080772b8377](https://github.com/aurora-silicon/linux/commit/8080772b83773a6ca224718de3fdb8bddc1b8511) | Ryan Murray | Merge branch 'integration/aurora' at a1b269f7 into aurora-wip | Not applicable — MERGE | V | +| [42c59170fe98](https://github.com/aurora-silicon/linux/commit/42c59170fe98eb0266c4ca5ea5e1abd0cbac97aa) | Ryan Murray | Merge integration/aurora at 3b77f0f9 into aurora-wip | Not applicable — MERGE | V | +| [1a3c5ca41f8f](https://github.com/aurora-silicon/linux/commit/1a3c5ca41f8f0215a355625ad7c4d456c78f2802) | Ryan Murray | Merge branch 'unified-7.3/bits/170-usb' into unified-7.3/canonical | Not applicable — MERGE | V | +| [2e1656f93c27](https://github.com/aurora-silicon/linux/commit/2e1656f93c27f76171fa265dadcb5ba019bce421) | Ryan Murray | Merge pull request #65 from joshuaswarren/ane-dt-polltx-aurora | Not applicable — MERGE | V | +| [9dbd46baf1b2](https://github.com/aurora-silicon/linux/commit/9dbd46baf1b289a30b5f9be6c4b5f6341135d7ce) | Ryan | Merge pull request #198 from aurora-silicon/fix/pr171-review [#171](https://github.com/aurora-silicon/linux/pull/171) | Not applicable — MERGE | V | +| [f1f334c2ec72](https://github.com/aurora-silicon/linux/commit/f1f334c2ec724b8c96f534a561df9480aa4310ee) | Ryan Murray | Merge branch 'backlog/kunit-parsers-fixes-20261017' into backlog/harden-all-20261017 [#345](https://github.com/aurora-silicon/linux/pull/345) | Not applicable — MERGE | V | + +### Public measured follow-ups outside the audited branch heads + +[#464](https://github.com/aurora-silicon/linux/pull/464), +[#468](https://github.com/aurora-silicon/linux/pull/468) and +[#494](https://github.com/aurora-silicon/linux/pull/494) were also read. Their +heads are not in either pinned branch snapshot; they are not silently counted +as inherited or qualified ports. They remain separate proposals for review: + +- #464 limits the T8140 GPU floor to streams and yields to PMP demand. Its + reported video power is 5.151 to 4.975 W, with light-submit gains retained. + Check the same video/browsing/heavy-load failure modes on g2, including actual + T6040 PMP voter behavior, before accepting its unconditional powered floor. +- #468 reports 7409 to 4625 doorbells in a traced hop, slow hops 19.6% to 13.9%, + and C-d64 13.89 to 13.11 us. Its shared compute-doorbell mechanism would need + no T6040 feature gate; it is an additional unmerged source dependency. +- #494 reports event passes 8.2 to 6.2 us and submit.empty d2 1.78 to 1.69 us. + Its shared event housekeeping change is another unmerged source dependency; + it is not evidence that the audited branches already provide those gains. + +No register, voltage, slot-key address, power curve, module parameter or new +platform option is invented to close an evidence gap. Applicability of dormant +25G83-specific feature/identity paths must come from validated facts; successful +compilation is not that qualification. diff --git a/MAINTAINERS b/MAINTAINERS index 897becc509f69..517a184d7f5ce 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2062,6 +2062,7 @@ F: Documentation/gpu/apple-dcp-*.rst F: drivers/gpu/drm/apple/ F: drivers/gpu/drm/apple/dcp-fabric* F: tools/testing/selftests/drivers/gpu/apple-afkv2/ +F: tools/testing/selftests/drivers/gpu/apple-dcp/ APPLE PCIE CONTROLLER DRIVER M: Marc Zyngier diff --git a/arch/arm64/boot/dts/apple/Makefile b/arch/arm64/boot/dts/apple/Makefile index 007e204fdbc56..0f6e6714f7de7 100644 --- a/arch/arm64/boot/dts/apple/Makefile +++ b/arch/arm64/boot/dts/apple/Makefile @@ -95,6 +95,7 @@ dtb-$(CONFIG_ARCH_APPLE) += t6032-j575d.dtb dtb-$(CONFIG_ARCH_APPLE) += t6034-j514m.dtb dtb-$(CONFIG_ARCH_APPLE) += t6034-j516m.dtb dtb-$(CONFIG_ARCH_APPLE) += t6040-j616s.dtb +dtb-$(CONFIG_ARCH_APPLE) += t6040-j616s-tether.dtb dtb-$(CONFIG_ARCH_APPLE) += t8112-j413.dtb dtb-$(CONFIG_ARCH_APPLE) += t8112-j415.dtb dtb-$(CONFIG_ARCH_APPLE) += t8112-j473.dtb diff --git a/arch/arm64/boot/dts/apple/t6040-j616s-tether.dts b/arch/arm64/boot/dts/apple/t6040-j616s-tether.dts new file mode 100644 index 0000000000000..8027360847204 --- /dev/null +++ b/arch/arm64/boot/dts/apple/t6040-j616s-tether.dts @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* J616s tether console; the host owns ATC0 during the debug session. */ +#include "t6040-j616s.dts" + +/ { + aliases { + serial0 = &serial0; + }; +}; + +&serial0 { + status = "okay"; +}; + +&ps_uart_p { + status = "okay"; +}; + +&ps_uart0 { + status = "okay"; +}; + +&dcuart { + status = "okay"; +}; + +&hpm0 { + status = "disabled"; +}; + +&atcphy0 { + status = "disabled"; +}; + +&atcphy0_mbox { + status = "disabled"; +}; + +&usb_drd0 { + status = "disabled"; +}; + +&usb_drd0_dart_0 { + status = "disabled"; +}; + +&usb_drd0_dart_1 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/apple/t6040-j616s.dts b/arch/arm64/boot/dts/apple/t6040-j616s.dts index bccb167340159..3995f5d4db16a 100644 --- a/arch/arm64/boot/dts/apple/t6040-j616s.dts +++ b/arch/arm64/boot/dts/apple/t6040-j616s.dts @@ -29,6 +29,9 @@ disp0-piodma = &disp0_piodma; /* the boot loader finds the GPU node through this alias */ gpu = &gpu; + pmp = &pmp; + /* the boot loader reserves the SEP firmware through this alias */ + sep = &sep; }; chosen { @@ -79,8 +82,9 @@ * Text segment of the loaded GPU firmware (ADT gfx-asc * segment-ranges[0]); the driver identifies the build from it. */ - gpu_fw_text: gpu-fw-text@10000be4000 { - reg = <0x100 0x00be4000 0x0 0x60000>; + gpu_fw_text: gpu-fw-text { + reg = <0 0 0 0>; + status = "disabled"; no-map; }; @@ -88,8 +92,9 @@ * Data segment of the loaded GPU firmware (ADT sgx gfx-data-*); it * places its crash log here. */ - gpu_fw_data: gpu-fw-data@10001d44000 { - reg = <0x100 0x01d44000 0x0 0x138000>; + gpu_fw_data: gpu-fw-data { + reg = <0 0 0 0>; + status = "disabled"; no-map; }; @@ -98,23 +103,27 @@ * gfx-shared-l2-region-*, gfx-handoff-*). They stay no-map until * the boot loader adds them to /memory. */ - uat_ttbs: uat-ttbs@10bfffb8000 { - reg = <0x10b 0xfffb8000 0x0 0x4000>; + uat_ttbs: uat-ttbs { + reg = <0 0 0 0>; + status = "disabled"; no-map; }; - uat_pagetables: uat-pagetables@10bfff78000 { - reg = <0x10b 0xfff78000 0x0 0x40000>; + uat_pagetables: uat-pagetables { + reg = <0 0 0 0>; + status = "disabled"; no-map; }; - uat_l2_pagetables: uat-l2-pagetables@10bfff74000 { - reg = <0x10b 0xfff74000 0x0 0x4000>; + uat_l2_pagetables: uat-l2-pagetables { + reg = <0 0 0 0>; + status = "disabled"; no-map; }; - uat_handoff: uat-handoff@10bfff70000 { - reg = <0x10b 0xfff70000 0x0 0x4000>; + uat_handoff: uat-handoff { + reg = <0 0 0 0>; + status = "disabled"; no-map; }; }; @@ -139,76 +148,76 @@ /* * GPU performance states, one opp-microvolt cell per cluster (ADT * /arm-io/sgx perf-states and perf-states1). The operating points are - * binned per device: these are this unit's values until the boot - * loader fills them in. Entries keep the order of the GPU performance - * states, which is not sorted by frequency. + * binned per device: the loader fills these placeholders. + * The driver requires a completed calibration handoff. Entries keep the + * order of the GPU performance states, which is not sorted by frequency. */ gpu_opp: opp-table-gpu { compatible = "operating-points-v2"; opp00 { opp-hz = /bits/ 64 <0>; - opp-microvolt = <125000 125000>; + opp-microvolt = <0 0>; }; opp01 { - opp-hz = /bits/ 64 <338000000>; - opp-microvolt = <605000 585000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0 0>; }; opp02 { - opp-hz = /bits/ 64 <618000000>; - opp-microvolt = <655000 640000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0 0>; }; opp03 { - opp-hz = /bits/ 64 <796000000>; - opp-microvolt = <690000 690000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0 0>; }; opp04 { - opp-hz = /bits/ 64 <924000000>; - opp-microvolt = <715000 725000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0 0>; }; opp05 { - opp-hz = /bits/ 64 <952000000>; - opp-microvolt = <755000 770000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0 0>; }; opp06 { - opp-hz = /bits/ 64 <1056000000>; - opp-microvolt = <755000 770000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0 0>; }; opp07 { - opp-hz = /bits/ 64 <1062000000>; - opp-microvolt = <795000 810000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0 0>; }; opp08 { - opp-hz = /bits/ 64 <1182000000>; - opp-microvolt = <795000 810000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0 0>; }; opp09 { - opp-hz = /bits/ 64 <1182000000>; - opp-microvolt = <845000 860000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0 0>; }; opp10 { - opp-hz = /bits/ 64 <1312000000>; - opp-microvolt = <845000 860000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0 0>; }; opp11 { - opp-hz = /bits/ 64 <1242000000>; - opp-microvolt = <900000 915000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0 0>; }; opp12 { - opp-hz = /bits/ 64 <1380000000>; - opp-microvolt = <900000 915000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0 0>; }; opp13 { - opp-hz = /bits/ 64 <1326000000>; - opp-microvolt = <945000 960000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0 0>; }; opp14 { - opp-hz = /bits/ 64 <1470000000>; - opp-microvolt = <945000 960000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0 0>; }; opp15 { - opp-hz = /bits/ 64 <1578000000>; - opp-microvolt = <1030000 1045000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0 0>; }; }; @@ -217,36 +226,36 @@ compatible = "operating-points-v2"; opp00 { - opp-hz = /bits/ 64 <24000000>; - opp-microvolt = <555000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0>; }; opp01 { - opp-hz = /bits/ 64 <338000000>; - opp-microvolt = <555000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0>; }; opp02 { - opp-hz = /bits/ 64 <618000000>; - opp-microvolt = <620000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0>; }; opp03 { - opp-hz = /bits/ 64 <796000000>; - opp-microvolt = <660000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0>; }; opp04 { - opp-hz = /bits/ 64 <992000000>; - opp-microvolt = <715000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0>; }; opp05 { - opp-hz = /bits/ 64 <1188000000>; - opp-microvolt = <780000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0>; }; opp06 { - opp-hz = /bits/ 64 <1380000000>; - opp-microvolt = <870000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0>; }; opp07 { - opp-hz = /bits/ 64 <1470000000>; - opp-microvolt = <910000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0>; }; }; @@ -255,275 +264,3534 @@ compatible = "operating-points-v2"; opp00 { - opp-hz = /bits/ 64 <24000000>; - opp-microvolt = <555000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0>; }; opp01 { - opp-hz = /bits/ 64 <338000000>; - opp-microvolt = <555000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0>; }; opp02 { - opp-hz = /bits/ 64 <618000000>; - opp-microvolt = <620000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0>; }; opp03 { - opp-hz = /bits/ 64 <796000000>; - opp-microvolt = <660000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0>; }; opp04 { - opp-hz = /bits/ 64 <992000000>; - opp-microvolt = <715000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0>; }; opp05 { - opp-hz = /bits/ 64 <1188000000>; - opp-microvolt = <780000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0>; }; opp06 { - opp-hz = /bits/ 64 <1380000000>; - opp-microvolt = <870000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0>; }; opp07 { - opp-hz = /bits/ 64 <1470000000>; - opp-microvolt = <910000>; + opp-hz = /bits/ 64 <0>; + opp-microvolt = <0>; + }; + }; + + /* + * CPU die temperature: SMC key TCMz (CPU die max), sensor 0 of + * smc_hwmon. The PMP's fast die-temperature limit regulates (TCMz + * settles near 111 C); this zone is the backstop above it: passive at + * 113 C, critical (orderly shutdown) at 118 C, polled once a second as + * the SMC refreshes TCMz. The cpufreq cooling device sits on a + * cluster's policy CPU, so each map lists every CPU of its cluster. + * + * Per-cluster and GPU zones follow, one per SMC die-maximum sensor + * (smc_hwmon channels 1-23, in the order of the temperature nodes + * below): Tex0-3 throttle the E cluster, Tpx0-5 the first P cluster + * and Tpx8-D the second, with the same 113/118 C backstop as TCMz. + * Unlike the fanless J700, this machine has two SMC-driven fans and + * the PMP regulates the dies itself, so these zones only catch a + * failure of that loop; they never act in normal use. + * + * The GPU has no cooling device of its own: its firmware sets the GPU + * performance state and starts limiting at 101 C. The SMC runs the + * fans in its own mode (3), only at their minimum speed in every load + * measured under Linux, and refuses manual fan modes from the OS. So + * when a GPU die sensor reaches the GPU firmware's limit, the seven + * hottest ones (Tg05, Tg0S, Tg0Y, Tg0k, Tg0z, Tg1V, Tg1l) also shed + * CPU power through the CPU clusters, from the same 101 C, leaving the + * shared heat budget to the GPU. They shut down at 118 C. While the + * GPU is powered off its sensors report placeholder values from -4 to + * 4 C. + */ + thermal-zones { + cpu-die-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 0>; /* TCMz */ + + trips { + cpu_die_passive: cpu-die-passive { + temperature = <113000>; + hysteresis = <5000>; + type = "passive"; + }; + + cpu-die-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-e { + trip = <&cpu_die_passive>; + cooling-device = + <&cpu_e0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + + map-p0 { + trip = <&cpu_die_passive>; + cooling-device = + <&cpu_p00 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p01 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p02 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p03 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p04 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + + map-p1 { + trip = <&cpu_die_passive>; + cooling-device = + <&cpu_p10 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p11 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p12 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p13 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p14 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu-e0-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 1>; /* Tex0 */ + + trips { + cpu_e0_passive: trip-passive { + temperature = <113000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-e { + trip = <&cpu_e0_passive>; + cooling-device = + <&cpu_e0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu-e1-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 2>; /* Tex1 */ + + trips { + cpu_e1_passive: trip-passive { + temperature = <113000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-e { + trip = <&cpu_e1_passive>; + cooling-device = + <&cpu_e0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu-e2-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 3>; /* Tex2 */ + + trips { + cpu_e2_passive: trip-passive { + temperature = <113000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-e { + trip = <&cpu_e2_passive>; + cooling-device = + <&cpu_e0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu-e3-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 4>; /* Tex3 */ + + trips { + cpu_e3_passive: trip-passive { + temperature = <113000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-e { + trip = <&cpu_e3_passive>; + cooling-device = + <&cpu_e0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu-p00-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 5>; /* Tpx0 */ + + trips { + cpu_p00_passive: trip-passive { + temperature = <113000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-p0 { + trip = <&cpu_p00_passive>; + cooling-device = + <&cpu_p00 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p01 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p02 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p03 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p04 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu-p01-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 6>; /* Tpx1 */ + + trips { + cpu_p01_passive: trip-passive { + temperature = <113000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-p0 { + trip = <&cpu_p01_passive>; + cooling-device = + <&cpu_p00 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p01 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p02 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p03 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p04 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; }; + + cpu-p02-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 7>; /* Tpx2 */ + + trips { + cpu_p02_passive: trip-passive { + temperature = <113000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-p0 { + trip = <&cpu_p02_passive>; + cooling-device = + <&cpu_p00 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p01 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p02 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p03 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p04 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu-p03-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 8>; /* Tpx3 */ + + trips { + cpu_p03_passive: trip-passive { + temperature = <113000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-p0 { + trip = <&cpu_p03_passive>; + cooling-device = + <&cpu_p00 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p01 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p02 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p03 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p04 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu-p04-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 9>; /* Tpx4 */ + + trips { + cpu_p04_passive: trip-passive { + temperature = <113000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-p0 { + trip = <&cpu_p04_passive>; + cooling-device = + <&cpu_p00 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p01 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p02 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p03 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p04 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu-p05-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 10>; /* Tpx5 */ + + trips { + cpu_p05_passive: trip-passive { + temperature = <113000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-p0 { + trip = <&cpu_p05_passive>; + cooling-device = + <&cpu_p00 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p01 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p02 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p03 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p04 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu-p10-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 11>; /* Tpx8 */ + + trips { + cpu_p10_passive: trip-passive { + temperature = <113000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-p1 { + trip = <&cpu_p10_passive>; + cooling-device = + <&cpu_p10 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p11 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p12 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p13 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p14 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu-p11-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 12>; /* Tpx9 */ + + trips { + cpu_p11_passive: trip-passive { + temperature = <113000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-p1 { + trip = <&cpu_p11_passive>; + cooling-device = + <&cpu_p10 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p11 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p12 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p13 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p14 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu-p12-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 13>; /* TpxA */ + + trips { + cpu_p12_passive: trip-passive { + temperature = <113000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-p1 { + trip = <&cpu_p12_passive>; + cooling-device = + <&cpu_p10 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p11 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p12 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p13 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p14 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu-p13-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 14>; /* TpxB */ + + trips { + cpu_p13_passive: trip-passive { + temperature = <113000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-p1 { + trip = <&cpu_p13_passive>; + cooling-device = + <&cpu_p10 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p11 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p12 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p13 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p14 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu-p14-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 15>; /* TpxC */ + + trips { + cpu_p14_passive: trip-passive { + temperature = <113000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-p1 { + trip = <&cpu_p14_passive>; + cooling-device = + <&cpu_p10 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p11 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p12 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p13 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p14 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu-p15-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 16>; /* TpxD */ + + trips { + cpu_p15_passive: trip-passive { + temperature = <113000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-p1 { + trip = <&cpu_p15_passive>; + cooling-device = + <&cpu_p10 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p11 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p12 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p13 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p14 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + gpu0-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 17>; /* Tg05 */ + + trips { + gpu0_passive: trip-passive { + temperature = <101000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-e { + trip = <&gpu0_passive>; + cooling-device = + <&cpu_e0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + + map-p0 { + trip = <&gpu0_passive>; + cooling-device = + <&cpu_p00 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p01 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p02 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p03 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p04 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + + map-p1 { + trip = <&gpu0_passive>; + cooling-device = + <&cpu_p10 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p11 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p12 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p13 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p14 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + gpu1-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 18>; /* Tg0S */ + + trips { + gpu1_passive: trip-passive { + temperature = <101000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-e { + trip = <&gpu1_passive>; + cooling-device = + <&cpu_e0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + + map-p0 { + trip = <&gpu1_passive>; + cooling-device = + <&cpu_p00 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p01 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p02 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p03 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p04 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + + map-p1 { + trip = <&gpu1_passive>; + cooling-device = + <&cpu_p10 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p11 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p12 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p13 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p14 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + gpu2-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 19>; /* Tg0Y */ + + trips { + gpu2_passive: trip-passive { + temperature = <101000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-e { + trip = <&gpu2_passive>; + cooling-device = + <&cpu_e0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + + map-p0 { + trip = <&gpu2_passive>; + cooling-device = + <&cpu_p00 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p01 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p02 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p03 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p04 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + + map-p1 { + trip = <&gpu2_passive>; + cooling-device = + <&cpu_p10 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p11 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p12 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p13 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p14 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + gpu3-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 20>; /* Tg0k */ + + trips { + gpu3_passive: trip-passive { + temperature = <101000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-e { + trip = <&gpu3_passive>; + cooling-device = + <&cpu_e0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + + map-p0 { + trip = <&gpu3_passive>; + cooling-device = + <&cpu_p00 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p01 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p02 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p03 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p04 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + + map-p1 { + trip = <&gpu3_passive>; + cooling-device = + <&cpu_p10 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p11 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p12 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p13 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p14 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + gpu4-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 21>; /* Tg0z */ + + trips { + gpu4_passive: trip-passive { + temperature = <101000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-e { + trip = <&gpu4_passive>; + cooling-device = + <&cpu_e0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + + map-p0 { + trip = <&gpu4_passive>; + cooling-device = + <&cpu_p00 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p01 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p02 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p03 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p04 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + + map-p1 { + trip = <&gpu4_passive>; + cooling-device = + <&cpu_p10 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p11 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p12 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p13 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p14 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + gpu5-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 22>; /* Tg1V */ + + trips { + gpu5_passive: trip-passive { + temperature = <101000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-e { + trip = <&gpu5_passive>; + cooling-device = + <&cpu_e0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + + map-p0 { + trip = <&gpu5_passive>; + cooling-device = + <&cpu_p00 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p01 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p02 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p03 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p04 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + + map-p1 { + trip = <&gpu5_passive>; + cooling-device = + <&cpu_p10 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p11 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p12 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p13 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p14 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + gpu6-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&smc_hwmon 23>; /* Tg1l */ + + trips { + gpu6_passive: trip-passive { + temperature = <101000>; + hysteresis = <5000>; + type = "passive"; + }; + + trip-critical { + temperature = <118000>; + hysteresis = <0>; + type = "critical"; + }; + }; + + cooling-maps { + map-e { + trip = <&gpu6_passive>; + cooling-device = + <&cpu_e0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_e3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + + map-p0 { + trip = <&gpu6_passive>; + cooling-device = + <&cpu_p00 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p01 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p02 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p03 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p04 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + + map-p1 { + trip = <&gpu6_passive>; + cooling-device = + <&cpu_p10 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p11 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p12 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p13 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu_p14 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + }; + + /* + * The speaker amps' shared shutdown line (SDZ, AP pin 54, low = + * shutdown). One fixed regulator, the SDZ supply of all six, not + * per-amp shutdown-gpios: with GPIO_SHARED six references become a + * vote, and one amp driving the line low would not shut the other + * five down. + */ + speaker_sdz: regulator-sn012776-sdz { + compatible = "regulator-fixed"; + regulator-name = "sn012776-sdz"; + gpios = <&pinctrl 54 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + /* + * The AOP firmware runs the stream (MCA cluster 0, one serializer for + * all six amps); the J616 variant of macaudio links the AOP audio + * component's speaker back-end (DAI 3) to the amps. The codecs are + * listed in the ADT provider order, so each gets its native playback + * slot (LW1 0, RW1 1, LW2 2, RW2 3, LT 4, RT 5). The idle masks are the + * ADT amp-tx-zd-config rows 0 and 1: LW1 drives zeros in the right + * amps' sense slots, RW1 in the left amps'. + */ + sound: sound { + compatible = "apple,j616-macaudio", "apple,macaudio"; + model = "MacBook Pro J616"; + speaker-id-gpios = <&pinctrl 52 GPIO_ACTIVE_HIGH>, + <&pinctrl 53 GPIO_ACTIVE_HIGH>; + #address-cells = <1>; + #size-cells = <0>; + + dai-link@0 { + reg = <0>; + link-name = "Speakers"; + dai-tdm-idle-mode-0 = "zero"; + dai-tdm-idle-mode-1 = "zero"; + dai-tdm-slot-tx-idle-mask-0 = <0xf0f0f0>; + dai-tdm-slot-tx-idle-mask-1 = <0x0f0f0f>; + + cpu { + sound-dai = <&aop_audio 3>; + }; + + codec { + sound-dai = <&speaker_left_woof1>, <&speaker_right_woof1>, + <&speaker_left_woof2>, <&speaker_right_woof2>, + <&speaker_left_tweet>, <&speaker_right_tweet>; + }; + }; + }; + + /* + * The headphone jack: the AOP audio component's jack back-end (DAI 4) + * to the CS42L84. A card of its own, so a codec that does not answer + * costs the jack only, never the speakers. + */ + sound_jack: sound-jack { + compatible = "apple,j616-macaudio-jack", "apple,macaudio"; + model = "MacBook Pro J616 Jack"; + #address-cells = <1>; + #size-cells = <0>; + + dai-link@0 { + reg = <0>; + link-name = "Headphone Jack"; + + cpu { + sound-dai = <&aop_audio 4>; + }; + + codec { + sound-dai = <&jack_codec>; + }; + }; + }; +}; + +&cpuidle { + status = "okay"; +}; + +&smc { + status = "okay"; +}; + +&smc_mbox { + status = "okay"; +}; + +&nub_spmi0 { + status = "okay"; +}; + +&pinctrl { + status = "okay"; + + wifi_clkreq_pins: wifi-clkreq-pins { + pinmux = ; + }; +}; + +/* + * The AP takes none of the always-on bank's interrupts: the AOP firmware + * owns some of its pins and interrupt registers, and a chained handler here + * would see their pending bits without a handler of ours. + */ +&pinctrl_nub { + status = "okay"; + /delete-property/ interrupt-controller; + /delete-property/ #interrupt-cells; + /delete-property/ interrupts; +}; + +&fpwm0 { + status = "okay"; +}; + +/* apple_sep enables the SEP and its DART when it loads. */ +&sep_mbox { + status = "okay"; +}; + +/* + * Touch ID sensor (Mesa 0x3356). Its power is NUB GPIO 53 and its + * data-ready line is AP GPIO 116, as the J616s ADT describes them. + */ +&spi2 { + status = "okay"; +}; + +&mesa { + status = "okay"; + enable-gpios = <&pinctrl_nub 53 GPIO_ACTIVE_HIGH>; + interrupt-parent = <&pinctrl>; + interrupts = <116 IRQ_TYPE_EDGE_RISING>; + spi-cs-setup-delay-ns = <20>; + spi-cs-hold-delay-ns = <20>; + /* + * Per-device factory data from this Mac's own iBoot system container: + * the sealed calibration and the sealed root key. + */ + firmware-name = "apple/mesacal-j616s.bin", "apple/msrk-j616s.bin"; +}; + +/* The Type-C retimers (ADT atcrt at 0x18/0x19/0x1a) sit on this bus; no driver yet. */ +&i2c6 { + status = "okay"; +}; + +&mtp { + status = "okay"; +}; + +&mtp_mbox { + status = "okay"; +}; + +&mtp_dart { + status = "okay"; +}; + +&mtp_dockchannel { + status = "okay"; +}; + +&mtp_hid { + /* SMC key gp1c powers the trackpad analog front end. */ + apple,afe-smc = <&smc>; + + multi-touch { + firmware-name = "apple/tpmtfw-j616s.bin"; + }; + + keyboard: keyboard { + hid-country-code = <0>; + apple,keyboard-layout-id = <2>; + }; + + stm { + }; + + actuator { + }; + + tp-accel { + }; + + mtp { + }; +}; + +&smc_hwmon { + /* + * TCMz and the 23 zone sensors after it must stay the first + * temperature children: the thermal zones name a sensor by its index, + * so the shared hwmon includes below only append after them. + * + * Every key below is in this machine's SMC key table as a readable + * float and was read on the J616s (SMC firmware of macOS 26.6.2). + * Keys that only ever read a fixed placeholder (Tz1*, Ta0*) or a + * fixed limit (TVMx, TVmS, TVms, PHPB, PHPM) are left out; rails that + * read 0 W while idle are kept. GPU (Tg*), P-cluster (Tp*) and + * E-cluster (Te*) labels follow load tests: Tg* only read live while + * the GPU is powered and rise with GPU load, Tp* and Te* rise with CPU + * load. Other names follow the SMC key groups and are not otherwise + * verified. SBA1-3 are the three cells of the 3S battery. + */ + temperature-TCMz { + apple,key-id = "TCMz"; + label = "CPU Die Max"; + }; + + temperature-Tex0 { + apple,key-id = "Tex0"; + label = "E-cluster Tex0"; + }; + + temperature-Tex1 { + apple,key-id = "Tex1"; + label = "E-cluster Tex1"; + }; + + temperature-Tex2 { + apple,key-id = "Tex2"; + label = "E-cluster Tex2"; + }; + + temperature-Tex3 { + apple,key-id = "Tex3"; + label = "E-cluster Tex3"; + }; + + temperature-Tpx0 { + apple,key-id = "Tpx0"; + label = "P-cluster Tpx0"; + }; + + temperature-Tpx1 { + apple,key-id = "Tpx1"; + label = "P-cluster Tpx1"; + }; + + temperature-Tpx2 { + apple,key-id = "Tpx2"; + label = "P-cluster Tpx2"; + }; + + temperature-Tpx3 { + apple,key-id = "Tpx3"; + label = "P-cluster Tpx3"; + }; + + temperature-Tpx4 { + apple,key-id = "Tpx4"; + label = "P-cluster Tpx4"; + }; + + temperature-Tpx5 { + apple,key-id = "Tpx5"; + label = "P-cluster Tpx5"; + }; + + temperature-Tpx8 { + apple,key-id = "Tpx8"; + label = "P-cluster Tpx8"; + }; + + temperature-Tpx9 { + apple,key-id = "Tpx9"; + label = "P-cluster Tpx9"; + }; + + temperature-TpxA { + apple,key-id = "TpxA"; + label = "P-cluster TpxA"; + }; + + temperature-TpxB { + apple,key-id = "TpxB"; + label = "P-cluster TpxB"; + }; + + temperature-TpxC { + apple,key-id = "TpxC"; + label = "P-cluster TpxC"; + }; + + temperature-TpxD { + apple,key-id = "TpxD"; + label = "P-cluster TpxD"; + }; + + temperature-Tg05 { + apple,key-id = "Tg05"; + label = "GPU Tg05"; + }; + + temperature-Tg0S { + apple,key-id = "Tg0S"; + label = "GPU Tg0S"; + }; + + temperature-Tg0Y { + apple,key-id = "Tg0Y"; + label = "GPU Tg0Y"; + }; + + temperature-Tg0k { + apple,key-id = "Tg0k"; + label = "GPU Tg0k"; + }; + + temperature-Tg0z { + apple,key-id = "Tg0z"; + label = "GPU Tg0z"; + }; + + temperature-Tg1V { + apple,key-id = "Tg1V"; + label = "GPU Tg1V"; + }; + + temperature-Tg1l { + apple,key-id = "Tg1l"; + label = "GPU Tg1l"; + }; + + temperature-TAOL { + apple,key-id = "TAOL"; + label = "SMC TAOL"; + }; + + temperature-TB1T { + apple,key-id = "TB1T"; + label = "Battery 1"; + }; + + temperature-TB2T { + apple,key-id = "TB2T"; + label = "Battery 2"; + }; + + temperature-TCMb { + apple,key-id = "TCMb"; + label = "SMC TCMb"; + }; + + temperature-TD00 { + apple,key-id = "TD00"; + label = "SMC TD00"; + }; + + temperature-TD01 { + apple,key-id = "TD01"; + label = "SMC TD01"; + }; + + temperature-TD02 { + apple,key-id = "TD02"; + label = "SMC TD02"; + }; + + temperature-TD03 { + apple,key-id = "TD03"; + label = "SMC TD03"; + }; + + temperature-TD04 { + apple,key-id = "TD04"; + label = "SMC TD04"; + }; + + temperature-TD10 { + apple,key-id = "TD10"; + label = "SMC TD10"; + }; + + temperature-TD11 { + apple,key-id = "TD11"; + label = "SMC TD11"; + }; + + temperature-TD12 { + apple,key-id = "TD12"; + label = "SMC TD12"; + }; + + temperature-TD13 { + apple,key-id = "TD13"; + label = "SMC TD13"; + }; + + temperature-TD14 { + apple,key-id = "TD14"; + label = "SMC TD14"; + }; + + temperature-TD20 { + apple,key-id = "TD20"; + label = "SMC TD20"; + }; + + temperature-TD21 { + apple,key-id = "TD21"; + label = "SMC TD21"; + }; + + temperature-TD22 { + apple,key-id = "TD22"; + label = "SMC TD22"; + }; + + temperature-TD23 { + apple,key-id = "TD23"; + label = "SMC TD23"; + }; + + temperature-TD24 { + apple,key-id = "TD24"; + label = "SMC TD24"; + }; + + temperature-TDBP { + apple,key-id = "TDBP"; + label = "SMC TDBP"; + }; + + temperature-TDCR { + apple,key-id = "TDCR"; + label = "SMC TDCR"; + }; + + temperature-TDEC { + apple,key-id = "TDEC"; + label = "SMC TDEC"; + }; + + temperature-TDEL { + apple,key-id = "TDEL"; + label = "SMC TDEL"; + }; + + temperature-TDER { + apple,key-id = "TDER"; + label = "SMC TDER"; + }; + + temperature-TDTP { + apple,key-id = "TDTP"; + label = "SMC TDTP"; + }; + + temperature-TDVx { + apple,key-id = "TDVx"; + label = "SMC TDVx"; + }; + + temperature-TDeL { + apple,key-id = "TDeL"; + label = "SMC TDeL"; + }; + + temperature-TDeR { + apple,key-id = "TDeR"; + label = "SMC TDeR"; + }; + + temperature-TF2S { + apple,key-id = "TF2S"; + label = "SMC TF2S"; + }; + + temperature-TH0a { + apple,key-id = "TH0a"; + label = "SMC TH0a"; + }; + + temperature-TH0b { + apple,key-id = "TH0b"; + label = "SMC TH0b"; + }; + + temperature-TMVR { + apple,key-id = "TMVR"; + label = "SMC TMVR"; + }; + + temperature-TPD0 { + apple,key-id = "TPD0"; + label = "SMC TPD0"; + }; + + temperature-TPD1 { + apple,key-id = "TPD1"; + label = "SMC TPD1"; + }; + + temperature-TPD2 { + apple,key-id = "TPD2"; + label = "SMC TPD2"; + }; + + temperature-TPD3 { + apple,key-id = "TPD3"; + label = "SMC TPD3"; + }; + + temperature-TPD4 { + apple,key-id = "TPD4"; + label = "SMC TPD4"; + }; + + temperature-TPD5 { + apple,key-id = "TPD5"; + label = "SMC TPD5"; + }; + + temperature-TPD6 { + apple,key-id = "TPD6"; + label = "SMC TPD6"; + }; + + temperature-TPD7 { + apple,key-id = "TPD7"; + label = "SMC TPD7"; + }; + + temperature-TPD8 { + apple,key-id = "TPD8"; + label = "SMC TPD8"; + }; + + temperature-TPD9 { + apple,key-id = "TPD9"; + label = "SMC TPD9"; + }; + + temperature-TPDX { + apple,key-id = "TPDX"; + label = "SMC TPDX"; + }; + + temperature-TPDa { + apple,key-id = "TPDa"; + label = "SMC TPDa"; + }; + + temperature-TPDb { + apple,key-id = "TPDb"; + label = "SMC TPDb"; + }; + + temperature-TPDc { + apple,key-id = "TPDc"; + label = "SMC TPDc"; + }; + + temperature-TPDd { + apple,key-id = "TPDd"; + label = "SMC TPDd"; + }; + + temperature-TPDe { + apple,key-id = "TPDe"; + label = "SMC TPDe"; + }; + + temperature-TPDf { + apple,key-id = "TPDf"; + label = "SMC TPDf"; + }; + + temperature-TPH3 { + apple,key-id = "TPH3"; + label = "SMC TPH3"; + }; + + temperature-TPMP { + apple,key-id = "TPMP"; + label = "SMC TPMP"; + }; + + temperature-TPSP { + apple,key-id = "TPSP"; + label = "SMC TPSP"; + }; + + temperature-TRD0 { + apple,key-id = "TRD0"; + label = "SMC TRD0"; + }; + + temperature-TRD1 { + apple,key-id = "TRD1"; + label = "SMC TRD1"; + }; + + temperature-TRD2 { + apple,key-id = "TRD2"; + label = "SMC TRD2"; + }; + + temperature-TRD3 { + apple,key-id = "TRD3"; + label = "SMC TRD3"; + }; + + temperature-TRD4 { + apple,key-id = "TRD4"; + label = "SMC TRD4"; + }; + + temperature-TRD5 { + apple,key-id = "TRD5"; + label = "SMC TRD5"; + }; + + temperature-TRD6 { + apple,key-id = "TRD6"; + label = "SMC TRD6"; + }; + + temperature-TRD7 { + apple,key-id = "TRD7"; + label = "SMC TRD7"; + }; + + temperature-TRD8 { + apple,key-id = "TRD8"; + label = "SMC TRD8"; + }; + + temperature-TRD9 { + apple,key-id = "TRD9"; + label = "SMC TRD9"; + }; + + temperature-TRDX { + apple,key-id = "TRDX"; + label = "SMC TRDX"; + }; + + temperature-TRDa { + apple,key-id = "TRDa"; + label = "SMC TRDa"; + }; + + temperature-TRDb { + apple,key-id = "TRDb"; + label = "SMC TRDb"; + }; + + temperature-TRDc { + apple,key-id = "TRDc"; + label = "SMC TRDc"; + }; + + temperature-TRDd { + apple,key-id = "TRDd"; + label = "SMC TRDd"; + }; + + temperature-TRDe { + apple,key-id = "TRDe"; + label = "SMC TRDe"; + }; + + temperature-TRDf { + apple,key-id = "TRDf"; + label = "SMC TRDf"; + }; + + temperature-TS0P { + apple,key-id = "TS0P"; + label = "SMC TS0P"; + }; + + temperature-TSG1 { + apple,key-id = "TSG1"; + label = "SMC TSG1"; + }; + + temperature-TUD0 { + apple,key-id = "TUD0"; + label = "SMC TUD0"; + }; + + temperature-TUD1 { + apple,key-id = "TUD1"; + label = "SMC TUD1"; + }; + + temperature-TUD2 { + apple,key-id = "TUD2"; + label = "SMC TUD2"; + }; + + temperature-TUD3 { + apple,key-id = "TUD3"; + label = "SMC TUD3"; + }; + + temperature-TUD4 { + apple,key-id = "TUD4"; + label = "SMC TUD4"; + }; + + temperature-TUD5 { + apple,key-id = "TUD5"; + label = "SMC TUD5"; + }; + + temperature-TUD6 { + apple,key-id = "TUD6"; + label = "SMC TUD6"; + }; + + temperature-TUD7 { + apple,key-id = "TUD7"; + label = "SMC TUD7"; + }; + + temperature-TUD8 { + apple,key-id = "TUD8"; + label = "SMC TUD8"; + }; + + temperature-TUD9 { + apple,key-id = "TUD9"; + label = "SMC TUD9"; + }; + + temperature-TUDX { + apple,key-id = "TUDX"; + label = "SMC TUDX"; + }; + + temperature-TUDa { + apple,key-id = "TUDa"; + label = "SMC TUDa"; + }; + + temperature-TUDb { + apple,key-id = "TUDb"; + label = "SMC TUDb"; + }; + + temperature-TUDc { + apple,key-id = "TUDc"; + label = "SMC TUDc"; + }; + + temperature-TUDd { + apple,key-id = "TUDd"; + label = "SMC TUDd"; + }; + + temperature-TUDe { + apple,key-id = "TUDe"; + label = "SMC TUDe"; + }; + + temperature-TUDf { + apple,key-id = "TUDf"; + label = "SMC TUDf"; + }; + + temperature-TVA0 { + apple,key-id = "TVA0"; + label = "SMC TVA0"; + }; + + temperature-TVD0 { + apple,key-id = "TVD0"; + label = "SMC TVD0"; + }; + + temperature-TVMR { + apple,key-id = "TVMR"; + label = "SMC TVMR"; + }; + + temperature-TVMX { + apple,key-id = "TVMX"; + label = "SMC TVMX"; + }; + + temperature-TVMr { + apple,key-id = "TVMr"; + label = "SMC TVMr"; + }; + + temperature-TVS0 { + apple,key-id = "TVS0"; + label = "SMC TVS0"; + }; + + temperature-TVS1 { + apple,key-id = "TVS1"; + label = "SMC TVS1"; + }; + + temperature-TVS2 { + apple,key-id = "TVS2"; + label = "SMC TVS2"; + }; + + temperature-TVV0 { + apple,key-id = "TVV0"; + label = "SMC TVV0"; + }; + + temperature-TVXX { + apple,key-id = "TVXX"; + label = "SMC TVXX"; + }; + + temperature-TVXh { + apple,key-id = "TVXh"; + label = "SMC TVXh"; + }; + + temperature-TVXm { + apple,key-id = "TVXm"; + label = "SMC TVXm"; + }; + + temperature-TVh0 { + apple,key-id = "TVh0"; + label = "SMC TVh0"; + }; + + temperature-TVh1 { + apple,key-id = "TVh1"; + label = "SMC TVh1"; + }; + + temperature-TVm0 { + apple,key-id = "TVm0"; + label = "SMC TVm0"; + }; + + temperature-TVxx { + apple,key-id = "TVxx"; + label = "SMC TVxx"; + }; + + temperature-TaLP { + apple,key-id = "TaLP"; + label = "SMC TaLP"; + }; + + temperature-TaLT { + apple,key-id = "TaLT"; + label = "SMC TaLT"; + }; + + temperature-TaLW { + apple,key-id = "TaLW"; + label = "SMC TaLW"; + }; + + temperature-TaRF { + apple,key-id = "TaRF"; + label = "SMC TaRF"; + }; + + temperature-TaRT { + apple,key-id = "TaRT"; + label = "SMC TaRT"; + }; + + temperature-TaRW { + apple,key-id = "TaRW"; + label = "SMC TaRW"; + }; + + temperature-TaTP { + apple,key-id = "TaTP"; + label = "SMC TaTP"; + }; + + temperature-Te04 { + apple,key-id = "Te04"; + label = "E-cluster Te04"; + }; + + temperature-Te05 { + apple,key-id = "Te05"; + label = "E-cluster Te05"; + }; + + temperature-Te06 { + apple,key-id = "Te06"; + label = "E-cluster Te06"; + }; + + temperature-Te0R { + apple,key-id = "Te0R"; + label = "E-cluster Te0R"; + }; + + temperature-Te0S { + apple,key-id = "Te0S"; + label = "E-cluster Te0S"; + }; + + temperature-Te0T { + apple,key-id = "Te0T"; + label = "E-cluster Te0T"; + }; + + temperature-TfC0 { + apple,key-id = "TfC0"; + label = "SMC TfC0"; + }; + + temperature-TfC1 { + apple,key-id = "TfC1"; + label = "SMC TfC1"; + }; + + temperature-TfC2 { + apple,key-id = "TfC2"; + label = "SMC TfC2"; + }; + + temperature-TfC3 { + apple,key-id = "TfC3"; + label = "SMC TfC3"; + }; + + temperature-TfC4 { + apple,key-id = "TfC4"; + label = "SMC TfC4"; + }; + + temperature-Tg04 { + apple,key-id = "Tg04"; + label = "GPU Tg04"; + }; + + temperature-Tg0K { + apple,key-id = "Tg0K"; + label = "GPU Tg0K"; + }; + + temperature-Tg0L { + apple,key-id = "Tg0L"; + label = "GPU Tg0L"; + }; + + temperature-Tg0R { + apple,key-id = "Tg0R"; + label = "GPU Tg0R"; + }; + + temperature-Tg0X { + apple,key-id = "Tg0X"; + label = "GPU Tg0X"; + }; + + temperature-Tg0d { + apple,key-id = "Tg0d"; + label = "GPU Tg0d"; + }; + + temperature-Tg0e { + apple,key-id = "Tg0e"; + label = "GPU Tg0e"; + }; + + temperature-Tg0j { + apple,key-id = "Tg0j"; + label = "GPU Tg0j"; + }; + + temperature-Tg0y { + apple,key-id = "Tg0y"; + label = "GPU Tg0y"; + }; + + temperature-Tg1E { + apple,key-id = "Tg1E"; + label = "GPU Tg1E"; + }; + + temperature-Tg1F { + apple,key-id = "Tg1F"; + label = "GPU Tg1F"; + }; + + temperature-Tg1U { + apple,key-id = "Tg1U"; + label = "GPU Tg1U"; + }; + + temperature-Tg1c { + apple,key-id = "Tg1c"; + label = "GPU Tg1c"; + }; + + temperature-Tg1d { + apple,key-id = "Tg1d"; + label = "GPU Tg1d"; + }; + + temperature-Tg1k { + apple,key-id = "Tg1k"; + label = "GPU Tg1k"; + }; + + temperature-Tp00 { + apple,key-id = "Tp00"; + label = "P-cluster Tp00"; + }; + + temperature-Tp01 { + apple,key-id = "Tp01"; + label = "P-cluster Tp01"; + }; + + temperature-Tp02 { + apple,key-id = "Tp02"; + label = "P-cluster Tp02"; + }; + + temperature-Tp04 { + apple,key-id = "Tp04"; + label = "P-cluster Tp04"; + }; + + temperature-Tp05 { + apple,key-id = "Tp05"; + label = "P-cluster Tp05"; + }; + + temperature-Tp06 { + apple,key-id = "Tp06"; + label = "P-cluster Tp06"; + }; + + temperature-Tp08 { + apple,key-id = "Tp08"; + label = "P-cluster Tp08"; + }; + + temperature-Tp09 { + apple,key-id = "Tp09"; + label = "P-cluster Tp09"; + }; + + temperature-Tp0A { + apple,key-id = "Tp0A"; + label = "P-cluster Tp0A"; + }; + + temperature-Tp0C { + apple,key-id = "Tp0C"; + label = "P-cluster Tp0C"; + }; + + temperature-Tp0D { + apple,key-id = "Tp0D"; + label = "P-cluster Tp0D"; + }; + + temperature-Tp0E { + apple,key-id = "Tp0E"; + label = "P-cluster Tp0E"; + }; + + temperature-Tp0G { + apple,key-id = "Tp0G"; + label = "P-cluster Tp0G"; + }; + + temperature-Tp0H { + apple,key-id = "Tp0H"; + label = "P-cluster Tp0H"; + }; + + temperature-Tp0I { + apple,key-id = "Tp0I"; + label = "P-cluster Tp0I"; + }; + + temperature-Tp0K { + apple,key-id = "Tp0K"; + label = "P-cluster Tp0K"; + }; + + temperature-Tp0L { + apple,key-id = "Tp0L"; + label = "P-cluster Tp0L"; + }; + + temperature-Tp0M { + apple,key-id = "Tp0M"; + label = "P-cluster Tp0M"; + }; + + temperature-Tp0O { + apple,key-id = "Tp0O"; + label = "P-cluster Tp0O"; + }; + + temperature-Tp0P { + apple,key-id = "Tp0P"; + label = "P-cluster Tp0P"; + }; + + temperature-Tp0Q { + apple,key-id = "Tp0Q"; + label = "P-cluster Tp0Q"; + }; + + temperature-Tp0S { + apple,key-id = "Tp0S"; + label = "P-cluster Tp0S"; + }; + + temperature-Tp0T { + apple,key-id = "Tp0T"; + label = "P-cluster Tp0T"; + }; + + temperature-Tp0U { + apple,key-id = "Tp0U"; + label = "P-cluster Tp0U"; + }; + + temperature-Tp0W { + apple,key-id = "Tp0W"; + label = "P-cluster Tp0W"; + }; + + temperature-Tp0X { + apple,key-id = "Tp0X"; + label = "P-cluster Tp0X"; + }; + + temperature-Tp0Y { + apple,key-id = "Tp0Y"; + label = "P-cluster Tp0Y"; + }; + + temperature-Tp0a { + apple,key-id = "Tp0a"; + label = "P-cluster Tp0a"; + }; + + temperature-Tp0b { + apple,key-id = "Tp0b"; + label = "P-cluster Tp0b"; + }; + + temperature-Tp0c { + apple,key-id = "Tp0c"; + label = "P-cluster Tp0c"; + }; + + temperature-Tp0d { + apple,key-id = "Tp0d"; + label = "P-cluster Tp0d"; + }; + + temperature-Tp0e { + apple,key-id = "Tp0e"; + label = "P-cluster Tp0e"; + }; + + temperature-Tp0f { + apple,key-id = "Tp0f"; + label = "P-cluster Tp0f"; + }; + + temperature-Tp0g { + apple,key-id = "Tp0g"; + label = "P-cluster Tp0g"; + }; + + temperature-Tp0i { + apple,key-id = "Tp0i"; + label = "P-cluster Tp0i"; + }; + + temperature-Tp0k { + apple,key-id = "Tp0k"; + label = "P-cluster Tp0k"; + }; + + temperature-Tp0l { + apple,key-id = "Tp0l"; + label = "P-cluster Tp0l"; + }; + + temperature-Tp0m { + apple,key-id = "Tp0m"; + label = "P-cluster Tp0m"; + }; + + temperature-Tp0n { + apple,key-id = "Tp0n"; + label = "P-cluster Tp0n"; + }; + + temperature-Tp0o { + apple,key-id = "Tp0o"; + label = "P-cluster Tp0o"; + }; + + temperature-Tp0q { + apple,key-id = "Tp0q"; + label = "P-cluster Tp0q"; + }; + + temperature-Tp0t { + apple,key-id = "Tp0t"; + label = "P-cluster Tp0t"; + }; + + temperature-Tp0u { + apple,key-id = "Tp0u"; + label = "P-cluster Tp0u"; + }; + + temperature-Tp0v { + apple,key-id = "Tp0v"; + label = "P-cluster Tp0v"; + }; + + temperature-Tp0w { + apple,key-id = "Tp0w"; + label = "P-cluster Tp0w"; + }; + + temperature-Tp1i { + apple,key-id = "Tp1i"; + label = "P-cluster Tp1i"; + }; + + temperature-Tp1j { + apple,key-id = "Tp1j"; + label = "P-cluster Tp1j"; + }; + + temperature-Tp1k { + apple,key-id = "Tp1k"; + label = "P-cluster Tp1k"; + }; + + temperature-Tp1m { + apple,key-id = "Tp1m"; + label = "P-cluster Tp1m"; + }; + + temperature-Tp1n { + apple,key-id = "Tp1n"; + label = "P-cluster Tp1n"; + }; + + temperature-Tp1o { + apple,key-id = "Tp1o"; + label = "P-cluster Tp1o"; + }; + + temperature-Tp1q { + apple,key-id = "Tp1q"; + label = "P-cluster Tp1q"; + }; + + temperature-Tp1t { + apple,key-id = "Tp1t"; + label = "P-cluster Tp1t"; + }; + + temperature-Tp1u { + apple,key-id = "Tp1u"; + label = "P-cluster Tp1u"; + }; + + temperature-Tp1v { + apple,key-id = "Tp1v"; + label = "P-cluster Tp1v"; + }; + + temperature-Tp1w { + apple,key-id = "Tp1w"; + label = "P-cluster Tp1w"; + }; + + temperature-Tp1x { + apple,key-id = "Tp1x"; + label = "P-cluster Tp1x"; + }; + + temperature-Tp1y { + apple,key-id = "Tp1y"; + label = "P-cluster Tp1y"; + }; + + temperature-Tp1z { + apple,key-id = "Tp1z"; + label = "P-cluster Tp1z"; + }; + + temperature-Tp20 { + apple,key-id = "Tp20"; + label = "P-cluster Tp20"; + }; + + temperature-Tp21 { + apple,key-id = "Tp21"; + label = "P-cluster Tp21"; + }; + + temperature-Tp22 { + apple,key-id = "Tp22"; + label = "P-cluster Tp22"; + }; + + temperature-Tp23 { + apple,key-id = "Tp23"; + label = "P-cluster Tp23"; + }; + + temperature-Tp24 { + apple,key-id = "Tp24"; + label = "P-cluster Tp24"; + }; + + temperature-Tp25 { + apple,key-id = "Tp25"; + label = "P-cluster Tp25"; + }; + + temperature-Tp26 { + apple,key-id = "Tp26"; + label = "P-cluster Tp26"; + }; + + temperature-Tp27 { + apple,key-id = "Tp27"; + label = "P-cluster Tp27"; + }; + + temperature-Tp28 { + apple,key-id = "Tp28"; + label = "P-cluster Tp28"; + }; + + temperature-Tp29 { + apple,key-id = "Tp29"; + label = "P-cluster Tp29"; + }; + + temperature-Tp2A { + apple,key-id = "Tp2A"; + label = "P-cluster Tp2A"; + }; + + temperature-Tp2B { + apple,key-id = "Tp2B"; + label = "P-cluster Tp2B"; + }; + + temperature-Tp2C { + apple,key-id = "Tp2C"; + label = "P-cluster Tp2C"; + }; + + temperature-Tp2D { + apple,key-id = "Tp2D"; + label = "P-cluster Tp2D"; + }; + + temperature-Tp2E { + apple,key-id = "Tp2E"; + label = "P-cluster Tp2E"; + }; + + temperature-Tp2G { + apple,key-id = "Tp2G"; + label = "P-cluster Tp2G"; + }; + + temperature-Tp2I { + apple,key-id = "Tp2I"; + label = "P-cluster Tp2I"; + }; + + temperature-Tp2J { + apple,key-id = "Tp2J"; + label = "P-cluster Tp2J"; + }; + + temperature-Tp2K { + apple,key-id = "Tp2K"; + label = "P-cluster Tp2K"; + }; + + temperature-Tp2L { + apple,key-id = "Tp2L"; + label = "P-cluster Tp2L"; + }; + + temperature-Tp2M { + apple,key-id = "Tp2M"; + label = "P-cluster Tp2M"; + }; + + temperature-Tp2N { + apple,key-id = "Tp2N"; + label = "P-cluster Tp2N"; + }; + + temperature-Tp2O { + apple,key-id = "Tp2O"; + label = "P-cluster Tp2O"; + }; + + temperature-Tp2Q { + apple,key-id = "Tp2Q"; + label = "P-cluster Tp2Q"; + }; + + temperature-Tp2R { + apple,key-id = "Tp2R"; + label = "P-cluster Tp2R"; + }; + + temperature-Tp2S { + apple,key-id = "Tp2S"; + label = "P-cluster Tp2S"; + }; + + temperature-Tp2T { + apple,key-id = "Tp2T"; + label = "P-cluster Tp2T"; + }; + + temperature-Tp2U { + apple,key-id = "Tp2U"; + label = "P-cluster Tp2U"; + }; + + temperature-Tp2V { + apple,key-id = "Tp2V"; + label = "P-cluster Tp2V"; + }; + + temperature-Tp2W { + apple,key-id = "Tp2W"; + label = "P-cluster Tp2W"; + }; + + temperature-Tp2X { + apple,key-id = "Tp2X"; + label = "P-cluster Tp2X"; + }; + + temperature-Ts00 { + apple,key-id = "Ts00"; + label = "SMC Ts00"; + }; + + temperature-Ts01 { + apple,key-id = "Ts01"; + label = "SMC Ts01"; + }; + + temperature-Ts02 { + apple,key-id = "Ts02"; + label = "SMC Ts02"; + }; + + temperature-Ts04 { + apple,key-id = "Ts04"; + label = "SMC Ts04"; + }; + + temperature-Ts05 { + apple,key-id = "Ts05"; + label = "SMC Ts05"; + }; + + temperature-Ts06 { + apple,key-id = "Ts06"; + label = "SMC Ts06"; + }; + + temperature-Ts08 { + apple,key-id = "Ts08"; + label = "SMC Ts08"; + }; + + temperature-Ts09 { + apple,key-id = "Ts09"; + label = "SMC Ts09"; + }; + + temperature-Ts0A { + apple,key-id = "Ts0A"; + label = "SMC Ts0A"; + }; + + temperature-Ts0C { + apple,key-id = "Ts0C"; + label = "SMC Ts0C"; + }; + + temperature-Ts0D { + apple,key-id = "Ts0D"; + label = "SMC Ts0D"; + }; + + temperature-Ts0E { + apple,key-id = "Ts0E"; + label = "SMC Ts0E"; + }; + + temperature-Ts0G { + apple,key-id = "Ts0G"; + label = "SMC Ts0G"; + }; + + temperature-Ts0H { + apple,key-id = "Ts0H"; + label = "SMC Ts0H"; + }; + + temperature-Ts0I { + apple,key-id = "Ts0I"; + label = "SMC Ts0I"; + }; + + temperature-Ts0K { + apple,key-id = "Ts0K"; + label = "SMC Ts0K"; + }; + + temperature-Ts0L { + apple,key-id = "Ts0L"; + label = "SMC Ts0L"; + }; + + temperature-Ts0M { + apple,key-id = "Ts0M"; + label = "SMC Ts0M"; + }; + + temperature-Ts0O { + apple,key-id = "Ts0O"; + label = "SMC Ts0O"; + }; + + temperature-Ts0P { + apple,key-id = "Ts0P"; + label = "SMC Ts0P"; + }; + + temperature-Ts0Q { + apple,key-id = "Ts0Q"; + label = "SMC Ts0Q"; + }; + + temperature-Ts0R { + apple,key-id = "Ts0R"; + label = "SMC Ts0R"; + }; + + temperature-Ts0T { + apple,key-id = "Ts0T"; + label = "SMC Ts0T"; + }; + + temperature-Ts0U { + apple,key-id = "Ts0U"; + label = "SMC Ts0U"; + }; + + temperature-Ts0W { + apple,key-id = "Ts0W"; + label = "SMC Ts0W"; + }; + + temperature-Ts1P { + apple,key-id = "Ts1P"; + label = "SMC Ts1P"; + }; + + temperature-Tsx0 { + apple,key-id = "Tsx0"; + label = "SMC Tsx0"; + }; + + temperature-Tsx1 { + apple,key-id = "Tsx1"; + label = "SMC Tsx1"; + }; + + voltage-VDM2 { + apple,key-id = "VDM2"; + label = "SMC VDM2"; + }; + + voltage-VDMA { + apple,key-id = "VDMA"; + label = "SMC VDMA"; + }; + + voltage-VDMM { + apple,key-id = "VDMM"; + label = "SMC VDMM"; + }; + + voltage-VP0R { + apple,key-id = "VP0R"; + label = "SMC VP0R"; + }; + + voltage-VP0b { + apple,key-id = "VP0b"; + label = "SMC VP0b"; + }; + + voltage-VP1b { + apple,key-id = "VP1b"; + label = "SMC VP1b"; + }; + + voltage-VP1l { + apple,key-id = "VP1l"; + label = "SMC VP1l"; + }; + + voltage-VP2b { + apple,key-id = "VP2b"; + label = "SMC VP2b"; + }; + + voltage-VP3b { + apple,key-id = "VP3b"; + label = "SMC VP3b"; + }; + + voltage-VP4b { + apple,key-id = "VP4b"; + label = "SMC VP4b"; + }; + + voltage-VP5l { + apple,key-id = "VP5l"; + label = "SMC VP5l"; + }; + + voltage-VP7b { + apple,key-id = "VP7b"; + label = "SMC VP7b"; + }; + + voltage-VP7l { + apple,key-id = "VP7l"; + label = "SMC VP7l"; + }; + + voltage-VP9b { + apple,key-id = "VP9b"; + label = "SMC VP9b"; + }; + + voltage-VP9l { + apple,key-id = "VP9l"; + label = "SMC VP9l"; + }; + + voltage-VPal { + apple,key-id = "VPal"; + label = "SMC VPal"; + }; + + voltage-VPbb { + apple,key-id = "VPbb"; + label = "SMC VPbb"; + }; + + voltage-VPdb { + apple,key-id = "VPdb"; + label = "SMC VPdb"; + }; + + voltage-VPeb { + apple,key-id = "VPeb"; + label = "SMC VPeb"; + }; + + voltage-VR0b { + apple,key-id = "VR0b"; + label = "SMC VR0b"; + }; + + voltage-VR1b { + apple,key-id = "VR1b"; + label = "SMC VR1b"; + }; + + voltage-VR2b { + apple,key-id = "VR2b"; + label = "SMC VR2b"; + }; + + voltage-VR3b { + apple,key-id = "VR3b"; + label = "SMC VR3b"; + }; + + voltage-VR3l { + apple,key-id = "VR3l"; + label = "SMC VR3l"; + }; + + voltage-VR4b { + apple,key-id = "VR4b"; + label = "SMC VR4b"; + }; + + voltage-VR7b { + apple,key-id = "VR7b"; + label = "SMC VR7b"; + }; + + voltage-VR8l { + apple,key-id = "VR8l"; + label = "SMC VR8l"; + }; + + voltage-VR9b { + apple,key-id = "VR9b"; + label = "SMC VR9b"; + }; + + voltage-VR9l { + apple,key-id = "VR9l"; + label = "SMC VR9l"; + }; + + voltage-VRal { + apple,key-id = "VRal"; + label = "SMC VRal"; + }; + + voltage-VRbb { + apple,key-id = "VRbb"; + label = "SMC VRbb"; + }; + + voltage-VRdb { + apple,key-id = "VRdb"; + label = "SMC VRdb"; + }; + + voltage-VReb { + apple,key-id = "VReb"; + label = "SMC VReb"; + }; + + voltage-VS0b { + apple,key-id = "VS0b"; + label = "SMC VS0b"; + }; + + voltage-VS1b { + apple,key-id = "VS1b"; + label = "SMC VS1b"; + }; + + voltage-VS2b { + apple,key-id = "VS2b"; + label = "SMC VS2b"; + }; + + voltage-VS3b { + apple,key-id = "VS3b"; + label = "SMC VS3b"; + }; + + voltage-VS4b { + apple,key-id = "VS4b"; + label = "SMC VS4b"; + }; + + voltage-VS7b { + apple,key-id = "VS7b"; + label = "SMC VS7b"; + }; + + voltage-VS9b { + apple,key-id = "VS9b"; + label = "SMC VS9b"; + }; + + voltage-VS9l { + apple,key-id = "VS9l"; + label = "SMC VS9l"; + }; + + voltage-VSal { + apple,key-id = "VSal"; + label = "SMC VSal"; + }; + + voltage-VSbb { + apple,key-id = "VSbb"; + label = "SMC VSbb"; + }; + + voltage-VSeb { + apple,key-id = "VSeb"; + label = "SMC VSeb"; + }; + + voltage-Vb0f { + apple,key-id = "Vb0f"; + label = "SMC Vb0f"; + }; + + voltage-Vb1f { + apple,key-id = "Vb1f"; + label = "SMC Vb1f"; + }; + + voltage-SBA1 { + apple,key-id = "SBA1"; + label = "Battery Cell 1"; + }; + + voltage-SBA2 { + apple,key-id = "SBA2"; + label = "Battery Cell 2"; + }; + + voltage-SBA3 { + apple,key-id = "SBA3"; + label = "Battery Cell 3"; + }; + + current-I3F2 { + apple,key-id = "I3F2"; + label = "SMC I3F2"; + }; + + current-I5SR { + apple,key-id = "I5SR"; + label = "SMC I5SR"; + }; + + current-IALR { + apple,key-id = "IALR"; + label = "SMC IALR"; + }; + + current-IARR { + apple,key-id = "IARR"; + label = "SMC IARR"; + }; + + current-ICMC { + apple,key-id = "ICMC"; + label = "SMC ICMC"; + }; + + current-ID3C { + apple,key-id = "ID3C"; + label = "SMC ID3C"; + }; + + current-IDBC { + apple,key-id = "IDBC"; + label = "SMC IDBC"; + }; + + current-IDBR { + apple,key-id = "IDBR"; + label = "SMC IDBR"; + }; + + current-IFLR { + apple,key-id = "IFLR"; + label = "SMC IFLR"; + }; + + current-IH0R { + apple,key-id = "IH0R"; + label = "SMC IH0R"; + }; + + current-IHCR { + apple,key-id = "IHCR"; + label = "SMC IHCR"; + }; + + current-IIDR { + apple,key-id = "IIDR"; + label = "SMC IIDR"; + }; + + current-IINC { + apple,key-id = "IINC"; + label = "SMC IINC"; + }; + + current-IMVC { + apple,key-id = "IMVC"; + label = "SMC IMVC"; + }; + + current-IMVR { + apple,key-id = "IMVR"; + label = "SMC IMVR"; + }; + + current-IP0b { + apple,key-id = "IP0b"; + label = "SMC IP0b"; + }; + + current-IP1b { + apple,key-id = "IP1b"; + label = "SMC IP1b"; + }; + + current-IP1l { + apple,key-id = "IP1l"; + label = "SMC IP1l"; + }; + + current-IP2b { + apple,key-id = "IP2b"; + label = "SMC IP2b"; + }; + + current-IP3b { + apple,key-id = "IP3b"; + label = "SMC IP3b"; + }; + + current-IP4b { + apple,key-id = "IP4b"; + label = "SMC IP4b"; + }; + + current-IP5l { + apple,key-id = "IP5l"; + label = "SMC IP5l"; + }; + + current-IP7b { + apple,key-id = "IP7b"; + label = "SMC IP7b"; + }; + + current-IP7l { + apple,key-id = "IP7l"; + label = "SMC IP7l"; + }; + + current-IP9b { + apple,key-id = "IP9b"; + label = "SMC IP9b"; + }; + + current-IP9l { + apple,key-id = "IP9l"; + label = "SMC IP9l"; + }; + + current-IPBR { + apple,key-id = "IPBR"; + label = "SMC IPBR"; + }; + + current-IPMC { + apple,key-id = "IPMC"; + label = "SMC IPMC"; + }; + + current-IPSC { + apple,key-id = "IPSC"; + label = "SMC IPSC"; + }; + + current-IPal { + apple,key-id = "IPal"; + label = "SMC IPal"; + }; + + current-IPbb { + apple,key-id = "IPbb"; + label = "SMC IPbb"; + }; + + current-IPdb { + apple,key-id = "IPdb"; + label = "SMC IPdb"; + }; + + current-IPeb { + apple,key-id = "IPeb"; + label = "SMC IPeb"; + }; + + current-IR0b { + apple,key-id = "IR0b"; + label = "SMC IR0b"; + }; + + current-IR1b { + apple,key-id = "IR1b"; + label = "SMC IR1b"; + }; + + current-IR2b { + apple,key-id = "IR2b"; + label = "SMC IR2b"; + }; + + current-IR3b { + apple,key-id = "IR3b"; + label = "SMC IR3b"; + }; + + current-IR3l { + apple,key-id = "IR3l"; + label = "SMC IR3l"; + }; + + current-IR4b { + apple,key-id = "IR4b"; + label = "SMC IR4b"; + }; + + current-IR7b { + apple,key-id = "IR7b"; + label = "SMC IR7b"; + }; + + current-IR8l { + apple,key-id = "IR8l"; + label = "SMC IR8l"; + }; + + current-IR9b { + apple,key-id = "IR9b"; + label = "SMC IR9b"; + }; + + current-IR9l { + apple,key-id = "IR9l"; + label = "SMC IR9l"; + }; + + current-IRal { + apple,key-id = "IRal"; + label = "SMC IRal"; + }; + + current-IRbb { + apple,key-id = "IRbb"; + label = "SMC IRbb"; + }; + + current-IRdb { + apple,key-id = "IRdb"; + label = "SMC IRdb"; + }; + + current-IReb { + apple,key-id = "IReb"; + label = "SMC IReb"; + }; + + current-IS0b { + apple,key-id = "IS0b"; + label = "SMC IS0b"; + }; + + current-IS1b { + apple,key-id = "IS1b"; + label = "SMC IS1b"; + }; + + current-IS2b { + apple,key-id = "IS2b"; + label = "SMC IS2b"; + }; + + current-IS3b { + apple,key-id = "IS3b"; + label = "SMC IS3b"; + }; + + current-IS4b { + apple,key-id = "IS4b"; + label = "SMC IS4b"; + }; + + current-IS7b { + apple,key-id = "IS7b"; + label = "SMC IS7b"; + }; + + current-IS9b { + apple,key-id = "IS9b"; + label = "SMC IS9b"; + }; + + current-IS9l { + apple,key-id = "IS9l"; + label = "SMC IS9l"; + }; + + current-ISDR { + apple,key-id = "ISDR"; + label = "SMC ISDR"; + }; + + current-ISal { + apple,key-id = "ISal"; + label = "SMC ISal"; + }; + + current-ISbb { + apple,key-id = "ISbb"; + label = "SMC ISbb"; + }; + + current-ISeb { + apple,key-id = "ISeb"; + label = "SMC ISeb"; + }; + + current-IU1C { + apple,key-id = "IU1C"; + label = "SMC IU1C"; + }; + + current-IU1R { + apple,key-id = "IU1R"; + label = "SMC IU1R"; + }; + + current-IU2C { + apple,key-id = "IU2C"; + label = "SMC IU2C"; + }; + + current-IU2R { + apple,key-id = "IU2R"; + label = "SMC IU2R"; + }; + + current-IW3C { + apple,key-id = "IW3C"; + label = "SMC IW3C"; + }; + + current-Ib0f { + apple,key-id = "Ib0f"; + label = "SMC Ib0f"; + }; + + current-Ib8f { + apple,key-id = "Ib8f"; + label = "SMC Ib8f"; + }; + + current-SBAC { + apple,key-id = "SBAC"; + label = "Battery Current"; + }; + + power-P3F2 { + apple,key-id = "P3F2"; + label = "SMC P3F2"; + }; + + power-P5SR { + apple,key-id = "P5SR"; + label = "SMC P5SR"; + }; + + power-PALR { + apple,key-id = "PALR"; + label = "SMC PALR"; + }; + + power-PARR { + apple,key-id = "PARR"; + label = "SMC PARR"; + }; + + power-PCMC { + apple,key-id = "PCMC"; + label = "SMC PCMC"; + }; + + power-PD3C { + apple,key-id = "PD3C"; + label = "SMC PD3C"; + }; + + power-PDBC { + apple,key-id = "PDBC"; + label = "SMC PDBC"; + }; + + power-PDBR { + apple,key-id = "PDBR"; + label = "SMC PDBR"; + }; + + power-PFLR { + apple,key-id = "PFLR"; + label = "SMC PFLR"; + }; + + power-PH0R { + apple,key-id = "PH0R"; + label = "SMC PH0R"; + }; + + power-PHCR { + apple,key-id = "PHCR"; + label = "SMC PHCR"; + }; + + power-PIDR { + apple,key-id = "PIDR"; + label = "SMC PIDR"; + }; + + power-PINC { + apple,key-id = "PINC"; + label = "SMC PINC"; + }; + + power-PMVC { + apple,key-id = "PMVC"; + label = "SMC PMVC"; + }; + + power-PP0b { + apple,key-id = "PP0b"; + label = "SMC PP0b"; + }; + + power-PP1b { + apple,key-id = "PP1b"; + label = "SMC PP1b"; + }; + + power-PP1l { + apple,key-id = "PP1l"; + label = "SMC PP1l"; + }; + + power-PP2b { + apple,key-id = "PP2b"; + label = "SMC PP2b"; + }; + + power-PP3b { + apple,key-id = "PP3b"; + label = "SMC PP3b"; + }; + + power-PP4b { + apple,key-id = "PP4b"; + label = "SMC PP4b"; + }; + + power-PP5l { + apple,key-id = "PP5l"; + label = "SMC PP5l"; + }; + + power-PP7b { + apple,key-id = "PP7b"; + label = "SMC PP7b"; + }; + + power-PP7l { + apple,key-id = "PP7l"; + label = "SMC PP7l"; + }; + + power-PP9b { + apple,key-id = "PP9b"; + label = "SMC PP9b"; + }; + + power-PP9l { + apple,key-id = "PP9l"; + label = "SMC PP9l"; + }; + + power-PPBR { + apple,key-id = "PPBR"; + label = "SMC PPBR"; + }; + + power-PPMC { + apple,key-id = "PPMC"; + label = "SMC PPMC"; + }; + + power-PPSC { + apple,key-id = "PPSC"; + label = "SMC PPSC"; + }; + + power-PPal { + apple,key-id = "PPal"; + label = "SMC PPal"; + }; + + power-PPbb { + apple,key-id = "PPbb"; + label = "SMC PPbb"; + }; + + power-PPdb { + apple,key-id = "PPdb"; + label = "SMC PPdb"; + }; + + power-PPeb { + apple,key-id = "PPeb"; + label = "SMC PPeb"; + }; + + power-PR0b { + apple,key-id = "PR0b"; + label = "SMC PR0b"; }; - /* - * CPU die temperature: SMC key TCMz (CPU die max), sensor 0 of - * smc_hwmon. The PMP's fast die-temperature limit regulates (TCMz - * settles near 111 C); this zone is the backstop above it: passive at - * 113 C, critical (orderly shutdown) at 118 C, polled once a second as - * the SMC refreshes TCMz. The cpufreq cooling device sits on a - * cluster's policy CPU, so each map lists every CPU of its cluster. - */ - thermal-zones { - cpu-die-thermal { - polling-delay-passive = <1000>; - polling-delay = <1000>; - thermal-sensors = <&smc_hwmon 0>; /* TCMz */ + power-PR1b { + apple,key-id = "PR1b"; + label = "SMC PR1b"; + }; - trips { - cpu_die_passive: cpu-die-passive { - temperature = <113000>; - hysteresis = <5000>; - type = "passive"; - }; + power-PR2b { + apple,key-id = "PR2b"; + label = "SMC PR2b"; + }; - cpu-die-critical { - temperature = <118000>; - hysteresis = <0>; - type = "critical"; - }; - }; + power-PR3b { + apple,key-id = "PR3b"; + label = "SMC PR3b"; + }; - cooling-maps { - map-e { - trip = <&cpu_die_passive>; - cooling-device = - <&cpu_e0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, - <&cpu_e1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, - <&cpu_e2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, - <&cpu_e3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; - }; + power-PR3l { + apple,key-id = "PR3l"; + label = "SMC PR3l"; + }; - map-p0 { - trip = <&cpu_die_passive>; - cooling-device = - <&cpu_p00 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, - <&cpu_p01 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, - <&cpu_p02 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, - <&cpu_p03 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, - <&cpu_p04 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; - }; + power-PR4b { + apple,key-id = "PR4b"; + label = "SMC PR4b"; + }; - map-p1 { - trip = <&cpu_die_passive>; - cooling-device = - <&cpu_p10 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, - <&cpu_p11 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, - <&cpu_p12 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, - <&cpu_p13 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, - <&cpu_p14 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; - }; - }; - }; + power-PR7b { + apple,key-id = "PR7b"; + label = "SMC PR7b"; }; - /* - * The speaker amps' shared shutdown line (SDZ, AP pin 54, low = - * shutdown). One fixed regulator, the SDZ supply of all six, not - * per-amp shutdown-gpios: with GPIO_SHARED six references become a - * vote, and one amp driving the line low would not shut the other - * five down. - */ - speaker_sdz: regulator-sn012776-sdz { - compatible = "regulator-fixed"; - regulator-name = "sn012776-sdz"; - gpios = <&pinctrl 54 GPIO_ACTIVE_HIGH>; - enable-active-high; + power-PR8l { + apple,key-id = "PR8l"; + label = "SMC PR8l"; }; - /* - * The AOP firmware runs the stream (MCA cluster 0, one serializer for - * all six amps); the J616 variant of macaudio links the AOP audio - * component's speaker back-end (DAI 3) to the amps. The codecs are - * listed in the ADT provider order, so each gets its native playback - * slot (LW1 0, RW1 1, LW2 2, RW2 3, LT 4, RT 5). The idle masks are the - * ADT amp-tx-zd-config rows 0 and 1: LW1 drives zeros in the right - * amps' sense slots, RW1 in the left amps'. - */ - sound: sound { - compatible = "apple,j616-macaudio", "apple,macaudio"; - model = "MacBook Pro J616"; - #address-cells = <1>; - #size-cells = <0>; + power-PR9b { + apple,key-id = "PR9b"; + label = "SMC PR9b"; + }; - dai-link@0 { - reg = <0>; - link-name = "Speakers"; - dai-tdm-idle-mode-0 = "zero"; - dai-tdm-idle-mode-1 = "zero"; - dai-tdm-slot-tx-idle-mask-0 = <0xf0f0f0>; - dai-tdm-slot-tx-idle-mask-1 = <0x0f0f0f>; + power-PR9l { + apple,key-id = "PR9l"; + label = "SMC PR9l"; + }; - cpu { - sound-dai = <&aop_audio 3>; - }; + power-PRal { + apple,key-id = "PRal"; + label = "SMC PRal"; + }; - codec { - sound-dai = <&speaker_left_woof1>, <&speaker_right_woof1>, - <&speaker_left_woof2>, <&speaker_right_woof2>, - <&speaker_left_tweet>, <&speaker_right_tweet>; - }; - }; + power-PRbb { + apple,key-id = "PRbb"; + label = "SMC PRbb"; }; - /* - * The headphone jack: the AOP audio component's jack back-end (DAI 4) - * to the CS42L84. A card of its own, so a codec that does not answer - * costs the jack only, never the speakers. - */ - sound_jack: sound-jack { - compatible = "apple,j616-macaudio-jack", "apple,macaudio"; - model = "MacBook Pro J616 Jack"; - #address-cells = <1>; - #size-cells = <0>; + power-PRdb { + apple,key-id = "PRdb"; + label = "SMC PRdb"; + }; - dai-link@0 { - reg = <0>; - link-name = "Headphone Jack"; + power-PReb { + apple,key-id = "PReb"; + label = "SMC PReb"; + }; - cpu { - sound-dai = <&aop_audio 4>; - }; + power-PS0b { + apple,key-id = "PS0b"; + label = "SMC PS0b"; + }; - codec { - sound-dai = <&jack_codec>; - }; - }; + power-PS1b { + apple,key-id = "PS1b"; + label = "SMC PS1b"; }; -}; -&cpuidle { - status = "okay"; -}; + power-PS2b { + apple,key-id = "PS2b"; + label = "SMC PS2b"; + }; -&smc { - status = "okay"; -}; + power-PS3b { + apple,key-id = "PS3b"; + label = "SMC PS3b"; + }; -&smc_mbox { - status = "okay"; -}; + power-PS4b { + apple,key-id = "PS4b"; + label = "SMC PS4b"; + }; -&nub_spmi0 { - status = "okay"; -}; + power-PS7b { + apple,key-id = "PS7b"; + label = "SMC PS7b"; + }; -&pinctrl { - status = "okay"; + power-PS9b { + apple,key-id = "PS9b"; + label = "SMC PS9b"; + }; - wifi_clkreq_pins: wifi-clkreq-pins { - pinmux = ; + power-PS9l { + apple,key-id = "PS9l"; + label = "SMC PS9l"; }; -}; -/* - * The AP takes none of the always-on bank's interrupts: the AOP firmware - * owns some of its pins and interrupt registers, and a chained handler here - * would see their pending bits without a handler of ours. - */ -&pinctrl_nub { - status = "okay"; - /delete-property/ interrupt-controller; - /delete-property/ #interrupt-cells; - /delete-property/ interrupts; -}; + power-PSDR { + apple,key-id = "PSDR"; + label = "SMC PSDR"; + }; -&fpwm0 { - status = "okay"; -}; + power-PSal { + apple,key-id = "PSal"; + label = "SMC PSal"; + }; -/* The Type-C retimers (ADT atcrt at 0x18/0x19/0x1a) sit on this bus; no driver yet. */ -&i2c6 { - status = "okay"; -}; + power-PSbb { + apple,key-id = "PSbb"; + label = "SMC PSbb"; + }; -&mtp { - status = "okay"; -}; + power-PSeb { + apple,key-id = "PSeb"; + label = "SMC PSeb"; + }; -&mtp_mbox { - status = "okay"; -}; + power-PTHS { + apple,key-id = "PTHS"; + label = "SMC PTHS"; + }; -&mtp_dart { - status = "okay"; -}; + power-PU1C { + apple,key-id = "PU1C"; + label = "SMC PU1C"; + }; -&mtp_dockchannel { - status = "okay"; -}; + power-PU1R { + apple,key-id = "PU1R"; + label = "SMC PU1R"; + }; -&mtp_hid { - /* SMC key gp1c powers the trackpad analog front end. */ - apple,afe-smc = <&smc>; + power-PU2C { + apple,key-id = "PU2C"; + label = "SMC PU2C"; + }; - multi-touch { - firmware-name = "apple/tpmtfw-j616s.bin"; + power-PU2R { + apple,key-id = "PU2R"; + label = "SMC PU2R"; }; - keyboard: keyboard { - hid-country-code = <0>; - apple,keyboard-layout-id = <2>; + power-PW3C { + apple,key-id = "PW3C"; + label = "SMC PW3C"; }; - stm { + power-PZC0 { + apple,key-id = "PZC0"; + label = "SMC PZC0"; }; - actuator { + power-PZC1 { + apple,key-id = "PZC1"; + label = "SMC PZC1"; }; - tp-accel { + power-PZCB { + apple,key-id = "PZCB"; + label = "SMC PZCB"; }; - mtp { + power-PZCU { + apple,key-id = "PZCU"; + label = "SMC PZCU"; }; -}; -&smc_hwmon { - /* - * TCMz must stay the first temperature child: the thermal zone names - * the sensor by its index, so the shared hwmon includes below only - * append after it. - */ - temperature-TCMz { - apple,key-id = "TCMz"; - label = "CPU Die Max"; + power-PZD1 { + apple,key-id = "PZD1"; + label = "SMC PZD1"; + }; + + power-PZT0 { + apple,key-id = "PZT0"; + label = "SMC PZT0"; + }; + + power-Pb0f { + apple,key-id = "Pb0f"; + label = "SMC Pb0f"; + }; + + power-SBAP { + apple,key-id = "SBAP"; + label = "Battery Power"; }; }; -/* Read-only fan speeds and more SMC sensors; every key exists on this machine's SMC. */ +/* + * Fan speeds, limits, targets and modes, and the common SMC sensors; every + * key exists on this machine's SMC. Both fans list their target (F0Tg, + * F1Tg) and mode (F0Md, F1Md) keys, so with the driver's fan_control + * parameter fanN_target becomes writable; by default the SMC drives the + * fans and the attributes are read-only. + */ #include "hwmon-common.dtsi" #include "hwmon-fan-dual.dtsi" #include "hwmon-laptop.dtsi" @@ -569,7 +3837,6 @@ device_type = "pci"; port00: pci@0,0 { - compatible = "pciclass,0604"; reg = <0 0 0 0 0>; ranges; interrupt-map = <0 0 0 1 &port00 0 0 0 0 0 0 0 2 &port00 0 0 0 1 0 0 0 3 &port00 0 0 0 2 0 0 0 4 &port00 0 0 0 3>; @@ -592,7 +3859,7 @@ }; wifi@0,0 { - compatible = "brcm,bcm4329-fmac"; + compatible = "pci14e4,4434", "brcm,bcm4329-fmac"; reg = <0 0 0 0 0>; brcm,ccode-map-trivial; brcm,board-type = "apple,hostomel"; @@ -634,6 +3901,7 @@ compatible = "apple,panel-j616", "apple,panel-mini-led", "apple,panel"; width-mm = <345>; height-mm = <223>; + adj-height-mm = <216>; status = "disabled"; }; }; @@ -822,12 +4090,23 @@ }; /* - * The GPU stays disabled until a boot loader fills in this unit's operating - * points, leakage and regions from the ADT; the values here are one unit's. - * Enabling it also takes gfx_mbox, keeps ps_afr and ps_gfx always on and - * claims pmp_gfx always-on, so that the PMP sees the GPU on exactly while it - * is powered. + * The loaded GPU firmware keeps running across system sleep. Like the Neo's + * GFX domain, these domains must not be switched off while that image lives. + * The PMP report vote is still owned by the GPU consumer. */ +&ps_afr { + apple,always-on; +}; + +&ps_gfx { + apple,always-on; +}; + +/* The loader fills the operating points, calibration and all six memory regions. */ +&gfx_mbox { + status = "okay"; +}; + &gpu { memory-region = <&uat_ttbs>, <&uat_pagetables>, <&uat_handoff>, <&gpu_fw_data>, <&uat_l2_pagetables>, <&gpu_fw_text>; @@ -836,7 +4115,9 @@ operating-points-v2 = <&gpu_opp>; apple,cs-opp = <&gpu_cs_opp>; apple,afr-opp = <&gpu_afr_opp>; - status = "disabled"; + /* Avoid a core power cycle between interactive submissions, then release. */ + apple,idle-off-delay-ms = <16>; + status = "okay"; }; /* @@ -1092,3 +4373,66 @@ #include "t6040-j616s-extdisp-mem-loader.dtsi" #include "t6040-j616s-extdisp-hdmi.dtsi" #include "t6040-j616s-extdisp-usbc.dtsi" +/* Additional Abbey PMICs on the two independent NUB SPMI buses. */ +&nub_spmi1 { + status = "okay"; + + pmic@e { + compatible = "apple,abbey-pmic", "apple,spmi-nvmem"; + reg = <0xe SPMI_USID>; + }; +}; + +&nub_spmi2 { + status = "okay"; + + pmic@e { + compatible = "apple,abbey-pmic", "apple,spmi-nvmem"; + reg = <0xe SPMI_USID>; + }; +}; + +&ps_nub_spmi_ahb { + status = "okay"; +}; + +&ps_nub_spmi1 { + status = "okay"; +}; + +&ps_nub_spmi2 { + status = "okay"; +}; + +&pinctrl { + sd_clkreq_pins: sd-clkreq-pins { + pinmux = ; + }; +}; + +&pcie0 { + interrupts = , + ; + /* Widen bus-range only with the SD power/port ownership handoff. */ + iommu-map = <0x100 &pcie_dart 1 2>, <0x200 &apcie1_dart 1 1>; + + port01: pci@1,0 { + device_type = "pci"; + reg = <0x800 0 0 0 0>; + reset-gpios = <&pinctrl 5 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&sd_clkreq_pins>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + interrupt-controller; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &port01 0 0 0 0>, + <0 0 0 2 &port01 0 0 0 1>, + <0 0 0 3 &port01 0 0 0 2>, + <0 0 0 4 &port01 0 0 0 3>; + /* The SD power switch needs an SMC service binding. */ + status = "disabled"; + }; +}; diff --git a/arch/arm64/boot/dts/apple/t6040-pmgr.dtsi b/arch/arm64/boot/dts/apple/t6040-pmgr.dtsi index 8c05fb4d4d5d3..e0af3e1903cfb 100644 --- a/arch/arm64/boot/dts/apple/t6040-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t6040-pmgr.dtsi @@ -140,6 +140,24 @@ power-domains = <&ps_sio>; }; + ps_spi_p: power-controller@198 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x198 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "spi_p"; + power-domains = <&ps_sio>; + }; + + ps_spi2: power-controller@1c8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1c8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "spi2"; + power-domains = <&ps_spi_p>; + }; + ps_ans: power-controller@370 { compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; reg = <0x370 4>; @@ -178,6 +196,16 @@ power-domains = <&ps_ans>, <&ps_apcie_st1>; apple,always-on; }; + + ps_sep: power-controller@c00 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0xc00 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "sep"; + power-domains = <&ps_fab3_soc>; + apple,always-on; + }; }; /* @@ -269,6 +297,7 @@ #power-domain-cells = <0>; #reset-cells = <0>; label = "avd_sys"; + power-domains = <&ps_fab0_soc>; }; ps_afc: power-controller@178 { @@ -287,6 +316,7 @@ #reset-cells = <0>; label = "pmp"; apple,always-on; + power-domains = <&ps_pms>; }; ps_pms_sram: power-controller@418 { @@ -296,6 +326,7 @@ #reset-cells = <0>; label = "pms_sram"; apple,always-on; + power-domains = <&ps_pms>; }; ps_fab6_soc: power-controller@480 { @@ -330,6 +361,7 @@ #reset-cells = <0>; label = "atc0_common"; apple,always-on; + power-domains = <&ps_fab2_soc>; }; ps_atc1_common: power-controller@3b8 { @@ -339,6 +371,7 @@ #reset-cells = <0>; label = "atc1_common"; apple,always-on; + power-domains = <&ps_fab2_soc>; }; ps_atc2_common: power-controller@3e0 { @@ -348,6 +381,7 @@ #reset-cells = <0>; label = "atc2_common"; apple,always-on; + power-domains = <&ps_fab2_soc>; }; }; @@ -478,3 +512,1735 @@ power-domains = <&ps_atc2_phymxwrap>; }; }; + +/* + * Remaining ADT power states. Keep providers without a qualified consumer + * disabled: registering an unused genpd can power down firmware-owned blocks. + * Enable the provider and its parents together with the consumer. + */ +&pmgr { + ps_apcie_gp: power-controller@100 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x100 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "apcie_gp"; + status = "disabled"; + }; + + ps_fab4_soc: power-controller@128 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x128 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "fab4_soc"; + power-domains = <&ps_afc>; + apple,always-on; + status = "disabled"; + }; + + ps_sio_cpu: power-controller@130 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x130 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "sio_cpu"; + power-domains = <&ps_sio>; + apple,always-on; + status = "disabled"; + }; + + ps_fpwm1: power-controller@140 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x140 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "fpwm1"; + power-domains = <&ps_sio>; + status = "disabled"; + }; + + ps_fpwm2: power-controller@148 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x148 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "fpwm2"; + power-domains = <&ps_sio>; + status = "disabled"; + }; + + ps_i2c0: power-controller@150 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x150 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "i2c0"; + power-domains = <&ps_sio>; + status = "disabled"; + }; + + ps_i2c5: power-controller@178 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x178 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "i2c5"; + power-domains = <&ps_sio>; + status = "disabled"; + }; + + ps_i2c7: power-controller@188 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x188 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "i2c7"; + power-domains = <&ps_sio>; + status = "disabled"; + }; + + + ps_uart_p: power-controller@1a0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1a0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "uart_p"; + power-domains = <&ps_sio>; + status = "disabled"; + }; + + ps_dpa_p: power-controller@1a8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1a8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dpa_p"; + power-domains = <&ps_sio>; + apple,always-on; + status = "disabled"; + }; + + ps_aes: power-controller@1b0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1b0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "aes"; + power-domains = <&ps_sio>; + apple,always-on; + status = "disabled"; + }; + + ps_spi0: power-controller@1b8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1b8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "spi0"; + power-domains = <&ps_spi_p>; + status = "disabled"; + }; + + ps_spi1: power-controller@1c0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1c0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "spi1"; + power-domains = <&ps_spi_p>; + status = "disabled"; + }; + + + ps_spi3: power-controller@1d0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1d0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "spi3"; + power-domains = <&ps_spi_p>; + status = "disabled"; + }; + + ps_spi4: power-controller@1d8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1d8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "spi4"; + power-domains = <&ps_spi_p>; + status = "disabled"; + }; + + ps_spi5: power-controller@1e0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1e0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "spi5"; + power-domains = <&ps_spi_p>; + status = "disabled"; + }; + + ps_qspi: power-controller@1e8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1e8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "qspi"; + power-domains = <&ps_spi_p>; + status = "disabled"; + }; + + ps_uart_n: power-controller@1f0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1f0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "uart_n"; + power-domains = <&ps_uart_p>; + status = "disabled"; + }; + + ps_uart0: power-controller@1f8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1f8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "uart0"; + power-domains = <&ps_uart_p>; + status = "disabled"; + }; + + ps_uart1: power-controller@200 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x200 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "uart1"; + power-domains = <&ps_uart_p>; + status = "disabled"; + }; + + ps_uart2: power-controller@208 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x208 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "uart2"; + power-domains = <&ps_uart_p>; + status = "disabled"; + }; + + ps_uart3: power-controller@210 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x210 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "uart3"; + power-domains = <&ps_uart_p>; + status = "disabled"; + }; + + ps_uart4: power-controller@218 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x218 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "uart4"; + power-domains = <&ps_uart_p>; + status = "disabled"; + }; + + ps_uart5: power-controller@220 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x220 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "uart5"; + power-domains = <&ps_uart_p>; + status = "disabled"; + }; + + ps_uart6: power-controller@228 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x228 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "uart6"; + power-domains = <&ps_uart_p>; + status = "disabled"; + }; + + ps_dpa0: power-controller@230 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x230 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dpa0"; + power-domains = <&ps_dpa_p>; + status = "disabled"; + }; + + ps_dpa1: power-controller@238 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x238 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dpa1"; + power-domains = <&ps_dpa_p>; + status = "disabled"; + }; + + ps_dpa2: power-controller@240 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x240 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dpa2"; + power-domains = <&ps_dpa_p>; + status = "disabled"; + }; + + ps_dpa3: power-controller@248 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x248 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dpa3"; + power-domains = <&ps_dpa_p>; + status = "disabled"; + }; + + ps_dpa4: power-controller@250 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x250 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dpa4"; + power-domains = <&ps_dpa_p>; + status = "disabled"; + }; + + ps_disp_sys: power-controller@258 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x258 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "disp_sys"; + power-domains = <&ps_fab6_soc>; + status = "disabled"; + }; + + ps_amcc4: power-controller@260 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x260 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "amcc4"; + power-domains = <&ps_afc>; + apple,always-on; + status = "disabled"; + }; + + ps_amcc5: power-controller@268 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x268 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "amcc5"; + power-domains = <&ps_afc>; + apple,always-on; + status = "disabled"; + }; + + ps_amcc6: power-controller@270 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x270 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "amcc6"; + power-domains = <&ps_afc>; + apple,always-on; + status = "disabled"; + }; + + ps_amcc7: power-controller@278 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x278 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "amcc7"; + power-domains = <&ps_afc>; + apple,always-on; + status = "disabled"; + }; + + ps_amcc12: power-controller@280 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x280 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "amcc12"; + power-domains = <&ps_afc>; + apple,always-on; + status = "disabled"; + }; + + ps_amcc13: power-controller@288 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x288 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "amcc13"; + power-domains = <&ps_afc>; + apple,always-on; + status = "disabled"; + }; + + ps_amcc14: power-controller@290 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x290 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "amcc14"; + power-domains = <&ps_afc>; + apple,always-on; + status = "disabled"; + }; + + ps_amcc15: power-controller@298 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x298 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "amcc15"; + power-domains = <&ps_afc>; + apple,always-on; + status = "disabled"; + }; + + ps_dcs_04: power-controller@2e0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x2e0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dcs_04"; + power-domains = <&ps_amcc4>; + apple,always-on; + status = "disabled"; + }; + + ps_dcs_05: power-controller@2e8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x2e8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dcs_05"; + power-domains = <&ps_amcc5>; + apple,always-on; + status = "disabled"; + }; + + ps_dcs_06: power-controller@2f0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x2f0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dcs_06"; + power-domains = <&ps_amcc6>; + apple,always-on; + status = "disabled"; + }; + + ps_dcs_07: power-controller@2f8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x2f8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dcs_07"; + power-domains = <&ps_amcc7>; + apple,always-on; + status = "disabled"; + }; + + ps_dcs_12: power-controller@300 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x300 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dcs_12"; + power-domains = <&ps_amcc12>; + apple,always-on; + status = "disabled"; + }; + + ps_dcs_13: power-controller@308 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x308 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dcs_13"; + power-domains = <&ps_amcc13>; + apple,always-on; + status = "disabled"; + }; + + ps_dcs_14: power-controller@310 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x310 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dcs_14"; + power-domains = <&ps_amcc14>; + apple,always-on; + status = "disabled"; + }; + + ps_dcs_15: power-controller@318 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x318 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dcs_15"; + power-domains = <&ps_amcc15>; + apple,always-on; + status = "disabled"; + }; + + ps_disp_fe: power-controller@368 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x368 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "disp_fe"; + power-domains = <&ps_disp_sys>; + status = "disabled"; + }; + + ps_apcie_sys_gp: power-controller@390 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x390 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "apcie_sys_gp"; + power-domains = <&ps_fab3_soc>, <&ps_apcie_gp>; + status = "disabled"; + }; + + ps_apcie_phy_sw: power-controller@398 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x398 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "apcie_phy_sw"; + power-domains = <&ps_apcie_sys_st0>, <&ps_apcie_sys_st1>; + status = "disabled"; + }; + + ps_jpg: power-controller@3a0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x3a0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "jpg"; + power-domains = <&ps_fab4_soc>; + status = "disabled"; + }; + + ps_ane_sys: power-controller@3c0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x3c0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "ane_sys"; + power-domains = <&ps_fab5_soc>, <&ps_fab4_soc>; + status = "disabled"; + }; + +}; + +&pmgr_aux { + ps_ecpu_0: power-controller@0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "ecpu_0"; + status = "disabled"; + }; + + ps_ecpu_1: power-controller@8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "ecpu_1"; + status = "disabled"; + }; + + ps_ecpu_2: power-controller@10 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x10 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "ecpu_2"; + status = "disabled"; + }; + + ps_ecpu_3: power-controller@18 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x18 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "ecpu_3"; + status = "disabled"; + }; + + ps_pcpu0_0: power-controller@20 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x20 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "pcpu0_0"; + status = "disabled"; + }; + + ps_pcpu0_1: power-controller@28 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x28 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "pcpu0_1"; + status = "disabled"; + }; + + ps_pcpu0_2: power-controller@30 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x30 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "pcpu0_2"; + status = "disabled"; + }; + + ps_pcpu0_3: power-controller@38 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x38 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "pcpu0_3"; + status = "disabled"; + }; + + ps_pcpu0_4: power-controller@40 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x40 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "pcpu0_4"; + status = "disabled"; + }; + + ps_pcpu0_5: power-controller@48 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x48 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "pcpu0_5"; + status = "disabled"; + }; + + ps_pcpu1_0: power-controller@50 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x50 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "pcpu1_0"; + status = "disabled"; + }; + + ps_pcpu1_1: power-controller@58 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x58 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "pcpu1_1"; + status = "disabled"; + }; + + ps_pcpu1_2: power-controller@60 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x60 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "pcpu1_2"; + status = "disabled"; + }; + + ps_pcpu1_3: power-controller@68 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x68 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "pcpu1_3"; + status = "disabled"; + }; + + ps_pcpu1_4: power-controller@70 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x70 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "pcpu1_4"; + status = "disabled"; + }; + + ps_pcpu1_5: power-controller@78 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x78 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "pcpu1_5"; + status = "disabled"; + }; + + ps_ecpm: power-controller@80 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x80 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "ecpm"; + status = "disabled"; + }; + + ps_pcpm0: power-controller@88 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x88 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "pcpm0"; + status = "disabled"; + }; + + ps_pcpm1: power-controller@90 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x90 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "pcpm1"; + status = "disabled"; + }; + + ps_dwi: power-controller@108 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x108 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dwi"; + status = "disabled"; + }; + + ps_aic: power-controller@118 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x118 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "aic"; + power-domains = <&ps_pms>; + status = "disabled"; + }; + + ps_pmc: power-controller@120 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x120 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "pmc"; + power-domains = <&ps_pms>; + apple,always-on; + status = "disabled"; + }; + + ps_pms_c1ppt: power-controller@128 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x128 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "pms_c1ppt"; + power-domains = <&ps_pms>; + apple,always-on; + status = "disabled"; + }; + + ps_soc_dpe: power-controller@130 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x130 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "soc_dpe"; + power-domains = <&ps_pms>; + apple,always-on; + status = "disabled"; + }; + + ps_pmgr_soc_ocla: power-controller@138 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x138 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "pmgr_soc_ocla"; + status = "disabled"; + }; + + ps_pms_fpwm0: power-controller@140 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x140 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "pms_fpwm0"; + power-domains = <&ps_pms>; + apple,always-on; + status = "disabled"; + }; + + ps_pms_fpwm1: power-controller@148 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x148 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "pms_fpwm1"; + power-domains = <&ps_pms>; + apple,always-on; + status = "disabled"; + }; + + ps_pms_fpwm2: power-controller@150 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x150 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "pms_fpwm2"; + power-domains = <&ps_pms>; + apple,always-on; + status = "disabled"; + }; + + ps_pms_fpwm3: power-controller@158 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x158 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "pms_fpwm3"; + power-domains = <&ps_pms>; + apple,always-on; + status = "disabled"; + }; + + ps_pms_fpwm4: power-controller@160 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x160 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "pms_fpwm4"; + power-domains = <&ps_pms>; + apple,always-on; + status = "disabled"; + }; + + ps_fab0_soc: power-controller@168 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x168 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "fab0_soc"; + power-domains = <&ps_afi>; + apple,always-on; + }; + + ps_amcc0: power-controller@180 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x180 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "amcc0"; + power-domains = <&ps_afc>; + apple,always-on; + status = "disabled"; + }; + + ps_dcs_00: power-controller@188 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x188 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dcs_00"; + power-domains = <&ps_amcc0>; + apple,always-on; + status = "disabled"; + }; + + ps_rom: power-controller@190 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x190 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "rom"; + power-domains = <&ps_amcc0>; + status = "disabled"; + }; + + ps_amcc1: power-controller@198 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x198 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "amcc1"; + power-domains = <&ps_afc>; + apple,always-on; + status = "disabled"; + }; + + ps_dcs_01: power-controller@1a0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1a0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dcs_01"; + power-domains = <&ps_amcc1>; + apple,always-on; + status = "disabled"; + }; + + ps_amcc2: power-controller@1a8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1a8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "amcc2"; + power-domains = <&ps_afc>; + apple,always-on; + status = "disabled"; + }; + + ps_dcs_02: power-controller@1b0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1b0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dcs_02"; + power-domains = <&ps_amcc2>; + apple,always-on; + status = "disabled"; + }; + + ps_amcc3: power-controller@1b8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1b8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "amcc3"; + power-domains = <&ps_afc>; + apple,always-on; + status = "disabled"; + }; + + ps_dcs_03: power-controller@1c0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1c0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dcs_03"; + power-domains = <&ps_amcc3>; + apple,always-on; + status = "disabled"; + }; + + ps_amcc8: power-controller@1c8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1c8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "amcc8"; + power-domains = <&ps_afc>; + apple,always-on; + status = "disabled"; + }; + + ps_dcs_08: power-controller@1d0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1d0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dcs_08"; + power-domains = <&ps_amcc8>; + apple,always-on; + status = "disabled"; + }; + + ps_amcc9: power-controller@1d8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1d8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "amcc9"; + power-domains = <&ps_afc>; + apple,always-on; + status = "disabled"; + }; + + ps_dcs_09: power-controller@1e0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1e0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dcs_09"; + power-domains = <&ps_amcc9>; + apple,always-on; + status = "disabled"; + }; + + ps_amcc10: power-controller@1e8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1e8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "amcc10"; + power-domains = <&ps_afc>; + apple,always-on; + status = "disabled"; + }; + + ps_dcs_10: power-controller@1f0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1f0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dcs_10"; + power-domains = <&ps_amcc10>; + apple,always-on; + status = "disabled"; + }; + + ps_amcc11: power-controller@1f8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x1f8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "amcc11"; + power-domains = <&ps_afc>; + apple,always-on; + status = "disabled"; + }; + + ps_dcs_11: power-controller@200 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x200 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dcs_11"; + power-domains = <&ps_amcc11>; + apple,always-on; + status = "disabled"; + }; + + ps_venc0_sys: power-controller@278 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x278 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "venc0_sys"; + power-domains = <&ps_fab0_soc>; + status = "disabled"; + }; + + ps_fab_gw_0: power-controller@288 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x288 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "fab_gw_0"; + apple,always-on; + status = "disabled"; + }; + + ps_fab_gw_1: power-controller@290 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x290 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "fab_gw_1"; + apple,always-on; + status = "disabled"; + }; + + ps_fab_afr: power-controller@298 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x298 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "fab_afr"; + apple,always-on; + status = "disabled"; + }; + + ps_fab1_soc: power-controller@2d0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x2d0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "fab1_soc"; + power-domains = <&ps_afi>; + apple,always-on; + status = "disabled"; + }; + + ps_scodec: power-controller@328 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x328 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "scodec"; + power-domains = <&ps_fab1_soc>; + status = "disabled"; + }; + + ps_scodec_streaming: power-controller@330 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x330 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "scodec_streaming"; + power-domains = <&ps_scodec>; + status = "disabled"; + }; + + ps_fab2_soc: power-controller@338 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x338 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "fab2_soc"; + power-domains = <&ps_afi>; + apple,always-on; + }; + + ps_dispext0_sys: power-controller@340 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x340 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dispext0_sys"; + power-domains = <&ps_fab2_soc>; + status = "disabled"; + }; + + ps_dispext0_fe: power-controller@348 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x348 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dispext0_fe"; + power-domains = <&ps_dispext0_sys>; + status = "disabled"; + }; + + ps_dispext0_cpu: power-controller@350 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x350 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dispext0_cpu"; + power-domains = <&ps_dispext0_fe>; + status = "disabled"; + }; + + ps_atc0_cio: power-controller@398 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x398 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "atc0_cio"; + power-domains = <&ps_atc0_common>; + status = "disabled"; + }; + + ps_atc0_pcie: power-controller@3a0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x3a0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "atc0_pcie"; + power-domains = <&ps_atc0_common>; + status = "disabled"; + }; + + ps_atc0_cio_pcie: power-controller@3a8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x3a8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "atc0_cio_pcie"; + power-domains = <&ps_atc0_cio>; + status = "disabled"; + }; + + ps_atc0_cio_usb: power-controller@3b0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x3b0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "atc0_cio_usb"; + power-domains = <&ps_atc0_cio>; + status = "disabled"; + }; + + ps_atc1_cio: power-controller@3c0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x3c0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "atc1_cio"; + power-domains = <&ps_atc1_common>; + status = "disabled"; + }; + + ps_atc1_pcie: power-controller@3c8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x3c8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "atc1_pcie"; + power-domains = <&ps_atc1_common>; + status = "disabled"; + }; + + ps_atc1_cio_pcie: power-controller@3d0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x3d0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "atc1_cio_pcie"; + power-domains = <&ps_atc1_cio>; + status = "disabled"; + }; + + ps_atc1_cio_usb: power-controller@3d8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x3d8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "atc1_cio_usb"; + power-domains = <&ps_atc1_cio>; + status = "disabled"; + }; + + ps_atc2_cio: power-controller@3e8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x3e8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "atc2_cio"; + power-domains = <&ps_atc2_common>; + status = "disabled"; + }; + + ps_atc2_pcie: power-controller@3f0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x3f0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "atc2_pcie"; + power-domains = <&ps_atc2_common>; + status = "disabled"; + }; + + ps_atc2_cio_pcie: power-controller@3f8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x3f8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "atc2_cio_pcie"; + power-domains = <&ps_atc2_cio>; + status = "disabled"; + }; + + ps_atc2_cio_usb: power-controller@400 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x400 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "atc2_cio_usb"; + power-domains = <&ps_atc2_cio>; + status = "disabled"; + }; + + ps_atc3_common: power-controller@408 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x408 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "atc3_common"; + power-domains = <&ps_fab2_soc>; + status = "disabled"; + }; + + ps_atc3_cio: power-controller@420 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x420 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "atc3_cio"; + power-domains = <&ps_atc3_common>; + status = "disabled"; + }; + + ps_atc3_pcie: power-controller@428 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x428 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "atc3_pcie"; + power-domains = <&ps_atc3_common>; + status = "disabled"; + }; + + ps_atc3_cio_pcie: power-controller@430 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x430 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "atc3_cio_pcie"; + power-domains = <&ps_atc3_cio>; + status = "disabled"; + }; + + ps_atc3_cio_usb: power-controller@438 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x438 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "atc3_cio_usb"; + power-domains = <&ps_atc3_cio>; + status = "disabled"; + }; + + ps_fab5_soc: power-controller@440 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x440 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "fab5_soc"; + power-domains = <&ps_afi>; + apple,always-on; + status = "disabled"; + }; + + ps_sbr: power-controller@448 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x448 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "sbr"; + power-domains = <&ps_fab5_soc>; + apple,always-on; + status = "disabled"; + }; + + ps_gpio: power-controller@450 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x450 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "gpio"; + power-domains = <&ps_sbr>; + status = "disabled"; + }; + + ps_msg: power-controller@458 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x458 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "msg"; + power-domains = <&ps_sbr>; + status = "disabled"; + }; + + ps_ap_tmm: power-controller@468 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x468 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "ap_tmm"; + power-domains = <&ps_sbr>; + status = "disabled"; + }; + + ps_msr0: power-controller@470 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x470 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "msr0"; + power-domains = <&ps_fab5_soc>; + status = "disabled"; + }; + + ps_msr0_ase_core: power-controller@478 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x478 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "msr0_ase_core"; + power-domains = <&ps_msr0>; + status = "disabled"; + }; + + ps_fab7_soc: power-controller@488 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x488 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "fab7_soc"; + power-domains = <&ps_afi>; + apple,always-on; + status = "disabled"; + }; + + ps_dispext1_sys: power-controller@490 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x490 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dispext1_sys"; + power-domains = <&ps_fab7_soc>; + status = "disabled"; + }; + + ps_dispext1_fe: power-controller@498 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x498 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dispext1_fe"; + power-domains = <&ps_dispext1_sys>; + status = "disabled"; + }; + + ps_dispext1_cpu: power-controller@4a0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x4a0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "dispext1_cpu"; + power-domains = <&ps_dispext1_fe>; + status = "disabled"; + }; +}; + +&pmgr_ane { + ps_ane_mpm: power-controller@0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "ane_mpm"; + power-domains = <&ps_ane_sys>; + status = "disabled"; + }; + + ps_ane_cpu: power-controller@8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "ane_cpu"; + power-domains = <&ps_ane_sys>; + status = "disabled"; + }; + + ps_ane_td: power-controller@10 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x10 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "ane_td"; + power-domains = <&ps_ane_sys>; + status = "disabled"; + }; + + ps_ane_base: power-controller@18 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x18 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "ane_base"; + power-domains = <&ps_ane_td>; + status = "disabled"; + }; +}; + +&pmgr_mini { + ps_debug_gated: power-controller@0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "debug_gated"; + apple,always-on; + status = "disabled"; + }; + + ps_nub_spmi_ahb: power-controller@58 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x58 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "nub_spmi_ahb"; + apple,always-on; + status = "disabled"; + }; + + ps_nub_spmi0: power-controller@60 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x60 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "nub_spmi0"; + power-domains = <&ps_nub_spmi_ahb>; + apple,always-on; + status = "disabled"; + }; + + ps_nub_spmi_a2: power-controller@78 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x78 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "nub_spmi_a2"; + apple,always-on; + status = "disabled"; + }; + + ps_nub_spi0: power-controller@80 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x80 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "nub_spi0"; + apple,always-on; + status = "disabled"; + }; + + ps_nub_aon: power-controller@88 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x88 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "nub_aon"; + apple,always-on; + status = "disabled"; + }; + + ps_nub_ocla: power-controller@98 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x98 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "nub_ocla"; + apple,always-on; + status = "disabled"; + }; + + ps_atc3_common_dp: power-controller@b8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0xb8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "atc3_common_dp"; + status = "disabled"; + }; + + ps_nub_spmi1: power-controller@c0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0xc0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "nub_spmi1"; + power-domains = <&ps_nub_spmi_ahb>; + apple,always-on; + status = "disabled"; + }; + + ps_atc3_usb_aon: power-controller@e0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0xe0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "atc3_usb_aon"; + status = "disabled"; + }; + + ps_nub_spmi2: power-controller@e8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0xe8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "nub_spmi2"; + power-domains = <&ps_nub_spmi_ahb>; + apple,always-on; + status = "disabled"; + }; + + ps_nub_spmi3: power-controller@f0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0xf0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "nub_spmi3"; + power-domains = <&ps_nub_spmi_ahb>; + apple,always-on; + status = "disabled"; + }; + + ps_nub_spmi4: power-controller@f8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0xf8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "nub_spmi4"; + power-domains = <&ps_nub_spmi_ahb>; + apple,always-on; + status = "disabled"; + }; + + ps_nub_fabric: power-controller@100 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x100 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "nub_fabric"; + apple,always-on; + status = "disabled"; + }; + + ps_nub_sram: power-controller@108 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x108 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "nub_sram"; + power-domains = <&ps_nub_fabric>; + apple,always-on; + status = "disabled"; + }; + + ps_debug_switch: power-controller@110 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x110 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "debug_switch"; + apple,always-on; + status = "disabled"; + }; + + ps_atc3_usb: power-controller@148 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x148 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "atc3_usb"; + power-domains = <&ps_atc3_common>, <&ps_atc3_usb_aon>; + status = "disabled"; + }; + + ps_atc3_phymxwrap: power-controller@168 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x168 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "atc3_phymxwrap"; + power-domains = <&ps_atc3_common>, <&ps_atc3_usb_aon>; + status = "disabled"; + }; +}; + +&pmgr_disp { + ps_disp_cpu: power-controller@0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "disp_cpu"; + power-domains = <&ps_disp_fe>; + status = "disabled"; + }; +}; + +&pmgr_gfx { + ps_gpx: power-controller@0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "gpx"; + apple,always-on; + status = "disabled"; + }; +}; + +&pmgr_venc { + ps_venc0_dma: power-controller@0 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x0 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "venc0_dma"; + power-domains = <&ps_venc0_sys>; + status = "disabled"; + }; + + ps_venc0_pipe4: power-controller@8 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x8 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "venc0_pipe4"; + power-domains = <&ps_venc0_dma>; + status = "disabled"; + }; + + ps_venc0_pipe5: power-controller@10 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x10 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "venc0_pipe5"; + power-domains = <&ps_venc0_dma>; + status = "disabled"; + }; + + ps_venc0_me0: power-controller@18 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x18 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "venc0_me0"; + power-domains = <&ps_venc0_dma>; + status = "disabled"; + }; + + ps_venc0_me1: power-controller@20 { + compatible = "apple,t6041-pmgr-pwrstate", "apple,t8103-pmgr-pwrstate"; + reg = <0x20 4>; + #power-domain-cells = <0>; + #reset-cells = <0>; + label = "venc0_me1"; + power-domains = <&ps_venc0_me0>; + status = "disabled"; + }; +}; diff --git a/arch/arm64/boot/dts/apple/t6040.dtsi b/arch/arm64/boot/dts/apple/t6040.dtsi index 7a4a2ff871b32..961af1b7b5bd4 100644 --- a/arch/arm64/boot/dts/apple/t6040.dtsi +++ b/arch/arm64/boot/dts/apple/t6040.dtsi @@ -616,7 +616,8 @@ <0x2 0x88000000 0x0 0x3758000>; reg-names = "asc", "sgx"; mboxes = <&gfx_mbox>; - power-domains = <&ps_gfx>; + power-domains = <&pmp_gfx>; + apple,clpc = <&clpc>; status = "disabled"; }; @@ -701,6 +702,48 @@ status = "disabled"; }; + /* + * iBoot hands over a running SEP. Keep Linux off its DART at boot; + * apple_sep enables it when the driver binds. The SEP only accepts + * IOVAs in the window iBoot programmed (ADT vm-base 0x10000004000, + * vm-size 0x3ff00000), which carries the BIT(40) alias the DART + * translates. + */ + sep_dart: iommu@404c80000 { + compatible = "apple,t6041-dart", "apple,t8110-dart"; + reg = <0x4 0x04c80000 0x0 0x20000>; + interrupt-parent = <&aic>; + interrupts = ; + #iommu-cells = <1>; + apple,dma-range = /bits/ 64 <0x10000004000 0x3ff00000>; + apple,dma-offset = /bits/ 64 <0x0>; + status = "disabled"; + }; + + sep: sep@406600000 { + compatible = "apple,sep"; + reg = <0x4 0x06600000 0x0 0x88000>; + mboxes = <&sep_mbox>; + mbox-names = "mbox"; + iommus = <&sep_dart 0>; + power-domains = <&ps_sep>; + status = "disabled"; + }; + + sep_mbox: mailbox@406608000 { + compatible = "apple,t6041-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x4 0x06608000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + status = "disabled"; + }; + ans_mbox: mailbox@409608000 { compatible = "apple,t6041-asc-mailbox", "apple,asc-mailbox-v4"; reg = <0x4 0x09608000 0x0 0x4000>; @@ -826,6 +869,30 @@ status = "disabled"; }; + /* + * Touch ID. iBoot leaves the pads muxed and the controller uses its + * native chip select, so there is no pinctrl here. + */ + spi2: spi@429108000 { + compatible = "apple,t6041-spi", "apple,t8103-spi"; + reg = <0x4 0x29108000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + clocks = <&clkref>; + power-domains = <&ps_spi2>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + + mesa: fingerprint@0 { + compatible = "apple,mesa-fingerprint"; + reg = <0>; + spi-max-frequency = <8000000>; + spi-cpol; + status = "disabled"; + }; + }; + nvme: nvme@44dcc0000 { compatible = "apple,t6041-nvme-ans2"; reg = <0x4 0x4dcc0000 0x0 0x10000>, @@ -909,8 +976,8 @@ * the GPU as off and holds it at its lowest state, but claiming * it while nothing powers the GPU crashes the PMP firmware * (SError) right after it starts. It sits on the GPU's own - * power state, and a board that powers the GPU (ps_gfx stays on - * while the GPU firmware runs) claims it always-on. + * power state. The GPU consumer takes this domain, so the request + * follows the GFX domain's power lifetime. */ pmp_gfx: report@d { compatible = "apple,t6041-pmp-v2-report-entry", @@ -963,6 +1030,27 @@ /* child nodes are added in t6040-pmgr.dtsi */ }; + pmgr_ane: power-management@50228c000 { + compatible = "apple,t6041-pmgr", "apple,t8103-pmgr", "syscon", "simple-mfd"; + reg = <0x5 0x0228c000 0x0 0x4000>; + #address-cells = <1>; + #size-cells = <1>; + }; + + pmgr_disp: power-management@502290000 { + compatible = "apple,t6041-pmgr", "apple,t8103-pmgr", "syscon", "simple-mfd"; + reg = <0x5 0x02290000 0x0 0x4000>; + #address-cells = <1>; + #size-cells = <1>; + }; + + pmgr_venc: power-management@502808000 { + compatible = "apple,t6041-pmgr", "apple,t8103-pmgr", "syscon", "simple-mfd"; + reg = <0x5 0x02808000 0x0 0x4000>; + #address-cells = <1>; + #size-cells = <1>; + }; + pmgr_isp: power-management@502284000 { compatible = "apple,t6041-pmgr", "apple,t8103-pmgr", "syscon", "simple-mfd"; #address-cells = <1>; @@ -1028,6 +1116,7 @@ clpc: performance-controller@1 { compatible = "apple,t6041-clpc"; reg = <1>; + }; }; @@ -1722,6 +1811,461 @@ phy-names = "usb2-phy", "usb3-phy"; status = "disabled"; }; + /* Additional DARTs; enabled together with their DMA consumers. */ + ave0_dart1: iommu@283080000 { + compatible = "apple,t6041-dart", "apple,t8110-dart"; + reg = <0x2 0x83080000 0x0 0xc000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_venc0_sys>; + status = "disabled"; + }; + + ave0_dart0: iommu@2830a0000 { + compatible = "apple,t6041-dart", "apple,t8110-dart"; + reg = <0x2 0x830a0000 0x0 0xc000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_venc0_sys>; + status = "disabled"; + }; + + apr0_dart: iommu@301020000 { + compatible = "apple,t6041-dart", "apple,t8110-dart"; + reg = <0x3 0x01020000 0x0 0xc000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_scodec>; + status = "disabled"; + }; + + apciec0_dart: iommu@384000000 { + compatible = "apple,t6041-dart", "apple,t8110-dart"; + reg = <0x3 0x84000000 0x0 0x20000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + status = "disabled"; + }; + + apciec1_dart: iommu@38c000000 { + compatible = "apple,t6041-dart", "apple,t8110-dart"; + reg = <0x3 0x8c000000 0x0 0x20000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + status = "disabled"; + }; + + apciec2_dart: iommu@394000000 { + compatible = "apple,t6041-dart", "apple,t8110-dart"; + reg = <0x3 0x94000000 0x0 0x20000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + status = "disabled"; + }; + + dispext0_dart: iommu@3a2300000 { + compatible = "apple,t6041-dart", "apple,t8110-dart"; + reg = <0x3 0xa2300000 0x0 0xc000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + status = "disabled"; + }; + + dcpext0_dart: iommu@3a2340000 { + compatible = "apple,t6041-dart", "apple,t8110-dart"; + reg = <0x3 0xa2340000 0x0 0x20000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + status = "disabled"; + }; + + apcie1_dart: iommu@411000000 { + compatible = "apple,t6041-dart", "apple,t8110-dart"; + reg = <0x4 0x11000000 0x0 0x20000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + status = "disabled"; + }; + + sio_dart: iommu@429060000 { + compatible = "apple,t6041-dart", "apple,t8110-dart"; + reg = <0x4 0x29060000 0x0 0x20000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_sio>; + status = "disabled"; + }; + + ane0_dart0: iommu@485800000 { + compatible = "apple,t6041-dart", "apple,t8110-dart"; + reg = <0x4 0x85800000 0x0 0xc000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_ane_sys>; + status = "disabled"; + }; + + ane0_dart1: iommu@485820000 { + compatible = "apple,t6041-dart", "apple,t8110-dart"; + reg = <0x4 0x85820000 0x0 0xc000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_ane_sys>; + status = "disabled"; + }; + + ane0_dart2: iommu@485840000 { + compatible = "apple,t6041-dart", "apple,t8110-dart"; + reg = <0x4 0x85840000 0x0 0xc000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_ane_sys>; + status = "disabled"; + }; + + jpeg0_dart: iommu@487020000 { + compatible = "apple,t6041-dart", "apple,t8110-dart"; + reg = <0x4 0x87020000 0x0 0xc000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_jpg>; + status = "disabled"; + }; + + jpeg1_dart: iommu@487080000 { + compatible = "apple,t6041-dart", "apple,t8110-dart"; + reg = <0x4 0x87080000 0x0 0xc000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_jpg>; + status = "disabled"; + }; + + scaler0_dart: iommu@521260000 { + compatible = "apple,t6041-dart", "apple,t8110-dart"; + reg = <0x5 0x21260000 0x0 0xc000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + power-domains = <&ps_msr0>; + status = "disabled"; + }; + + dispext1_dart: iommu@602300000 { + compatible = "apple,t6041-dart", "apple,t8110-dart"; + reg = <0x6 0x02300000 0x0 0xc000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + status = "disabled"; + }; + + dcpext1_dart: iommu@602340000 { + compatible = "apple,t6041-dart", "apple,t8110-dart"; + reg = <0x6 0x02340000 0x0 0xc000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + status = "disabled"; + }; + + pinctrl_aop: pinctrl@510824000 { + compatible = "apple,t6041-pinctrl", "apple,t8103-pinctrl"; + reg = <0x5 0x10824000 0x0 0x4000>; + clocks = <&clkref>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pinctrl_aop 0 0 85>; + apple,npins = <85>; + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&aic>; + interrupts = , + , + , + , + , + , + ; + /* Coprocessor pins and interrupt groups need an ownership handoff. */ + status = "disabled"; + }; + + pinctrl_smc: pinctrl@50c824000 { + compatible = "apple,t6041-pinctrl", "apple,t8103-pinctrl"; + reg = <0x5 0x0c824000 0x0 0x4000>; + clocks = <&clkref>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pinctrl_smc 0 0 24>; + apple,npins = <24>; + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&aic>; + interrupts = , + , + , + , + , + , + ; + /* Coprocessor pins and interrupt groups need an ownership handoff. */ + status = "disabled"; + }; + + nub_spmi1: spmi@509054000 { + compatible = "apple,t6041-spmi", "apple,t8103-spmi"; + reg = <0x5 0x09054000 0x0 0x100>; + #address-cells = <2>; + #size-cells = <0>; + power-domains = <&ps_nub_spmi1>; + status = "disabled"; + }; + + nub_spmi2: spmi@509094000 { + compatible = "apple,t6041-spmi", "apple,t8103-spmi"; + reg = <0x5 0x09094000 0x0 0x100>; + #address-cells = <2>; + #size-cells = <0>; + power-domains = <&ps_nub_spmi2>; + status = "disabled"; + }; + + nub_spmi3: spmi@5090d4000 { + compatible = "apple,t6041-spmi", "apple,t8103-spmi"; + reg = <0x5 0x090d4000 0x0 0x100>; + #address-cells = <2>; + #size-cells = <0>; + power-domains = <&ps_nub_spmi3>; + status = "disabled"; + }; + + nub_spmi4: spmi@509114000 { + compatible = "apple,t6041-spmi", "apple,t8103-spmi"; + reg = <0x5 0x09114000 0x0 0x100>; + #address-cells = <2>; + #size-cells = <0>; + power-domains = <&ps_nub_spmi4>; + status = "disabled"; + }; + + aop_spmi0: spmi@510914000 { + compatible = "apple,t6041-spmi", "apple,t8103-spmi"; + reg = <0x5 0x10914000 0x0 0x100>; + #address-cells = <2>; + #size-cells = <0>; + /* Shared with AOP firmware; no AP bus ownership handoff yet. */ + status = "disabled"; + }; + + /* The QSPIMC input is ADT clock 434 (200 MHz). */ + clk_200m: clock-200m { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <200000000>; + clock-output-names = "clk_200m"; + }; + + spi4: spi@429110000 { + compatible = "apple,t6041-spi", "apple,t8103-spi"; + reg = <0x4 0x29110000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + clocks = <&clkref>; + power-domains = <&ps_spi4>; + #address-cells = <1>; + #size-cells = <0>; + /* Panel/TCON control belongs to the display firmware. */ + status = "disabled"; + }; + + qspi: spi@429118000 { + compatible = "apple,t6041-qspi"; + reg = <0x4 0x29118000 0x0 0x4800>; + interrupt-parent = <&aic>; + interrupts = ; + clocks = <&clk_200m>; + power-domains = <&ps_qspi>; + #address-cells = <1>; + #size-cells = <0>; + /* The firmware NOR needs a qualified partition/write policy. */ + status = "disabled"; + }; + + /* The loader supplies the SIO firmware regions and protocol tables. */ + sio: dma-controller@429e00000 { + compatible = "apple,t6041-sio"; + reg = <0x4 0x29e00000 0x0 0x8000>; + dma-channels = <128>; + #dma-cells = <1>; + mboxes = <&sio_mbox>; + iommus = <&sio_dart 0>; + power-domains = <&ps_sio_cpu>; + status = "disabled"; + }; + + sio_mbox: mailbox@429e08000 { + compatible = "apple,t6041-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x4 0x29e08000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + power-domains = <&ps_sio_cpu>; + status = "disabled"; + }; + + /* Firmware interface support is required before enabling the ANE. */ + ane: ane@484000000 { + compatible = "apple,t6041-ane"; + reg = <0x4 0x84000000 0x0 0x2000000>; + reg-names = "engine"; + interrupt-parent = <&aic>; + interrupts = ; + iommus = <&ane0_dart0 0>, <&ane0_dart1 0>, <&ane0_dart2 0>; + power-domains = <&ps_ane_cpu>; + status = "disabled"; + }; + + /* The video encoder stays disabled until its firmware backend is available. */ + ave: video-codec@283100000 { + compatible = "apple,t6041-ave"; + reg = <0x2 0x83100000 0x0 0x45c000>, + <0x2 0x83050000 0x0 0x4000>, + <0x2 0x83800000 0x0 0x800000>; + reg-names = "codec", "mailbox", "coproc"; + interrupt-parent = <&aic>; + interrupts = , + , + , + , + ; + power-domains = <&ps_venc0_dma>, <&ps_venc0_pipe4>, + <&ps_venc0_pipe5>, <&ps_venc0_me0>, <&ps_venc0_me1>; + power-domain-names = "dma", "pipe4", "pipe5", "me0", "me1"; + iommus = <&ave0_dart0 0>, <&ave0_dart1 0>; + status = "disabled"; + }; + + serial0: serial@429200000 { + compatible = "apple,s5l-uart"; + reg = <0x4 0x29200000 0x0 0x4000>; + reg-io-width = <4>; + interrupt-parent = <&aic>; + interrupts = ; + clocks = <&clkref>, <&clkref>; + clock-names = "uart", "clk_uart_baud0"; + power-domains = <&ps_uart0>; + status = "disabled"; + }; + + dcuart: serial@50883c000 { + compatible = "apple,dockchannel-uart"; + reg = <0x5 0x0883c000 0x0 0x4000>, + <0x5 0x08838000 0x0 0x4000>, + <0x5 0x0880c000 0x0 0x4000>; + reg-names = "data", "config", "irq"; + interrupt-parent = <&aic>; + interrupts = ; + apple,channel = <2>; + apple,sw-drain; + status = "disabled"; + }; + + /* ACIO 0 remains disabled until its host-router handoff is supported. */ + usb4_0_mbox: mailbox@381108000 { + compatible = "apple,t6041-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x3 0x81108000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + power-domains = <&ps_atc0_cio>; + status = "disabled"; + }; + + usb4_0_dart: iommu@381a80000 { + compatible = "apple,t6041-dart", "apple,t8110-dart"; + reg = <0x3 0x81a80000 0x0 0xc000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + status = "disabled"; + }; + + /* ACIO 1 remains disabled until its host-router handoff is supported. */ + usb4_1_mbox: mailbox@389108000 { + compatible = "apple,t6041-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x3 0x89108000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + power-domains = <&ps_atc1_cio>; + status = "disabled"; + }; + + usb4_1_dart: iommu@389a80000 { + compatible = "apple,t6041-dart", "apple,t8110-dart"; + reg = <0x3 0x89a80000 0x0 0xc000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + status = "disabled"; + }; + + /* ACIO 2 remains disabled until its host-router handoff is supported. */ + usb4_2_mbox: mailbox@391108000 { + compatible = "apple,t6041-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x3 0x91108000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + power-domains = <&ps_atc2_cio>; + status = "disabled"; + }; + + usb4_2_dart: iommu@391a80000 { + compatible = "apple,t6041-dart", "apple,t8110-dart"; + reg = <0x3 0x91a80000 0x0 0xc000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + status = "disabled"; + }; + }; #include "t6040-pmgr.dtsi" diff --git a/drivers/gpu/drm/apple/Makefile b/drivers/gpu/drm/apple/Makefile index a592e776e99ab..48a3bb9009bc7 100644 --- a/drivers/gpu/drm/apple/Makefile +++ b/drivers/gpu/drm/apple/Makefile @@ -7,7 +7,7 @@ appledrm-y += afk.o dcp.o dcp-fabric.o dcp_backlight.o dptxep.o iomfb.o parser.o appledrm-y += dcp-hdmi.o appledrm-$(CONFIG_DRM_APPLE_AUDIO) += audio.o appledrm-$(CONFIG_DRM_APPLE_AUDIO) += av.o -appledrm-y += connector.o dcp-display-metadata.o dcp-180hz.o +appledrm-y += connector.o dcp-panel-adt.o dcp-display-metadata.o dcp-180hz.o appledrm-y += ibootep.o appledrm-y += dcpext_scanout.o appledrm-y += dcpext_drm.o diff --git a/drivers/gpu/drm/apple/afk-parser-test.c b/drivers/gpu/drm/apple/afk-parser-test.c index fd8c5cb0c26e5..8b93e67fd3d5b 100644 --- a/drivers/gpu/drm/apple/afk-parser-test.c +++ b/drivers/gpu/drm/apple/afk-parser-test.c @@ -89,7 +89,22 @@ static void afk_reply_index_test(struct kunit *test) KUNIT_EXPECT_EQ(test, pending->retcode, 7U); } +static void afk_serialized_report_test(struct kunit *test) +{ + u8 dictionary[8] = {}; + unsigned int length; + + put_unaligned_le32(0xd3, dictionary); + put_unaligned_le32(1U << 24, dictionary + 4); + for (length = 0; length < sizeof(dictionary); length++) + KUNIT_EXPECT_FALSE(test, afk_compact_report_dictionary(dictionary, length)); + KUNIT_EXPECT_TRUE(test, afk_compact_report_dictionary(dictionary, sizeof(dictionary))); + put_unaligned_le32(0, dictionary); + KUNIT_EXPECT_FALSE(test, afk_compact_report_dictionary(dictionary, sizeof(dictionary))); +} + static struct kunit_case afk_parser_cases[] = { + KUNIT_CASE(afk_serialized_report_test), KUNIT_CASE(afk_compact_header_test), KUNIT_CASE(afk_short_epic_test), KUNIT_CASE(afk_short_reply_test), diff --git a/drivers/gpu/drm/apple/afk.c b/drivers/gpu/drm/apple/afk.c index 248ca42640b40..688b2d2f5c622 100644 --- a/drivers/gpu/drm/apple/afk.c +++ b/drivers/gpu/drm/apple/afk.c @@ -1024,6 +1024,13 @@ static void afk_recv_handle_compact_call(struct apple_dcp_afkep *ep, queue_work(ep->tx_wq, &r->work); } +/* A serialized report starts at its serialization header, without a command descriptor. */ +static bool afk_compact_report_dictionary(const void *payload, size_t size) +{ + return size >= 2 * sizeof(u32) && get_unaligned_le32(payload) == 0xd3 && + ((get_unaligned_le32(payload + sizeof(u32)) >> 24) & 0x1f) == 1; +} + static void afk_recv_compact_message(struct apple_dcp_afkep *ep, u16 intf_id, const void *body, size_t size) { @@ -1046,9 +1053,10 @@ static void afk_recv_compact_message(struct apple_dcp_afkep *ep, u16 intf_id, return; } if (sub->category == 0) { - /* A standard-service report carries a descriptor first. */ + /* Preserve bare dictionaries; command-style reports have a descriptor. */ if (sub->type == EPIC_SUBTYPE_STD_SERVICE && - payload_size >= sizeof(struct epic_compact_desc)) { + payload_size >= sizeof(struct epic_compact_desc) && + !afk_compact_report_dictionary(payload, payload_size)) { payload += sizeof(struct epic_compact_desc); payload_size -= sizeof(struct epic_compact_desc); } diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 2ce9fb420f2f4..8fc8cb7f7a6ab 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -268,8 +268,11 @@ static void apple_crtc_atomic_enable(struct drm_crtc *crtc, /* A cable generation can change while the CRTC stays active. */ dcp_external_recover(to_apple_crtc(crtc)->dcp); } - if (crtc_state->active) - dcp_crtc_atomic_modeset(crtc, commit); + if (crtc_state->active) { + ret = dcp_crtc_atomic_modeset(crtc, commit); + if (ret) + dcp_drm_crtc_error(apple_crtc, crtc_state, ret); + } dcp_typec_follow_done(locked); return; diff --git a/drivers/gpu/drm/apple/dcp-display-metadata.c b/drivers/gpu/drm/apple/dcp-display-metadata.c index cc4c69736d098..729eb58002f21 100644 --- a/drivers/gpu/drm/apple/dcp-display-metadata.c +++ b/drivers/gpu/drm/apple/dcp-display-metadata.c @@ -1,11 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-only OR MIT #include #include +#include #include #include #include #include "dcp-display-metadata.h" +#include "parser.h" static int dcp_metadata_timing(struct detailed_timing *timing, const struct drm_display_mode *mode, @@ -202,3 +204,169 @@ const struct drm_edid *dcp_metadata_edid(const struct dcp_display_metadata *meta } return edid; } + +/* + * This identity-only DTD is not enumerated or programmed. Keep the DCP clock, + * active area and blanking, but fit vertical sync placement into EDID's six-bit + * fields. Notch hiding expands the real sync pulse beyond those fields; the + * exact syncs remain in the authoritative TimingElements catalogue. + */ +static int dcp_panel_identity_timing(struct detailed_timing *timing, + const struct drm_display_mode *mode, + const struct dcp_display_metadata *metadata) +{ + struct drm_display_mode summary = *mode; + + /* Refuse invalid source geometry before reducing the EDID summary. */ + if (mode->vdisplay <= 0 || mode->vdisplay > 4095 || + mode->vsync_start < mode->vdisplay || mode->vsync_end <= mode->vsync_start || + mode->vsync_end > mode->vtotal) + return -EINVAL; + summary.vsync_start = mode->vdisplay + min(mode->vsync_start - mode->vdisplay, 63); + summary.vsync_end = summary.vsync_start + min(mode->vsync_end - mode->vsync_start, 63); + return dcp_metadata_timing(timing, &summary, metadata); +} + +/* EDID 1.4 range offsets extend each one-byte limit by 255. */ +static int dcp_panel_range_byte(u32 limit, u8 *out, u8 *offsets, u8 flag) +{ + if (!limit || limit > 510) + return -ERANGE; + if (limit > 255) { + limit -= 255; + *offsets |= flag; + } + *out = limit; + return 0; +} + +/* + * Supply one representable DCP timing and its catalog limits for EDID + * conformity. DRM still enumerates TimingElements, never these rounded bytes. + */ +const struct drm_edid *dcp_panel_edid(const struct dcp_display_metadata *metadata, + const struct dcp_display_mode *modes, unsigned int count, + u32 width_mm, u32 height_mm) +{ + struct edid block = {}; + struct dcp_display_metadata dimensions = *metadata; + struct detailed_non_pixel *limits = &block.detailed_timings[3].data.other_data; + struct detailed_data_monitor_range *range = &limits->data.range; + const struct drm_edid *edid; + char serial[14], name[14]; + u32 min_v = U32_MAX, max_v = 0, min_h = U32_MAX, max_h = 0, clock = 0; + u16 manufacturer = 0; + u8 sum = 0; + bool timing = false, preferred = false; + int ret; + + if (!modes || !count || strnlen(metadata->manufacturer, 4) != 3 || + metadata->product > U16_MAX || !width_mm || width_mm > 2550 || + !height_mm || height_mm > 2550) + return ERR_PTR(-EINVAL); + for (unsigned int i = 0; i < 3; i++) { + if (metadata->manufacturer[i] < 'A' || metadata->manufacturer[i] > 'Z') + return ERR_PTR(-EINVAL); + manufacturer = manufacturer << 5 | (metadata->manufacturer[i] - '@'); + } + dimensions.width_cm = DIV_ROUND_CLOSEST(width_mm, 10); + dimensions.height_cm = DIV_ROUND_CLOSEST(height_mm, 10); + for (unsigned int i = 0; i < count; i++) { + const struct dcp_display_mode *entry = &modes[i]; + struct drm_display_mode mode = entry->mode; + struct detailed_timing candidate = {}; + u32 low, high; + u32 pixels; + + if (mode.clock <= 0 || mode.clock > 2550000 || mode.htotal <= 0 || + mode.htotal > 65535 || mode.vtotal <= 0 || mode.vtotal > 65535) + return ERR_PTR(-EINVAL); + pixels = (u32)mode.htotal * mode.vtotal; + low = div_u64((u64)mode.clock * 1000, pixels); + high = DIV_ROUND_UP_ULL((u64)mode.clock * 1000, pixels); + if (entry->vrr && entry->min_vrr && entry->min_vrr <= entry->max_vrr) { + low = entry->min_vrr >> 16; + high = DIV_ROUND_UP_ULL(entry->max_vrr, 1U << 16); + } + min_v = min(min_v, low); + max_v = max(max_v, high); + min_h = min(min_h, (u32)mode.clock / mode.htotal); + max_h = max(max_h, DIV_ROUND_UP_ULL((u32)mode.clock, mode.htotal)); + clock = max_t(u32, clock, mode.clock); + /* The EDID base timing clock has units of 10 kHz. */ + mode.clock = DIV_ROUND_CLOSEST(mode.clock, 10) * 10; + ret = dcp_panel_identity_timing(&candidate, &mode, &dimensions); + if (!ret && (!timing || (!preferred && (mode.type & DRM_MODE_TYPE_PREFERRED)))) { + struct detailed_pixel_timing *pixel = &candidate.data.pixel_data; + + /* The base size is in cm, but the DTD preserves exact panel mm. */ + pixel->width_mm_lo = width_mm; + pixel->height_mm_lo = height_mm; + pixel->width_height_mm_hi = (width_mm >> 8) << 4 | height_mm >> 8; + block.detailed_timings[0] = candidate; + timing = true; + preferred = mode.type & DRM_MODE_TYPE_PREFERRED; + } + } + if (!timing || clock > 2550000) + return ERR_PTR(-ERANGE); + + memcpy(block.header, "\x00\xff\xff\xff\xff\xff\xff\x00", 8); + block.product_id.manufacturer_name = cpu_to_be16(manufacturer); + block.product_id.product_code = cpu_to_le16(metadata->product); + block.product_id.serial_number = cpu_to_le32(metadata->serial); + /* J616 is a 2024 model; this is not a panel manufacture date. */ + block.product_id.week_of_manufacture = 0xff; + block.product_id.year_of_manufacture = 2024 - 1990; + block.version = 1; + block.revision = 4; + block.input = DRM_EDID_INPUT_DIGITAL; + block.width_cm = dimensions.width_cm; + block.height_cm = dimensions.height_cm; + block.gamma = 0xff; + /* Nominal D65 reference white, without calibrated primaries or gamma. */ + block.white_x = 320 >> 2; + block.white_y = 337 >> 2; + block.blue_white_lo = (320 & 3) << 2 | (337 & 3); + block.features = DRM_EDID_FEATURE_PREFERRED_TIMING; + memset(block.standard_timings, 1, sizeof(block.standard_timings)); + snprintf(name, sizeof(name), "%.3s %04x", metadata->manufacturer, metadata->product); + ret = dcp_metadata_text(&block.detailed_timings[1], EDID_DETAIL_MONITOR_NAME, + metadata->name[0] ? metadata->name : name); + if (ret) + return ERR_PTR(ret); + if (metadata->serial) + snprintf(serial, sizeof(serial), "%08x", metadata->serial); + else + snprintf(serial, sizeof(serial), "unspecified"); + ret = dcp_metadata_text(&block.detailed_timings[2], EDID_DETAIL_MONITOR_SERIAL, + metadata->serial_text[0] ? metadata->serial_text : serial); + if (ret) + return ERR_PTR(ret); + limits->type = EDID_DETAIL_MONITOR_RANGE; + ret = dcp_panel_range_byte(min_v, &range->min_vfreq, &limits->pad2, + DRM_EDID_RANGE_OFFSET_MIN_VFREQ); + ret = ret ?: dcp_panel_range_byte(max_v, &range->max_vfreq, &limits->pad2, + DRM_EDID_RANGE_OFFSET_MAX_VFREQ); + ret = ret ?: dcp_panel_range_byte(min_h, &range->min_hfreq_khz, &limits->pad2, + DRM_EDID_RANGE_OFFSET_MIN_HFREQ); + ret = ret ?: dcp_panel_range_byte(max_h, &range->max_hfreq_khz, &limits->pad2, + DRM_EDID_RANGE_OFFSET_MAX_HFREQ); + if (ret) + return ERR_PTR(ret); + range->pixel_clock_mhz = DIV_ROUND_UP_ULL(clock, 10000); + range->flags = DRM_EDID_RANGE_LIMITS_ONLY_FLAG; + memset(&range->formula, ' ', sizeof(range->formula)); + *((u8 *)&range->formula) = '\n'; + for (unsigned int i = 0; i < sizeof(block) - 1; i++) + sum += ((const u8 *)&block)[i]; + block.checksum = -sum; + edid = drm_edid_alloc(&block, sizeof(block)); + if (!edid) + return ERR_PTR(-ENOMEM); + if (!drm_edid_valid(edid)) { + drm_edid_free(edid); + return ERR_PTR(-EINVAL); + } + return edid; +} diff --git a/drivers/gpu/drm/apple/dcp-display-metadata.h b/drivers/gpu/drm/apple/dcp-display-metadata.h index 537994420ebd6..d97b090af699d 100644 --- a/drivers/gpu/drm/apple/dcp-display-metadata.h +++ b/drivers/gpu/drm/apple/dcp-display-metadata.h @@ -18,7 +18,12 @@ struct dcp_display_metadata { struct drm_edid; struct drm_display_mode; +struct dcp_display_mode; const struct drm_edid *dcp_metadata_edid(const struct dcp_display_metadata *metadata, const struct drm_display_mode *mode); +const struct drm_edid *dcp_panel_edid(const struct dcp_display_metadata *metadata, + const struct dcp_display_mode *modes, unsigned int count, + u32 width_mm, u32 height_mm); + #endif diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index 4ffa59bcf9b68..da06e5e21eab5 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -5,6 +5,7 @@ #define __APPLE_DCP_INTERNAL_H__ #include +#include #include #include #include @@ -216,6 +217,12 @@ struct apple_dcp_hw_data { * (the T8122 dcpext), see dcp_fw_is_14_7(). */ bool fw_14_7_endpoints; + /* + * An external processor with no DCPAV service to read the EDID + * through, whose sinks' EDID is built from their firmware metadata + * (desktop 26.x), see dcp_sink_metadata(). + */ + bool sink_metadata_edid; }; enum dcp_test_point { @@ -229,6 +236,11 @@ enum dcp_test_point { * for such a DCP only. */ struct dcp_desktop_state { + /* Protected by backlight.lock, independent of framebuffer retirement. */ + struct dcp_backlight_swap backlight; + bool inherited_brightness_valid; + u32 inherited_brightness; + /* Queued swap; too large for the shared swap union. */ struct dcp_swap_submit_req_v26_6_desktop swap; /* Opaque client identity the firmware's IOMFB calls carry. */ @@ -239,6 +251,21 @@ struct dcp_desktop_state { * element before the real one is selected. */ bool mode_live; + /* Identity supplied by this fixed panel, independent of optional color data. */ + struct dcp_display_metadata panel_metadata; + struct dcp_display_metadata panel_adt_identity; + u64 panel_metadata_generation; + bool panel_metadata_valid; + atomic_t identity_log_count[4]; + u8 panel_raw_edid[4096]; + size_t panel_raw_edid_size; + /* RX records an unsupported callback until the modeset RPC has drained. */ + bool modeset_active; + bool modeset_callback_error; + /* An adaptive swap that timed out keeps fixed refresh until the next boot. */ + bool vrr_failed; + u32 present_id; + bool present_pending; /* * GAMMA_LUT: the firmware's own table, read before the first change; * the table being sent; the LUT it is composed from, if any. @@ -277,6 +304,14 @@ struct dcp_desktop_state { struct dcpext_scanout; struct dcp_hdmi; +bool dcp_desktop_present_complete(struct apple_dcp *dcp, u32 id); +void dcp_panel_metadata_replace(struct apple_dcp *dcp, + const struct dcp_display_metadata *metadata); +const struct drm_edid *dcp_panel_edid_snapshot(struct apple_dcp *dcp, + const struct dcp_display_mode *modes, + unsigned int count, u64 *generation); +bool dcp_panel_metadata_current(struct apple_dcp *dcp, u64 generation); + struct apple_dcp { /* Generation-specific wire/transaction state, sharing the common KMS owner. */ struct iomfb_m4 *v26_6; @@ -695,6 +730,8 @@ struct apple_dcp { void (*kick)(struct apple_dcp *dcp); /* Steps a running fade; fade_start is its start in jiffies. */ struct delayed_work fade_work; + struct work_struct notify_work; + struct delayed_work retry_work; unsigned long fade_start; /* Rate limit for firmware re-send requests. */ unsigned long resent_at; @@ -777,6 +814,7 @@ static inline bool dcp_fail_stop(const struct apple_dcp *dcp) iomfb_is_m4(dcp); } +bool dcp_firmware_metadata_enabled(struct apple_dcp *dcp); void dcp_firmware_metadata_connection(struct apple_dcp *dcp, u64 generation, bool connected); void dcp_firmware_metadata_hotplug(struct apple_dcp *dcp, bool connected); void dcp_firmware_metadata_announced(struct apple_dcp *dcp); @@ -901,4 +939,9 @@ static inline bool dcp_ext_dptx_607(const struct apple_dcp *dcp) #define DCP_AUDIO_MAX_CHANS 15 +void dcp_trace_panel_property(struct apple_dcp *dcp, int tag, const char *key, + size_t key_len, const void *blob, size_t len); + +void dcp_panel_edid_receive(struct apple_dcp *dcp, const void *blob, size_t len); + #endif /* __APPLE_DCP_INTERNAL_H__ */ diff --git a/drivers/gpu/drm/apple/dcp-panel-adt.c b/drivers/gpu/drm/apple/dcp-panel-adt.c new file mode 100644 index 0000000000000..5887091aa57cf --- /dev/null +++ b/drivers/gpu/drm/apple/dcp-panel-adt.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Read-only Apple device tree decoding. The wire layout is documented by + * m1n1's public src/adt.h (GPL-2.0-or-later OR BSD-2-Clause). No firmware + * implementation or captured device identifiers are used here. + */ +#include +#include +#include +#include +#include + +#include "dcp-panel-adt.h" + +struct dcp_adt_cursor { + const u8 *data; + size_t pos, size; + u32 nodes; +}; + +static const u8 *dcp_adt_bytes(struct dcp_adt_cursor *c, size_t size) +{ + const u8 *p; + + if (c->pos > c->size || size > c->size - c->pos) + return ERR_PTR(-EINVAL); + p = c->data + c->pos; + c->pos += size; + return p; +} + +/* Hash panel serial bytes into EDID's numeric serial; never log the input. */ +static u32 dcp_adt_serial(const u8 *data, size_t size) +{ + u32 serial = 2166136261U; + + for (size_t i = 0; i < size; i++) + serial = (serial ^ data[i]) * 16777619U; + return serial ?: 1; +} + +static void dcp_adt_panel_property(struct dcp_panel_adt *panel, const char *name, + const u8 *value, u32 size) +{ + struct dcp_display_metadata *id = &panel->identity; + u32 number = size == 4 ? get_unaligned_le32(value) : 0; + + if ((!strcmp(name, "vendor-id") || !strcmp(name, "display-vendor-id")) && + size == 4 && number <= 0x7fff) { + char manufacturer[4] = { (number >> 10 & 31) + '@', + (number >> 5 & 31) + '@', (number & 31) + '@', 0 }; + + if (manufacturer[0] >= 'A' && manufacturer[0] <= 'Z' && + manufacturer[1] >= 'A' && manufacturer[1] <= 'Z' && + manufacturer[2] >= 'A' && manufacturer[2] <= 'Z') + memcpy(id->manufacturer, manufacturer, sizeof(manufacturer)); + } else if ((!strcmp(name, "product-id") || !strcmp(name, "display-product-id") || + !strcmp(name, "panel-id")) && size == 4) { + id->product = number & U16_MAX; + } else if ((!strcmp(name, "serial-number") || + !strcmp(name, "raw-panel-serial-number")) && size && size <= 256) { + id->serial = dcp_adt_serial(value, size); + } else if (!strcmp(name, "display-serial-number") && size == 4) { + id->serial = number; + } else if (!strcmp(name, "width-mm") && size == 4 && number && number <= 2550) { + panel->width_mm = number; + } else if (!strcmp(name, "height-mm") && size == 4 && number && number <= 2550) { + panel->height_mm = number; + } +} + +static int dcp_adt_node(struct dcp_adt_cursor *c, struct dcp_panel_adt *panel, + u32 depth, bool product_parent) +{ + const u8 *header = dcp_adt_bytes(c, 8); + size_t props_start, props_end; + u32 props, children; + char node_name[33] = {}; + bool product, selected; + + if (IS_ERR(header)) + return PTR_ERR(header); + if (depth > 32 || ++c->nodes > 4096) + return -E2BIG; + props = get_unaligned_le32(header); + children = get_unaligned_le32(header + 4); + if (props > 4096 || children > 4096) + return -E2BIG; + props_start = c->pos; + /* Names need not be the first property. Validate every extent first. */ + for (u32 i = 0; i < props; i++) { + const u8 *prop = dcp_adt_bytes(c, 36), *value; + u32 size; + + if (IS_ERR(prop) || !memchr(prop, 0, 32)) + return -EINVAL; + size = get_unaligned_le32(prop + 32) & 0x7fffffff; + value = dcp_adt_bytes(c, ALIGN((size_t)size, 4)); + if (IS_ERR(value)) + return PTR_ERR(value); + if (!strcmp((const char *)prop, "name") && size && size <= 32 && + memchr(value, 0, size)) + memcpy(node_name, value, size); + } + props_end = c->pos; + product = depth == 1 && !strcmp(node_name, "product"); + selected = (depth == 1 && !strcmp(node_name, "panel")) || + (product_parent && !strcmp(node_name, "panel")); + /* /product carries raw-panel-serial-number on machines without /panel. */ + c->pos = props_start; + for (u32 i = 0; i < props; i++) { + const u8 *prop = c->data + c->pos; + u32 size = get_unaligned_le32(prop + 32) & 0x7fffffff; + const char *name = (const char *)prop; + + if (selected || (product && !strcmp(name, "raw-panel-serial-number"))) + dcp_adt_panel_property(panel, name, prop + 36, size); + c->pos += 36 + ALIGN((size_t)size, 4); + } + /* A valid ADT may have neither panel node; retain the OF defaults. */ + panel->found |= selected; + c->pos = props_end; + for (u32 i = 0; i < children; i++) { + int ret = dcp_adt_node(c, panel, depth + 1, product); + + if (ret) + return ret; + } + return 0; +} + +int dcp_panel_adt_parse(const void *data, size_t size, struct dcp_panel_adt *panel) +{ + struct dcp_adt_cursor cursor = { .data = data, .size = size }; + struct dcp_panel_adt parsed = *panel; + int ret; + + if (!data || size > SZ_16M) + return -EINVAL; + ret = dcp_adt_node(&cursor, &parsed, 0, false); + if (!ret) + *panel = parsed; + return ret; +} diff --git a/drivers/gpu/drm/apple/dcp-panel-adt.h b/drivers/gpu/drm/apple/dcp-panel-adt.h new file mode 100644 index 0000000000000..2d724adb7d0e1 --- /dev/null +++ b/drivers/gpu/drm/apple/dcp-panel-adt.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +#ifndef APPLE_DCP_PANEL_ADT_H +#define APPLE_DCP_PANEL_ADT_H + +#include "dcp-display-metadata.h" + +/* Only panel properties, never the machine or account identity. */ +struct dcp_panel_adt { + struct dcp_display_metadata identity; + u32 width_mm, height_mm; + bool found; +}; + +int dcp_panel_adt_parse(const void *data, size_t size, struct dcp_panel_adt *panel); +#endif diff --git a/drivers/gpu/drm/apple/dcp-panel-test.h b/drivers/gpu/drm/apple/dcp-panel-test.h new file mode 100644 index 0000000000000..05d575d975fd5 --- /dev/null +++ b/drivers/gpu/drm/apple/dcp-panel-test.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Synthetic J616s-shaped input: no /panel or /product/panel. */ +static const u8 panel_test_adt[] = { + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6e, 0x61, 0x6d, 0x65, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x2d, 0x74, 0x72, 0x65, 0x65, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x00, + 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x2d, 0x69, 0x64, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x70, 0x61, 0x72, 0x74, + 0x69, 0x61, 0x6c, 0x6c, 0x79, 0x2d, 0x6f, 0x63, 0x63, 0x6c, 0x75, 0x64, + 0x65, 0x64, 0x2d, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x6e, 0x61, 0x6d, 0x65, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x61, 0x72, 0x6d, 0x2d, + 0x69, 0x6f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x64, 0x69, 0x73, 0x70, 0x30, 0x00, 0x00, 0x00, 0x64, 0x69, 0x73, 0x70, + 0x6c, 0x61, 0x79, 0x2d, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x2d, 0x69, + 0x6e, 0x66, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x63, 0x68, 0x6f, 0x73, 0x65, 0x6e, 0x00, 0x00, 0x73, 0x65, 0x72, 0x69, + 0x61, 0x6c, 0x2d, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x53, 0x59, 0x4e, 0x54, + 0x48, 0x45, 0x54, 0x49, 0x43, 0x2d, 0x4d, 0x41, 0x43, 0x48, 0x49, 0x4e, + 0x45, 0x00, 0x00, 0x00, +}; + +/* Notch-hidden mode geometry observed through the public DRM interface. */ +static const struct dcp_display_mode panel_test_mode = { + .mode = { + .clock = 521065, .hdisplay = 3456, .hsync_start = 3464, + .hsync_end = 3496, .htotal = 3536, .vdisplay = 2160, + .vsync_start = 2278, .vsync_end = 2360, .vtotal = 2456, + .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC, + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, + }, + .vrr = true, .min_vrr = 24U << 16, .max_vrr = 120U << 16, +}; diff --git a/drivers/gpu/drm/apple/dcp-refresh.h b/drivers/gpu/drm/apple/dcp-refresh.h new file mode 100644 index 0000000000000..06a8d1c703294 --- /dev/null +++ b/drivers/gpu/drm/apple/dcp-refresh.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright 2026 Ryan Murray */ +#ifndef __APPLE_DCP_REFRESH_H__ +#define __APPLE_DCP_REFRESH_H__ + +#include + +/* Accept nominal 120 Hz and its 1000/1001 fractional timing. */ +static inline bool dcp_refresh_is_promotion(u32 rate) +{ + return rate >= (120U << 16) - (120U << 16) / 500 && + rate <= (120U << 16) + (120U << 16) / 500; +} + +/* All rates are unsigned 16.16 Hz, including fractional fixed timings. */ +static inline bool dcp_refresh_range(u32 rate, u32 minimum, u32 maximum, + bool internal, u32 *low, u32 *high) +{ + if (!minimum && !maximum && internal && dcp_refresh_is_promotion(rate)) { + minimum = 24U << 16; + maximum = rate; + } + /* A partial, inverted or out-of-mode range leaves a fixed timing. */ + if (minimum < (1U << 16) || minimum >= maximum || + maximum > rate || maximum > (65535U << 16)) + return false; + *low = minimum; + *high = maximum; + return true; +} + +/* + * Desktop 26.6 uses its own presentation clock. A deadline long past asks + * for the next permitted refresh. ARM counter values can stall the swap. + */ +static inline u64 dcp_desktop_present_deadline(bool have_surface, bool required) +{ + return have_surface && required ? 120 : 0; +} + +#endif diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 28274b11f43f3..b6af560ffc50a 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -51,6 +51,10 @@ #include "afk.h" #include "av.h" #include "dcp.h" +#include "dcp-panel-adt.h" +#include +#include +#include #include "dcp-fabric-effects.h" #include "dcp-fabric.h" #include "dcp-hdmi.h" @@ -552,9 +556,39 @@ void dcp_drm_crtc_page_flip(struct apple_dcp *dcp, ktime_t now) spin_unlock_irqrestore(&crtc->base.dev->event_lock, flags); } -static bool dcp_firmware_metadata_enabled(struct apple_dcp *dcp) +/* + * A desktop 26.x external processor has no dcpav-service-epic to read the + * sink's EDID through, but exports the TimingElements and DisplayAttributes + * of every sink it drives, on HDMI and Type-C alike. + */ +static bool dcp_sink_metadata(struct apple_dcp *dcp) { - return dcp->hw.iomfb_method_profile == DCP_IOMFB_METHODS_H17P_EXTERNAL; + return dcp->hw.sink_metadata_edid; +} + +bool dcp_firmware_metadata_enabled(struct apple_dcp *dcp) +{ + return dcp->hw.iomfb_method_profile == DCP_IOMFB_METHODS_H17P_EXTERNAL || + dcp_sink_metadata(dcp); +} + +/* + * Such metadata belongs to the sink the firmware reports, as these + * processors have no cable generation of their own: each sink change starts + * a new connection, which the metadata of an earlier sink can never match. + */ +static void dcp_desktop_metadata_new_sink(struct apple_dcp *dcp) +{ + unsigned long flags; + + spin_lock_irqsave(&dcp->metadata.lock, flags); + dcp->metadata.attributes_valid = false; + dcp->metadata.timing_valid = false; + dcp->metadata.epoch = (struct dcp_metadata_epoch) { + .generation = dcp->metadata.epoch.generation + 1, + .connected = true, + }; + spin_unlock_irqrestore(&dcp->metadata.lock, flags); } void dcp_firmware_metadata_connection(struct apple_dcp *dcp, u64 generation, bool connected) @@ -563,6 +597,10 @@ void dcp_firmware_metadata_connection(struct apple_dcp *dcp, u64 generation, boo if (!dcp_firmware_metadata_enabled(dcp)) return; + if (dcp_sink_metadata(dcp)) { + dcp_desktop_metadata_new_sink(dcp); + return; + } spin_lock_irqsave(&dcp->metadata.lock, flags); if (generation != dcp->metadata.epoch.generation || !connected) { dcp->rate_proof.connection_sequence++; @@ -635,6 +673,12 @@ void dcp_firmware_metadata_hotplug(struct apple_dcp *dcp, bool connected) if (!dcp_firmware_metadata_enabled(dcp)) return; + /* + * A desktop 26.x link can bounce without the firmware describing its + * sink again: the cable and HDMI HPD paths report a sink change. + */ + if (dcp_sink_metadata(dcp)) + return; spin_lock_irqsave(&dcp->metadata.lock, flags); if (connected) { dcp_firmware_metadata_present_locked(dcp); @@ -725,6 +769,8 @@ void dcp_firmware_metadata_timing(struct apple_dcp *dcp) !dcp->modes[i].metadata_timing_valid) continue; dcp->metadata.preferred = dcp->modes[i].mode; + /* Desktop 26.x publishes its modes without their polarity. */ + dcp->metadata.preferred.flags |= dcp->modes[i].metadata_sync_flags; dcp->metadata.timing_ticket = dcp->chunks.metadata_ticket; dcp->metadata.timing_valid = true; break; @@ -780,6 +826,214 @@ const struct drm_edid *dcp_firmware_edid(struct apple_dcp *dcp, u64 *generation) return edid; } +/* + * m1n1 reserves the original ADT as a phram node labelled "adt". Read it + * without an MTD dependency. Only the desktop panel calls this; other DCP + * profiles retain their established identity and brightness paths. + */ +void dcp_panel_read_dimensions(struct apple_dcp *dcp, struct device_node *panel_np) +{ + of_property_read_u32(panel_np, "width-mm", &dcp->panel.width_mm); + /* Read the full height first: adj-height-mm is optional. */ + of_property_read_u32(panel_np, "height-mm", &dcp->panel.height_mm); + if (dcp->notch_height) + of_property_read_u32(panel_np, "adj-height-mm", &dcp->panel.height_mm); +} + +static void dcp_panel_adt_identity(struct apple_dcp *dcp) +{ + struct dcp_panel_adt panel = { + .identity = { .manufacturer = "APP", .product = 0x0616 }, + .width_mm = dcp->panel.width_mm, + .height_mm = dcp->panel.height_mm, + }; + struct device_node *np; + const char *label; + struct resource res; + void *data; + int ret = -ENODATA; + + /* Explicit generic label when no serial or product is provided. */ + strscpy(panel.identity.name, "Apple Panel", sizeof(panel.identity.name)); + for_each_compatible_node(np, NULL, "phram") { + if (of_property_read_string(np, "label", &label) || strcmp(label, "adt")) + continue; + if (of_address_to_resource(np, 0, &res) || + !resource_size(&res) || resource_size(&res) > SZ_16M) + break; + data = memremap(res.start, resource_size(&res), MEMREMAP_WB); + if (!data) + break; + ret = dcp_panel_adt_parse(data, resource_size(&res), &panel); + memunmap(data); + break; + } + of_node_put(np); + /* The board's OF panel dimensions remain valid if ADT has none. */ + dcp->panel.width_mm = panel.width_mm; + dcp->panel.height_mm = panel.height_mm; + dcp->desktop->panel_adt_identity = panel.identity; + dev_info(dcp->dev, "panel identity fallback: ADT %s, panel node %s, dimensions %ux%u mm\n", + ret ? "unavailable" : "parsed", panel.found ? "present" : "absent", + panel.width_mm, panel.height_mm); +} + +/* Internal panel identity has no detachable Type-C service or EDID transport. */ +void dcp_panel_metadata_replace(struct apple_dcp *dcp, + const struct dcp_display_metadata *metadata) +{ + struct dcp_display_metadata merged; + unsigned long flags; + bool changed = false; + + if (!metadata || dcp->fw_compat != DCP_FIRMWARE_V_26_6_DESKTOP || + !dcp_has_panel(dcp)) + return; + spin_lock_irqsave(&dcp->metadata.lock, flags); + merged = *metadata; + if (dcp->desktop->panel_metadata_valid && + merged.product == dcp->desktop->panel_metadata.product && + !memcmp(merged.manufacturer, dcp->desktop->panel_metadata.manufacturer, 4)) { + const struct dcp_display_metadata *old = &dcp->desktop->panel_metadata; + bool same_serial = !merged.serial || merged.serial == old->serial; + + /* Zero/empty optional fields mean unspecified, not a new panel. */ + if (!merged.name[0]) + memcpy(merged.name, old->name, sizeof(merged.name)); + if (!merged.serial) + merged.serial = old->serial; + if (same_serial && !merged.serial_text[0]) + memcpy(merged.serial_text, old->serial_text, sizeof(merged.serial_text)); + if (!merged.year) + merged.year = old->year; + if (!merged.week) + merged.week = old->week; + } + if (!dcp->desktop->panel_metadata_valid || + memcmp(&dcp->desktop->panel_metadata, &merged, sizeof(merged))) { + dcp->desktop->panel_metadata = merged; + dcp->desktop->panel_metadata_valid = true; + if (!dcp->desktop->panel_raw_edid_size) { + dcp->desktop->panel_metadata_generation++; + changed = true; + } + } + spin_unlock_irqrestore(&dcp->metadata.lock, flags); + if (changed && dcp->connector) + dcp_queue_hotplug(dcp->connector); +} + +/* A checksum alone does not guarantee usable userspace identity strings. */ +static bool dcp_panel_edid_has_identity(const u8 *raw) +{ + u16 vendor = (u16)raw[8] << 8 | raw[9]; + bool model = raw[10] || raw[11]; + bool serial = raw[12] || raw[13] || raw[14] || raw[15]; + + if (vendor & 0x8000) + return false; + for (u32 shift = 0; shift <= 10; shift += 5) + if (!((vendor >> shift) & 31) || ((vendor >> shift) & 31) > 26) + return false; + for (u32 offset = 54; offset <= 108; offset += 18) { + const u8 *desc = raw + offset; + bool text = false; + + if (desc[0] || desc[1] || desc[2] || desc[4]) + continue; + for (u32 i = 5; i < 18 && desc[i] != '\n'; i++) { + if (desc[i] < 0x20 || desc[i] > 0x7e) { + text = false; + break; + } + text |= desc[i] != ' '; + } + if (desc[3] == EDID_DETAIL_MONITOR_NAME) + model |= text; + if (desc[3] == EDID_DETAIL_MONITOR_SERIAL) + serial |= text; + } + return model && serial; +} + +/* Preserve a validated real EDID ahead of any metadata-derived fallback. */ +void dcp_panel_edid_receive(struct apple_dcp *dcp, const void *blob, size_t len) +{ + const struct drm_edid *edid; + const u8 *raw; + size_t size; + unsigned long flags; + bool changed; + + if (dcp->fw_compat != DCP_FIRMWARE_V_26_6_DESKTOP || !dcp_has_panel(dcp) || + parse_panel_edid_data(blob, len, &raw, &size)) + return; + edid = drm_edid_alloc(raw, size); + if (!edid) + return; + if (!drm_edid_valid(edid) || !dcp_panel_edid_has_identity(raw)) { + drm_edid_free(edid); + return; + } + spin_lock_irqsave(&dcp->metadata.lock, flags); + changed = size != dcp->desktop->panel_raw_edid_size || + memcmp(raw, dcp->desktop->panel_raw_edid, size); + if (changed) { + memcpy(dcp->desktop->panel_raw_edid, raw, size); + dcp->desktop->panel_raw_edid_size = size; + dcp->desktop->panel_metadata_generation++; + } + spin_unlock_irqrestore(&dcp->metadata.lock, flags); + drm_edid_free(edid); + if (changed && dcp->connector) + dcp_queue_hotplug(dcp->connector); +} + +bool dcp_panel_metadata_current(struct apple_dcp *dcp, u64 generation) +{ + unsigned long flags; + bool valid; + + spin_lock_irqsave(&dcp->metadata.lock, flags); + valid = generation == dcp->desktop->panel_metadata_generation; + spin_unlock_irqrestore(&dcp->metadata.lock, flags); + return valid; +} + +const struct drm_edid *dcp_panel_edid_snapshot(struct apple_dcp *dcp, + const struct dcp_display_mode *modes, + unsigned int count, u64 *generation) +{ + struct dcp_display_metadata metadata; + const struct drm_edid *edid; + u8 *raw; + size_t size; + unsigned long flags; + + raw = kmalloc_objs(u8, sizeof(dcp->desktop->panel_raw_edid), GFP_KERNEL); + if (!raw) + return ERR_PTR(-ENOMEM); + spin_lock_irqsave(&dcp->metadata.lock, flags); + size = dcp->desktop->panel_raw_edid_size; + memcpy(raw, dcp->desktop->panel_raw_edid, size); + metadata = dcp->desktop->panel_metadata_valid ? + dcp->desktop->panel_metadata : dcp->desktop->panel_adt_identity; + *generation = dcp->desktop->panel_metadata_generation; + spin_unlock_irqrestore(&dcp->metadata.lock, flags); + /* The caller supplies the same retained catalog it is publishing. */ + edid = size ? drm_edid_alloc(raw, size) : + dcp_panel_edid(&metadata, modes, count, + dcp->panel.width_mm, dcp->panel.height_mm); + kfree(raw); + if (!edid) + return ERR_PTR(-ENOMEM); + if (!IS_ERR(edid) && !dcp_panel_metadata_current(dcp, *generation)) { + drm_edid_free(edid); + return ERR_PTR(-ESTALE); + } + return edid; +} + void dcp_set_dimensions(struct apple_dcp *dcp, u64 generation) { struct apple_connector *apple_connector = READ_ONCE(dcp->connector); @@ -895,6 +1149,13 @@ static struct apple_dcp_afkep *dcp_afkep(struct apple_dcp *dcp, u8 endpoint) } } +/* RX and the watchdog may race to finish one present, never both. */ +bool dcp_desktop_present_complete(struct apple_dcp *dcp, u32 id) +{ + return id == READ_ONCE(dcp->desktop->present_id) && + xchg(&dcp->desktop->present_pending, false); +} + #define DCP_SWAP_WATCHDOG_MS 1000 #define DCP_SWAP_WATCHDOG_RETRAINS 5 @@ -913,9 +1174,25 @@ VISIBLE_IF_KUNIT void dcp_swap_watchdog(struct work_struct *work) container_of(to_delayed_work(work), struct apple_dcp, swap_watchdog_wq); + if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP) { + if (time_before(jiffies, dcp->swap_watchdog_deadline) || + !xchg(&dcp->desktop->present_pending, false)) + return; + /* Retain displaced buffers until a later present really completes. */ + dcp_unarm_retained_framebuffers(dcp, READ_ONCE(dcp->desktop->present_id)); + dcp_backlight_desktop_finish(dcp, READ_ONCE(dcp->desktop->present_id), + true, false); + } + if (!iomfb_retire_dropped_present_h17p(dcp)) return; + if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP && + READ_ONCE(dcp->vrr_enabled)) { + WRITE_ONCE(dcp->desktop->vrr_failed, true); + WRITE_ONCE(dcp->vrr_enabled, false); + dev_warn(dcp->dev, "adaptive swap timed out, restoring fixed refresh\n"); + } dev_warn(dcp->dev, "swap not completed, retraining the display\n"); dcp_mode_invalidate(&dcp->mode_state); dcp_drm_crtc_vblank(dcp->crtc); @@ -926,7 +1203,9 @@ VISIBLE_IF_KUNIT void dcp_swap_watchdog(struct work_struct *work) void dcp_swap_watchdog_arm(struct apple_dcp *dcp) { - if (dcp_is_typec_output(dcp)) { + if (dcp_is_typec_output(dcp) || + (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP && + READ_ONCE(dcp->vrr_enabled))) { dcp->swap_watchdog_deadline = jiffies + msecs_to_jiffies(DCP_SWAP_WATCHDOG_MS); mod_delayed_work(system_dfl_wq, &dcp->swap_watchdog_wq, @@ -2653,6 +2932,9 @@ static void disconnected_hpd_event(struct apple_connector *con) struct apple_dcp *dcp = platform_get_drvdata(pdev); WRITE_ONCE(dcp->ext_backlight, false); + /* Nothing else reports an HDMI unplug to the firmware metadata. */ + if (dcp_sink_metadata(dcp)) + dcp_desktop_metadata_new_sink(dcp); } /* May run with DRM modeset locks held; defer property/cache cleanup. */ @@ -4709,8 +4991,6 @@ static int dcp_comp_bind_pipe(struct device *dev, struct device *main, void *dat panel_np = of_get_compatible_child(dev->of_node, "apple,panel"); if (panel_np) { - const char height_prop[2][16] = { "adj-height-mm", "height-mm" }; - if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP) { /* * Desktop 26.x swaps carry nits, and the panel node @@ -4728,11 +5008,10 @@ static int dcp_comp_bind_pipe(struct device *dev, struct device *main, void *dat dev_err(dev, "Missing property 'apple,max-brightness'\n"); } - of_property_read_u32(panel_np, "width-mm", &dcp->panel.width_mm); - /* use adjusted height as long as the notch is hidden */ - of_property_read_u32(panel_np, height_prop[!dcp->notch_height], - &dcp->panel.height_mm); + dcp_panel_read_dimensions(dcp, panel_np); + if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP) + dcp_panel_adt_identity(dcp); of_node_put(panel_np); dcp->fixed_connector_type = DRM_MODE_CONNECTOR_eDP; dcp->connector_type = DRM_MODE_CONNECTOR_eDP; @@ -5302,6 +5581,9 @@ static int dcp_platform_probe_pipe(struct platform_device *pdev) mutex_init(&dcp->dcpavserv.lock); INIT_WORK(&dcp->dcpavserv.close_wq, dcpavserv_close_work); dcp->hw = *(struct apple_dcp_hw_data *)of_device_get_match_data(dev); + /* The display such a processor finds at boot is its first sink. */ + if (dcp_sink_metadata(dcp)) + dcp_desktop_metadata_new_sink(dcp); if (dcp->hw.iomfb_method_profile == DCP_IOMFB_METHODS_H17P_EXTERNAL) { if (!dcp->hw.firmware_clock || @@ -6201,6 +6483,7 @@ static const struct apple_dcp_hw_data apple_dcp_hw_t6041 = { static const struct apple_dcp_hw_data apple_dcp_hw_t6041_dcpext = { .num_dptx_ports = 2, + .sink_metadata_edid = true, }; static const struct of_device_id of_match[] = { diff --git a/drivers/gpu/drm/apple/dcp.h b/drivers/gpu/drm/apple/dcp.h index 9ad01a427f6a2..bcdbf49318a6a 100644 --- a/drivers/gpu/drm/apple/dcp.h +++ b/drivers/gpu/drm/apple/dcp.h @@ -101,6 +101,7 @@ bool dcp_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); void dcp_set_dimensions(struct apple_dcp *dcp, u64 generation); +void dcp_panel_read_dimensions(struct apple_dcp *dcp, struct device_node *panel_np); void dcp_update_connector_dimensions(struct apple_dcp *dcp, struct drm_connector *connector); void dcp_send_message(struct apple_dcp *dcp, u8 endpoint, u64 message); diff --git a/drivers/gpu/drm/apple/dcp_backlight.c b/drivers/gpu/drm/apple/dcp_backlight.c index e9bc471654cde..2a9318beb4d32 100644 --- a/drivers/gpu/drm/apple/dcp_backlight.c +++ b/drivers/gpu/drm/apple/dcp_backlight.c @@ -22,9 +22,6 @@ #define MIN_BRIGHTNESS_PART2 103U #define MAX_BRIGHTNESS_PART2 510U -/* Initial level of a desktop 26.x panel, which reports none. */ -#define DCP_DESKTOP_DEFAULT_NITS 250U - /* * lookup for display brightness 2 to 99 nits * */ @@ -104,19 +101,119 @@ bool dcp_backlight_active(struct apple_dcp *dcp) READ_ONCE(dcp->backlight.state.ready); } +static bool dcp_backlight_desktop(struct apple_dcp *dcp) +{ + return dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP && + READ_ONCE(dcp->backlight.state.ready); +} + +static void dcp_backlight_desktop_kick(struct apple_dcp *dcp) +{ + if (!READ_ONCE(dcp->quiescing) && !READ_ONCE(dcp->crashed)) + schedule_work(&dcp->bl_update_wq); +} + +/* + * Match the backlight core's change event without force_update(), which + * would replace a user's target with an intermediate fade level. + */ +static void dcp_backlight_notify_work(struct work_struct *work) +{ + struct apple_dcp *dcp = container_of(work, struct apple_dcp, backlight.notify_work); + struct backlight_device *bd = READ_ONCE(dcp->brightness.bl_dev); + char *envp[2]; + + if (!bd || READ_ONCE(dcp->quiescing)) + return; + envp[0] = "SOURCE=unknown"; + envp[1] = NULL; + kobject_uevent_env(&bd->dev.kobj, KOBJ_CHANGE, envp); + sysfs_notify(&bd->dev.kobj, NULL, "brightness"); + sysfs_notify(&bd->dev.kobj, NULL, "actual_brightness"); +} + +/* Run before the later-registered backlight devres unregisters the device. */ +static void dcp_backlight_notify_release(void *data) +{ + struct apple_dcp *dcp = data; + + WRITE_ONCE(dcp->brightness.bl_dev, NULL); + cancel_work_sync(&dcp->backlight.notify_work); +} + +static void dcp_backlight_notify(struct apple_dcp *dcp) +{ + if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP && + !READ_ONCE(dcp->quiescing) && READ_ONCE(dcp->brightness.bl_dev)) + schedule_work(&dcp->backlight.notify_work); +} + +/* A loader hint describes the inherited level, not a new brightness request. */ +static void dcp_backlight_sync_hint(struct apple_dcp *dcp) +{ + struct backlight_device *bd = READ_ONCE(dcp->brightness.bl_dev); + unsigned long flags; + + if (!bd) + return; + /* Match update_status's lock order and never replace a user's target. */ + mutex_lock(&bd->ops_lock); + spin_lock_irqsave(&dcp->backlight.lock, flags); + if (!dcp->backlight.state.controlled && dcp->backlight.state.level_known) + bd->props.brightness = dcp->backlight.state.target; + spin_unlock_irqrestore(&dcp->backlight.lock, flags); + mutex_unlock(&bd->ops_lock); + dcp_backlight_notify(dcp); +} + int dcp_backlight_takeover(struct apple_dcp *dcp, u32 millinits) { u32 maximum, nits; int ret; - if (dcp_backlight_active(dcp)) - return 0; /* H17P takes nits up to the panel ceiling, not the older DAC table's. */ maximum = dcp->brightness.maximum; ret = dcp_bl_takeover_nits(maximum, millinits, &nits); if (ret) return ret; + if (dcp_backlight_active(dcp) || + (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP && + READ_ONCE(dcp->backlight.state.ready))) { + unsigned long flags; + bool seeded; + + spin_lock_irqsave(&dcp->backlight.lock, flags); + seeded = dcp_bl_report(&dcp->backlight.state, nits); + if (seeded && dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP) { + dcp->desktop->inherited_brightness = nits; + dcp->desktop->inherited_brightness_valid = true; + } + spin_unlock_irqrestore(&dcp->backlight.lock, flags); + if (seeded) + dcp_backlight_sync_hint(dcp); + return 0; + } + + if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP) { + unsigned long flags; + bool seeded = false; + + /* Accept the first hint until a user request takes ownership. */ + spin_lock_irqsave(&dcp->backlight.lock, flags); + if (!dcp->desktop->inherited_brightness_valid && + !dcp->backlight.state.controlled) { + dcp->desktop->inherited_brightness = nits; + dcp->desktop->inherited_brightness_valid = true; + if (dcp->backlight.state.ready) + seeded = dcp_bl_report(&dcp->backlight.state, nits); + } + spin_unlock_irqrestore(&dcp->backlight.lock, flags); + if (seeded) + dcp_backlight_sync_hint(dcp); + return 0; + } + /* Keep the first bounded hint. Later reports are not user requests. */ return iomfb_configure_backlight_h17p(dcp, maximum, true, nits, false, 0); } @@ -125,14 +222,17 @@ static int dcp_get_brightness(struct backlight_device *bd) { struct apple_dcp *dcp = bl_get_data(bd); unsigned long flags; - u32 actual; + int actual; if (READ_ONCE(dcp->quiescing)) return -ENODEV; - if (dcp_backlight_active(dcp)) { + if (dcp_backlight_active(dcp) || dcp_backlight_desktop(dcp)) { spin_lock_irqsave(&dcp->backlight.lock, flags); - actual = dcp->backlight.state.actual; + if (dcp_backlight_desktop(dcp)) + actual = dcp_bl_actual(&dcp->backlight.state); + else + actual = dcp->backlight.state.actual; spin_unlock_irqrestore(&dcp->backlight.lock, flags); return actual; } @@ -227,9 +327,21 @@ static void dcp_backlight_fade_work(struct work_struct *work) msecs_to_jiffies(DCP_BL_FADE_STEP_MS)); } +static void dcp_backlight_retry_work(struct work_struct *work) +{ + struct apple_dcp *dcp = container_of(to_delayed_work(work), + struct apple_dcp, backlight.retry_work); + + if (!READ_ONCE(dcp->quiescing) && !READ_ONCE(dcp->crashed)) + dcp_backlight_kick(dcp); +} + void dcp_backlight_stop(struct apple_dcp *dcp) { cancel_delayed_work_sync(&dcp->backlight.fade_work); + cancel_delayed_work_sync(&dcp->backlight.retry_work); + if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP) + cancel_work_sync(&dcp->backlight.notify_work); } static void dcp_backlight_release(void *data) @@ -246,6 +358,8 @@ int dcp_backlight_init(struct apple_dcp *dcp, struct device *dev) { spin_lock_init(&dcp->backlight.lock); INIT_DELAYED_WORK(&dcp->backlight.fade_work, dcp_backlight_fade_work); + INIT_WORK(&dcp->backlight.notify_work, dcp_backlight_notify_work); + INIT_DELAYED_WORK(&dcp->backlight.retry_work, dcp_backlight_retry_work); return devm_add_action_or_reset(dev, dcp_backlight_release, dcp); } @@ -350,6 +464,69 @@ unsigned int dcp_backlight_retry_delay(struct apple_dcp *dcp) return delay; } +/* Power changes jump; a target chosen while off is restored on enable. */ +void dcp_backlight_desktop_power(struct apple_dcp *dcp, bool on) +{ + unsigned long flags; + + if (!dcp_backlight_desktop(dcp)) + return; + if (!on) + dcp_backlight_stop(dcp); + spin_lock_irqsave(&dcp->backlight.lock, flags); + dcp->backlight.state.dpms_off = !on; + if (dcp->backlight.state.controlled) + dcp_bl_jump(&dcp->backlight.state); + spin_unlock_irqrestore(&dcp->backlight.lock, flags); +} + +/* Reserve only after swap_start succeeds, using its actual firmware ID. */ +bool dcp_backlight_desktop_prepare(struct apple_dcp *dcp, u32 id, + bool have_surface, u32 *nits) +{ + unsigned long flags; + int ret; + + if (!dcp_backlight_desktop(dcp)) + return false; + spin_lock_irqsave(&dcp->backlight.lock, flags); + ret = dcp_bl_swap_prepare(&dcp->backlight.state, + &dcp->desktop->backlight, id, have_surface); + if (!ret) + *nits = dcp->desktop->backlight.present.nits; + spin_unlock_irqrestore(&dcp->backlight.lock, flags); + return !ret; +} + +void dcp_backlight_desktop_finish(struct apple_dcp *dcp, u32 id, + bool reply, bool accepted) +{ + unsigned long flags; + unsigned int delay; + bool finished, pending, changed; + int previous; + + if (!dcp_backlight_desktop(dcp)) + return; + spin_lock_irqsave(&dcp->backlight.lock, flags); + previous = dcp_bl_actual(&dcp->backlight.state); + finished = dcp_bl_swap_finish(&dcp->backlight.state, + &dcp->desktop->backlight, id, reply, accepted); + changed = previous != dcp_bl_actual(&dcp->backlight.state); + delay = dcp_bl_retry_delay(&dcp->backlight.state); + pending = dcp->backlight.state.dirty; + spin_unlock_irqrestore(&dcp->backlight.lock, flags); + if (changed) + dcp_backlight_notify(dcp); + if (!finished || READ_ONCE(dcp->quiescing) || READ_ONCE(dcp->crashed)) + return; + if (delay) + mod_delayed_work(system_dfl_wq, &dcp->backlight.retry_work, + msecs_to_jiffies(delay)); + else if (pending && (!reply || accepted)) + dcp_backlight_kick(dcp); +} + #define SCALE_FACTOR (1 << 10) static u32 interpolate(int val, int min, int max, u32 *tbl, size_t tbl_size) @@ -416,7 +593,8 @@ static int drm_crtc_set_brightness(struct apple_dcp *dcp) if (ret) goto out; - if (!dcp->brightness.update) + if (dcp_backlight_desktop(dcp) ? !dcp_backlight_pending(dcp) : + !dcp->brightness.update) goto out; crtc_state = drm_atomic_get_crtc_state(commit, crtc); @@ -425,12 +603,19 @@ static int drm_crtc_set_brightness(struct apple_dcp *dcp) goto out; } + /* A request made while DPMS is off is restored on the enable swap. */ + if (dcp_backlight_desktop(dcp) && !crtc_state->active) + goto out; + /* Re-present the current scanout when brightness is the only change. */ ret = drm_atomic_add_affected_planes(commit, crtc); if (ret) goto out; - crtc_state->color_mgmt_changed |= true; + if (dcp_backlight_desktop(dcp)) + crtc_state->planes_changed = true; + else + crtc_state->color_mgmt_changed = true; ret = drm_atomic_commit(commit); out: @@ -471,7 +656,8 @@ int dcp_backlight_update(struct apple_dcp *dcp) /* Wait 1 vblank cycle in the hope an atomic swap has already updated * the brightness */ - msleep((1001 + 23) / 24); // 42ms for 23.976 fps + if (!dcp_backlight_desktop(dcp)) + msleep((1001 + 23) / 24); // 42ms for 23.976 fps return drm_crtc_set_brightness(dcp); } @@ -486,7 +672,7 @@ static int dcp_set_brightness(struct backlight_device *bd) if (READ_ONCE(dcp->quiescing)) return -ENODEV; - if (dcp_backlight_active(dcp)) { + if (dcp_backlight_active(dcp) || dcp_backlight_desktop(dcp)) { struct dcp_backlight_state *state = &dcp->backlight.state; bool fade; u32 old; @@ -510,24 +696,11 @@ static int dcp_set_brightness(struct backlight_device *bd) return ret; } - /* - * Desktop 26.x swaps carry nits. A level of 0 must not turn the - * panel off (DPMS does that); only a blanked panel still gets 0. - */ - if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP && !brightness && - !bd->props.brightness) - brightness = min(bd->props.max_brightness, 2); - ret = drm_modeset_lock_single_interruptible(&dcp->crtc->base.mutex); if (ret) return ret; - if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP) { - dcp->brightness.nits = brightness; - dcp->brightness.requested = true; - } else { - dcp->brightness.dac = calculate_dac(dcp, brightness); - } + dcp->brightness.dac = calculate_dac(dcp, brightness); dcp->brightness.update = true; drm_modeset_unlock(&dcp->crtc->base.mutex); @@ -540,6 +713,12 @@ static bool dcp_backlight_controls_device(struct backlight_device *bd, { struct apple_dcp *dcp = bl_get_data(bd); + /* A framebuffer's first unblank must not apply the reporting default. */ + if (dcp_backlight_desktop(dcp) && + !READ_ONCE(dcp->backlight.state.level_known) && + !READ_ONCE(dcp->backlight.state.controlled)) + return false; + return !display_dev || display_dev == dcp->dev || (dcp->crtc && display_dev == dcp->crtc->base.dev->dev); } @@ -555,6 +734,7 @@ int dcp_backlight_register(struct apple_dcp *dcp) { struct device *dev = dcp->dev; struct backlight_device *bl_dev; + int ret; unsigned long flags; struct backlight_properties props = { .type = BACKLIGHT_PLATFORM, @@ -588,10 +768,25 @@ int dcp_backlight_register(struct apple_dcp *dcp) if (!dcp->brightness.maximum || dcp->brightness.maximum > INT_MAX) return -ERANGE; props.max_brightness = dcp->brightness.maximum; - if (!props.brightness) - props.brightness = min_t(u32, DCP_DESKTOP_DEFAULT_NITS, - props.max_brightness); - dcp->brightness.nits = props.brightness; + /* Match the Neo's reporting default without presenting a guess. */ + props.brightness = props.max_brightness / 2; + spin_lock_irqsave(&dcp->backlight.lock, flags); + if (!dcp->backlight.state.ready) { + int ret = dcp_bl_init(&dcp->backlight.state, props.max_brightness, + dcp->desktop->inherited_brightness_valid, + dcp->desktop->inherited_brightness, + true, props.brightness); + + if (ret) { + spin_unlock_irqrestore(&dcp->backlight.lock, flags); + return ret; + } + dcp->backlight.state.fade = true; + dcp->backlight.state.preserve_inherited = true; + dcp->backlight.kick = dcp_backlight_desktop_kick; + } + props.brightness = dcp->backlight.state.target; + spin_unlock_irqrestore(&dcp->backlight.lock, flags); } bl_dev = devm_backlight_device_register(dev, "apple-panel-bl", dev, dcp, @@ -599,7 +794,16 @@ int dcp_backlight_register(struct apple_dcp *dcp) if (IS_ERR(bl_dev)) return PTR_ERR(bl_dev); - dcp->brightness.bl_dev = bl_dev; + /* LIFO devres order drains event work before unregistering the backlight. */ + if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP) { + ret = devm_add_action_or_reset(dev, dcp_backlight_notify_release, dcp); + if (ret) + return ret; + } + WRITE_ONCE(dcp->brightness.bl_dev, bl_dev); + /* A hint may have arrived between the property snapshot and registration. */ + if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP) + dcp_backlight_sync_hint(dcp); if (!dcp_backlight_active(dcp) && dcp->fw_compat != DCP_FIRMWARE_V_26_6_DESKTOP) dcp->brightness.dac = calculate_dac(dcp, dcp->brightness.nits); diff --git a/drivers/gpu/drm/apple/dcp_backlight.h b/drivers/gpu/drm/apple/dcp_backlight.h index 92ac07b2e9d9d..ad8ca6d5623fc 100644 --- a/drivers/gpu/drm/apple/dcp_backlight.h +++ b/drivers/gpu/drm/apple/dcp_backlight.h @@ -22,10 +22,14 @@ struct dcp_backlight_state { u32 fade_from; u32 actual; u32 sent_nits; + /* Firmware observations confirm output; they never become user requests. */ + u32 reported; u64 sequence; u8 retries; bool ready; bool controlled; + /* An unchanged registration default cannot take over an unknown level. */ + bool preserve_inherited; bool dpms_off; bool core_blank; bool suspended; @@ -41,6 +45,7 @@ struct dcp_backlight_state { * without a loader report is not known until a present carries a level. */ bool level_known; + bool report_valid; }; struct dcp_backlight_present { @@ -86,8 +91,10 @@ static inline int dcp_bl_init(struct dcp_backlight_state *state, u32 maximum, .target = nits, .level = nits, .actual = nits, + .reported = nits, .ready = true, .level_known = inherited_valid, + .report_valid = inherited_valid, }; return 0; } @@ -107,6 +114,30 @@ static inline bool dcp_bl_seed(struct dcp_backlight_state *state, u32 nits) return true; } +/* + * A report can establish takeover once. After Linux has a level or target, + * keep the observation separate from target, fade progress and completion. + * In particular, delayed reports cannot restart a fade or request a resend. + */ +static inline bool dcp_bl_report(struct dcp_backlight_state *state, u32 nits) +{ + if (!state->ready || nits > state->maximum) + return false; + state->reported = nits; + state->report_valid = true; + if (state->controlled || state->level_known) + return false; + return dcp_bl_seed(state, nits); +} + +/* Report a usable target until a hint or completed present gives the level. */ +static inline int dcp_bl_actual(const struct dcp_backlight_state *state) +{ + if (!state->ready) + return -ENODATA; + return state->level_known ? state->actual : state->target; +} + static inline bool dcp_bl_lit(const struct dcp_backlight_state *state) { return !state->dpms_off && !state->core_blank && !state->suspended; @@ -139,6 +170,20 @@ static inline int dcp_bl_request(struct dcp_backlight_state *state, u32 nits, return -ENODATA; if (nits > state->maximum) return -ERANGE; + /* Save/restore clients may echo the reporting default without a change. */ + if (state->preserve_inherited && !state->controlled && !state->level_known && + state->target == nits) { + state->core_blank = core_blank; + state->suspended = suspended; + return 0; + } + /* Core blank/suspend notifications do not establish a panel level. */ + if (!state->controlled && !state->level_known && state->target == nits && + (state->core_blank != core_blank || state->suspended != suspended)) { + state->core_blank = core_blank; + state->suspended = suspended; + return 0; + } state->controlled = true; state->retries = 0; /* @@ -150,7 +195,8 @@ static inline int dcp_bl_request(struct dcp_backlight_state *state, u32 nits, state->target = nits; state->core_blank = core_blank; state->suspended = suspended; - if (state->fade && state->level_known && was_lit && + /* With no hint, the reporting target is the first fade's origin. */ + if (state->fade && (state->level_known || state->preserve_inherited) && was_lit && dcp_bl_lit(state) && state->level != nits) { state->fade_from = state->level; state->fading = true; @@ -272,8 +318,57 @@ static inline unsigned int dcp_bl_retry_delay(const struct dcp_backlight_state * return 100U << (state->retries - 1); } +/* A desktop present can complete before its submit reply arrives. */ +struct dcp_backlight_swap { + struct dcp_backlight_present present; + u32 id; + bool pending; + bool accepted; + bool completed; +}; + +static inline int dcp_bl_swap_prepare(struct dcp_backlight_state *state, + struct dcp_backlight_swap *swap, + u32 id, bool have_surface) +{ + struct dcp_backlight_present present; + int ret = dcp_bl_prepare(state, have_surface, &present); + + if (ret) + return ret; + *swap = (struct dcp_backlight_swap) { + .present = present, + .id = id, + .pending = true, + }; + return 0; +} + +static inline bool dcp_bl_swap_finish(struct dcp_backlight_state *state, + struct dcp_backlight_swap *swap, + u32 id, bool reply, bool accepted) +{ + if (!swap->pending || swap->id != id) + return false; + if (reply) + swap->accepted = accepted; + else + swap->completed = true; + if ((!reply || accepted) && !(swap->accepted && swap->completed)) + return false; + swap->pending = false; + return dcp_bl_complete(state, swap->present.sequence, + swap->accepted && swap->completed); +} + struct apple_dcp; +void dcp_backlight_desktop_power(struct apple_dcp *dcp, bool on); +bool dcp_backlight_desktop_prepare(struct apple_dcp *dcp, u32 id, + bool have_surface, u32 *nits); +void dcp_backlight_desktop_finish(struct apple_dcp *dcp, u32 id, + bool reply, bool accepted); + /* * kick only queues work; it must not submit a top-level RPC from the caller. * The central IOMFB queue owns surface references, retries, and wire encoding. diff --git a/drivers/gpu/drm/apple/dcp_backlight_test.c b/drivers/gpu/drm/apple/dcp_backlight_test.c index 70379571a5dd3..c8a4d31bde2af 100644 --- a/drivers/gpu/drm/apple/dcp_backlight_test.c +++ b/drivers/gpu/drm/apple/dcp_backlight_test.c @@ -292,6 +292,70 @@ static void backlight_fallback_test(struct kunit *test) KUNIT_EXPECT_EQ(test, present.nits, 262U); } +static void backlight_unknown_takeover_test(struct kunit *test) +{ + struct dcp_backlight_state state; + struct dcp_backlight_present present; + + KUNIT_ASSERT_EQ(test, dcp_bl_init(&state, 600, false, 0, true, 300), 0); + state.preserve_inherited = true; + KUNIT_EXPECT_EQ(test, dcp_bl_actual(&state), 300); + /* Core suspend/blank and resume do not turn the guess into a write. */ + KUNIT_ASSERT_EQ(test, dcp_bl_request(&state, 300, true, true), 0); + KUNIT_ASSERT_EQ(test, dcp_bl_request(&state, 300, false, false), 0); + KUNIT_EXPECT_FALSE(test, state.controlled); + KUNIT_EXPECT_FALSE(test, state.dirty); + KUNIT_EXPECT_EQ(test, dcp_bl_prepare(&state, true, &present), -EALREADY); + KUNIT_EXPECT_EQ(test, dcp_bl_actual(&state), 300); + + /* A late loader hint supplies the inherited level without a present. */ + KUNIT_EXPECT_TRUE(test, dcp_bl_seed(&state, 250)); + KUNIT_EXPECT_EQ(test, dcp_bl_actual(&state), 250); + KUNIT_EXPECT_FALSE(test, state.dirty); + + /* Echoing the unknown registration value leaves the panel alone. */ + KUNIT_ASSERT_EQ(test, dcp_bl_init(&state, 600, false, 0, true, 300), 0); + state.preserve_inherited = true; + KUNIT_ASSERT_EQ(test, dcp_bl_request(&state, 300, false, false), 0); + KUNIT_EXPECT_FALSE(test, state.controlled); + KUNIT_EXPECT_EQ(test, dcp_bl_prepare(&state, true, &present), -EALREADY); + /* A new target is reported even before its present completes. */ + KUNIT_ASSERT_EQ(test, dcp_bl_request(&state, 250, false, false), 0); + KUNIT_ASSERT_EQ(test, dcp_bl_prepare(&state, true, &present), 0); + KUNIT_EXPECT_TRUE(test, dcp_bl_complete(&state, present.sequence, false)); + KUNIT_EXPECT_EQ(test, dcp_bl_actual(&state), 250); + KUNIT_ASSERT_EQ(test, dcp_bl_prepare(&state, true, &present), 0); + KUNIT_EXPECT_TRUE(test, dcp_bl_complete(&state, present.sequence, true)); + KUNIT_EXPECT_EQ(test, dcp_bl_actual(&state), 250); +} + +static void backlight_first_change_fades_test(struct kunit *test) +{ + struct dcp_backlight_state state; + struct dcp_backlight_present present; + + KUNIT_ASSERT_EQ(test, dcp_bl_init(&state, 600, false, 0, true, 300), 0); + state.preserve_inherited = true; + state.fade = true; + KUNIT_EXPECT_EQ(test, dcp_bl_actual(&state), 300); + KUNIT_ASSERT_EQ(test, dcp_bl_request(&state, 300, false, false), 0); + KUNIT_EXPECT_FALSE(test, state.dirty); + KUNIT_EXPECT_FALSE(test, state.controlled); + KUNIT_ASSERT_EQ(test, dcp_bl_request(&state, 100, false, false), 0); + KUNIT_EXPECT_TRUE(test, state.fading); + KUNIT_EXPECT_FALSE(test, state.level_known); + KUNIT_EXPECT_FALSE(test, state.dirty); + KUNIT_EXPECT_EQ(test, state.fade_from, 300U); + KUNIT_EXPECT_EQ(test, dcp_bl_actual(&state), 100); + KUNIT_EXPECT_TRUE(test, dcp_bl_fade_step(&state, 125, 250)); + KUNIT_EXPECT_EQ(test, state.level, 200U); + KUNIT_ASSERT_EQ(test, dcp_bl_prepare(&state, true, &present), 0); + KUNIT_EXPECT_TRUE(test, dcp_bl_complete(&state, present.sequence, true)); + KUNIT_EXPECT_EQ(test, dcp_bl_actual(&state), 200); + KUNIT_EXPECT_FALSE(test, dcp_bl_fade_step(&state, 250, 250)); + KUNIT_EXPECT_EQ(test, state.level, 100U); +} + static void backlight_fallback_same_level_test(struct kunit *test) { struct dcp_backlight_state state; @@ -501,7 +565,76 @@ static void backlight_fade_blank_test(struct kunit *test) KUNIT_EXPECT_EQ(test, dcp_bl_effective(&state), 400U); } +static void backlight_desktop_swap_test(struct kunit *test) +{ + struct dcp_backlight_state state; + struct dcp_backlight_swap swap = {}; + + KUNIT_ASSERT_EQ(test, dcp_bl_init(&state, 600, true, 300, false, 0), 0); + state.fade = true; + KUNIT_ASSERT_EQ(test, dcp_bl_request(&state, 600, false, false), 0); + KUNIT_EXPECT_TRUE(test, dcp_bl_fade_step(&state, 125, 250)); + KUNIT_EXPECT_EQ(test, state.level, 450U); + KUNIT_ASSERT_EQ(test, dcp_bl_swap_prepare(&state, &swap, 42, true), 0); + /* A completion alone must not publish an unaccepted panel level. */ + KUNIT_EXPECT_FALSE(test, dcp_bl_swap_finish(&state, &swap, 42, false, true)); + KUNIT_EXPECT_EQ(test, state.actual, 300U); + KUNIT_EXPECT_FALSE(test, dcp_bl_swap_finish(&state, &swap, 41, true, true)); + KUNIT_EXPECT_TRUE(test, dcp_bl_swap_finish(&state, &swap, 42, true, true)); + KUNIT_EXPECT_EQ(test, state.actual, 450U); + KUNIT_EXPECT_FALSE(test, dcp_bl_swap_finish(&state, &swap, 42, true, false)); + KUNIT_EXPECT_FALSE(test, dcp_bl_fade_step(&state, 250, 250)); + KUNIT_ASSERT_EQ(test, dcp_bl_swap_prepare(&state, &swap, 43, true), 0); + KUNIT_EXPECT_TRUE(test, dcp_bl_swap_finish(&state, &swap, 43, true, false)); + KUNIT_EXPECT_EQ(test, state.actual, 450U); + KUNIT_EXPECT_EQ(test, dcp_bl_retry_delay(&state), 100U); + KUNIT_ASSERT_EQ(test, dcp_bl_swap_prepare(&state, &swap, 44, true), 0); + KUNIT_EXPECT_FALSE(test, dcp_bl_swap_finish(&state, &swap, 44, true, true)); + KUNIT_EXPECT_TRUE(test, dcp_bl_swap_finish(&state, &swap, 44, false, true)); + KUNIT_EXPECT_EQ(test, state.actual, 600U); + /* A lit zero is a valid request, with the same fade as the Neo. */ + KUNIT_ASSERT_EQ(test, dcp_bl_request(&state, 0, false, false), 0); + KUNIT_EXPECT_TRUE(test, dcp_bl_fade_step(&state, 125, 250)); + KUNIT_EXPECT_EQ(test, state.level, 300U); + KUNIT_EXPECT_FALSE(test, dcp_bl_fade_step(&state, 250, 250)); + KUNIT_EXPECT_EQ(test, state.level, 0U); +} + +static void backlight_reports_keep_target_test(struct kunit *test) +{ + struct dcp_backlight_state state; + struct dcp_backlight_present present; + + KUNIT_ASSERT_EQ(test, dcp_bl_init(&state, 600, false, 0, true, 300), 0); + state.fade = true; + KUNIT_EXPECT_TRUE(test, dcp_bl_report(&state, 100)); + KUNIT_EXPECT_EQ(test, state.actual, 100U); + KUNIT_ASSERT_EQ(test, dcp_bl_request(&state, 600, false, false), 0); + KUNIT_EXPECT_TRUE(test, dcp_bl_fade_step(&state, 125, 250)); + KUNIT_EXPECT_FALSE(test, dcp_bl_report(&state, 50)); + KUNIT_EXPECT_EQ(test, state.target, 600U); + KUNIT_EXPECT_EQ(test, state.level, 350U); + KUNIT_EXPECT_EQ(test, state.actual, 100U); + KUNIT_ASSERT_EQ(test, dcp_bl_prepare(&state, true, &present), 0); + KUNIT_EXPECT_FALSE(test, dcp_bl_report(&state, 600)); + KUNIT_EXPECT_TRUE(test, dcp_bl_complete(&state, present.sequence, true)); + KUNIT_EXPECT_EQ(test, state.actual, 350U); + KUNIT_ASSERT_EQ(test, dcp_bl_request(&state, 500, false, false), 0); + KUNIT_EXPECT_FALSE(test, dcp_bl_report(&state, 100)); + KUNIT_EXPECT_TRUE(test, dcp_bl_fade_step(&state, 125, 250)); + KUNIT_EXPECT_EQ(test, state.level, 425U); + KUNIT_EXPECT_FALSE(test, dcp_bl_fade_step(&state, 250, 250)); + KUNIT_ASSERT_EQ(test, dcp_bl_prepare(&state, true, &present), 0); + KUNIT_EXPECT_TRUE(test, dcp_bl_complete(&state, present.sequence, true)); + KUNIT_EXPECT_FALSE(test, dcp_bl_report(&state, 200)); + KUNIT_EXPECT_EQ(test, state.reported, 200U); + KUNIT_EXPECT_EQ(test, state.target, 500U); + KUNIT_EXPECT_EQ(test, state.actual, 500U); + KUNIT_EXPECT_FALSE(test, state.dirty); +} + static struct kunit_case backlight_cases[] = { + KUNIT_CASE(backlight_reports_keep_target_test), KUNIT_CASE(backlight_millinits_takeover_test), KUNIT_CASE(backlight_takeover_test), KUNIT_CASE(backlight_range_test), @@ -513,8 +646,11 @@ static struct kunit_case backlight_cases[] = { KUNIT_CASE(backlight_inflight_dpms_test), KUNIT_CASE(backlight_resend_test), KUNIT_CASE(backlight_fallback_test), + KUNIT_CASE(backlight_unknown_takeover_test), + KUNIT_CASE(backlight_first_change_fades_test), KUNIT_CASE(backlight_fallback_same_level_test), KUNIT_CASE(backlight_seed_known_test), + KUNIT_CASE(backlight_desktop_swap_test), KUNIT_CASE(backlight_fade_test), KUNIT_CASE(backlight_fade_restart_test), KUNIT_CASE(backlight_fade_blank_test), diff --git a/drivers/gpu/drm/apple/dcp_dropped_swap_test.c b/drivers/gpu/drm/apple/dcp_dropped_swap_test.c index b45010d38fac1..cd11dbc374aa8 100644 --- a/drivers/gpu/drm/apple/dcp_dropped_swap_test.c +++ b/drivers/gpu/drm/apple/dcp_dropped_swap_test.c @@ -497,7 +497,29 @@ static void retired_completion_in_flight_test(struct kunit *test) KUNIT_EXPECT_TRUE(test, READ_ONCE(dcp->crashed)); } +static void desktop_present_completion_test(struct kunit *test) +{ + struct apple_dcp *dcp = kunit_kzalloc(test, sizeof(*dcp), GFP_KERNEL); + + KUNIT_ASSERT_NOT_NULL(test, dcp); + dcp->desktop = kunit_kzalloc(test, sizeof(*dcp->desktop), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dcp->desktop); + dcp->desktop->present_id = 42; + dcp->desktop->present_pending = true; + KUNIT_EXPECT_FALSE(test, dcp_desktop_present_complete(dcp, 41)); + KUNIT_EXPECT_TRUE(test, dcp->desktop->present_pending); + KUNIT_EXPECT_TRUE(test, dcp_desktop_present_complete(dcp, 42)); + KUNIT_EXPECT_FALSE(test, dcp_desktop_present_complete(dcp, 42)); + /* A late completion cannot claim the replacement's event. */ + dcp->desktop->present_id = 43; + dcp->desktop->present_pending = true; + KUNIT_EXPECT_FALSE(test, dcp_desktop_present_complete(dcp, 42)); + KUNIT_EXPECT_TRUE(test, dcp->desktop->present_pending); + KUNIT_EXPECT_TRUE(test, dcp_desktop_present_complete(dcp, 43)); +} + static struct kunit_case dropped_swap_cases[] = { + KUNIT_CASE(desktop_present_completion_test), KUNIT_CASE(dropped_present_retired_test), KUNIT_CASE(dropped_present_buffers_test), KUNIT_CASE(completed_present_kept_test), diff --git a/drivers/gpu/drm/apple/dcp_external_announce_test.c b/drivers/gpu/drm/apple/dcp_external_announce_test.c index fcb9a7125b51b..a1bd2dcd4b2ab 100644 --- a/drivers/gpu/drm/apple/dcp_external_announce_test.c +++ b/drivers/gpu/drm/apple/dcp_external_announce_test.c @@ -115,6 +115,7 @@ static struct announce_test *announce_test_init(struct kunit *test) INIT_DELAYED_WORK(&dcp->hybrid_wq, announce_test_noop); INIT_DELAYED_WORK(&dcp->bl_fallback_wq, announce_test_noop); INIT_DELAYED_WORK(&dcp->backlight.fade_work, announce_test_noop); + INIT_DELAYED_WORK(&dcp->backlight.retry_work, announce_test_noop); INIT_WORK(&dcp->external_stop_work, announce_test_noop); INIT_DELAYED_WORK(&dcp->external_stop_retry_wq, announce_test_noop); INIT_WORK(&dcp->vblank_wq, announce_test_noop); diff --git a/drivers/gpu/drm/apple/dcp_external_relink_test.c b/drivers/gpu/drm/apple/dcp_external_relink_test.c index b9d7767be80d8..cf3dfa6d911bf 100644 --- a/drivers/gpu/drm/apple/dcp_external_relink_test.c +++ b/drivers/gpu/drm/apple/dcp_external_relink_test.c @@ -222,6 +222,7 @@ static void relink_test_exit(void *data) wait_for_completion_timeout(&t->userspace_done, 10 * HZ); disable_delayed_work_sync(&t->dcp.announce_wq); disable_work_sync(&t->con.hotplug_wq); + apple_connector_backlight_early_unregister(&t->con); drm_atomic_helper_shutdown(&t->drm); drm_framebuffer_put(&t->fb); } @@ -408,6 +409,7 @@ static struct relink_test *relink_test_init(struct kunit *test) spin_lock_init(&dcp->mode_state.lock); INIT_DELAYED_WORK(&dcp->announce_wq, dcp_announce_work); INIT_WORK(&dcp->vblank_wq, relink_test_noop); + apple_connector_backlight_init(&t->con); INIT_WORK(&t->con.hotplug_wq, dcp_hotplug); dcp->test_point = relink_test_point; dcp->typec_cable_connected = true; diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 7daa5ac848a5c..0d266130531ed 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include "dcp.h" @@ -40,6 +41,117 @@ #include "parser.h" #include "trace.h" +struct dcp_identity_trace { + struct apple_dcp *dcp; + atomic_t *count; +}; + +static void dcp_identity_key_log(void *data, const char *key, u32 key_len, + u32 type, u32 size, u32 depth) +{ + struct dcp_identity_trace *trace = data; + struct apple_dcp *dcp = trace->dcp; + char safe[65]; + u32 i; + + if (!atomic_add_unless(trace->count, 1, 128)) + return; + for (i = 0; i < min_t(u32, key_len, sizeof(safe) - 1); i++) + safe[i] = key[i] >= 0x20 && key[i] <= 0x7e ? key[i] : '?'; + safe[i] = 0; + dev_info(dcp->dev, "panel identity depth=%u key=%s type=%u size=%u\n", + depth, safe, type, size); +} + +void dcp_trace_panel_property(struct apple_dcp *dcp, int tag, const char *key, + size_t key_len, const void *blob, size_t len) +{ + struct dcp_identity_trace trace; + u32 slot = tag == 552 ? 0 : tag == 561 ? 1 : tag == 114 ? 2 : 3; + int ret; + + if (!drm_debug_enabled(DRM_UT_KMS) || + dcp->fw_compat != DCP_FIRMWARE_V_26_6_DESKTOP || !dcp_has_panel(dcp)) + return; + trace.dcp = dcp; + trace.count = &dcp->desktop->identity_log_count[slot]; + /* Periodic statistics cannot consume the panel setters' budget. */ + if (!atomic_add_unless(trace.count, 1, 128)) + return; + dev_info(dcp->dev, "panel identity callback=D%03d bytes=%zu null=%d\n", + tag, len, !blob); + dcp_identity_key_log(&trace, key, key_len, 0, len, 0); + if (!blob) + return; + ret = parse_identity_keys(blob, len, dcp_identity_key_log, &trace); + if (ret) + dev_info(dcp->dev, "panel identity inspection stopped: %d\n", ret); +} + +/* Other property setters use fixed scalar records, not serialization. */ +static void dcp_trace_panel_setter(struct apple_dcp *dcp, int tag, + const void *data, size_t len) +{ + struct dcp_identity_trace trace = { .dcp = dcp }; + const char *in = data; + u32 offset = 0, type, size, expected; + bool is_null = false; + + if (!drm_debug_enabled(DRM_UT_KMS) || + dcp->fw_compat != DCP_FIRMWARE_V_26_6_DESKTOP || !dcp_has_panel(dcp)) + return; + switch (tag) { + case 414: + offset = 4; + fallthrough; + case 563: + type = 4; + size = 8; + expected = offset + 76; + break; + case 415: + offset = 4; + fallthrough; + case 565: + type = 11; + size = 4; + expected = offset + 72; + break; + case 567: + type = 9; + size = 64; + expected = 128; + break; + case 102: + type = 4; + size = 4; + expected = 68; + break; + case 104: + type = 11; + size = 1; + expected = 68; + break; + default: + return; + } + /* Inspect only the public wire shape, without changing its handler. */ + if (len != expected) + return; + if (tag == 414 || tag == 415 || tag == 563 || tag == 565) + is_null = in[offset + 64 + size] & 1; + if (tag == 567) + size = strnlen(in + 64, 64); + /* Scalar/service setters share the bounded miscellaneous-property budget. */ + trace.count = &dcp->desktop->identity_log_count[3]; + if (!atomic_add_unless(trace.count, 1, 128)) + return; + dev_info(dcp->dev, "panel identity callback=D%03d bytes=%zu null=%d\n", + tag, len, is_null); + dcp_identity_key_log(&trace, in + offset, strnlen(in + offset, 64), + type, is_null ? 0 : size, 0); +} + static int dcp_tx_offset(enum dcp_context_id id) { switch (id) { @@ -1267,7 +1379,8 @@ static void dcpep_handle_cb(struct apple_dcp *dcp, enum dcp_context_id context, u8 depth; bool handled, zero_reply; - if (dcp->fw_compat == DCP_FIRMWARE_H17P && + if ((dcp->fw_compat == DCP_FIRMWARE_H17P || + dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP) && (length < sizeof(*hdr) || !ch || ch->depth >= DCP_MAX_CALL_DEPTH || offset >= 0x8000 || length > 0x8000 - offset)) { dev_err(dev, "invalid IOMFB callback envelope\n"); @@ -1285,6 +1398,14 @@ static void dcpep_handle_cb(struct apple_dcp *dcp, enum dcp_context_id context, tag = dcp_parse_tag(hdr->tag); + if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP && + (!iomfb_check_callback_v26_6_desktop(tag, hdr->in_len, hdr->out_len) || + (u64)sizeof(*hdr) + hdr->in_len + hdr->out_len != length)) { + dev_err(dev, "invalid desktop IOMFB callback %d (%u, %u)\n", + tag, hdr->in_len, hdr->out_len); + dcp->crashed = true; + return; + } handled = tag >= 0 && tag < IOMFB_MAX_CB && dcp->cb_handlers && dcp->cb_handlers[tag]; @@ -1311,6 +1432,8 @@ static void dcpep_handle_cb(struct apple_dcp *dcp, enum dcp_context_id context, in = data + sizeof(*hdr); out = in + hdr->in_len; + dcp_trace_panel_setter(dcp, tag, in, hdr->in_len); + if (!handled) { zero_reply = dcp_external_zero_reply(dcp, tag, context, hdr->in_len, hdr->out_len); @@ -1328,11 +1451,15 @@ static void dcpep_handle_cb(struct apple_dcp *dcp, enum dcp_context_id context, return; } /* - * Leaving a callback unanswered wedges the coprocessor: it - * waits for the ack forever and the outer call never returns. - * On H17P answer it with a zeroed output instead. + * Drain an unsupported desktop callback too. The outer modeset + * must see an error even if firmware accepts the zero reply; + * never pop its command before its real ACK arrives. */ - if (dcp->fw_compat == DCP_FIRMWARE_H17P) { + if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP && + READ_ONCE(dcp->desktop->modeset_active)) + WRITE_ONCE(dcp->desktop->modeset_callback_error, true); + if (dcp->fw_compat == DCP_FIRMWARE_H17P || + dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP) { if (hdr->out_len) memset(out, 0, hdr->out_len); depth = dcp_push_depth(&ch->depth); @@ -1613,7 +1740,9 @@ int dcp_modes_replace(struct apple_dcp *dcp, struct dcp_parse_ctx *ctx, ret = parse_array_count(ctx, &entries); if (ret) return ret; + ctx->metadata_timing = dcp_firmware_metadata_enabled(dcp); } + ctx->zero_vrr_range_fallback = dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP; modes = enumerate_modes(ctx, &nr_modes, dcp->width_mm, dcp->height_mm, dcp->notch_height, dcp->fixed_connector_type == DRM_MODE_CONNECTOR_eDP, @@ -1914,8 +2043,11 @@ int dcp_get_modes(struct drm_connector *connector) } } else { u64 generation = 0; + bool panel = dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP && + dcp_has_panel(dcp); const struct drm_edid *derived = nr_modes ? - dcp_firmware_edid(dcp, &generation) : ERR_PTR(-ENODATA); + (panel ? dcp_panel_edid_snapshot(dcp, modes, nr_modes, &generation) : + dcp_firmware_edid(dcp, &generation)) : ERR_PTR(-ENODATA); if (!IS_ERR(derived)) { bool log = !apple_connector->drm_edid_firmware_derived || @@ -1926,16 +2058,21 @@ int dcp_get_modes(struct drm_connector *connector) apple_connector->drm_edid_generation = generation; update = drm_edid_connector_update(connector, derived); drm_edid_free(derived); - if (update || !dcp_firmware_metadata_current(dcp, generation)) { + if (update || !(panel ? dcp_panel_metadata_current(dcp, generation) : + dcp_firmware_metadata_current(dcp, generation))) { drm_edid_connector_update(connector, NULL); apple_connector->drm_edid_firmware_derived = false; } else if (log) { dev_info(dcp->dev, "using firmware-derived EDID metadata for connection %llu\n", generation); } - } else if (apple_connector->drm_edid_firmware_derived) { - drm_edid_connector_update(connector, NULL); - apple_connector->drm_edid_firmware_derived = false; + } else { + if (panel && nr_modes) + dev_dbg(dcp->dev, "panel EDID generation failed: %pe\n", derived); + if (apple_connector->drm_edid_firmware_derived) { + drm_edid_connector_update(connector, NULL); + apple_connector->drm_edid_firmware_derived = false; + } } } @@ -1955,7 +2092,7 @@ int dcp_get_modes(struct drm_connector *connector) published_180hz |= modes[i].automatic_rgb8_quirk; published++; if (modes[i].vrr) { - u16 lo = modes[i].min_vrr >> 16; + u16 lo = DIV_ROUND_UP(modes[i].min_vrr, 1U << 16); u16 hi = modes[i].max_vrr >> 16; if (!min_vfreq || lo < min_vfreq) @@ -1987,6 +2124,10 @@ int dcp_get_modes(struct drm_connector *connector) connector->display_info.monitor_range.max_vfreq = max_vfreq; } + /* EDID base dimensions are rounded to cm; the panel's OF/ADT size is exact. */ + if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP && dcp_has_panel(dcp)) + dcp_update_connector_dimensions(dcp, connector); + return published; } @@ -2003,7 +2144,8 @@ static int dcp_desktop_modeset(struct apple_dcp *dcp, if (d->trial_modeset_ok) { d->trial_modeset_ok = false; - if (d->mode_live && lookup_mode(dcp, &crtc_state->mode, &matched) && + if (!crtc_state->vrr_enabled && d->mode_live && + lookup_mode(dcp, &crtc_state->mode, &matched) && matched.timing_mode_id == d->trial_timing_mode_id && matched.color_mode_id == d->trial_color_mode_id) { dev_dbg(dcp->dev, "keeping the takeover mode " DRM_MODE_FMT "\n", diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index bdb78c999eab2..9d248cccf72da 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -32,6 +32,7 @@ #include "iomfb.h" #include "iomfb_internal.h" #include "parser.h" +#include "dcp-refresh.h" #include "dcp-property.h" #include "trace.h" #include "version_utils.h" @@ -462,6 +463,12 @@ static void dcpep_cb_swap_complete(struct apple_dcp *dcp, } if (dcp->hw.iomfb_method_profile != DCP_IOMFB_METHODS_H17G) retire = iomfb_present_complete_h17p(dcp); +#endif +#if DCP_FW_DESKTOP_26(DCP_FW_VER) + /* Late or duplicate completions cannot retire the replacement's flip. */ + if (!dcp_desktop_present_complete(dcp, resp->swap_id)) + return; + dcp_backlight_desktop_finish(dcp, resp->swap_id, false, true); #endif trace_iomfb_swap_complete(dcp, resp->swap_id); dcp->last_swap_id = resp->swap_id; @@ -1139,7 +1146,11 @@ dcpep_cb_read_edt_data(struct apple_dcp *dcp, struct dcp_read_edt_data_req *req) static void iomfbep_cb_enable_backlight_message_ap_gated(struct apple_dcp *dcp, u8 *enabled) { -#if DCP_FW_H17(DCP_FW_VER) +#if DCP_FW_DESKTOP_26(DCP_FW_VER) + if (dcp_has_panel(dcp) && dcp_backlight_resend(dcp)) + schedule_work(&dcp->bl_update_wq); + return; +#elif DCP_FW_H17(DCP_FW_VER) /* H17P carries the level only in presents; ask for one more. */ if (dcp->hw.iomfb_method_profile != DCP_IOMFB_METHODS_H17G) { if (dcp_has_panel(dcp) && dcp_backlight_active(dcp) && @@ -1253,11 +1264,20 @@ static bool dcpep_process_chunks(struct apple_dcp *dcp, } /* Validate serialized extent outside the metadata spinlock. */ - if (dcp->hw.iomfb_method_profile == DCP_IOMFB_METHODS_H17P_EXTERNAL) + if (dcp_firmware_metadata_enabled(dcp)) dcp->chunks.payload_complete = dcp->chunks.contiguous && dcp_property_complete(dcp->chunks.data, dcp->chunks.received, dcp->chunks.length); + dcp_trace_panel_property(dcp, 0, req->key, + strnlen(req->key, sizeof(req->key)), + dcp->chunks.data, dcp->chunks.length); + + if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP) + dcp_panel_edid_receive(dcp, dcp->chunks.data, dcp->chunks.length); + if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP && + (!strcmp(req->key, "EDID") || !strcmp(req->key, "edid"))) + return true; ret = parse(dcp->chunks.data, dcp->chunks.length, &ctx); if (ret) { @@ -1309,11 +1329,20 @@ static bool dcpep_process_chunks(struct apple_dcp *dcp, return false; } - if (dcp->hw.iomfb_method_profile == DCP_IOMFB_METHODS_H17P_EXTERNAL) { + if (dcp_firmware_metadata_enabled(dcp)) { struct dcp_display_metadata metadata; ret = parse_display_metadata(&metadata_ctx, &metadata); + if (ret) + dev_dbg(dcp->dev, "DisplayAttributes without sink identity: %d\n", + ret); dcp_firmware_metadata_attributes(dcp, ret ? NULL : &metadata); + } else if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP && + dcp_has_panel(dcp)) { + struct dcp_display_metadata metadata; + + ret = parse_panel_metadata(&metadata_ctx, &metadata); + dcp_panel_metadata_replace(dcp, ret ? NULL : &metadata); } } @@ -1739,6 +1768,9 @@ static bool dcp_present_begin(struct apple_dcp *dcp, u32 swap_id) dcp->crashed = true; return false; } +#elif DCP_FW_DESKTOP_26(DCP_FW_VER) + WRITE_ONCE(dcp->desktop->present_id, swap_id); + WRITE_ONCE(dcp->desktop->present_pending, true); #endif return true; } @@ -1825,6 +1857,12 @@ static void dcp_swap_cleared(struct apple_dcp *dcp, void *data, void *cookie) if (ret) status = ~0U; +#if DCP_FW_DESKTOP_26(DCP_FW_VER) + if (status && swap_id == READ_ONCE(dcp->desktop->present_id)) + WRITE_ONCE(dcp->desktop->present_pending, false); + dcp_backlight_desktop_finish(dcp, swap_id, true, !status); +#endif + if (status) { /* An aborted present has already been reported. */ if (ret != -ECANCELED) @@ -2038,6 +2076,8 @@ void DCP_FW_NAME(iomfb_poweron)(struct apple_dcp *dcp) ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(10000)); #if DCP_FW_DESKTOP_26(DCP_FW_VER) dcp->desktop->poweron_result = ret > 0 ? 0 : ret == 0 ? 1 : 2; + if (ret > 0) + dcp_backlight_desktop_power(dcp, true); #endif if (ret == 0) { @@ -2168,6 +2208,9 @@ void DCP_FW_NAME(iomfb_poweroff)(struct apple_dcp *dcp) if (dcp->hw.iomfb_method_profile == DCP_IOMFB_METHODS_H17G) dcp_prepare_clear_swap(dcp, swap); #else +#if DCP_FW_DESKTOP_26(DCP_FW_VER) + dcp_backlight_desktop_power(dcp, false); +#endif dcp_prepare_clear_swap(dcp, swap); #endif @@ -2643,6 +2686,12 @@ static void dcp_swapped(struct apple_dcp *dcp, void *data, void *cookie) if (ret) status = ~0U; +#if DCP_FW_DESKTOP_26(DCP_FW_VER) + if (status && swap_id == READ_ONCE(dcp->desktop->present_id)) + WRITE_ONCE(dcp->desktop->present_pending, false); + dcp_backlight_desktop_finish(dcp, swap_id, true, !status); +#endif + if (status) { /* An aborted present has already been reported. */ if (ret != -ECANCELED) @@ -2678,6 +2727,18 @@ static void dcp_swap_started(struct apple_dcp *dcp, void *data, void *cookie) if (dcp_present_retires(dcp)) dcp_arm_retained_framebuffers(dcp, resp->swap_id); +#if DCP_FW_DESKTOP_26(DCP_FW_VER) + if (dcp_has_panel(dcp)) { + u32 nits; + bool have_surface = false; + + for (unsigned int i = 0; i < SWAP_SURFACES; i++) + have_surface |= !DCP_FW_SWAP(dcp).surf_null[i]; + if (dcp_backlight_desktop_prepare(dcp, resp->swap_id, + have_surface, &nits)) + dcp_swap_set_brightness(&DCP_FW_SWAP(dcp).swap, nits); + } +#endif trace_iomfb_swap_submit(dcp, resp->swap_id); dcp_h17p_prepare_swap(dcp); #if DCP_FW_DESKTOP_26(DCP_FW_VER) @@ -2888,8 +2949,19 @@ struct dcp_modeset_cookie { struct dcp_set_digital_out_mode_req actual; struct dcp_set_digital_out_mode_req bootstrap; bool use_bootstrap; + /* Adaptive sync is programmed on (a nonzero minimum rate). */ + bool vrr; }; +/* Finish only after the RPC which contained the unknown callback has replied. */ +static bool dcp_modeset_callback_failed(struct apple_dcp *dcp, void *cookie) +{ + if (!READ_ONCE(dcp->desktop->modeset_callback_error)) + return false; + complete_set_digital_out_mode(dcp, NULL, cookie); + return true; +} + /* * get_mode_info (A364) fills the firmware's mode record for a timing and * color pair before set_digital_out_mode (A413) selects it. The request @@ -2914,17 +2986,27 @@ static void dcp_get_mode_info(struct apple_dcp *dcp, sizeof(struct dcp_get_mode_info_resp_v26_6), &req, cb, cookie); } +static void dcp_modeset_actual_done(struct apple_dcp *dcp, void *data, + void *cookie) +{ + if (dcp_modeset_callback_failed(dcp, cookie)) + return; + complete_set_digital_out_mode(dcp, data, cookie); +} + static void dcp_modeset_after_actual_info(struct apple_dcp *dcp, void *data, void *cookie) { struct dcp_modeset_cookie *modeset = cookie; + if (dcp_modeset_callback_failed(dcp, cookie)) + return; if (!data) { complete_set_digital_out_mode(dcp, NULL, cookie); return; } dcp_set_digital_out_mode(dcp, false, &modeset->actual, - complete_set_digital_out_mode, cookie); + dcp_modeset_actual_done, cookie); } static void dcp_modeset_after_bootstrap(struct apple_dcp *dcp, void *data, @@ -2932,8 +3014,10 @@ static void dcp_modeset_after_bootstrap(struct apple_dcp *dcp, void *data, { struct dcp_modeset_cookie *modeset = cookie; + if (dcp_modeset_callback_failed(dcp, cookie)) + return; if (!data || get_unaligned_le32(data)) { - complete_set_digital_out_mode(dcp, data, cookie); + dcp_modeset_actual_done(dcp, data, cookie); return; } dcp_get_mode_info(dcp, &modeset->actual, dcp_modeset_after_actual_info, @@ -2945,6 +3029,8 @@ static void dcp_modeset_after_bootstrap_info(struct apple_dcp *dcp, void *data, { struct dcp_modeset_cookie *modeset = cookie; + if (dcp_modeset_callback_failed(dcp, cookie)) + return; if (!data) { complete_set_digital_out_mode(dcp, NULL, cookie); return; @@ -2977,6 +3063,9 @@ int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp, if (dcp->crashed) return -ENODEV; + /* A timed-out command still owns its channel until firmware replies. */ + if (READ_ONCE(dcp->ch_cmd.depth)) + return -EBUSY; if (!lookup_mode(dcp, &crtc_state->mode, mode)) { dev_err(dcp->dev, "no match for " DRM_MODE_FMT "\n", @@ -3007,8 +3096,8 @@ int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp, .timing_mode_id = mode->timing_mode_id }; - /* VRR modes need swap timestamps, see iomfb_flush(). */ - dcp->use_timestamps = mode->vrr; + /* VRR modes need swap timestamps, see iomfb_flush(); ProMotion fixed 120 too. */ + dcp->use_timestamps = mode->needs_timestamps; /* A display within one pipe may let the other input have a pipe again. */ { @@ -3037,6 +3126,8 @@ int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp, mode->vrr ? mode->min_vrr : 0); if (ret != -EOPNOTSUPP) { dcp->desktop->mode_live = !ret; + /* The pair runs with the mode's adaptive-sync rate. */ + dcp->vrr_enabled = !ret && mode->vrr; if (!ret) atomic_inc(&dcp->modesets_done); return ret; @@ -3059,6 +3150,9 @@ int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp, return -ENOMEM; cookie = &modeset->wait; modeset->actual = dcp->mode; + /* After an adaptive swap timed out (dcp_swap_watchdog), stay fixed. */ + modeset->vrr = mode->vrr && crtc_state->vrr_enabled && + !READ_ONCE(dcp->desktop->vrr_failed); cookie->status = ~0U; init_completion(&cookie->done); @@ -3081,15 +3175,18 @@ int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp, modeset->bootstrap.timing_mode_id); } + WRITE_ONCE(dcp->desktop->modeset_callback_error, false); + WRITE_ONCE(dcp->desktop->modeset_active, true); /* * Adaptive sync (IOMFBParameter 14, minimum rate in 16.16 Hz, 0 = off) * goes before the mode is selected; the firmware then reports the mode - * set with VRR on. + * set with VRR on. A fixed mode after adaptive sync was on turns it + * off explicitly. */ - if (mode->vrr) { + if (mode->vrr || dcp->vrr_enabled) { struct dcp_set_parameter_dcp param = { .param = IOMFBPARAM_ADAPTIVE_SYNC, - .value = { crtc_state->vrr_enabled ? mode->min_vrr : 0 }, + .value = { modeset->vrr ? mode->min_vrr : 0 }, .count = 3, }; @@ -3114,6 +3211,9 @@ int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp, dev_err(dcp->dev, "waiting on set_digital_out_mode failed:%d\n", ret); ret = -EIO; + } else if (READ_ONCE(dcp->desktop->modeset_callback_error)) { + dev_err(dcp->dev, "unsupported callback during mode set\n"); + ret = -EOPNOTSUPP; } else if (!READ_ONCE(cookie->replied)) { dev_err(dcp->dev, "set_digital_out_mode got no reply\n"); ret = -EIO; @@ -3127,6 +3227,8 @@ int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp, jiffies_to_msecs(ret)); ret = 0; } + WRITE_ONCE(dcp->desktop->modeset_active, false); + dcp->vrr_enabled = !ret && modeset->vrr; kref_put(&cookie->refcount, release_wait_cookie); dcp->desktop->mode_live = !ret; @@ -3289,7 +3391,10 @@ void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, stru struct DCP_FW_NAME(dcp_swap_submit_req) *req = &DCP_FW_SWAP(dcp); int plane_idx, l; int has_surface = 0; + +#if !DCP_FW_DESKTOP_26(DCP_FW_VER) bool update_brightness; +#endif crtc_state = drm_atomic_get_new_crtc_state(commit, crtc); @@ -3390,19 +3495,13 @@ void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, stru } #if DCP_FW_DESKTOP_26(DCP_FW_VER) - if (has_surface && dcp->use_timestamps) { - /* - * Timestamp types 1, 2 and 7 hold the time the swap should be - * shown, in the firmware's clock. A target long past shows it - * at the next refresh: 120 Hz in the fixed mode, as soon as - * the panel allows with adaptive sync. The architectural - * counter must not go here: after a VRR mode set the firmware - * then never shows the swap, and it holds a swap whose target - * it reads as more than about a frame ahead. - */ - req->swap.timestamp[0] = 120; - req->swap.timestamp[1] = 120; - req->swap.timestamp[6] = 120; + { + u64 deadline = dcp_desktop_present_deadline(has_surface, dcp->use_timestamps); + + /* Types 1, 2 and 7 carry the same desktop presentation deadline. */ + req->swap.timestamp[0] = deadline; + req->swap.timestamp[1] = deadline; + req->swap.timestamp[6] = deadline; } #else if (has_surface && (dcp->use_timestamps || dcp->vrr_enabled)) { @@ -3431,28 +3530,16 @@ void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, stru /* These fields should be set together */ req->swap.swap_completed = req->swap.swap_enabled; +#if DCP_FW_DESKTOP_26(DCP_FW_VER) + /* The shared ramp is encoded after swap_start, at submission time. */ + dcp->brightness.update = false; +#else /* update brightness if changed */ update_brightness = dcp_has_panel(dcp) && dcp->brightness.update; #if DCP_FW_H17(DCP_FW_VER) update_brightness = update_brightness && dcp->hw.iomfb_method_profile == DCP_IOMFB_METHODS_H17G; #endif -#if DCP_FW_DESKTOP_26(DCP_FW_VER) - /* - * The firmware publishes no initial level, so nits stays at the - * registration default until userspace writes the backlight. Sending - * that from the forced post-poweron update can turn the panel off: - * keep the level the boot loader set until one is requested. - */ - if (update_brightness && !dcp->brightness.requested) { - dcp->brightness.update = false; - update_brightness = false; - } - if (update_brightness) { - dcp_swap_set_brightness(&req->swap, dcp->brightness.nits); - dcp->brightness.update = false; - } -#else if (update_brightness) { #if DCP_FW_LAYOUT_14_7(DCP_FW_VER) req->swap.bl_update = 1; diff --git a/drivers/gpu/drm/apple/iomfb_v26_6_desktop.c b/drivers/gpu/drm/apple/iomfb_v26_6_desktop.c index b5e71329eb9ce..064eff4beff53 100644 --- a/drivers/gpu/drm/apple/iomfb_v26_6_desktop.c +++ b/drivers/gpu/drm/apple/iomfb_v26_6_desktop.c @@ -81,9 +81,14 @@ static bool trampoline_passthrough_dict_v26_6(struct apple_dcp *dcp, int tag, struct dcp_dict_passthrough_resp_v26_6 *resp = out; trace_iomfb_callback(dcp, tag, "dcpep_cb_passthrough_dict"); + dcp_trace_panel_property(dcp, tag, (const char *)req->key, + strnlen((const char *)req->key, sizeof(req->key)), + req->value_null ? NULL : req->value, sizeof(req->value)); - if (!req->value_null) + if (!req->value_null) { + dcp_panel_edid_receive(dcp, req->value, sizeof(req->value)); memcpy(resp->value, req->value, sizeof(resp->value)); + } resp->ret = false; memset(resp->padding, 0, sizeof(resp->padding)); @@ -91,6 +96,65 @@ static bool trampoline_passthrough_dict_v26_6(struct apple_dcp *dcp, int tag, return true; } +/* The firmware can publish panel identity through dictionary setters too. */ +static bool trampoline_panel_dictionary_v26_6(struct apple_dcp *dcp, int tag, + void *out, void *in) +{ + /* Service dictionary setters prefix the same record with a handle. */ + const struct dcp_dict_passthrough_req_v26_6 *req = tag == 413 ? in + 4 : in; + const char *key = (const char *)req->key; + struct dcp_display_metadata metadata; + struct dcp_parse_ctx ctx; + int ret; + + trace_iomfb_callback(dcp, tag, __func__); + dcp_trace_panel_property(dcp, tag, key, strnlen(key, sizeof(req->key)), + req->value_null ? NULL : req->value, sizeof(req->value)); + if (!dcp_has_panel(dcp) || req->value_null || + !memchr(key, 0, sizeof(req->key))) + goto done; + /* A real EDID may be a blob or nested in a property dictionary. */ + dcp_panel_edid_receive(dcp, req->value, sizeof(req->value)); + if (strcmp(key, "DisplayAttributes") && strcmp(key, "ProductAttributes")) + goto done; + ret = parse(req->value, sizeof(req->value), &ctx); + if (ret) + goto done; + if (!strcmp(key, "ProductAttributes")) + ret = parse_panel_product_metadata(&ctx, &metadata); + else + ret = parse_panel_metadata(&ctx, &metadata); + if (!ret) + dcp_panel_metadata_replace(dcp, &metadata); +done: + /* Preserve the existing setter acknowledgment for unrelated properties. */ + *(u8 *)out = true; + return true; +} + +static bool iomfb_check_callback_exact_v26_6_desktop(int tag, u32 in_len, u32 out_len) +{ + switch (tag) { + case 201: + return in_len == 12 && out_len == 16; + case 202: + return in_len == 24 && !out_len; + case 451: + return in_len == 20 && out_len == 28; + case 452: + return in_len == 24 && out_len == 20; + case 454: + return in_len == 4 && out_len == 4; + case 413: + return in_len == 4168 && (out_len == 1 || out_len == 4); + case 552: + case 561: + return in_len == 4164 && (out_len == 1 || out_len == 4); + default: + return true; + } +} + /* * unmap_piodma (D202): the buffer id, the id again, the DVA (unaligned) and a * trailing word. The older firmware sends the DVA after a virtual address. @@ -175,13 +239,14 @@ static const iomfb_cb_handler cb_handlers[IOMFB_MAX_CB] = { [406] = trampoline_nop, /* set_fx_prop: a property notification */ [408] = trampoline_get_frequency, [411] = trampoline_map_reg, - [413] = trampoline_true, /* sr_set_property_dict */ + [413] = trampoline_panel_dictionary_v26_6, /* sr_set_property_dict */ [414] = trampoline_sr_set_property_int, [415] = trampoline_true, /* sr_set_property_bool */ [451] = trampoline_allocate_buffer, + [452] = trampoline_map_physical, [454] = trampoline_release_mem_desc_v26_6, - [552] = trampoline_true, /* set_property_dict_0 */ - [561] = trampoline_true, /* set_property_dict */ + [552] = trampoline_panel_dictionary_v26_6, + [561] = trampoline_panel_dictionary_v26_6, [563] = trampoline_true, /* set_property_int */ [565] = trampoline_true, /* set_property_bool */ [567] = trampoline_true, /* set_property_str */ @@ -191,6 +256,7 @@ static const iomfb_cb_handler cb_handlers[IOMFB_MAX_CB] = { [576] = trampoline_nop, [581] = trampoline_nop, [582] = trampoline_true, /* create_default_fb_surface */ + [588] = trampoline_nop, /* resize_default_fb_surface_gated */ [590] = trampoline_swap_complete, [592] = trampoline_swap_complete_intent_gated, [593] = trampoline_abort_swap_ap_gated, @@ -247,6 +313,7 @@ static const struct dcp_cb_min_v26_6_desktop cb_min[IOMFB_MAX_CB] = { [134] = CB_MIN(0, sizeof(u32)), [201] = CB_MIN(sizeof(struct dcp_map_buf_req), sizeof(struct DCP_FW_NAME(dcp_map_buf_resp))), + [202] = CB_MIN(sizeof(struct dcp_unmap_buf_req_v26_6), 0), [206] = CB_MIN(0, sizeof(u8)), [207] = CB_MIN(0, sizeof(u8)), [208] = CB_MIN(0, 0), @@ -264,6 +331,9 @@ static const struct dcp_cb_min_v26_6_desktop cb_min[IOMFB_MAX_CB] = { [415] = CB_MIN(0, sizeof(u8)), [451] = CB_MIN(sizeof(struct dcp_allocate_buffer_req), sizeof(struct dcp_allocate_buffer_resp)), + [452] = CB_MIN(sizeof(struct dcp_map_physical_req), + sizeof(struct dcp_map_physical_resp)), + [454] = CB_MIN(sizeof(u32), sizeof(u32)), [552] = CB_MIN(0, sizeof(u8)), [561] = CB_MIN(0, sizeof(u8)), [563] = CB_MIN(0, sizeof(u8)), @@ -275,6 +345,7 @@ static const struct dcp_cb_min_v26_6_desktop cb_min[IOMFB_MAX_CB] = { [576] = CB_MIN(0, 0), [581] = CB_MIN(0, 0), [582] = CB_MIN(0, sizeof(u8)), + [588] = CB_MIN(0, 0), [590] = CB_MIN(sizeof(struct DCP_FW_NAME(dc_swap_complete_resp)), 0), [592] = CB_MIN(sizeof(dcp_swap_intent_t), 0), [593] = CB_MIN(sizeof(u32), 0), @@ -290,7 +361,8 @@ bool iomfb_check_callback_v26_6_desktop(int tag, u32 in_len, u32 out_len) { if (tag < 0 || tag >= IOMFB_MAX_CB || !cb_min[tag].valid) return false; - return in_len >= cb_min[tag].in_len && out_len >= cb_min[tag].out_len; + return in_len >= cb_min[tag].in_len && out_len >= cb_min[tag].out_len && + iomfb_check_callback_exact_v26_6_desktop(tag, in_len, out_len); } void DCP_FW_NAME(iomfb_start)(struct apple_dcp *dcp) diff --git a/drivers/gpu/drm/apple/iomfb_v26_6_desktop.h b/drivers/gpu/drm/apple/iomfb_v26_6_desktop.h index 697edc47fde3b..6f61493858e8b 100644 --- a/drivers/gpu/drm/apple/iomfb_v26_6_desktop.h +++ b/drivers/gpu/drm/apple/iomfb_v26_6_desktop.h @@ -49,4 +49,6 @@ struct dcp_set_frame_sync_props_resp_v26_6_desktop { u8 props[0x50]; } __packed; +bool iomfb_check_callback_v26_6_desktop(int tag, u32 in_len, u32 out_len); + #endif /* __APPLE_IOMFB_V26_6_DESKTOP_H__ */ diff --git a/drivers/gpu/drm/apple/parser-test.c b/drivers/gpu/drm/apple/parser-test.c index 9cf57af1b3c94..dc807a2013a43 100644 --- a/drivers/gpu/drm/apple/parser-test.c +++ b/drivers/gpu/drm/apple/parser-test.c @@ -3,9 +3,12 @@ #include #include #include +#include +#include #include #include "dcp.h" +#include #include #include "connector.h" @@ -13,6 +16,8 @@ #include "dcp-property.h" #include "iomfb_internal.h" #include "parser.h" +#include "dcp-panel-adt.h" +#include "dcp-panel-test.h" static struct device parser_device = { .init_name = "apple-display-parser-test", @@ -22,6 +27,13 @@ static struct apple_dcp parser_dcp = { .dev = &parser_device, }; +/* The probe owns the metadata lock beside the mode catalogue. */ +static void parser_modes_init(struct apple_dcp *dcp) +{ + dcp_modes_init(dcp); + spin_lock_init(&dcp->metadata.lock); +} + struct parser_blob { u8 data[8192]; u32 size; @@ -34,6 +46,7 @@ struct parser_record { s64 first_format; bool bad_score_type; bool vrr; + bool vrr_zero; }; enum parser_missing { @@ -143,8 +156,8 @@ static void blob_record(struct parser_blob *blob, const struct parser_record *re record->second_color_missing, DCP_COLOR_FORMAT_RGB); } if (record->vrr) { - blob_int(blob, "MinimumVariableRefreshRate", 48 << 16); - blob_int(blob, "MaximumVariableRefreshRate", 120 << 16); + blob_int(blob, "MinimumVariableRefreshRate", record->vrr_zero ? 0 : 48 << 16); + blob_int(blob, "MaximumVariableRefreshRate", record->vrr_zero ? 0 : 120 << 16); } } @@ -435,6 +448,58 @@ static void parser_notch_vrr(struct kunit *test) kfree(modes); } +static void parser_vrr_empty_range(struct kunit *test) +{ + struct parser_record record = valid_record(7, 10); + struct dcp_display_mode *modes; + struct parser_blob *blob; + unsigned int count, desktop; + struct dcp_parse_ctx ctx; + + /* Desktop firmware can publish both fields as zero placeholders. */ + record.rate = 120 << 16; + record.vrr = true; + record.vrr_zero = true; + blob = make_blob(test, &record, 1); + KUNIT_ASSERT_NOT_NULL(test, blob); + modes = parse_blob(blob, &count, 0, true); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, modes); + KUNIT_ASSERT_EQ(test, count, 1U); + KUNIT_EXPECT_FALSE(test, modes[0].vrr); + KUNIT_EXPECT_EQ(test, modes[0].min_vrr, 0U); + KUNIT_EXPECT_EQ(test, modes[0].max_vrr, 0U); + kfree(modes); + + /* A display without the internal ProMotion policy stays fixed. */ + modes = parse_blob(blob, &count, 0, false); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, modes); + KUNIT_ASSERT_EQ(test, count, 1U); + KUNIT_EXPECT_FALSE(test, modes[0].vrr); + KUNIT_EXPECT_EQ(test, modes[0].min_vrr, 0U); + KUNIT_EXPECT_EQ(test, modes[0].max_vrr, 0U); + kfree(modes); + /* The catalog owner enables this convention only for desktop firmware. */ + for (desktop = 0; desktop < 2; desktop++) { + struct apple_dcp *dcp = kunit_kzalloc(test, sizeof(*dcp), GFP_KERNEL); + u64 generation; + + KUNIT_ASSERT_NOT_NULL(test, dcp); + parser_modes_init(dcp); + dcp->fw_compat = desktop ? DCP_FIRMWARE_V_26_6_DESKTOP : + DCP_FIRMWARE_V_13_5; + dcp->fixed_connector_type = DRM_MODE_CONNECTOR_eDP; + KUNIT_ASSERT_EQ(test, parse(blob->data, blob->size, &ctx), 0); + ctx.dcp = &parser_dcp; + generation = dcp_modes_transfer_begin(dcp); + KUNIT_ASSERT_EQ(test, dcp_modes_replace(dcp, &ctx, generation), 0); + KUNIT_ASSERT_EQ(test, dcp->nr_modes, 1U); + KUNIT_EXPECT_EQ(test, dcp->modes[0].vrr, !!desktop); + KUNIT_EXPECT_EQ(test, dcp->modes[0].min_vrr, desktop ? 24U << 16 : 0U); + KUNIT_EXPECT_EQ(test, dcp->modes[0].max_vrr, desktop ? 120U << 16 : 0U); + kfree(dcp->modes); + } +} + static void parser_replacement(struct kunit *test) { struct parser_record record = valid_record(7, 10); @@ -445,7 +510,7 @@ static void parser_replacement(struct kunit *test) struct drm_display_mode requested; KUNIT_ASSERT_NOT_NULL(test, dcp); - dcp_modes_init(dcp); + parser_modes_init(dcp); blob = make_blob(test, &record, 1); KUNIT_ASSERT_NOT_NULL(test, blob); KUNIT_ASSERT_EQ(test, parse(blob->data, blob->size, &ctx), 0); @@ -493,8 +558,7 @@ static void parser_held_keeps_edid(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, dcp); KUNIT_ASSERT_NOT_NULL(test, connector); KUNIT_ASSERT_NOT_NULL(test, pipeline); - dcp_modes_init(dcp); - spin_lock_init(&dcp->metadata.lock); + parser_modes_init(dcp); apple_connector_edid_init(connector); apple_connector_set_pipeline(connector, pipeline); dcp->connector = connector; @@ -528,7 +592,7 @@ static void parser_attachment_admission(struct kunit *test) struct drm_display_mode requested; KUNIT_ASSERT_NOT_NULL(test, dcp); - dcp_modes_init(dcp); + parser_modes_init(dcp); spin_lock_init(&dcp->mode_state.lock); blob = make_blob(test, &record, 1); KUNIT_ASSERT_NOT_NULL(test, blob); @@ -575,7 +639,7 @@ static void parser_stale_transfer(struct kunit *test) u64 old_generation, current_generation; KUNIT_ASSERT_NOT_NULL(test, dcp); - dcp_modes_init(dcp); + parser_modes_init(dcp); blob = make_blob(test, &record, 1); KUNIT_ASSERT_NOT_NULL(test, blob); KUNIT_ASSERT_EQ(test, parse(blob->data, blob->size, &ctx), 0); @@ -649,7 +713,7 @@ static void parser_route_catalog_handoff(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, connector); KUNIT_ASSERT_NOT_NULL(test, other); KUNIT_ASSERT_NOT_NULL(test, pipeline); - dcp_modes_init(dcp); + parser_modes_init(dcp); dcp->dev = &pipeline->dev; connector->dcp = pipeline; observer->dcp = dcp; @@ -770,7 +834,7 @@ static void parser_route_programming_guard(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, connector->base.state); connector->base.state->crtc = &crtc->base; crtc_state.connector_mask = drm_connector_mask(&connector->base); - dcp_modes_init(dcp); + parser_modes_init(dcp); dcp->modes_admitted = true; dcp->fw_compat = DCP_FIRMWARE_V_13_5; dcp->nr_typec_routes = 1; @@ -856,7 +920,7 @@ static void parser_route_invalid_own_mode(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, dcp); KUNIT_ASSERT_NOT_NULL(test, crtc); KUNIT_ASSERT_NOT_NULL(test, pdev); - dcp_modes_init(dcp); + parser_modes_init(dcp); dcp->fw_compat = DCP_FIRMWARE_V_13_5; dcp->nr_typec_routes = 1; crtc->dcp = pdev; @@ -875,7 +939,7 @@ static void parser_connector_ownership(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, dcp); KUNIT_ASSERT_NOT_NULL(test, fixed); KUNIT_ASSERT_NOT_NULL(test, borrowed); - dcp_modes_init(dcp); + parser_modes_init(dcp); mutex_lock(&dcp->modes_lock); dcp->connector = borrowed; dcp->modes_admitted = true; @@ -903,7 +967,7 @@ static void parser_route_retirement(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, fixed); KUNIT_ASSERT_NOT_NULL(test, route); KUNIT_ASSERT_NOT_NULL(test, other); - dcp_modes_init(dcp); + parser_modes_init(dcp); dcp->connector = fixed; dcp->active_typec_route = route; dcp->modes_admitted = true; @@ -927,7 +991,7 @@ static void parser_rejected_replacement(struct kunit *test) struct parser_blob *blob; KUNIT_ASSERT_NOT_NULL(test, dcp); - dcp_modes_init(dcp); + parser_modes_init(dcp); blob = make_blob(test, &record, 1); KUNIT_ASSERT_NOT_NULL(test, blob); KUNIT_ASSERT_EQ(test, parse(blob->data, blob->size, &ctx), 0); @@ -969,7 +1033,7 @@ static void parser_attribute_epoch(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, dcp); KUNIT_ASSERT_NOT_NULL(test, blob); - dcp_modes_init(dcp); + parser_modes_init(dcp); put_unaligned_le32(0xd3, blob->data); blob->size = 4; blob_tag(blob, 1, 3); @@ -1003,7 +1067,7 @@ static void parser_attributes_malformed(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, dcp); KUNIT_ASSERT_NOT_NULL(test, blob); - dcp_modes_init(dcp); + parser_modes_init(dcp); dcp->width_mm = 520; dcp->height_mm = 290; dcp->ext_backlight = true; @@ -1040,7 +1104,7 @@ static void parser_optional_flag_extent(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, dcp); KUNIT_ASSERT_NOT_NULL(test, blob); - dcp_modes_init(dcp); + parser_modes_init(dcp); put_unaligned_le32(0xd3, blob->data); blob->size = 4; blob_tag(blob, 1, 3); @@ -1089,11 +1153,12 @@ static void parser_attribute_deferred(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, connector); KUNIT_ASSERT_NOT_NULL(test, dev); KUNIT_ASSERT_NOT_NULL(test, blob); - dcp_modes_init(dcp); + parser_modes_init(dcp); observer->dcp = dcp; INIT_WORK(&connector->hotplug_wq, parser_catalog_ready); mutex_init(&dev->mode_config.mutex); connector->base.dev = dev; + apple_connector_backlight_init(connector); dcp->connector = connector; dcp->panel.width_mm = 600; dcp->modes = kmalloc_obj(*dcp->modes); @@ -1118,6 +1183,9 @@ static void parser_attribute_deferred(struct kunit *test) KUNIT_EXPECT_EQ(test, connector->base.display_info.width_mm, 520U); KUNIT_EXPECT_EQ(test, connector->base.display_info.height_mm, 290U); dcp_modes_release(dcp); + cancel_work_sync(&connector->hotplug_wq); + cancel_work_sync(&connector->bl_sync_wq); + apple_connector_backlight_early_unregister(connector); } static void parser_dimensions_lifetime(struct kunit *test) @@ -1129,7 +1197,7 @@ static void parser_dimensions_lifetime(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, dcp); KUNIT_ASSERT_NOT_NULL(test, connector); KUNIT_ASSERT_NOT_NULL(test, dev); - dcp_modes_init(dcp); + parser_modes_init(dcp); mutex_init(&dev->mode_config.mutex); connector->base.dev = dev; dcp->connector = connector; @@ -1158,7 +1226,7 @@ static void parser_catalog_release(struct kunit *test) struct apple_dcp *dcp = kunit_kzalloc(test, sizeof(*dcp), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, dcp); - dcp_modes_init(dcp); + parser_modes_init(dcp); dcp->modes = kmalloc_obj(*dcp->modes); KUNIT_ASSERT_NOT_NULL(test, dcp->modes); dcp->nr_modes = 1; @@ -1238,12 +1306,341 @@ static void parser_route_prepare_scope(struct kunit *test) dcp->typec_follow_start = true; dcp->typec_follow_gen = dcp->typec_generation; mutex_init(&dcp->hpd_mutex); + mutex_init(&dcp->tb_lock); KUNIT_EXPECT_EQ(test, dcp_typec_prepare_route(dcp), -ENODEV); + mutex_destroy(&dcp->tb_lock); mutex_destroy(&dcp->hpd_mutex); mutex_destroy(&dcp->modes_lock); } +static void parser_panel_identity(struct kunit *test) +{ + struct parser_blob *blob = kunit_kzalloc(test, sizeof(*blob), GFP_KERNEL); + struct dcp_display_metadata metadata; + struct dcp_parse_ctx ctx; + const struct drm_edid *edid; + const struct edid *raw; + struct dcp_display_mode modes[2] = {}; + + modes[0].mode = (struct drm_display_mode) { + .clock = 540005, .hdisplay = 3456, .hsync_start = 3500, + .hsync_end = 3550, .htotal = 4000, .vdisplay = 2160, + .vsync_start = 2165, .vsync_end = 2170, .vtotal = 2250, + .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC, + }; + modes[1] = modes[0]; + modes[1].mode.clock = 1080005; + modes[1].vrr = true; + modes[1].min_vrr = 24U << 16; + modes[1].max_vrr = 120U << 16; + + KUNIT_ASSERT_NOT_NULL(test, blob); + put_unaligned_le32(0xd3, blob->data); + blob->size = 4; + blob_tag(blob, 1, 1); + blob_key(blob, "ProductAttributes"); + blob_tag(blob, 1, 4); + blob_key(blob, "ManufacturerID"); + blob_key(blob, "TST"); + blob_key(blob, "ProductName"); + blob_key(blob, "Synthetic Internal Panel"); + blob_int(blob, "ProductID", 42); + blob_int(blob, "SerialNumber", 0x12345678); + KUNIT_ASSERT_EQ(test, parse(blob->data, blob->size, &ctx), 0); + KUNIT_ASSERT_EQ(test, parse_panel_metadata(&ctx, &metadata), 0); + KUNIT_EXPECT_STREQ(test, metadata.name, "Synthetic Int"); + edid = dcp_panel_edid(&metadata, modes, ARRAY_SIZE(modes), 345, 223); + KUNIT_ASSERT_FALSE(test, IS_ERR(edid)); + KUNIT_EXPECT_TRUE(test, drm_edid_valid(edid)); + raw = drm_edid_raw(edid); + KUNIT_EXPECT_EQ(test, le16_to_cpu(raw->product_id.product_code), 42); + KUNIT_EXPECT_EQ(test, le32_to_cpu(raw->product_id.serial_number), 0x12345678U); + KUNIT_EXPECT_EQ(test, raw->gamma, 0xff); + KUNIT_EXPECT_EQ(test, le16_to_cpu(raw->detailed_timings[0].pixel_clock), 54001); + KUNIT_EXPECT_EQ(test, raw->detailed_timings[3].data.other_data.data.range.min_vfreq, 24); + KUNIT_EXPECT_EQ(test, raw->detailed_timings[3].data.other_data.data.range.max_vfreq, 120); + KUNIT_EXPECT_EQ(test, modes[0].mode.clock, 540005); + drm_edid_free(edid); +} + +static void parser_panel_identity_stable(struct kunit *test) +{ + struct apple_dcp *dcp = kunit_kzalloc(test, sizeof(*dcp), GFP_KERNEL); + struct dcp_desktop_state *desktop = kunit_kzalloc(test, sizeof(*desktop), GFP_KERNEL); + struct dcp_display_metadata metadata = { + .manufacturer = "TST", .name = "Synthetic", .product = 42, .serial = 1, + }; + u64 generation; + + KUNIT_ASSERT_NOT_NULL(test, dcp); + KUNIT_ASSERT_NOT_NULL(test, desktop); + dcp->desktop = desktop; + dcp->fw_compat = DCP_FIRMWARE_V_26_6_DESKTOP; + dcp->panel.width_mm = 345; + spin_lock_init(&dcp->metadata.lock); + dcp_panel_metadata_replace(dcp, &metadata); + generation = desktop->panel_metadata_generation; + KUNIT_EXPECT_TRUE(test, dcp_panel_metadata_current(dcp, generation)); + dcp_panel_metadata_replace(dcp, NULL); + dcp_panel_metadata_replace(dcp, &metadata); + { + struct dcp_display_metadata partial = { .manufacturer = "TST", .product = 42 }; + + dcp_panel_metadata_replace(dcp, &partial); + } + KUNIT_EXPECT_TRUE(test, dcp_panel_metadata_current(dcp, generation)); + KUNIT_EXPECT_EQ(test, desktop->panel_metadata.serial, 1U); + metadata.serial++; + dcp_panel_metadata_replace(dcp, &metadata); + KUNIT_EXPECT_FALSE(test, dcp_panel_metadata_current(dcp, generation)); + KUNIT_EXPECT_EQ(test, desktop->panel_metadata.serial, 2U); +} + +static void parser_identity_emit(void *data, const char *key, u32 len, + u32 type, u32 size, u32 depth) +{ + u32 *seen = data; + + (*seen)++; +} + +static void parser_identity_bounds(struct kunit *test) +{ + u32 seen = 0; + __le32 huge[] = { cpu_to_le32(0xd3), cpu_to_le32(0x01ffffff) }; + int ret; + + ret = parse_identity_keys(huge, sizeof(huge), parser_identity_emit, &seen); + KUNIT_EXPECT_EQ(test, ret, -E2BIG); + KUNIT_EXPECT_EQ(test, seen, 1U); + for (u32 len = 0; len < sizeof(huge); len++) { + ret = parse_identity_keys(huge, len, parser_identity_emit, &seen); + KUNIT_EXPECT_LT(test, ret, 0); + } +} + +static void parser_adt_bounds(struct kunit *test) +{ + struct dcp_panel_adt panel = { .identity = { .manufacturer = "APP" } }; + struct dcp_panel_adt before = panel; + __le32 huge[] = { cpu_to_le32(4097), 0 }; + + KUNIT_EXPECT_EQ(test, dcp_panel_adt_parse(huge, sizeof(huge), &panel), -E2BIG); + KUNIT_EXPECT_MEMEQ(test, &panel, &before, sizeof(panel)); + KUNIT_EXPECT_EQ(test, dcp_panel_adt_parse(NULL, 0, &panel), -EINVAL); +} + +static void parser_panel_of_dimensions(struct kunit *test) +{ + __be32 width = cpu_to_be32(345), height = cpu_to_be32(223); + __be32 adjusted = cpu_to_be32(216); + struct property adj = { .name = "adj-height-mm", .length = 4, .value = &adjusted }; + struct property h = { .name = "height-mm", .length = 4, .value = &height }; + struct property w = { .name = "width-mm", .length = 4, .value = &width, .next = &h }; + struct device_node node = { .properties = &w }; + struct apple_dcp *dcp = kunit_kzalloc(test, sizeof(*dcp), GFP_KERNEL); + + KUNIT_ASSERT_NOT_NULL(test, dcp); + dcp->notch_height = 74; + /* A missing optional adj-height-mm must not erase the full height. */ + dcp_panel_read_dimensions(dcp, &node); + KUNIT_EXPECT_EQ(test, dcp->panel.width_mm, 345U); + KUNIT_EXPECT_EQ(test, dcp->panel.height_mm, 223U); + h.next = &adj; + dcp_panel_read_dimensions(dcp, &node); + KUNIT_EXPECT_EQ(test, dcp->panel.height_mm, 216U); + dcp->notch_height = 0; + dcp_panel_read_dimensions(dcp, &node); + KUNIT_EXPECT_EQ(test, dcp->panel.height_mm, 223U); +} + +static void parser_panel_j616s_publication(struct kunit *test) +{ + struct apple_dcp *dcp = kunit_kzalloc(test, sizeof(*dcp), GFP_KERNEL); + struct dcp_desktop_state *desktop = kunit_kzalloc(test, sizeof(*desktop), GFP_KERNEL); + struct platform_device *pipeline = kunit_kzalloc(test, sizeof(*pipeline), GFP_KERNEL); + struct apple_connector *connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + struct dcp_panel_adt panel = { + .identity = { .manufacturer = "APP", .product = 0x616, .name = "Apple Panel" }, + .width_mm = 345, .height_mm = 216, + }; + struct dcp_panel_adt before = panel; + struct device *dev = drm_kunit_helper_alloc_device(test); + struct drm_device *drm; + struct drm_display_mode *published; + struct dcp_display_mode mode = panel_test_mode; + const struct edid *raw; + int count, ret; + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + drm = __drm_kunit_helper_alloc_drm_device(test, dev, sizeof(*drm), 0, + DRIVER_MODESET | DRIVER_ATOMIC); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); + KUNIT_ASSERT_NOT_NULL(test, dcp); + KUNIT_ASSERT_NOT_NULL(test, desktop); + KUNIT_ASSERT_NOT_NULL(test, pipeline); + KUNIT_ASSERT_NOT_NULL(test, connector); + ret = drmm_connector_init(drm, &connector->base, &parser_route_connector_funcs, + DRM_MODE_CONNECTOR_eDP, NULL); + KUNIT_ASSERT_EQ(test, ret, 0); + ret = dcp_panel_adt_parse(panel_test_adt, sizeof(panel_test_adt), &panel); + KUNIT_ASSERT_EQ(test, ret, 0); + /* /product's machine ID is not a panel product; neither panel node exists. */ + KUNIT_EXPECT_FALSE(test, panel.found); + KUNIT_EXPECT_MEMEQ(test, &panel, &before, sizeof(panel)); + parser_modes_init(dcp); + mutex_init(&dcp->dcpavserv.lock); + mutex_init(&dcp->hpd_mutex); + dcp->dev = dev; + dcp->desktop = desktop; + dcp->fw_compat = DCP_FIRMWARE_V_26_6_DESKTOP; + dcp->panel.width_mm = panel.width_mm; + dcp->panel.height_mm = panel.height_mm; + desktop->panel_adt_identity = panel.identity; + dcp->connector = connector; + dcp->modes = &mode; + dcp->nr_modes = 1; + dcp->modes_admitted = true; + connector->dcp = pipeline; + connector->edid_live = true; + platform_set_drvdata(pipeline, dcp); + + /* Probe through the real iomfb publication path and DRM blob allocator. */ + mutex_lock(&drm->mode_config.mutex); + count = dcp_get_modes(&connector->base); + mutex_unlock(&drm->mode_config.mutex); + KUNIT_EXPECT_EQ(test, count, 1); + KUNIT_ASSERT_NOT_NULL(test, connector->base.edid_blob_ptr); + KUNIT_EXPECT_EQ(test, connector->base.edid_blob_ptr->length, (size_t)128); + KUNIT_EXPECT_TRUE(test, connector->drm_edid_firmware_derived); + raw = connector->base.edid_blob_ptr->data; + KUNIT_EXPECT_MEMEQ(test, raw->detailed_timings[1].data.other_data.data.str.str, + "Apple Panel", 11); + KUNIT_EXPECT_EQ(test, connector->base.display_info.width_mm, 345U); + KUNIT_EXPECT_EQ(test, connector->base.display_info.height_mm, 216U); + KUNIT_EXPECT_MEMEQ(test, &mode, &panel_test_mode, sizeof(mode)); + KUNIT_ASSERT_FALSE(test, list_empty(&connector->base.probed_modes)); + published = list_first_entry(&connector->base.probed_modes, struct drm_display_mode, head); + KUNIT_EXPECT_EQ(test, published->clock, 521065); + KUNIT_EXPECT_EQ(test, published->vsync_start, 2278); + KUNIT_EXPECT_EQ(test, published->vsync_end, 2360); + list_del(&published->head); + drm_mode_destroy(drm, published); + /* The stack catalogue is not owned by dcp_modes_release(). */ + dcp->modes = NULL; + dcp->nr_modes = 0; + dcp_modes_release(dcp); +} + +static void blob_timing_dimension(struct parser_blob *blob, const char *key, + s64 active, s64 total, s64 porch, s64 sync, s64 rate) +{ + blob_key(blob, key); + blob_tag(blob, 1, 7); + blob_int(blob, "Active", active); + blob_int(blob, "Total", total); + blob_int(blob, "FrontPorch", porch); + blob_int(blob, "SyncWidth", sync); + blob_int(blob, "PreciseSyncRate", rate); + blob_int(blob, "SyncPolarity", 1); + blob_int(blob, "PixelRepetition", 0); +} + +/* A desktop 26.x sink timing: the keys the firmware-derived EDID needs. */ +static struct parser_blob *make_timing_blob(struct kunit *test) +{ + struct parser_blob *blob = kunit_kzalloc(test, sizeof(*blob), GFP_KERNEL); + + if (!blob) + return NULL; + put_unaligned_le32(0xd3, blob->data); + blob->size = 4; + blob_tag(blob, 2, 1); + blob_tag(blob, 1, 8); + blob_bool(blob, "IsVirtual", false); + blob_int(blob, "ID", 7); + blob_int(blob, "Score", 10); + blob_timing_dimension(blob, "HorizontalAttributes", 1920, 2200, 88, 44, + (675U << 16) / 10); + blob_timing_dimension(blob, "VerticalAttributes", 1080, 1125, 4, 5, 60 << 16); + blob_key(blob, "ColorModes"); + blob_tag(blob, 2, 1); + blob_color(blob, 100, 10, 0, DCP_COLOR_FORMAT_RGB); + blob_bool(blob, "IsPreferred", true); + blob_bool(blob, "IsInterlaced", false); + return blob; +} + +static void parser_desktop_metadata_timing(struct kunit *test) +{ + const struct dcp_display_metadata metadata = { + .width_cm = 60, .height_cm = 33, + .chromaticity = { { 44608, 20288 }, { 16960, 40448 }, { 9344, 5184 } }, + .white = { 20544, 21568 }, .gamma = 144179, + .product = 2895, .serial = 305419896, .week = 7, .year = 2013, + .manufacturer = "ACR", .name = "QG270 S3", .serial_text = "ANON000000001", + }; + struct parser_blob *blob = make_timing_blob(test); + struct drm_display_mode preferred; + struct dcp_display_mode *modes; + const struct drm_edid *edid; + const struct edid *raw; + struct dcp_parse_ctx ctx; + unsigned int count; + + KUNIT_ASSERT_NOT_NULL(test, blob); + + /* Other DCPs keep their modes and give no metadata timing. */ + KUNIT_ASSERT_EQ(test, parse(blob->data, blob->size, &ctx), 0); + ctx.dcp = &parser_dcp; + modes = enumerate_modes(&ctx, &count, 600, 330, 0, false, false); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, modes); + KUNIT_ASSERT_EQ(test, count, 1U); + KUNIT_EXPECT_TRUE(test, modes[0].preferred); + KUNIT_EXPECT_FALSE(test, modes[0].metadata_timing_valid); + KUNIT_EXPECT_EQ(test, modes[0].metadata_sync_flags, 0U); + KUNIT_EXPECT_EQ(test, modes[0].mode.flags, 0U); + kfree(modes); + + KUNIT_ASSERT_EQ(test, parse(blob->data, blob->size, &ctx), 0); + ctx.dcp = &parser_dcp; + ctx.metadata_timing = true; + modes = enumerate_modes(&ctx, &count, 600, 330, 0, false, false); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, modes); + KUNIT_ASSERT_EQ(test, count, 1U); + KUNIT_EXPECT_TRUE(test, modes[0].preferred); + KUNIT_EXPECT_TRUE(test, modes[0].mode.type & DRM_MODE_TYPE_PREFERRED); + KUNIT_EXPECT_TRUE(test, modes[0].metadata_timing_valid); + KUNIT_EXPECT_EQ(test, modes[0].metadata_sync_flags, + (u32)(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)); + /* The published mode is unchanged: only the EDID timing takes the polarity. */ + KUNIT_EXPECT_EQ(test, modes[0].mode.flags, 0U); + KUNIT_EXPECT_EQ(test, modes[0].mode.clock, 148500); + + preferred = modes[0].mode; + preferred.flags |= modes[0].metadata_sync_flags; + kfree(modes); + edid = dcp_metadata_edid(&metadata, &preferred); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, edid); + raw = drm_edid_raw(edid); + KUNIT_EXPECT_EQ(test, be16_to_cpu(raw->product_id.manufacturer_name), + (u16)(('A' - '@') << 10 | ('C' - '@') << 5 | ('R' - '@'))); + KUNIT_EXPECT_EQ(test, le16_to_cpu(raw->product_id.product_code), 2895); + KUNIT_EXPECT_EQ(test, raw->detailed_timings[1].data.other_data.type, + EDID_DETAIL_MONITOR_NAME); + KUNIT_EXPECT_MEMEQ(test, raw->detailed_timings[1].data.other_data.data.str.str, + "QG270 S3\n", 9); + KUNIT_EXPECT_EQ(test, le16_to_cpu(raw->detailed_timings[0].pixel_clock), 14850); + drm_edid_free(edid); +} + static struct kunit_case parser_cases[] = { + KUNIT_CASE(parser_panel_of_dimensions), + KUNIT_CASE(parser_panel_j616s_publication), + KUNIT_CASE(parser_adt_bounds), + KUNIT_CASE(parser_identity_bounds), + KUNIT_CASE(parser_panel_identity_stable), + KUNIT_CASE(parser_panel_identity), KUNIT_CASE(parser_score_presence), KUNIT_CASE(parser_legacy_ties), KUNIT_CASE(parser_color_presence), @@ -1252,6 +1649,7 @@ static struct kunit_case parser_cases[] = { KUNIT_CASE(parser_horizontal_rate), KUNIT_CASE(parser_bad_score), KUNIT_CASE(parser_notch_vrr), + KUNIT_CASE(parser_vrr_empty_range), KUNIT_CASE(parser_replacement), KUNIT_CASE(parser_held_keeps_edid), KUNIT_CASE(parser_attachment_admission), @@ -1271,6 +1669,7 @@ static struct kunit_case parser_cases[] = { KUNIT_CASE(parser_dimensions_lifetime), KUNIT_CASE(parser_catalog_release), KUNIT_CASE(parser_property_bounds), + KUNIT_CASE(parser_desktop_metadata_timing), {} }; diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index cecad5a1fb866..e5a8c8e6cc700 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -14,6 +14,7 @@ #endif #include "parser.h" +#include "dcp-refresh.h" #include "trace.h" #define DCP_PARSE_HEADER 0xd3 @@ -723,7 +724,8 @@ static int parse_mode(struct dcp_parse_ctx *handle, struct timing_color_restrictions limits = {}; struct dcp_color_mode unsafe_rgb8 = { .score = -1 }; bool have_horiz = false, have_vert = false, have_virtual = false; - bool duplicate_scope = false; + bool duplicate_scope = false, vrr_ambiguous = false, vrr_fallback; + unsigned int vrr_seen = 0; s64 min_vrr = 0, max_vrr = 0; u64 clock; s64 id = -1; @@ -732,6 +734,7 @@ static int parse_mode(struct dcp_parse_ctx *handle, bool preferred = false, interlaced = false; bool have_preferred = false, have_interlaced = false; bool is_preferred = false; + bool timing_keys = external_sdr || handle->metadata_timing; struct drm_display_mode *mode = &out->mode; *out = (struct dcp_display_mode) { @@ -773,16 +776,20 @@ static int parse_mode(struct dcp_parse_ctx *handle, else if (!strcmp(key, "HorizontalAttributes")) { duplicate_scope |= have_horiz; have_horiz = true; - ret = parse_dimension(it.handle, &horiz, external_sdr); + ret = parse_dimension(it.handle, &horiz, timing_keys || internal); } else if (!strcmp(key, "VerticalAttributes")) { duplicate_scope |= have_vert; have_vert = true; - ret = parse_dimension(it.handle, &vert, external_sdr); - } else if (!strcmp(key, "MinimumVariableRefreshRate")) + ret = parse_dimension(it.handle, &vert, timing_keys || internal); + } else if (!strcmp(key, "MinimumVariableRefreshRate")) { + vrr_ambiguous |= !!(vrr_seen & 1); + vrr_seen |= 1; ret = parse_int_bound(it.handle, &min_vrr, 0, U32_MAX); - else if (!strcmp(key, "MaximumVariableRefreshRate")) + } else if (!strcmp(key, "MaximumVariableRefreshRate")) { + vrr_ambiguous |= !!(vrr_seen & 2); + vrr_seen |= 2; ret = parse_int_bound(it.handle, &max_vrr, 0, U32_MAX); - else if (!strcmp(key, "ColorModes")) + } else if (!strcmp(key, "ColorModes")) ret = parse_color_modes(it.handle, out, external_sdr, &limits, handle->automatic_1080p180_rgb8 ? &unsafe_rgb8 : NULL); @@ -792,13 +799,16 @@ static int parse_mode(struct dcp_parse_ctx *handle, duplicate_scope |= have_virtual; have_virtual = true; ret = parse_bool(it.handle, &is_virtual); - } else if (external_sdr && !strcmp(key, "IsPreferred")) { + } else if (timing_keys && !strcmp(key, "IsPreferred")) { if (have_preferred) ret = -EINVAL; else ret = parse_bool(it.handle, &preferred); have_preferred = true; - } else if (external_sdr && !strcmp(key, "IsInterlaced")) { + /* Outside the external SDR path it also ranks the timing. */ + if (!external_sdr) + is_preferred = preferred; + } else if (timing_keys && !strcmp(key, "IsInterlaced")) { if (have_interlaced) ret = -EINVAL; else @@ -870,26 +880,16 @@ static int parse_mode(struct dcp_parse_ctx *handle, return -EINVAL; /* - * An internal ProMotion panel carries no EDID or DisplayID, so DCP - * reports no adaptive-sync range for it. Assume the ProMotion floor of - * 24 Hz up to whatever rate the mode itself advertises. + * External H17P stays fixed. Desktop zero placeholders are also an + * absent range; a partial or duplicate pair must not enable fallback. */ - if (internal && vert.precise_sync_rate >> 16 == 120) { - out->min_vrr = 24 << 16; - out->max_vrr = vert.precise_sync_rate; - out->vrr = true; - } - - /* - * Refresh rates are reported by DCP as 16.16 fixed-point Hz. - * External H17P skips adaptive-sync programming: keep the fixed timing - * available, but do not advertise its sink VRR range as driver support. - */ - if (!external_sdr && min_vrr && max_vrr > min_vrr) { - out->min_vrr = min_vrr; - out->max_vrr = max_vrr; - out->vrr = true; - } + vrr_fallback = !vrr_seen || (vrr_seen == 3 && handle->zero_vrr_range_fallback); + if (!external_sdr && !vrr_ambiguous) + out->vrr = dcp_refresh_range(vert.precise_sync_rate, min_vrr, + max_vrr, internal && vrr_fallback, + &out->min_vrr, &out->max_vrr); + out->needs_timestamps = out->vrr || + (internal && dcp_refresh_is_promotion(vert.precise_sync_rate)); clock = calculate_clock(&horiz, &vert); if (!clock || clock > INT_MAX) @@ -918,10 +918,16 @@ static int parse_mode(struct dcp_parse_ctx *handle, .height_mm = height_mm, }; - if (external_sdr && horiz.sync_polarity >= 0 && vert.sync_polarity >= 0) - mode->flags = (horiz.sync_polarity ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC) | - (vert.sync_polarity ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC); - out->metadata_timing_valid = external_sdr && !out->automatic_rgb8_quirk && + /* The internal panel also publishes its polarity with its modes (J616s series). */ + if ((timing_keys || internal) && horiz.sync_polarity >= 0 && + vert.sync_polarity >= 0) + out->metadata_sync_flags = + (horiz.sync_polarity ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC) | + (vert.sync_polarity ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC); + /* The external SDR path and the internal panel publish it with their modes. */ + if (external_sdr || internal) + mode->flags = out->metadata_sync_flags; + out->metadata_timing_valid = timing_keys && !out->automatic_rgb8_quirk && have_preferred && preferred && have_interlaced && !interlaced && horiz.pixel_repetition == 0 && vert.pixel_repetition == 0 && horiz.sync_polarity >= 0 && vert.sync_polarity >= 0; @@ -1322,7 +1328,38 @@ static int parse_metadata_chromaticity(struct dcp_parse_ctx *handle, return seen == 7 ? 0 : -EINVAL; } -static int parse_metadata_product(struct dcp_parse_ctx *handle, struct dcp_display_metadata *out) +/* Internal panel labels can be longer than an EDID descriptor. */ +static int parse_panel_label(struct dcp_parse_ctx *handle, char out[14]) +{ + const struct dcp_parse_tag *tag = parse_tag_of_type(handle, DCP_TYPE_STRING); + const u8 *value; + unsigned int length; + + if (IS_ERR(tag)) + return PTR_ERR(tag); + if (tag->size > 255) + return -EINVAL; + value = parse_bytes(handle, tag->size); + if (IS_ERR(value)) + return PTR_ERR(value); + for (unsigned int i = 0; i < tag->size; i++) + if (value[i] < 0x20 || value[i] > 0x7e) + return -EINVAL; + length = tag->size; + if (length > 13) + length = 13; + memcpy(out, value, length); + out[length] = 0; + /* A blank descriptor is absent to libdisplay-info; use the fallback. */ + for (unsigned int i = 0; i < length; i++) + if (out[i] != ' ') + return 0; + out[0] = 0; + return 0; +} + +static int parse_metadata_product(struct dcp_parse_ctx *handle, + struct dcp_display_metadata *out, bool panel) { struct iterator it; unsigned int seen = 0; @@ -1347,7 +1384,8 @@ static int parse_metadata_product(struct dcp_parse_ctx *handle, struct dcp_displ if (seen & 2) return -EINVAL; seen |= 2; - ret = parse_metadata_string(handle, out->name, sizeof(out->name)); + ret = panel ? parse_panel_label(handle, out->name) : + parse_metadata_string(handle, out->name, sizeof(out->name)); } else if (!strcmp(key, "ProductID")) { if (seen & 4) return -EINVAL; @@ -1357,12 +1395,14 @@ static int parse_metadata_product(struct dcp_parse_ctx *handle, struct dcp_displ if (seen & 8) return -EINVAL; seen |= 8; - ret = parse_metadata_number(handle, &out->week, 0, 53); + ret = panel ? skip(handle) : + parse_metadata_number(handle, &out->week, 0, 53); } else if (!strcmp(key, "YearOfManufacture")) { if (seen & 16) return -EINVAL; seen |= 16; - ret = parse_metadata_number(handle, &out->year, 1990, 2245); + ret = panel ? skip(handle) : + parse_metadata_number(handle, &out->year, 1990, 2245); } else if (!strcmp(key, "SerialNumber")) { if (seen & 64) return -EINVAL; @@ -1372,8 +1412,9 @@ static int parse_metadata_product(struct dcp_parse_ctx *handle, struct dcp_displ if (seen & 128) return -EINVAL; seen |= 128; - ret = parse_metadata_string(handle, out->serial_text, - sizeof(out->serial_text)); + ret = panel ? parse_panel_label(handle, out->serial_text) : + parse_metadata_string(handle, out->serial_text, + sizeof(out->serial_text)); } else if (!strcmp(key, "LegacyManufacturerID")) { if (seen & 32) return -EINVAL; @@ -1385,7 +1426,13 @@ static int parse_metadata_product(struct dcp_parse_ctx *handle, struct dcp_displ if (ret) return ret; } - if ((seen & 31) != 31 || strlen(out->manufacturer) != 3) + if (panel && !(seen & 1) && (seen & 32)) { + for (unsigned int i = 0; i < 3; i++) + out->manufacturer[i] = '@' + ((legacy >> (10 - 5 * i)) & 31); + seen |= 1; + } + if ((seen & (panel ? 5 : 31)) != (panel ? 5 : 31) || + strlen(out->manufacturer) != 3) return -EINVAL; for (unsigned int i = 0; i < 3; i++) if (out->manufacturer[i] < 'A' || out->manufacturer[i] > 'Z') @@ -1436,7 +1483,7 @@ int parse_display_metadata(struct dcp_parse_ctx *handle, struct dcp_display_meta if (seen & 16) return -EINVAL; seen |= 16; - ret = parse_metadata_product(handle, &parsed); + ret = parse_metadata_product(handle, &parsed, false); } else { ret = skip(handle); } @@ -1449,6 +1496,50 @@ int parse_display_metadata(struct dcp_parse_ctx *handle, struct dcp_display_meta return 0; } +/* Identity does not depend on optional colorimetry or EDID-compatible timing. */ +int parse_panel_product_metadata(struct dcp_parse_ctx *handle, + struct dcp_display_metadata *out) +{ + struct dcp_display_metadata parsed = {}; + int ret = parse_metadata_product(handle, &parsed, true); + + if (!ret) + *out = parsed; + return ret; +} + +int parse_panel_metadata(struct dcp_parse_ctx *handle, struct dcp_display_metadata *out) +{ + struct dcp_display_metadata parsed = {}; + struct iterator it; + bool product = false; + int ret = iterator_begin(handle, &it, true); + + if (ret) + return ret; + for (; it.idx < it.len; it.idx++) { + char key[64]; + + ret = parse_metadata_string(handle, key, sizeof(key)); + if (ret) + return ret; + if (!strcmp(key, "ProductAttributes")) { + if (product) + return -EINVAL; + product = true; + ret = parse_metadata_product(handle, &parsed, true); + } else { + ret = skip(handle); + } + if (ret) + return ret; + } + if (!product) + return -ENODATA; + *out = parsed; + return 0; +} + int parse_epic_service_init(struct dcp_parse_ctx *handle, const char **name, const char **class, s64 *unit, bool h17p_keys) { @@ -1926,3 +2017,145 @@ int parse_system_log_mnits(struct dcp_parse_ctx *handle, struct dcp_system_ev_mn return 0; } + +/* Inspect structure only. Never emit a firmware value or allocate from its size. */ +static int parse_identity_tree(struct dcp_parse_ctx *ctx, dcp_identity_key_fn emit, + void *data, u32 depth, u32 *budget) +{ + const struct dcp_parse_tag *tag; + u32 count; + + if (!*budget || depth > 4) + return -E2BIG; + --*budget; + tag = parse_tag(ctx); + if (IS_ERR(tag)) + return PTR_ERR(tag); + if (tag->type != DCP_TYPE_DICTIONARY && tag->type != DCP_TYPE_ARRAY) { + /* Reuse the bounded scalar decoder from the original position. */ + ctx->pos -= sizeof(*tag); + return skip(ctx); + } + count = tag->size; + if (count > *budget) + return -E2BIG; + for (u32 i = 0; i < count; i++) { + struct dcp_parse_ctx value; + const struct dcp_parse_tag *key_tag, *value_tag; + const char *key = "[]"; + u32 length = 2; + int ret; + + if (tag->type == DCP_TYPE_DICTIONARY) { + key_tag = parse_tag_of_type(ctx, DCP_TYPE_STRING); + if (IS_ERR(key_tag)) + return PTR_ERR(key_tag); + length = key_tag->size; + key = parse_bytes(ctx, length); + if (IS_ERR(key)) + return PTR_ERR(key); + } + value = *ctx; + value_tag = parse_tag(&value); + if (IS_ERR(value_tag)) + return PTR_ERR(value_tag); + emit(data, key, length, value_tag->type, value_tag->size, depth); + ret = parse_identity_tree(ctx, emit, data, depth + 1, budget); + if (ret) + return ret; + } + return 0; +} + +int parse_identity_keys(const void *blob, size_t len, dcp_identity_key_fn emit, void *data) +{ + struct dcp_parse_ctx ctx, root; + const struct dcp_parse_tag *tag; + u32 budget = 64; + int ret = parse(blob, len, &ctx); + + if (ret) + return ret; + root = ctx; + tag = parse_tag(&root); + if (IS_ERR(tag)) + return PTR_ERR(tag); + emit(data, "", 6, tag->type, tag->size, 0); + return parse_identity_tree(&ctx, emit, data, 0, &budget); +} + +/* Look for named EDID blobs inside bounded property dictionaries. */ +static int parse_panel_edid_value(struct dcp_parse_ctx *ctx, u32 depth, u32 *budget, + bool edid_key, const u8 **edid, size_t *size) +{ + const struct dcp_parse_tag *tag; + struct dcp_parse_ctx start = *ctx; + + if (depth > 4 || !*budget) + return -E2BIG; + --*budget; + tag = parse_tag(ctx); + if (IS_ERR(tag)) + return PTR_ERR(tag); + if (tag->type == DCP_TYPE_BLOB && edid_key) { + const u8 *data; + + if (tag->size < 128 || tag->size > 4096 || tag->size % 128) + return -EINVAL; + data = parse_bytes(ctx, tag->size); + if (IS_ERR(data)) + return PTR_ERR(data); + if (memcmp(data, "\x00\xff\xff\xff\xff\xff\xff\x00", 8) || + (1U + data[126]) * 128 != tag->size) + return -EINVAL; + *edid = data; + *size = tag->size; + return 0; + } + if (tag->type == DCP_TYPE_DICTIONARY) { + if (tag->size > *budget) + return -E2BIG; + for (u32 i = 0; i < tag->size; i++) { + const struct dcp_parse_tag *key_tag; + const char *key; + bool match; + int ret; + + key_tag = parse_tag_of_type(ctx, DCP_TYPE_STRING); + if (IS_ERR(key_tag)) + return PTR_ERR(key_tag); + key = parse_bytes(ctx, key_tag->size); + if (IS_ERR(key)) + return PTR_ERR(key); + match = (key_tag->size == 4 && (!memcmp(key, "EDID", 4) || + !memcmp(key, "edid", 4))) || + (key_tag->size == 13 && !memcmp(key, "IODisplayEDID", 13)); + ret = parse_panel_edid_value(ctx, depth + 1, budget, match, edid, size); + if (ret != -ENOENT) + return ret; + } + return -ENOENT; + } + *ctx = start; + return skip(ctx) ?: -ENOENT; +} + +/* A setter may deliver raw bytes, a blob, or a dictionary containing a blob. */ +int parse_panel_edid_data(const void *blob, size_t len, const u8 **edid, size_t *size) +{ + struct dcp_parse_ctx ctx; + const u8 *data = blob; + u32 bytes, budget = 64; + int ret; + + if (len >= 128 && !memcmp(data, "\x00\xff\xff\xff\xff\xff\xff\x00", 8)) { + bytes = (1U + data[126]) * 128; + if (bytes > len || bytes > 4096) + return -EINVAL; + *edid = data; + *size = bytes; + return 0; + } + ret = parse(blob, len, &ctx); + return ret ?: parse_panel_edid_value(&ctx, 0, &budget, true, edid, size); +} diff --git a/drivers/gpu/drm/apple/parser.h b/drivers/gpu/drm/apple/parser.h index 97a75a8b046a0..5e573371e0072 100644 --- a/drivers/gpu/drm/apple/parser.h +++ b/drivers/gpu/drm/apple/parser.h @@ -11,6 +11,10 @@ #include "dcp-metadata-epoch.h" struct apple_dcp; +int parse_panel_edid_data(const void *blob, size_t len, const u8 **edid, size_t *size); +typedef void (*dcp_identity_key_fn)(void *data, const char *key, u32 key_len, + u32 type, u32 size, u32 depth); +int parse_identity_keys(const void *blob, size_t len, dcp_identity_key_fn emit, void *data); struct apple_connector; struct apple_dcp_typec_route; @@ -21,6 +25,14 @@ struct dcp_parse_ctx { /* External H17P candidates require separate publication/lookup proof. */ bool automatic_1080p180_rgb8; bool ambiguous_180hz; + /* Desktop 26.6 uses zero placeholders for an unreported VRR range. */ + bool zero_vrr_range_fallback; + /* + * A desktop 26.x external processor: read the sink timing keys that + * describe the preferred timing for the firmware-derived EDID, without + * the H17P external color policy. + */ + bool metadata_timing; #if IS_ENABLED(CONFIG_DRM_APPLE_DCP_T6030_AUDIO) /* * T6030 external processor audio elements (DCP firmware 14.7, external @@ -120,10 +132,14 @@ struct dcp_display_mode { struct dcp_color_mode sdr; struct dcp_color_mode best; bool vrr; + /* ProMotion fixed timings still need presentation deadlines. */ + bool needs_timestamps; /* IsPreferred, outside the external SDR path */ bool preferred; bool metadata_timing_valid; bool automatic_rgb8_quirk; + /* The sync polarity flags of the timing, if it reports them */ + u32 metadata_sync_flags; /* * The best virtual color element of the timing, which the desktop * 26.x firmware selects first to bootstrap its generic pipe (valid if @@ -192,6 +208,9 @@ int replace_modes(struct dcp_parse_ctx *handle, struct dcp_display_mode **modes, unsigned int *count, int width_mm, int height_mm, unsigned int notch_height, bool internal); +int parse_panel_metadata(struct dcp_parse_ctx *handle, struct dcp_display_metadata *out); +int parse_panel_product_metadata(struct dcp_parse_ctx *handle, + struct dcp_display_metadata *out); int parse_display_attributes(struct dcp_parse_ctx *handle, int *width_mm, int *height_mm, bool *backlight_control); int parse_epic_service_init(struct dcp_parse_ctx *handle, const char **name, diff --git a/drivers/gpu/drm/apple/parser_mode_test.c b/drivers/gpu/drm/apple/parser_mode_test.c index 40d0c8d9cd196..877d012740df1 100644 --- a/drivers/gpu/drm/apple/parser_mode_test.c +++ b/drivers/gpu/drm/apple/parser_mode_test.c @@ -7,6 +7,7 @@ #include #include "parser.h" +#include "dcp-refresh.h" struct dcp_mode_rank_case { const char *desc; @@ -40,7 +41,34 @@ static void dcp_mode_rank_test(struct kunit *test) c->above); } +static void dcp_refresh_range_test(struct kunit *test) +{ + u32 low = 0, high = 0; + + KUNIT_EXPECT_TRUE(test, dcp_refresh_range(120U << 16, 0, 0, + true, &low, &high)); + KUNIT_EXPECT_EQ(test, low, 24U << 16); + KUNIT_EXPECT_EQ(test, high, 120U << 16); + KUNIT_EXPECT_FALSE(test, dcp_refresh_range(60U << 16, 0, 0, + true, &low, &high)); + KUNIT_EXPECT_FALSE(test, dcp_refresh_range(120U << 16, 0, 0, + false, &low, &high)); + KUNIT_EXPECT_FALSE(test, dcp_refresh_range(120U << 16, 0, 120U << 16, + true, &low, &high)); + KUNIT_EXPECT_FALSE(test, dcp_refresh_range(120U << 16, 24U << 16, + 144U << 16, true, &low, &high)); + KUNIT_EXPECT_FALSE(test, dcp_refresh_range(120U << 16, 120U << 16, + 24U << 16, true, &low, &high)); + KUNIT_EXPECT_TRUE(test, dcp_refresh_range(120U << 16, 48U << 16, + 120U << 16, true, &low, &high)); + KUNIT_EXPECT_EQ(test, low, 48U << 16); + KUNIT_EXPECT_EQ(test, dcp_desktop_present_deadline(true, true), 120ULL); + KUNIT_EXPECT_EQ(test, dcp_desktop_present_deadline(false, true), 0ULL); + KUNIT_EXPECT_EQ(test, dcp_desktop_present_deadline(true, false), 0ULL); +} + static struct kunit_case dcp_mode_rank_test_cases[] = { + KUNIT_CASE(dcp_refresh_range_test), KUNIT_CASE_PARAM(dcp_mode_rank_test, dcp_mode_rank_gen_params), {} }; diff --git a/drivers/gpu/drm/apple/systemep.c b/drivers/gpu/drm/apple/systemep.c index a32f0a0bcf6a0..44e239c6016b0 100644 --- a/drivers/gpu/drm/apple/systemep.c +++ b/drivers/gpu/drm/apple/systemep.c @@ -75,7 +75,7 @@ static int powerlog_report(struct apple_epic_service *service, enum epic_subtype struct apple_dcp *dcp = service->ep->dcp; int ret; - /* Later reports must not overwrite a latched hint or Linux's target. */ + /* Keep the existing filtering on active non-desktop backends. */ if (dcp_backlight_active(dcp)) return 0; @@ -100,8 +100,9 @@ static int powerlog_report(struct apple_epic_service *service, enum epic_subtype dev_dbg(dcp->dev, "systemep: mNits event: Nits: %u.%03u, iDAC: %u\n", mnits.millinits / 1000, mnits.millinits % 1000, mnits.idac); - if (dcp->fw_compat == DCP_FIRMWARE_H17P && - dcp->hw.iomfb_method_profile != DCP_IOMFB_METHODS_H17G) { + if ((dcp->fw_compat == DCP_FIRMWARE_H17P && + dcp->hw.iomfb_method_profile != DCP_IOMFB_METHODS_H17G) || + dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP) { if (!dcp_has_panel(dcp)) return 0; ret = dcp_backlight_takeover(dcp, mnits.millinits); diff --git a/drivers/gpu/drm/asahi/Makefile b/drivers/gpu/drm/asahi/Makefile index db46090abd943..fd8421f27590d 100644 --- a/drivers/gpu/drm/asahi/Makefile +++ b/drivers/gpu/drm/asahi/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_DRM_ASAHI) += asahi.o -asahi-y := asahi_core.o fwid.o sysfs.o g17/sync_input.o g17/sync_output.o g17/sync_scope.o g17/trace.o +asahi-y := asahi_core.o fwid.o sysfs.o g17/sync_input.o g17/sync_output.o g17/sync_scope.o g17/trace.o g17/clpc.o CFLAGS_g17/trace.o := -I$(src)/g17 asahi-$(CONFIG_DRM_ASAHI_G15) += g15/glue.o diff --git a/drivers/gpu/drm/asahi/float.rs b/drivers/gpu/drm/asahi/float.rs index 46c05699d7722..224e92c6d97e2 100644 --- a/drivers/gpu/drm/asahi/float.rs +++ b/drivers/gpu/drm/asahi/float.rs @@ -44,6 +44,11 @@ struct F32U { } impl F32 { + /// Return the IEEE-754 binary32 representation. + pub(crate) const fn to_bits(self) -> u32 { + self.0 + } + /// Convert a raw 32-bit representation into an F32 pub(crate) const fn from_bits(u: u32) -> F32 { F32(u) diff --git a/drivers/gpu/drm/asahi/g17/buffer.rs b/drivers/gpu/drm/asahi/g17/buffer.rs index 8f3a03fbb8dba..fd6460db2d1fb 100644 --- a/drivers/gpu/drm/asahi/g17/buffer.rs +++ b/drivers/gpu/drm/asahi/g17/buffer.rs @@ -677,6 +677,22 @@ impl Manager { pub(crate) fn blocks(&self) -> usize { self.tvb.blocks() } + /// Read-only diagnostic while firmware is halted. In particular this + /// does not reset the high-water counter as automatic_target() does. + pub(crate) fn report_recovery(&self) { + let read = |offset| -> Result { + Ok(self.graph.word(offset)?.load(Ordering::Relaxed)) + }; + pr_warn!("G17: recovery tvb id={} blocks={} cursors={:?} pages={:?} capacity={:?} producer={:?} consumer={:?} max_pages={:?} reset={:?} initialized={}\n", + self.buffer_id, self.blocks(), self.cursors(), + read(core::mem::offset_of!(fw::State, pages)), + read(core::mem::offset_of!(fw::State, block_capacity)), + read(core::mem::offset_of!(fw::State, producer)), + read(core::mem::offset_of!(fw::State, consumer)), + read(fw::CONTROL + core::mem::offset_of!(fw::Control, max_pages)), + read(fw::CONTROL + core::mem::offset_of!(fw::Control, reset)), + self.initialized); + } pub(crate) fn state_va(&self) -> u64 { self.graph.gpu_va() } diff --git a/drivers/gpu/drm/asahi/g17/buffer/target.rs b/drivers/gpu/drm/asahi/g17/buffer/target.rs index 64c88da8eca14..42a2c04b40112 100644 --- a/drivers/gpu/drm/asahi/g17/buffer/target.rs +++ b/drivers/gpu/drm/asahi/g17/buffer/target.rs @@ -139,6 +139,16 @@ impl Layout { }) } pub(crate) fn tpc_capacity(&self) -> Result { + // TPC_MAX bounds the tail-pointer cache itself. On 25G83 this object + // also contains GTP sub-buffers, notably 64 times the tile map. Applying + // the cache limit to their combined extent rejects valid large targets. + // Keep the original cache and tile-record limits; they also bound the + // derived auxiliary extent before any backing allocation or replacement. + if aligned(self.tpc_size, mmu::UAT_PGSZ)?.max(cfg::TPC_MIN) > cfg::TPC_MAX + || self.record_stride()? > cfg::TARGET_RECORD_MAX + { + return Err(ERANGE); + } Ok(aligned(self.tpc_end, mmu::UAT_PGSZ)?.max(cfg::TPC_MIN)) } pub(crate) fn record_stride(&self) -> Result { @@ -188,7 +198,7 @@ impl Target { pub(crate) fn new(dev: &AsahiDrmDevice, vm: &mmu::Vm, layout: &Layout) -> Result> { let tpc = layout.tpc_capacity()?; let stride = layout.record_stride()?; - if tpc > cfg::TPC_MAX || stride > cfg::TARGET_RECORD_MAX { + if stride > cfg::TARGET_RECORD_MAX { return Err(ERANGE); } let records = Object::new(dev, vm, stride * DEPTH)?; @@ -272,9 +282,6 @@ impl Target { let stride = layout.record_stride()?; let mut grew = false; if tpc > self.tpc.backing.size() { - if tpc > cfg::TPC_MAX { - return Err(ERANGE); - } Self::replace(&mut self.retired, &mut self.tpc, dev, vm, tpc, owners)?; grew = true; } @@ -350,3 +357,59 @@ impl Target { } } } + + +#[kernel::macros::kunit_tests(asahi_g17_target_capacity)] +mod tests { + use super::*; + + fn pass(size: u32, utile: u8) -> RenderPass { + // SAFETY: RenderPass and its nested surfaces/programs contain only + // integers and bools, for which the all-zero representation is valid. + let mut pass: RenderPass = unsafe { core::mem::zeroed() }; + pass.width = size; + pass.height = size; + pass.layers = 1; + pass.utile_width = utile; + pass.utile_height = utile; + pass + } + + #[test] + fn t6040_auxiliary_tiler_storage_is_not_tail_pointer_cache() { + // CTS point_size_10000 renders to 10240 squared. Its GTP sub-buffer + // is required by the existing 25G83 program, not a larger cache. + for utile in [16, 32] { + let layout = Layout::new(&pass(10240, utile), 2, RegisterPrograms::V25G83).unwrap(); + assert!(layout.tpc_size < cfg::TPC_MAX); + assert!(layout.record_stride().unwrap() < cfg::TARGET_RECORD_MAX); + assert!(layout.tpc_end > cfg::TPC_MAX); + assert!(layout.tpc_capacity().unwrap() >= layout.tpc_end); + assert_eq!(layout.gtp[0], Some(0)); + assert!(layout.gtp[1].unwrap() >= layout.tpc_size); + } + } + + #[test] + fn core_and_tile_record_limits_still_reject_oversized_targets() { + let mut layout = Layout::new(&pass(10240, 32), 2, RegisterPrograms::V25G83).unwrap(); + layout.tpc_size = cfg::TPC_MAX + 1; + assert_eq!(layout.tpc_capacity(), Err(ERANGE)); + let mut layered = pass(16384, 16); + layered.layers = 2; + let layout = Layout::new(&layered, 2, RegisterPrograms::V25G83).unwrap(); + assert!(layout.record_stride().unwrap() > cfg::TARGET_RECORD_MAX); + assert_eq!(layout.tpc_capacity(), Err(ERANGE)); + } + + #[test] + fn neo_keeps_the_same_cache_extent_and_limit() { + let mut layout = Layout::new(&pass(10240, 32), 1, RegisterPrograms::G17P).unwrap(); + assert!(layout.gtp.iter().all(Option::is_none)); + assert_eq!(layout.tpc_capacity().unwrap(), + aligned(layout.tpc_size, mmu::UAT_PGSZ).unwrap().max(cfg::TPC_MIN)); + layout.tpc_size = cfg::TPC_MAX + 1; + layout.tpc_end = layout.tpc_size; + assert_eq!(layout.tpc_capacity(), Err(ERANGE)); + } +} diff --git a/drivers/gpu/drm/asahi/g17/calibration.rs b/drivers/gpu/drm/asahi/g17/calibration.rs new file mode 100644 index 0000000000000..704a91f49b0c1 --- /dev/null +++ b/drivers/gpu/drm/asahi/g17/calibration.rs @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT + +//! Per-chip T6040 power data supplied by the boot loader. + +use super::fw::initdata::{Opp, PerfConfig, PERF_STATES}; +use crate::driver::AsahiDrmDevice; +use kernel::{c_str, of, prelude::*, str::CStr}; + +/// Validated calibration of the running chip. There is no built-in fallback. +pub(super) struct Calibration { + pub(super) perf: PerfConfig, + pub(super) leakage: [u32; 56], + pub(super) max_power_mw: u32, + core_mask: u32, + base: usize, +} + +fn words(node: &of::Node, name: &CStr) -> Result<[u32; N]> { + let values: KVec = node.get_property(name)?; + values.as_slice().try_into().map_err(|_| EINVAL) +} + +// Raw ADT OPP slots and SGX register slots number 16. The supported firmware consumes +// the 11 populated selectors; the remaining handoff entries must all be zero. +const RAW_STATES: usize = 16; + +fn active(values: [u32; RAW_STATES]) -> Result<[u32; PERF_STATES]> { + if values[PERF_STATES..].iter().any(|&v| v != 0) { + return Err(EINVAL); + } + values[..PERF_STATES].try_into().map_err(|_| EINVAL) +} + +fn map(values: [u32; RAW_STATES]) -> Result<[u8; PERF_STATES]> { + let values = active(values)?; + if values[0] != 0 + || values[PERF_STATES - 1] != RAW_STATES as u32 - 1 + || values.iter().any(|&v| v >= RAW_STATES as u32) + || values.windows(2).any(|p| p[0] >= p[1]) + { + return Err(EINVAL); + } + Ok(values.map(|v| v as u8)) +} + +fn relative(values: &[u32; PERF_STATES]) -> bool { + values[0] == 0 + && values[PERF_STATES - 1] == 100 + && values.windows(2).all(|p| p[0] <= p[1] && p[1] <= 100) +} + +impl Calibration { + pub(super) fn read(dev: &AsahiDrmDevice) -> Result { + let node = dev.as_ref().as_ref().of_node().ok_or(EIO)?; + if node.get_property::(c_str!("apple,t6040-handoff-version"))? != 2 { + return Err(EINVAL); + } + let core_mask = node.get_property(c_str!("apple,core-mask"))?; + let max_power_mw = node.get_property(c_str!("apple,max-power-mw"))?; + let base = node.get_property::(c_str!("apple,perf-base-pstate"))? as usize; + if core_mask == 0 + || core_mask & !0xfffff != 0 + || !(1..=200_000).contains(&max_power_mw) + || base != 1 + { + return Err(EINVAL); + } + let perf = PerfConfig { + states_a: map(words(&node, c_str!("apple,perf-map-a"))?)?, + states_b: map(words(&node, c_str!("apple,perf-map-b"))?)?, + relative_a: active(words(&node, c_str!("apple,perf-relative-a"))?)?, + relative_b: active(words(&node, c_str!("apple,perf-relative-b"))?)?, + sram_min_mv: 810, + }; + if !relative(&perf.relative_a) + || !relative(&perf.relative_b) + || perf.relative_b[..=base].iter().any(|&v| v != 0) + { + return Err(EINVAL); + } + let core = words::<2>(&node, c_str!("apple,core-leak-coef"))?; + let sram = words::<2>(&node, c_str!("apple,sram-leak-coef"))?; + let cs = words::<1>(&node, c_str!("apple,cs-leak-coef"))?; + let afr = words::<1>(&node, c_str!("apple,afr-leak-coef"))?; + // Positive finite binary32 values. Integer comparisons preserve their ordering. + if Iterator::chain( + Iterator::chain(Iterator::chain(core.iter(), &sram), &cs), + &afr, + ) + .any(|&v| !(0x3f80_0000..0x4780_0000).contains(&v)) + { + return Err(EINVAL); + } + let mut leakage = [0; 56]; + leakage[..2].copy_from_slice(&core); + leakage[32..34].copy_from_slice(&sram); + leakage[52] = cs[0]; + leakage[54] = afr[0]; + Ok(Self { + perf, + leakage, + max_power_mw, + core_mask, + base, + }) + } + + /// Check the loader's mask against the GPU before starting its firmware. + pub(super) fn check_cores(&self, masks: &[u32]) -> Result { + if masks != [self.core_mask & 0x3ff, self.core_mask >> 10] { + return Err(EINVAL); + } + Ok(()) + } + + /// The performance maps index OPPs in ADT order, rather than sorted frequency order. + pub(super) fn check_opps(&self, opps: &[Opp]) -> Result { + if opps.len() != 16 || opps[0].freq_hz != 0 || opps.iter().any(|opp| opp.cells != 2) { + return Err(EINVAL); + } + for map in [&self.perf.states_a, &self.perf.states_b] { + let mut previous = 0; + for &index in &map[1..] { + let opp = &opps[usize::from(index)]; + let mhz = opp.freq_hz / 1_000_000; + if mhz <= previous + || opp.volt_uv[..2] + .iter() + .any(|v| !(400_000..=1_300_000).contains(v)) + { + return Err(EINVAL); + } + previous = mhz; + } + } + let base = opps[usize::from(self.perf.states_a[self.base])].freq_hz / 1_000_000; + let top = opps[usize::from(self.perf.states_a[PERF_STATES - 1])].freq_hz / 1_000_000; + for (i, &index) in self.perf.states_a.iter().enumerate() { + let freq = opps[usize::from(index)].freq_hz / 1_000_000; + let expected = if i < self.base { + 0 + } else { + 100 * (freq - base) / (top - base) + }; + if u64::from(self.perf.relative_b[i]) != expected { + return Err(EINVAL); + } + } + Ok(()) + } +} + +#[kunit_tests(asahi_t6040_calibration)] +mod tests { + use super::*; + + fn chip() -> Calibration { + Calibration { + perf: PerfConfig { + states_a: map([0, 1, 2, 3, 4, 6, 8, 10, 12, 14, 15, 0, 0, 0, 0, 0]).unwrap(), + states_b: map([0, 1, 2, 3, 4, 6, 7, 9, 11, 13, 15, 0, 0, 0, 0, 0]).unwrap(), + relative_a: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100], + relative_b: [0, 0, 7, 14, 21, 35, 50, 64, 78, 92, 100], + sram_min_mv: 810, + }, + leakage: [0; 56], + max_power_mw: 70_000, + core_mask: 0xfffff, + base: 1, + } + } + + #[test] + fn operating_points_and_mask_must_match_the_handoff() { + let chip = chip(); + let mut opps = [Opp { + freq_hz: 0, + volt_uv: [800_000; super::super::fw::initdata::OPP_CELLS], + cells: 2, + }; 16]; + for (i, opp) in opps.iter_mut().enumerate() { + opp.freq_hz = i as u64 * 100_000_000; + } + assert!(chip.check_opps(&opps).is_ok()); + opps[0].freq_hz = 100_000_000; + assert!(chip.check_opps(&opps).is_err()); + opps[0].freq_hz = 0; + assert!(chip.check_cores(&[0x3ff, 0x3ff]).is_ok()); + assert!(chip.check_cores(&[0x3ff, 0x1ff]).is_err()); + assert!(chip.check_cores(&[0xfffff]).is_err()); + // Both eleven-state maps must reach raw OPP 15, including its second voltage lane. + assert_eq!(chip.perf.states_a[PERF_STATES - 1], 15); + assert_eq!(chip.perf.states_b[PERF_STATES - 1], 15); + opps[15].volt_uv[1] = 0; + assert!(chip.check_opps(&opps).is_err()); + opps[15].volt_uv[1] = 800_000; + assert!(chip.check_opps(&opps).is_ok()); + opps[6].volt_uv[1] = 0; + assert!(chip.check_opps(&opps).is_err()); + opps[6].volt_uv[1] = 800_000; + opps[6].freq_hz = opps[4].freq_hz; + assert!(chip.check_opps(&opps).is_err()); + opps[6].freq_hz = 601_000_000; + assert!(chip.check_opps(&opps).is_ok()); + opps[6].freq_hz = 611_000_000; + assert!(chip.check_opps(&opps).is_err()); + } + + #[test] + fn maps_refuse_off_repeats_and_out_of_range() { + let valid = [0, 1, 2, 3, 4, 6, 8, 10, 12, 14, 15, 0, 0, 0, 0, 0]; + assert!(map(valid).is_ok()); + for (index, value) in [ + (0, 1), + (1, 0), + (5, 4), + (10, 16), + (10, u32::MAX), + (10, 14), + (11, 1), + (15, 15), + ] { + let mut bad = valid; + bad[index] = value; + assert!(map(bad).is_err()); + } + } + + #[test] + fn relative_tables_are_bounded_and_monotonic() { + let mut values = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]; + assert!(relative(&values)); + values[5] = 101; + assert!(!relative(&values)); + values[5] = 39; + assert!(!relative(&values)); + } +} diff --git a/drivers/gpu/drm/asahi/g17/channel.rs b/drivers/gpu/drm/asahi/g17/channel.rs index d96924cbbf9f9..568647ebb444a 100644 --- a/drivers/gpu/drm/asahi/g17/channel.rs +++ b/drivers/gpu/drm/asahi/g17/channel.rs @@ -43,16 +43,21 @@ impl ReportRing { /// Host-side receive-ring state. A quarantined ring stays stopped until the /// device is destroyed; firmware restart does not clear it. -#[derive(Debug)] pub(crate) struct Rings { quarantined: [[bool; 2]; 2], + reports: super::reports::Reports, } impl Rings { - pub(crate) const fn new() -> Self { - Self { + pub(crate) fn new() -> Result { + Ok(Self { quarantined: [[false; 2]; 2], - } + reports: super::reports::Reports::new()?, + }) + } + + pub(super) fn write_reports(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result { + self.reports.write(f) } pub(crate) fn drain_trace(&mut self, dev: &Device, init: &InitData, role: Role) { @@ -68,7 +73,7 @@ impl Rings { if *quarantined { return; } - if let Err(error) = Self::consume_report(init, role, ring) { + if let Err(error) = Self::consume_report(init, role, ring, &mut self.reports) { *quarantined = true; dev_err!( dev.as_ref(), @@ -80,7 +85,7 @@ impl Rings { } } - fn consume_report(init: &InitData, role: Role, ring: ReportRing) -> Result { + fn consume_report(init: &InitData, role: Role, ring: ReportRing, reports: &mut super::reports::Reports) -> Result { let (state, entries, capacity, allowed_types) = ring.geometry(); let consumer_word = init.status_word(role, state)?; let producer_word = init.status_word(role, state + RX_PRODUCER_OFFSET)?; @@ -94,14 +99,17 @@ impl Rings { } while consumer != producer { let offset = entries + consumer as usize * EVENT_RECORD_SIZE; - let tag = match ring { - // Copy the entire trace entry before returning its slot. - ReportRing::Trace => init.status_read_words::(role, offset)?[0], - ReportRing::Statistics => init.status_read_words::<1>(role, offset)?[0], - }; + // Copy all words before returning the slot. Interpretation is a + // separate firmware qualification step, never a scheduling input. + let words = init.status_read_words::(role, offset)?; + super::trace::report(role as u8, ring as u8, &words); + let tag = words[0]; if tag >= u32::BITS || allowed_types & (1 << tag) == 0 { return Err(EIO); } + if matches!(ring, ReportRing::Statistics) { + reports.record(role, words, super::now_ns()); + } consumer = (consumer + 1) & (capacity - 1); fence(Ordering::SeqCst); consumer_word.store(consumer, Ordering::Relaxed); @@ -147,6 +155,7 @@ impl Rings { /// the caller can service the copied event using the complete device owner. pub(crate) struct Events { role: Role, + graph: super::fw::variant::InitGraph, remaining: u32, unknown_reported: bool, } @@ -167,9 +176,10 @@ impl Events { Ok(consumer != producer) } - pub(crate) fn new(role: Role) -> Self { + pub(crate) fn new(role: Role, graph: super::fw::variant::InitGraph) -> Self { Self { role, + graph, remaining: EVENT_SLOTS, unknown_reported: false, } @@ -195,8 +205,10 @@ impl Events { // Observe the producer before reading the record it publishes. fence(Ordering::Acquire); let offset = status::EVENT_RING as usize + consumer as usize * EVENT_RECORD_SIZE; - let record = EventRecord::from_words(init.status_read_words(role, offset)?); - let event = record.decode(); + let words = init.status_read_words(role, offset)?; + super::trace::report(role as u8, 2, &words); + let record = EventRecord::from_words(words); + let event = record.decode_for(self.graph); fence(Ordering::SeqCst); consumer_word.store((consumer + 1) & (EVENT_SLOTS - 1), Ordering::Relaxed); fence(Ordering::SeqCst); diff --git a/drivers/gpu/drm/asahi/g17/clpc.c b/drivers/gpu/drm/asahi/g17/clpc.c new file mode 100644 index 0000000000000..58adaba19910f --- /dev/null +++ b/drivers/gpu/drm/asahi/g17/clpc.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +#include +#include "clpc.h" + +int asahi_clpc_gpu_get(void *dev, struct apple_clpc_gpu **out) +{ + return apple_clpc_gpu_get(dev, out); +} +void asahi_clpc_gpu_set_active(struct apple_clpc_gpu *gpu, bool active) +{ + apple_clpc_gpu_set_active(gpu, active); +} +void asahi_clpc_gpu_put(struct apple_clpc_gpu *gpu) +{ + apple_clpc_gpu_put(gpu); +} diff --git a/drivers/gpu/drm/asahi/g17/clpc.h b/drivers/gpu/drm/asahi/g17/clpc.h new file mode 100644 index 0000000000000..372820ade7eff --- /dev/null +++ b/drivers/gpu/drm/asahi/g17/clpc.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +#ifndef _ASAHI_G17_CLPC_H +#define _ASAHI_G17_CLPC_H +struct device; +struct apple_clpc_gpu; +int asahi_clpc_gpu_get(void *dev, struct apple_clpc_gpu **out); +void asahi_clpc_gpu_set_active(struct apple_clpc_gpu *gpu, bool active); +void asahi_clpc_gpu_put(struct apple_clpc_gpu *gpu); +#endif diff --git a/drivers/gpu/drm/asahi/g17/fw/channels.rs b/drivers/gpu/drm/asahi/g17/fw/channels.rs index 53e899085a0e4..26c6adcf34c83 100644 --- a/drivers/gpu/drm/asahi/g17/fw/channels.rs +++ b/drivers/gpu/drm/asahi/g17/fw/channels.rs @@ -165,6 +165,16 @@ impl EventRecord { unsafe { self.words[0] } } + /// Type 7 is a retirement witness only on the firmware whose layout and + /// visibility semantics are qualified. 25G83 must keep consuming it as an + /// unknown event until its own fault contract is established. + pub(crate) fn decode_for(&self, graph: super::variant::InitGraph) -> Result { + if graph == super::variant::InitGraph::V25G83 && self.tag() == Self::COMPUTE_FAULT { + return Ok(Event::Unknown(Self::COMPUTE_FAULT)); + } + self.decode() + } + /// Validates the record's fields without interpreting resource ownership. /// A malformed known event is an error, unlike an unknown tag. pub(crate) fn decode(&self) -> Result { @@ -560,6 +570,17 @@ mod tests { assert_eq!(event, Ok(Event::ComputeFault { qid: 0x39, stamp: 0x300 })); } + #[test] + fn v25g83_does_not_accept_an_unqualified_fault_witness() { + use super::super::variant::InitGraph; + let fault = record(&[7, 1, 2, 0x39, 0x300]); + assert_eq!(fault.decode_for(InitGraph::G17P), + Ok(Event::ComputeFault { qid: 0x39, stamp: 0x300 })); + assert_eq!(fault.decode_for(InitGraph::V25G83), Ok(Event::Unknown(7))); + assert_eq!(record(&[7, 1, 2, 128, 0x301]).decode_for(InitGraph::V25G83), + Ok(Event::Unknown(7))); + } + #[test] fn an_unrecognized_type_7_layout_stays_unknown() { assert_eq!(record(&[7, 1, 2, 128, 0x300]).decode(), Ok(Event::Unknown(7))); diff --git a/drivers/gpu/drm/asahi/g17/fw/initdata/v25g83.rs b/drivers/gpu/drm/asahi/g17/fw/initdata/v25g83.rs index c237896a49fb1..f4c3ee5db22e2 100644 --- a/drivers/gpu/drm/asahi/g17/fw/initdata/v25g83.rs +++ b/drivers/gpu/drm/asahi/g17/fw/initdata/v25g83.rs @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only OR MIT +// SPDX-License-Identifier: GPL-2.0-only //! Initialization data of the 25G83 firmware build (macOS 26.6, `mBoot-18000.161.10`). //! @@ -17,9 +17,9 @@ //! firmware on the M4 Pro, by evaluating them for this SoC; see the commit that added them. use super::{ - fill_words, perf_states, CompletionRingDescriptor, HwDataArgs, Opp, PerfGroup, RegisterMapEntry, - Words, COMPLETION_RING_ENTRIES, COMPLETION_RING_ENTRY_SIZE, COMPLETION_RING_INDEX, PERF_STATES, - REGISTER_MAP_SLOTS, + fill_words, perf_states, CompletionRingDescriptor, HwDataArgs, Opp, PerfGroup, + RegisterMapEntry, Words, COMPLETION_RING_ENTRIES, COMPLETION_RING_ENTRY_SIZE, + COMPLETION_RING_INDEX, PERF_STATES, REGISTER_MAP_SLOTS, }; use kernel::prelude::*; @@ -381,26 +381,10 @@ impl HwData25G83 { /// Power configuration control period, in 24 MHz clocks (8 ms). const PERIOD_CLOCKS: u32 = 200_000; -/// Highest GPU power, in mW. -pub(crate) const MAX_POWER_MW: u32 = 69_116; -/// [`MAX_POWER_MW`] as an IEEE-754 single. -const MAX_POWER_MW_F32: u32 = 0x4786_fe00; /// Temperature sensors read by the die and MTR temperature limiters. const TEMP_SENSOR_MASK: u32 = 0x2a22_2aa2; -/// Leakage coefficients, IEEE-754 singles: three arrays of sixteen clusters, then four arrays -/// of two dies. Not found in the ADT; constant for the SoC. -const LEAKAGE: [u32; 56] = { - let mut words = [0; 56]; - words[0] = 0x44f0_3000; // 1921.5 - words[1] = 0x44f3_0000; // 1944.0 - words[32] = 0x4373_0000; // 243.0 - words[33] = 0x4373_0000; // 243.0 - words[52] = 0x437b_0000; // 251.0 - words[54] = 0x43bd_c000; // 379.5 - words -}; -/// Offset of [`LEAKAGE`] in the power configuration. +/// Offset of the per-chip leakage coefficients in the power configuration. const LEAKAGE_OFFSET: usize = 0x642c; /// Offset in the power configuration of the GPU address of the configuration's status (the /// configuration named by the root, `+0x80`). @@ -433,12 +417,10 @@ const POWER_CONFIG_WORDS: Words = &[ (0x9c4, 0x3f7f2e9f, 1), (0x9cc, 0x3b516154, 1), (0x9d4, 0x3ca59586, 1), - (0x9e0, MAX_POWER_MW_F32, 1), (0x9e4, 0x40a90fdb, 1), (0x9ec, 0xbc6d0cfe, 1), (0x9f0, 0x64, 1), (0x9f4, 0x3e8, 2), - (0xa04, MAX_POWER_MW, 1), (0xa38, 0x3e8, 1), (0xa70, 0x8, 1), (0xa7c, 0x3f000000, 1), @@ -448,7 +430,6 @@ const POWER_CONFIG_WORDS: Words = &[ (0xa9c, 0x3ecf2e49, 1), (0xaa8, 0x64, 1), (0xaac, 0x3e8, 1), - (0xabc, MAX_POWER_MW, 1), (0xac8, 0x10, 1), (0xad0, 0x5dc00, 1), (0xb28, 0x50, 1), @@ -508,7 +489,6 @@ const POWER_CONFIG_WORDS: Words = &[ (0x27b8, 0xc80000, 1), (0x2870, 0x3e8, 1), (0x28c0, 0x1, 1), - (0x28c8, MAX_POWER_MW, 3), (0x28e0, 0x3f800000, 1), (0x28e4, 0x4, 1), (0x28e8, 0x8, 1), @@ -521,8 +501,6 @@ const POWER_CONFIG_WORDS: Words = &[ (0x29e8, 0x3f4f2e49, 1), (0x29f4, 0x28, 1), (0x29f8, 0x3e8, 2), - (0x2a04, MAX_POWER_MW_F32, 1), - (0x2a08, MAX_POWER_MW, 1), (0x2a14, 0xf8, 1), (0x2a1c, 0x5ad200, 1), (0x2a3c, 0x3e8, 1), @@ -591,27 +569,34 @@ unsafe impl Zeroable for PowerConfig25G83 {} static_assert!(LEAKAGE_OFFSET + size_of::<[u32; 56]>() <= size_of::()); impl PowerConfig25G83 { - /// Pointer and random words, which [`Self::hash`] leaves out. - const RUNTIME: [(usize, usize); 3] = - [(CONFIG_STATUS_POINTER, 8), (AON_POINTER, 8), (ENTROPY, 4)]; - /// Initializes the power configuration. - pub(crate) fn init(&mut self, config_va: u64, aon_va: u64, entropy: u32) { + pub(crate) fn init( + &mut self, + config_va: u64, + aon_va: u64, + entropy: u32, + max_power_mw: u32, + leakage: &[u32; 56], + ) { + self.0.fill(0); fill_words(&mut self.0, POWER_CONFIG_WORDS); - for (index, word) in LEAKAGE.iter().enumerate() { + for (index, word) in leakage.iter().enumerate() { let at = LEAKAGE_OFFSET + 4 * index; self.0[at..at + 4].copy_from_slice(&word.to_le_bytes()); } + // All copies of the power ceiling must describe the same chip. + for offset in [0xa04, 0xabc, 0x28c8, 0x28cc, 0x28d0, 0x2a08] { + self.0[offset..offset + 4].copy_from_slice(&max_power_mw.to_le_bytes()); + } + let power = crate::float::F32::from(max_power_mw).to_bits(); + for offset in [0x9e0, 0x2a04] { + self.0[offset..offset + 4].copy_from_slice(&power.to_le_bytes()); + } self.0[CONFIG_STATUS_POINTER..CONFIG_STATUS_POINTER + 8] .copy_from_slice(&(config_va + 0x80).to_le_bytes()); self.0[AON_POINTER..AON_POINTER + 8].copy_from_slice(&aon_va.to_le_bytes()); self.0[ENTROPY..ENTROPY + 4].copy_from_slice(&entropy.to_le_bytes()); } - - /// Hash of the contents without the pointers and the random word. - pub(crate) fn hash(&self) -> u64 { - hash_without(&self.0, &Self::RUNTIME) - } } /// The AON temperature-source rows the power configuration names: the GPU cluster 0, GPU @@ -632,50 +617,3 @@ pub(crate) const AON_TABLE: [u32; 9] = [ /// (`+0xc8`, 101.00 °C) and release (`+0xcc`, 96.00 °C) temperatures in 0.01 °C; left at zero /// the limit is 0 °C and the GPU stays at its lowest state whenever the PMP runs. pub(crate) const POWER_STATE_WORDS: Words = &[(0xc8, 10_100, 1), (0xcc, 9_600, 1)]; - -impl HwData25G83 { - /// What [`Self::hash`] leaves out: the runtime words (completion descriptors, QoS pointer) - /// and what comes from the device: the performance tables built from its operating points, - /// which are binned per device, and its enabled core count. - const UNHASHED: [(usize, usize); 10] = [ - (COMPLETION_DESCRIPTORS, 0x80), - (QOS_POINTER, 8), - (core::mem::offset_of!(Self, unk_f88), size_of::()), - (core::mem::offset_of!(Self, unk_fc0), size_of::()), - (core::mem::offset_of!(Self, perf_a), size_of::()), - (core::mem::offset_of!(Self, perf_b_freq_mhz), size_of::<[u32; PERF_STATES]>()), - (core::mem::offset_of!(Self, cs_perf), size_of::()), - (core::mem::offset_of!(Self, afr_perf), size_of::()), - (core::mem::offset_of!(Self, perf_b), size_of::()), - (core::mem::offset_of!(Self, unk_2570), size_of::()), - ]; - - /// Hash of the constant contents (everything but [`Self::UNHASHED`]). - pub(crate) fn hash(&self) -> u64 { - // SAFETY: `HwData25G83` consists of integers only and has no padding. - let bytes = unsafe { - core::slice::from_raw_parts((self as *const Self).cast::(), size_of::()) - }; - hash_without(bytes, &Self::UNHASHED) - } -} - -/// FNV-1a 64 of `bytes` with the `(offset, length)` ranges in `skip` left out. -pub(crate) fn hash_without(bytes: &[u8], skip: &[(usize, usize)]) -> u64 { - let mut hash = 0xcbf2_9ce4_8422_2325u64; - for (offset, byte) in bytes.iter().enumerate() { - if skip.iter().any(|&(at, len)| (at..at + len).contains(&offset)) { - continue; - } - hash = (hash ^ u64::from(*byte)).wrapping_mul(0x100_0000_01b3); - } - hash -} - -/// Hashes ([`hash_without`]) of the hardware data, power configuration and region C that the -/// Gravity Linux G16 encoders produce for the J616s, which this build's objects must equal. -pub(crate) mod expected { - pub(crate) const HW_DATA: u64 = 0xfd3b_a26f_570f_57cd; - pub(crate) const POWER_CONFIG: u64 = 0xb669_5b84_2ad2_36fe; - pub(crate) const REGION_C: u64 = 0xd2db_fa6b_8976_47b4; -} diff --git a/drivers/gpu/drm/asahi/g17/fw/queue.rs b/drivers/gpu/drm/asahi/g17/fw/queue.rs index 5708a2251a062..66e7d07db48a5 100644 --- a/drivers/gpu/drm/asahi/g17/fw/queue.rs +++ b/drivers/gpu/drm/asahi/g17/fw/queue.rs @@ -96,8 +96,6 @@ pub(crate) enum CommandTag { QueueConfig = 0x0f, /// [`KickPredecessor`]. KickPredecessor = 0x10, - /// [`FragmentBarrier`] (25G83 firmware). - FragmentBarrier = 4, /// [`InitBufferManager`] (25G83 firmware). InitBufferManager = 5, } @@ -569,63 +567,6 @@ impl QueueConfig { } } -/// The 25G83 firmware's barrier before every fragment descriptor (tag 4): the fragment work -/// waits for the paired tiling stamp, in addition to the kick-entry dependency. -#[repr(C, packed)] -#[derive(Copy, Clone, Debug)] -pub(crate) struct FragmentBarrier { - /// [`CommandTag::FragmentBarrier`]. - pub(crate) tag: u32, - /// GPU address of the tiling queue's completion stamp word. - pub(crate) stamp_va: u64, - /// Timestamp of the tiling kick. - pub(crate) tiling_kick: u64, - /// QID of the tiling queue. - pub(crate) tiling_qid: u32, - /// Tiling stamp value waited for. - pub(crate) wait: u32, - pub(crate) unk_1c: [u8; 8], - /// Event of the wait: the tiling QID. - pub(crate) event: u32, - /// Fragment stamp value. - pub(crate) stamp_self: u32, - /// Current object ID of the fragment stage. - pub(crate) fragment_id: u64, - pub(crate) unk_34: u32, - pub(crate) unk_38: [u8; 0x10], -} - -static_assert!(size_of::() == 0x48); -static_assert!(core::mem::offset_of!(FragmentBarrier, wait) == 0x18); -static_assert!(core::mem::offset_of!(FragmentBarrier, event) == 0x24); -static_assert!(core::mem::offset_of!(FragmentBarrier, fragment_id) == 0x2c); - -impl FragmentBarrier { - /// The barrier of a pass whose tiling stage, kicked at `tiling_kick` on `tiling_qid`, - /// signals `stamp_value` at `stamp_va`. - pub(crate) fn new( - stamp_va: u64, - tiling_kick: KickTimestamp, - tiling_qid: u32, - stamp_value: u32, - fragment_id: u64, - ) -> Self { - Self { - tag: CommandTag::FragmentBarrier as u32, - stamp_va, - tiling_kick: tiling_kick.get(), - tiling_qid, - wait: stamp_value, - unk_1c: [0; 8], - event: tiling_qid, - stamp_self: stamp_value, - fragment_id, - unk_34: 1, - unk_38: [0; 0x10], - } - } -} - /// The 25G83 firmware's buffer-manager initialization (tag 5), before the first tiling /// descriptor that uses a parameter buffer. The block count is read from the manager. #[repr(C, packed)] diff --git a/drivers/gpu/drm/asahi/g17/fw/variant.rs b/drivers/gpu/drm/asahi/g17/fw/variant.rs index d2d6d2404dfc8..39c8cfcd77a2a 100644 --- a/drivers/gpu/drm/asahi/g17/fw/variant.rs +++ b/drivers/gpu/drm/asahi/g17/fw/variant.rs @@ -57,17 +57,19 @@ pub(crate) struct Variant { pub(crate) few_primitives: bool, /// The firmware runs ray-traversal work (ray queries and intersection programs). pub(crate) ray_traversal: bool, + /// Tiling descriptors report whether the fragment queue still had earlier kicks + /// outstanding (`fragment_busy`). + pub(crate) fragment_busy: bool, /// Render item rings carry lead commands: InitBufferManager before the first tiling pass - /// of a buffer manager, and FragmentBarrier before every fragment descriptor. + /// of a buffer manager. pub(crate) lead_commands: bool, /// Register programs of the render descriptors. pub(crate) programs: super::render::RegisterPrograms, } -/// A firmware build a SoC supports, selected by the macOS release it ships with and proven by -/// its loaded TEXT. +/// A firmware build a SoC supports, identified by its loaded TEXT. pub(crate) struct Build { - /// macOS version (major, minor, patch). + /// macOS version (major, minor, patch), used to order image candidates. pub(crate) os_version: [u32; 3], /// Name, for the log. pub(crate) name: &'static str, @@ -81,9 +83,8 @@ pub(crate) struct Build { pub(crate) enum Firmware { /// The SoC runs a single build, which is not identified (A18 Pro). Fixed(&'static Variant), - /// One of these builds, selected by the macOS version that the boot loader reports in - /// `apple,firmware-version` (the release whose boot firmware loaded the GPU firmware) and - /// then proven by the loaded TEXT. Any other version or image is refused. + /// One of these exact loaded images. The loader's `apple,firmware-version` is only + /// a candidate-ordering hint because multiple OS releases can share an iBoot string. Identified(&'static [Build]), } @@ -101,6 +102,7 @@ pub(crate) const V26A428: Variant = Variant { mcache: true, few_primitives: true, ray_traversal: true, + fragment_busy: true, lead_commands: false, programs: super::render::RegisterPrograms::G17P, }; @@ -126,6 +128,9 @@ pub(crate) const V25G83: Variant = Variant { few_primitives: false, // A stalled ray traversal wedges the GPU without a firmware recovery. ray_traversal: false, + // When a tiling pass runs out of memory with this set, the firmware resumes it without + // adding memory and the tiler stops silently; macOS always writes 0. + fragment_busy: false, lead_commands: true, programs: super::render::RegisterPrograms::V25G83, }; diff --git a/drivers/gpu/drm/asahi/g17/initdata.rs b/drivers/gpu/drm/asahi/g17/initdata.rs index 7e0c36ece43ad..c8763f40871d1 100644 --- a/drivers/gpu/drm/asahi/g17/initdata.rs +++ b/drivers/gpu/drm/asahi/g17/initdata.rs @@ -1328,7 +1328,11 @@ impl InitData { /// The GPU's operating points: `count` of them, each with one voltage cell for the whole /// GPU or one per cluster. - fn opps(dev: &AsahiDrmDevice, count: usize, clusters: u32) -> Result> { + pub(super) fn opps( + dev: &AsahiDrmDevice, + count: usize, + clusters: u32, + ) -> Result> { let opps = Self::opp_table(dev, c_str!("operating-points-v2"))?; if opps.len() != count { return Err(EINVAL); @@ -1559,7 +1563,10 @@ impl super::recovery::Memory for InitData { .dword(self.status_b + abi::PrimaryStatusB::RECOVERY_SLOT_MASK)? .load(Ordering::Relaxed); let mut sources = super::recovery::Sources::sample(mask as u32, |slot| { - let key = super::recovery::slot_key(slot, self.recovery_slot_keys)?; + // Progress is independently available even when this firmware's key + // address is unknown. Zero keeps ownership unknown and cannot override + // firmware blame, but preserves the progress evidence for diagnostics. + let key = super::recovery::slot_key(slot, self.recovery_slot_keys).unwrap_or(0); let monitor = self.recovery_view_words::<6>(abi::recovery::PROGRESS_VIEW, abi::recovery::PROGRESS_START + slot * abi::recovery::PROGRESS_STRIDE) .ok().map(|words| words[0])?; diff --git a/drivers/gpu/drm/asahi/g17/initdata/v25g83.rs b/drivers/gpu/drm/asahi/g17/initdata/v25g83.rs index 85aa6468fa311..670f4b96dcb76 100644 --- a/drivers/gpu/drm/asahi/g17/initdata/v25g83.rs +++ b/drivers/gpu/drm/asahi/g17/initdata/v25g83.rs @@ -8,7 +8,7 @@ //! ([`abi::v25g83`]); free lists, pools, descriptor tables, rings and regions are the A18 Pro's. use super::{abi, low, BuildEnv, BundleObjects, CpuMap, DescriptorTables, InitData, KernelObject}; -use crate::g17::{Config, Role}; +use crate::g17::{calibration::Calibration, Config, Role}; use crate::mmu; use abi::v25g83 as v; use core::sync::atomic::{fence, Ordering}; @@ -74,7 +74,7 @@ impl InitData { }) } - /// Writes the 25G83 firmware objects, checks them against the expected contents, then + /// Writes the 25G83 firmware objects using the chip calibration, then /// stages the opening control-ring state. fn initialize_25g83( &mut self, @@ -101,12 +101,14 @@ impl InitData { let opps = Self::opps(env.dev, cfg.opps, env.clusters)?; let cs_opps = Self::opp_table(env.dev, c_str!("apple,cs-opp"))?; let afr_opps = Self::opp_table(env.dev, c_str!("apple,afr-opp"))?; + let calibration = Calibration::read(env.dev)?; + calibration.check_opps(&opps)?; let hw_args = abi::HwDataArgs { chip_id: cfg.chip_id, windows: cfg.windows, reserved_slots: cfg.reserved_slots, opps: &opps, - perf: cfg.perf, + perf: &calibration.perf, completion_rings: bundle.completion_rings, qos_va: self.object(bundle.qos)?.gpu_va(), }; @@ -117,41 +119,28 @@ impl InitData { }; // SAFETY: Plain C entropy source. let entropy = unsafe { kernel::bindings::get_random_u32() }; - let mut hashes = [0; 3]; self.object_mut(self.bundle)? .initialize::(0, |hw| { hw.init(&hw_args, &hw_args_25g83)?; - hashes[0] = hw.hash(); Ok(()) })?; self.object_mut(self.bundle)? .initialize::(v::POWER_CONFIG, |power| { - power.init(config_va, aon_va, entropy); - hashes[1] = power.hash(); + power.init( + config_va, + aon_va, + entropy, + calibration.max_power_mw, + &calibration.leakage, + ); Ok(()) })?; self.object_mut(self.region_c)? .initialize::(0, |region| { region.init(cfg.region_c); + region.max_power_mw = calibration.max_power_mw; Ok(()) })?; - hashes[2] = v::hash_without(self.object_mut(self.region_c)?.bytes_mut(), &[]); - // The constant contents must equal what the Gravity Linux encoders produce on the proven - // setup; the operating points and the core count come from the device and are not hashed. - let expected = [ - v::expected::HW_DATA, - v::expected::POWER_CONFIG, - v::expected::REGION_C, - ]; - if hashes != expected { - dev_err!( - env.dev.as_ref(), - "25G83 initialization data differs: {:#x?}, expected {:#x?}\n", - hashes, - expected - ); - return Err(EINVAL); - } for (index, word) in v::AON_TABLE.iter().enumerate() { self.object_mut(objects.aon)?.write(index * 4, *word)?; } @@ -167,7 +156,9 @@ impl InitData { v::private::STATUS_B + core::mem::offset_of!(abi::PrimaryStatusB, fwctl_state_va); let cluster = self.object(self.cluster)?; cluster.dword(fwctl)?.store(fwctl_va, Ordering::Relaxed); - cluster.dword(fwctl + 8)?.store(fwctl_va + abi::FWCTL_RING, Ordering::Relaxed); + cluster + .dword(fwctl + 8)? + .store(fwctl_va + abi::FWCTL_RING, Ordering::Relaxed); // One main configuration carrying both roles. let main_offset = abi::bundle::MAIN_CONFIG[Role::Primary as usize]; @@ -218,7 +209,8 @@ impl InitData { )?; for &(offset, value, repeat) in v::POWER_STATE_WORDS { for index in 0..repeat { - self.object_mut(objects.power_state)?.write(offset + 4 * index, value)?; + self.object_mut(objects.power_state)? + .write(offset + 4 * index, value)?; } } diff --git a/drivers/gpu/drm/asahi/g17/job.rs b/drivers/gpu/drm/asahi/g17/job.rs index 61033aa9e52b4..ad9adcecf0f81 100644 --- a/drivers/gpu/drm/asahi/g17/job.rs +++ b/drivers/gpu/drm/asahi/g17/job.rs @@ -363,10 +363,17 @@ impl sched::JobImpl for Job { } fn prepare(job: &mut Self) -> Option { - job.backend.prepare(&job.packet) + super::trace::schedule(&job.packet, 5, 0); + let wait = job.backend.prepare(&job.packet); + super::trace::schedule(&job.packet, 6, i32::from(wait.is_some())); + wait } fn run(job: &mut Self) -> Result> { - job.backend.publish(job.packet.clone()).map(Some) + super::trace::schedule(&job.packet, 3, 0); + let result = job.backend.publish(job.packet.clone()).map(Some); + super::trace::schedule(&job.packet, 4, + result.as_ref().err().map_or(0, |error| error.to_errno())); + result } fn timed_out(job: &mut Timeout, finished: bool) -> sched::Status { // The scheduler retains the detached job throughout this callback. Keep @@ -430,6 +437,7 @@ impl Scheduler { early: bool, publication: Option, ) -> Result { + super::trace::schedule(&packet, 0, dependencies.len() as i32); let completed = packet.completion.fence(); let host_timestamps = packet.completion.has_timestamps(); // Only commands which actually write timestamps become host-prefix @@ -462,7 +470,9 @@ impl Scheduler { }; // All fallible scheduler setup precedes early hardware publication. The // scheduler still owns watchdog, credits and cancellation after push. - if !(early && self.backend.try_early(&packet)) { + let inline = early && self.backend.try_early(&packet); + super::trace::schedule(&packet, if inline { 1 } else { 2 }, 0); + if !inline { if packet.completion.needs_source_ready() { packet.completion.set_source_ready(ready.clone()); } diff --git a/drivers/gpu/drm/asahi/g17/kick.rs b/drivers/gpu/drm/asahi/g17/kick.rs index 3b55c283254b5..6499cae106309 100644 --- a/drivers/gpu/drm/asahi/g17/kick.rs +++ b/drivers/gpu/drm/asahi/g17/kick.rs @@ -314,7 +314,7 @@ mod accounting_tests { #[test] fn dynamic_compute_growth_preserves_render_headroom_above_firmware_floor() { - for floor in [0, 4] { + for floor in [0, 4, 32] { let mut ids = KBox::init(QueueIds::new(floor), GFP_KERNEL).unwrap(); let queues = 2; let mut owner = 1; @@ -333,7 +333,7 @@ mod accounting_tests { #[test] fn full_logical_queue_budget_keeps_compute_floor_and_excludes_firmware_ids() { - for floor in [0, 4] { + for floor in [0, 4, 32] { let mut ids = KBox::init(QueueIds::new(floor), GFP_KERNEL).unwrap(); let queues = queue_limit(floor) as usize; for owner in 1..=COMPUTE_FLOOR as u64 { diff --git a/drivers/gpu/drm/asahi/g17/mod.rs b/drivers/gpu/drm/asahi/g17/mod.rs index 5d43f4734b53f..1bb7a30e8e033 100644 --- a/drivers/gpu/drm/asahi/g17/mod.rs +++ b/drivers/gpu/drm/asahi/g17/mod.rs @@ -23,6 +23,7 @@ mod availability; mod buffer; +mod calibration; mod channel; mod command; pub(crate) use command::Attachments; @@ -49,6 +50,7 @@ mod kick; mod object; mod port; pub(crate) mod power; +mod reports; mod preparation; mod qos; mod queue; @@ -647,6 +649,7 @@ impl Drop for Firmware { secondary.stop(); } self.primary.stop(); + self.port.set_effort_full(false); self.pool.stop_after_firmware(); // RTKit destruction releases the mailbox power references. Clear the bridge while // those references still keep the register block powered. @@ -693,6 +696,7 @@ impl Firmware { /// Called at actual submission publication, not while a queued job is merely being built. fn note_submission(&mut self) -> Result { + self.port.note_gpu_activity(); self.effort.submit(); self.apply_effort(power::FULL_EFFORT) } @@ -1113,6 +1117,8 @@ pub(crate) struct Gpu { ids: gpu::SequenceIDs, shared: Arc, _capacity: Pin>>, + _reports: Pin>>, + _power: Pin>>, } impl Gpu { @@ -1123,8 +1129,19 @@ impl Gpu { cfg: &'static Config, regs: regs::G17Resources, ) -> Result> { + let calibration = if cfg.chip_id == crate::hw::t6040::CHIP_ID { + Some(calibration::Calibration::read(dev).inspect_err(|_| { + dev_err!(dev.as_ref(), "Missing or invalid T6040 GPU calibration handoff\n"); + })?) + } else { + None + }; let variant = Self::firmware_variant(dev, &cfg.firmware)?; let id = regs.get_g17_id()?; + if let Some(calibration) = calibration { + calibration.check_cores(&id.core_masks)?; + calibration.check_opps(&initdata::InitData::opps(dev, cfg.opps, id.num_clusters)?)?; + } if id.family != cfg.identity.family || id.variant != cfg.identity.variant || id.num_dies != cfg.identity.num_dies @@ -1144,6 +1161,12 @@ impl Gpu { oas: cfg.uat_oas, }; let max_frequency_khz = Self::max_frequency_khz(dev)?; + let node = dev.as_ref().as_ref().of_node().ok_or(EIO)?; + let idle_delay = power::idle_delay( + node.get_opt_property(c_str!("apple,idle-off-delay-ms"))?, + )?; + power::IDLE_HOLD_MS.store(idle_delay, Ordering::Relaxed); + dev_info!(dev.as_ref(), "GPU idle power-off delay: {} ms\n", idle_delay); let channels = availability::Availability::new()?; let progress = availability::Availability::new()?; let queues = availability::Availability::new()?; @@ -1205,6 +1228,14 @@ impl Gpu { directory.read_only_file(c"capacity", capacity::View(shared.clone())), GFP_KERNEL, )?; + let reports = KBox::pin_init( + directory.read_only_file(c"firmware_reports", reports::View(shared.clone())), + GFP_KERNEL, + )?; + let power = KBox::pin_init( + directory.read_only_file(c"power", reports::PowerView(shared.clone())), + GFP_KERNEL, + )?; let gpu = Arc::new( Gpu { cfg, @@ -1214,6 +1245,8 @@ impl Gpu { ids: Default::default(), shared, _capacity: capacity, + _reports: reports, + _power: power, }, GFP_KERNEL, )?; @@ -1237,24 +1270,26 @@ impl Gpu { fw::variant::Firmware::Identified(builds) => builds, }; let version = Self::os_firmware_version(dev); - if let Some(build) = builds.iter().find(|build| Some(build.os_version) == version) { - dev_info!(dev.as_ref(), "GPU firmware: {}\n", build.name); - Self::identify_image(dev, build).map_err(|_| ENODEV)?; - return Ok(build.variant); + // iBoot strings can be shared by OS releases. Prefer the reported release's + // candidate, but admit only an exact loaded image, regardless of that hint. + let preferred = builds.iter().filter(|build| Some(build.os_version) == version); + let others = builds.iter().filter(|build| Some(build.os_version) != version); + for build in Iterator::chain(preferred, others) { + if Self::identify_image(dev, build).is_ok() { + dev_info!(dev.as_ref(), "GPU firmware: {}\n", build.name); + return Ok(build.variant); + } } dev_err!( dev.as_ref(), - "Unsupported GPU firmware: macOS version {:?} (apple,firmware-version)\n", + "Unsupported GPU firmware image (reported macOS version {:?})\n", version ); - for build in builds.iter() { - dev_err!(dev.as_ref(), "Supported GPU firmware: {}\n", build.name); - } Err(ENODEV) } /// Checks that the loaded TEXT (the `fw-text` region) is one of `build`'s images, with the DATA size - /// of the `fw-data` region. The version property only says which build to expect. + /// of the `fw-data` region. The version property is only a selection hint. fn identify_image(dev: &AsahiDrmDevice, build: &'static fw::variant::Build) -> Result { let node = dev.as_ref().as_ref().of_node().ok_or(EINVAL)?; let text = node.reserved_mem_region_to_resource_byname(c_str!("fw-text"))?; @@ -1351,9 +1386,9 @@ impl Gpu { secondary, primary, bridge, - port: port::Port::new()?, + port: port::Port::new(power::Demand::new(pdev.as_ref().as_raw())?)?, seen_submissions: 0, - rings: channel::Rings::new(), + rings: channel::Rings::new()?, recovery: recovery::State::new(), effort: power::Effort::new(), hold: power::Hold::new(), diff --git a/drivers/gpu/drm/asahi/g17/port.rs b/drivers/gpu/drm/asahi/g17/port.rs index db043ef9d6f91..243fbfce07f9e 100644 --- a/drivers/gpu/drm/asahi/g17/port.rs +++ b/drivers/gpu/drm/asahi/g17/port.rs @@ -105,6 +105,8 @@ pub(super) struct Port { compute_announced: [AtomicU64; PRIORITIES], /// Full firmware effort is applied; cleared before an idle clear rechecks `submissions`. effort_full: AtomicBool, + demand: super::power::Demand, + last_submission_ns: AtomicU64, submissions: AtomicU64, } @@ -142,7 +144,7 @@ impl Drop for OuterClaim<'_> { } impl Port { - pub(super) fn new() -> Result> { + pub(super) fn new(demand: super::power::Demand) -> Result> { Arc::pin_init( pin_init!(Self { views: SetOnce::new(), @@ -155,6 +157,8 @@ impl Port { compute_written: [const { AtomicU64::new(0) }; PRIORITIES], compute_announced: [const { AtomicU64::new(0) }; PRIORITIES], effort_full: AtomicBool::new(false), + demand, + last_submission_ns: AtomicU64::new(0), submissions: AtomicU64::new(0), }), GFP_KERNEL, @@ -453,6 +457,7 @@ impl Port { /// clear sees this submission or this submission sees the flag withdrawn. pub(super) fn note_submission(&self) -> bool { self.submissions.fetch_add(1, Ordering::SeqCst); + self.note_gpu_activity(); self.effort_full.load(Ordering::SeqCst) } @@ -462,5 +467,17 @@ impl Port { pub(super) fn set_effort_full(&self, full: bool) { self.effort_full.store(full, Ordering::SeqCst); + self.demand.set_active(full); + } + + pub(super) fn note_gpu_activity(&self) { + self.last_submission_ns.store(super::now_ns(), Ordering::Relaxed); + self.demand.set_active(true); + } + + pub(super) fn write_power(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result { + writeln!(f, "effort_full={} direct_submissions={} last_submission_ns={}", + self.effort_full.load(Ordering::SeqCst), self.submissions(), + self.last_submission_ns.load(Ordering::Relaxed)) } } diff --git a/drivers/gpu/drm/asahi/g17/power.rs b/drivers/gpu/drm/asahi/g17/power.rs index b0e21e70bbe18..a2e293df91510 100644 --- a/drivers/gpu/drm/asahi/g17/power.rs +++ b/drivers/gpu/drm/asahi/g17/power.rs @@ -12,10 +12,22 @@ pub(super) const IDLE_HOLD_NS: u64 = 16_000_000; /// the last job. Not measured on other firmware; at or below it the hold stays off. pub(crate) const FIRMWARE_IDLE_OFF_MS: u32 = 2; -/// The `idle_off_delay_ms` sysfs knob. Above [`FIRMWARE_IDLE_OFF_MS`] the driver keeps the GPU -/// cores powered until the GPU has been idle this long ([`Hold`]). +/// Idle policy initialized from the existing DT `apple,idle-off-delay-ms` property. +/// The existing `idle_off_delay_ms` sysfs attribute may override it. Above +/// [`FIRMWARE_IDLE_OFF_MS`] the driver keeps the GPU cores powered until it has been idle +/// this long ([`Hold`]). pub(crate) static IDLE_HOLD_MS: AtomicU32 = AtomicU32::new(FIRMWARE_IDLE_OFF_MS); +/// Use the same bounds as the existing idle-delay attribute. Missing DT policy +/// preserves the native delay; malformed or out-of-range values fail probe. +pub(super) fn idle_delay(requested: Option) -> kernel::error::Result { + let delay = requested.unwrap_or(FIRMWARE_IDLE_OFF_MS); + if !(FIRMWARE_IDLE_OFF_MS..=1000).contains(&delay) { + return Err(kernel::error::code::EINVAL); + } + Ok(delay) +} + #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub(super) enum IdleAction { None, @@ -237,6 +249,34 @@ pub(super) fn release(control: &mut C) -> kernel::error::Result { mod tests { use super::*; + #[test] + fn dt_idle_delay_is_bounded_and_absence_preserves_native_policy() { + assert_eq!(idle_delay(None), Ok(FIRMWARE_IDLE_OFF_MS)); + for delay in [2, 16, 1000] { + assert_eq!(idle_delay(Some(delay)), Ok(delay)); + } + for delay in [0, 1, 1001, u32::MAX] { + assert!(idle_delay(Some(delay)).is_err()); + } + } + + #[test] + fn frame_hold_survives_depth_one_gaps_but_releases_at_idle() { + let mut hold = Hold::new(); + assert_eq!(hold.step(0, true, 1, 16), HoldAction::Assert); + hold.asserted(); + for submission in 1..=8 { + // A completed depth-one dispatch followed by another short gap. + let now = submission * 3_000_000; + assert_eq!(hold.step(now, false, submission, 16), HoldAction::Recheck(16_000_000)); + assert!(hold.active()); + } + assert_eq!(hold.step(39_999_999, false, 8, 16), HoldAction::Recheck(1)); + assert_eq!(hold.step(40_000_000, false, 8, 16), HoldAction::Release); + hold.released(); + assert!(!hold.active()); + } + #[test] fn holds_until_idle_for_the_delay_then_releases() { let mut hold = Hold::new(); @@ -260,3 +300,43 @@ mod tests { assert_eq!(hold.step(1, false, 2, FIRMWARE_IDLE_OFF_MS), HoldAction::Release); } } + +unsafe extern "C" { + fn asahi_clpc_gpu_get(dev: *mut core::ffi::c_void, out: *mut *mut core::ffi::c_void) -> core::ffi::c_int; + fn asahi_clpc_gpu_set_active(gpu: *mut core::ffi::c_void, active: bool); + fn asahi_clpc_gpu_put(gpu: *mut core::ffi::c_void); +} + +/// DT-selected memory-demand provider. Its independent allocation outlives a +/// detached provider; a device link orders supplier removal and suspend. +pub(super) struct Demand(*mut core::ffi::c_void); + +// SAFETY: the provider's leaf spinlock serializes all access; no GPU callback. +unsafe impl Send for Demand {} +// SAFETY: shared access only changes that locked request, never its lifetime. +unsafe impl Sync for Demand {} + +impl Demand { + pub(super) fn new(dev: *mut kernel::bindings::device) -> kernel::error::Result { + let mut out = core::ptr::null_mut(); + // SAFETY: live platform device and writable result; C returns an owned reference. + let ret = unsafe { asahi_clpc_gpu_get(dev.cast(), &mut out) }; + if ret < 0 { + Err(kernel::error::Error::from_errno(ret)) + } else { + Ok(Self(out)) + } + } + + pub(super) fn set_active(&self, active: bool) { + // SAFETY: the retained reference is live; NULL denotes no DT provider. + unsafe { asahi_clpc_gpu_set_active(self.0, active) }; + } +} + +impl Drop for Demand { + fn drop(&mut self) { + // SAFETY: releases exactly the reference returned by get, clearing demand first. + unsafe { asahi_clpc_gpu_put(self.0) }; + } +} diff --git a/drivers/gpu/drm/asahi/g17/queue/compute/retirement.rs b/drivers/gpu/drm/asahi/g17/queue/compute/retirement.rs index 8a737546cb7cf..6b942dcd86a3e 100644 --- a/drivers/gpu/drm/asahi/g17/queue/compute/retirement.rs +++ b/drivers/gpu/drm/asahi/g17/queue/compute/retirement.rs @@ -94,13 +94,17 @@ pub(crate) fn record_aged(observed_at: Option, now: u64) -> bool { static STAMPLESS: core::sync::atomic::AtomicU64 = core::sync::atomic::AtomicU64::new(0); +pub(crate) fn stampless_total() -> u64 { + STAMPLESS.load(core::sync::atomic::Ordering::Relaxed) +} + /// Counts a command that retired on its record because its completion stamp never arrived; /// logs the first and every 64th. pub(crate) fn note_stampless(qid: u8, kick: u64) { let count = STAMPLESS.fetch_add(1, core::sync::atomic::Ordering::Relaxed) + 1; if count == 1 || count % 64 == 0 { pr_info!( - "G17: {} compute commands retired on their completion record after {} ms without a completion stamp (latest: queue {} kick {:#x})\n", + "G17: lifetime total {} compute commands retired on their completion record after {} ms without a completion stamp (latest: queue {} kick {:#x})\n", count, STAMP_FALLBACK_NS / 1_000_000, qid, @@ -112,7 +116,9 @@ pub(crate) fn note_stampless(qid: u8, kick: u64) { /// A zero-duration record fails before visibility polling. It proves execution did not occur; /// ownership still requires a separate retirement witness before mappings can be released. /// The command is complete when its stamp covers it, or when its exact completion record has -/// been present for [`STAMP_FALLBACK_NS`] without one (`record_aged`); in both cases its items +/// been present for [`STAMP_FALLBACK_NS`] without one (`record_aged`) in the same recovery +/// generation. Recovery may consume items without completing their writes, so an old record +/// and consumed items cannot replace the stamp across a restart. In both cases its items /// must be consumed. #[expect( clippy::too_many_arguments, @@ -131,7 +137,9 @@ pub(crate) fn poll( if observed.is_some_and(|record| record.timestamps[0] == record.timestamps[1]) { return Err(ENODATA); } - let completed = stamp_covers(stamp, ticket.kick) || (observed.is_some() && record_aged); + let same_generation = !matches!(recordless, Recordless::CrossedRecovery); + let completed = stamp_covers(stamp, ticket.kick) + || (same_generation && observed.is_some() && record_aged); if !completed || !cursors.consumed(ticket.item_producer, ticket.item_count)? { return Ok(None); } @@ -542,6 +550,30 @@ mod tests { assert_eq!(result, Ok(None)); } + #[test] + fn recovery_cannot_turn_an_aged_record_into_a_release_witness() { + let record = Some(Observation { timestamps: [10, 20] }); + // Recovery can advance the consumer without completing cache maintenance. + // Even a record observed before the restart and aged past the fallback + // must retain its command's mappings until a real stamp covers it. + for stamp in [0, own(KICK - 1)] { + assert_eq!(poll(&ticket(), record, consumed(), stamp, + Recordless::CrossedRecovery, 0, None, true), Ok(None)); + } + // A real stamp restores the release witness; completion's interrupted + // classification independently prevents false successful fences. + for stamp in [own(KICK), own(KICK + 1)] { + assert_eq!(poll(&ticket(), record, consumed(), stamp, + Recordless::CrossedRecovery, 0, None, true), Ok(Some([10, 20]))); + } + let busy = Cursors { producer: 4, consumer: 2, count: 64 }; + assert_eq!(poll(&ticket(), record, busy, own(KICK), + Recordless::CrossedRecovery, 0, None, true), Ok(None)); + // The existing same-generation fallback remains available. + assert_eq!(poll(&ticket(), record, consumed(), own(KICK - 1), + Recordless::Allowed, 0, None, true), Ok(Some([10, 20]))); + } + #[test] fn a_zero_duration_record_fails() { let record = Some(Observation { timestamps: [5, 5] }); diff --git a/drivers/gpu/drm/asahi/g17/queue/mod.rs b/drivers/gpu/drm/asahi/g17/queue/mod.rs index 16891524e25a0..d22c0f617f374 100644 --- a/drivers/gpu/drm/asahi/g17/queue/mod.rs +++ b/drivers/gpu/drm/asahi/g17/queue/mod.rs @@ -212,6 +212,9 @@ impl Queue { } // Only convert inputs when no independent host-prefix wait could keep // the retained source bookkeeping queued behind an external signal. + if engine == Engine::Compute { + super::trace::inputs(self.context.id(), order.sequence, 0, inputs.len(), None); + } let sources = if engine == Engine::Compute && !inputs.is_empty() && !destinations.has_outputs() && self.frontiers[0].iter().all(|(sequence, fences)| @@ -225,6 +228,9 @@ impl Queue { failure.as_ref().is_none_or(|(sequence, _)| !Order::contains(order.wait_through[index], *sequence))) { self.backend.input_sources(inputs) } else { None }; + if engine == Engine::Compute { + super::trace::inputs(self.context.id(), order.sequence, 1, inputs.len(), sources.as_deref()); + } // Only earlier render ordinals covered by the explicit logical prefix // can delay host publication. These links never become GPU dependencies. diff --git a/drivers/gpu/drm/asahi/g17/queue/render.rs b/drivers/gpu/drm/asahi/g17/queue/render.rs index 3261f2f0640b3..6fb4e233de8d6 100644 --- a/drivers/gpu/drm/asahi/g17/queue/render.rs +++ b/drivers/gpu/drm/asahi/g17/queue/render.rs @@ -587,12 +587,16 @@ impl Pair { let stamps = host.stamps(qids)?; fence(Ordering::Acquire); let covered = stamps.map(|stamp| retirement::stamp_covers(stamp, ordinal)); - self.memory - .completion_links(ordinal, kicks, payload, !covered[1])?; + self.memory.completion_links( + ordinal, + kicks, + payload, + self.variant.fragment_busy && !covered[1], + )?; let leads = match self.variant.lead_commands { true => { let init_manager = !self.memory.manager.initialized(); - self.memory.write_leads(ordinal, kicks[0], init_manager)? + self.memory.write_leads(ordinal, init_manager)? } false => [false; 2], }; @@ -1033,6 +1037,25 @@ impl Pair { }, ] } + /// Bounded first-fault evidence while the recovery owner has firmware + /// halted, before it acknowledges state 1. Never changes retirement. + pub(crate) fn report_recovery(&self, stamps: Result<[u32; 2]>) { + pr_warn!("G17: recovery render slot={} qids={:?} active={} prepared={} quarantined={} stamps={:?} freelist={:?}\n", + self.slot, self.qids(), self.active.len(), self.prepared.is_some(), + self.quarantined, stamps, self.free_list.counters()); + self.memory.manager.report_recovery(); + for active in self.active.iter().take(4) { + if let Validated::Render { pass, .. } = &active.packet.command { + pr_warn!("G17: recovery render geometry ordinal={} width={} height={} layers={} layout={:?}\n", + active.ticket.ordinal, pass.width, pass.height, pass.layers, + target::Layout::new(pass, self.clusters, self.variant.programs)); + } + pr_warn!("G17: recovery render ordinal={} published={} ctx={} seq={} snapshot={:?}\n", + active.ticket.ordinal, active.outer_published, active.packet.context.id(), + active.packet.order.sequence, observation::snapshot(&self.memory, active.ticket.ordinal)); + } + } + fn selected(&self, masks: Option<[u64; 2]>) -> bool { masks.is_none_or(|masks| completion_noted(self.qids(), masks)) } diff --git a/drivers/gpu/drm/asahi/g17/queue/render/memory.rs b/drivers/gpu/drm/asahi/g17/queue/render/memory.rs index 884537b99b6d8..90a11ca977684 100644 --- a/drivers/gpu/drm/asahi/g17/queue/render/memory.rs +++ b/drivers/gpu/drm/asahi/g17/queue/render/memory.rs @@ -31,8 +31,8 @@ pub(super) const ANNOUNCE: [usize; 2] = [0x17000, 0x1b000]; pub(super) const JOB_LIST: usize = 0xa000; pub(super) const CONFIG: [usize; 2] = [0, 0x4000]; pub(super) const CONFIG_STRIDE: usize = 0x180; -/// The lead command of a stage (InitBM before tiling, the barrier before fragment), after the -/// stage's configuration record in its slot ([`crate::g17::fw::Variant::lead_commands`]). +/// The lead command of a stage (InitBM before tiling), after the stage's configuration record +/// in its slot ([`crate::g17::fw::Variant::lead_commands`]). pub(super) const LEAD: usize = 0xc0; const DEPTH: usize = queues::RENDER_DEPTH as usize; /// Offsets of the priority pair and the policy word in a queue record. @@ -534,7 +534,6 @@ const _: () = { assert!(abi::FRAGMENT_SLOT_SIZE * RECORDS <= FRAGMENT_BYTES); assert!(RECORDS * CONFIG_STRIDE <= 0x4000); assert!(size_of::() <= LEAD); - assert!(LEAD + size_of::() <= CONFIG_STRIDE); assert!(LEAD + size_of::() <= CONFIG_STRIDE); }; @@ -648,14 +647,8 @@ impl Memory { } /// Writes the lead commands of `ordinal` from its linked descriptors: InitBM before the - /// tiling descriptor when `init_manager`, and the fragment barrier. Returns which stages - /// have one. - pub(super) fn write_leads( - &mut self, - ordinal: u64, - tiling_kick: fw::kick::KickTimestamp, - init_manager: bool, - ) -> Result<[bool; 2]> { + /// tiling descriptor when `init_manager`. Returns which stages have one. + pub(super) fn write_leads(&mut self, ordinal: u64, init_manager: bool) -> Result<[bool; 2]> { if !self.owns(ordinal) { return Err(EIO); } @@ -664,23 +657,10 @@ impl Memory { .descriptors .pointer(Graph::offset(0, ordinal), size_of::())? .cast::(); - let fragment = self - .graph - .descriptors - .pointer(Graph::offset(1, ordinal), size_of::())? - .cast::(); - // SAFETY: Both views were checked during preparation and the ordinal still owns them; - // only integer fields are read. - let (ta, fragment_id) = unsafe { (&*ta, (*fragment).descriptor.current_id) }; + // SAFETY: The view was checked during preparation and the ordinal still owns it; only + // integer fields are read. + let ta = unsafe { &*ta }; let slot = ordinal as usize % RECORDS * CONFIG_STRIDE + LEAD; - let barrier = queue::FragmentBarrier::new( - ta.stamp_va, - tiling_kick, - ta.qid, - ta.stamp_value, - fragment_id, - ); - self.graph.config.write(CONFIG[1] + slot, barrier)?; if init_manager { let init = queue::InitBufferManager::new( ta.context_id, @@ -690,7 +670,7 @@ impl Memory { ); self.graph.config.write(CONFIG[0] + slot, init)?; } - Ok([init_manager, true]) + Ok([init_manager, false]) } } diff --git a/drivers/gpu/drm/asahi/g17/recovery.rs b/drivers/gpu/drm/asahi/g17/recovery.rs index 3bb9d20190b57..dcd8d59e4c412 100644 --- a/drivers/gpu/drm/asahi/g17/recovery.rs +++ b/drivers/gpu/drm/asahi/g17/recovery.rs @@ -103,6 +103,8 @@ pub(super) trait Queues { /// partly executed, so it must never report success; see /// `Completion::note_interrupted`. fn note_interrupted(&mut self, qid: u8, stamp: u32) -> Interrupted; + /// Optional diagnostics only; absence cannot alter attribution or release. + fn report_blamed_render(&self, _qid: u8) {} fn settle_blamed_render(&mut self, qid: u8, error: Error); fn settle_cancelled_renders(&mut self, qids: u128); fn rescan_compute(&mut self) -> Result; @@ -244,6 +246,15 @@ pub(super) fn service( host.clear_timestamps()?; } let mut sources = host.fault_sources()?; + sources.report(request.generation, super::queue::compute::retirement::stampless_total()); + if let Some(qid) = request.blamed { + host.report_blamed_render(qid); + } + // The keys, monitor and pass-start evidence must describe the same halted + // instant. Firmware may update pass timestamps as soon as state 3 is + // acknowledged; sampling them during classification can blame a bystander. + let attribution = sources.attribute(request.blamed, + |qid, stamp| host.render_pass_started(qid, stamp)); let target_error = host.publish_recovery_targets(); host.transition(1, 2)?; let mut recovered = false; @@ -274,7 +285,7 @@ pub(super) fn service( host.recovery_mut().pending = None; let (listed, exact, fallback) = (entries.listed, entries.exact, entries.fallback); - let (cancelled, blame) = classify(host, entries, request.blamed, &mut sources); + let (cancelled, blame) = classify(host, entries, request.blamed, attribution); if listed != 0 { pr_warn!( "G17: GPU recovery: {} commands were in flight ({} matched by stamp, {} by queue); they fail instead of completing\n", @@ -329,10 +340,9 @@ fn read_entries(host: &mut H) -> Entries { /// Also returns whether the firmware's blamed QID is still guilty after attribution: a blamed /// render that had not started while others had (or with no culprit at all) is innocent, and its /// work is spared like that of every other innocent VM instead of failing with EIO. -fn classify(queues: &mut Q, entries: Entries, blamed: Option, sources: &mut Sources) -> (u128, bool) { +fn classify(queues: &mut Q, entries: Entries, blamed: Option, attribution: Attribution) -> (u128, bool) { let mut guilty_qids = entries.guilty | blamed.map_or(0, |qid| 1u128 << qid); let cancelled = entries.cancelled; - let attribution = sources.attribute(blamed, |qid, stamp| queues.render_pass_started(qid, stamp)); match attribution { Attribution::Firmware => {} Attribution::Queues(qids) => guilty_qids = qids, @@ -392,3 +402,102 @@ fn classify(queues: &mut Q, entries: Entries, blamed: Option, sou }); (cancelled & !(guilty_qids & !collateral) & spared, blame) } + + +#[kernel::macros::kunit_tests(asahi_g17_recovery_snapshot)] +mod tests { + use super::*; + use core::cell::Cell; + + struct Halted { + recovery: State, + state: Cell, + reads: Cell, + known_keys: bool, + blamed: bool, + } + + impl Halted { + fn new(known_keys: bool) -> Self { + let mut recovery = State::new(); + assert!(recovery.request(0, 38).unwrap()); + Self { recovery, state: Cell::new(1), reads: Cell::new(0), + known_keys, blamed: false } + } + } + + impl Memory for Halted { + fn recovery_state(&self) -> Result { Ok(self.state.get()) } + fn host_recovery(&self) -> Result { Ok(0) } + fn fault_sources(&self) -> Result { + assert_eq!(self.state.get(), 1); + let mut sources = Sources::sample(3, |slot| { + Some((if self.known_keys { ((38 + slot as u64) << 40) | 1 } else { 0 }, 0)) + }); + sources.reason = Some(2); + Ok(sources) + } + fn transition(&self, expected: u32, next: u32) -> Result { + assert_eq!(self.state.get(), expected); + self.state.set(next); + Ok(()) + } + fn clear_timestamps(&self) -> Result { panic!("active render scheduler") } + fn entry(&self, _index: usize) -> Result<(u32, u32)> { + assert_eq!(self.state.get(), 3); + Ok((0, 0)) + } + fn wait_tick(&self) { + assert_eq!(self.state.get(), 2); + self.state.set(3); + } + } + + impl Queues for Halted { + fn render_scheduler_active(&self) -> bool { true } + fn render_pass_started(&self, qid: u8, _stamp: u64) -> Option { + // This is the regression oracle: state 0/2/3 is too late. A + // resumed peer may have started after the fault we are attributing. + assert_eq!(self.state.get(), 1); + self.reads.set(self.reads.get() + 1); + Some(qid == 39) + } + fn publish_recovery_targets(&self) -> Option { None } + fn oldest_spared(&self, _qid: u8) -> Option { None } + fn visit_owners(&self, _qid: u8, _visit: &mut dyn FnMut(&Arc)) {} + fn classify_work(&mut self, _class: &dyn Fn(&Arc) -> Class) -> u128 { 0 } + fn note_interrupted(&mut self, _qid: u8, _stamp: u32) -> Interrupted { Interrupted::None } + fn settle_blamed_render(&mut self, qid: u8, _error: Error) { + assert_eq!(qid, 38); + self.blamed = true; + } + fn settle_cancelled_renders(&mut self, _qids: u128) {} + fn rescan_compute(&mut self) -> Result { Ok(()) } + fn release_retired_compute(&mut self) {} + fn announce_withheld(&mut self) {} + } + + impl Host for Halted { + fn recovery(&self) -> &State { &self.recovery } + fn recovery_mut(&mut self) -> &mut State { &mut self.recovery } + } + + #[test] + fn pass_evidence_is_frozen_before_the_first_acknowledgement() { + let mut host = Halted::new(true); + service(&mut host, || false).unwrap(); + assert_eq!(host.reads.get(), 2); + assert_eq!(host.state.get(), 0); + assert_eq!(host.recovery.generation(), 1); + assert!(!host.blamed); + } + + #[test] + fn unknown_25g83_keys_still_preserve_firmware_blame() { + let mut host = Halted::new(false); + service(&mut host, || false).unwrap(); + assert_eq!(host.reads.get(), 0); + assert!(host.blamed); + assert_eq!(host.state.get(), 0); + } +} diff --git a/drivers/gpu/drm/asahi/g17/recovery/attribution.rs b/drivers/gpu/drm/asahi/g17/recovery/attribution.rs index 1755484219187..38ad8612a0b99 100644 --- a/drivers/gpu/drm/asahi/g17/recovery/attribution.rs +++ b/drivers/gpu/drm/asahi/g17/recovery/attribution.rs @@ -30,6 +30,23 @@ pub(super) enum Attribution { } impl Sources { + /// Bounded evidence from the existing halted-firmware snapshot. No guessed + /// MMIO/key addresses or additional firmware reads are used for diagnostics. + pub(super) fn report(&self, generation: u64, stampless_total: u64) { + kernel::pr_warn!( + "G17: recovery snapshot gen {} reason {:?} slot_mask {:#x} sampled {} known_keys {}; stampless lifetime total {} before restart\n", + generation, self.reason, self.mask, self.count, + self.records[..self.count].iter().filter(|source| source.qid.is_some()).count(), + stampless_total + ); + for source in &self.records[..self.count] { + kernel::pr_warn!( + "G17: recovery slot {} queue {:?} kick {:#x} monitor {:#x}\n", + source.slot, source.qid, source.stamp, source.monitor + ); + } + } + /// Firmware is halted for every selected key and progress-record read. /// Failed reads stay unknown; incomplete evidence cannot override firmware blame. pub(crate) fn sample(mask: u32, mut read: impl FnMut(usize) -> Option<(u64, u32)>) -> Self { @@ -64,7 +81,10 @@ impl Sources { blamed: Option, mut started: impl FnMut(u8, u64) -> Option, ) -> Attribution { - if self.reason.is_none() || self.count as u32 != self.mask.count_ones() { + if self.reason.is_none() + || self.count as u32 != self.mask.count_ones() + || self.records[..self.count].iter().any(|source| source.qid.is_none()) + { return Attribution::Firmware; } for source in &mut self.records[..self.count] { @@ -103,3 +123,54 @@ impl Sources { } } } + +#[kernel::macros::kunit_tests(asahi_g17_recovery_sources)] +mod tests { + use super::*; + + #[test] + fn progress_without_keys_preserves_firmware_blame() { + let mut reads = 0; + let mut sources = Sources::sample(0b101, |slot| { + reads += 1; + Some((0, (slot + 1) as u32)) + }); + sources.reason = Some(2); + assert_eq!(reads, 2); + assert_eq!(sources.count, 2); + assert_eq!(sources.records[1].slot, 2); + assert_eq!(sources.records[1].monitor, 3); + assert!(sources.records[..sources.count].iter().all(|source| source.qid.is_none())); + assert_eq!(sources.attribute(Some(38), |_, _| panic!("unknown key")), + Attribution::Firmware); + } + + #[test] + fn incomplete_progress_cannot_clear_firmware_blame() { + let mut sources = Sources::sample(0b11, |slot| { + (slot == 0).then_some(((38 << 40) | 1, 0)) + }); + sources.reason = Some(2); + assert_eq!(sources.attribute(Some(38), |_, _| Some(false)), Attribution::Firmware); + } + + #[test] + fn a_missing_peer_key_cannot_clear_a_known_queues_blame() { + let mut sources = Sources::sample(0b11, |slot| { + Some((if slot == 0 { (38 << 40) | 1 } else { 0 }, 0)) + }); + sources.reason = Some(2); + assert_eq!(sources.count, 2); + assert_eq!(sources.attribute(Some(38), |_, _| Some(false)), Attribution::Firmware); + } + + #[test] + fn page_fault_keeps_firmware_blame_with_complete_keys() { + let mut sources = Sources::sample(0b11, |slot| { + Some(((((38 + slot) as u64) << 40) | 1, 0)) + }); + sources.reason = Some(PAGE_FAULT); + assert_eq!(sources.attribute(Some(38), |qid, _| Some(qid == 39)), + Attribution::Firmware); + } +} diff --git a/drivers/gpu/drm/asahi/g17/reports.rs b/drivers/gpu/drm/asahi/g17/reports.rs new file mode 100644 index 0000000000000..b67512f7eec6b --- /dev/null +++ b/drivers/gpu/drm/asahi/g17/reports.rs @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT + +//! Preserve the firmware's statistics without assigning unqualified units or +//! treating another firmware's pstate report as an achieved GPU frequency. +//! Writers and readers hold the device mutex; reads never power the GPU up. + +use super::{fw::channels::{EVENT_WORDS, STATISTICS_TYPES}, Role}; +use kernel::prelude::*; + +#[derive(Clone, Copy, Default)] +struct Sample { + count: u64, + host_ns: u64, + words: [u32; EVENT_WORDS], +} + +pub(super) struct Reports { + samples: KVec, +} + +impl Reports { + pub(super) fn new() -> Result { + // Allocate before firmware starts; receive and debugfs paths allocate nothing. + let mut samples = KVec::with_capacity(64, GFP_KERNEL)?; + for _ in 0..64 { + samples.push(Sample::default(), GFP_KERNEL)?; + } + Ok(Self { samples }) + } + + pub(super) fn record(&mut self, role: Role, words: [u32; EVENT_WORDS], host_ns: u64) { + let tag = words[0]; + if tag >= u32::BITS || STATISTICS_TYPES & (1 << tag) == 0 { + return; + } + let sample = &mut self.samples[role as usize * 32 + tag as usize]; + sample.count = sample.count.saturating_add(1); + sample.host_ns = host_ns; + sample.words = words; + } + + pub(super) fn write(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result { + writeln!(f, "format=1 units=raw host_time=monotonic_ns achieved_frequency=unqualified")?; + for (index, sample) in self.samples.iter().enumerate().filter(|(_, s)| s.count != 0) { + writeln!(f, "role={} tag={:#x} count={} host_ns={} words={:08x?}", + index / 32, index % 32, sample.count, sample.host_ns, sample.words)?; + } + Ok(()) + } +} + +/// The view owns the session, not a pointer into a detachable Firmware. +pub(super) struct View(pub(super) kernel::sync::Arc); + +impl kernel::debugfs::Writer for View { + fn write(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result { + let state = self.0.state.lock(); + let Some(firmware) = state.as_deref() else { return writeln!(f, "online=0"); }; + writeln!(f, "online=1 graph={:?} sampled_ns={}", self.0.variant.graph, super::now_ns())?; + write_power(firmware, f)?; + firmware.rings.write_reports(f) + } +} + +fn write_power(firmware: &super::Firmware, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result { + let core_power = firmware.bridge.regs.g17_core_power(); + writeln!(f, "power_management=firmware sampled_ns={} core_power={:#x} cores_powered={} host_hold={} idle_hold_ms={}", + super::now_ns(), core_power, core_power & 0xf != 0, firmware.hold.active(), + super::power::IDLE_HOLD_MS.load(core::sync::atomic::Ordering::Relaxed))?; + firmware.port.write_power(f) +} + +/// Actual firmware-managed core gating, separate from generic Linux runtime PM. +pub(super) struct PowerView(pub(super) kernel::sync::Arc); + +impl kernel::debugfs::Writer for PowerView { + fn write(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result { + let state = self.0.state.lock(); + let Some(firmware) = state.as_deref() else { return writeln!(f, "online=0"); }; + write_power(firmware, f) + } +} + +#[kernel::macros::kunit_tests(asahi_g17_reports)] +mod tests { + use super::*; + + #[test] + fn retains_all_words_and_separates_role_and_tag() { + let mut reports = Reports::new().unwrap(); + let mut words = [0; EVENT_WORDS]; + words[0] = 16; + words[17] = 0xdeadbeef; + reports.record(Role::Primary, words, 100); + reports.record(Role::Primary, words, 101); + reports.record(Role::Secondary, words, 102); + assert_eq!(reports.samples[16].count, 2); + assert_eq!(reports.samples[16].host_ns, 101); + assert_eq!(reports.samples[16].words, words); + assert_eq!(reports.samples[48].count, 1); + words[0] = 17; + reports.record(Role::Primary, words, 103); + assert_eq!(reports.samples[17].count, 1); + assert_eq!(reports.samples[16].count, 2); + } + + #[test] + fn rejects_invalid_tags_before_indexing() { + let mut reports = Reports::new().unwrap(); + for tag in [0, 15, 29, 31, 32, u32::MAX] { + let mut words = [0; EVENT_WORDS]; + words[0] = tag; + reports.record(Role::Primary, words, 1); + } + assert!(reports.samples.iter().all(|s| s.count == 0)); + } +} diff --git a/drivers/gpu/drm/asahi/g17/runtime/events.rs b/drivers/gpu/drm/asahi/g17/runtime/events.rs index 16bcb50523d65..74eb6b7228c72 100644 --- a/drivers/gpu/drm/asahi/g17/runtime/events.rs +++ b/drivers/gpu/drm/asahi/g17/runtime/events.rs @@ -47,7 +47,7 @@ impl event::Transport for Service<'_> { role: Role, mut consume: impl FnMut(&mut Self, Event) -> Result, ) -> Result { - let mut events = channel::Events::new(role); + let mut events = channel::Events::new(role, self.firmware.primary.state.shared.variant.graph); while let Some(event) = events.next(&self.firmware.primary.state.shared.dev, &self.firmware.init)? { @@ -231,6 +231,9 @@ impl recovery::Queues for Service<'_> { queue.note_interrupted(stamp) }) } + fn report_blamed_render(&self, qid: u8) { + self.firmware.queues.report_render_recovery(qid, &self.firmware.port); + } fn settle_blamed_render(&mut self, qid: u8, error: Error) { let result = self .firmware diff --git a/drivers/gpu/drm/asahi/g17/runtime/render/grow.rs b/drivers/gpu/drm/asahi/g17/runtime/render/grow.rs index 20569af84bc7b..277534328406e 100644 --- a/drivers/gpu/drm/asahi/g17/runtime/render/grow.rs +++ b/drivers/gpu/drm/asahi/g17/runtime/render/grow.rs @@ -14,8 +14,8 @@ use kernel::prelude::*; const RETRY_LIMIT: u16 = 1000; pub(super) struct Grow { - requests: KVec, - active: Option, + pub(super) requests: KVec, + pub(super) active: Option, pub(super) reply: Option<(TvbGrowRequest, bool, u16)>, release_attempts: u16, } diff --git a/drivers/gpu/drm/asahi/g17/runtime/render/recovery.rs b/drivers/gpu/drm/asahi/g17/runtime/render/recovery.rs index 3a5b042fff683..7a6d987591c76 100644 --- a/drivers/gpu/drm/asahi/g17/runtime/render/recovery.rs +++ b/drivers/gpu/drm/asahi/g17/runtime/render/recovery.rs @@ -6,6 +6,22 @@ use crate::g17::runtime::Registry; use kernel::prelude::*; impl Registry { + pub(in crate::g17::runtime) fn report_render_recovery(&self, qid: u8, port: &crate::g17::port::Port) { + pr_warn!("G17: recovery grow pending={} active={:?} reply={:?}\n", + self.render.grow.requests.len(), self.render.grow.active, self.render.grow.reply); + for entry in self.render.entries.iter().flatten().filter(|entry| { + entry.reservation.ids.iter().any(|id| id.qid() == qid) + }) { + if let Some(pair) = &entry.pair { + let qids = pair.qids(); + pair.report_recovery((|| Ok([port.stamp(qids[0])?, port.stamp(qids[1])?]))()); + } else { + pr_warn!("G17: recovery render queue={} unavailable borrowed={} constructing={}\n", + qid, entry.borrowed_active, entry.constructing); + } + } + } + pub(in crate::g17::runtime) fn render_pass_started(&self, qid: u8, stamp: u64) -> Option { self.render.entries.iter().flatten() .filter_map(|entry| entry.pair.as_ref()) diff --git a/drivers/gpu/drm/asahi/g17/trace.c b/drivers/gpu/drm/asahi/g17/trace.c index 9e93291f5114e..62307838bb36e 100644 --- a/drivers/gpu/drm/asahi/g17/trace.c +++ b/drivers/gpu/drm/asahi/g17/trace.c @@ -60,3 +60,28 @@ void asahi_g17_trace_teardown(u8 kind, u64 a, u64 b, u64 c) if (trace_asahi_g17_teardown_enabled()) trace_asahi_g17_teardown(kind, a, b, c); } + +bool asahi_g17_trace_report_enabled(void) +{ + return trace_asahi_g17_report_enabled(); +} + +void asahi_g17_trace_report(u8 role, u8 ring, const u32 *words) +{ + trace_asahi_g17_report(role, ring, words, __arch_counter_get_cntvct()); +} + +void asahi_g17_trace_inputs(u32 ctx, u64 seq, u8 phase, u32 inputs, u32 sources, + u32 waits, bool converted) +{ + if (trace_asahi_g17_inputs_enabled()) + trace_asahi_g17_inputs(ctx, seq, phase, inputs, sources, waits, + converted, __arch_counter_get_cntvct()); +} + +void asahi_g17_trace_schedule(u8 engine, u32 ctx, u64 seq, u8 phase, s32 detail) +{ + if (trace_asahi_g17_schedule_enabled()) + trace_asahi_g17_schedule(engine, ctx, seq, phase, detail, + __arch_counter_get_cntvct()); +} diff --git a/drivers/gpu/drm/asahi/g17/trace.h b/drivers/gpu/drm/asahi/g17/trace.h index d1529a2775fcc..c8cecbcc7f400 100644 --- a/drivers/gpu/drm/asahi/g17/trace.h +++ b/drivers/gpu/drm/asahi/g17/trace.h @@ -158,6 +158,83 @@ TRACE_EVENT(asahi_g17_event, TP_printk("phase=%u counter=%llu", __entry->phase, __entry->counter) ); +/* Input conversion: phase 0 entry, 1 result. converted=0 also covers no live input. */ +TRACE_EVENT(asahi_g17_inputs, + TP_PROTO(u32 ctx, u64 seq, u8 phase, u32 inputs, u32 sources, u32 waits, + bool converted, u64 counter), + TP_ARGS(ctx, seq, phase, inputs, sources, waits, converted, counter), + TP_STRUCT__entry( + __field(u32, ctx) + __field(u64, seq) + __field(u8, phase) + __field(u32, inputs) + __field(u32, sources) + __field(u32, waits) + __field(bool, converted) + __field(u64, counter) + ), + TP_fast_assign( + __entry->ctx = ctx; + __entry->seq = seq; + __entry->phase = phase; + __entry->inputs = inputs; + __entry->sources = sources; + __entry->waits = waits; + __entry->converted = converted; + __entry->counter = counter; + ), + TP_printk("ctx=%u seq=%llu phase=%u inputs=%u sources=%u waits=%u converted=%u counter=%llu", + __entry->ctx, __entry->seq, __entry->phase, __entry->inputs, + __entry->sources, __entry->waits, __entry->converted, __entry->counter) +); + +/* phase: 0 enqueue, 1 inline published, 2 scheduler fallback, 3 run begin, + * 4 run end, 5 prepare begin, 6 prepare end. detail: dependencies, error or wait. */ +TRACE_EVENT(asahi_g17_schedule, + TP_PROTO(u8 engine, u32 ctx, u64 seq, u8 phase, s32 detail, u64 counter), + TP_ARGS(engine, ctx, seq, phase, detail, counter), + TP_STRUCT__entry( + __field(u8, engine) + __field(u32, ctx) + __field(u64, seq) + __field(u8, phase) + __field(s32, detail) + __field(u64, counter) + ), + TP_fast_assign( + __entry->engine = engine; + __entry->ctx = ctx; + __entry->seq = seq; + __entry->phase = phase; + __entry->detail = detail; + __entry->counter = counter; + ), + TP_printk("engine=%u ctx=%u seq=%llu phase=%u detail=%d counter=%llu", + __entry->engine, __entry->ctx, __entry->seq, __entry->phase, + __entry->detail, __entry->counter) +); + +/* Raw 18-word records: trace=0, statistics=1, event=2. No unit inference. */ +TRACE_EVENT(asahi_g17_report, + TP_PROTO(u8 role, u8 ring, const u32 *words, u64 counter), + TP_ARGS(role, ring, words, counter), + TP_STRUCT__entry( + __field(u8, role) + __field(u8, ring) + __array(u32, words, 18) + __field(u64, counter) + ), + TP_fast_assign( + __entry->role = role; + __entry->ring = ring; + memcpy(__entry->words, words, sizeof(__entry->words)); + __entry->counter = counter; + ), + TP_printk("role=%u ring=%u words=%s counter=%llu", __entry->role, + __entry->ring, __print_array(__entry->words, 18, sizeof(u32)), + __entry->counter) +); + #endif #undef TRACE_INCLUDE_PATH diff --git a/drivers/gpu/drm/asahi/g17/trace.rs b/drivers/gpu/drm/asahi/g17/trace.rs index d5f61f50a93f6..78d88db4718c6 100644 --- a/drivers/gpu/drm/asahi/g17/trace.rs +++ b/drivers/gpu/drm/asahi/g17/trace.rs @@ -90,3 +90,49 @@ pub(super) fn event(phase: u8) { // SAFETY: Plain values only. unsafe { asahi_g17_trace_event(phase) }; } + +unsafe extern "C" { + fn asahi_g17_trace_report_enabled() -> bool; + fn asahi_g17_trace_report(role: u8, ring: u8, words: *const u32); +} + +/// Copies a complete firmware record while the host still owns its slot. +/// Static-key gated; no MMIO reads or firmware requests are issued. +pub(super) fn report(role: u8, ring: u8, words: &[u32; super::fw::channels::EVENT_WORDS]) { + // SAFETY: Pure static-key query. + if !unsafe { asahi_g17_trace_report_enabled() } { + return; + } + // SAFETY: C synchronously copies the complete initialized 18-word array. + unsafe { asahi_g17_trace_report(role, ring, words.as_ptr()) }; +} + + +unsafe extern "C" { + fn asahi_g17_trace_inputs(ctx: u32, seq: u64, phase: u8, inputs: u32, + sources: u32, waits: u32, converted: bool); + fn asahi_g17_trace_schedule(engine: u8, ctx: u32, seq: u64, phase: u8, detail: i32); +} + +/// Input conversion boundaries. No fence status is polled for tracing. +pub(super) fn inputs(ctx: u32, seq: u64, phase: u8, inputs: usize, + sources: Option<&super::sync_input::Sources>) +{ + let (count, waits) = sources.map_or((0, 0), + |s| (s.sources.len(), s.publication_waits().len())); + // SAFETY: Bounded UAPI counts and plain values only. C reads the timer only + // behind this event's static key; no GPU registers or pointers are exposed. + unsafe { asahi_g17_trace_inputs(ctx, seq, phase, inputs as u32, + count as u32, waits as u32, sources.is_some()) }; +} + +/// Host scheduling boundaries, joined to publish/complete by engine/context/sequence. +pub(super) fn schedule(packet: &super::job::Packet, phase: u8, detail: i32) { + let engine = match packet.command { + super::command::Validated::Render { .. } => RENDER, + super::command::Validated::Compute { .. } => COMPUTE, + }; + // SAFETY: Plain values only; counter sampling is static-key gated in C. + unsafe { asahi_g17_trace_schedule(engine, packet.context.id(), + packet.order.sequence, phase, detail) }; +} diff --git a/drivers/gpu/drm/asahi/g17/trace_api.h b/drivers/gpu/drm/asahi/g17/trace_api.h index 0049aa0079634..cd4d360c55595 100644 --- a/drivers/gpu/drm/asahi/g17/trace_api.h +++ b/drivers/gpu/drm/asahi/g17/trace_api.h @@ -22,4 +22,11 @@ void asahi_g17_trace_message(u8 kind, u64 value); void asahi_g17_trace_event(u8 phase); void asahi_g17_trace_teardown(u8 kind, u64 a, u64 b, u64 c); +void asahi_g17_trace_inputs(u32 ctx, u64 seq, u8 phase, u32 inputs, u32 sources, + u32 waits, bool converted); +void asahi_g17_trace_schedule(u8 engine, u32 ctx, u64 seq, u8 phase, s32 detail); + +bool asahi_g17_trace_report_enabled(void); +void asahi_g17_trace_report(u8 role, u8 ring, const u32 *words); + #endif /* _ASAHI_G17_TRACE_API_H */ diff --git a/drivers/gpu/drm/asahi/hw/t6040.rs b/drivers/gpu/drm/asahi/hw/t6040.rs index c6ea928630e56..11ed37a7c1ef8 100644 --- a/drivers/gpu/drm/asahi/hw/t6040.rs +++ b/drivers/gpu/drm/asahi/hw/t6040.rs @@ -7,7 +7,7 @@ use crate::g17::fw::{ self, - initdata::{v25g83, PerfConfig, RegionCConfig, RegisterWindow, PERF_STATES}, + initdata::{PerfConfig, RegionCConfig, RegisterWindow}, }; use crate::g17::{Config, CoreLayout, CrashBuffers, FixedBuffer, Identity}; use crate::hw::{GpuGen, GpuVariant}; @@ -180,33 +180,23 @@ pub(crate) const REGISTER_WINDOWS: [RegisterWindow; 25] = [ window(43, 0x2_88e6_0000, 0x47_4000, 0x58, 0xe6_0000, 2), ]; -/// Performance-table configuration. The device tree lists the sixteen operating points in -/// performance-state order, with one voltage per cluster. +/// Inert defaults. The 25G83 builder requires the loader's per-chip performance maps. pub(crate) const PERF: PerfConfig = PerfConfig { - states_a: [0, 1, 2, 3, 4, 6, 8, 10, 12, 14, 15], - states_b: [0, 1, 2, 3, 4, 6, 7, 9, 11, 13, 15], - relative_a: [0, 9, 16, 23, 29, 38, 49, 60, 69, 79, 100], - relative_b: [0, 0, 22, 36, 47, 57, 68, 78, 84, 91, 100], + states_a: [0; 11], + states_b: [0; 11], + relative_a: [0; 11], + relative_b: [0; 11], sram_min_mv: 810, }; /// Operating points the performance tables reference. pub(crate) const OPPS: usize = 16; -const _: () = { - let mut index = 0; - while index < PERF_STATES { - assert!((PERF.states_a[index] as usize) < OPPS); - assert!((PERF.states_b[index] as usize) < OPPS); - index += 1; - } -}; - /// Region C. const REGION_C: RegionCConfig = RegionCConfig { unk_54: 0x0fa0, unk_78: [1, 0, 0], - max_power_mw: v25g83::MAX_POWER_MW, + max_power_mw: 0, unk_9c: 0, unk_a0: 0, unk_7c4: 1500, diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 54eb5745105ff..9b6b0593310da 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1252,10 +1252,13 @@ config SENSORS_MACSMC_HWMON_KUNIT_TEST depends on SENSORS_MACSMC_HWMON && KUNIT=y default KUNIT_ALL_TESTS help - Unit tests for the channel numbering of the Apple SMC hwmon driver. + Unit tests for the channel numbering and the manual fan control of + the Apple SMC hwmon driver. Thermal zones refer to temperature channels by index, so a sensor that cannot be set up must keep its channel; the tests check that. - No SMC is needed. Only useful for kernel developers. + No SMC is needed: the fan tests model its fan keys and need + MFD_MACSMC_KUNIT_TEST for its read and write stubs. Only useful for + kernel developers. config SENSORS_MAX1111 tristate "Maxim MAX1111 Serial 8-bit ADC chip and compatibles" diff --git a/drivers/hwmon/macsmc-hwmon-test.c b/drivers/hwmon/macsmc-hwmon-test.c index e7239986ce3c9..596e904a6f3e8 100644 --- a/drivers/hwmon/macsmc-hwmon-test.c +++ b/drivers/hwmon/macsmc-hwmon-test.c @@ -1,11 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-only OR MIT /* * KUnit tests for the Apple SMC hwmon driver: channel numbering of the - * temperature sensors, which thermal zones depend on. + * temperature sensors, which thermal zones depend on, and manual fan control. * * Included by macsmc-hwmon.c. */ +#include #include /* What macsmc_hwmon_create_sensor() leaves behind for one node. */ @@ -84,4 +85,261 @@ static struct kunit_suite macsmc_hwmon_test_suite = { .test_cases = macsmc_hwmon_test_cases, }; -kunit_test_suite(macsmc_hwmon_test_suite); +/* + * Manual fan control against a model of the SMC fan keys. In "locked" firmware + * the fans start in system mode (3) and refuse manual mode until Ftst is 1; + * Ftst back to 0 returns them to system mode. "Legacy" firmware takes the mode + * write directly. The model needs the SMC read/write stubs of + * CONFIG_MFD_MACSMC_KUNIT_TEST. + */ +#define FAN_TEST_F1350 0x44a8c000 /* 1350.0f */ +#define FAN_TEST_F2500 0x451c4000 /* 2500.0f */ +#define FAN_TEST_F5777 0x45b48800 /* 5777.0f */ + +struct macsmc_fan_test { + struct apple_smc smc; + struct kunit *test; + struct macsmc_hwmon hwmon; + struct macsmc_hwmon_fan fans[2]; + bool legacy; + u8 mode[2]; + u32 target[2]; + u8 ftst; + int ftst_writes; + int refused; + smc_key fail_key; +}; + +static int fan_test_index(smc_key key) +{ + return ((key >> 16) & 0xff) - '0'; +} + +static int fan_test_read(struct apple_smc *smc, smc_key key, void *buf, size_t size) +{ + struct macsmc_fan_test *t = container_of(smc, struct macsmc_fan_test, smc); + u32 value; + int i = fan_test_index(key); + + switch (key & 0xff00ffff) { + case __SMC_KEY('F', 0, 'M', 'd'): + value = t->mode[i]; + break; + case __SMC_KEY('F', 0, 'M', 'n'): + value = FAN_TEST_F1350; + break; + case __SMC_KEY('F', 0, 'M', 'x'): + value = FAN_TEST_F5777; + break; + case __SMC_KEY('F', 0, 'T', 'g'): + value = t->target[i]; + break; + default: + KUNIT_FAIL(t->test, "unexpected read of %08x", key); + return -EINVAL; + } + memcpy(buf, &value, size); + return size; +} + +static int fan_test_write(struct apple_smc *smc, smc_key key, const void *buf, size_t size) +{ + struct macsmc_fan_test *t = container_of(smc, struct macsmc_fan_test, smc); + u32 value = 0; + int i = fan_test_index(key); + + memcpy(&value, buf, min(size, sizeof(value))); + if (key == t->fail_key) + return -EIO; + + if (key == SMC_KEY(Ftst)) { + t->ftst_writes++; + t->ftst = value; + for (i = 0; i < ARRAY_SIZE(t->mode); i++) { + if (value && t->mode[i] == MACSMC_FAN_MODE_SYSTEM) + t->mode[i] = MACSMC_FAN_MODE_AUTO; + else if (!value) + t->mode[i] = MACSMC_FAN_MODE_SYSTEM; + } + return size; + } + + switch (key & 0xff00ffff) { + case __SMC_KEY('F', 0, 'M', 'd'): + if (!t->legacy && t->mode[i] == MACSMC_FAN_MODE_SYSTEM) { + t->refused++; + return -EIO; + } + t->mode[i] = value; + return size; + case __SMC_KEY('F', 0, 'T', 'g'): + t->target[i] = value; + return size; + } + KUNIT_FAIL(t->test, "unexpected write of %08x", key); + return -EINVAL; +} + +static void fan_test_key(struct macsmc_hwmon_sensor *sensor, const char *key, u32 type) +{ + sensor->macsmc_key = _SMC_KEY(key); + sensor->info.type_code = type; + sensor->info.flags = APPLE_SMC_READABLE | APPLE_SMC_WRITABLE; +} + +static int macsmc_fan_test_init(struct kunit *test) +{ + static const char *const keys[2][5] = { + { "F0Ac", "F0Mn", "F0Mx", "F0Tg", "F0Md" }, + { "F1Ac", "F1Mn", "F1Mx", "F1Tg", "F1Md" }, + }; + const u32 flt = __SMC_KEY('f', 'l', 't', ' '), ui8 = __SMC_KEY('u', 'i', '8', ' '); + struct macsmc_fan_test *t; + int i; + + if (!IS_ENABLED(CONFIG_MFD_MACSMC_KUNIT_TEST)) + kunit_skip(test, "needs CONFIG_MFD_MACSMC_KUNIT_TEST for the SMC stubs"); + + t = kunit_kzalloc(test, sizeof(*t), GFP_KERNEL); + if (!t) + return -ENOMEM; + t->test = test; + for (i = 0; i < 2; i++) { + fan_test_key(&t->fans[i].now, keys[i][0], flt); + fan_test_key(&t->fans[i].min, keys[i][1], flt); + fan_test_key(&t->fans[i].max, keys[i][2], flt); + fan_test_key(&t->fans[i].set, keys[i][3], flt); + fan_test_key(&t->fans[i].mode, keys[i][4], ui8); + t->mode[i] = MACSMC_FAN_MODE_SYSTEM; + } + t->hwmon.smc = &t->smc; + t->hwmon.fan.fans = t->fans; + t->hwmon.fan.count = 2; + t->hwmon.fan.unlock_key = SMC_KEY(Ftst); + mutex_init(&t->hwmon.fan.lock); + test->priv = t; + fan_control = true; + kunit_activate_static_stub(test, apple_smc_read, fan_test_read); + kunit_activate_static_stub(test, apple_smc_write, fan_test_write); + return 0; +} + +static void macsmc_fan_test_exit(struct kunit *test) +{ + fan_control = false; +} + +static void fan_test_released(struct kunit *test, struct macsmc_fan_test *t) +{ + KUNIT_EXPECT_EQ(test, t->ftst, 0); + KUNIT_EXPECT_EQ(test, t->mode[0], MACSMC_FAN_MODE_SYSTEM); + KUNIT_EXPECT_EQ(test, t->mode[1], MACSMC_FAN_MODE_SYSTEM); + KUNIT_EXPECT_EQ(test, t->hwmon.fan.manual, 0); + KUNIT_EXPECT_FALSE(test, t->hwmon.fan.unlocked); + KUNIT_EXPECT_FALSE(test, t->fans[0].manual); +} + +/* Locked firmware: unlock on the first manual request, release on auto. */ +static void macsmc_fan_test_unlock_and_release(struct kunit *test) +{ + struct macsmc_fan_test *t = test->priv; + + KUNIT_ASSERT_EQ(test, macsmc_hwmon_set_fan(&t->hwmon, 0, 2500), 0); + KUNIT_EXPECT_EQ(test, t->ftst, 1); + KUNIT_EXPECT_EQ(test, t->mode[0], MACSMC_FAN_MODE_MANUAL); + KUNIT_EXPECT_EQ(test, t->target[0], FAN_TEST_F2500); + KUNIT_EXPECT_EQ(test, t->hwmon.fan.manual, 1); + + /* A second target needs no new unlock. */ + KUNIT_ASSERT_EQ(test, macsmc_hwmon_set_fan(&t->hwmon, 0, 2500), 0); + KUNIT_EXPECT_EQ(test, t->ftst_writes, 1); + + KUNIT_ASSERT_EQ(test, macsmc_hwmon_set_fan(&t->hwmon, 0, 0), 0); + fan_test_released(test, t); + KUNIT_EXPECT_EQ(test, t->ftst_writes, 2); +} + +/* Targets are clamped to [min, max]; negative ones are refused. */ +static void macsmc_fan_test_clamp(struct kunit *test) +{ + struct macsmc_fan_test *t = test->priv; + + KUNIT_ASSERT_EQ(test, macsmc_hwmon_set_fan(&t->hwmon, 0, 100), 0); + KUNIT_EXPECT_EQ(test, t->target[0], FAN_TEST_F1350); + KUNIT_ASSERT_EQ(test, macsmc_hwmon_set_fan(&t->hwmon, 0, 100000), 0); + KUNIT_EXPECT_EQ(test, t->target[0], FAN_TEST_F5777); + KUNIT_EXPECT_EQ(test, macsmc_hwmon_set_fan(&t->hwmon, 0, -1), -EINVAL); + KUNIT_ASSERT_EQ(test, macsmc_hwmon_set_fan(&t->hwmon, 0, 0), 0); + fan_test_released(test, t); +} + +/* A failed target write hands the fan straight back. */ +static void macsmc_fan_test_target_write_fails(struct kunit *test) +{ + struct macsmc_fan_test *t = test->priv; + + t->fail_key = SMC_KEY(F0Tg); + KUNIT_EXPECT_EQ(test, macsmc_hwmon_set_fan(&t->hwmon, 0, 2500), -EIO); + fan_test_released(test, t); +} + +/* Two manual fans: the unlock is released only with the last one. */ +static void macsmc_fan_test_two_fans(struct kunit *test) +{ + struct macsmc_fan_test *t = test->priv; + + KUNIT_ASSERT_EQ(test, macsmc_hwmon_set_fan(&t->hwmon, 0, 2500), 0); + KUNIT_ASSERT_EQ(test, macsmc_hwmon_set_fan(&t->hwmon, 1, 2500), 0); + KUNIT_EXPECT_EQ(test, t->hwmon.fan.manual, 2); + KUNIT_ASSERT_EQ(test, macsmc_hwmon_set_fan(&t->hwmon, 0, 0), 0); + KUNIT_EXPECT_EQ(test, t->ftst, 1); + KUNIT_EXPECT_EQ(test, t->mode[1], MACSMC_FAN_MODE_MANUAL); + /* Removal, shutdown and suspend all restore through this. */ + macsmc_hwmon_fans_restore(&t->hwmon); + fan_test_released(test, t); +} + +/* Locked firmware without the unlock key: refused, nothing else written. */ +static void macsmc_fan_test_no_unlock_key(struct kunit *test) +{ + struct macsmc_fan_test *t = test->priv; + + t->hwmon.fan.unlock_key = 0; + KUNIT_EXPECT_EQ(test, macsmc_hwmon_set_fan(&t->hwmon, 0, 2500), -EIO); + KUNIT_EXPECT_EQ(test, t->ftst_writes, 0); + KUNIT_EXPECT_EQ(test, t->mode[0], MACSMC_FAN_MODE_SYSTEM); + KUNIT_EXPECT_EQ(test, t->hwmon.fan.manual, 0); +} + +/* Firmware that takes the mode directly keeps the mode 0 restore. */ +static void macsmc_fan_test_legacy(struct kunit *test) +{ + struct macsmc_fan_test *t = test->priv; + + t->legacy = true; + t->mode[0] = MACSMC_FAN_MODE_AUTO; + KUNIT_ASSERT_EQ(test, macsmc_hwmon_set_fan(&t->hwmon, 0, 2500), 0); + KUNIT_EXPECT_EQ(test, t->mode[0], MACSMC_FAN_MODE_MANUAL); + KUNIT_ASSERT_EQ(test, macsmc_hwmon_set_fan(&t->hwmon, 0, 0), 0); + KUNIT_EXPECT_EQ(test, t->mode[0], MACSMC_FAN_MODE_AUTO); + KUNIT_EXPECT_EQ(test, t->ftst_writes, 0); + KUNIT_EXPECT_EQ(test, t->refused, 0); +} + +static struct kunit_case macsmc_fan_test_cases[] = { + KUNIT_CASE(macsmc_fan_test_unlock_and_release), + KUNIT_CASE(macsmc_fan_test_clamp), + KUNIT_CASE(macsmc_fan_test_target_write_fails), + KUNIT_CASE(macsmc_fan_test_two_fans), + KUNIT_CASE(macsmc_fan_test_no_unlock_key), + KUNIT_CASE(macsmc_fan_test_legacy), + {} +}; + +static struct kunit_suite macsmc_fan_test_suite = { + .name = "macsmc-hwmon-fan", + .init = macsmc_fan_test_init, + .exit = macsmc_fan_test_exit, + .test_cases = macsmc_fan_test_cases, +}; +kunit_test_suites(&macsmc_hwmon_test_suite, &macsmc_fan_test_suite); diff --git a/drivers/hwmon/macsmc-hwmon.c b/drivers/hwmon/macsmc-hwmon.c index 06d2c6e2d0ac3..76f07add003d3 100644 --- a/drivers/hwmon/macsmc-hwmon.c +++ b/drivers/hwmon/macsmc-hwmon.c @@ -23,12 +23,15 @@ #include #include #include +#include #include #include #include #include +#include #include #include +#include #include #include #include @@ -189,10 +192,30 @@ struct macsmc_hwmon_sensors { u32 count; }; +/* Fan mode key values */ +#define MACSMC_FAN_MODE_AUTO 0 +#define MACSMC_FAN_MODE_MANUAL 1 +/* The firmware's own fan policy; it refuses a manual mode request. */ +#define MACSMC_FAN_MODE_SYSTEM 3 + +/* How long the firmware gets to leave or re-enter its system mode */ +#define MACSMC_FAN_MODE_TIMEOUT_MS 10000 +#define MACSMC_FAN_MODE_POLL_MS 100 + struct macsmc_hwmon_fans { struct hwmon_channel_info channel_info; struct macsmc_hwmon_fan *fans; u32 count; + /* Serialises manual fan control and its unlock. */ + struct mutex lock; + /* + * Firmware that keeps its fans in system mode releases them to the + * OS only while this key is 1. Zero if the SMC has no such key. + */ + smc_key unlock_key; + bool unlocked; + /* Fans in manual mode */ + u32 manual; }; struct macsmc_hwmon { @@ -466,57 +489,199 @@ static int macsmc_hwmon_read_fan(struct macsmc_hwmon *hwmon, u32 attr, int chan, } } -static int macsmc_hwmon_write_fan(struct device *dev, u32 attr, int channel, - long val) +static int macsmc_hwmon_fan_read_mode(struct macsmc_hwmon *hwmon, + struct macsmc_hwmon_fan *fan, u8 *mode) { - struct macsmc_hwmon *hwmon = dev_get_drvdata(dev); + return apple_smc_read_u8(hwmon->smc, fan->mode.macsmc_key, mode); +} + +/* + * Hand the fans back to the firmware once no fan is manual: clear the unlock + * key and wait for every fan to return to system mode. Called with the fan + * lock held. + */ +static int macsmc_hwmon_fans_relock(struct macsmc_hwmon *hwmon) +{ + unsigned long timeout; + u32 i; + u8 mode; + int ret; + + ret = apple_smc_write_u8(hwmon->smc, hwmon->fan.unlock_key, 0); + if (ret < 0) { + dev_err(hwmon->dev, "cannot hand the fans back to the SMC (%d)\n", ret); + return ret; + } + hwmon->fan.unlocked = false; + + timeout = jiffies + msecs_to_jiffies(MACSMC_FAN_MODE_TIMEOUT_MS); + for (i = 0; i < hwmon->fan.count; i++) { + struct macsmc_hwmon_fan *fan = &hwmon->fan.fans[i]; + + if (!fan->mode.macsmc_key) + continue; + for (;;) { + ret = macsmc_hwmon_fan_read_mode(hwmon, fan, &mode); + if (ret) + return ret; + if (mode == MACSMC_FAN_MODE_SYSTEM) + break; + if (time_after(jiffies, timeout)) { + dev_warn(hwmon->dev, "%s stays in mode %u after release\n", + fan->label, mode); + return -ETIMEDOUT; + } + msleep(MACSMC_FAN_MODE_POLL_MS); + } + } + + return 0; +} + +/* Return a fan to firmware control. Called with the fan lock held. */ +static int macsmc_hwmon_fan_auto(struct macsmc_hwmon *hwmon, + struct macsmc_hwmon_fan *fan) +{ + int ret, relock; + + if (!fan->manual) + return 0; + + ret = macsmc_hwmon_write_key(hwmon->smc, &fan->mode, MACSMC_FAN_MODE_AUTO); + fan->manual = false; + hwmon->fan.manual--; + + if (hwmon->fan.unlocked && !hwmon->fan.manual) { + relock = macsmc_hwmon_fans_relock(hwmon); + if (relock) + return relock; + } + + return ret < 0 ? ret : 0; +} + +/* + * Put a fan in manual mode. Firmware in system mode refuses that until the + * unlock key is set; it then leaves system mode and accepts the request. + * Called with the fan lock held. + */ +static int macsmc_hwmon_fan_manual(struct macsmc_hwmon *hwmon, + struct macsmc_hwmon_fan *fan) +{ + unsigned long timeout; + u8 mode; + int ret; + + if (fan->manual) + return 0; + + ret = macsmc_hwmon_write_key(hwmon->smc, &fan->mode, MACSMC_FAN_MODE_MANUAL); + if (ret < 0 && hwmon->fan.unlock_key && !hwmon->fan.unlocked && + !macsmc_hwmon_fan_read_mode(hwmon, fan, &mode) && + mode == MACSMC_FAN_MODE_SYSTEM) { + ret = apple_smc_write_u8(hwmon->smc, hwmon->fan.unlock_key, 1); + if (ret < 0) { + /* The key may have been set anyway; clear it. */ + apple_smc_write_u8(hwmon->smc, hwmon->fan.unlock_key, 0); + return ret; + } + hwmon->fan.unlocked = true; + + /* Ask again once the fan has left system mode. */ + timeout = jiffies + msecs_to_jiffies(MACSMC_FAN_MODE_TIMEOUT_MS); + for (;;) { + ret = macsmc_hwmon_fan_read_mode(hwmon, fan, &mode); + if (!ret && mode == MACSMC_FAN_MODE_SYSTEM) + ret = -EBUSY; + else if (!ret) + ret = macsmc_hwmon_write_key(hwmon->smc, &fan->mode, + MACSMC_FAN_MODE_MANUAL); + if (ret >= 0 || time_after(jiffies, timeout)) + break; + msleep(MACSMC_FAN_MODE_POLL_MS); + } + if (ret < 0) { + dev_err(hwmon->dev, "%s refused manual mode after unlock (%d)\n", + fan->label, ret); + if (!hwmon->fan.manual) + macsmc_hwmon_fans_relock(hwmon); + return ret; + } + } + if (ret < 0) + return ret; + + fan->manual = true; + hwmon->fan.manual++; + return 0; +} + +static int macsmc_hwmon_set_fan(struct macsmc_hwmon *hwmon, int channel, long val) +{ + struct macsmc_hwmon_fan *fan = &hwmon->fan.fans[channel]; long min, max; int ret; - if (!fan_control || hwmon->fan.fans[channel].mode.macsmc_key == 0) + if (!fan_control || fan->mode.macsmc_key == 0) return -EOPNOTSUPP; + if (val < 0) + return -EINVAL; + + guard(mutex)(&hwmon->fan.lock); + + /* 0 hands the fan back to the firmware. */ + if (!val) + return macsmc_hwmon_fan_auto(hwmon, fan); /* * The SMC does no sanity checks on requested fan speeds, so we need to. */ - ret = macsmc_hwmon_read_key(hwmon->smc, &hwmon->fan.fans[channel].min, - 1, &min); + ret = macsmc_hwmon_read_key(hwmon->smc, &fan->min, 1, &min); if (ret) return ret; - ret = macsmc_hwmon_read_key(hwmon->smc, &hwmon->fan.fans[channel].max, - 1, &max); + ret = macsmc_hwmon_read_key(hwmon->smc, &fan->max, 1, &max); if (ret) return ret; - if (val >= min && val <= max) { - if (!hwmon->fan.fans[channel].manual) { - /* Write 1 to mode key for manual control */ - ret = macsmc_hwmon_write_key(hwmon->smc, - &hwmon->fan.fans[channel].mode, 1); - if (ret < 0) - return ret; + if (min <= 0 || max < min) + return -EIO; + val = clamp(val, min, max); - hwmon->fan.fans[channel].manual = true; - } - return macsmc_hwmon_write_key(hwmon->smc, - &hwmon->fan.fans[channel].set, val); - } else if (!val) { - if (hwmon->fan.fans[channel].manual) { - ret = macsmc_hwmon_write_key(hwmon->smc, - &hwmon->fan.fans[channel].mode, 0); - if (ret < 0) - return ret; + ret = macsmc_hwmon_fan_manual(hwmon, fan); + if (ret) + return ret; - hwmon->fan.fans[channel].manual = false; - } - } else { - return -EINVAL; + ret = macsmc_hwmon_write_key(hwmon->smc, &fan->set, val); + if (ret < 0) { + macsmc_hwmon_fan_auto(hwmon, fan); + return ret; } return 0; } +static int macsmc_hwmon_write_fan(struct device *dev, u32 attr, int channel, + long val) +{ + return macsmc_hwmon_set_fan(dev_get_drvdata(dev), channel, val); +} + +/* Hand every manual fan back to the firmware. */ +static void macsmc_hwmon_fans_restore(struct macsmc_hwmon *hwmon) +{ + u32 i; + + guard(mutex)(&hwmon->fan.lock); + for (i = 0; i < hwmon->fan.count; i++) + macsmc_hwmon_fan_auto(hwmon, &hwmon->fan.fans[i]); +} + +static void macsmc_hwmon_fans_restore_action(void *data) +{ + macsmc_hwmon_fans_restore(data); +} + static int macsmc_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { @@ -1343,6 +1508,11 @@ static int macsmc_hwmon_probe(struct platform_device *pdev) hwmon->dev = &pdev->dev; hwmon->smc = smc; hwmon->register_thermal_zones = true; + platform_set_drvdata(pdev, hwmon); + + ret = devm_mutex_init(&pdev->dev, &hwmon->fan.lock); + if (ret) + return ret; ret = macsmc_hwmon_populate_sensors(hwmon, hwmon->dev->of_node); if (ret) { @@ -1372,6 +1542,21 @@ static int macsmc_hwmon_probe(struct platform_device *pdev) if (ret) return ret; + if (hwmon->fan.count) { + struct apple_smc_key_info info; + + if (!apple_smc_get_key_info(smc, SMC_KEY(Ftst), &info) && + info.type_code == __SMC_KEY('u', 'i', '8', ' ') && + (info.flags & APPLE_SMC_WRITABLE)) + hwmon->fan.unlock_key = SMC_KEY(Ftst); + + /* Runs after the hwmon device is gone, so no write races it. */ + ret = devm_add_action_or_reset(&pdev->dev, + macsmc_hwmon_fans_restore_action, hwmon); + if (ret) + return ret; + } + hwmon->chip_info.ops = &macsmc_hwmon_ops; hwmon->chip_info.info = (const struct hwmon_channel_info *const *)&hwmon->channel_infos; @@ -1407,11 +1592,29 @@ static const struct of_device_id macsmc_hwmon_of_table[] = { }; MODULE_DEVICE_TABLE(of, macsmc_hwmon_of_table); +static void macsmc_hwmon_shutdown(struct platform_device *pdev) +{ + struct macsmc_hwmon *hwmon = platform_get_drvdata(pdev); + + if (hwmon) + macsmc_hwmon_fans_restore(hwmon); +} + +static int macsmc_hwmon_suspend(struct device *dev) +{ + macsmc_hwmon_fans_restore(dev_get_drvdata(dev)); + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(macsmc_hwmon_pm_ops, macsmc_hwmon_suspend, NULL); + static struct platform_driver macsmc_hwmon_driver = { .probe = macsmc_hwmon_probe, + .shutdown = macsmc_hwmon_shutdown, .driver = { .name = "macsmc-hwmon", .of_match_table = macsmc_hwmon_of_table, + .pm = pm_sleep_ptr(&macsmc_hwmon_pm_ops), }, }; module_platform_driver(macsmc_hwmon_driver); diff --git a/drivers/media/platform/apple/isp/Kconfig b/drivers/media/platform/apple/isp/Kconfig index b51d19ee115de..7a797a1a7e1a3 100644 --- a/drivers/media/platform/apple/isp/Kconfig +++ b/drivers/media/platform/apple/isp/Kconfig @@ -24,8 +24,9 @@ config VIDEO_APPLE_ISP_KUNIT_TEST depends on KUNIT=y || VIDEO_APPLE_ISP=m default KUNIT_ALL_TESTS help - Builds unit tests for the videobuf2 queue set-up and for the - shared memory the firmware frees into the Apple ISP driver. They + Builds unit tests for queue setup, P010 layouts and buffer identity, + firmware sensor geometry, the capture command stream and shared + memory retirement. The command tests stub the IPC transport and need no hardware. Only useful for kernel developers running KUnit. If unsure, say N. diff --git a/drivers/media/platform/apple/isp/isp-cam-test.c b/drivers/media/platform/apple/isp/isp-cam-test.c new file mode 100644 index 0000000000000..27c9e23f348aa --- /dev/null +++ b/drivers/media/platform/apple/isp/isp-cam-test.c @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Synthetic geometry checks: no camera firmware or setfile data. */ +#include +#include +#include +#include "isp-ipc.h" + +static void isp_test_sensor_geometry(struct kunit *test) +{ + struct cmd_ch_camera_config config = { + .in_width = 1920, .in_height = 2160, + .out_width = 1920, .out_height = 1080, + .in_width2 = 1920, .in_height2 = 2160, + .out_width2 = 1920, .out_height2 = 1080, + }; + + KUNIT_EXPECT_TRUE(test, isp_sensor_config_usable(&config)); + /* A future larger output is accepted only when both descriptions agree. */ + config.in_width = 4096; + config.in_width2 = 4096; + config.in_height = 3072; + config.in_height2 = 3072; + config.out_width = 4096; + config.out_width2 = 4096; + config.out_height = 3072; + config.out_height2 = 3072; + KUNIT_EXPECT_TRUE(test, isp_sensor_config_usable(&config)); + config.out_height2 = 2160; + KUNIT_EXPECT_FALSE(test, isp_sensor_config_usable(&config)); + config.out_height2 = 3072; + config.in_height2 = 2160; + KUNIT_EXPECT_FALSE(test, isp_sensor_config_usable(&config)); + config.in_height2 = 3072; + config.out_width = 4097; + config.out_width2 = 4097; + KUNIT_EXPECT_FALSE(test, isp_sensor_config_usable(&config)); + config.out_width = 0; + config.out_width2 = 0; + KUNIT_EXPECT_FALSE(test, isp_sensor_config_usable(&config)); + config.out_width = 8192; + config.out_width2 = 8192; + KUNIT_EXPECT_FALSE(test, isp_sensor_config_usable(&config)); +} + +struct isp_test_command { + u32 words[39]; + u32 size; +}; + +/* Derived from the proxy command fixture, with contiguous V4L2 plane sizes. */ +static const struct isp_test_command isp_test_preview_commands[] = { + {{0, 0x24, 0}, 0xc}, + {{0, 0x13b, 0, 1}, 0x10}, + {{0, 0x107, 0, 5}, 0x10}, + {{0, 0x10e, 0, 1}, 0x10}, + {{0, 0x10f, 0}, 0xc}, + {{0, 0x80c, 0, 0, 0, 1280, 720}, 0x1c}, + {{0, 0xb09, 0, 1280, 720, 1, 0x12, 2560, 2560, 0, 0, 720, 0, 1280}, 0x38}, + {{0, 0x208, 0, 7680}, 0x10}, + {{0, 0x20a, 0, 3840}, 0x10}, + {{[1] = 0x117, [3] = 8 << 16, [4] = 0x8000, [5] = 0x8000, [37] = 1}, 0x9c}, + {{[1] = 0x117, [3] = 9 | 2 << 16, [4] = 1843200, [5] = 2560, + [12] = 921600, [13] = 2560, [37] = 2}, 0x9c}, + {{0, 0x125, 0, 1}, 0x10}, + {{0, 0xb0d, 0, 1}, 0x10}, + {{0, 0x138, 0, 0}, 0x10}, +}; + +struct isp_test_command_trace { + unsigned int count; + unsigned int fail_at; +}; + +static int isp_test_send(struct apple_isp *isp, void *args, u32 insize, + u32 outsize, int timeout) +{ + struct kunit *test = kunit_get_current_test(); + struct isp_test_command_trace *trace = test->priv; + const struct isp_test_command *expected; + + if (trace->count >= ARRAY_SIZE(isp_test_preview_commands)) { + KUNIT_FAIL(test, "unexpected extra command"); + return -EIO; + } + expected = &isp_test_preview_commands[trace->count++]; + KUNIT_EXPECT_EQ(test, insize, expected->size); + KUNIT_EXPECT_EQ(test, outsize, expected->words[1] == 0x117 ? insize : 0); + if (insize == expected->size) + KUNIT_EXPECT_MEMEQ(test, args, expected->words, insize); + if (trace->count == trace->fail_at) + return -ETIME; + return 0; +} + +static void isp_test_free_controls(void *ptr) +{ + v4l2_ctrl_handler_free(ptr); +} + +static struct apple_isp *isp_test_capture_alloc(struct kunit *test) +{ + static const struct apple_isp_hw hw = { + .gen = ISP_GEN_T6040, .fw_abi = ISP_FW_ABI_H17, + .scl1 = true, .meta_size = ISP_META_SIZE_T8140, + }; + static struct isp_preset preset = { + .index = 5, .input_dim = {1920, 2160}, + .output_dim = {1280, 720}, .crop_size = {1280, 720}, + }; + struct apple_isp *isp = kunit_kzalloc(test, sizeof(*isp), GFP_KERNEL); + struct isp_format *fmt; + int err; + + KUNIT_ASSERT_NOT_NULL(test, isp); + isp->dev = kunit_device_register(test, "apple-isp-camera-test"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, isp->dev); + isp->hw = &hw; + isp->frame_rate = 30; + fmt = isp_get_current_format(isp); + fmt->version = 0x958; + fmt->pixelformat = V4L2_PIX_FMT_P010; + fmt->preset = &preset; + fmt->strides[0] = 2560; + fmt->strides[1] = 2560; + fmt->plane_size[0] = 1843200; + fmt->plane_size[1] = 921600; + v4l2_ctrl_handler_init(&isp->ctrl_handler, 1); + err = kunit_add_action_or_reset(test, isp_test_free_controls, &isp->ctrl_handler); + KUNIT_ASSERT_EQ(test, err, 0); + isp->exposure_priority = + v4l2_ctrl_new_std(&isp->ctrl_handler, NULL, + V4L2_CID_EXPOSURE_AUTO_PRIORITY, 0, 1, 1, 1); + KUNIT_ASSERT_EQ(test, isp->ctrl_handler.error, 0); + kunit_activate_static_stub(test, isp_cmd_send, isp_test_send); + return isp; +} + +static void isp_test_preview_stream(struct kunit *test) +{ + struct apple_isp *isp = isp_test_capture_alloc(test); + struct isp_test_command_trace trace = {}; + + test->priv = &trace; + KUNIT_EXPECT_EQ(test, isp_ch_configure_capture(isp, 0), 0); + KUNIT_EXPECT_EQ(test, trace.count, ARRAY_SIZE(isp_test_preview_commands)); +} + +static void isp_test_preview_command_failures(struct kunit *test) +{ + struct apple_isp *isp = isp_test_capture_alloc(test); + struct isp_test_command_trace trace; + + test->priv = &trace; + for (unsigned int i = 1; i <= ARRAY_SIZE(isp_test_preview_commands); i++) { + trace = (struct isp_test_command_trace){ .fail_at = i }; + KUNIT_EXPECT_EQ(test, isp_ch_configure_capture(isp, 0), -ETIME); + KUNIT_EXPECT_EQ(test, trace.count, i); + } +} + +static int isp_test_config_reply(struct apple_isp *isp, void *args, u32 insize, + u32 outsize, int timeout) +{ + struct kunit *test = kunit_get_current_test(); + u32 *count = test->priv; + struct cmd_ch_camera_config_h17 *reply = isp->cmd_virt; + struct cmd_ch_camera_config *config = &reply->config; + + KUNIT_EXPECT_EQ(test, insize, 0x120); + KUNIT_EXPECT_EQ(test, outsize, 0x120); + memcpy(reply, args, sizeof(*reply)); + KUNIT_EXPECT_EQ(test, config->preset, (*count)++); + config->in_width = 1920; + config->in_height = 2160; + config->out_width = 1920; + config->out_height = 1080; + if (config->preset == 1) { + config->in_width = 4096; + config->in_height = 3072; + config->out_width = 4096; + config->out_height = 3072; + } + config->in_width2 = config->in_width; + config->in_height2 = config->in_height; + config->out_width2 = config->out_width; + config->out_height2 = config->out_height; + if (config->preset == 2) + config->out_height2 = 2160; + reply->unknown[sizeof(reply->unknown) - 1] = 0x5a; + return 0; +} + +static void isp_test_free_configs(void *ptr) +{ + kfree(ptr); +} + +static void isp_test_sensor_config_list(struct kunit *test) +{ + struct apple_isp *isp = isp_test_capture_alloc(test); + struct isp_preset presets[] = { + { .index = 5, .input_dim = {1920, 2160}, + .output_dim = {1920, 1080}, .crop_size = {1920, 1080} }, + { .index = 5, .input_dim = {1920, 2160}, + .output_dim = {1280, 720}, .crop_size = {1280, 720} }, + }; + u32 count = 0; + int err; + + isp->cmd_virt = kunit_kzalloc(test, 0x120, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, isp->cmd_virt); + isp->presets = presets; + isp->num_presets = ARRAY_SIZE(presets); + test->priv = &count; + kunit_activate_static_stub(test, isp_cmd_send, isp_test_config_reply); + KUNIT_ASSERT_EQ(test, isp_ch_cache_sensor_configs(isp, 0, 6), 0); + err = kunit_add_action_or_reset(test, isp_test_free_configs, isp->sensor_configs); + KUNIT_ASSERT_EQ(test, err, 0); + KUNIT_EXPECT_EQ(test, get_unaligned_le32(isp->sensor_configs[5].reply + 0xc), 5); + KUNIT_EXPECT_EQ(test, isp->sensor_configs[5].reply[ISP_SENSOR_CONFIG_SIZE - 1], 0x5a); + KUNIT_EXPECT_EQ(test, count, 6); + KUNIT_EXPECT_EQ(test, isp->num_sensor_configs, 6); + KUNIT_EXPECT_EQ(test, isp->num_presets, 3); + KUNIT_EXPECT_EQ(test, isp->presets[0].index, 5); + KUNIT_EXPECT_EQ(test, isp->presets[0].output_dim.y, 1080); + KUNIT_EXPECT_TRUE(test, isp->presets[1].p010); + KUNIT_EXPECT_TRUE(test, isp->presets[2].p010); + KUNIT_EXPECT_EQ(test, isp->presets[2].index, 1); + KUNIT_EXPECT_EQ(test, isp->presets[2].output_dim.x, 4096); + KUNIT_EXPECT_EQ(test, isp->presets[2].crop_size.y, 3072); + KUNIT_EXPECT_FALSE(test, isp->sensor_configs[2].usable); + KUNIT_EXPECT_EQ(test, isp_ch_cache_sensor_configs(isp, 0, 0), -EINVAL); + KUNIT_EXPECT_EQ(test, isp_ch_cache_sensor_configs(isp, 0, 65), -EINVAL); + KUNIT_EXPECT_EQ(test, count, 6); +} + +static int isp_test_bad_config_reply(struct apple_isp *isp, void *args, u32 insize, + u32 outsize, int timeout) +{ + struct cmd_ch_camera_config_h17 *reply = isp->cmd_virt; + + isp_test_config_reply(isp, args, insize, outsize, timeout); + reply->config.preset = 65; + return 0; +} + +static void isp_test_sensor_config_failure(struct kunit *test) +{ + struct apple_isp *isp = isp_test_capture_alloc(test); + struct isp_preset preset = {}; + u32 count = 0; + + isp->cmd_virt = kunit_kzalloc(test, ISP_SENSOR_CONFIG_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, isp->cmd_virt); + isp->presets = &preset; + isp->num_presets = 1; + test->priv = &count; + kunit_activate_static_stub(test, isp_cmd_send, isp_test_bad_config_reply); + KUNIT_EXPECT_EQ(test, isp_ch_cache_sensor_configs(isp, 0, 6), -EIO); + KUNIT_EXPECT_EQ(test, count, 1); + KUNIT_EXPECT_EQ(test, isp->num_sensor_configs, 0); + KUNIT_EXPECT_PTR_EQ(test, isp->sensor_configs, NULL); + KUNIT_EXPECT_PTR_EQ(test, isp->presets, &preset); +} + +struct isp_test_ack_state { + unsigned int fault, calls; +}; + +static int isp_test_ack_reply(struct apple_isp *isp, struct isp_channel *chan, + unsigned long timeout) +{ + struct kunit *test = kunit_get_current_test(); + struct isp_test_ack_state *state = test->priv; + + state->calls++; + chan->rsp = (struct isp_message){ .arg0 = chan->req.arg0 | ISP_IPC_FLAG_ACK, + .arg1 = chan->req.arg2 }; + switch (state->fault) { + case 1: + chan->rsp.arg2 = 1; + break; + case 2: + put_unaligned_le32(1, isp->cmd_virt); + break; + case 3: + chan->rsp.arg1 += 4; + break; + case 4: + return -ETIME; + } + return 0; +} + +static void isp_test_command_ack_failure_latches(struct kunit *test) +{ + struct apple_isp *isp = isp_test_capture_alloc(test); + struct isp_test_ack_state state = {}; + + isp->cmd_virt = kunit_kzalloc(test, 0x400, GFP_KERNEL); + isp->chan_io = kunit_kzalloc(test, sizeof(*isp->chan_io), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, isp->cmd_virt); + KUNIT_ASSERT_NOT_NULL(test, isp->chan_io); + isp->chan_io->name = "IO"; + test->priv = &state; + kunit_deactivate_static_stub(test, isp_cmd_send); + kunit_activate_static_stub(test, ipc_chan_send, isp_test_ack_reply); + KUNIT_EXPECT_EQ(test, isp_cmd_ch_camera_config_select(isp, 0, 5), 0); + for (state.fault = 1; state.fault <= 4; state.fault++) { + isp->ipc_failed = false; + state.calls = 0; + KUNIT_EXPECT_EQ(test, isp_cmd_ch_camera_config_select(isp, 0, 5), + state.fault == 4 ? -ETIME : -EIO); + KUNIT_EXPECT_TRUE(test, isp->ipc_failed); + /* A late ACK cannot admit another command on an uncertain slot. */ + KUNIT_EXPECT_EQ(test, isp_cmd_ch_camera_config_select(isp, 0, 5), -EIO); + KUNIT_EXPECT_EQ(test, state.calls, 1); + } +} + +static struct kunit_case isp_cam_test_cases[] = { + KUNIT_CASE(isp_test_command_ack_failure_latches), + KUNIT_CASE(isp_test_sensor_geometry), + KUNIT_CASE(isp_test_sensor_config_list), + KUNIT_CASE(isp_test_sensor_config_failure), + KUNIT_CASE(isp_test_preview_stream), + KUNIT_CASE(isp_test_preview_command_failures), + {} +}; + +static struct kunit_suite isp_cam_test_suite = { + .name = "apple-isp-camera", + .test_cases = isp_cam_test_cases, +}; + +kunit_test_suite(isp_cam_test_suite); diff --git a/drivers/media/platform/apple/isp/isp-cam.c b/drivers/media/platform/apple/isp/isp-cam.c index a266c60609614..a5cbb2b4d8eb0 100644 --- a/drivers/media/platform/apple/isp/isp-cam.c +++ b/drivers/media/platform/apple/isp/isp-cam.c @@ -10,8 +10,6 @@ #include "isp-iommu.h" #include "isp-regs.h" -#define ISP_MAX_PRESETS 32 - struct isp_setfile { u32 version; u32 magic; @@ -217,6 +215,122 @@ static int isp_check_presets(struct apple_isp *isp, u32 num_fw_presets) return 0; } +/* Do not mistake stacked sensor input rows for a processed output mode. */ +static bool isp_sensor_config_usable(const struct cmd_ch_camera_config *config) +{ + return config->in_width && config->in_height && + config->out_width && config->out_height && + config->out_width <= 16384 && config->out_height <= 16384 && + !(config->out_width & 1) && !(config->out_height & 1) && + config->out_width <= config->in_width && + config->out_height <= config->in_height && + config->in_width == config->in_width2 && + config->in_height == config->in_height2 && + config->out_width == config->out_width2 && + config->out_height == config->out_height2; +} + +static int isp_ch_cache_sensor_configs(struct apple_isp *isp, u32 ch, u32 count) +{ + struct cmd_ch_camera_config_h17 args; + struct isp_sensor_config *configs; + struct isp_preset *presets; + unsigned int num = isp->num_presets; + int err; + + /* The extended reply is established only for the IMX958 T6040 path. */ + if (!isp_supports_p010(isp)) + return 0; + if (!count || count > ISP_MAX_SENSOR_CONFIGS) + return -EINVAL; + + presets = devm_kcalloc(isp->dev, num + count, sizeof(*presets), GFP_KERNEL); + if (!presets) + return -ENOMEM; + memcpy(presets, isp->presets, num * sizeof(*presets)); + configs = kcalloc(count, sizeof(*configs), GFP_KERNEL); + if (!configs) + return -ENOMEM; + + for (u32 i = 0; i < count; i++) { + struct cmd_ch_camera_config *config = &args.config; + struct isp_sensor_config *cached = &configs[i]; + bool duplicate = false; + + err = isp_cmd_ch_camera_config_get(isp, ch, i, &args); + if (err) + goto free_configs; + if (config->opcode != (u64)CISP_CMD_CH_CAMERA_CONFIG_GET << 32 || + config->chan != ch || config->preset != i) { + err = -EIO; + goto free_configs; + } + + memcpy(cached->reply, &args, sizeof(args)); + cached->input_dim = (struct coord){config->in_width, config->in_height}; + cached->output_dim = (struct coord){config->out_width, config->out_height}; + cached->sensor_clk = config->sensor_clk; + cached->usable = isp_sensor_config_usable(config); + dev_dbg(isp->dev, "sensor config %u: input %ux%u output %ux%u clock %u%s\n", + i, config->in_width, config->in_height, + config->out_width, config->out_height, config->sensor_clk, + cached->usable ? "" : " (unverified geometry)"); + print_hex_dump_debug("apple-isp config: ", DUMP_PREFIX_OFFSET, + 16, 4, &args, sizeof(args), false); + /* Both processed preview sizes are recorded in the proxy source. */ + for (unsigned int j = 0; j < num; j++) { + struct isp_preset *preset = &presets[j]; + bool preview = i == 5 && config->in_width == 1920 && + config->in_height == 2160 && + ((preset->output_dim.x == 1280 && + preset->output_dim.y == 720) || + (preset->output_dim.x == 1920 && + preset->output_dim.y == 1080)); + bool reported = cached->usable && + preset->output_dim.x == config->out_width && + preset->output_dim.y == config->out_height; + + if (preset->index == i && + preset->input_dim.x == config->in_width && + preset->input_dim.y == config->in_height && + !preset->crop_offset.x && !preset->crop_offset.y && + preset->crop_size.x == preset->output_dim.x && + preset->crop_size.y == preset->output_dim.y) + preset->p010 = preview || reported; + } + if (!cached->usable) + continue; + + /* Keep the board's config-5 presets, including the default, first. */ + for (unsigned int j = 0; j < num; j++) { + if (presets[j].output_dim.x == config->out_width && + presets[j].output_dim.y == config->out_height) { + duplicate = true; + break; + } + } + if (duplicate) + continue; + + presets[num++] = (struct isp_preset){ + .index = i, + .input_dim = cached->input_dim, + .output_dim = cached->output_dim, + .crop_size = cached->output_dim, + .p010 = true, + }; + } + isp->sensor_configs = configs; + isp->num_sensor_configs = count; + isp->presets = presets; + isp->num_presets = num; + return 0; + +free_configs: + kfree(configs); + return err; +} + static int isp_ch_cache_sensor_info(struct apple_isp *isp, u32 ch) { struct isp_format *fmt = isp_get_format(isp, ch); @@ -264,6 +378,8 @@ static int isp_ch_cache_sensor_info(struct apple_isp *isp, u32 ch) } err = isp_check_presets(isp, args->num_presets); + if (!err) + err = isp_ch_cache_sensor_configs(isp, ch, args->num_presets); exit: kfree(args); @@ -475,7 +591,7 @@ static int isp_ch_configure_pools(struct apple_isp *isp, u32 ch) return 0; return isp_cmd_ch_buffer_pool_config_set_rendered(isp, ch, - ISP_MAX_BUFFERS, + isp_num_rendered(isp), fmt->plane_size[0], fmt->strides[0], fmt->plane_size[1], @@ -488,7 +604,13 @@ static int isp_ch_configure_capture(struct apple_isp *isp, u32 ch) bool h17 = isp->hw->fw_abi == ISP_FW_ABI_H17; int err; - isp_cmd_flicker_sensor_set(isp, 0); + err = isp_cmd_flicker_sensor_set(isp, 0); + if (isp_is_p010(isp) && err) + return err; + + /* The proxy-qualified P010 route uses the firmware defaults. */ + if (isp_is_p010(isp)) + goto select_config; /* * The setfile isn't requisite but then we don't get calibration. If @@ -504,6 +626,7 @@ static int isp_ch_configure_capture(struct apple_isp *isp, u32 ch) return err; } +select_config: err = isp_cmd_ch_sbs_enable(isp, ch, 1); if (err) return err; @@ -531,7 +654,8 @@ static int isp_ch_configure_capture(struct apple_isp *isp, u32 ch) err = isp_cmd_ch_output_config_set(isp, ch, fmt->preset->output_dim.x, fmt->preset->output_dim.y, fmt->strides, CISP_COLORSPACE_REC709, - CISP_OUTPUT_FORMAT_YUV_2PLANE); + isp_is_p010(isp) ? CISP_OUTPUT_FORMAT_P010 : + CISP_OUTPUT_FORMAT_YUV_2PLANE); if (err) return err; @@ -559,6 +683,9 @@ static int isp_ch_configure_capture(struct apple_isp *isp, u32 ch) if (err) return err; + if (isp_is_p010(isp)) + return isp_cmd_ch_master_slave_sync_mode_set(isp, ch, 0); + err = isp_cmd_ch_cnr_start(isp, ch); if (err) return err; @@ -633,7 +760,10 @@ int apple_isp_start_camera(struct apple_isp *isp) err = isp_configure_capture(isp); if (err) { dev_err(isp->dev, "failed to configure capture: %d\n", err); - apple_isp_firmware_shutdown(isp); + if (isp->ipc_failed) + apple_isp_firmware_halt(isp); + else + apple_isp_firmware_shutdown(isp); return err; } @@ -642,7 +772,10 @@ int apple_isp_start_camera(struct apple_isp *isp) void apple_isp_stop_camera(struct apple_isp *isp) { - apple_isp_firmware_shutdown(isp); + if (isp->ipc_failed) + apple_isp_firmware_halt(isp); + else + apple_isp_firmware_shutdown(isp); } /* @@ -714,9 +847,15 @@ int apple_isp_start_capture(struct apple_isp *isp) void apple_isp_stop_capture(struct apple_isp *isp) { + if (isp->ipc_failed || isp->fw_state == ISP_FW_DEAD) + return; isp_cmd_ch_stop(isp, 0); // TODO channel mask isp_cmd_ch_buffer_return(isp, isp->current_ch); /* ... until its buffers are back. */ apple_isp_wdt_stop(isp); } + +#if IS_ENABLED(CONFIG_VIDEO_APPLE_ISP_KUNIT_TEST) +#include "isp-cam-test.c" +#endif diff --git a/drivers/media/platform/apple/isp/isp-cmd.c b/drivers/media/platform/apple/isp/isp-cmd.c index fd7dcfd52899a..a525e68c230f5 100644 --- a/drivers/media/platform/apple/isp/isp-cmd.c +++ b/drivers/media/platform/apple/isp/isp-cmd.c @@ -5,30 +5,54 @@ #include "isp-drv.h" #include "isp-iommu.h" #include "isp-ipc.h" +#include + +#if IS_ENABLED(CONFIG_VIDEO_APPLE_ISP_KUNIT_TEST) +#include +#endif #define CISP_OPCODE_SHIFT 32UL #define CISP_OPCODE(x) (((u64)(x)) << CISP_OPCODE_SHIFT) #define CISP_OPCODE_GET(x) (((u64)(x)) >> CISP_OPCODE_SHIFT) #define CISP_TIMEOUT msecs_to_jiffies(3000) -#define CISP_SEND_IN(x, a) (cisp_send((x), &(a), sizeof(a), 0, CISP_TIMEOUT)) -#define CISP_SEND_INOUT(x, a) (cisp_send((x), &(a), sizeof(a), sizeof(a), CISP_TIMEOUT)) +#define CISP_SEND_IN(x, a) (isp_cmd_send((x), &(a), sizeof(a), 0, CISP_TIMEOUT)) +#define CISP_SEND_INOUT(x, a) (isp_cmd_send((x), &(a), sizeof(a), sizeof(a), CISP_TIMEOUT)) #define CISP_SEND_OUT(x, a) (cisp_send_read((x), (a), sizeof(*a), sizeof(*a))) -#define CISP_POST_IN(x, a) (cisp_send((x), &(a), sizeof(a), 0, 0)) -#define CISP_POST_INOUT(x, a) (cisp_send((x), &(a), sizeof(a), sizeof(a), 0)) +#define CISP_POST_IN(x, a) (isp_cmd_send((x), &(a), sizeof(a), 0, 0)) +#define CISP_POST_INOUT(x, a) (isp_cmd_send((x), &(a), sizeof(a), sizeof(a), 0)) -static int cisp_send(struct apple_isp *isp, void *args, u32 insize, u32 outsize, int timeout) +int isp_cmd_send(struct apple_isp *isp, void *args, u32 insize, u32 outsize, int timeout) { - struct isp_channel *chan = isp->chan_io; - struct isp_message *req = &chan->req; + struct isp_channel *chan; + struct isp_message *req; int err; +#if IS_ENABLED(CONFIG_VIDEO_APPLE_ISP_KUNIT_TEST) + KUNIT_STATIC_STUB_REDIRECT(isp_cmd_send, isp, args, insize, outsize, timeout); +#endif + if (isp->ipc_failed) + return -EIO; + chan = isp->chan_io; + req = &chan->req; + req->arg0 = isp->cmd_iova; req->arg1 = insize; req->arg2 = outsize; memcpy(isp->cmd_virt, args, insize); err = ipc_chan_send(isp, chan, timeout); + if (isp_is_p010(isp)) { + struct isp_message *rsp = &chan->rsp; + + if (!err && timeout && + (rsp->arg1 != outsize || rsp->arg2 || rsp->arg3 || + rsp->arg4 || rsp->arg5 || rsp->arg6 || rsp->arg7 || + get_unaligned_le64(isp->cmd_virt) != get_unaligned_le64(args))) + err = -EIO; + if (err) + isp->ipc_failed = true; + } if (err) { u64 opcode; memcpy(&opcode, args, sizeof(opcode)); @@ -45,7 +69,7 @@ static int cisp_send_read(struct apple_isp *isp, void *args, u32 insize, u32 outsize) { /* TODO do I need to lock the iova space? */ - int err = cisp_send(isp, args, insize, outsize, CISP_TIMEOUT); + int err = isp_cmd_send(isp, args, insize, outsize, CISP_TIMEOUT); if (err) return err; @@ -165,7 +189,7 @@ int isp_cmd_set_dsid_clr_multi_bc(struct apple_isp *isp, const u64 *bases, return -EINVAL; memcpy(args.dsid_clr_base, bases, count * sizeof(*bases)); - return cisp_send(isp, &args, + return isp_cmd_send(isp, &args, offsetof(typeof(args), dsid_clr_base) + count * sizeof(*bases), 0, CISP_TIMEOUT); @@ -232,6 +256,8 @@ int isp_cmd_flicker_sensor_set(struct apple_isp *isp, u32 mode) .opcode = CISP_OPCODE(CISP_CMD_FLICKER_SENSOR_SET), .mode = mode, }; + if (isp_is_p010(isp)) + return CISP_SEND_IN(isp, args); return CISP_SEND_INOUT(isp, args); } @@ -260,6 +286,16 @@ int isp_cmd_ch_info_get(struct apple_isp *isp, u32 chan, return err; } +int isp_cmd_ch_camera_config_get(struct apple_isp *isp, u32 chan, u32 index, + struct cmd_ch_camera_config_h17 *args) +{ + memset(args, 0, sizeof(*args)); + args->config.opcode = CISP_OPCODE(CISP_CMD_CH_CAMERA_CONFIG_GET); + args->config.chan = chan; + args->config.preset = index; + return CISP_SEND_OUT(isp, args); +} + int isp_cmd_ch_camera_config_current_get(struct apple_isp *isp, u32 chan, struct cmd_ch_camera_config *args) { @@ -468,9 +504,9 @@ int isp_cmd_ch_buffer_pool_config_set(struct apple_isp *isp, u32 chan, u16 type) .opcode = CISP_OPCODE(CISP_CMD_CH_BUFFER_POOL_CONFIG_SET), .chan = chan, .type = type, - .count = ISP_MAX_BUFFERS, - .meta_size0 = size, - .meta_size1 = size, + .count = isp_num_meta(isp), + .meta_size0 = isp_is_p010(isp) ? 0x8000 : size, + .meta_size1 = isp_is_p010(isp) ? 0x8000 : size, .unk0 = 0, .unk1 = 0, .unk2 = 0, diff --git a/drivers/media/platform/apple/isp/isp-cmd.h b/drivers/media/platform/apple/isp/isp-cmd.h index 695c0a9f7ab70..a3167108b128e 100644 --- a/drivers/media/platform/apple/isp/isp-cmd.h +++ b/drivers/media/platform/apple/isp/isp-cmd.h @@ -124,6 +124,7 @@ #define CISP_COLORSPACE_REC709 0x1 #define CISP_OUTPUT_FORMAT_YUV_2PLANE 0x0 #define CISP_OUTPUT_FORMAT_YUV_1PLANE 0x1 +#define CISP_OUTPUT_FORMAT_P010 0x12 #define CISP_OUTPUT_FORMAT_RGB 0x2 #define CISP_BUFFER_RECYCLE_MODE_EMPTY_ONLY 0x1 @@ -255,6 +256,8 @@ struct cmd_flicker_sensor_set { } __packed; static_assert(sizeof(struct cmd_flicker_sensor_set) == 0xc); +int isp_cmd_send(struct apple_isp *isp, void *args, u32 insize, u32 outsize, + int timeout); int isp_cmd_start(struct apple_isp *isp, u32 mode); int isp_cmd_stop(struct apple_isp *isp, u32 mode); int isp_cmd_power_down(struct apple_isp *isp); @@ -380,6 +383,15 @@ struct cmd_ch_camera_config { } __packed; static_assert(sizeof(struct cmd_ch_camera_config) == 0xdc); +/* H17 appends 0x44 bytes to the common camera configuration record. */ +struct cmd_ch_camera_config_h17 { + struct cmd_ch_camera_config config; + u8 unknown[0x44]; +} __packed; +static_assert(sizeof(struct cmd_ch_camera_config_h17) == 0x120); +static_assert(offsetof(struct cmd_ch_camera_config, in_width) == 0x10); +static_assert(offsetof(struct cmd_ch_camera_config, out_width2) == 0xd4); + struct cmd_ch_camera_config_select { u64 opcode; u32 chan; @@ -516,6 +528,8 @@ int isp_cmd_ch_start(struct apple_isp *isp, u32 chan); int isp_cmd_ch_stop(struct apple_isp *isp, u32 chan); int isp_cmd_ch_info_get(struct apple_isp *isp, u32 chan, struct cmd_ch_info *args); +int isp_cmd_ch_camera_config_get(struct apple_isp *isp, u32 chan, u32 index, + struct cmd_ch_camera_config_h17 *args); int isp_cmd_ch_camera_config_current_get(struct apple_isp *isp, u32 chan, struct cmd_ch_camera_config *args); int isp_cmd_ch_camera_config_select(struct apple_isp *isp, u32 chan, diff --git a/drivers/media/platform/apple/isp/isp-drv.c b/drivers/media/platform/apple/isp/isp-drv.c index da9d459dd8bdb..084cca4dc3707 100644 --- a/drivers/media/platform/apple/isp/isp-drv.c +++ b/drivers/media/platform/apple/isp/isp-drv.c @@ -538,6 +538,7 @@ static int apple_isp_probe(struct platform_device *pdev) mutex_init(&isp->gc_lock); INIT_LIST_HEAD(&isp->bufs_pending); INIT_LIST_HEAD(&isp->bufs_submitted); + INIT_LIST_HEAD(&isp->bufs_returned); isp->wq = alloc_workqueue("apple-isp-wq", WQ_UNBOUND, 0); if (!isp->wq) { dev_err(dev, "failed to create workqueue\n"); @@ -585,6 +586,7 @@ static int apple_isp_probe(struct platform_device *pdev) detach_genpd: apple_isp_detach_genpd(isp); free_isp: + kfree(isp->sensor_configs); /* Nothing else can reach it before the video device is registered. */ kfree(isp); return err; diff --git a/drivers/media/platform/apple/isp/isp-drv.h b/drivers/media/platform/apple/isp/isp-drv.h index ed3e48eef569e..dbe6b6ed5a2c4 100644 --- a/drivers/media/platform/apple/isp/isp-drv.h +++ b/drivers/media/platform/apple/isp/isp-drv.h @@ -30,6 +30,8 @@ /* used to limit the user space buffers to the buffer_pool_config */ #define ISP_MAX_BUFFERS 16 +#define ISP_MAX_SENSOR_CONFIGS 64 +#define ISP_SENSOR_CONFIG_SIZE 0x120 /* capture metadata buffers, for firmware that has that pool */ #define ISP_CAPMETA_BUFFERS 8 @@ -116,6 +118,8 @@ struct isp_channel { void *virt; u32 doorbell; u32 cursor; + unsigned long deadline; + bool deadline_active; struct mutex lock; struct isp_message req; struct isp_message rsp; @@ -139,6 +143,8 @@ struct isp_preset { struct coord output_dim; struct coord crop_offset; struct coord crop_size; + /* Processed P010 geometry established by preview or a config reply. */ + bool p010; }; struct apple_isp_hw { @@ -255,9 +261,18 @@ enum isp_sensor_id { ISP_VD56G0_6222_01, }; +struct isp_sensor_config { + struct coord input_dim; + struct coord output_dim; + u32 sensor_clk; + bool usable; + u8 reply[ISP_SENSOR_CONFIG_SIZE]; +}; + struct isp_format { enum isp_sensor_id id; u32 version; + u32 pixelformat; struct isp_preset *preset; unsigned int num_planes; u32 strides[VB2_MAX_PLANES]; @@ -274,6 +289,9 @@ struct apple_isp { u32 cam_connections_scheme; struct isp_preset *presets; int num_presets; + /* Read-only sensor configuration replies, cached before video registration. */ + unsigned int num_sensor_configs; + struct isp_sensor_config *sensor_configs; int num_channels; struct isp_format fmts[ISP_MAX_CHANNELS]; @@ -287,7 +305,6 @@ struct apple_isp { struct vb2_queue vbq; struct mutex video_lock; unsigned int sequence; - bool multiplanar; /* capture rate in frames per second, changes under video_lock */ unsigned int frame_rate; @@ -358,16 +375,25 @@ struct apple_isp { spinlock_t buf_lock; struct list_head bufs_pending; struct list_head bufs_submitted; + struct list_head bufs_returned; + u64 buffer_tag; + unsigned long meta_returned; + /* A timed-out command leaves slot ownership ambiguous until reboot. */ + bool ipc_failed; }; struct isp_chan_ops { int (*handle)(struct apple_isp *isp, struct isp_channel *chan); + /* Called after the response slot and its doorbell are published. */ + void (*complete)(struct apple_isp *isp); }; struct isp_buffer { struct vb2_v4l2_buffer vb; struct list_head link; struct isp_surf surfs[VB2_MAX_PLANES]; + u64 tag; + enum vb2_buffer_state return_state; }; #define to_isp_buffer(x) container_of((x), struct isp_buffer, vb) @@ -395,4 +421,36 @@ enum { ((isp)->hw->capture_meta_size ? ISP_CAPMETA_BUFFERS : 0) #define isp_get_current_format(isp) (isp_get_format(isp, isp->current_ch)) +static inline bool isp_supports_p010(struct apple_isp *isp) +{ + return isp->hw->gen == ISP_GEN_T6040 && + isp_get_current_format(isp)->version == 0x958; +} + +static inline bool isp_has_p010(struct apple_isp *isp) +{ + if (!isp_supports_p010(isp)) + return false; + for (int i = 0; i < isp->num_presets; i++) { + if (isp->presets[i].p010) + return true; + } + return false; +} + +static inline bool isp_is_p010(struct apple_isp *isp) +{ + return isp_get_current_format(isp)->pixelformat == V4L2_PIX_FMT_P010; +} + +static inline unsigned int isp_num_rendered(struct apple_isp *isp) +{ + return isp_is_p010(isp) ? 2 : ISP_MAX_BUFFERS; +} + +static inline unsigned int isp_num_meta(struct apple_isp *isp) +{ + return isp_is_p010(isp) ? 8 : ISP_MAX_BUFFERS; +} + #endif /* __ISP_DRV_H__ */ diff --git a/drivers/media/platform/apple/isp/isp-fw.c b/drivers/media/platform/apple/isp/isp-fw.c index 30ff704ef193f..d448225fceec5 100644 --- a/drivers/media/platform/apple/isp/isp-fw.c +++ b/drivers/media/platform/apple/isp/isp-fw.c @@ -212,6 +212,7 @@ static const struct isp_chan_ops sm_ops = { static const struct isp_chan_ops bt_ops = { .handle = ipc_bt_handle, + .complete = ipc_bt_complete, }; static irqreturn_t apple_isp_isr(int irq, void *dev) diff --git a/drivers/media/platform/apple/isp/isp-ipc-test.c b/drivers/media/platform/apple/isp/isp-ipc-test.c index 6521cecb4cfb6..caa9e92f735f1 100644 --- a/drivers/media/platform/apple/isp/isp-ipc-test.c +++ b/drivers/media/platform/apple/isp/isp-ipc-test.c @@ -144,7 +144,69 @@ static void isp_test_sm_free_log_bt(struct kunit *test) KUNIT_EXPECT_NULL(test, t->isp->bt_surf); } +static void isp_test_command_ring_wrap(struct kunit *test) +{ + struct isp_sm_test *t = isp_sm_test_alloc(test); + struct isp_channel *chan = t->chan; + struct isp_message *slots; + + chan->num = 8; + chan->virt = kunit_kcalloc(test, chan->num, sizeof(*slots), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, chan->virt); + slots = chan->virt; + chan->req.arg0 = 0x5000000; + isp_get_current_format(t->isp)->pixelformat = V4L2_PIX_FMT_P010; + for (unsigned int i = 0; i < 100000; i++) { + unsigned int cursor = chan->cursor; + + /* Re-publication must clear the old ACK before waiting on a wrap. */ + slots[cursor].arg0 = chan->req.arg0; + KUNIT_ASSERT_FALSE(test, chan_tx_done(t->isp, chan)); + KUNIT_ASSERT_EQ(test, chan->cursor, cursor); + slots[cursor].arg0 |= ISP_IPC_FLAG_ACK; + KUNIT_ASSERT_TRUE(test, chan_tx_done(t->isp, chan)); + KUNIT_ASSERT_EQ(test, chan->cursor, (cursor + 1) % chan->num); + } + KUNIT_EXPECT_EQ(test, chan->cursor, 0); +} + +static void isp_test_late_ack_keeps_cursor(struct kunit *test) +{ + struct isp_sm_test *t = isp_sm_test_alloc(test); + struct isp_message response = { .arg0 = 0x5000001 }; + + isp_get_current_format(t->isp)->pixelformat = V4L2_PIX_FMT_P010; + t->chan->virt = &response; + t->chan->num = 8; + t->chan->req.arg0 = 0x5000000; + t->chan->deadline_active = true; + t->chan->deadline = jiffies; + KUNIT_EXPECT_FALSE(test, chan_tx_done(t->isp, t->chan)); + KUNIT_EXPECT_EQ(test, t->chan->cursor, 0); + t->chan->deadline = jiffies + HZ; + KUNIT_EXPECT_TRUE(test, chan_tx_done(t->isp, t->chan)); + KUNIT_EXPECT_EQ(test, t->chan->cursor, 1); +} + +static void isp_test_message_payload_copy(struct kunit *test) +{ + struct isp_sm_test *t = isp_sm_test_alloc(test); + struct isp_message response = { + .arg0 = 0x5000001, .arg1 = 1, .arg2 = 2, .arg3 = 3, + .arg4 = 4, .arg5 = 5, .arg6 = 6, .arg7 = 7, + }; + struct isp_message copied = {}; + + isp_get_current_format(t->isp)->pixelformat = V4L2_PIX_FMT_P010; + t->chan->virt = &response; + chan_read_msg_index(t->isp, t->chan, &copied, 0); + KUNIT_EXPECT_MEMEQ(test, &copied, &response, sizeof(response)); +} + static struct kunit_case isp_ipc_test_cases[] = { + KUNIT_CASE(isp_test_message_payload_copy), + KUNIT_CASE(isp_test_late_ack_keeps_cursor), + KUNIT_CASE(isp_test_command_ring_wrap), KUNIT_CASE(isp_test_sm_free_releases), KUNIT_CASE(isp_test_sm_free_alias), KUNIT_CASE(isp_test_sm_free_unknown), diff --git a/drivers/media/platform/apple/isp/isp-ipc.c b/drivers/media/platform/apple/isp/isp-ipc.c index f709f6e305763..74cc717eadc07 100644 --- a/drivers/media/platform/apple/isp/isp-ipc.c +++ b/drivers/media/platform/apple/isp/isp-ipc.c @@ -6,6 +6,10 @@ #include "isp-regs.h" #include "isp-fw.h" +#if IS_ENABLED(CONFIG_VIDEO_APPLE_ISP_KUNIT_TEST) +#include +#endif + #define ISP_IPC_FLAG_TERMINAL_ACK 0x3 /* longer firmware log lines are cut */ #define ISP_IPC_TERMINAL_MAX_LEN 512 @@ -66,7 +70,17 @@ static inline void chan_read_msg_index(struct apple_isp *isp, struct isp_channel *chan, struct isp_message *msg, u32 index) { - memcpy(msg, chan_msg_virt(chan, index), sizeof(*msg)); + if (isp_is_p010(isp)) { + u64 *slot = chan_msg_virt(chan, index); + u64 arg0 = READ_ONCE(*slot); + + /* Acquire validity before consuming the firmware-owned payload. */ + dma_rmb(); + memcpy(msg, slot, sizeof(*msg)); + msg->arg0 = arg0; + } else { + memcpy(msg, chan_msg_virt(chan, index), sizeof(*msg)); + } } static inline void chan_read_msg(struct apple_isp *isp, @@ -124,6 +138,8 @@ static int chan_handle_once(struct apple_isp *isp, struct isp_channel *chan) isp_mbox2_write32(isp, ISP_MBOX2_IRQ_DOORBELL, chan->doorbell); chan_update_cursor(chan); + if (chan->ops->complete) + chan->ops->complete(isp); return 0; } @@ -139,10 +155,12 @@ static inline bool chan_rx_done(struct apple_isp *isp, struct isp_channel *chan) int ipc_chan_handle(struct apple_isp *isp, struct isp_channel *chan) { + unsigned int budget = isp_is_p010(isp) ? chan->num : UINT_MAX; int err = 0; mutex_lock(&chan->lock); - while (1) { + /* A busy producer must not starve the other completion/control rings. */ + while (budget--) { chan_read_msg(isp, chan, &chan->req); if (chan_rx_done(isp, chan)) { err = 0; @@ -160,6 +178,9 @@ int ipc_chan_handle(struct apple_isp *isp, struct isp_channel *chan) static inline bool chan_tx_done(struct apple_isp *isp, struct isp_channel *chan) { + if (isp_is_p010(isp) && chan->deadline_active && + time_after_eq(jiffies, chan->deadline)) + return false; dma_rmb(); chan_read_msg(isp, chan, &chan->rsp); @@ -176,6 +197,15 @@ int ipc_chan_send(struct apple_isp *isp, struct isp_channel *chan, { long t; +#if IS_ENABLED(CONFIG_VIDEO_APPLE_ISP_KUNIT_TEST) + KUNIT_STATIC_STUB_REDIRECT(ipc_chan_send, isp, chan, timeout); +#endif + if (isp->ipc_failed) + return -EIO; + if (isp_is_p010(isp) && timeout) { + chan->deadline = jiffies + timeout; + chan->deadline_active = true; + } chan_write_msg(isp, chan, &chan->req); dma_wmb(); @@ -186,6 +216,8 @@ int ipc_chan_send(struct apple_isp *isp, struct isp_channel *chan, t = wait_event_timeout(isp->wait, chan_tx_done(isp, chan), timeout); if (t == 0) { + if (isp_is_p010(isp)) + isp->ipc_failed = true; dev_err(isp->dev, "%s: timed out on request [0x%llx, 0x%llx, 0x%llx]\n", chan->name, chan->req.arg0, chan->req.arg1, @@ -193,6 +225,7 @@ int ipc_chan_send(struct apple_isp *isp, struct isp_channel *chan, return -ETIME; } + chan->deadline_active = false; isp_dbg(isp, "%s: request success (%ld)\n", chan->name, t); return 0; diff --git a/drivers/media/platform/apple/isp/isp-v4l2-test.c b/drivers/media/platform/apple/isp/isp-v4l2-test.c index 895a6d1299c22..398e8e92356ab 100644 --- a/drivers/media/platform/apple/isp/isp-v4l2-test.c +++ b/drivers/media/platform/apple/isp/isp-v4l2-test.c @@ -7,6 +7,7 @@ #include #include +#include #include #define ISP_TEST_FRAME_SIZE 0x1000 @@ -19,6 +20,8 @@ static struct apple_isp *isp_test_alloc(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, isp); mutex_init(&isp->video_lock); + spin_lock_init(&isp->buf_lock); + INIT_LIST_HEAD(&isp->bufs_returned); isp_get_current_format(isp)->total_size = ISP_TEST_FRAME_SIZE; return isp; @@ -34,6 +37,8 @@ static void isp_test_queue_init(struct kunit *test) KUNIT_ASSERT_EQ(test, isp_vb2_queue_init(isp), 0); KUNIT_EXPECT_GE(test, isp->vbq.max_num_buffers, VB2_MAX_FRAME); + KUNIT_EXPECT_EQ(test, isp->vbq.type, V4L2_BUF_TYPE_VIDEO_CAPTURE); + KUNIT_EXPECT_FALSE(test, isp->vbq.is_multiplanar); vb2_queue_release(&isp->vbq); } @@ -99,6 +104,12 @@ static void isp_test_create_bufs_limit(struct kunit *test) KUNIT_EXPECT_EQ(test, isp_test_setup(test, ISP_MAX_BUFFERS, &nbuffers, &num_planes, sizes), -ENOBUFS); + /* The contiguous API must refuse a second independent memory plane. */ + nbuffers = 1; + num_planes = 2; + KUNIT_EXPECT_EQ(test, isp_test_setup(test, 0, &nbuffers, &num_planes, + sizes), -EINVAL); + /* A plane smaller than a frame is still refused. */ nbuffers = 1; num_planes = 1; @@ -107,7 +118,243 @@ static void isp_test_create_bufs_limit(struct kunit *test) sizes), -EINVAL); } +static const struct apple_isp_hw isp_test_t6040 = { + .gen = ISP_GEN_T6040, + .fw_abi = ISP_FW_ABI_H17, +}; + +static void isp_test_p010_format(struct kunit *test) +{ + struct apple_isp *isp = isp_test_alloc(test); + struct isp_format *fmt = isp_get_current_format(isp); + struct isp_preset preset = { .output_dim = { 1920, 1080 }, .p010 = true }; + struct v4l2_format f = {}; + unsigned int sizes[VB2_MAX_PLANES] = {}; + unsigned int nbuffers = 16, num_planes = 0; + + isp->hw = &isp_test_t6040; + fmt->version = 0x958; + KUNIT_ASSERT_EQ(test, isp_set_preset(isp, fmt, &preset, V4L2_PIX_FMT_P010), 0); + isp_get_sp_pix_format(isp, &f, fmt); + KUNIT_EXPECT_EQ(test, f.fmt.pix.pixelformat, V4L2_PIX_FMT_P010); + KUNIT_EXPECT_EQ(test, f.fmt.pix.bytesperline, 3840); + KUNIT_EXPECT_EQ(test, f.fmt.pix.sizeimage, 6220800); + KUNIT_EXPECT_EQ(test, fmt->plane_size[0], (size_t)4147200); + KUNIT_EXPECT_EQ(test, fmt->plane_size[1], (size_t)2073600); + KUNIT_EXPECT_EQ(test, f.fmt.pix.quantization, V4L2_QUANTIZATION_LIM_RANGE); + + isp->vbq.drv_priv = isp; + isp_test_set_num_buffers(test, &isp->vbq, 0); + KUNIT_ASSERT_EQ(test, + isp_vb2_queue_setup(&isp->vbq, &nbuffers, &num_planes, sizes, NULL), 0); + KUNIT_EXPECT_EQ(test, nbuffers, 2); + KUNIT_EXPECT_EQ(test, num_planes, 1); + KUNIT_EXPECT_EQ(test, sizes[0], 6220800); + KUNIT_EXPECT_EQ(test, isp_num_meta(isp), 8); + + preset.output_dim = (struct coord){1280, 720}; + KUNIT_ASSERT_EQ(test, isp_set_preset(isp, fmt, &preset, V4L2_PIX_FMT_P010), 0); + KUNIT_EXPECT_EQ(test, fmt->strides[0], 2560); + KUNIT_EXPECT_EQ(test, fmt->total_size, (size_t)2764800); + /* NV12 remains the default and restores the existing pool capacity. */ + KUNIT_ASSERT_EQ(test, isp_set_preset(isp, fmt, &preset, V4L2_PIX_FMT_NV12), 0); + KUNIT_EXPECT_EQ(test, fmt->pixelformat, V4L2_PIX_FMT_NV12); + KUNIT_EXPECT_EQ(test, fmt->total_size, (size_t)1382400); + KUNIT_EXPECT_EQ(test, isp_num_rendered(isp), ISP_MAX_BUFFERS); + KUNIT_EXPECT_EQ(test, isp_num_meta(isp), ISP_MAX_BUFFERS); + +} + +static void isp_test_p010_admission(struct kunit *test) +{ + static const struct apple_isp_hw neo = { .gen = ISP_GEN_T8140 }; + struct apple_isp *isp = isp_test_alloc(test); + struct isp_format *fmt = isp_get_current_format(isp); + struct isp_preset preset = { .output_dim = {1280, 720}, .p010 = true }; + struct v4l2_fmtdesc desc = { .index = 0 }; + + isp->presets = &preset; + isp->num_presets = 1; + isp->hw = &isp_test_t6040; + fmt->version = 0x958; + KUNIT_ASSERT_EQ(test, isp_enum_pixelformat(isp, &desc), 0); + KUNIT_EXPECT_EQ(test, desc.pixelformat, V4L2_PIX_FMT_NV12); + desc.index = 1; + KUNIT_ASSERT_EQ(test, isp_enum_pixelformat(isp, &desc), 0); + KUNIT_EXPECT_EQ(test, desc.pixelformat, V4L2_PIX_FMT_P010); + desc.index = 2; + KUNIT_EXPECT_EQ(test, isp_enum_pixelformat(isp, &desc), -EINVAL); + desc.index = 1; + isp->hw = &neo; + KUNIT_EXPECT_EQ(test, isp_enum_pixelformat(isp, &desc), -EINVAL); + KUNIT_ASSERT_EQ(test, isp_set_preset(isp, fmt, &preset, V4L2_PIX_FMT_P010), 0); + KUNIT_EXPECT_EQ(test, fmt->pixelformat, V4L2_PIX_FMT_NV12); + KUNIT_EXPECT_EQ(test, fmt->strides[0], 1280); +} + +static void isp_test_p010_completion_identity(struct kunit *test) +{ + struct isp_format fmt = { .plane_size = {4147200, 2073600} }; + struct isp_buffer buf = { .surfs = {{ .iova = 0x10020000000ULL }}, .tag = 42 }; + struct isp_buflist_buffer desc = { + .iovas = {0x20000000, 0x203f4800}, + .num_planes = 2, .pool_type = CISP_POOL_TYPE_RENDERED_SCL1, .tag = 42, + }; + + KUNIT_EXPECT_TRUE(test, isp_p010_buffer_matches(&fmt, &buf, &desc)); + desc.tag--; + KUNIT_EXPECT_FALSE(test, isp_p010_buffer_matches(&fmt, &buf, &desc)); + desc.tag++; + desc.iovas[1]++; + KUNIT_EXPECT_FALSE(test, isp_p010_buffer_matches(&fmt, &buf, &desc)); + desc.iovas[1]--; + desc.iovas[0] |= BIT_ULL(40); + /* Reject mixed encodings, accept both exact full submitted addresses. */ + KUNIT_EXPECT_FALSE(test, isp_p010_buffer_matches(&fmt, &buf, &desc)); + desc.iovas[1] |= BIT_ULL(40); + KUNIT_EXPECT_TRUE(test, isp_p010_buffer_matches(&fmt, &buf, &desc)); + desc.iovas[0] |= BIT_ULL(41); + desc.iovas[1] |= BIT_ULL(41); + KUNIT_EXPECT_FALSE(test, isp_p010_buffer_matches(&fmt, &buf, &desc)); + desc.iovas[0] = (u32)desc.iovas[0]; + desc.iovas[1] = (u32)desc.iovas[1]; + desc.pool_type = CISP_POOL_TYPE_RENDERED; + KUNIT_EXPECT_FALSE(test, isp_p010_buffer_matches(&fmt, &buf, &desc)); + desc.pool_type = CISP_POOL_TYPE_RENDERED_SCL1; + desc.num_planes = 1; + KUNIT_EXPECT_FALSE(test, isp_p010_buffer_matches(&fmt, &buf, &desc)); +} + +static void isp_test_p010_report_defers_completion(struct kunit *test) +{ + struct apple_isp *isp = isp_test_alloc(test); + struct isp_format *fmt = isp_get_current_format(isp); + struct isp_buffer *buf = kunit_kzalloc(test, sizeof(*buf), GFP_KERNEL); + struct { + u64 type, count; + struct isp_buflist_buffer desc; + } report = { + .type = 2, .count = 1, + .desc = { .iovas = {0x20000000, 0x203f4800}, + .num_planes = 2, .pool_type = 9, .tag = 42 }, + }; + struct isp_surf report_surf = { + .iova = 0x30000000, .size = sizeof(report), .virt = &report, + }; + struct isp_channel chan = { + .req = { .arg0 = report_surf.iova, .arg1 = sizeof(report), + .arg2 = ISP_IPC_BUFEXC_FLAG_RENDER }, + }; + + KUNIT_ASSERT_NOT_NULL(test, buf); + fmt->pixelformat = V4L2_PIX_FMT_P010; + fmt->plane_size[0] = 4147200; + isp->fw_iova_mask = U64_MAX; + isp->bt_surf = &report_surf; + buf->surfs[0].iova = 0x10020000000ULL; + buf->tag = 42; + INIT_LIST_HEAD(&isp->bufs_submitted); + list_add(&buf->link, &isp->bufs_submitted); + /* There is no vb2 queue: completing it before the ACK would fault. */ + KUNIT_ASSERT_EQ(test, ipc_bt_handle(isp, &chan), 0); + KUNIT_EXPECT_TRUE(test, list_empty(&isp->bufs_submitted)); + KUNIT_EXPECT_PTR_EQ(test, + list_first_entry(&isp->bufs_returned, struct isp_buffer, link), buf); + KUNIT_EXPECT_EQ(test, buf->return_state, VB2_BUF_STATE_DONE); + KUNIT_EXPECT_EQ(test, chan.rsp.arg0, report_surf.iova | ISP_IPC_FLAG_ACK); +} + +static void isp_test_p010_mode_selection(struct kunit *test) +{ + struct apple_isp *isp = isp_test_alloc(test); + struct isp_preset presets[] = { + { .index = 5, .output_dim = {1920, 1080} }, + { .index = 5, .output_dim = {1280, 720}, .p010 = true }, + { .index = 1, .output_dim = {4096, 3072} }, + }; + + isp->hw = &isp_test_t6040; + isp_get_current_format(isp)->version = 0x958; + isp->presets = presets; + isp->num_presets = ARRAY_SIZE(presets); + KUNIT_EXPECT_PTR_EQ(test, isp_select_preset(isp, 0, 0, V4L2_PIX_FMT_P010), &presets[1]); + KUNIT_EXPECT_PTR_EQ(test, isp_select_preset(isp, 1920, 1080, V4L2_PIX_FMT_P010), + &presets[1]); + KUNIT_EXPECT_PTR_EQ(test, isp_select_preset(isp, 4096, 3072, V4L2_PIX_FMT_P010), + &presets[1]); + KUNIT_EXPECT_PTR_EQ(test, isp_select_preset(isp, 1920, 1080, V4L2_PIX_FMT_NV12), + &presets[0]); + /* Only a matching firmware output enables the larger P010 size. */ + presets[2].p010 = true; + KUNIT_EXPECT_PTR_EQ(test, isp_select_preset(isp, 4096, 3072, V4L2_PIX_FMT_P010), + &presets[2]); +} + +static int isp_test_buffer_ack(struct apple_isp *isp, struct isp_channel *chan, + unsigned long timeout) +{ + unsigned int *calls = kunit_get_current_test()->priv; + + (*calls)++; + return 0; +} + +static void isp_test_imx958_last_lease_requeue(struct kunit *test) +{ + struct apple_isp *isp = isp_test_alloc(test); + struct isp_format *fmt = isp_get_current_format(isp); + struct isp_buffer *buf = kunit_kzalloc(test, sizeof(*buf), GFP_KERNEL); + struct isp_buffer *submitted; + unsigned int calls = 0; + + KUNIT_ASSERT_NOT_NULL(test, buf); + isp->hw = &isp_test_t6040; + fmt->version = 0x958; + fmt->pixelformat = V4L2_PIX_FMT_P010; + fmt->num_planes = 2; + fmt->plane_size[0] = 1843200; + fmt->plane_size[1] = 921600; + INIT_LIST_HEAD(&isp->bufs_pending); + INIT_LIST_HEAD(&isp->bufs_submitted); + isp->cmd_virt = kunit_kzalloc(test, ISP_CMD_AREA_SIZE(0), GFP_KERNEL); + isp->chan_bh = kunit_kzalloc(test, sizeof(*isp->chan_bh), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, isp->cmd_virt); + KUNIT_ASSERT_NOT_NULL(test, isp->chan_bh); + for (unsigned int i = 0; i < ISP_MAX_BUFFERS; i++) { + isp->meta_surfs[i] = kunit_kzalloc(test, sizeof(struct isp_surf), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, isp->meta_surfs[i]); + isp->meta_surfs[i]->submitted = true; + } + isp->vbq.drv_priv = isp; + buf->vb.vb2_buf.vb2_queue = &isp->vbq; + buf->surfs[0].iova = 0x20000000; + set_bit(ISP_STATE_STREAMING, &isp->state); + test->priv = &calls; + kunit_activate_static_stub(test, ipc_chan_send, isp_test_buffer_ack); + for (unsigned int format = 0; format < 2; format++) { + fmt->pixelformat = format ? V4L2_PIX_FMT_NV12 : V4L2_PIX_FMT_P010; + calls = 0; + for (unsigned int i = 1; i <= 10000; i++) { + /* Both lists empty, as after the last completion was dequeued. */ + isp_vb2_buf_queue(&buf->vb.vb2_buf); + KUNIT_ASSERT_EQ(test, calls, i); + if (!format) + KUNIT_ASSERT_EQ(test, buf->tag, (u64)i); + KUNIT_ASSERT_TRUE(test, list_empty(&isp->bufs_pending)); + submitted = list_first_entry(&isp->bufs_submitted, struct isp_buffer, link); + KUNIT_ASSERT_PTR_EQ(test, submitted, buf); + list_del(&buf->link); + } + } +} + static struct kunit_case isp_v4l2_test_cases[] = { + KUNIT_CASE(isp_test_imx958_last_lease_requeue), + KUNIT_CASE(isp_test_p010_report_defers_completion), + KUNIT_CASE(isp_test_p010_mode_selection), + KUNIT_CASE(isp_test_p010_format), + KUNIT_CASE(isp_test_p010_admission), + KUNIT_CASE(isp_test_p010_completion_identity), KUNIT_CASE(isp_test_queue_init), KUNIT_CASE(isp_test_reqbufs_limit), KUNIT_CASE(isp_test_create_bufs_limit), diff --git a/drivers/media/platform/apple/isp/isp-v4l2.c b/drivers/media/platform/apple/isp/isp-v4l2.c index 072b8523279c2..ff0133e34dee5 100644 --- a/drivers/media/platform/apple/isp/isp-v4l2.c +++ b/drivers/media/platform/apple/isp/isp-v4l2.c @@ -21,24 +21,18 @@ #define ISP_MIN_FRAMES 2 #define ISP_MAX_PLANES 4 -#define ISP_MAX_PIX_FORMATS 2 #define ISP_BUFFER_TIMEOUT msecs_to_jiffies(1500) #define ISP_STRIDE_ALIGNMENT 64 /* Capture rates offered to userspace, in frames per second */ static const unsigned int isp_frame_rates[] = { 30, 25, 24, 20, 15 }; -static bool multiplanar = false; -module_param(multiplanar, bool, 0644); -MODULE_PARM_DESC(multiplanar, "Enable multiplanar API"); - struct isp_buflist_buffer { u64 iovas[ISP_MAX_PLANES]; u32 flags[ISP_MAX_PLANES]; u32 num_planes; u32 pool_type; - u32 tag; - u32 pad; + u64 tag; } __packed; static_assert(sizeof(struct isp_buflist_buffer) == ISP_BUFLIST_DESC_SIZE); @@ -57,6 +51,38 @@ static_assert(ISP_CMD_AREA_SIZE(0) >= ISP_IPC_BUFEXC_STAT_SIZE); */ #define ISP_BUFLIST_POOL_CAPTURE_META 2 +static bool isp_p010_buffer_matches(const struct isp_format *fmt, + const struct isp_buffer *buf, + const struct isp_buflist_buffer *desc) +{ + u64 y = buf->surfs[0].iova; + u64 uv = y + fmt->plane_size[0]; + bool full = desc->iovas[0] == y && desc->iovas[1] == uv; + bool low = desc->iovas[0] == (u32)y && desc->iovas[1] == (u32)uv; + + /* Accept the submitted pair or the zero-extended pair observed on H17. */ + return desc->num_planes == 2 && + desc->pool_type == CISP_POOL_TYPE_RENDERED_SCL1 && (full || low) && + !desc->iovas[2] && !desc->iovas[3] && desc->tag == buf->tag; +} + +void ipc_bt_complete(struct apple_isp *isp) +{ + struct isp_buffer *buf, *tmp; + unsigned long flags, i; + + /* The firmware has received its ACK before userspace can reuse a lease. */ + spin_lock_irqsave(&isp->buf_lock, flags); + for_each_set_bit(i, &isp->meta_returned, ISP_MAX_BUFFERS) + isp->meta_surfs[i]->submitted = false; + isp->meta_returned = 0; + list_for_each_entry_safe(buf, tmp, &isp->bufs_returned, link) { + list_del(&buf->link); + vb2_buffer_done(&buf->vb.vb2_buf, buf->return_state); + } + spin_unlock_irqrestore(&isp->buf_lock, flags); +} + int ipc_bt_handle(struct apple_isp *isp, struct isp_channel *chan) { struct isp_message *req = &chan->req, *rsp = &chan->rsp; @@ -96,11 +122,14 @@ int ipc_bt_handle(struct apple_isp *isp, struct isp_channel *chan) bufd->pool_type); */ if (bufd->pool_type == 0) { - for (int j = 0; j < ARRAY_SIZE(isp->meta_surfs); j++) { + for (int j = 0; j < isp_num_meta(isp); j++) { struct isp_surf *meta = isp->meta_surfs[j]; if ((u32)bufd->iovas[0] == (u32)meta->iova) { WARN_ON(!meta->submitted); - meta->submitted = false; + if (isp_is_p010(isp)) + __set_bit(j, &isp->meta_returned); + else + meta->submitted = false; } } } else if (bufd->pool_type == ISP_BUFLIST_POOL_CAPTURE_META && @@ -114,8 +143,10 @@ int ipc_bt_handle(struct apple_isp *isp, struct isp_channel *chan) } else { list_for_each_entry_safe_reverse( buf, tmp, &isp->bufs_submitted, link) { - if ((u32)buf->surfs[0].iova == - (u32)bufd->iovas[0]) { + if (isp_is_p010(isp) ? + isp_p010_buffer_matches(isp_get_current_format(isp), + buf, bufd) : + (u32)buf->surfs[0].iova == (u32)bufd->iovas[0]) { enum vb2_buffer_state state = VB2_BUF_STATE_ERROR; @@ -126,9 +157,13 @@ int ipc_bt_handle(struct apple_isp *isp, struct isp_channel *chan) if (req->arg2 == ISP_IPC_BUFEXC_FLAG_RENDER) state = VB2_BUF_STATE_DONE; - vb2_buffer_done(&buf->vb.vb2_buf, - state); - list_del(&buf->link); + if (isp_is_p010(isp)) { + buf->return_state = state; + list_move_tail(&buf->link, &isp->bufs_returned); + } else { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->link); + } } } } @@ -166,7 +201,7 @@ static int isp_submit_buffers(struct apple_isp *isp, bool pre_start) bl->type = 1; spin_lock_irqsave(&isp->buf_lock, flags); - for (int i = 0; i < ARRAY_SIZE(isp->meta_surfs); i++) { + for (int i = 0; i < isp_num_meta(isp); i++) { struct isp_surf *meta = isp->meta_surfs[i]; if (meta->submitted) @@ -205,9 +240,13 @@ static int isp_submit_buffers(struct apple_isp *isp, bool pre_start) } /* One rendered pool per batch; the rest stays pending for the next. */ - while (num_rendered < ISP_MAX_BUFFERS && + while (num_rendered < isp_num_rendered(isp) && (buf = list_first_entry_or_null(&isp->bufs_pending, struct isp_buffer, link))) { + if (isp_is_p010(isp)) { + buf->tag = ++isp->buffer_tag; + bufd->tag = buf->tag; + } bufd->num_planes = fmt->num_planes; bufd->pool_type = isp->hw->scl1 ? CISP_POOL_TYPE_RENDERED_SCL1 : CISP_POOL_TYPE_RENDERED; @@ -249,6 +288,11 @@ static int isp_submit_buffers(struct apple_isp *isp, bool pre_start) err = ipc_chan_send(isp, chan, ISP_BUFFER_TIMEOUT); if (err) { + if (isp_is_p010(isp)) { + /* Keep leases owned until stop_camera halts the coprocessor. */ + isp->ipc_failed = true; + return err; + } /* If we fail, consider the buffer not submitted. */ dev_err(isp->dev, "%s: failed to send bufs: [0x%llx, 0x%llx, 0x%llx]\n", @@ -269,7 +313,7 @@ static int isp_submit_buffers(struct apple_isp *isp, bool pre_start) &isp->bufs_pending); } } - for (int j = 0; j < ARRAY_SIZE(isp->meta_surfs); j++) { + for (int j = 0; j < isp_num_meta(isp); j++) { struct isp_surf *meta = isp->meta_surfs[j]; if (bufd->iovas[0] == meta->iova) { meta->submitted = false; @@ -308,13 +352,13 @@ static int isp_vb2_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, * REQBUFS frees the old buffers first, CREATE_BUFS adds to them, so * count the buffers the queue already has. */ - if (q_num_bufs >= ISP_MAX_BUFFERS) + if (q_num_bufs >= isp_num_rendered(isp)) return -ENOBUFS; *nbuffers = min_t(unsigned int, *nbuffers, - ISP_MAX_BUFFERS - q_num_bufs); + isp_num_rendered(isp) - q_num_bufs); if (*num_planes) { - if (sizes[0] < fmt->total_size) + if (*num_planes != 1 || sizes[0] < fmt->total_size) return -EINVAL; return 0; @@ -407,8 +451,15 @@ static void isp_vb2_buf_queue(struct vb2_buffer *vb) list_add_tail(&buf->link, &isp->bufs_pending); spin_unlock_irqrestore(&isp->buf_lock, flags); - if (test_bit(ISP_STATE_STREAMING, &isp->state) && !empty) - isp_submit_buffers(isp, false); + /* Restart an empty IMX958 queue when its last returned lease is queued. */ + if (test_bit(ISP_STATE_STREAMING, &isp->state) && + (isp_supports_p010(isp) || !empty)) { + if (isp_submit_buffers(isp, false) && isp_is_p010(isp)) { + apple_isp_firmware_halt(isp); + isp_vb2_release_buffers(isp, VB2_BUF_STATE_ERROR); + vb2_queue_error(&isp->vbq); + } + } } static int apple_isp_start_streaming(struct apple_isp *isp) @@ -565,18 +616,23 @@ static const struct vb2_ops isp_vb2_ops = { }; static int isp_set_preset(struct apple_isp *isp, struct isp_format *fmt, - struct isp_preset *preset) + struct isp_preset *preset, u32 pixelformat) { + unsigned int bytes_per_sample; int i; size_t total_size; fmt->preset = preset; - /* I really fucking hope they all use NV12. */ + /* P010 has MSB-aligned 10-bit samples in little-endian 16-bit words. */ + fmt->pixelformat = pixelformat == V4L2_PIX_FMT_P010 && + isp_supports_p010(isp) && preset->p010 ? + V4L2_PIX_FMT_P010 : V4L2_PIX_FMT_NV12; + bytes_per_sample = fmt->pixelformat == V4L2_PIX_FMT_P010 ? 2 : 1; fmt->num_planes = 2; - fmt->strides[0] = ALIGN(preset->output_dim.x, ISP_STRIDE_ALIGNMENT); - /* UV subsampled interleaved */ - fmt->strides[1] = ALIGN(preset->output_dim.x, ISP_STRIDE_ALIGNMENT); + fmt->strides[0] = ALIGN(preset->output_dim.x * bytes_per_sample, + ISP_STRIDE_ALIGNMENT); + fmt->strides[1] = fmt->strides[0]; fmt->plane_size[0] = fmt->strides[0] * preset->output_dim.y; fmt->plane_size[1] = fmt->strides[1] * preset->output_dim.y / 2; @@ -589,17 +645,25 @@ static int isp_set_preset(struct apple_isp *isp, struct isp_format *fmt, } static struct isp_preset *isp_select_preset(struct apple_isp *isp, u32 width, - u32 height) + u32 height, u32 pixelformat) { struct isp_preset *preset, *best = &isp->presets[0]; int i, score, best_score = INT_MAX; - /* Default if no dimensions */ - if (width == 0 || height == 0) - return &isp->presets[0]; + bool p010 = pixelformat == V4L2_PIX_FMT_P010 && isp_has_p010(isp); + + /* An unspecified P010 size selects the processed 720p preview first. */ + if (width == 0 || height == 0) { + if (!p010) + return &isp->presets[0]; + width = 1280; + height = 720; + } for (i = 0; i < isp->num_presets; i++) { preset = &isp->presets[i]; + if (p010 && !preset->p010) + continue; score = abs((int)preset->output_dim.x - (int)width) + abs((int)preset->output_dim.y - (int)height); if (score < best_score) { @@ -623,44 +687,47 @@ static int isp_vidioc_querycap(struct file *file, void *priv, return 0; } -static int isp_vidioc_enum_format(struct file *file, void *fh, - struct v4l2_fmtdesc *f) +static int isp_enum_pixelformat(struct apple_isp *isp, struct v4l2_fmtdesc *f) { - struct apple_isp *isp = video_drvdata(file); - - if (f->index >= ISP_MAX_PIX_FORMATS) - return -EINVAL; - - switch (f->index) { - case 0: + /* Browser/WebRTC compatibility: the shipped first format remains NV12. */ + if (!f->index) f->pixelformat = V4L2_PIX_FMT_NV12; - break; - case 1: - if (!isp->multiplanar) - return -EINVAL; - f->pixelformat = V4L2_PIX_FMT_NV12M; - break; - default: + else if (f->index == 1 && isp_has_p010(isp)) + f->pixelformat = V4L2_PIX_FMT_P010; + else return -EINVAL; - } - return 0; } +static int isp_vidioc_enum_format(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + return isp_enum_pixelformat(video_drvdata(file), f); +} + static int isp_vidioc_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *f) { struct apple_isp *isp = video_drvdata(file); + struct isp_preset *preset = NULL; + u32 index = 0; - if (f->index >= isp->num_presets) + if (f->pixel_format != V4L2_PIX_FMT_NV12 && + !(f->pixel_format == V4L2_PIX_FMT_P010 && isp_has_p010(isp))) return -EINVAL; - if ((f->pixel_format != V4L2_PIX_FMT_NV12) && - (f->pixel_format != V4L2_PIX_FMT_NV12M)) + for (int i = 0; i < isp->num_presets; i++) { + if (f->pixel_format == V4L2_PIX_FMT_P010 && !isp->presets[i].p010) + continue; + if (index++ == f->index) { + preset = &isp->presets[i]; + break; + } + } + if (!preset) return -EINVAL; - - f->discrete.width = isp->presets[f->index].output_dim.x; - f->discrete.height = isp->presets[f->index].output_dim.y; + f->discrete.width = preset->output_dim.x; + f->discrete.height = preset->output_dim.y; f->type = V4L2_FRMSIZE_TYPE_DISCRETE; return 0; @@ -676,10 +743,12 @@ static int isp_vidioc_enum_frameintervals(struct file *file, void *fh, return -EINVAL; if (interval->pixel_format != V4L2_PIX_FMT_NV12 && - interval->pixel_format != V4L2_PIX_FMT_NV12M) + !(interval->pixel_format == V4L2_PIX_FMT_P010 && isp_has_p010(isp))) return -EINVAL; for (i = 0; i < isp->num_presets; i++) { + if (interval->pixel_format == V4L2_PIX_FMT_P010 && !isp->presets[i].p010) + continue; if (isp->presets[i].output_dim.x == interval->width && isp->presets[i].output_dim.y == interval->height) break; @@ -726,29 +795,12 @@ static inline void isp_get_sp_pix_format(struct apple_isp *isp, f->fmt.pix.sizeimage = fmt->total_size; f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_NV12; + f->fmt.pix.pixelformat = fmt->pixelformat; f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_709; f->fmt.pix.xfer_func = V4L2_XFER_FUNC_709; -} - -static inline void isp_get_mp_pix_format(struct apple_isp *isp, - struct v4l2_format *f, - struct isp_format *fmt) -{ - f->fmt.pix_mp.width = fmt->preset->output_dim.x; - f->fmt.pix_mp.height = fmt->preset->output_dim.y; - f->fmt.pix_mp.num_planes = fmt->num_planes; - for (int i = 0; i < fmt->num_planes; i++) { - f->fmt.pix_mp.plane_fmt[i].sizeimage = fmt->plane_size[i]; - f->fmt.pix_mp.plane_fmt[i].bytesperline = fmt->strides[i]; - } - - f->fmt.pix_mp.field = V4L2_FIELD_NONE; - f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M; - f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709; - f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_709; - f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_709; + if (fmt->pixelformat == V4L2_PIX_FMT_P010) + f->fmt.pix.quantization = V4L2_QUANTIZATION_LIM_RANGE; } static int isp_vidioc_get_format(struct file *file, void *fh, @@ -774,8 +826,9 @@ static int isp_vidioc_set_format(struct file *file, void *fh, if (vb2_is_busy(&isp->vbq)) return -EBUSY; - preset = isp_select_preset(isp, f->fmt.pix.width, f->fmt.pix.height); - err = isp_set_preset(isp, fmt, preset); + preset = isp_select_preset(isp, f->fmt.pix.width, f->fmt.pix.height, + f->fmt.pix.pixelformat); + err = isp_set_preset(isp, fmt, preset, f->fmt.pix.pixelformat); if (err) return err; @@ -794,8 +847,9 @@ static int isp_vidioc_try_format(struct file *file, void *fh, struct isp_preset *preset; int err; - preset = isp_select_preset(isp, f->fmt.pix.width, f->fmt.pix.height); - err = isp_set_preset(isp, &fmt, preset); + preset = isp_select_preset(isp, f->fmt.pix.width, f->fmt.pix.height, + f->fmt.pix.pixelformat); + err = isp_set_preset(isp, &fmt, preset, f->fmt.pix.pixelformat); if (err) return err; @@ -804,69 +858,6 @@ static int isp_vidioc_try_format(struct file *file, void *fh, return 0; } -static int isp_vidioc_get_format_mplane(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct apple_isp *isp = video_drvdata(file); - struct isp_format *fmt = isp_get_current_format(isp); - - if (!isp->multiplanar) - return -ENOTTY; - - isp_get_mp_pix_format(isp, f, fmt); - - return 0; -} - -static int isp_vidioc_set_format_mplane(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct apple_isp *isp = video_drvdata(file); - struct isp_format *fmt = isp_get_current_format(isp); - struct isp_preset *preset; - int err; - - if (!isp->multiplanar) - return -ENOTTY; - - if (vb2_is_busy(&isp->vbq)) - return -EBUSY; - - preset = isp_select_preset(isp, f->fmt.pix_mp.width, - f->fmt.pix_mp.height); - err = isp_set_preset(isp, fmt, preset); - if (err) - return err; - - isp_get_mp_pix_format(isp, f, fmt); - - isp->vbq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - - return 0; -} - -static int isp_vidioc_try_format_mplane(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct apple_isp *isp = video_drvdata(file); - struct isp_format fmt = *isp_get_current_format(isp); - struct isp_preset *preset; - int err; - - if (!isp->multiplanar) - return -ENOTTY; - - preset = isp_select_preset(isp, f->fmt.pix_mp.width, - f->fmt.pix_mp.height); - err = isp_set_preset(isp, &fmt, preset); - if (err) - return err; - - isp_get_mp_pix_format(isp, f, &fmt); - - return 0; -} - static int isp_vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *inp) { @@ -899,9 +890,7 @@ static int isp_vidioc_get_param(struct file *file, void *fh, { struct apple_isp *isp = video_drvdata(file); - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - (!isp->multiplanar || - a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)) + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; @@ -917,9 +906,7 @@ static int isp_vidioc_set_param(struct file *file, void *fh, struct apple_isp *isp = video_drvdata(file); struct v4l2_fract *tpf = &a->parm.capture.timeperframe; - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - (!isp->multiplanar || - a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)) + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; /* The rate is configured when the stream starts. */ @@ -940,6 +927,27 @@ static int isp_vidioc_set_param(struct file *file, void *fh, return 0; } +static int isp_vidioc_log_status(struct file *file, void *fh) +{ + struct apple_isp *isp = video_drvdata(file); + + v4l2_ctrl_log_status(file, fh); + for (unsigned int i = 0; i < isp->num_sensor_configs; i++) { + const struct isp_sensor_config *config = &isp->sensor_configs[i]; + + v4l2_info(&isp->v4l2_dev, + "sensor config %u: input %ux%u output %ux%u clock %u%s\n", + i, config->input_dim.x, config->input_dim.y, + config->output_dim.x, config->output_dim.y, + config->sensor_clk, + config->usable ? "" : " (unverified geometry)"); + /* This explicit diagnostic ioctl replaces a probe-time parameter. */ + print_hex_dump(KERN_INFO, "apple-isp config: ", DUMP_PREFIX_OFFSET, + 16, 4, config->reply, sizeof(config->reply), false); + } + return 0; +} + static const struct v4l2_ioctl_ops isp_v4l2_ioctl_ops = { .vidioc_querycap = isp_vidioc_querycap, @@ -947,9 +955,6 @@ static const struct v4l2_ioctl_ops isp_v4l2_ioctl_ops = { .vidioc_g_fmt_vid_cap = isp_vidioc_get_format, .vidioc_s_fmt_vid_cap = isp_vidioc_set_format, .vidioc_try_fmt_vid_cap = isp_vidioc_try_format, - .vidioc_g_fmt_vid_cap_mplane = isp_vidioc_get_format_mplane, - .vidioc_s_fmt_vid_cap_mplane = isp_vidioc_set_format_mplane, - .vidioc_try_fmt_vid_cap_mplane = isp_vidioc_try_format_mplane, .vidioc_enum_framesizes = isp_vidioc_enum_framesizes, .vidioc_enum_frameintervals = isp_vidioc_enum_frameintervals, @@ -969,7 +974,7 @@ static const struct v4l2_ioctl_ops isp_v4l2_ioctl_ops = { .vidioc_streamon = vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_log_status = isp_vidioc_log_status, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; @@ -1001,6 +1006,7 @@ static void apple_isp_release(struct v4l2_device *v4l2_dev) v4l2_ctrl_handler_free(&isp->ctrl_handler); media_device_cleanup(&isp->mdev); + kfree(isp->sensor_configs); kfree(isp); } @@ -1044,7 +1050,7 @@ int apple_isp_setup_video(struct apple_isp *isp) struct isp_format *fmt = isp_get_current_format(isp); int err; - err = isp_set_preset(isp, fmt, &isp->presets[0]); + err = isp_set_preset(isp, fmt, &isp->presets[0], V4L2_PIX_FMT_NV12); if (err) { dev_err(isp->dev, "failed to set default preset: %d\n", err); return err; @@ -1054,7 +1060,8 @@ int apple_isp_setup_video(struct apple_isp *isp) for (int i = 0; i < ARRAY_SIZE(isp->meta_surfs); i++) { isp->meta_surfs[i] = - isp_alloc_surface_vmap(isp, isp->hw->meta_size); + isp_alloc_surface_vmap(isp, isp_has_p010(isp) ? 0x8000 : + isp->hw->meta_size); if (!isp->meta_surfs[i]) { isp_err(isp, "failed to alloc meta surface\n"); err = -ENOMEM; @@ -1086,8 +1093,6 @@ int apple_isp_setup_video(struct apple_isp *isp) goto media_cleanup; } - isp->multiplanar = multiplanar; - /* * Auto exposure is always on. By default it holds the frame rate, as * V4L2 specifies; with exposure priority it may slow down in low light. @@ -1119,8 +1124,6 @@ int apple_isp_setup_video(struct apple_isp *isp) vdev->fops = &isp_v4l2_fops; vdev->ioctl_ops = &isp_v4l2_ioctl_ops; vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - if (isp->multiplanar) - vdev->device_caps |= V4L2_CAP_VIDEO_CAPTURE_MPLANE; vdev->entity.flags = MEDIA_ENT_FL_DEFAULT; vdev->v4l2_dev = &isp->v4l2_dev; vdev->vfl_type = VFL_TYPE_VIDEO; diff --git a/drivers/media/platform/apple/isp/isp-v4l2.h b/drivers/media/platform/apple/isp/isp-v4l2.h index 622288296aaae..55135594576b2 100644 --- a/drivers/media/platform/apple/isp/isp-v4l2.h +++ b/drivers/media/platform/apple/isp/isp-v4l2.h @@ -9,6 +9,7 @@ int apple_isp_setup_video(struct apple_isp *isp); void apple_isp_remove_video(struct apple_isp *isp); void apple_isp_free_video(struct apple_isp *isp); +void ipc_bt_complete(struct apple_isp *isp); int ipc_bt_handle(struct apple_isp *isp, struct isp_channel *chan); int apple_isp_video_suspend(struct apple_isp *isp); diff --git a/drivers/phy/apple/atc-t6040-dp2-data.h b/drivers/phy/apple/atc-t6040-dp2-data.h new file mode 100644 index 0000000000000..c76fbe14d5ac3 --- /dev/null +++ b/drivers/phy/apple/atc-t6040-dp2-data.h @@ -0,0 +1,462 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Two-lane DisplayPort link sequences for the T6040 ATC core. + * From Dj's public T6050 combo PHY tables, licensed GPL-2.0-only OR MIT: + * https://github.com/aurora-silicon/linux/pull/480 + * The common ATC banks and two USB3/DP lane groups match the T6040 core. + */ +static const struct atc_op dp_group0_off[] = { + { ATC_MASK, 39, 0x14, 0x1007, 0x7, 0 }, + { ATC_MASK, 39, 0x18, 0x1, 0x0, 0 }, + { ATC_POLL, 39, 0x1c, 0x1, 0x0, 5000 }, + { ATC_MASK, 14, 0x0, 0x1, 0x1, 0 }, + { ATC_MASK, 22, 0x1c, 0xc00000, 0xc00000, 0 }, + { ATC_MASK, 22, 0x10, 0x600, 0x400, 0 }, + { ATC_MASK, 22, 0x4, 0x30000000, 0x20000000, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 22, 0x44, 0x100000, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 22, 0x44, 0x400000, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 22, 0x44, 0x4000000, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 22, 0x44, 0x1000000, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 22, 0x44, 0x1000, 0x0, 0 }, + { ATC_MASK, 2, 0x120, 0x100, 0x100, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x120, 0x1, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x120, 0x10, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 19, 0x44, 0x6000000, 0x4000000, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 19, 0x44, 0x18000000, 0x10000000, 0 }, + { ATC_MASK, 19, 0x24, 0xc0000000, 0x80000000, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 19, 0x24, 0x30000000, 0x20000000, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 19, 0x28, 0x10, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 19, 0x40, 0xc000, 0xc000, 0 }, + { ATC_MASK, 16, 0x1d0, 0x3, 0x1, 0 }, + { ATC_MASK, 19, 0x40, 0x10000, 0x0, 0 }, + { ATC_MASK, 2, 0xc, 0x4000, 0x4000, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0xc, 0x40, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0xc, 0x400, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x10, 0x1, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x10, 0x10, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 14, 0x0, 0xc, 0x8, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x160, 0x40000, 0x40000, 0 }, + { ATC_MASK, 3, 0x8, 0x1, 0x1, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x160, 0x80000, 0x0, 0 }, + { ATC_MASK, 3, 0x8, 0x2, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x160, 0x80000, 0x80000, 0 }, + { ATC_MASK, 3, 0x8, 0x2, 0x2, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x160, 0x40000, 0x0, 0 }, + { ATC_MASK, 3, 0x8, 0x1, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 6, 0x1c, 0x7, 0x1, 0 }, + { ATC_MASK, 6, 0x1c, 0x8, 0x8, 0 }, + { ATC_POLL, 6, 0x2c, 0xff00, 0x100, 500000 }, + { ATC_MASK, 6, 0x1c, 0x8, 0x0, 0 }, + { ATC_POLL, 6, 0x2c, 0xff00, 0x0, 500000 }, + { ATC_POLL, 14, 0x34, 0x8, 0x0, 500000 }, + { ATC_DELAY, 0, 0x0, 0x0, 0xa, 0 }, + { ATC_MASK, 6, 0x34, 0xf, 0x1, 0 }, + { ATC_MASK, 6, 0x1c, 0x7, 0x0, 0 }, + { ATC_MASK, 6, 0x1c, 0x8, 0x8, 0 }, + { ATC_POLL, 6, 0x2c, 0xff00, 0x100, 500000 }, + { ATC_MASK, 6, 0x1c, 0x8, 0x0, 0 }, + { ATC_POLL, 6, 0x2c, 0xff00, 0x0, 500000 }, + { ATC_MASK, 8, 0x0, 0xaaa, 0x0, 0 }, +}; + +static const struct atc_rate_op dp_group0_on[] = { + { { ATC_MASK, 39, 0x14, 0x7, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 39, 0x18, 0x1, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x6c, 0x1c, 0, 0 }, 0xf, { 0x8, 0x8, 0x8, 0x8 } }, + { { ATC_MASK, 14, 0x0, 0x40fff0, 0, 0 }, 0xf, { 0x1110, 0x1110, 0x1110, 0x1110 } }, + { { ATC_MASK, 8, 0x34, 0x3, 0, 0 }, 0x1, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x80, 0xffffffff, 0, 0 }, 0x1, { 0x1e0e021c, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x84, 0xfffffff, 0, 0 }, 0x1, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x88, 0x7fffff, 0, 0 }, 0x1, { 0x156600, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 8, 0x8, 0x1f0000, 0, 0 }, 0x1, { 0x70000, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 8, 0x18, 0x1, 0, 0 }, 0x1, { 0x1, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 8, 0x34, 0x3, 0, 0 }, 0x2, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x80, 0xffffffff, 0, 0 }, 0x2, { 0x0, 0x1e0e01c2, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x84, 0xfffffff, 0, 0 }, 0x2, { 0x0, 0x7fffffe, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x88, 0x7fffff, 0, 0 }, 0x2, { 0x0, 0x155200, 0x0, 0x0 } }, + { { ATC_MASK, 8, 0x8, 0x1f0000, 0, 0 }, 0x2, { 0x0, 0x70000, 0x0, 0x0 } }, + { { ATC_MASK, 8, 0x18, 0x1, 0, 0 }, 0x2, { 0x0, 0x1, 0x0, 0x0 } }, + { { ATC_MASK, 8, 0x34, 0x3, 0, 0 }, 0x4, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x80, 0xffffffff, 0, 0 }, 0x4, { 0x0, 0x0, 0x1e0e01c2, 0x0 } }, + { { ATC_MASK, 7, 0x84, 0xfffffff, 0, 0 }, 0x4, { 0x0, 0x0, 0x7fffffe, 0x0 } }, + { { ATC_MASK, 7, 0x88, 0x7fffff, 0, 0 }, 0x4, { 0x0, 0x0, 0x554800, 0x0 } }, + { { ATC_MASK, 8, 0x8, 0x1f0000, 0, 0 }, 0x4, { 0x0, 0x0, 0x70000, 0x0 } }, + { { ATC_MASK, 8, 0x18, 0x1, 0, 0 }, 0x4, { 0x0, 0x0, 0x1, 0x0 } }, + { { ATC_MASK, 8, 0x34, 0x3, 0, 0 }, 0x8, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x80, 0xffffffff, 0, 0 }, 0x8, { 0x0, 0x0, 0x0, 0x1e0e02a3 } }, + { { ATC_MASK, 7, 0x84, 0xfffffff, 0, 0 }, 0x8, { 0x0, 0x0, 0x0, 0xbff7ffc } }, + { { ATC_MASK, 7, 0x88, 0x7fffff, 0, 0 }, 0x8, { 0x0, 0x0, 0x0, 0x564800 } }, + { { ATC_MASK, 8, 0x8, 0x1f0000, 0, 0 }, 0x8, { 0x0, 0x0, 0x0, 0x70000 } }, + { { ATC_MASK, 8, 0x18, 0x1, 0, 0 }, 0x8, { 0x0, 0x0, 0x0, 0x1 } }, + { { ATC_MASK, 8, 0x0, 0xaaa, 0, 0 }, 0xf, { 0xaaa, 0xaaa, 0xaaa, 0xaaa } }, + { { ATC_MASK, 6, 0x30, 0x4000, 0, 0 }, 0xf, { 0x4000, 0x4000, 0x4000, 0x4000 } }, + { { ATC_MASK, 6, 0x34, 0xf, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 6, 0x18, 0x1fe000, 0, 0 }, 0xf, { 0x2000, 0x2000, 0x2000, 0x2000 } }, + { { ATC_MASK, 6, 0x20, 0x1, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 6, 0x1c, 0x7, 0, 0 }, 0xf, { 0x7, 0x7, 0x7, 0x7 } }, + { { ATC_MASK, 6, 0x1c, 0x8, 0, 0 }, 0xf, { 0x8, 0x8, 0x8, 0x8 } }, + { { ATC_POLL, 6, 0x2c, 0xff00, 0, 500000 }, 0xf, { 0x100, 0x100, 0x100, 0x100 } }, + { { ATC_MASK, 6, 0x1c, 0x8, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_POLL, 6, 0x2c, 0xff00, 0, 500000 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_POLL, 14, 0x34, 0x8, 0, 500000 }, 0xf, { 0x8, 0x8, 0x8, 0x8 } }, + { { ATC_MASK, 19, 0x3c, 0x18000, 0, 0 }, 0xf, { 0x10000, 0x10000, 0x10000, 0x10000 } }, + { { ATC_MASK, 14, 0x0, 0xc, 0, 0 }, 0xf, { 0xc, 0xc, 0xc, 0xc } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x120, 0x10, 0, 0 }, 0xf, { 0x10, 0x10, 0x10, 0x10 } }, + { { ATC_MASK, 2, 0x120, 0x40, 0, 0 }, 0xf, { 0x40, 0x40, 0x40, 0x40 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x120, 0x1, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x120, 0x4, 0, 0 }, 0xf, { 0x4, 0x4, 0x4, 0x4 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x120, 0x100, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 2, 0x120, 0x400, 0, 0 }, 0xf, { 0x400, 0x400, 0x400, 0x400 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 22, 0x44, 0xfffffc0, 0, 0 }, 0xf, { 0xaafb800, 0xaafb800, 0xaafb800, 0xaafb800 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 22, 0x44, 0x1000000, 0, 0 }, 0xf, { 0x1000000, 0x1000000, 0x1000000, 0x1000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 22, 0x44, 0x4000000, 0, 0 }, 0xf, { 0x4000000, 0x4000000, 0x4000000, 0x4000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 22, 0x44, 0x7c0, 0, 0 }, 0xf, { 0x300, 0x300, 0x300, 0x300 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 22, 0x44, 0x6c000, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 21, 0x100, 0x100000, 0, 0 }, 0xf, { 0x100000, 0x100000, 0x100000, 0x100000 } }, + { { ATC_MASK, 21, 0x58, 0xc00f, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 21, 0x104, 0x20000000, 0, 0 }, 0xf, { 0x20000000, 0x20000000, 0x20000000, 0x20000000 } }, + { { ATC_MASK, 21, 0x100, 0x7ffff, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 22, 0x44, 0x100000, 0, 0 }, 0x7, { 0x100000, 0x100000, 0x100000, 0x0 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0x7, { 0x1, 0x1, 0x1, 0x0 } }, + { { ATC_MASK, 22, 0x44, 0x400000, 0, 0 }, 0x7, { 0x400000, 0x400000, 0x400000, 0x0 } }, + { { ATC_MASK, 22, 0x4, 0x3000000, 0, 0 }, 0xf, { 0x2000000, 0x2000000, 0x2000000, 0x2000000 } }, + { { ATC_MASK, 22, 0x4, 0x30000000, 0, 0 }, 0xf, { 0x30000000, 0x30000000, 0x30000000, 0x30000000 } }, + { { ATC_MASK, 22, 0xc, 0x7000000, 0, 0 }, 0x1, { 0x5000000, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 22, 0xc, 0x7000000, 0, 0 }, 0xe, { 0x0, 0x4000000, 0x4000000, 0x4000000 } }, + { { ATC_MASK, 22, 0x8, 0x3, 0, 0 }, 0xf, { 0x2, 0x2, 0x2, 0x2 } }, + { { ATC_MASK, 22, 0x10, 0x600, 0, 0 }, 0xf, { 0x600, 0x600, 0x600, 0x600 } }, + { { ATC_MASK, 22, 0x1c, 0xc00000, 0, 0 }, 0xf, { 0x800000, 0x800000, 0x800000, 0x800000 } }, + { { ATC_MASK, 19, 0xf4, 0x3000, 0, 0 }, 0xf, { 0x3000, 0x3000, 0x3000, 0x3000 } }, + { { ATC_MASK, 16, 0x188, 0x1, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 16, 0x1d4, 0x700, 0, 0 }, 0xf, { 0x300, 0x300, 0x300, 0x300 } }, + { { ATC_MASK, 19, 0x24, 0x3f0000, 0, 0 }, 0xf, { 0x2a0000, 0x2a0000, 0x2a0000, 0x2a0000 } }, + { { ATC_MASK, 19, 0x3c, 0x18000, 0, 0 }, 0xf, { 0x10000, 0x10000, 0x10000, 0x10000 } }, + { { ATC_MASK, 19, 0x78, 0xc0000, 0, 0 }, 0xf, { 0x80000, 0x80000, 0x80000, 0x80000 } }, + { { ATC_MASK, 19, 0x7c, 0xc0, 0, 0 }, 0xf, { 0x80, 0x80, 0x80, 0x80 } }, + { { ATC_MASK, 19, 0x84, 0xc000, 0, 0 }, 0xf, { 0x8000, 0x8000, 0x8000, 0x8000 } }, + { { ATC_MASK, 19, 0xd4, 0x600000, 0, 0 }, 0xf, { 0x400000, 0x400000, 0x400000, 0x400000 } }, + { { ATC_MASK, 19, 0xd8, 0x300, 0, 0 }, 0xf, { 0x200, 0x200, 0x200, 0x200 } }, + { { ATC_MASK, 19, 0x40, 0xc00, 0, 0 }, 0xf, { 0x800, 0x800, 0x800, 0x800 } }, + { { ATC_MASK, 19, 0x48, 0x600, 0, 0 }, 0xf, { 0x400, 0x400, 0x400, 0x400 } }, + { { ATC_MASK, 19, 0x28, 0x1ff030, 0, 0 }, 0xf, { 0x11c020, 0x11c020, 0x11c020, 0x11c020 } }, + { { ATC_MASK, 19, 0x2c, 0xfc, 0, 0 }, 0xf, { 0xb0, 0xb0, 0xb0, 0xb0 } }, + { { ATC_MASK, 19, 0x28, 0xe00, 0, 0 }, 0xf, { 0x800, 0x800, 0x800, 0x800 } }, + { { ATC_MASK, 19, 0x28, 0x1c0, 0, 0 }, 0xf, { 0x180, 0x180, 0x180, 0x180 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x10, 0x10, 0, 0 }, 0xf, { 0x10, 0x10, 0x10, 0x10 } }, + { { ATC_MASK, 2, 0x10, 0x40, 0, 0 }, 0xf, { 0x40, 0x40, 0x40, 0x40 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x10, 0x1, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x10, 0x4, 0, 0 }, 0xf, { 0x4, 0x4, 0x4, 0x4 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0xc, 0x400, 0, 0 }, 0xf, { 0x400, 0x400, 0x400, 0x400 } }, + { { ATC_MASK, 2, 0xc, 0x1000, 0, 0 }, 0xf, { 0x1000, 0x1000, 0x1000, 0x1000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0xc, 0x40, 0, 0 }, 0xf, { 0x40, 0x40, 0x40, 0x40 } }, + { { ATC_MASK, 2, 0xc, 0x100, 0, 0 }, 0xf, { 0x100, 0x100, 0x100, 0x100 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0xc, 0x4000, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 2, 0xc, 0x10000, 0, 0 }, 0xf, { 0x10000, 0x10000, 0x10000, 0x10000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 16, 0x1d0, 0x3, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 16, 0x1d0, 0x2, 0, 0 }, 0xf, { 0x2, 0x2, 0x2, 0x2 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 19, 0x40, 0x30000, 0, 0 }, 0xf, { 0x30000, 0x30000, 0x30000, 0x30000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 19, 0x40, 0xc000, 0, 0 }, 0xf, { 0xc000, 0xc000, 0xc000, 0xc000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 19, 0x28, 0x3010, 0, 0 }, 0xf, { 0x1010, 0x1010, 0x1010, 0x1010 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 19, 0x28, 0x600, 0, 0 }, 0xf, { 0x200, 0x200, 0x200, 0x200 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 19, 0x28, 0x3000, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 19, 0x24, 0x30000000, 0, 0 }, 0xf, { 0x30000000, 0x30000000, 0x30000000, 0x30000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 19, 0x24, 0xc0000000, 0, 0 }, 0xf, { 0xc0000000, 0xc0000000, 0xc0000000, 0xc0000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 19, 0x28, 0x600, 0, 0 }, 0xf, { 0x600, 0x600, 0x600, 0x600 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 19, 0x28, 0x600, 0, 0 }, 0xf, { 0x400, 0x400, 0x400, 0x400 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 19, 0x28, 0x600, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x2, 0x2, 0x2, 0x2 } }, + { { ATC_MASK, 19, 0x44, 0x18000000, 0, 0 }, 0xf, { 0x18000000, 0x18000000, 0x18000000, 0x18000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x2, 0x2, 0x2, 0x2 } }, + { { ATC_MASK, 19, 0x40, 0x38, 0, 0 }, 0x1, { 0x28, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 19, 0x40, 0x38, 0, 0 }, 0xe, { 0x0, 0x20, 0x20, 0x20 } }, + { { ATC_MASK, 19, 0x44, 0x60000000, 0, 0 }, 0xf, { 0x40000000, 0x40000000, 0x40000000, 0x40000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 19, 0x44, 0x6000000, 0, 0 }, 0xf, { 0x6000000, 0x6000000, 0x6000000, 0x6000000 } }, + { { ATC_MASK, 19, 0x40, 0xc000, 0, 0 }, 0xf, { 0x8000, 0x8000, 0x8000, 0x8000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 20, 0x68, 0xc00f, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 20, 0xac, 0x20000000, 0, 0 }, 0xf, { 0x20000000, 0x20000000, 0x20000000, 0x20000000 } }, + { { ATC_MASK, 20, 0xa8, 0x7ffff, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 20, 0x18, 0x38, 0, 0 }, 0xf, { 0x20, 0x20, 0x20, 0x20 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 14, 0x0, 0x1, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_POLL, 39, 0x1c, 0x1, 0, 5000 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, +}; + +static const struct atc_op dp_group1_off[] = { + { ATC_MASK, 39, 0x14, 0x1007, 0x7, 0 }, + { ATC_MASK, 39, 0x18, 0x1, 0x0, 0 }, + { ATC_POLL, 39, 0x1c, 0x1, 0x0, 5000 }, + { ATC_MASK, 14, 0x0, 0x1, 0x1, 0 }, + { ATC_MASK, 31, 0x1c, 0xc00000, 0xc00000, 0 }, + { ATC_MASK, 31, 0x10, 0x600, 0x400, 0 }, + { ATC_MASK, 31, 0x4, 0x30000000, 0x20000000, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 31, 0x44, 0x100000, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 31, 0x44, 0x400000, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 31, 0x44, 0x4000000, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 31, 0x44, 0x1000000, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 31, 0x44, 0x1000, 0x0, 0 }, + { ATC_MASK, 2, 0x120, 0x200, 0x200, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x120, 0x2, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x120, 0x20, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 28, 0x44, 0x6000000, 0x4000000, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 28, 0x44, 0x18000000, 0x10000000, 0 }, + { ATC_MASK, 28, 0x24, 0xc0000000, 0x80000000, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 28, 0x24, 0x30000000, 0x20000000, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 28, 0x28, 0x10, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 28, 0x40, 0xc000, 0xc000, 0 }, + { ATC_MASK, 25, 0x1d0, 0x3, 0x1, 0 }, + { ATC_MASK, 28, 0x40, 0x10000, 0x0, 0 }, + { ATC_MASK, 2, 0xc, 0x8000, 0x8000, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0xc, 0x80, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0xc, 0x800, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x10, 0x2, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x10, 0x20, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 14, 0x0, 0xc, 0x8, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x160, 0x400000, 0x400000, 0 }, + { ATC_MASK, 3, 0x8, 0x4, 0x4, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x160, 0x800000, 0x0, 0 }, + { ATC_MASK, 3, 0x8, 0x8, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x160, 0x800000, 0x800000, 0 }, + { ATC_MASK, 3, 0x8, 0x8, 0x8, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x160, 0x400000, 0x0, 0 }, + { ATC_MASK, 3, 0x8, 0x4, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 6, 0x1c, 0x7, 0x1, 0 }, + { ATC_MASK, 6, 0x1c, 0x8, 0x8, 0 }, + { ATC_POLL, 6, 0x2c, 0xff00, 0x100, 500000 }, + { ATC_MASK, 6, 0x1c, 0x8, 0x0, 0 }, + { ATC_POLL, 6, 0x2c, 0xff00, 0x0, 500000 }, + { ATC_POLL, 14, 0x34, 0x8, 0x0, 500000 }, + { ATC_DELAY, 0, 0x0, 0x0, 0xa, 0 }, + { ATC_MASK, 6, 0x34, 0xf, 0x1, 0 }, + { ATC_MASK, 6, 0x1c, 0x7, 0x0, 0 }, + { ATC_MASK, 6, 0x1c, 0x8, 0x8, 0 }, + { ATC_POLL, 6, 0x2c, 0xff00, 0x100, 500000 }, + { ATC_MASK, 6, 0x1c, 0x8, 0x0, 0 }, + { ATC_POLL, 6, 0x2c, 0xff00, 0x0, 500000 }, + { ATC_MASK, 8, 0x0, 0xaaa, 0x0, 0 }, +}; + +static const struct atc_rate_op dp_group1_on[] = { + { { ATC_MASK, 39, 0x14, 0x7, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 39, 0x18, 0x1, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x6c, 0xe0, 0, 0 }, 0xf, { 0x40, 0x40, 0x40, 0x40 } }, + { { ATC_MASK, 14, 0x0, 0x40fff0, 0, 0 }, 0xf, { 0x1110, 0x1110, 0x1110, 0x1110 } }, + { { ATC_MASK, 8, 0x34, 0x3, 0, 0 }, 0x1, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x80, 0xffffffff, 0, 0 }, 0x1, { 0x1e0e021c, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x84, 0xfffffff, 0, 0 }, 0x1, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x88, 0x7fffff, 0, 0 }, 0x1, { 0x156600, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 8, 0x8, 0x1f0000, 0, 0 }, 0x1, { 0x70000, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 8, 0x18, 0x1, 0, 0 }, 0x1, { 0x1, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 8, 0x34, 0x3, 0, 0 }, 0x2, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x80, 0xffffffff, 0, 0 }, 0x2, { 0x0, 0x1e0e01c2, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x84, 0xfffffff, 0, 0 }, 0x2, { 0x0, 0x7fffffe, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x88, 0x7fffff, 0, 0 }, 0x2, { 0x0, 0x155200, 0x0, 0x0 } }, + { { ATC_MASK, 8, 0x8, 0x1f0000, 0, 0 }, 0x2, { 0x0, 0x70000, 0x0, 0x0 } }, + { { ATC_MASK, 8, 0x18, 0x1, 0, 0 }, 0x2, { 0x0, 0x1, 0x0, 0x0 } }, + { { ATC_MASK, 8, 0x34, 0x3, 0, 0 }, 0x4, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x80, 0xffffffff, 0, 0 }, 0x4, { 0x0, 0x0, 0x1e0e01c2, 0x0 } }, + { { ATC_MASK, 7, 0x84, 0xfffffff, 0, 0 }, 0x4, { 0x0, 0x0, 0x7fffffe, 0x0 } }, + { { ATC_MASK, 7, 0x88, 0x7fffff, 0, 0 }, 0x4, { 0x0, 0x0, 0x554800, 0x0 } }, + { { ATC_MASK, 8, 0x8, 0x1f0000, 0, 0 }, 0x4, { 0x0, 0x0, 0x70000, 0x0 } }, + { { ATC_MASK, 8, 0x18, 0x1, 0, 0 }, 0x4, { 0x0, 0x0, 0x1, 0x0 } }, + { { ATC_MASK, 8, 0x34, 0x3, 0, 0 }, 0x8, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x80, 0xffffffff, 0, 0 }, 0x8, { 0x0, 0x0, 0x0, 0x1e0e02a3 } }, + { { ATC_MASK, 7, 0x84, 0xfffffff, 0, 0 }, 0x8, { 0x0, 0x0, 0x0, 0xbff7ffc } }, + { { ATC_MASK, 7, 0x88, 0x7fffff, 0, 0 }, 0x8, { 0x0, 0x0, 0x0, 0x564800 } }, + { { ATC_MASK, 8, 0x8, 0x1f0000, 0, 0 }, 0x8, { 0x0, 0x0, 0x0, 0x70000 } }, + { { ATC_MASK, 8, 0x18, 0x1, 0, 0 }, 0x8, { 0x0, 0x0, 0x0, 0x1 } }, + { { ATC_MASK, 8, 0x0, 0xaaa, 0, 0 }, 0xf, { 0xaaa, 0xaaa, 0xaaa, 0xaaa } }, + { { ATC_MASK, 6, 0x30, 0x4000, 0, 0 }, 0xf, { 0x4000, 0x4000, 0x4000, 0x4000 } }, + { { ATC_MASK, 6, 0x34, 0xf, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 6, 0x18, 0x1fe000, 0, 0 }, 0xf, { 0x2000, 0x2000, 0x2000, 0x2000 } }, + { { ATC_MASK, 6, 0x20, 0x1, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 6, 0x1c, 0x7, 0, 0 }, 0xf, { 0x7, 0x7, 0x7, 0x7 } }, + { { ATC_MASK, 6, 0x1c, 0x8, 0, 0 }, 0xf, { 0x8, 0x8, 0x8, 0x8 } }, + { { ATC_POLL, 6, 0x2c, 0xff00, 0, 500000 }, 0xf, { 0x100, 0x100, 0x100, 0x100 } }, + { { ATC_MASK, 6, 0x1c, 0x8, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_POLL, 6, 0x2c, 0xff00, 0, 500000 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_POLL, 14, 0x34, 0x8, 0, 500000 }, 0xf, { 0x8, 0x8, 0x8, 0x8 } }, + { { ATC_MASK, 28, 0x3c, 0x18000, 0, 0 }, 0xf, { 0x10000, 0x10000, 0x10000, 0x10000 } }, + { { ATC_MASK, 14, 0x0, 0xc, 0, 0 }, 0xf, { 0xc, 0xc, 0xc, 0xc } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x120, 0x20, 0, 0 }, 0xf, { 0x20, 0x20, 0x20, 0x20 } }, + { { ATC_MASK, 2, 0x120, 0x80, 0, 0 }, 0xf, { 0x80, 0x80, 0x80, 0x80 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x120, 0x2, 0, 0 }, 0xf, { 0x2, 0x2, 0x2, 0x2 } }, + { { ATC_MASK, 2, 0x120, 0x8, 0, 0 }, 0xf, { 0x8, 0x8, 0x8, 0x8 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x120, 0x200, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 2, 0x120, 0x800, 0, 0 }, 0xf, { 0x800, 0x800, 0x800, 0x800 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 31, 0x44, 0xfffffc0, 0, 0 }, 0xf, { 0xaafb800, 0xaafb800, 0xaafb800, 0xaafb800 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 31, 0x44, 0x1000000, 0, 0 }, 0xf, { 0x1000000, 0x1000000, 0x1000000, 0x1000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 31, 0x44, 0x4000000, 0, 0 }, 0xf, { 0x4000000, 0x4000000, 0x4000000, 0x4000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 31, 0x44, 0x7c0, 0, 0 }, 0xf, { 0x300, 0x300, 0x300, 0x300 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 31, 0x44, 0x6c000, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 30, 0x100, 0x100000, 0, 0 }, 0xf, { 0x100000, 0x100000, 0x100000, 0x100000 } }, + { { ATC_MASK, 30, 0x58, 0xc00f, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 30, 0x104, 0x20000000, 0, 0 }, 0xf, { 0x20000000, 0x20000000, 0x20000000, 0x20000000 } }, + { { ATC_MASK, 30, 0x100, 0x7ffff, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 31, 0x44, 0x100000, 0, 0 }, 0x7, { 0x100000, 0x100000, 0x100000, 0x0 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0x7, { 0x1, 0x1, 0x1, 0x0 } }, + { { ATC_MASK, 31, 0x44, 0x400000, 0, 0 }, 0x7, { 0x400000, 0x400000, 0x400000, 0x0 } }, + { { ATC_MASK, 31, 0x4, 0x3000000, 0, 0 }, 0xf, { 0x2000000, 0x2000000, 0x2000000, 0x2000000 } }, + { { ATC_MASK, 31, 0x4, 0x30000000, 0, 0 }, 0xf, { 0x30000000, 0x30000000, 0x30000000, 0x30000000 } }, + { { ATC_MASK, 31, 0xc, 0x7000000, 0, 0 }, 0x1, { 0x5000000, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 31, 0xc, 0x7000000, 0, 0 }, 0xe, { 0x0, 0x4000000, 0x4000000, 0x4000000 } }, + { { ATC_MASK, 31, 0x8, 0x3, 0, 0 }, 0xf, { 0x2, 0x2, 0x2, 0x2 } }, + { { ATC_MASK, 31, 0x10, 0x600, 0, 0 }, 0xf, { 0x600, 0x600, 0x600, 0x600 } }, + { { ATC_MASK, 31, 0x1c, 0xc00000, 0, 0 }, 0xf, { 0x800000, 0x800000, 0x800000, 0x800000 } }, + { { ATC_MASK, 28, 0xf4, 0x3000, 0, 0 }, 0xf, { 0x3000, 0x3000, 0x3000, 0x3000 } }, + { { ATC_MASK, 25, 0x188, 0x1, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 25, 0x1d4, 0x700, 0, 0 }, 0xf, { 0x300, 0x300, 0x300, 0x300 } }, + { { ATC_MASK, 28, 0x24, 0x3f0000, 0, 0 }, 0xf, { 0x2a0000, 0x2a0000, 0x2a0000, 0x2a0000 } }, + { { ATC_MASK, 28, 0x3c, 0x18000, 0, 0 }, 0xf, { 0x10000, 0x10000, 0x10000, 0x10000 } }, + { { ATC_MASK, 28, 0x78, 0xc0000, 0, 0 }, 0xf, { 0x80000, 0x80000, 0x80000, 0x80000 } }, + { { ATC_MASK, 28, 0x7c, 0xc0, 0, 0 }, 0xf, { 0x80, 0x80, 0x80, 0x80 } }, + { { ATC_MASK, 28, 0x84, 0xc000, 0, 0 }, 0xf, { 0x8000, 0x8000, 0x8000, 0x8000 } }, + { { ATC_MASK, 28, 0xd4, 0x600000, 0, 0 }, 0xf, { 0x400000, 0x400000, 0x400000, 0x400000 } }, + { { ATC_MASK, 28, 0xd8, 0x300, 0, 0 }, 0xf, { 0x200, 0x200, 0x200, 0x200 } }, + { { ATC_MASK, 28, 0x40, 0xc00, 0, 0 }, 0xf, { 0x800, 0x800, 0x800, 0x800 } }, + { { ATC_MASK, 28, 0x48, 0x600, 0, 0 }, 0xf, { 0x400, 0x400, 0x400, 0x400 } }, + { { ATC_MASK, 28, 0x28, 0x1ff030, 0, 0 }, 0xf, { 0x11c020, 0x11c020, 0x11c020, 0x11c020 } }, + { { ATC_MASK, 28, 0x2c, 0xfc, 0, 0 }, 0xf, { 0xb0, 0xb0, 0xb0, 0xb0 } }, + { { ATC_MASK, 28, 0x28, 0xe00, 0, 0 }, 0xf, { 0x800, 0x800, 0x800, 0x800 } }, + { { ATC_MASK, 28, 0x28, 0x1c0, 0, 0 }, 0xf, { 0x180, 0x180, 0x180, 0x180 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x10, 0x20, 0, 0 }, 0xf, { 0x20, 0x20, 0x20, 0x20 } }, + { { ATC_MASK, 2, 0x10, 0x80, 0, 0 }, 0xf, { 0x80, 0x80, 0x80, 0x80 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x10, 0x2, 0, 0 }, 0xf, { 0x2, 0x2, 0x2, 0x2 } }, + { { ATC_MASK, 2, 0x10, 0x8, 0, 0 }, 0xf, { 0x8, 0x8, 0x8, 0x8 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0xc, 0x800, 0, 0 }, 0xf, { 0x800, 0x800, 0x800, 0x800 } }, + { { ATC_MASK, 2, 0xc, 0x2000, 0, 0 }, 0xf, { 0x2000, 0x2000, 0x2000, 0x2000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0xc, 0x80, 0, 0 }, 0xf, { 0x80, 0x80, 0x80, 0x80 } }, + { { ATC_MASK, 2, 0xc, 0x200, 0, 0 }, 0xf, { 0x200, 0x200, 0x200, 0x200 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0xc, 0x8000, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 2, 0xc, 0x20000, 0, 0 }, 0xf, { 0x20000, 0x20000, 0x20000, 0x20000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 25, 0x1d0, 0x3, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 25, 0x1d0, 0x2, 0, 0 }, 0xf, { 0x2, 0x2, 0x2, 0x2 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0x40, 0x30000, 0, 0 }, 0xf, { 0x30000, 0x30000, 0x30000, 0x30000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0x40, 0xc000, 0, 0 }, 0xf, { 0xc000, 0xc000, 0xc000, 0xc000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0x28, 0x3010, 0, 0 }, 0xf, { 0x1010, 0x1010, 0x1010, 0x1010 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0x28, 0x600, 0, 0 }, 0xf, { 0x200, 0x200, 0x200, 0x200 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0x28, 0x3000, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0x24, 0x30000000, 0, 0 }, 0xf, { 0x30000000, 0x30000000, 0x30000000, 0x30000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0x24, 0xc0000000, 0, 0 }, 0xf, { 0xc0000000, 0xc0000000, 0xc0000000, 0xc0000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0x28, 0x600, 0, 0 }, 0xf, { 0x600, 0x600, 0x600, 0x600 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0x28, 0x600, 0, 0 }, 0xf, { 0x400, 0x400, 0x400, 0x400 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0x28, 0x600, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x2, 0x2, 0x2, 0x2 } }, + { { ATC_MASK, 28, 0x44, 0x18000000, 0, 0 }, 0xf, { 0x18000000, 0x18000000, 0x18000000, 0x18000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x2, 0x2, 0x2, 0x2 } }, + { { ATC_MASK, 28, 0x40, 0x38, 0, 0 }, 0x1, { 0x28, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 28, 0x40, 0x38, 0, 0 }, 0xe, { 0x0, 0x20, 0x20, 0x20 } }, + { { ATC_MASK, 28, 0x44, 0x60000000, 0, 0 }, 0xf, { 0x40000000, 0x40000000, 0x40000000, 0x40000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0x44, 0x6000000, 0, 0 }, 0xf, { 0x6000000, 0x6000000, 0x6000000, 0x6000000 } }, + { { ATC_MASK, 28, 0x40, 0xc000, 0, 0 }, 0xf, { 0x8000, 0x8000, 0x8000, 0x8000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 29, 0x68, 0xc00f, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 29, 0xac, 0x20000000, 0, 0 }, 0xf, { 0x20000000, 0x20000000, 0x20000000, 0x20000000 } }, + { { ATC_MASK, 29, 0xa8, 0x7ffff, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 29, 0x18, 0x38, 0, 0 }, 0xf, { 0x20, 0x20, 0x20, 0x20 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 14, 0x0, 0x1, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_POLL, 39, 0x1c, 0x1, 0, 5000 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, +}; diff --git a/drivers/phy/apple/t6040-usb2.c b/drivers/phy/apple/t6040-usb2.c index 20fb5e9da5cb0..cb4146d91faf1 100644 --- a/drivers/phy/apple/t6040-usb2.c +++ b/drivers/phy/apple/t6040-usb2.c @@ -106,7 +106,7 @@ #define T6040_PIPEHANDLER_DUMMY_PHY_READY BIT(15) /* - * DisplayPort (PHY mode 3). Offsets are into the "core" and "cfg" windows + * DisplayPort (four-lane mode 3 and two-lane mode 4). Offsets are into the "core" and "cfg" windows * the USB3 path uses; names follow the paired AppleT6040TypeCPhy driver. */ #define T6040_CORE_AUX_CTRL 0x28000 /* ACIOPHY_AUX_TOP_BLK_AUX_CTRL */ @@ -242,12 +242,15 @@ struct t6040_usb2_phy { bool usb3_lent; /* DP mode took the lanes of this Type-C port */ bool pipe_switched; /* the controller's PIPE runs on this PHY */ - /* DisplayPort (PHY mode 3); dp_ok is false when the DT lacks a DP route */ + /* DisplayPort; dp_ok is false when the DT lacks a DP route */ struct phy *dp; struct typec_mux_dev *mux; bool dp_ok; bool dp_up; /* DP mode is programmed on the PHY */ - bool dp_alt; /* the Type-C port is in DP alt mode, pin assignment C/E */ + bool dp_alt; /* the Type-C port is in DP alt mode */ + bool dp_two_lanes; /* pin D keeps USB3 on the other pair */ + bool dp_mode_two_lanes; /* latched until DP power_off, even after mux exit */ + bool dp_mode_flip; bool dp_flip; /* plug orientation reversed for the DP lanes */ int dp_rate; /* link rate index of the running link sequence, -1 = off */ }; @@ -952,6 +955,9 @@ static void t6040_usb3_down_locked(struct t6040_usb2_phy *tphy) t6040_usb3_pipe_park(tphy); tphy->pipe_switched = false; } + /* DWC3 can drop its role before the DP fabric has released pin D. */ + if (tphy->dp_up && tphy->dp_mode_two_lanes) + return; if (tphy->usb3_up) { u32 uc = readl(t6040_core(tphy) + T6040_CORE_UC_CTRL); @@ -1033,8 +1039,8 @@ static void t6040_usb3_switch_unregister(void *sw) * link rate through phy_configure(): the per-rate link sequence is the T6050 * fixed-DP table (atc-t6050-dp-data.h; the T6050 ADT names its PHY * "atc-phy,t6050", "atc-phy,t6040"), followed by the T6040 DP clock switch in - * the "cfg" window. Pin assignment D (two DP lanes next to USB3) is not - * supported. + * the "cfg" window. Pin assignment D shares USB3's running PHY and uses + * the two-lane group link sequences below. */ /* The fixed DPTX tunable writes (apply_dptx_tunables, shared with USB3). */ @@ -1272,6 +1278,7 @@ struct atc_rate_op { }; #include "atc-t6050-dp-data.h" +#include "atc-t6040-dp2-data.h" static const struct { u8 bank; u32 offset; } t6040_dp_core_banks[] = { {2, 0}, {3, 0x400}, {4, 0x800}, {5, 0xa00}, {6, 0x1000}, @@ -1325,12 +1332,29 @@ static int t6040_dp_seq_step(struct t6040_usb2_phy *tphy, const struct atc_op *s /* Take the link down if it is up, then bring it up at rate index @idx (-1: off). */ static int t6040_dp_set_rate(struct t6040_usb2_phy *tphy, int idx) { + const struct atc_op *off = dp_link_off; + const struct atc_rate_op *on = dp_link_on; + size_t nr_off = ARRAY_SIZE(dp_link_off); + size_t nr_on = ARRAY_SIZE(dp_link_on); size_t i; int ret; + if (tphy->dp_mode_two_lanes) { + if (tphy->dp_mode_flip) { + off = dp_group0_off; + on = dp_group0_on; + nr_off = ARRAY_SIZE(dp_group0_off); + nr_on = ARRAY_SIZE(dp_group0_on); + } else { + off = dp_group1_off; + on = dp_group1_on; + nr_off = ARRAY_SIZE(dp_group1_off); + nr_on = ARRAY_SIZE(dp_group1_on); + } + } if (tphy->dp_rate >= 0) { - for (i = 0; i < ARRAY_SIZE(dp_link_off); i++) { - ret = t6040_dp_seq_step(tphy, &dp_link_off[i]); + for (i = 0; i < nr_off; i++) { + ret = t6040_dp_seq_step(tphy, &off[i]); if (ret) return ret; } @@ -1338,12 +1362,12 @@ static int t6040_dp_set_rate(struct t6040_usb2_phy *tphy, int idx) } if (idx < 0) return 0; - for (i = 0; i < ARRAY_SIZE(dp_link_on); i++) { - struct atc_op op = dp_link_on[i].op; + for (i = 0; i < nr_on; i++) { + struct atc_op op = on[i].op; - if (!(dp_link_on[i].rates & BIT(idx))) + if (!(on[i].rates & BIT(idx))) continue; - op.value = dp_link_on[i].values[idx]; + op.value = on[i].values[idx]; ret = t6040_dp_seq_step(tphy, &op); if (ret) return ret; @@ -1359,7 +1383,8 @@ static int t6040_dp_set_rate(struct t6040_usb2_phy *tphy, int idx) /* Drive settings of the T6050 sequence: lanes 0/1 on PMA group 1, 2/3 on group 0. */ static void t6040_dp_drive(struct t6040_usb2_phy *tphy, unsigned int lane, u32 preset) { - unsigned int group = lane < 2 ? 1 : 0; + unsigned int group = (lane < 2 ? 1 : 0) ^ + (tphy->dp_mode_two_lanes && tphy->dp_mode_flip); bool tx = lane & 1; void __iomem *base = t6040_dp_bank(tphy, (tx ? 21 : 20) + 9 * group); u32 ctrl = ((preset >> 2) & 0xf) | ((preset & 3) << 14); @@ -1407,11 +1432,25 @@ static int t6040_dp_power_on(struct phy *phy) guard(mutex)(&tphy->lock); if (!tphy->dp_ok || tphy->dp_up) return 0; - /* - * A Type-C PHY in DP alt mode (pin assignment C/E) hands all four - * lanes to DP: aciophy_phy_shutdown of the USB3 side first, then the - * same mode-3 init as the fixed DP PHY. - */ + /* Pin D shares the running PHY with USB3. Keep its PIPE and firmware. */ + if (tphy->dp_two_lanes) { + if (!tphy->usb3_ok || !tphy->dp_alt) + return -ENOLINK; + if (!tphy->usb3_up) + t6040_usb3_up_locked(tphy); + if (!tphy->usb3_up) + return -ENOLINK; + /* USB3 startup already applied these DPTX tunables before firmware. */ + t6040_dp_aux_init(tphy); + tphy->dp_rate = -1; + tphy->dp_mode_two_lanes = true; + tphy->dp_mode_flip = tphy->dp_flip; + tphy->dp_up = true; + dev_info(tphy->dev, "DP PHY pin D: two lanes, USB3 retained, %s orientation\n", + tphy->dp_flip ? "reverse" : "normal"); + return 0; + } + /* C/E and the fixed HDMI PHY hand all four lanes to DP. */ if (!t6040_dp_only(tphy)) { t6040_usb3_down_locked(tphy); tphy->usb3_lent = true; @@ -1423,6 +1462,7 @@ static int t6040_dp_power_on(struct phy *phy) } t6040_dp_mode_setup(tphy, tphy->dp_flip); tphy->dp_rate = -1; + tphy->dp_mode_two_lanes = false; tphy->dp_up = true; dev_dbg(tphy->dev, "DP PHY mode set, lane map %s\n", tphy->dp_flip ? "reversed" : "normal"); @@ -1432,22 +1472,33 @@ static int t6040_dp_power_on(struct phy *phy) static int t6040_dp_power_off(struct phy *phy) { struct t6040_usb2_phy *tphy = phy_get_drvdata(phy); + int ret = 0; guard(mutex)(&tphy->lock); + if (tphy->dp_up && tphy->dp_mode_two_lanes) { + ret = t6040_dp_set_rate(tphy, -1); + if (ret) + return ret; + } tphy->dp_up = false; + if (tphy->dp_mode_two_lanes && !tphy->usb3_on) + t6040_usb3_down_locked(tphy); + tphy->dp_mode_two_lanes = false; t6040_usb3_reclaim_locked(tphy); - return 0; + return ret; } -/* GET_MAX_LANE_COUNT: DP on all four lanes in mode 3. */ +/* GET_MAX_LANE_COUNT follows the negotiated pin assignment. */ static int t6040_dp_validate(struct phy *phy, enum phy_mode mode, int submode, union phy_configure_opts *opts_) { + struct t6040_usb2_phy *tphy = phy_get_drvdata(phy); struct phy_configure_opts_dp *opts = &opts_->dp; if (mode != PHY_MODE_DP) return -EINVAL; - opts->lanes = 4; + guard(mutex)(&tphy->lock); + opts->lanes = tphy->dp_two_lanes ? 2 : 4; return 0; } @@ -1461,6 +1512,9 @@ static int t6040_dp_configure(struct phy *phy, union phy_configure_opts *opts_) guard(mutex)(&tphy->lock); if (!tphy->dp_up) return -EINVAL; + if ((opts->set_lanes || opts->set_voltages) && + opts->lanes > (tphy->dp_mode_two_lanes ? 2 : 4)) + return -EINVAL; if (opts->set_rate) { switch (opts->link_rate) { @@ -1491,7 +1545,7 @@ static int t6040_dp_configure(struct phy *phy, union phy_configure_opts *opts_) if (opts->set_voltages && tphy->dp_rate >= 0) { unsigned int i; - for (i = 0; i < opts->lanes && i < 4; i++) { + for (i = 0; i < opts->lanes; i++) { unsigned int v = min_t(unsigned int, opts->voltage[i], 3); unsigned int pe = min_t(unsigned int, opts->pre[i], 3); @@ -1530,10 +1584,9 @@ static int t6040_dp_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *s tphy->dp_flip = (tphy->orientation == TYPEC_ORIENTATION_REVERSE); /* * Latch the DP alt mode state for the DCP's Type-C route. Pin - * assignments C and E put DP on all four lanes (PHY mode 3, as the - * fixed DP route); D (two DP lanes next to USB3, PHY mode 4 with the - * coprocessor) is not supported. The DP lanes themselves are brought - * up by the DCP through phy_power_on()/phy_configure() on the DP phy. + * assignments C and E put DP on all four lanes. D leaves the USB3 + * pair with its running coprocessor and gives DP the other pair. + * The DCP brings the DP lanes up through phy_power_on()/phy_configure(). */ if (dp) { switch (state->mode) { @@ -1542,16 +1595,17 @@ static int t6040_dp_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *s alt = true; break; case TYPEC_DP_STATE_D: - dev_warn_once(tphy->dev, "Type-C mux: DP pin assignment D (two lanes next to USB3) not supported\n"); + alt = true; break; default: break; } } tphy->dp_alt = alt; + tphy->dp_two_lanes = alt && state->mode == TYPEC_DP_STATE_D; dev_dbg(tphy->dev, "Type-C mux: %s state %lu -> DP alt mode %d, lane map %s\n", dp ? "DP" : "non-DP", state->mode, alt, tphy->dp_flip ? "reversed" : "normal"); - if (alt && tphy->dp_up) + if (alt && tphy->dp_up && !tphy->dp_mode_two_lanes && !tphy->dp_two_lanes) t6040_dp_xbar_config(tphy, tphy->dp_flip); t6040_usb3_reclaim_locked(tphy); return 0; diff --git a/drivers/pmdomain/apple/pmp-report-test.c b/drivers/pmdomain/apple/pmp-report-test.c index e04a91d339f23..d99ece1ac4b6f 100644 --- a/drivers/pmdomain/apple/pmp-report-test.c +++ b/drivers/pmdomain/apple/pmp-report-test.c @@ -276,4 +276,5 @@ static struct kunit_suite apple_pmp_report_suite = { }; kunit_test_suite(apple_pmp_report_suite); +MODULE_DESCRIPTION("KUnit tests for Apple PMP report validation"); MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/soc/apple/clpc.c b/drivers/soc/apple/clpc.c index 946bfdb52de92..2620532424cf2 100644 --- a/drivers/soc/apple/clpc.c +++ b/drivers/soc/apple/clpc.c @@ -21,10 +21,17 @@ */ #include +#include +#include +#include +#include +#include +#include #include #include #include #include +#include #include #include #include @@ -95,6 +102,16 @@ struct apple_clpc_vote { unsigned long last_demand; }; +/* Independent lifetime: a late consumer put never dereferences a removed provider. */ +struct apple_clpc_gpu { + struct kref ref; + spinlock_t lock; + bool active; + u64 updated_ns; + u64 sequence; + struct delayed_work *wake; +}; + struct apple_clpc { struct device *dev; struct device *pmc; @@ -103,6 +120,7 @@ struct apple_clpc { /* Samples while a vote is held; idle_work defers to busy CPUs. */ struct delayed_work work; struct delayed_work idle_work; + struct mutex sample_lock; bool stopped; struct apple_clpc_cluster clusters[APPLE_CLPC_MAX_CLUSTERS]; unsigned int nr_clusters; @@ -117,6 +135,10 @@ struct apple_clpc { unsigned long update_errors; struct dentry *debugfs; struct regmap *gpu_pmgr; + struct apple_clpc_gpu *gpu; + u32 last_floor; + u32 last_reason; + u64 last_vote_ns; u32 gpu_offset; /* Graphics powered at the last sample, since when, and that sample's time */ bool gpu_on; @@ -127,6 +149,100 @@ struct apple_clpc { unsigned long gpu_limited_at; }; +/* Protect publication/removal of provider drvdata, never a submission path. */ +static DEFINE_MUTEX(apple_clpc_clients_lock); + +static void apple_clpc_gpu_free(struct kref *ref) +{ + kfree(container_of(ref, struct apple_clpc_gpu, ref)); +} + +void apple_clpc_gpu_set_active(struct apple_clpc_gpu *gpu, bool active) +{ + unsigned long flags; + + if (!gpu) + return; + spin_lock_irqsave(&gpu->lock, flags); + /* A cold GPU publisher must not wait for a deferrable CPU-idle timer. + * Removal clears wake under this same lock before draining work. + */ + if (active && !gpu->active && gpu->wake) + mod_delayed_work(system_freezable_power_efficient_wq, gpu->wake, 0); + gpu->active = active; + gpu->updated_ns = ktime_get_mono_fast_ns(); + gpu->sequence++; + spin_unlock_irqrestore(&gpu->lock, flags); +} +EXPORT_SYMBOL_GPL(apple_clpc_gpu_set_active); + +void apple_clpc_gpu_put(struct apple_clpc_gpu *gpu) +{ + if (gpu) { + apple_clpc_gpu_set_active(gpu, false); + kref_put(&gpu->ref, apple_clpc_gpu_free); + } +} +EXPORT_SYMBOL_GPL(apple_clpc_gpu_put); + +int apple_clpc_gpu_get(struct device *dev, struct apple_clpc_gpu **out) +{ + struct device_node *np; + struct platform_device *provider; + struct apple_clpc *clpc; + struct device_link *link; + int ret = -EPROBE_DEFER; + + *out = NULL; + np = of_parse_phandle(dev->of_node, "apple,clpc", 0); + if (!np) + return of_find_property(dev->of_node, "apple,clpc", NULL) ? -EINVAL : 0; + if (!of_device_is_compatible(np, "apple,t6041-clpc")) { + of_node_put(np); + return -EINVAL; + } + provider = of_find_device_by_node(np); + of_node_put(np); + if (!provider) + return ret; + link = device_link_add(dev, &provider->dev, DL_FLAG_AUTOREMOVE_CONSUMER); + if (!link) { + put_device(&provider->dev); + return -ENOMEM; + } + mutex_lock(&apple_clpc_clients_lock); + clpc = dev_get_drvdata(&provider->dev); + if (clpc && clpc->gpu && refcount_read(&clpc->gpu->ref.refcount) == 1) { + kref_get(&clpc->gpu->ref); + *out = clpc->gpu; + ret = 0; + } + mutex_unlock(&apple_clpc_clients_lock); + put_device(&provider->dev); + return ret; +} +EXPORT_SYMBOL_GPL(apple_clpc_gpu_get); + +static bool apple_clpc_gpu_active(struct apple_clpc_gpu *gpu, u64 *updated, u64 *sequence) +{ + unsigned long flags; + bool active; + + spin_lock_irqsave(&gpu->lock, flags); + active = gpu->active; + *updated = gpu->updated_ns; + *sequence = gpu->sequence; + spin_unlock_irqrestore(&gpu->lock, flags); + return active; +} + +static void apple_clpc_gpu_owner_put(void *data) +{ + struct apple_clpc_gpu *gpu = data; + + kref_put(&gpu->ref, apple_clpc_gpu_free); +} + /* cpufreq policies can appear after probe; pick up new ones as they do. */ static void apple_clpc_find_clusters(struct apple_clpc *clpc) { @@ -246,6 +362,10 @@ static void apple_clpc_queue(struct apple_clpc *clpc, bool idle) * stops; with no vote held, sampling can wait for a CPU that wakes up * anyway rather than waking an idle one every period. */ + /* A demand edge can queue work while the deferrable pass is pending. + * Keep one sampling cadence once the serialized pass settles. + */ + cancel_delayed_work(idle ? &clpc->work : &clpc->idle_work); queue_delayed_work(system_freezable_power_efficient_wq, idle ? &clpc->idle_work : &clpc->work, msecs_to_jiffies(APPLE_CLPC_SAMPLE_MS)); @@ -313,6 +433,7 @@ static void apple_clpc_sample(struct apple_clpc *clpc) unsigned int state[APPLE_CLPC_RAILS]; unsigned long now = jiffies; unsigned int i, rail; + u32 reason = 0, floor; clpc->samples++; if (!cpumask_equal(clpc->covered, cpu_possible_mask) && @@ -328,17 +449,38 @@ static void apple_clpc_sample(struct apple_clpc *clpc) for (rail = 0; rail < APPLE_CLPC_RAILS; rail++) demand[rail] = max(demand[rail], cl->request[rail]); } + for (rail = 0; rail < APPLE_CLPC_RAILS; rail++) + if (demand[rail]) + reason |= 1; + if (clpc->gpu && !clpc->hw->gpu_demand) { + u64 updated, sequence; + + if (apple_clpc_gpu_active(clpc->gpu, &updated, &sequence)) { + reason |= 2; + for (rail = 0; rail < APPLE_CLPC_RAILS; rail++) + demand[rail] = max(demand[rail], + clpc->hw->map[rail][APPLE_CLPC_LEVELS - 1]); + } + } if (clpc->gpu_pmgr && apple_clpc_gpu_streaming(clpc, now) && - !apple_clpc_gpu_limited(clpc, now)) + !apple_clpc_gpu_limited(clpc, now)) { + reason |= 4; for (rail = 0; rail < APPLE_CLPC_RAILS; rail++) demand[rail] = max(demand[rail], clpc->hw->gpu[rail]); + } for (rail = 0; rail < APPLE_CLPC_RAILS; rail++) state[rail] = apple_clpc_vote(&clpc->vote[rail], demand[rail], now); - if (apple_pmc_floor_update(clpc->pmc, clpc->agent, clpc->mask, - apple_clpc_floor(clpc, state))) + floor = apple_clpc_floor(clpc, state); + for (rail = 0; rail < APPLE_CLPC_RAILS; rail++) + if (state[rail] > demand[rail]) + reason |= 16; + clpc->last_floor = floor; + clpc->last_reason = reason; + clpc->last_vote_ns = ktime_get_mono_fast_ns(); + if (apple_pmc_floor_update(clpc->pmc, clpc->agent, clpc->mask, floor)) clpc->update_errors++; /* Sample on time while graphics is powered, to see it power off. */ @@ -347,14 +489,22 @@ static void apple_clpc_sample(struct apple_clpc *clpc) static void apple_clpc_work(struct work_struct *work) { - apple_clpc_sample(container_of(to_delayed_work(work), struct apple_clpc, - work)); + struct apple_clpc *clpc = container_of(to_delayed_work(work), struct apple_clpc, work); + + mutex_lock(&clpc->sample_lock); + if (!READ_ONCE(clpc->stopped)) + apple_clpc_sample(clpc); + mutex_unlock(&clpc->sample_lock); } static void apple_clpc_idle_work(struct work_struct *work) { - apple_clpc_sample(container_of(to_delayed_work(work), struct apple_clpc, - idle_work)); + struct apple_clpc *clpc = container_of(to_delayed_work(work), struct apple_clpc, idle_work); + + mutex_lock(&clpc->sample_lock); + if (!READ_ONCE(clpc->stopped)) + apple_clpc_sample(clpc); + mutex_unlock(&clpc->sample_lock); } static int apple_clpc_status_show(struct seq_file *s, void *unused) @@ -373,6 +523,16 @@ static int apple_clpc_status_show(struct seq_file *s, void *unused) clpc->hw->rail[rail]), clpc->hw->map[rail][0], clpc->hw->map[rail][1], clpc->hw->map[rail][2], clpc->hw->map[rail][3]); + seq_printf(s, "last_vote: %#x reason_bits %#x sampled_ns %llu (cpu=1 host_gpu=2 streaming_gpu=4 hold=16)\n", + READ_ONCE(clpc->last_floor), READ_ONCE(clpc->last_reason), + READ_ONCE(clpc->last_vote_ns)); + if (clpc->gpu) { + u64 updated, sequence; + bool active = apple_clpc_gpu_active(clpc->gpu, &updated, &sequence); + + seq_printf(s, "host_gpu: active %u request_ns %llu sequence %llu\n", + active, updated, sequence); + } seq_printf(s, "samples: %lu update errors: %lu\n", clpc->samples, clpc->update_errors); if (clpc->gpu_pmgr) { @@ -429,6 +589,16 @@ static void apple_clpc_stop(void *data) { struct apple_clpc *clpc = data; + mutex_lock(&apple_clpc_clients_lock); + dev_set_drvdata(clpc->dev, NULL); + mutex_unlock(&apple_clpc_clients_lock); + if (clpc->gpu) { + unsigned long flags; + + spin_lock_irqsave(&clpc->gpu->lock, flags); + clpc->gpu->wake = NULL; + spin_unlock_irqrestore(&clpc->gpu->lock, flags); + } apple_clpc_halt(clpc); debugfs_remove_recursive(clpc->debugfs); apple_clpc_free_clusters(clpc); @@ -448,6 +618,19 @@ static int apple_clpc_probe(struct platform_device *pdev) clpc->dev = dev; clpc->pmc = dev->parent; clpc->hw = of_device_get_match_data(dev); + /* T6040 firmware keeps GFX powered for its whole lifetime. A separate + * host publication/idle input, not that supplier state, drives demand. + */ + if (of_device_is_compatible(dev->of_node, "apple,t6041-clpc")) { + clpc->gpu = kzalloc_obj(*clpc->gpu); + if (!clpc->gpu) + return -ENOMEM; + kref_init(&clpc->gpu->ref); + spin_lock_init(&clpc->gpu->lock); + ret = devm_add_action_or_reset(dev, apple_clpc_gpu_owner_put, clpc->gpu); + if (ret) + return ret; + } if (clpc->hw->gpu_demand) { struct device_node *gpu = of_parse_phandle(dev->of_node, "apple,gpu-power-state", 0); @@ -503,8 +686,11 @@ static int apple_clpc_probe(struct platform_device *pdev) clpc->mask; apple_clpc_find_clusters(clpc); clpc->next_scan = jiffies + HZ; + mutex_init(&clpc->sample_lock); INIT_DELAYED_WORK(&clpc->work, apple_clpc_work); INIT_DEFERRABLE_WORK(&clpc->idle_work, apple_clpc_idle_work); + if (clpc->gpu) + clpc->gpu->wake = &clpc->work; clpc->debugfs = debugfs_create_dir(dev_name(dev), NULL); debugfs_create_file("status", 0400, clpc->debugfs, clpc, @@ -514,7 +700,9 @@ static int apple_clpc_probe(struct platform_device *pdev) if (ret) return ret; + mutex_lock(&apple_clpc_clients_lock); platform_set_drvdata(pdev, clpc); + mutex_unlock(&apple_clpc_clients_lock); apple_clpc_queue(clpc, true); return 0; } @@ -603,6 +791,9 @@ static const struct apple_clpc_hw apple_clpc_hw_t8140 = { * A cluster requests only after three busy samples in a row (12 ms). At idle * a single busy sample raised the floor about 60 % of the time, from short * bursts of background work; three in a row bring that to about 11 %. + * GPU demand bypasses the CPU streak and uses the same qualified DCS 5 / + * fabric 2 states until publication/in-flight demand ends and HOLD_MS expires. + * GFX supplier power cannot drive this input: firmware keeps it always on. */ static const struct apple_clpc_hw apple_clpc_hw_t6040 = { .rail = { [APPLE_CLPC_DCS] = 0, [APPLE_CLPC_SOC] = 1 }, @@ -611,7 +802,96 @@ static const struct apple_clpc_hw apple_clpc_hw_t6040 = { [APPLE_CLPC_SOC] = { 0, 1, 2, 2 }, }, .sustain = 3, + .gpu_demand = false, +}; + +#if IS_BUILTIN(CONFIG_KUNIT) && !defined(MODULE) +#include +#include + +static void apple_clpc_gpu_request_lifetime_test(struct kunit *test) +{ + struct apple_clpc_gpu *gpu = kzalloc_obj(*gpu); + u64 updated, sequence; + + KUNIT_ASSERT_NOT_NULL(test, gpu); + kref_init(&gpu->ref); + spin_lock_init(&gpu->lock); + KUNIT_EXPECT_FALSE(test, apple_clpc_gpu_active(gpu, &updated, &sequence)); + KUNIT_EXPECT_EQ(test, sequence, 0ull); + /* Provider and one consumer own independent references. */ + kref_get(&gpu->ref); + apple_clpc_gpu_set_active(gpu, true); + KUNIT_EXPECT_TRUE(test, apple_clpc_gpu_active(gpu, &updated, &sequence)); + KUNIT_EXPECT_GT(test, updated, 0ull); + KUNIT_EXPECT_EQ(test, sequence, 1ull); + apple_clpc_gpu_put(gpu); + KUNIT_EXPECT_FALSE(test, apple_clpc_gpu_active(gpu, &updated, &sequence)); + KUNIT_EXPECT_EQ(test, sequence, 2ull); + kref_put(&gpu->ref, apple_clpc_gpu_free); +} + +static void apple_clpc_gpu_provider_removal_test(struct kunit *test) +{ + struct apple_clpc_gpu *gpu = kzalloc_obj(*gpu); + u64 updated, sequence; + + KUNIT_ASSERT_NOT_NULL(test, gpu); + kref_init(&gpu->ref); + spin_lock_init(&gpu->lock); + kref_get(&gpu->ref); + /* Provider removal must not leave a late consumer with freed storage. */ + kref_put(&gpu->ref, apple_clpc_gpu_free); + apple_clpc_gpu_set_active(gpu, true); + KUNIT_EXPECT_TRUE(test, apple_clpc_gpu_active(gpu, &updated, &sequence)); + apple_clpc_gpu_set_active(gpu, false); + KUNIT_EXPECT_FALSE(test, apple_clpc_gpu_active(gpu, &updated, &sequence)); + apple_clpc_gpu_put(gpu); +} + +struct apple_clpc_wake_test { + struct delayed_work work; + struct completion done; +}; + +static void apple_clpc_wake_complete(struct work_struct *work) +{ + struct apple_clpc_wake_test *wake = container_of(to_delayed_work(work), + struct apple_clpc_wake_test, work); + complete(&wake->done); +} + +static void apple_clpc_gpu_cold_wake_test(struct kunit *test) +{ + struct apple_clpc_gpu *gpu = kzalloc_obj(*gpu); + struct apple_clpc_wake_test wake; + + KUNIT_ASSERT_NOT_NULL(test, gpu); + kref_init(&gpu->ref); + spin_lock_init(&gpu->lock); + INIT_DELAYED_WORK(&wake.work, apple_clpc_wake_complete); + init_completion(&wake.done); + gpu->wake = &wake.work; + apple_clpc_gpu_set_active(gpu, true); + KUNIT_EXPECT_NE(test, wait_for_completion_timeout(&wake.done, HZ), 0ul); + /* Model provider withdrawal before its work storage is destroyed. */ + gpu->wake = NULL; + cancel_delayed_work_sync(&wake.work); + apple_clpc_gpu_put(gpu); +} + +static struct kunit_case apple_clpc_cases[] = { + KUNIT_CASE(apple_clpc_gpu_cold_wake_test), + KUNIT_CASE(apple_clpc_gpu_request_lifetime_test), + KUNIT_CASE(apple_clpc_gpu_provider_removal_test), + {} +}; +static struct kunit_suite apple_clpc_suite = { + .name = "apple-clpc-gpu", + .test_cases = apple_clpc_cases, }; +kunit_test_suite(apple_clpc_suite); +#endif static const struct of_device_id apple_clpc_of_match[] = { { .compatible = "apple,t8132-clpc", .data = &apple_clpc_hw_t8132 }, diff --git a/drivers/soc/apple/profile.rs b/drivers/soc/apple/profile.rs index 0ba0eb510122b..0e632c65da81f 100644 --- a/drivers/soc/apple/profile.rs +++ b/drivers/soc/apple/profile.rs @@ -9,7 +9,7 @@ //! here once. The driver reads addresses from the device tree, never scans for //! them, and never patches properties at runtime. //! -//! Five bring-up targets are modelled: +//! These bring-up targets are modelled: //! //! * `T8103` / J313 (MacBook Air, M1): the host boots the SEP with the boot //! endpoint handshake over a 0x30000 shared-memory window. @@ -24,6 +24,8 @@ //! a powered sensor, so the driver uses the warm registration path. //! * `T8122` / J613, J615 (MacBook Air, M3): the T8112 warm registration with //! the T8140 SEP DART aperture above 4 GiB. +//! * `T6040` / J616s (MacBook Pro 16", M4 Pro): iBoot boots sepOS, so the +//! driver uses the warm registration path; the sensor is the J700 part. // The boot handshake, identity source, sensor and DART fields are consumed by // the boot-endpoint, identity and sensor-transport paths. @@ -426,6 +428,28 @@ pub(crate) const T8122: PlatformProfile = PlatformProfile { ..T8112 }; +/// MacBook Pro 16" M4 Pro (J616s). The J616s ADT (26.6.2, 25G83) records a +/// pre-booted SEP (`sepfw-load-at-boot`), an `iop-sep,ascwrap-v6` SEP whose +/// DART aperture starts at the BIT(40) alias (`vm-base` 0x10000004000), and +/// the Mesa sensor J700 carries (`sensor-id` 0x3356) on spi2 at +/// `0x429108000`, 8 MHz, chip select 0. Its power is NUB GPIO 53 and its +/// data-ready line AP GPIO 116. `/defaults` carries `cpx-encryption-mode = 2`. +/// +/// A warm-registration platform like T8140, with T8140's authenticated +/// sensor session: the enclave serves this sensor only after the nonce and +/// the signed root key. Device-tree nodes remain authoritative for addresses. +pub(crate) const T6040: PlatformProfile = PlatformProfile { + name: "T6040/J616s", + sensor: SensorProfile { + controller_base: 0x4_2910_8000, + ..T8140.sensor + }, + // macOS shares this enclave and Linux has no records here yet: keep + // Linux to its own xART records from the first boot. + xart_guard: 1, + ..T8140 +}; + static_assert!(T8103.shmem_capacity == 0x30000); static_assert!(T6020.shmem_capacity == 0x40000); static_assert!(T8140.shmem_capacity == 0x40000); @@ -441,6 +465,8 @@ static_assert!(T8112.sensor.controller_base == T8103.sensor.controller_base); static_assert!(T8122.shmem_capacity == T8112.shmem_capacity); static_assert!(T8122.sensor.controller_base == 0x2a1108000); static_assert!(T8122.wide_dma_mask); +static_assert!(T6040.sensor.controller_base == 0x429108000); +static_assert!(T6040.sensor.expected_id == T8140.sensor.expected_id); /// Whether the machine root declares `compatible`. /// @@ -587,6 +613,11 @@ pub(crate) fn detect() -> Result<&'static PlatformProfile> { if machine_has(b"apple,t8122") && (machine_has(b"apple,j613") || machine_has(b"apple,j615")) { return Ok(&T8122); } + // Only the board whose ADT was read: J616s. The other T6040 Macs share + // the SoC but their sensor wiring is unchecked. + if machine_has(b"apple,t6040") && machine_has(b"apple,j616s") { + return Ok(&T6040); + } Err(ENODEV) } @@ -637,6 +668,11 @@ mod tests { assert!(T8122.wide_dma_mask); assert!(T8122.shmem_capacity == T8112.shmem_capacity); assert!(T8122.sensor.controller_base == 0x2_a110_8000); + assert!(T6040.nonce_session); + assert!(T6040.wide_dma_mask); + assert!(T6040.bootstrap == Bootstrap::WarmRegister); + assert!(T6040.identity == IdentitySource::Chosen); + assert!(T6040.xart_guard == 1); } } @@ -667,7 +703,7 @@ mod dart_window_tests { #[test] fn the_other_profiles_keep_the_bit40_window() { - for profile in [&T8103, &T6000, &T6020, &T8140, &T6020_SEPOS13, &T8122] { + for profile in [&T8103, &T6000, &T6020, &T8140, &T6020_SEPOS13, &T8122, &T6040] { assert_eq!(offset(profile.dart_window), 1 << 40); assert_eq!(range(profile.dart_window), (0, 1 << 32)); } diff --git a/drivers/soc/apple/sbio.rs b/drivers/soc/apple/sbio.rs index 88be672817b1c..5a81a370dda38 100644 --- a/drivers/soc/apple/sbio.rs +++ b/drivers/soc/apple/sbio.rs @@ -2183,14 +2183,15 @@ impl SepData { self.sensor_trace(c"SENSOR_NONCE"); if !self.sensor_key_loaded.load(Relaxed) { - let firmware = match kernel::firmware::Firmware::request( - c"apple/msrk-j700.bin", &self.dev, - ) { + // Per-device data, so the board names it; J700 predates that. + let name = sensor::root_key_firmware_name().unwrap_or(ROOT_KEY_FIRMWARE); + let firmware = match kernel::firmware::Firmware::request(name, &self.dev) { Ok(firmware) => firmware, Err(error) => { dev_err!( self.dev, - "sensor: signed root-key container unavailable: {:?}\n", + "sensor: signed root-key container {} unavailable: {:?}\n", + name, error ); return false; diff --git a/drivers/soc/apple/sensor.rs b/drivers/soc/apple/sensor.rs index 63a4ae28fbc6d..13bb470c89b3b 100644 --- a/drivers/soc/apple/sensor.rs +++ b/drivers/soc/apple/sensor.rs @@ -25,15 +25,26 @@ extern "C" { fn sep_sensor_irq_available() -> c_int; fn sep_sensor_irq_arm(); fn sep_sensor_irq_wait(timeout_ms: c_uint) -> c_int; - fn sep_sensor_firmware_name() -> *const c_char; + fn sep_sensor_firmware_name(index: c_uint) -> *const c_char; } /// The calibration blob's name from the board description, or `None` when the /// sensor node has no `firmware-name` property (the caller then uses a default). pub(crate) fn firmware_name() -> Option<&'static CStr> { + firmware_name_at(0) +} + +/// The signed root-key container's name from the board description: the +/// second `firmware-name` entry, or `None` when the board gives only the +/// calibration (the caller then uses a default). +pub(crate) fn root_key_firmware_name() -> Option<&'static CStr> { + firmware_name_at(1) +} + +fn firmware_name_at(index: c_uint) -> Option<&'static CStr> { // SAFETY: the shim returns NULL or a pointer to a NUL-terminated string // owned by the device property and valid for the device's lifetime. - let ptr = unsafe { sep_sensor_firmware_name() }; + let ptr = unsafe { sep_sensor_firmware_name(index) }; if ptr.is_null() { return None; } diff --git a/drivers/soc/apple/sensor_shim.c b/drivers/soc/apple/sensor_shim.c index 68405e0a74507..bed228b4d1ca3 100644 --- a/drivers/soc/apple/sensor_shim.c +++ b/drivers/soc/apple/sensor_shim.c @@ -568,19 +568,23 @@ static int sep_sensor_power_line_locked(void) } /* - * The per-device calibration blob name, as the board description gives it in - * the sensor node's "firmware-name" property. NULL when absent, so the caller - * can fall back to a default. + * A per-device blob name, as the board description gives it in the sensor + * node's "firmware-name" list: index 0 is the calibration, index 1 the signed + * root key. NULL when that entry is absent, so the caller can fall back to a + * default. */ -static const char *sep_sensor_firmware_name_locked(void) +static const char *sep_sensor_firmware_name_locked(unsigned int index) { - const char *name = NULL; + const char *names[2] = { NULL, NULL }; + int count; - if (!sep_spi) + if (!sep_spi || index >= ARRAY_SIZE(names)) return NULL; - if (device_property_read_string(&sep_spi->dev, "firmware-name", &name)) + count = device_property_read_string_array(&sep_spi->dev, "firmware-name", + names, ARRAY_SIZE(names)); + if (count <= (int)index) return NULL; - return name; + return names[index]; } /* Powers the sensor on/off, holding the hardware settling delay. */ @@ -871,14 +875,14 @@ int sep_sensor_power_line(void) return result; } -const char *sep_sensor_firmware_name(void) +const char *sep_sensor_firmware_name(unsigned int index) { struct sep_sensor_lifetime *sensor = sep_lock_sensor(); const char *result; if (!sensor) return NULL; - result = sep_sensor_firmware_name_locked(); + result = sep_sensor_firmware_name_locked(index); mutex_unlock(&sensor->lock); return result; } diff --git a/drivers/soc/apple/sep.rs b/drivers/soc/apple/sep.rs index df71496a564fe..7d9f4f2af1869 100644 --- a/drivers/soc/apple/sep.rs +++ b/drivers/soc/apple/sep.rs @@ -449,6 +449,9 @@ const PATCH_POLL_MS: u32 = 20; const PATCH_POLL_ATTEMPTS: u32 = 250; const CALIBRATION_FIRMWARE: &CStr = c"apple/mesa_calibration.bin"; +/// The signed root-key container on boards whose sensor node names only the +/// calibration (J700). +const ROOT_KEY_FIRMWARE: &CStr = c"apple/msrk-j700.bin"; const SBIO_PROBE_USER_ID: i32 = 1000; static_assert!(SBIO_PROBE_USER_ID >= crate::sks::SKS_DESIGNATE_USER_MIN); diff --git a/drivers/soc/apple/shim.h b/drivers/soc/apple/shim.h index 0425bf2cdaa6e..99d2661e2cb1c 100644 --- a/drivers/soc/apple/shim.h +++ b/drivers/soc/apple/shim.h @@ -132,7 +132,7 @@ int sep_sensor_cs_timing_mode(void); int sep_sensor_power_cycle(void); int sep_sensor_power_source(void); int sep_sensor_power(int on); -const char *sep_sensor_firmware_name(void); +const char *sep_sensor_firmware_name(unsigned int index); int sep_sensor_xfer(const void *tx, void *rx, size_t len); int sep_sensor_xfer_tx(const void *tx, size_t len); int sep_sensor_xfer2(const void *tx, size_t tx_len, void *rx, size_t rx_len); diff --git a/drivers/soc/apple/t6041-display-gate.c b/drivers/soc/apple/t6041-display-gate.c index cccbec79e095f..3f27fe2cff832 100644 --- a/drivers/soc/apple/t6041-display-gate.c +++ b/drivers/soc/apple/t6041-display-gate.c @@ -379,6 +379,40 @@ static int gate_stage_apply(const struct gate_stage *st, struct device_node *dis #define GATE_PMP_WAIT_MS 20000 +/* A tether boot reserves ATC0 for the debug link. Its DP PHY is disabled, + * so exposing a route@0 would make its DCP (dcpext1, or the dcpext0 hybrid) + * defer on that missing supplier and take the usable ATC1/2 display routes + * (and dcpext0's HDMI) down with it. + */ +static bool gate_route_phy_available(struct device_node *route) +{ + struct device_node *routes, *dcp; + struct of_phandle_args phy; + char name[16]; + u32 index; + int match; + bool available = false; + + if (of_property_read_u32(route, "reg", &index)) + return false; + routes = of_get_parent(route); + dcp = routes ? of_get_parent(routes) : NULL; + of_node_put(routes); + if (!dcp) + return false; + /* The fabric takes a route's PHY by name ("typecN"), not by position. */ + snprintf(name, sizeof(name), "typec%u", index); + match = of_property_match_string(dcp, "phy-names", name); + if (match >= 0) + index = match; + if (!of_parse_phandle_with_args(dcp, "phys", "#phy-cells", index, &phy)) { + available = of_device_is_available(phy.np); + of_node_put(phy.np); + } + of_node_put(dcp); + return available; +} + /* Set the status of the processors (externals) or the routes, and the display subsystem, in one changeset. */ static int gate_set(struct device_node *display, bool dcps, const char *dcp_status, bool routes, const char *route_status, const char *display_status) @@ -399,6 +433,12 @@ static int gate_set(struct device_node *display, bool dcps, const char *dcp_stat if (!(dcps && gate_is_dcp(path)) && !(routes && gate_is_route(path))) continue; np = of_find_node_by_path(path); + if (np && gate_is_route(path) && route_status && + !strcmp(route_status, "okay") && !gate_route_phy_available(np)) { + pr_info("%s remains disabled: DP PHY unavailable\n", path); + of_node_put(np); + continue; + } ret = np ? of_changeset_update_prop_string(cs, np, "status", gate_is_dcp(path) ? dcp_status : route_status) : -ENODEV; diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index aa630b95dafe9..dd6f7b99e698a 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -290,6 +290,11 @@ static int tps6598x_connect(struct tps6598x *tps, u32 status) static void tps6598x_drop_partner(struct tps6598x *tps, u32 status) { + if (tps->partner_altmode_dp) { + typec_altmode_update_active(tps->partner_altmode_dp, false); + typec_unregister_altmode(tps->partner_altmode_dp); + tps->partner_altmode_dp = NULL; + } if (!IS_ERR(tps->partner)) typec_unregister_partner(tps->partner); tps->partner = NULL; @@ -1171,6 +1176,11 @@ static void cd321x_update_work(struct work_struct *work) /* Process partner disconnection or change */ if (!new_connected || partner_changed) { + if (tps->partner_altmode_dp) { + typec_altmode_update_active(tps->partner_altmode_dp, false); + typec_unregister_altmode(tps->partner_altmode_dp); + tps->partner_altmode_dp = NULL; + } if (!IS_ERR(tps->partner)) typec_unregister_partner(tps->partner); tps->partner = NULL; @@ -1245,6 +1255,29 @@ static void cd321x_update_work(struct work_struct *work) typec_partner_set_identity(tps->partner); } + /* The CD321x firmware owns VDM discovery and Enter Mode. Publish the + * discovered DP mode only after it reports an entered DP connection; + * keep Linux's DP Alt Mode driver from issuing a second Enter Mode. + */ + if (dp_connected && !tps->partner_altmode_dp) { + struct typec_altmode_desc desc = { + .svid = USB_TYPEC_DP_SID, + .mode = USB_TYPEC_DP_MODE, + .vdo = le32_to_cpu(st.dp_sid_status.mode_data), + .mode_selection = true, + }; + + tps->partner_altmode_dp = typec_partner_register_altmode(tps->partner, + &desc); + if (IS_ERR(tps->partner_altmode_dp)) { + ret = PTR_ERR(tps->partner_altmode_dp); + tps->partner_altmode_dp = NULL; + goto retry; + } + } + if (tps->partner_altmode_dp) + typec_altmode_update_active(tps->partner_altmode_dp, dp_connected); + /* Update the TypeC MUX/PHY state */ if (dp_route_was_active && dp_connected && !was_disconnected && !dp_mode_changed && st.dp_sid_event) { @@ -1997,7 +2030,8 @@ static int cd321x_register_port_altmodes(struct cd321x *cd321x) memset(&desc, 0, sizeof(desc)); desc.svid = USB_TYPEC_DP_SID; desc.mode = USB_TYPEC_DP_MODE; - desc.vdo = DP_CONF_SET_PIN_ASSIGN(BIT(DP_PIN_ASSIGN_C) | BIT(DP_PIN_ASSIGN_D)); + desc.vdo = DP_CONF_SET_PIN_ASSIGN(BIT(DP_PIN_ASSIGN_C) | + BIT(DP_PIN_ASSIGN_D) | BIT(DP_PIN_ASSIGN_E)); desc.vdo |= DP_CAP_DFP_D; amode = typec_port_register_altmode(cd321x->tps.port, &desc); if (IS_ERR(amode)) @@ -2111,6 +2145,7 @@ EXPORT_SYMBOL_IF_KUNIT(cd321x_init_work); static int cd321x_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode) { + struct fwnode_handle *connector_fwnode; struct cd321x *cd321x = container_of(tps, struct cd321x, tps); int ret; @@ -2130,9 +2165,12 @@ cd321x_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode) goto err_unregister_altmodes; } - cd321x->connector_fwnode = fwnode_find_reference(fwnode, "displayport", 0); - if (IS_ERR(cd321x->connector_fwnode)) - cd321x->connector_fwnode = NULL; + if (fwnode_property_present(fwnode, "displayport")) + connector_fwnode = fwnode_find_reference(fwnode, "displayport", 0); + else + connector_fwnode = fwnode_handle_get(fwnode); + if (!IS_ERR_OR_NULL(connector_fwnode)) + cd321x->connector_fwnode = connector_fwnode; cd321x->tbt_switch = cd321x_tbt_switch_get(tps->dev, fwnode); if (IS_ERR(cd321x->tbt_switch)) { diff --git a/drivers/usb/typec/tipd/spmi.c b/drivers/usb/typec/tipd/spmi.c index ba89b4b57a255..4f0ceca25ee7f 100644 --- a/drivers/usb/typec/tipd/spmi.c +++ b/drivers/usb/typec/tipd/spmi.c @@ -318,9 +318,14 @@ static int sn201202x_probe(struct spmi_device *device) return ret; ret = tipd_init(tps); - if (ret) + if (ret) { spmi_command_sleep(device); - return ret; + return ret; + } + + /* Events can latch before the IRQ is registered. Consume that state now. */ + tps->data->irq_handler(tps->irq, tps); + return 0; } static void sn201202x_remove(struct spmi_device *device) diff --git a/drivers/usb/typec/tipd/tps6598x.h b/drivers/usb/typec/tipd/tps6598x.h index caac3cb771bd4..f6d51724a1f5d 100644 --- a/drivers/usb/typec/tipd/tps6598x.h +++ b/drivers/usb/typec/tipd/tps6598x.h @@ -371,6 +371,7 @@ struct tps6598x { const struct tipd_data *data; struct tipd_debugfs *debugfs; + struct typec_altmode *partner_altmode_dp; }; /* TPS_REG_USB4_STATUS */ diff --git a/include/linux/soc/apple/clpc.h b/include/linux/soc/apple/clpc.h new file mode 100644 index 0000000000000..37b2cfce5594a --- /dev/null +++ b/include/linux/soc/apple/clpc.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +#ifndef _LINUX_SOC_APPLE_CLPC_H +#define _LINUX_SOC_APPLE_CLPC_H + +#include +#include +#include + +struct apple_clpc_gpu; + +#if IS_REACHABLE(CONFIG_APPLE_CLPC) +int apple_clpc_gpu_get(struct device *dev, struct apple_clpc_gpu **out); +void apple_clpc_gpu_set_active(struct apple_clpc_gpu *gpu, bool active); +void apple_clpc_gpu_put(struct apple_clpc_gpu *gpu); +#else +static inline int apple_clpc_gpu_get(struct device *dev, struct apple_clpc_gpu **out) +{ + *out = NULL; + return device_property_present(dev, "apple,clpc") ? -ENODEV : 0; +} +static inline void apple_clpc_gpu_set_active(struct apple_clpc_gpu *gpu, bool active) {} +static inline void apple_clpc_gpu_put(struct apple_clpc_gpu *gpu) {} +#endif +#endif diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index cc1123e10f398..96cd57aae29a8 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -62,6 +63,7 @@ #define MACAUDIO_NUM_FE_LINKS 3 /* milliseconds */ #define SPEAKER_LOCK_TIMEOUT 250 +#define MACAUDIO_MAX_SPEAKER_ID_GPIOS 8 enum macaudio_amp_type { AMP_NONE, @@ -166,6 +168,7 @@ struct macaudio_snd_data { bool has_sense; bool has_safety; unsigned int max_channels; + struct gpio_descs *speaker_id_gpios; struct macaudio_link_props { /* frontend props */ @@ -1430,6 +1433,50 @@ static const struct snd_soc_jack_pin macaudio_jack_pins[] = { }, }; +static int macaudio_speaker_id_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = BIT(ma->speaker_id_gpios->ndescs) - 1; + uinfo->value.integer.step = 1; + + return 0; +} + +static int macaudio_speaker_id_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); + unsigned int value = 0; + unsigned int i; + int ret; + + for (i = 0; i < ma->speaker_id_gpios->ndescs; i++) { + ret = gpiod_get_value_cansleep(ma->speaker_id_gpios->desc[i]); + if (ret < 0) + return ret; + if (ret) + value |= BIT(i); + } + uvalue->value.integer.value[0] = value; + + return 0; +} + +static const struct snd_kcontrol_new macaudio_speaker_id_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Speaker Variant ID", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = macaudio_speaker_id_info, + .get = macaudio_speaker_id_get, +}; + static int macaudio_probe(struct snd_soc_card *card) { struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card); @@ -1584,6 +1631,13 @@ static int macaudio_late_probe(struct snd_soc_card *card) mutex_unlock(&ma->volume_lock_mutex); } + /* Append identity metadata without renumbering the existing controls. */ + if (ma->speaker_id_gpios) { + ret = snd_soc_add_card_controls(card, &macaudio_speaker_id_control, 1); + if (ret) + return ret; + } + /* The notifier walks the card's controls: only while the card is bound */ return register_pm_notifier(&ma->pm_nb); } @@ -2183,6 +2237,16 @@ static int macaudio_snd_platform_probe(struct platform_device *pdev) if (ret) return ret; + data->speaker_id_gpios = devm_gpiod_get_array_optional(dev, "speaker-id", + GPIOD_IN); + if (IS_ERR(data->speaker_id_gpios)) + return dev_err_probe(dev, PTR_ERR(data->speaker_id_gpios), + "Failed to get speaker ID inputs\n"); + if (data->speaker_id_gpios && + (!data->speaker_id_gpios->ndescs || + data->speaker_id_gpios->ndescs > MACAUDIO_MAX_SPEAKER_ID_GPIOS)) + return dev_err_probe(dev, -EINVAL, "Invalid speaker ID input count\n"); + /* * Without sense the PCM2 link is gone, and a card whose front-ends * are not the MCA's (the J616 jack) has no PCM2 RX widget at all. diff --git a/tools/asahi/apple-dart-range-tests.py b/tools/asahi/apple-dart-range-tests.py index 6fa006381918c..5d311fef0813d 100644 --- a/tools/asahi/apple-dart-range-tests.py +++ b/tools/asahi/apple-dart-range-tests.py @@ -31,6 +31,7 @@ def main(): #include #include typedef uint64_t u64; +typedef uint32_t u32; #define BITS_TO_LONGS(n) 1 #define atomic_long_read(x) (*(x)) #define bitmap_empty(x, n) (!(x)[0]) @@ -42,31 +43,36 @@ def main(): struct apple_dart; struct apple_dart_stream_map { struct apple_dart *dart; unsigned long sidmap[1]; }; struct apple_dart_hw { int (*invalidate_tlb)(struct apple_dart_stream_map *); }; -struct apple_dart { int num_streams, locked, version; void *dev; struct apple_dart_hw *hw; }; +struct apple_dart { int num_streams, locked, version; bool fw_mirror, fw_handoff, managed_sids; void *dev; struct apple_dart_hw *hw; }; struct apple_dart_atomic_stream_map { struct apple_dart *dart; unsigned long sidmap[1]; }; struct apple_dart_domain { - void *pgtbl_ops; struct apple_dart_atomic_stream_map streams[2]; int count; + void *pgtbl_ops; bool dcp; struct apple_dart_atomic_stream_map streams[2]; int count; u64 dma_offset, mask; }; #define for_each_stream_map(i,d,m) for ((i)=0; (i)<(d)->count && ((m)=&(d)->streams[i],1); (i)++) -static int powered, power_error, sync_error, range_error, syncs, ranges, fulls; +static int powered, power_error, sync_error, range_error, syncs, ranges, fulls, firmware_flushes, quarantines, dcp_flushes; static u64 seen_first, seen_last; -static int pm_runtime_resume_and_get(void *dev) { if (power_error) return power_error; powered++; return 0; } +static int pm_runtime_resume_and_get(void *dev) { if (power_error && !dev) return power_error; powered++; return 0; } static void pm_runtime_put(void *dev) { assert(powered>0); powered--; } -static int apple_dart_hw_sync_locked(struct io_pgtable_cfg *cfg, struct apple_dart_stream_map *stream) { - syncs++; return sync_error; +static int apple_dart_hw_sync_locked(struct io_pgtable_cfg *cfg, struct apple_dart_stream_map *stream, bool invalidate) { + assert(invalidate); syncs++; return sync_error; } static int apple_dart_t8110_hw_tlb_command_range(struct apple_dart_stream_map *stream, int command, bool range, u64 first, u64 last) { assert(command==1 && range); ranges++; seen_first=first; seen_last=last; return range_error; } +static int apple_dart_dcp_domain_flush(struct apple_dart_domain *domain) { assert(domain->dcp); dcp_flushes++; return -EAGAIN; } +static void apple_dart_managed_quarantine(struct apple_dart *dart) { assert(dart->managed_sids); quarantines++; } +static int apple_dart_fw_invalidate(struct apple_dart_stream_map *stream, u64 first, u64 last) { + firmware_flushes++; seen_first=first; seen_last=last; return range_error; +} static int full(struct apple_dart_stream_map *stream) { fulls++; return 0; } ''' source += production source += r''' static void reset(void) { assert(powered==0); power_error=sync_error=range_error=syncs=ranges=fulls=0; - seen_first=seen_last=0; + seen_first=seen_last=0; firmware_flushes=quarantines=dcp_flushes=0; } int main(void) { struct apple_dart_hw hw={.invalidate_tlb=full}; @@ -88,14 +94,35 @@ def main(): assert(fulls==1 && ranges==0 && syncs==0); reset(); dart.locked=1; sync_error=-EBUSY; assert(apple_dart_domain_flush_tlb_range(&domain,true,0x4000,0x7fff)==-EBUSY); - assert(ranges==0 && fulls==0 && powered==0); + assert(ranges==1 && fulls==0 && powered==0); reset(); power_error=-EHOSTDOWN; assert(apple_dart_domain_flush_tlb_range(&domain,true,0x4000,0x7fff)==-EHOSTDOWN); assert(syncs==0 && ranges==0 && fulls==0 && powered==0); reset(); range_error=-ETIMEDOUT; assert(apple_dart_domain_flush_tlb_range(&domain,true,0x4000,0x7fff)==-ETIMEDOUT); assert(ranges==1 && powered==0); - reset(); domain.streams[0].sidmap[0]=0; + reset(); dart.managed_sids=true; range_error=-ETIMEDOUT; + assert(apple_dart_domain_flush_tlb_range(&domain,true,0x4000,0x7fff)==-ETIMEDOUT); + assert(ranges==1 && quarantines==1 && powered==0); + reset(); sync_error=-EBUSY; range_error=-ETIMEDOUT; + assert(apple_dart_domain_flush_tlb_range(&domain,true,0x4000,0x7fff)==-EBUSY); + assert(ranges==1 && quarantines==1 && powered==0); + reset(); dart.fw_mirror=true; + assert(!apple_dart_domain_flush_tlb_range(&domain,true,0x4000,0x7fff)); + assert(firmware_flushes==1 && ranges==0 && syncs==1); + reset(); dart.fw_mirror=false; dart.fw_handoff=true; + assert(!apple_dart_domain_flush_tlb_range(&domain,true,0x4000,0x7fff)); + assert(firmware_flushes==1 && ranges==0); + reset(); dart.fw_handoff=false; domain.dcp=true; + assert(apple_dart_domain_flush_tlb_range(&domain,true,0x4000,0x7fff)==-EAGAIN); + assert(dcp_flushes==1 && ranges==0 && syncs==0); + reset(); domain.dcp=false; + struct apple_dart second=dart; second.dev=(void *)1; + domain.streams[1].dart=&second; domain.streams[1].sidmap[0]=1; domain.count=2; + power_error=-EHOSTDOWN; + assert(apple_dart_domain_flush_tlb_range(&domain,true,0x4000,0x7fff)==-EHOSTDOWN); + assert(ranges==1 && syncs==1 && powered==0); + reset(); domain.count=1; domain.streams[0].sidmap[0]=0; assert(!apple_dart_domain_flush_tlb_range(&domain,true,0x4000,0x7fff)); assert(syncs==0 && ranges==0 && fulls==0 && powered==0); puts("PASS: device DVA, low window, legacy/unlocked fallback, sync/power/command errors and empty stream"); diff --git a/tools/asahi/clpc-gpu-demand-tests.py b/tools/asahi/clpc-gpu-demand-tests.py new file mode 100644 index 0000000000000..ecf0fff465711 --- /dev/null +++ b/tools/asahi/clpc-gpu-demand-tests.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only OR MIT +"""Run production CLPC demand/hold logic with controlled PMGR reads and time.""" +from pathlib import Path +import os +import re +import subprocess +import tempfile + +ROOT = Path(os.environ.get('CLPC_ROOT', Path(__file__).resolve().parents[2])) + + +def item(text, signature): + start = text.index(signature) + begin = text.index('{', start) + depth, end = 1, begin + 1 + while depth: + depth += (text[end] == '{') - (text[end] == '}') + end += 1 + return text[start:end] + + +def main(): + source = (ROOT / 'drivers/soc/apple/clpc.c').read_text() + header = (ROOT / 'include/linux/soc/apple/pmc.h').read_text() + rail_shift = re.search(r'^#define APPLE_PMC_RAIL_SHIFT\(rail\).*$', header, re.M).group(0) + types = '\n'.join(item(source, s) + ';' for s in [ + 'struct apple_clpc_hw {', 'struct apple_clpc_vote {']) + tables = '\n'.join(item(source, 'static const struct apple_clpc_hw ' + n) + ';' + for n in ['apple_clpc_hw_t8132', 'apple_clpc_hw_t8140', 'apple_clpc_hw_t6040']) + methods = '\n'.join(item(source, s) for s in [ + 'static unsigned int apple_clpc_vote(', 'static u32 apple_clpc_floor(', + 'static bool apple_clpc_gpu_streaming(', + 'static bool apple_clpc_gpu_limited(', + 'static void apple_clpc_sample(']) + code = r''' +#include +#include +#include +#include +#include +typedef uint8_t u8; +typedef uint32_t u32; +typedef uint64_t u64; +#define APPLE_CLPC_LEVELS 4 +#define APPLE_CLPC_HOLD_MS 32 +#define APPLE_CLPC_SAMPLE_MS 4 +#define APPLE_CLPC_GPU_BACKOFF_MS 100 +#define HZ 1000 +#define max(a,b) ((a) > (b) ? (a) : (b)) +#define GENMASK(h,l) (((1u << ((h)-(l)+1))-1) << (l)) +#define APPLE_PMC_RAIL_SHIFT(r) ((r)*8) +#define time_after_eq(a,b) ((long)((a)-(b)) >= 0) +#define time_after(a,b) ((long)((a)-(b)) > 0) +#define time_before(a,b) ((long)((a)-(b)) < 0) +#define msecs_to_jiffies(ms) (ms) +enum apple_clpc_rail { APPLE_CLPC_DCS, APPLE_CLPC_SOC, APPLE_CLPC_RAILS }; +''' + types + r''' +struct apple_clpc_gpu { bool active; }; +struct apple_clpc_cluster { unsigned int request[APPLE_CLPC_RAILS]; }; +struct apple_clpc { + const struct apple_clpc_hw *hw; + struct apple_clpc_cluster clusters[1]; + struct apple_clpc_vote vote[APPLE_CLPC_RAILS]; + unsigned int nr_clusters, agent; + unsigned long samples, next_scan, update_errors; + void *covered, *pmc, *gpu_pmgr; + u32 gpu_offset, mask, last_floor, last_reason; + bool gpu_on, gpu_limited; + unsigned long gpu_on_since, gpu_sampled, gpu_limited_at; + u64 last_vote_ns; + struct apple_clpc_gpu *gpu; +}; +static unsigned long jiffies; +static u64 ktime_get_mono_fast_ns(void) { return jiffies * 1000000ull; } +static bool apple_clpc_gpu_active(struct apple_clpc_gpu *g, u64 *u, u64 *s) { *u=jiffies; *s=1; return g->active; } +static void *cpu_possible_mask; +static u32 pmgr_status, pmp_floor, published; +static int read_error, vote_error; +static bool queued_idle; +static bool cpumask_equal(void *a, void *b) { return a == b; } +static void apple_clpc_find_clusters(struct apple_clpc *c) { (void)c; } +static void apple_clpc_sample_cluster(struct apple_clpc *c, struct apple_clpc_cluster *cl) { (void)c; (void)cl; } +static int regmap_read(void *map, u32 offset, u32 *status) { + assert(map); (void)offset; *status=pmgr_status; return read_error; +} +static u32 apple_pmc_floor_read(void *pmc, unsigned int agent) { + (void)pmc; assert(agent==0); return pmp_floor; +} +static int apple_pmc_floor_update(void *pmc, unsigned int agent, u32 mask, u32 floor) { + (void)pmc; assert(agent==1 && mask==3); published=floor; return vote_error; +} +static void apple_clpc_queue(struct apple_clpc *c, bool idle) { (void)c; queued_idle=idle; } +''' + tables + '\n' + methods + r''' +static void check_t8132(void) { + struct apple_clpc c={.hw=&apple_clpc_hw_t8132,.agent=1,.mask=3,.gpu_pmgr=(void *)1}; + read_error=vote_error=0; pmgr_status=0; pmp_floor=0; jiffies=0; + apple_clpc_sample(&c); assert(published==0 && queued_idle); + jiffies=100; pmgr_status=0xf0; + apple_clpc_sample(&c); assert(published==0x35 && c.last_reason==4); + jiffies=131; pmgr_status=0; apple_clpc_sample(&c); + assert(published==0x35 && c.last_reason==16); + jiffies=132; apple_clpc_sample(&c); assert(published==0 && queued_idle); + // A failed power-state read still protects an active graphics consumer. + jiffies=200; pmgr_status=0xe0; read_error=-EIO; + apple_clpc_sample(&c); assert(published==0x35); + jiffies=232; read_error=0; apple_clpc_sample(&c); assert(published==0); +} + +static void check_t8140(void) { + struct apple_clpc c={.hw=&apple_clpc_hw_t8140,.agent=1,.mask=3,.gpu_pmgr=(void *)1}; + read_error=vote_error=0; pmp_floor=4; pmgr_status=0xf0; + for (jiffies=100; jiffies<=164; jiffies+=4) { + apple_clpc_sample(&c); + assert(published==(jiffies<164 ? 0 : 0x34)); + } + // The PMP's lower DCS vote suppresses the GPU floor during heavy work. + pmp_floor=3; + for (jiffies=168; jiffies<=204; jiffies+=4) apple_clpc_sample(&c); + assert(published==0 && c.gpu_limited && !queued_idle); + pmp_floor=4; + for (jiffies=208; jiffies<=304; jiffies+=4) apple_clpc_sample(&c); + assert(published==0x34 && c.last_reason==4); + jiffies=308; pmgr_status=0; apple_clpc_sample(&c); + jiffies=340; apple_clpc_sample(&c); assert(published==0 && queued_idle); +} +int main(void) { + // T6040's supplier is always powered: it must NEVER substitute for work. + struct apple_clpc_gpu gpu={0}; + struct apple_clpc c={.hw=&apple_clpc_hw_t6040,.agent=1,.mask=3,.gpu=&gpu}; + assert(!c.hw->gpu_demand); + jiffies=0; read_error=vote_error=0; pmgr_status=0xf0; published=~0u; + apple_clpc_sample(&c); assert(published==0 && queued_idle && c.last_reason==0); + gpu.active=true; jiffies=100; apple_clpc_sample(&c); + assert(published==0x25 && c.last_reason==2 && c.last_floor==0x25); + assert(c.last_vote_ns==100000000ull); + // Core/firmware supplier is still powered, but continuous idle ends demand. + gpu.active=false; jiffies=131; apple_clpc_sample(&c); + assert(published==0x25 && c.last_reason==16); + jiffies=132; apple_clpc_sample(&c); + assert(published==0 && queued_idle && c.last_reason==0); + // A new submission reasserts demand; independent CPU requests survive release. + c.nr_clusters=1; c.clusters[0].request[0]=2; c.clusters[0].request[1]=1; + gpu.active=true; jiffies=200; apple_clpc_sample(&c); + assert(published==0x25 && c.last_reason==3); + gpu.active=false; jiffies=232; apple_clpc_sample(&c); + assert(published==0x12 && c.last_reason==1); + c.clusters[0].request[0]=c.clusters[0].request[1]=0; + jiffies=264; apple_clpc_sample(&c); assert(published==0); + check_t8132(); + check_t8140(); + puts("PASS: T6040 host demand; T8132 powered floor; T8140 streaming and PMP backoff; idle release"); +} +''' + code = code.replace('#define APPLE_PMC_RAIL_SHIFT(r) ((r)*8)', rail_shift) + with tempfile.TemporaryDirectory(prefix='apple-clpc-gpu-') as tmp: + root = Path(tmp) + for name, text in [ + ('fixed', code), + ('omit-host-demand', code.replace('if (clpc->gpu && !clpc->hw->gpu_demand) {', 'if (false) {')), + ('omit-streaming-demand', code.replace('if (clpc->gpu_pmgr && apple_clpc_gpu_streaming(clpc, now) &&', 'if (false && apple_clpc_gpu_streaming(clpc, now) &&')), + ('omit-pmp-backoff', code.replace('!apple_clpc_gpu_limited(clpc, now))', + '(!apple_clpc_gpu_limited(clpc, now) || true))')), + ('release-too-early', code.replace('msecs_to_jiffies(APPLE_CLPC_HOLD_MS)', '0')), + ('retain-at-idle', code.replace('vote->state = 0;', '')), + ]: + path = root / (name + '.c') + path.write_text(text) + binary = path.with_suffix('') + subprocess.run([os.environ.get('CC', 'cc'), '-std=gnu11', '-Wall', '-Wextra', + '-Werror', str(path), '-o', str(binary)], check=True) + run = subprocess.run([str(binary)], capture_output=True, text=True) + if name == 'fixed': + assert run.returncode == 0, run.stderr + print(run.stdout.strip()) + else: + assert run.returncode != 0, name + print('PASS negative control:', name) + + +if __name__ == '__main__': + main() diff --git a/tools/asahi/compute-preempt-tests.py b/tools/asahi/compute-preempt-tests.py index a13f103341b95..fdab1e9e7406b 100755 --- a/tools/asahi/compute-preempt-tests.py +++ b/tools/asahi/compute-preempt-tests.py @@ -48,7 +48,7 @@ def policy(text): pub static compute_preempt: Parameter = Parameter; pub static mut PARAM: i32 = -1; pub struct Parameter; - impl Parameter { pub fn value(&self) -> &i32 { unsafe { &*std::ptr::addr_of!(PARAM) } } } + impl Parameter { pub fn value(&self) -> i32 { unsafe { *std::ptr::addr_of!(PARAM) } } } } #[derive(Clone, Copy, Debug, PartialEq)] struct U64(u64); #[derive(Clone, Copy, Debug, PartialEq)] enum PipeType { Vertex, Fragment, Compute } diff --git a/tools/asahi/g17-admission-deadline-tests.py b/tools/asahi/g17-admission-deadline-tests.py index 5002f70997307..b58715f6fcc58 100644 --- a/tools/asahi/g17-admission-deadline-tests.py +++ b/tools/asahi/g17-admission-deadline-tests.py @@ -40,7 +40,7 @@ def fixture(): const QUARANTINE_WAIT_MS:u32=1000; #[derive(Clone,Copy,PartialEq)] enum Plan {Any,Conflict} #[derive(Clone,Copy)] struct Request<'a>(&'a ()); -struct State {owners:[u64;2],quarantined:u64,next_ticket:u64,waiters:usize,conflict:bool,ready:bool} +struct State {owners:[u64;2],quarantined:u64,spared:u64,next_ticket:u64,waiters:usize,conflict:bool,ready:bool} impl State { fn exhausted(&self)->bool {false} fn refusal(&self,_:u64)->Result {Ok(())} @@ -69,7 +69,7 @@ def fixture(): ''' + item(source, 'fn wait_slice_ms(') + '\nimpl Pool {\n' + item(source, 'pub(crate) fn acquire_render(') + r''' } fn new_pool(conflict:bool,quarantined:u64)->Arc { - Arc::new(Pool{state:Lock(Mutex::new(State{owners:[0,1],quarantined,next_ticket:1,waiters:0,conflict,ready:false})),released:CondVar}) + Arc::new(Pool{state:Lock(Mutex::new(State{owners:[0,1],quarantined,spared:0,next_ticket:1,waiters:0,conflict,ready:false})),released:CondVar}) } fn reset(mode:usize){NOW.store(0,Ordering::SeqCst);WAITS.store(0,Ordering::SeqCst);MODE.store(mode,Ordering::SeqCst);} fn main(){ @@ -89,6 +89,9 @@ def fixture(): reset(mode);let pool=new_pool(false,2); assert!(matches!(pool.acquire_render(1,None,2_000_000_000,||Ok(())),Err(EIO))); assert!(NOW.load(Ordering::SeqCst)<=1_010_000_000,"quarantine budget extended"); + reset(mode);let pool=new_pool(false,2);pool.state.lock().spared=2; + assert!(matches!(pool.acquire_render(1,None,2_000_000_000,||Ok(())),Err(EBUSY))); + assert!(NOW.load(Ordering::SeqCst)<=1_010_000_000,"spared quarantine budget extended"); } reset(2);let pool=new_pool(true,0); assert!(matches!(pool.acquire_render(1,Some(Request(&())),2_000_000_000,||Ok(())),Err(ERESTARTSYS))); diff --git a/tools/asahi/g17-capacity-fault-tests.py b/tools/asahi/g17-capacity-fault-tests.py index 09a70caaaccb4..7c33e92a74812 100644 --- a/tools/asahi/g17-capacity-fault-tests.py +++ b/tools/asahi/g17-capacity-fault-tests.py @@ -28,13 +28,13 @@ def fixture(): retirement = (ROOT / 'queue/compute/retirement.rs').read_text() previous = item(source, 'struct Previous<') + '\n' + item(source, 'impl Previous') methods = '\n'.join(item(source, sig) for sig in [ - 'pub(crate) fn release_owner(', 'pub(crate) fn reusable(', + 'fn witness_stamp(', 'pub(crate) fn release_owner(', 'pub(crate) fn reusable(', 'pub(crate) fn recycle_quarantined_idle(', 'pub(crate) fn recycle_killed(', - 'fn recycle_settled(', 'fn release_witnessed_inner(', 'pub(crate) fn share_idle(', 'pub(crate) fn can_rearm(', 'pub(crate) fn recycled_pending(']) + 'fn recycle_settled(', 'fn release_witnessed_inner(', 'pub(crate) fn share_idle(', 'pub(crate) fn shareable(', 'pub(crate) fn can_rearm(', 'pub(crate) fn recycled_pending(']) exit_source = (ROOT / 'queue/compute/exit.rs').read_text() methods += '\n' + item(exit_source, 'pub(crate) fn settle_exit(').replace('super::Previous', 'Previous').replace('crate::g17::qos::Owner', 'qos::Owner') drain = item((ROOT / 'mod.rs').read_text(), 'fn drain_reclaims(') - proof = item(retirement, 'pub(crate) const fn stamp_covers(') + '\n' + item(retirement, 'pub(crate) fn quarantined_retired(') + proof = item(retirement, 'pub(crate) const fn stamp_newer(') + '\n' + item(retirement, 'pub(crate) const fn stamp_covers(') + '\n' + item(retirement, 'pub(crate) fn quarantined_retired(') return r''' #![allow(dead_code)] use std::sync::{Arc, atomic::{AtomicUsize, AtomicU32, Ordering}}; @@ -48,6 +48,7 @@ def fixture(): fn now_ns() -> u64 { 123 } struct Context { id: u64, live: Arc, killed: AtomicU32 } impl Context { + fn note_compute_binding(&self, _: bool) {} fn new(id: u64, live: &Arc) -> Arc { live.fetch_add(1, Ordering::SeqCst); Arc::new(Self { id, live: live.clone(), killed: AtomicU32::new(1) }) @@ -55,14 +56,14 @@ def fixture(): } impl Drop for Context { fn drop(&mut self) { self.live.fetch_sub(1, Ordering::SeqCst); } } struct Binding { context: Arc } -''' + previous + r''' +''' + previous + '\n' + item((ROOT / 'sync_input.rs').read_text(), 'pub(super) struct ReaderHint').replace('pub(super) ', '') + '\nimpl ReaderHint {\n' + item((ROOT / 'sync_input.rs').read_text(), 'pub(super) fn has_readers(').replace('pub(super) ', '') + '\n}\n' + r''' #[derive(Clone, Copy, PartialEq)] enum PoolPhase { Populated, ReleaseSent(u32), Vacant } #[derive(Clone, Copy)] struct Ticket { owner: u64, item_count: u32, descriptor: u64, item_producer: u32, kick: u64, } #[derive(Clone, Copy)] struct Cursors { count: u32, producer: u32, consumer: u32 } ''' + proof + r''' -mod retirement { pub(crate) use super::quarantined_retired; } +mod retirement { pub(crate) use super::{quarantined_retired, stamp_newer}; } struct Graph { cursors: Cursors, stamp: u32, fail: bool, resets: usize, owner: u64 } impl Graph { fn set_owner(&mut self, context: &Context) -> Result { if self.fail { Err(EIO) } else { self.owner = context.id; Ok(()) } } @@ -107,6 +108,7 @@ def fixture(): retired_by_teardown: bool, spared_quarantine: bool, spared_deferred: bool, retire_pending: bool, retirement_proved: bool, replay_retirement: bool, replay_error: Option, replay_prepared: bool, submitted: u32, kick: Kick, + config_cache: Option<()>, source_hint: ReaderHint, source_readers: AtomicU32, claims: u32, fault_stamp: Option, } impl Queue { fn valid_ticket(&self, _: &Active, _: &Ticket) -> Result { Ok(()) } @@ -126,6 +128,7 @@ def fixture(): retired_by_teardown: false, spared_quarantine: false, spared_deferred: false, retire_pending: false, retirement_proved: false, replay_retirement: false, replay_error: None, replay_prepared: false, submitted: 0, kick: Kick, + fault_stamp: None, config_cache: None, source_hint: ReaderHint { possible: false }, source_readers: AtomicU32::new(0), claims: 0, } } fn fault(live: &Arc) -> Queue { @@ -146,20 +149,34 @@ def fixture(): impl Reclaims { fn take_one(&mut self) -> Option { self.0.pop() } } struct Mutex(std::sync::Mutex); impl Mutex { fn lock(&self) -> std::sync::MutexGuard<'_, T> { self.0.lock().unwrap() } } -struct Shared { reclaim: Mutex, wakes: AtomicUsize } +struct Available(AtomicUsize); impl Available {fn notify(&self){self.0.fetch_add(1, Ordering::SeqCst);}} +struct Shared { reclaim: Mutex, wakes: AtomicUsize, queues: Available } impl Shared { fn queue_events(&self) { self.wakes.fetch_add(1, Ordering::SeqCst); } ''' + drain + r''' } fn main() { let finished = Arc::new(AtomicUsize::new(0)); - let shared = Arc::new(Shared { reclaim: Mutex(std::sync::Mutex::new(Reclaims(vec![Reclaim(finished.clone())]))), wakes: AtomicUsize::new(0) }); + let shared = Arc::new(Shared { reclaim: Mutex(std::sync::Mutex::new(Reclaims(vec![Reclaim(finished.clone())]))), wakes: AtomicUsize::new(0), queues: Available(AtomicUsize::new(0)) }); shared.drain_reclaims(); assert_eq!(finished.load(Ordering::SeqCst), 1); assert_eq!(shared.wakes.load(Ordering::SeqCst), 1); shared.drain_reclaims(); assert_eq!(shared.wakes.load(Ordering::SeqCst), 1); + assert_eq!(shared.queues.0.load(Ordering::SeqCst), 1); let live = Arc::new(AtomicUsize::new(0)); + let mut borrowed = idle(&live); + borrowed.retirement_ready = true; + borrowed.source_hint.possible = true; + borrowed.source_readers.store(1, Ordering::Release); + assert!(!borrowed.shareable()); + borrowed.release_owner().unwrap(); + assert!(!borrowed.reusable()); + borrowed.pool_phase = PoolPhase::Vacant; + assert!(!borrowed.can_rearm()); + borrowed.source_readers.store(0, Ordering::Release); + assert!(borrowed.reusable() && borrowed.can_rearm()); + drop(borrowed); for _ in 0..512 { // Rebound detached queue; lost VM / killed publisher / allocation error // before the first new command. No future completion exists to free it. @@ -266,8 +283,8 @@ def main(): ('missing-fault-release', source.replace(fault_fix, '', 1), False), ('missing-kill-release', source.replace('Previous::retire_witnessed(&mut self.previous);\n self.exit', 'self.exit'), False), ('missing-reclaim-wake', source.replace('self.queue_events();', ''), False), - ('share-before-retirement', source.replace('|| !self.retirement_ready', ''), False), - ('share-during-kill', source.replace('|| self.exit.is_some() || self.active.len', '|| self.active.len'), False)] + ('share-before-retirement', source.replace('&& self.owner.is_some() && self.retirement_ready', '&& self.owner.is_some()'), False), + ('share-during-kill', source.replace('&& self.exit.is_none() && self.active.len', '&& self.active.len'), False)] with tempfile.TemporaryDirectory(prefix='g17-capacity-') as tmp: for name, rust, passes in controls: path = Path(tmp) / (name + '.rs') diff --git a/tools/asahi/g17-compute-constructor-tests.py b/tools/asahi/g17-compute-constructor-tests.py index f4fdcf2c68222..8c0c4dc772885 100644 --- a/tools/asahi/g17-compute-constructor-tests.py +++ b/tools/asahi/g17-compute-constructor-tests.py @@ -50,7 +50,7 @@ def fixture(): fn notify_all(&self){} } struct Shared{preparations:Gate,state:State,changed:Changed,crashed:AtomicBool} -impl Shared{fn drm(&self)->Result<()>{Ok(())}} +impl Shared{fn drm(&self)->Result<()>{Ok(())} fn note_changed(&self){self.changed.notify_all();}} struct Context;impl Context{fn vm(&self)->&(){&()}} struct Backend{shared:Shared,context:Context} struct Allocator<'a>{dev:&'a (),uat:&'a ()} diff --git a/tools/asahi/g17-compute-deadline-tests.py b/tools/asahi/g17-compute-deadline-tests.py index eca8b73c5b12c..88c89f6857799 100644 --- a/tools/asahi/g17-compute-deadline-tests.py +++ b/tools/asahi/g17-compute-deadline-tests.py @@ -49,7 +49,7 @@ def fixture(): static MODE:AtomicUsize=AtomicUsize::new(0);static CANCEL:AtomicU64=AtomicU64::new(u64::MAX); static STATUS:AtomicU64=AtomicU64::new(u64::MAX);static CLOSED:AtomicBool=AtomicBool::new(false); fn now_ns()->u64{NOW.load(Ordering::SeqCst)} -fn advance(ns:u64){NOW.fetch_add(ns,Ordering::SeqCst);} +fn advance(ns:u64){NOW.fetch_add(ns,Ordering::SeqCst);assert!(now_ns()<=6_000_000_000,"admission budget exceeded");} mod kernel{pub mod time{ pub fn msecs_to_jiffies(ms:u32)->u32{ms} pub struct Delta(pub u64);impl Delta{pub fn from_millis(ms:u64)->Self{Self(ms*1_000_000)}pub fn from_micros(us:i64)->Self{Self(us as u64*1_000)}} @@ -76,20 +76,33 @@ def fixture(): impl Packet{fn check_dependencies(&self)->Result{Ok(())}fn cancel_requested(&self)->bool{now_ns()>=CANCEL.load(Ordering::SeqCst)}fn is_published(&self)->bool{false}} struct DeferredBatch;impl DeferredBatch{fn publication()->Result{Ok(Self)}fn finish(&mut self){}} struct Recovery;impl Recovery{fn pending(&self)->bool{MODE.load(Ordering::SeqCst)==1}} -struct Memory;impl Memory{fn recovery_state(_:&())->Result{Ok(0)}} +struct Init;impl Init{fn compute_vm_cached(&self,_:&())->bool{true}} +struct Context;impl Context{fn vm(&self)->&(){&()}} +struct Memory;impl Memory{fn recovery_state(_:&Init)->Result{Ok(0)}} struct Queues; impl Queues{ + fn compute_bound(&self,_:u64,_:&Context,_:usize)->Result{Ok(MODE.load(Ordering::SeqCst)!=0)} fn compute(&self,_:u64)->Option<()>{if MODE.load(Ordering::SeqCst)==0{None}else{Some(())}} fn render_prefix(&self,_:u64,_:&Arc)->Result>{Ok(vec![])} - fn release_compute_witness(&self,_:&(),_:u64,_:&mut DeferredBatch)->Result{Ok(MODE.load(Ordering::SeqCst)==2)} + fn release_compute_witness(&self,_:&Init,_:&(),_:u64,_:&mut DeferredBatch)->Result{Ok(MODE.load(Ordering::SeqCst)==2)} } -struct Firmware{recovery:Recovery,init:(),queues:Queues} +struct Firmware{recovery:Recovery,init:Init,queues:Queues,port:()} impl Firmware{fn replay_compute(&mut self,_:&mut DeferredBatch)->Result{Ok(())}fn publish_compute(&mut self,_:u64,_:Arc,_:&[()],_:&mut DeferredBatch)->Result{Err(EAGAIN)}} struct State(Mutex>>);impl State{fn lock(&self)->MutexGuard<'_,Option>>{self.0.lock().unwrap()}} -struct Shared{preparations:Arc,state:State,crashed:AtomicBool,active_cores:u32}impl Shared{fn queue_events(&self){}} -struct Backend{shared:Shared,owner:u64} +struct Available;impl Available{fn observe(&self)->u64{now_ns()}} +struct Shared{preparations:Arc,state:State,crashed:AtomicBool,active_cores:u32,progress:Available}impl Shared{fn queue_events(&self){}} +struct ComputeDemand;impl ComputeDemand{fn new(_:&Shared)->Self{Self}} +struct Backend{shared:Shared,owner:u64,context:Context} fn required_blocks(_:u32,_:u16)->Result{Ok(1)} impl Backend{ + fn compute_direct(&self,_:&Arc,_:usize,_:&mut DeferredBatch)->Option{None} + fn sources_published(&self,_:&Arc)->Result{Ok(true)} + fn remember_direct(&self,_:&Queues){} + fn wait_release(&self,_:&Available,_:u64,admission:&Admission<'_>)->Result{ + let _=admission.slice_ms()?; + advance(1_000_000); + admission.check() + } fn admission_open(&self)->Result{Ok(())} // The real ensure_compute forwards the original context into this allocation // boundary. Each success loses its binding before the publication mutex. @@ -99,7 +112,7 @@ def fixture(): ''' + ensure + '\n' + packet + r''' } fn main(){ - let b=Backend{owner:1,shared:Shared{active_cores:1,preparations:Arc::new(Gate{wait:Wait,changed:Changed}),state:State(Mutex::new(Some(Box::new(Firmware{recovery:Recovery,init:(),queues:Queues})))),crashed:AtomicBool::new(false)}}; + let b=Backend{owner:1,context:Context,shared:Shared{active_cores:1,progress:Available,preparations:Arc::new(Gate{wait:Wait,changed:Changed}),state:State(Mutex::new(Some(Box::new(Firmware{recovery:Recovery,init:Init,queues:Queues,port:()})))),crashed:AtomicBool::new(false)}}; let p=Arc::new(Packet{command:Validated::Compute{scratch:Scratch},completion:Completion}); for mode in 0..4 { MODE.store(mode,Ordering::SeqCst);NOW.store(0,Ordering::SeqCst);CALLS.store(0,Ordering::SeqCst); @@ -144,8 +157,8 @@ def main(): source = fixture() mutants = [ ('fixed', source, ''), - ('renew-on-binding-loss', source.replace('let admission = Admission::new(deadline, &check);\n loop {', 'loop {\n let admission = Admission::new(now_ns().saturating_add(WORK_RING_WAIT_NS), &check);'), 'one admission budget'), - ('omit-nested-check', source.replace(' admission.check()?;\n let pending', ' let pending'), 'nested unavailable-capacity'), + ('renew-on-binding-loss', source.replace('let admission = Admission::new(deadline, &check);\n let progress = &self.shared.progress;\n loop {', 'let progress = &self.shared.progress;\n loop {\n let admission = Admission::new(now_ns().saturating_add(WORK_RING_WAIT_NS), &check);'), 'admission budget exceeded'), + ('ignore-deadline', source.replace('if now_ns() >= self.deadline', 'if false'), 'admission budget exceeded'), ('omit-gate-check', source.replace(' admission.check()?;\n match self.try_enter()', ' match self.try_enter()').replace('admission.slice_ms()?', '10'), 'gate wake storm'), ] # Bind the old unbounded mutant with the review's eventual cancellation. diff --git a/tools/asahi/g17-compute-growth-deadline-tests.py b/tools/asahi/g17-compute-growth-deadline-tests.py index 1e1e715de8377..3c652a96df759 100644 --- a/tools/asahi/g17-compute-growth-deadline-tests.py +++ b/tools/asahi/g17-compute-growth-deadline-tests.py @@ -50,7 +50,8 @@ def fixture(): impl Lease{fn is_current(&self)->bool{true}} struct State(Mutex>>); impl State{fn lock(&self)->MutexGuard<'_,Option>>{self.0.lock().unwrap()}} -struct Shared{preparations:Gate,state:State,crashed:AtomicBool} +struct Available;impl Available{fn observe(&self)->u64{now_ns()}fn notify(&self){}} +struct Shared{preparations:Gate,state:State,crashed:AtomicBool,progress:Available,channels:Available} impl Shared{fn drm(&self)->Result<()>{Ok(())}fn queue_events(&self){}} struct Context;impl Context{fn vm(&self)->&(){&()}} struct Backend{shared:Shared,context:Context,owner:u64} @@ -85,12 +86,17 @@ def fixture(): impl Firmware{fn scan_compute(&mut self,_:Option<()>,_:&mut DeferredBatch)->Result{Ok(())}} struct DeferredBatch;impl DeferredBatch{fn publication()->Result{Ok(Self)}fn finish(&mut self){}} impl Backend{ + fn wait_release(&self,_:&Available,_:u64,admission:&Admission<'_>)->Result{ + let _=admission.slice_ms()?; + kernel::time::delay::fsleep(kernel::time::Delta::from_millis(1)); + admission.check() + } ''' + methods + r''' } fn main(){ for fresh in [false,true]{for cancel in [false,true]{ NOW.store(0,Ordering::SeqCst);ABORTED.store(false,Ordering::SeqCst);RESERVED.store(true,Ordering::SeqCst); - let b=Backend{owner:1,context:Context,shared:Shared{preparations:Gate,crashed:AtomicBool::new(false),state:State(Mutex::new(Some(Box::new(Firmware{uat:(),init:(),primary:Primary,queues:Registry{compute:vec![Some(Entry{reservation:ComputeReservation,queue:Some(Box::new(Queue{fresh}))})]}}))))}}; + let b=Backend{owner:1,context:Context,shared:Shared{preparations:Gate,progress:Available,channels:Available,crashed:AtomicBool::new(false),state:State(Mutex::new(Some(Box::new(Firmware{uat:(),init:(),primary:Primary,queues:Registry{compute:vec![Some(Entry{reservation:ComputeReservation,queue:Some(Box::new(Queue{fresh}))})]}}))))}}; let check=||{if cancel&&now_ns()>=10_000_000{Err(ECANCELED)}else{Ok(())}}; let admission=Admission::new(20_000_000,&check); assert_eq!(b.grow_compute(42,&admission),Err(if cancel{ECANCELED}else{EBUSY}),"growth must propagate cancel/deadline"); @@ -106,7 +112,7 @@ def fixture(): def main(): source = fixture() mutants = [('fixed', source, ''), - ('omit-growth-check', source.replace(' admission.check()?;\n let firmware', ' let firmware'), 'growth admission exceeded'), + ('ignore-shared-deadline', source.replace('if now_ns() >= self.deadline', 'if false').replace('if remaining == 0 { return Err(EBUSY); }', ''), 'growth admission exceeded'), ('keep-growth-alias', source.replace(' drop(alias.take());', ''), 'growth aliases must precede')] with tempfile.TemporaryDirectory(prefix='g17-compute-growth-') as tmp: for name, code, expected in mutants: diff --git a/tools/asahi/g17-firmware-selection-tests.py b/tools/asahi/g17-firmware-selection-tests.py new file mode 100644 index 0000000000000..771c9c19c6e1e --- /dev/null +++ b/tools/asahi/g17-firmware-selection-tests.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only OR MIT +"""Compile the G17 build selector with a modelled image-identity result. + +The shared fwid KUnit tests check the actual SHA-256 normalization. This host +check ensures OS metadata cannot admit a different image or veto a known one. +""" + +from pathlib import Path +import subprocess +import tempfile + +DRIVER = Path(__file__).resolve().parents[2] / "drivers/gpu/drm/asahi" + + +def method(source, signature): + start = source.index(signature) + end = source.index("{", start) + 1 + depth = 1 + while depth: + depth += (source[end] == "{") - (source[end] == "}") + end += 1 + return source[start:end] + + +HEADER = r''' +#![allow(dead_code)] +type Result = std::result::Result; +const ENODEV: i32 = -19; +macro_rules! dev_info { ($($t:tt)*) => { () }; } +macro_rules! dev_err { ($($t:tt)*) => { () }; } +struct AsahiDrmDevice { + version: Option<[u32; 3]>, + image: u32, + checked: std::cell::RefCell>, +} +mod fw { + pub struct Variant(pub u32); + pub mod variant { + use super::Variant; + pub struct Build { + pub os_version: [u32; 3], + pub name: &'static str, + pub image: u32, + pub variant: &'static Variant, + } + pub enum Firmware { Fixed(&'static Variant), Identified(&'static [Build]) } + } +} +struct Gpu; +impl Gpu { + fn os_firmware_version(dev: &AsahiDrmDevice) -> Option<[u32; 3]> { dev.version } + fn identify_image(dev: &AsahiDrmDevice, build: &'static fw::variant::Build) -> Result { + dev.checked.borrow_mut().push(build.image); + if dev.image == build.image { Ok(()) } else { Err(ENODEV) } + } +''' + +TESTS = r''' +} +static FIRST: fw::Variant = fw::Variant(1); +static SECOND: fw::Variant = fw::Variant(2); +static BUILDS: [fw::variant::Build; 2] = [ + fw::variant::Build { os_version: [26,6,2], name: "first", image: 1, variant: &FIRST }, + fw::variant::Build { os_version: [27,0,0], name: "second", image: 2, variant: &SECOND }, +]; +fn device(version: Option<[u32; 3]>, image: u32) -> AsahiDrmDevice { + AsahiDrmDevice { version, image, checked: std::cell::RefCell::new(Vec::new()) } +} +fn main() { + use fw::variant::Firmware; + for version in [None, Some([26,6,2]), Some([26,7,0]), Some([99,0,0])] { + let dev = device(version, 1); + assert_eq!(Gpu::firmware_variant(&dev, &Firmware::Identified(&BUILDS)).unwrap().0, 1); + } + let dev = device(Some([27,0,0]), 1); + assert_eq!(Gpu::firmware_variant(&dev, &Firmware::Identified(&BUILDS)).unwrap().0, 1); + assert_eq!(*dev.checked.borrow(), [2,1], "the OS version only orders the candidates"); + let dev = device(Some([26,6,2]), 9); + assert!(Gpu::firmware_variant(&dev, &Firmware::Identified(&BUILDS)).is_err()); + assert_eq!(*dev.checked.borrow(), [1,2]); + let dev = device(None, 9); + assert_eq!(Gpu::firmware_variant(&dev, &Firmware::Fixed(&SECOND)).unwrap().0, 2); + assert!(dev.checked.borrow().is_empty(), "fixed Neo admission is unchanged"); + println!("PASS G17 selection: exact image required, OS hint optional, fixed Neo unchanged"); +} +''' + + +def main(): + source = (DRIVER / "g17/mod.rs").read_text() + selector = method(source, " fn firmware_variant(") + with tempfile.TemporaryDirectory(prefix="asahi-firmware-selection-") as tmp: + root = Path(tmp) + (root / "main.rs").write_text(HEADER + selector + TESTS) + binary = root / "test" + subprocess.run(["rustc", "--edition=2021", "-Dwarnings", str(root / "main.rs"), + "-o", str(binary)], check=True) + subprocess.run([str(binary)], check=True) + + +if __name__ == "__main__": + main() diff --git a/tools/asahi/g17-growth-gate-tests.py b/tools/asahi/g17-growth-gate-tests.py index d6ad5f9beef30..8b4285782f25b 100644 --- a/tools/asahi/g17-growth-gate-tests.py +++ b/tools/asahi/g17-growth-gate-tests.py @@ -38,6 +38,7 @@ def fixture(): struct Gate{state:AtomicU64,wait:Mutex,changed:CondVar} struct Lease{gate:Arc,epoch:u64} impl Gate { + fn wake_waiters(&self) {} ''' + methods + '\n}\n' + item(gate,'impl Drop for Lease') + r''' struct Backend {gate:Arc,ready:mpsc::Sender<()>,proceed:StdMutex>} impl Backend { diff --git a/tools/asahi/g17-growth-tests.py b/tools/asahi/g17-growth-tests.py index 1236f166876af..2c46ccbf6359a 100644 --- a/tools/asahi/g17-growth-tests.py +++ b/tools/asahi/g17-growth-tests.py @@ -23,7 +23,7 @@ def fixture(): const EIO:i32=-5;const EBUSY:i32=-16;const EINVAL:i32=-22; fn now_ns()->u64{123} struct Context {live:Arc} -impl Context {fn new(live:&Arc)->Arc{live.fetch_add(1,Ordering::SeqCst);Arc::new(Self{live:live.clone()})}} +impl Context {fn note_compute_binding(&self,_:bool){} fn new(live:&Arc)->Arc{live.fetch_add(1,Ordering::SeqCst);Arc::new(Self{live:live.clone()})}} impl Drop for Context {fn drop(&mut self){self.live.fetch_sub(1,Ordering::SeqCst);}} struct Binding {context:Arc,pool_alias:Option,descriptors:Option} struct Previous {context:Arc,binding:Option,publication:Option<(u64,u64)>} @@ -31,17 +31,19 @@ def fixture(): struct Fifo {len:usize} struct Graph; impl Graph {fn set_owner(&mut self,_:&Context)->Result{Ok(())}} +struct ReaderHint; impl ReaderHint {fn has_readers(&self,count:&AtomicU32)->bool{count.load(Ordering::Acquire)!=0}} struct Queue { owner:Option,installed:bool,growing:bool,growth_blocks:usize,free_list_generation:u64, pool_phase:PoolPhase,release_sent_ns:Option,pool:Pool,binding:Option,previous:Option,graph:Graph, active:Fifo,replays:Fifo,quarantined:bool,released:bool,retirement_ready:bool, retired_by_teardown:bool,released_at:u64,exit:Option<()>,needs_fresh_backing:bool, + source_hint:ReaderHint,source_readers:AtomicU32, } fn idle(live:&Arc)->Queue{Queue{owner:Some(1),installed:true,growing:false,growth_blocks:0, free_list_generation:0,pool_phase:PoolPhase::Populated,release_sent_ns:None,pool:Pool{pages:Some(Pages(10)),fail:false}, binding:Some(Binding{context:Context::new(live),pool_alias:Some(1),descriptors:Some(2)}),previous:None,graph:Graph, active:Fifo{len:0},replays:Fifo{len:0},quarantined:false,released:false,retirement_ready:true, - retired_by_teardown:false,released_at:0,exit:None,needs_fresh_backing:false}} + retired_by_teardown:false,released_at:0,exit:None,needs_fresh_backing:false,source_hint:ReaderHint,source_readers:AtomicU32::new(0)}} impl Queue { ''' + item(source,'pub(crate) fn release_owner(') + item(source,'pub(crate) fn reusable(') + '\n}\n' methods = '\n'.join(item(source, sig) for sig in ['pub(crate) fn begin_growth(', 'pub(crate) fn finish_growth(', 'pub(crate) fn growth_token(', 'pub(crate) fn abort_growth(', 'pub(crate) fn observe_vacancy(']) @@ -156,7 +158,7 @@ def fixture(): def main(): source = fixture() runtime = (ROOT / 'runtime.rs').read_text() - assert 'growth = queue.growth_token();' in runtime and 'queue.abort_growth(token)?' in runtime + assert 'growth = queue.growth_token();' in runtime and 'queue.abort_growth(token)' in runtime cases = [('fixed', source), ('missing-abort-transfer', source.replace('self.growing = false;\n self.growth_blocks = 0;\n self.release_owner().map(Some)', 'self.release_owner().map(Some)')), ('ignore-consumption', source.replace('if !crate::g17::freelist::control_consumed(consumer, producer, cursor)? {', 'if false {')), diff --git a/tools/asahi/g17-owner-release-order-tests.py b/tools/asahi/g17-owner-release-order-tests.py index 201f525718950..55b95af1b279d 100755 --- a/tools/asahi/g17-owner-release-order-tests.py +++ b/tools/asahi/g17-owner-release-order-tests.py @@ -13,7 +13,8 @@ import subprocess import tempfile -ROOT = Path(__file__).resolve().parents[2] / 'drivers/gpu/drm/asahi/g17' +ROOT = Path(os.environ.get('G17_ROOT', + Path(__file__).resolve().parents[2] / 'drivers/gpu/drm/asahi/g17')) def item(source, signature): @@ -64,19 +65,27 @@ def check_guards(): ordered(detach, '.park();', 'parked.release_consumed(context.scheduler_va());') runtime = (ROOT / 'runtime.rs').read_text() # A compute queue does not change owner while its scheduler is released. - ordered(item(runtime, 'pub(super) fn rebind_compute('), '.filter(|queue| queue.reusable())', + ordered(item(runtime, 'fn bind_reusable('), '.filter(|queue| queue.reusable())', 'teardown.releasing(bound)', 'continue;', 'queue.bind_owner(owner, context.clone())?;') ordered(item(runtime, 'pub(super) fn bind_rearmed('), 'teardown.releasing(bound)', 'return Err(EAGAIN);', 'queue.bind_owner(') - ordered(item(runtime, 'fn publish_compute('), '.pending_switch_from()', 'queues.teardown.releasing(previous)', + ordered(item(runtime, 'fn publish_compute_raw('), '.pending_switch_from()', 'queues.teardown.releasing(previous)', 'return Err(EAGAIN);', 'queue.publish(') # A busy pair (for example one whose scheduler release is still being consumed) # kicks the event worker, which observes the release and parks the pair. - ordered(item(submit, 'fn wait_render_busy('), 'WORK_RING_WAIT_NS', 'return Err(ETIMEDOUT);', - 'self.shared.queue_events();', 'fsleep(') - for name, path in [('take_pair', 'runtime/render.rs'), ('take_render_observation', 'runtime/render/observe.rs')]: - body = item((ROOT / path).read_text(), f'fn {name}(') - ordered(body, 'entry.borrowed_switch = pair.switch_in_flight();', 'entry.pair.take()') + ordered(item(submit, 'fn wait_render_busy('), 'WORK_RING_WAIT_NS', + 'self.shared.queue_events();', 'self.wait_domain(') + ordered(item(submit, 'fn wait_domain('), 'if remaining == 0', 'return Err(ETIMEDOUT);', 'domain.block(') + borrowed = item((ROOT / 'runtime/render.rs').read_text(), 'fn take_pair(') + ordered(borrowed, 'entry.borrowed_switch = pair.switch_in_flight();', + 'entry.pair.take()') + # The current event collector observes and retires the pair under one + # device-mutex hold; it no longer borrows it through a second take path. + observed = item((ROOT / 'runtime/render/observe.rs').read_text(), + 'fn observe_renders(') + ordered(observed, 'let mut state = self.state.lock();', + 'firmware.next_render_observation(cursor)?', '.observe_front()', + 'pair.retire_observed(') print('PASS guard placement: rebind decision, rebind commit, first publication, switch record, borrows, parked scheduler, compute owner changes, busy wait') @@ -110,7 +119,10 @@ def fixture(): } #[derive(Clone, Copy, PartialEq, Eq)] enum Phase { Deferred, Draining, Released(u32) } -struct Entry { context: Arc, phase: Phase, armed: u64, announce_pending: bool } +struct Entry { + context: Arc, phase: Phase, armed: u64, announce_pending: bool, + render_kill: Option<()>, kill_wait: Option<(u64, bool)>, kill_refused: bool, +} struct Pending { entries: Vec } impl Pending { PENDING @@ -118,8 +130,10 @@ def fixture(): struct Ticket { ordinal: u64 } struct Active { ticket: Ticket } struct Packet { context: Arc } -struct Pair { executed: u64, switch: Option<(u64, u64)>, replay: Option<()>, active: Vec, idle: bool } +struct Pair { executed: u64, switch: Option<(u64, u64)>, replay: Vec<()>, active: Vec, idle: bool } impl Pair { + // This ordering fixture models no faulted/executed unstamped pair. + fn executed_for_kill(&self, _: &Arc) -> bool { false } SWITCH /// Host stand-in for a successful publication of `ordinal` (stand-in, not production). fn publish(&mut self, packet: &Packet, ordinal: u64) { @@ -177,7 +191,7 @@ def fixture(): assert!(!table.releasing(&quiet)); // Switch tracking across an owner change of one pair. - let mut pair = Pair { executed: 0, switch: None, replay: None, active: Vec::new(), idle: true }; + let mut pair = Pair { executed: 0, switch: None, replay: Vec::new(), active: Vec::new(), idle: true }; assert_eq!(pair.owner_switch_from(&c), None, "a fresh graph has no previous scheduler"); pair.publish(&Packet { context: d.clone() }, 0); pair.active.clear(); diff --git a/tools/asahi/g17-pool-release-tests.py b/tools/asahi/g17-pool-release-tests.py index b7c715727acab..70004fe94ecf9 100644 --- a/tools/asahi/g17-pool-release-tests.py +++ b/tools/asahi/g17-pool-release-tests.py @@ -31,7 +31,7 @@ def fixture(): predicate = '.any(|queue| queue.retirement_polling() || queue.pool_release_pending())' assert events.count(predicate) == 1 methods = '\n'.join(item(runtime, name) for name in [ - 'fn compute_admission_error(', 'pub(super) fn reserve_compute(']).replace('pub(super) ', '') + 'fn compute_admission_error(', 'pub(super) fn reserve_compute(']).replace('pub(super) ', '').replace('queue: ComputeSlot::new()?', 'queue: None') pending = item(compute, 'pub(crate) fn pool_release_pending(') + '\n' + \ item(compute, 'pub(crate) fn release_settling(') return r''' @@ -72,10 +72,12 @@ def fixture(): struct Id { qid: u8 } #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct ComputeReservation { slot: usize, id: Id, owner: u64 } -struct ComputeEntry { reservation: ComputeReservation, queue: Option> } +type ComputeSlot = Option>; +struct ComputeEntry { reservation: ComputeReservation, queue: ComputeSlot } +struct Admission; impl Admission {fn queues(&self)->u32{4}} struct QueueIds { free: bool, fail: Option } impl QueueIds { - fn reserve(&mut self, _: u64, _: DataMaster, preferred: Option) -> Result { + fn reserve_for(&mut self, _: u64, _: DataMaster, preferred: Option, _: usize) -> Result { if let Some(error) = self.fail { return Err(error); } if !self.free { return Err(ENOSPC); } self.free = false; @@ -93,8 +95,11 @@ def fixture(): compute: Vec>, identities: Identities, qids: QueueIds, + admission: Admission, } impl Registry { + // Physical-QID capacity notification does not change this pool-release fixture. + fn note_build_room(&self) {} METHODS fn poll(&self) -> bool { self.compute.iter().flatten().filter_map(|entry| entry.queue.as_deref())PREDICATE @@ -109,7 +114,7 @@ def fixture(): })); } entries.push(None); - Registry { compute: entries, identities: Identities { next: 100 }, qids: QueueIds { free, fail: None } } + Registry { compute: entries, identities: Identities { next: 100 }, qids: QueueIds { free, fail: None }, admission: Admission } } fn main() { // Captured failure: four idle released queues, full physical namespace, diff --git a/tools/asahi/g17-resource-tests.py b/tools/asahi/g17-resource-tests.py index 35f970799b086..ae7e53ec6cf6f 100644 --- a/tools/asahi/g17-resource-tests.py +++ b/tools/asahi/g17-resource-tests.py @@ -24,7 +24,7 @@ def item(start): limits='\n'.join(re.search(r'const '+name+r': [^;]+;',mmu).group(0) for name in ['EXECUTION_CONTEXT_MASK','MAX_EXECUTION_CONTEXTS']) header='''#![allow(dead_code, unused_macros)] type Result = std::result::Result; - mod g17 { pub mod capacity { + mod g17 { pub mod trace { pub fn teardown(_:u8,_:u64,_:u64,_:u64) {} } pub mod capacity { pub enum Site { RenderSlots, LogicalQueues, ComputeReserve, PhysicalQids } pub fn refused(_: Site) -> i32 { -28 } }} @@ -134,10 +134,10 @@ def item(s,start): return s[at:end] s=(source/'kick.rs').read_text() fw=(source/'fw/kick.rs').read_text() - constants=re.search(r'const QID_MAX: [^;]+;',fw).group(0)+'\n'+'\n'.join(re.search(r'const '+name+r': [^;]+;',s).group(0) for name in ['QID_COUNT','COMPUTE_RENDER_RESERVE']) + constants=re.search(r'const QID_MAX: [^;]+;',fw).group(0)+'\n'+'\n'.join(re.search(r'const '+name+r': [^;]+;',s).group(0) for name in ['QID_COUNT','RENDER_HEADROOM','FULL_RENDER_RESERVE']) header='''#![allow(dead_code, unused_macros)] type Result = std::result::Result; - mod g17 { pub mod capacity { + mod g17 { pub mod trace { pub fn teardown(_:u8,_:u64,_:u64,_:u64) {} } pub mod capacity { pub enum Site { RenderSlots, LogicalQueues, ComputeReserve, PhysicalQids } pub fn refused(_: Site) -> i32 { -28 } }} @@ -148,16 +148,19 @@ def item(s,start): #[derive(Clone,Copy,PartialEq,Eq,Debug)] enum DataMaster {Compute,Tiling,Fragment} #[derive(Clone,Copy,PartialEq,Eq,Debug)] struct Id {qid:u8,owner:u64,engine:DataMaster} #[derive(Clone,Copy)] struct Identity {id:Id,published:bool} - '''+constants+'\n'+item(s,'pub(crate) struct QueueIds')+'\nimpl QueueIds {\n'+''.join(item(s,'pub(crate) fn '+name+'(')+'\n' for name in ['reserve_initial_render','reserve','publish','can_cancel_unpublished','cancel_unpublished'])+'}\n' + '''+constants+'\n'+item(s,'const fn render_reserve(')+'\n'+item(s,'pub(crate) struct QueueIds')+'\nimpl QueueIds {\n'+''.join(item(s,'pub(crate) fn '+name+'(')+'\n' for name in ['reserve_initial_render','reserve','reserve_for','publish','can_cancel_unpublished','cancel_unpublished'])+''.join(item(s, sig)+'\n' for sig in ['fn render_ids(', 'fn compute_room('])+'}\n' test=''' - fn fresh()->QueueIds {QueueIds {entries:[None;QID_COUNT],initial_render:[None;2],compute_publication:0,leased:0}} + fn fresh()->QueueIds {QueueIds {entries:[None;QID_COUNT],initial_render:[None;2],leased:0,floor:0}} fn main(){ - for render in [0,2,16,32,112,126] { - let mut q=fresh(); + for floor in [0,32] { + for render in [0,2,16,32,80,96,112,126].into_iter().filter(|render|*render<=QID_COUNT-floor) { + let mut q=fresh();q.floor=floor; for i in 0..render {q.reserve(i as u64+1,if i%2==0 {DataMaster::Tiling}else{DataMaster::Fragment},None).unwrap();} let mut compute=0; while q.reserve(1000+compute,DataMaster::Compute,None).is_ok(){compute+=1;} - assert_eq!(compute as usize,QID_COUNT-render.max(COMPUTE_RENDER_RESERVE)); + assert_eq!(compute as usize,QID_COUNT-floor-render.max(FULL_RENDER_RESERVE-floor)); + assert!(q.entries[..floor].iter().all(Option::is_none)); + } } let mut q=fresh();let first=q.reserve(1,DataMaster::Tiling,None).unwrap(); assert!(q.can_cancel_unpublished(first));q.publish(first).unwrap(); @@ -165,7 +168,7 @@ def item(s,start): let unused=q.reserve(2,DataMaster::Fragment,None).unwrap();q.cancel_unpublished(unused).unwrap();assert_eq!(q.leased,1); let next=q.reserve(3,DataMaster::Fragment,None).unwrap();assert_eq!(next.qid,unused.qid); assert!(!q.can_cancel_unpublished(unused));assert_eq!(q.cancel_unpublished(unused),Err(EINVAL)); - println!("queue IDs: six render/compute capacity boundaries; compute-only render reserve retained; published and stale identities protected"); + println!("queue IDs: render/compute capacity boundaries at zero and reserved floors; compute-only render reserve retained; published and stale identities protected"); // Reproduce the hardware snapshot: 124 installed render identities, two // installed compute identities, and two initial reservations fill128 IDs. let mut q=fresh();q.reserve_initial_render().unwrap(); @@ -192,8 +195,8 @@ def item(s,start): assert_eq!(q.entries[0].unwrap().id,protected); // Replacement must not consume the protected render minimum. let mut q=fresh();q.reserve_initial_render().unwrap(); - for owner in 3..=COMPUTE_RENDER_RESERVE as u64 {q.reserve(owner,DataMaster::Tiling,None).unwrap();} - for owner in 1000..1000+(QID_COUNT-COMPUTE_RENDER_RESERVE) as u64 {q.reserve(owner,DataMaster::Compute,None).unwrap();} + for owner in 3..=FULL_RENDER_RESERVE as u64 {q.reserve(owner,DataMaster::Tiling,None).unwrap();} + for owner in 1000..1000+(QID_COUNT-FULL_RENDER_RESERVE) as u64 {q.reserve(owner,DataMaster::Compute,None).unwrap();} assert_eq!(q.leased,QID_COUNT);assert_eq!(q.reserve(2000,DataMaster::Compute,None),Err(ENOSPC)); assert!(q.initial_render.iter().all(Option::is_some)); // Ordinary free capacity leaves the initialization reservations untouched. diff --git a/tools/asahi/g17-uninstalled-cleanup-tests.py b/tools/asahi/g17-uninstalled-cleanup-tests.py index 33d2f252eb81c..d74f67aba6267 100644 --- a/tools/asahi/g17-uninstalled-cleanup-tests.py +++ b/tools/asahi/g17-uninstalled-cleanup-tests.py @@ -61,7 +61,8 @@ def fixture(): impl Drop for Guard<'_>{fn drop(&mut self){LOCKED.store(false,Ordering::SeqCst);}} struct State(Mutex>>); impl State{fn lock(&self)->Guard<'_>{let g=self.0.lock().unwrap();assert!(!LOCKED.swap(true,Ordering::SeqCst));Guard(g)}} -struct Shared{state:State} +struct Available; impl Available {fn notify(&self){}} +struct Shared{state:State,channels:Available} impl Shared{fn queue_events(&self){}} struct Backend{shared:Arc} impl Backend { @@ -76,7 +77,7 @@ def fixture(): } } fn main(){ - let b=Backend{shared:Arc::new(Shared{state:State(Mutex::new(Some(Box::new(Firmware{queues:Registry}))))})}; + let b=Backend{shared:Arc::new(Shared{state:State(Mutex::new(Some(Box::new(Firmware{queues:Registry})))),channels:Available})}; b.finish_uninstalled_compute(None).unwrap(); for _ in 0..512 { MAPPED.store(true,Ordering::SeqCst);RESERVED.store(true,Ordering::SeqCst); diff --git a/tools/asahi/t6040-calibration-tests.py b/tools/asahi/t6040-calibration-tests.py new file mode 100644 index 0000000000000..d802013175b97 --- /dev/null +++ b/tools/asahi/t6040-calibration-tests.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +"""Compile the T6040 wire builders and calibration checks without a GPU.""" + +from pathlib import Path +import re +import subprocess +import tempfile + +DRIVER = Path(__file__).resolve().parents[2] / "drivers/gpu/drm/asahi" + + + +HEADER = r''' +#![allow(dead_code)] +extern crate self as kernel; +mod prelude { + pub type KVec = Vec; + pub use std::mem::size_of; + pub type Result = std::result::Result; + #[derive(Debug)] + pub struct Error; + impl From for Error { + fn from(_: std::num::TryFromIntError) -> Self { Self } + } + pub const EINVAL: Error = Error; + pub const EIO: Error = Error; + pub const EOVERFLOW: Error = Error; + pub unsafe trait Zeroable {} +} +macro_rules! static_assert { ($e:expr) => { const _: () = assert!($e); } } +fn clear(value: &mut T) { + unsafe { std::ptr::write_bytes(value, 0, 1); } +} +#[macro_export] +macro_rules! c_str { + ($s:literal) => { std::ffi::CStr::from_bytes_with_nul(concat!($s, "\0").as_bytes()).unwrap() }; +} +pub mod str { pub use std::ffi::CStr; } +mod of { + use crate::prelude::*; + #[derive(Clone)] + pub struct Node(pub std::collections::BTreeMap>); + pub trait Property: Sized { fn decode(value: &[u32]) -> Result; } + impl Property for u32 { + fn decode(value: &[u32]) -> Result { + if value.len() == 1 { Ok(value[0]) } else { Err(EINVAL) } + } + } + impl Property for Vec { fn decode(value: &[u32]) -> Result { Ok(value.to_vec()) } } + impl Node { + pub fn get_property(&self, name: &crate::str::CStr) -> Result { + T::decode(self.0.get(name.to_str().unwrap()).ok_or(EINVAL)?) + } + } +} +mod driver { + pub struct AsahiDrmDevice(pub DrmDevice); + pub struct DrmDevice(pub Device); + pub struct Device(pub crate::of::Node); + impl AsRef for AsahiDrmDevice { fn as_ref(&self) -> &DrmDevice { &self.0 } } + impl AsRef for DrmDevice { fn as_ref(&self) -> &Device { &self.0 } } + impl Device { pub fn of_node(&self) -> Option { Some(self.0.clone()) } } +} +mod float; +mod initdata; +mod g17 { + pub mod fw { pub(crate) use crate::initdata; } + mod calibration; + pub fn check_property_adapter() { + use crate::{driver::*, of::Node}; + let fields: &[(&str, &[u32])] = &[ + ("apple,t6040-handoff-version", &[2]), + ("apple,core-mask", &[0xfffff]), + ("apple,max-power-mw", &[70000]), + ("apple,perf-base-pstate", &[1]), + ("apple,perf-map-a", &[0,1,2,3,4,5,6,7,8,9,15,0,0,0,0,0]), + ("apple,perf-map-b", &[0,1,2,3,4,5,6,7,8,9,15,0,0,0,0,0]), + ("apple,perf-relative-a", &[0,10,20,30,40,50,60,70,80,90,100,0,0,0,0,0]), + ("apple,perf-relative-b", &[0,0,11,22,33,44,55,66,77,88,100,0,0,0,0,0]), + ("apple,core-leak-coef", &[0x44480000, 0x44800000]), + ("apple,sram-leak-coef", &[0x43000000, 0x43000000]), + ("apple,cs-leak-coef", &[0x43800000]), + ("apple,afr-leak-coef", &[0x44000000]), + ]; + let node = Node(fields.iter().map(|(k,v)| (k.to_string(), v.to_vec())).collect()); + let read = |n| calibration::Calibration::read(&AsahiDrmDevice(DrmDevice(Device(n)))); + let chip = read(node.clone()).unwrap(); + assert_eq!(chip.max_power_mw, 70000); + assert_eq!(chip.leakage[0], 0x44480000); + assert_eq!(chip.leakage[1], 0x44800000); + assert_eq!(chip.leakage[32..34], [0x43000000; 2]); + assert_eq!(chip.leakage[52], 0x43800000); + assert_eq!(chip.leakage[54], 0x44000000); + for (key, _) in fields { + let mut missing = node.clone(); missing.0.remove(*key); + assert!(read(missing).is_err(), "accepted missing {key}"); + let mut short = node.clone(); short.0.get_mut(*key).unwrap().pop(); + assert!(read(short).is_err(), "accepted short {key}"); + } + for key in ["apple,perf-map-a", "apple,perf-map-b", "apple,perf-relative-a", "apple,perf-relative-b"] { + for index in 11..16 { + let mut bad = node.clone(); bad.0.get_mut(key).unwrap()[index] = 1; + assert!(read(bad).is_err(), "accepted nonzero tail {key}[{index}]"); + } + } + for (key, value) in [ + ("apple,t6040-handoff-version", 1), + ("apple,core-mask", 0x100000), + ("apple,max-power-mw", 0), + ("apple,perf-base-pstate", 0), + ("apple,perf-base-pstate", 2), + ("apple,cs-leak-coef", 0x7fc00000), + ("apple,afr-leak-coef", 0xbf800000), + ] { + let mut bad = node.clone(); bad.0.insert(key.to_string(), vec![value]); + assert!(read(bad).is_err(), "accepted invalid {key}"); + } + } +} +''' + +TEST = r''' +#[test] +fn property_adapter_requires_complete_chip_data() { g17::check_property_adapter(); } + +#[test] +fn power_configuration_uses_only_its_inputs() { + use initdata::v25g83::PowerConfig25G83; + let mut first = PowerConfig25G83([0xcc; 0x6510]); + let mut leakage = [0; 56]; + leakage[0] = 0x44480000; + leakage[1] = 0x44800000; + leakage[32] = 0x43000000; + leakage[33] = 0x43000000; + leakage[52] = 0x43800000; + leakage[54] = 0x44000000; + first.init(0x12340000, 0x56780000, 0xabcdef, 76543, &leakage); + let word = |bytes: &[u8], off| u32::from_le_bytes(bytes[off..off + 4].try_into().unwrap()); + for off in [0xa04, 0xabc, 0x28c8, 0x28cc, 0x28d0, 0x2a08] { + assert_eq!(word(&first.0, off), 76543); + } + for off in [0x9e0, 0x2a04] { + assert_eq!(word(&first.0, off), (76543f32).to_bits()); + } + for (i, value) in leakage.iter().enumerate() { + assert_eq!(word(&first.0, 0x642c + 4 * i), *value); + } + assert_eq!(u64::from_le_bytes(first.0[0x3b50..0x3b58].try_into().unwrap()), 0x12340080); + assert_eq!(u64::from_le_bytes(first.0[0x6318..0x6320].try_into().unwrap()), 0x56780000); + assert_eq!(word(&first.0, 0x63d4), 0xabcdef); + let mut second = PowerConfig25G83([0; 0x6510]); + second.init(0x12340000, 0x56780000, 0xabcdef, 76543, &leakage); + assert_eq!(first.0, second.0); + second.init(0x12340000, 0x56780000, 0xabcdef, 80000, &[0; 56]); + assert_eq!(word(&second.0, 0xa04), 80000); + assert_eq!(word(&second.0, 0x9e0), (80000f32).to_bits()); + for i in 0..56 { + assert_eq!(word(&second.0, 0x642c + 4 * i), 0); + } +} +''' + + +def main(): + with tempfile.TemporaryDirectory(prefix="asahi-t6040-") as tmp: + root = Path(tmp) + (root / "initdata").mkdir() + (root / "g17").mkdir() + for source, target in [ + ("g17/fw/initdata.rs", "initdata.rs"), + ("g17/fw/initdata/v25g83.rs", "initdata/v25g83.rs"), + ]: + text = (DRIVER / source).read_text() + (root / target).write_text(text.replace("use kernel::prelude::*;", "use crate::prelude::*;")) + text = (DRIVER / "float.rs").read_text().split("impl<'a> TryFromchannel;', + 'return c==DCP_CONTEXT_CMD?&d->command:&d->channel;') + prefix = prefix.replace('((void)dev)', '((void)dcp->dev)') + prefix += r''' +#include +typedef u64 __le64;typedef u32 __le32; +#define __packed __attribute__((packed)) +typedef u64 dma_addr_t; +#define DCP_FW_NAME(n) n +#define DCP_FW_H17(v) 0 +#define DCP_FW_DESKTOP_26(v) 1 +#define SZ_16K 16384U +#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) +#define DCP_MAX_MAPPINGS 4 +#undef static_assert +#define static_assert(c) _Static_assert(c,#c) +#define trace_iomfb_callback(...) ((void)0) +#define dcp_trace_panel_property(...) ((void)0) +#define GFP_KERNEL 0 +#define kmalloc_objs(t,n,f) malloc(sizeof(t)*(n)) +#define dev_warn_dma(...) ((void)0) +struct dcp_map_physical_req {u64 paddr,size;u32 flags;u8 dva_null,dva_size_null,padding[2];} __attribute__((packed)); +struct dcp_map_physical_resp {u64 dva,dva_size;u32 mem_desc_id;} __attribute__((packed)); +struct dcp_map_buf_req {u64 buffer;}; +struct dcp_map_buf_resp {u64 buffer,dva;}; +struct dcp_unmap_buf_resp {u64 buffer,vaddr,dva;u8 unk,buf_null;}; +struct dcp_wait_cookie {unsigned status;bool replied;int done,refcount;}; +#define dev_warn_release(...) ((void)0) +#define dev_err_release(...) ((void)0) +#define DMA_BIDIRECTIONAL 0 +#define round_down(x,y) ((x) & ~((__typeof__(x))(y)-1)) +#define check_add_overflow(a,b,p) __builtin_add_overflow(a,b,p) +static unsigned physical_maps; +static unsigned find_first_zero_bit(unsigned long *map,unsigned count) +{for(unsigned i=0;isgl);} +static void dma_free_coherent(struct device *dev,size_t size,void *buf,u64 dva) +{assert(dev && size==SZ_16K && buf && dva==0x8000);frees++;} +static void dma_unmap_resource(struct device *dev,u64 dva,size_t size,int dir,int flags) +{assert(dev && dva==0x8000 && size==4096 && !dir && !flags);resource_unmaps++;} +static void clear_bit(unsigned b,unsigned long *p) {*p &= ~(1UL<sgl && flags==3);maps++;return SZ_16K;} +static size_t iommu_unmap(void *domain,u64 dva,size_t size) +{assert(domain && dva==0x8000 && size==SZ_16K);unmaps++;return size;} +''' + # dev logging in the DMA helper has no local 'dev', unlike the dispatcher. + unmap = block(template, 'static bool dcpep_unmap_piodma(') + unmap = unmap.replace('dev_warn(', 'dev_warn_dma(').replace('dev_err(', 'dev_warn_dma(') + handlers = block(desktop, 'static const iomfb_cb_handler cb_handlers[') + binding = '\n'.join(line for line in handlers.splitlines() if '[202]' in line or '[454]' in line) + mapping = block(template, 'static bool dcpep_map_piodma(').replace('dev_err(', 'dev_warn_dma(') + release = block(template, 'static u8 dcpep_cb_release_mem_desc(').replace('dev_warn(', 'dev_warn_release(').replace('dev_err(', 'dev_err_release(') + physical = block(template, 'static bool is_disp_register(') + '\n' + block(template, 'static struct dcp_map_physical_resp\ndcpep_cb_map_physical(').replace('dev_err(', 'dev_err_release(').replace('dev_warn(', 'dev_warn_release(') + physical += r""" +static bool trampoline_map_physical(struct apple_dcp *dcp,int tag,void *out,void *in) +{struct dcp_map_physical_resp r=dcpep_cb_map_physical(dcp,in);(void)tag;memcpy(out,&r,sizeof(r));return true;} +""" + # Assert the table still binds the shared production mapper. + assert '[452] = trampoline_map_physical,' in handlers + binding += '\n[452] = trampoline_map_physical,' + wire = block(desktop, 'struct dcp_unmap_buf_req_v26_6 {')+';\n' + production = (wire + '\n' + physical + '\n' + release + '\n' + block(desktop, 'static bool trampoline_release_mem_desc_v26_6(') + '\n' + mapping + '\n' + unmap + '\n' + block(desktop, 'static bool trampoline_unmap_piodma_v26_6(') + + '\n' + block(iomfb, 'static bool dcp_external_zero_reply(') + + '\n' + block(iomfb, 'static void dcpep_handle_cb(').replace('struct device *dev =', 'struct device *dev __attribute__((unused)) =') + + '\n' + block(iomfb, 'static void dcpep_handle_ack(') + + '\n' + block(template, 'static void complete_set_digital_out_mode(')) + return prefix + production + '\n' + r''' +int main(void) +{ + struct device_node node={0};struct device dev={&node}; + bool (*handlers[IOMFB_MAX_CB])(struct apple_dcp *,int,void *,void *)={ +''' + binding + r''' + }; + struct apple_dcp d={.dev=&dev,.fw_compat=DCP_FIRMWARE_V_26_6_DESKTOP, + .cb_handlers=handlers,.memdesc_map={2},.iommu_dom=&d}; + union {u64 align;u8 data[sizeof(struct dcp_packet_header)+24];} packet; + struct dcp_packet_header *h=(void *)packet.data; + struct {struct dcp_packet_header h;u32 input[2];u32 status;} command={ + .h={.in_len=8,.out_len=4},.status=0}; + struct dcp_wait_cookie wait={.status=~0U,.refcount=2}; + /* The firmware pauses an A413 modeset until its D202 is acknowledged. */ + __typeof__(*d.desktop) desktop={0};d.desktop=&desktop; + d.command.depth=1;d.command.header[0]=command.h; + d.command.callbacks[0]=complete_set_digital_out_mode; + d.command.cookies[0]=&wait;d.command.output[0]=&command.status; + d.memdesc[1]=(struct dcp_mem_descriptor){.buf=&d,.dva=0x8000,.size=4096,.map={.sgl=&d}}; + struct dcp_map_buf_req map_request={.buffer=1};struct dcp_map_buf_resp map_reply; + assert(dcpep_map_piodma(&d,&map_request,&map_reply)); + assert(maps==1 && map_reply.buffer==1 && map_reply.dva==0x8000); + memset(&packet,0,sizeof(packet));h->in_len=24; + struct dcp_unmap_buf_req_v26_6 fields={.buffer=1,.id=1,.dva=0x8000};memcpy(packet.data+sizeof(*h),&fields,24); + case_tag=202;acks=0; + dcpep_handle_cb(&d,DCP_CONTEXT_CB,h,sizeof(*h)+24,0); + assert(acks==1 && !d.crashed && !d.channel.depth && unmaps==1); + assert(!d.memdesc[1].piodma_mapped && d.command.depth==1 && !wait.done); + /* No firmware response can arrive until the empty callback ACK above. */ + dcpep_handle_ack(&d,DCP_CONTEXT_CMD); + assert(wait.done && wait.replied && !wait.status && wait.refcount==1 && !d.command.depth); + /* A later mode can reuse a descriptor after the firmware unmaps it. */ + assert(dcpep_map_piodma(&d,&map_request,&map_reply) && maps==2); + /* Descriptor release handles both RAM and mapped resources, once. */ + fields.buffer=1;fields.dva=0x8000;memcpy(packet.data+sizeof(*h),&fields,24); + dcpep_handle_cb(&d,DCP_CONTEXT_CB,h,sizeof(*h)+24,0); + assert(unmaps==2); + case_tag=454;h->in_len=4;h->out_len=4; + put_unaligned_le32(1,packet.data+sizeof(*h)); + memset(packet.data+sizeof(*h)+4,0xa5,4); + dcpep_handle_cb(&d,DCP_CONTEXT_CB,h,sizeof(*h)+8,0); + assert(get_unaligned_le32(packet.data+sizeof(*h)+4)==1); + assert(frees==1 && !d.memdesc_map[0] && !d.memdesc[1].buf && !d.memdesc[1].size); + dcpep_handle_cb(&d,DCP_CONTEXT_CB,h,sizeof(*h)+8,0); + assert(!get_unaligned_le32(packet.data+sizeof(*h)+4) && frees==1); + d.memdesc_map[0]=1; + struct resource reg={.start=0x123400,.end=0x1243ff};d.disp_registers[0]=®d.nr_disp_registers=1; + struct dcp_map_physical_req request={.paddr=0x123400,.size=4096}; + struct {struct dcp_packet_header h;struct dcp_map_physical_req in;struct dcp_map_physical_resp out;} __attribute__((packed)) phys={.h={.in_len=24,.out_len=20},.in=request}; + case_tag=452; + dcpep_handle_cb(&d,DCP_CONTEXT_CB,&phys,sizeof(phys),0); + assert(physical_maps==1 && phys.out.mem_desc_id==1 && phys.out.dva==0x8000 && phys.out.dva_size==4096); + assert(d.memdesc_map[0]==3 && d.memdesc[1].reg==0x123400); + case_tag=454;put_unaligned_le32(1,packet.data+sizeof(*h)); + dcpep_handle_cb(&d,DCP_CONTEXT_CB,h,sizeof(*h)+8,0); + assert(get_unaligned_le32(packet.data+sizeof(*h)+4)==1 && resource_unmaps==1 && d.memdesc_map[0]==1); + for(u32 id=4;id<8;id++) { + put_unaligned_le32(id,packet.data+sizeof(*h)); + dcpep_handle_cb(&d,DCP_CONTEXT_CB,h,sizeof(*h)+8,0); + assert(!get_unaligned_le32(packet.data+sizeof(*h)+4) && frees==1); + } + /* Out-of-range and overflowing physical regions receive no mapping. */ + case_tag=452;phys.in.paddr=0x124000; + dcpep_handle_cb(&d,DCP_CONTEXT_CB,&phys,sizeof(phys),0); + assert(!phys.out.dva_size && physical_maps==1); + phys.in.paddr=~0ULL;phys.in.size=8192; + dcpep_handle_cb(&d,DCP_CONTEXT_CB,&phys,sizeof(phys),0); + assert(!phys.out.dva_size && physical_maps==1); + unsigned before=acks; + assert(!iomfb_check_callback_v26_6_desktop(454,4,1)); + assert(!iomfb_check_callback_v26_6_desktop(452,23,20)); + /* The declared desktop shape is exact, with no guessed reply bytes. */ + assert(!iomfb_check_callback_v26_6_desktop(202,26,0)); + assert(!iomfb_check_callback_v26_6_desktop(202,24,4)); + case_tag=202;h->in_len=23;h->out_len=0; + dcpep_handle_cb(&d,DCP_CONTEXT_CB,h,sizeof(*h)+24,0); + assert(d.crashed && acks==before && unmaps==2); + return 0; +} +''' + + +def brightness_takeover(root): + import display + source = (root / 'drivers/gpu/drm/apple/dcp_backlight.c').read_text() + prefix = display.backlight(root).split('static void completed(')[0] + prefix = prefix.replace('static inline ', 'static inline __attribute__((unused)) ') + functions = '\n'.join(block(source, marker) for marker in ( + 'static bool dcp_backlight_desktop(', 'static int dcp_get_brightness(', + 'static void dcp_backlight_sync_hint(', + 'int dcp_backlight_register(', 'static int dcp_set_brightness(', + 'static bool dcp_backlight_controls_device(')) + return prefix + r''' +#include +#include +#define READ_ONCE(x) (x) +#define WRITE_ONCE(x,v) ((x)=(v)) +#define DCP_FIRMWARE_V_26_6_DESKTOP 5 +#define DCP_FIRMWARE_H17P 1 +#define DCP_IOMFB_METHODS_H17G 7 +#define MAX_BRIGHTNESS_PART2 510U +#define BACKLIGHT_PLATFORM 1 +#define BACKLIGHT_SCALE_LINEAR 1 +#define BL_CORE_SUSPENDED 1 +#define BL_CORE_FBBLANK 2 +#define devm_add_action_or_reset(...) (0) +#define DCP_BL_FADE_STEP_MS 16 +#define system_dfl_wq NULL +#define IS_ERR(p) false +#define PTR_ERR(p) (-EINVAL) +#define msecs_to_jiffies(n) (n) +#define spin_lock_irqsave(p,f) do {assert(!*(p));*(p)=true;(f)=0;} while(0) +#define spin_unlock_irqrestore(p,f) do {assert(*(p));*(p)=false;(void)(f);} while(0) +struct device {int unused;}; +struct backlight_properties {int type,brightness,max_brightness,scale,state;}; +struct backlight_device {struct backlight_properties props;void *data;bool ops_lock;}; +struct apple_dcp { + int fw_compat;bool quiescing; + struct {int iomfb_method_profile;} hw; + struct {u32 maximum,nits,dac;bool update;struct backlight_device *bl_dev;} brightness; + struct {bool lock;struct dcp_backlight_state state;unsigned long fade_start; + int fade_work;void (*kick)(struct apple_dcp *);} backlight; + struct {bool inherited_brightness_valid;u32 inherited_brightness;} *desktop; + struct {struct {struct {struct device *dev;} *dev;int mutex;} base;} *crtc; + struct device *dev; +}; +static unsigned long jiffies; +static bool inject_hint,inject_user; +static void mutex_lock(bool *p) +{ + assert(!*p);*p=true; + if(inject_user) { + inject_user=false; + struct backlight_device *bd=(void *)((char *)p - offsetof(struct backlight_device,ops_lock)); + struct apple_dcp *d=bd->data; + bd->props.brightness=450; + assert(!dcp_bl_request(&d->backlight.state,450,false,false)); + } +} +static void mutex_unlock(bool *p) {assert(*p);*p=false;} +static struct backlight_device registered; +static const int dcp_backlight_ops; +static void *bl_get_data(struct backlight_device *bd) {return bd->data;} +static bool iomfb_is_m4(struct apple_dcp *d) {(void)d;return false;} +bool dcp_backlight_active(struct apple_dcp *d) {(void)d;return false;} +static unsigned notify_count; +static void dcp_backlight_notify(struct apple_dcp *d) {(void)d;notify_count++;} +static void dcp_backlight_desktop_kick(struct apple_dcp *d) {(void)d;} +static void dcp_backlight_kick(struct apple_dcp *d) {(void)d;} +static unsigned fades; +static void queue_delayed_work(void *q,int *work,unsigned delay) +{assert(!q && delay==16);(void)work;fades++;} +static struct backlight_device *devm_backlight_device_register(struct device *dev, + const char *name,struct device *parent,void *data,const int *ops, + const struct backlight_properties *props) +{(void)dev;(void)name;(void)parent;assert(ops==&dcp_backlight_ops); + registered=(struct backlight_device){.props=*props,.data=data}; + if(inject_hint) {inject_hint=false;struct apple_dcp *d=data;assert(dcp_bl_seed(&d->backlight.state,180));} + return ®istered;} +static int backlight_get_brightness(struct backlight_device *bd) +{return bd->props.state?0:bd->props.brightness;} +static bool backlight_is_blank(struct backlight_device *bd) {return !!bd->props.state;} +static u32 calculate_dac(struct apple_dcp *d,int nits) {(void)d;(void)nits;assert(false);return 0;} +static int drm_modeset_lock_single_interruptible(int *p) {(void)p;assert(false);return 0;} +static void drm_modeset_unlock(int *p) {(void)p;assert(false);} +static int dcp_backlight_update(struct apple_dcp *d) {(void)d;assert(false);return 0;} +''' + functions + r''' +int main(void) +{ + struct apple_dcp d={.fw_compat=5,.brightness={.maximum=600}}; + __typeof__(*d.desktop) saved={0};d.desktop=&saved; + assert(!dcp_backlight_register(&d)); + assert(registered.props.brightness==300 && registered.props.max_brightness==600); + assert(dcp_get_brightness(®istered)==300); + assert(!dcp_backlight_controls_device(®istered,NULL)); + /* A save/restore client echoes the exposed value, not a new target. */ + assert(!dcp_set_brightness(®istered)); + assert(!d.backlight.state.controlled && !d.backlight.state.dirty); + registered.props.state=BL_CORE_SUSPENDED|BL_CORE_FBBLANK; + assert(!dcp_set_brightness(®istered)); + registered.props.state=0;assert(!dcp_set_brightness(®istered)); + assert(!d.backlight.state.controlled && !d.backlight.state.dirty); + assert(dcp_get_brightness(®istered)==300); + /* A different explicit target takes over the unknown inherited level. */ + registered.props.brightness=250;assert(!dcp_set_brightness(®istered)); + assert(d.backlight.state.controlled && d.backlight.state.fading && !d.backlight.state.dirty && fades==1); + assert(!dcp_bl_fade_step(&d.backlight.state,250,250)); + assert(dcp_backlight_controls_device(®istered,NULL)); + struct dcp_backlight_present present; + assert(!dcp_bl_prepare(&d.backlight.state,true,&present) && present.nits==250); + assert(dcp_get_brightness(®istered)==250); + assert(dcp_bl_complete(&d.backlight.state,present.sequence,true)); + assert(dcp_get_brightness(®istered)==250); + /* Even the former midpoint is an ordinary faded target after takeover. */ + registered.props.brightness=300;assert(!dcp_set_brightness(®istered)); + assert(d.backlight.state.fading && fades==2 && d.backlight.state.target==300); + /* A real loader hint seeds registration without needing a present. */ + d.brightness.bl_dev=NULL;d.backlight.state=(struct dcp_backlight_state){0}; + inject_hint=true; + assert(!dcp_backlight_register(&d) && registered.props.brightness==180); + assert(dcp_get_brightness(®istered)==180); + assert(dcp_backlight_controls_device(®istered,NULL)); + /* A user write racing the late-hint notification keeps its target. */ + inject_user=true;dcp_backlight_sync_hint(&d); + assert(d.backlight.state.controlled && d.backlight.state.target==450); + assert(registered.props.brightness==450 && !registered.ops_lock); + return 0; +} +''' + + +def modeset_unknown(root): + template = (root / 'drivers/gpu/drm/apple/iomfb_template.c').read_text() + unit = modeset_unmap(root) + helper = block(template, 'static bool dcp_modeset_callback_failed(') + # Keep the real modeset completion and its callback-error check. + helper += r''' +struct dcp_modeset_cookie {struct dcp_wait_cookie wait;bool vrr;}; +''' + block(template, 'static void dcp_modeset_actual_done(') + unit = unit.replace('int main(void)', helper + '\nint main(void)') + tests = r''' + /* A fresh channel has an A413 outstanding; inject a valid but unknown D. */ + d.crashed=false;desktop.modeset_active=true;desktop.modeset_callback_error=false; + struct dcp_modeset_cookie mode={.wait={.status=~0U,.refcount=2},.vrr=true}; + d.command.depth=1;d.command.header[0]=command.h; + d.command.callbacks[0]=dcp_modeset_actual_done; + d.command.cookies[0]=&mode;d.command.output[0]=&command.status; + case_tag=499;h->in_len=4;h->out_len=4; + memset(packet.data+sizeof(*h)+4,0xa5,4);before=acks; + dcpep_handle_cb(&d,DCP_CONTEXT_CB,h,sizeof(*h)+8,0); + assert(acks==before+1 && !d.crashed && !d.channel.depth); + assert(!get_unaligned_le32(packet.data+sizeof(*h)+4)); + assert(desktop.modeset_callback_error && d.command.depth==1 && !mode.wait.done); + /* Draining the real ACK fails the set, even if firmware reports success. */ + dcpep_handle_ack(&d,DCP_CONTEXT_CMD); + assert(mode.wait.done && !mode.wait.replied && mode.wait.status==~0U); + assert(mode.wait.refcount==1 && !d.command.depth); + /* The next fixed modeset is independent and completes normally. */ + desktop.modeset_active=false;desktop.modeset_callback_error=false; + mode=(struct dcp_modeset_cookie){.wait={.status=~0U,.refcount=2}}; + d.command.depth=1;d.command.header[0]=command.h; + d.command.callbacks[0]=dcp_modeset_actual_done; + d.command.cookies[0]=&mode;d.command.output[0]=&command.status; + dcpep_handle_ack(&d,DCP_CONTEXT_CMD); + assert(mode.wait.done && mode.wait.replied && !mode.wait.status); + assert(!d.crashed && !d.command.depth && !d.channel.depth); +''' + return unit.replace(' return 0;\n}', tests + ' return 0;\n}') + + +def panel_identity(root, output=None): + import struct + import rate + f = rate.parser_fixture(root) + attributes = {'MaxHorizontalImageSize': 0, 'MaxVerticalImageSize': 0, + 'ProductAttributes': {'ManufacturerID': 'TST', 'ProductID': 42, + 'ProductName': 'Synthetic Internal Panel', + 'SerialNumber': 0x12345678}} + raw = bytearray(struct.pack('has_panel;} +static void dcp_queue_hotplug(void *con) {assert(con);notifications++;} +''' + helpers + dictionary + '\nstatic const u8 attributes[]={'+','.join(map(str,raw))+'};\n' + 'static const u8 product[]={'+','.join(map(str,product))+'};\n' + 'static const u8 minimal[]={'+','.join(map(str,minimal))+'};\n' + r''' +int main(int argc, char **argv) +{ + struct dcp_parse_ctx ctx; + struct dcp_display_metadata metadata; + assert(!parse(attributes,sizeof(attributes),&ctx)); + assert(!parse_panel_metadata(&ctx,&metadata)); + assert(!strcmp(metadata.manufacturer,"TST") && metadata.product==42 && metadata.serial==0x12345678); + assert(!strcmp(metadata.name,"Synthetic Int")); + /* Optional color/date fields are absent; timings come from the catalog. */ + struct apple_dcp d={.fw_compat=5,.has_panel=true,.panel={345,223},.connector=&d}; + __typeof__(*d.desktop) desktop={0};d.desktop=&desktop; + struct dcp_display_mode modes[3]={0}; + for(unsigned i=0;i<3;i++)modes[i].mode=(struct drm_display_mode){.clock=i==0?1080005:i==1?540005:432000, + .hdisplay=3456,.hsync_start=3500,.hsync_end=3550,.htotal=4000, + .vdisplay=2160,.vsync_start=2165,.vsync_end=2170,.vtotal=2250, + .flags=DRM_MODE_FLAG_PHSYNC|DRM_MODE_FLAG_PVSYNC,.type=i==0?DRM_MODE_TYPE_PREFERRED:0}; + modes[0].vrr=true;modes[0].min_vrr=24U<<16;modes[0].max_vrr=120U<<16; + u64 generation=99; + desktop.panel_adt_identity=(struct dcp_display_metadata){.manufacturer="APP",.product=0x616}; + const struct drm_edid *fallback=dcp_panel_edid_snapshot(&d,modes,3,&generation); + assert(!IS_ERR(fallback) && drm_edid_valid(fallback)); + if(argc==2) { + char path[4096];snprintf(path,sizeof(path),"%s.adt-fallback",argv[1]); + FILE *file=fopen(path,"wb");assert(file);assert(fwrite(&fallback->raw,128,1,file)==1);fclose(file); + } + drm_edid_free(fallback); + bool (*handlers[600])(struct apple_dcp *,int,void *,void *)={ +''' + bindings + r''' + }; + for(unsigned form=0;form<3;form++) { + desktop=(__typeof__(desktop)){0};notifications=0; + struct dcp_dict_passthrough_req_v26_6 wire={0};u32 result=0; + strcpy((char *)wire.key,form?"ProductAttributes":"DisplayAttributes"); + const u8 *data=form==2?minimal:form?product:attributes; + size_t size=form==2?sizeof(minimal):form?sizeof(product):sizeof(attributes); + memcpy(wire.value,data,size); + unsigned tag=form?552:561; + assert(iomfb_check_callback_v26_6_desktop(tag,sizeof(wire),4)); + assert(iomfb_check_callback_v26_6_desktop(tag,sizeof(wire),1)); + assert(!iomfb_check_callback_v26_6_desktop(tag,sizeof(wire)-1,4)); + u8 short_reply[2]={0,0xa5}; + assert(handlers[tag](&d,tag,short_reply,&wire)); + assert(short_reply[0]==1 && short_reply[1]==0xa5); + assert(handlers[tag](&d,tag,&result,&wire) && result==1); + u8 service[sizeof(wire)+4]={0};memcpy(service+4,&wire,sizeof(wire)); + assert(iomfb_check_callback_v26_6_desktop(413,sizeof(service),4)); + assert(!iomfb_check_callback_v26_6_desktop(413,sizeof(service)-1,4)); + assert(handlers[413](&d,413,&result,service) && result==1); + assert(desktop.panel_metadata_valid && notifications==1); + assert(desktop.panel_metadata.serial==(form==2?0:metadata.serial)); + assert(!strcmp(desktop.panel_metadata.manufacturer,"TST")); + if(form==2) { + const struct drm_edid *minimal_edid=dcp_panel_edid_snapshot(&d,modes,3,&generation); + assert(!IS_ERR(minimal_edid) && drm_edid_valid(minimal_edid)); + const u8 *m=(const void *)&minimal_edid->raw; + assert(!memcmp(m+72+5,"TST 002a",8) && !memcmp(m+90+5,"unspecified",11)); + drm_edid_free(minimal_edid); + } + /* Null and unrelated setters preserve the existing ACK and identity. */ + wire.value_null=1;result=0; + assert(handlers[tag](&d,tag,&result,&wire) && result==1 && notifications==1); + wire.value_null=0;strcpy((char *)wire.key,"Unrelated");result=0; + assert(handlers[tag](&d,tag,&result,&wire) && result==1 && notifications==1); + } + desktop=(__typeof__(desktop)){0};notifications=0; + dcp_panel_metadata_replace(&d,&metadata); + assert(notifications==1 && desktop.panel_metadata_valid); + const struct drm_edid *edid=dcp_panel_edid_snapshot(&d,modes,3,&generation); + assert(!IS_ERR(edid) && drm_edid_valid(edid) && dcp_panel_metadata_current(&d,generation)); + const u8 *bytes=(const void *)&edid->raw; + assert(bytes[8]==0x52 && bytes[9]==0x74 && bytes[10]==42 && !bytes[11]); + assert(bytes[12]==0x78 && bytes[13]==0x56 && bytes[14]==0x34 && bytes[15]==0x12); + assert(bytes[18]==1 && bytes[19]==4 && bytes[21]==35 && bytes[22]==22 && bytes[23]==0xff); + assert(bytes[16]==0xff && bytes[17]==34); + assert(((unsigned)bytes[33]<<2 | ((bytes[26]>>2)&3))==320); + assert(((unsigned)bytes[34]<<2 | (bytes[26]&3))==337); + assert(((unsigned)bytes[66] | (unsigned)(bytes[68] >> 4) << 8)==345); + assert(((unsigned)bytes[67] | (unsigned)(bytes[68] & 15) << 8)==223); + assert(bytes[72+3]==EDID_DETAIL_MONITOR_NAME && bytes[90+3]==EDID_DETAIL_MONITOR_SERIAL); + assert(!memcmp(bytes+90+5,"12345678\n",9)); + if(argc==2) {FILE *file=fopen(argv[1],"wb");assert(file);assert(fwrite(bytes,128,1,file)==1);fclose(file);} + assert(((unsigned)bytes[54]|(unsigned)bytes[55]<<8)==54001); + assert(bytes[108+3]==EDID_DETAIL_MONITOR_RANGE && bytes[108+5]==24 && bytes[108+6]==120); + assert(bytes[108+4] & DRM_EDID_RANGE_OFFSET_MAX_HFREQ); + /* The encoded timing clock rounds to 10 kHz; the DCP catalog is unchanged. */ + assert(modes[1].mode.clock==540005 && modes[0].mode.clock==1080005); + /* A real EDID replaces the fallback; invalid replacement preserves it. */ + u8 real[128];memcpy(real,bytes,128);real[12]^=1;real[127]^=1; + dcp_panel_edid_receive(&d,real,sizeof(real)); + assert(desktop.panel_raw_edid_size==128); + u8 nested[148]={0};u32 word; + word=0xd3;memcpy(nested,&word,4);word=0x01000001;memcpy(nested+4,&word,4); + word=0x09000004;memcpy(nested+8,&word,4);memcpy(nested+12,"EDID",4); + word=0x0a000080;memcpy(nested+16,&word,4);memcpy(nested+20,real,128); + const u8 *nested_raw=NULL;size_t nested_size=0; + assert(!parse_panel_edid_data(nested,sizeof(nested),&nested_raw,&nested_size)); + assert(nested_size==128 && !memcmp(nested_raw,real,128)); + dcp_panel_edid_receive(&d,nested,sizeof(nested)); + generation=desktop.panel_metadata_generation; + const struct drm_edid *copied=dcp_panel_edid_snapshot(&d,modes,3,&generation); + assert(!IS_ERR(copied) && !memcmp(&copied->raw,real,128));drm_edid_free(copied); + unsigned previous=notifications; + real[127]^=1;dcp_panel_edid_receive(&d,real,sizeof(real)); + assert(notifications==previous); + /* A checksum-valid block with no serial must not reintroduce null strings. */ + memset(real+12,0,4);memset(real+95,' ',13);real[127]=0; + for(unsigned j=0;j<127;j++)real[127]-=real[j]; + assert(!dcp_panel_edid_has_identity(real)); + dcp_panel_edid_receive(&d,real,sizeof(real));assert(notifications==previous); + desktop.panel_raw_edid_size=0;notifications=1; + drm_edid_free(edid); + /* Repeated and partial dictionaries cannot trigger monitor/brightness restore loops. */ + for(unsigned repeat=0;repeat<32;repeat++) { + dcp_panel_metadata_replace(&d,NULL); + dcp_panel_metadata_replace(&d,&metadata); + assert(dcp_panel_metadata_current(&d,generation) && notifications==1); + } + struct dcp_display_metadata partial={.manufacturer="TST",.product=42}; + dcp_panel_metadata_replace(&d,&partial); + assert(dcp_panel_metadata_current(&d,generation) && notifications==1); + assert(desktop.panel_metadata.serial==metadata.serial); + metadata.serial++; + dcp_panel_metadata_replace(&d,&metadata); + assert(!dcp_panel_metadata_current(&d,generation) && notifications==2); + edid=dcp_panel_edid_snapshot(&d,modes,3,&generation); + assert(!IS_ERR(edid) && dcp_panel_metadata_current(&d,generation)); + drm_edid_free(edid); + /* Another firmware/profile cannot publish a desktop panel identity. */ + desktop=(__typeof__(desktop)){0};d.fw_compat=2;dcp_panel_metadata_replace(&d,&metadata); + assert(!desktop.panel_metadata_valid); + metadata.manufacturer[0]='?';assert(IS_ERR(dcp_panel_edid(&metadata,modes,3,345,223))); + return 0; +} +''' + + return (unit, ((str(output / "desktop-panel-identity.edid"),),)) if output else unit + + +def powerlog_framing(root): + import rate + import struct + f = rate.parser_fixture(root) + # Use only the existing integer/string parser; no captured device data. + raw = bytearray(struct.pack('service->channel==id);return e->service;} +static struct apple_epic_service *afk_service_get(struct apple_epic_service *s) {s->users++;return s;} +static void afk_service_put(struct apple_epic_service *s) {assert(s->users);s->users--;} +static void afk_recv_handle_compact_init(struct apple_dcp_afkep *e,u16 id,const void *p,size_t n) +{(void)e;(void)id;(void)p;(void)n;assert(false);} +static void afk_recv_handle_compact_call(struct apple_dcp_afkep *e,u16 id,const void *p,size_t n) +{(void)e;(void)id;(void)p;(void)n;assert(false);} +static void afk_recv_handle_compact_reply(struct apple_dcp_afkep *e,u16 id,u8 t,const void *p,size_t n) +{(void)e;(void)id;(void)t;(void)p;(void)n;assert(false);} +''' + functions = '\n'.join(block(source, name) for source, name in ( + (system, 'static int powerlog_report('), + (afk, 'static bool afk_compact_report_dictionary('), + (afk, 'static void afk_recv_compact_message('))) + return prefix + '\n' + functions + '\nstatic const u8 report[]={'+','.join(map(str,raw))+'};\n' + r''' +int main(void) +{ + struct apple_dcp d={.fw_compat=5}; + struct apple_dcp_afkep ep={.dcp=&d,.endpoint=0x20}; + struct apple_epic_service_ops ops={.report=powerlog_report}; + struct apple_epic_service service={.ep=&ep,.ops=&ops,.channel=3};ep.service=&service; + u8 wire[16+8+sizeof(report)]={0}; + struct epic_compact_sub_hdr *sub=(void *)(wire+8);sub->category=0;sub->type=0xc0; + memcpy(wire+16,report,sizeof(report)); + afk_recv_compact_message(&ep,3,wire,16+sizeof(report)); + assert(hints==1 && hint==123000 && !service.users); + /* Descriptor-wrapped reports keep their existing transport behavior. */ + memset(wire+16,0,8);memcpy(wire+24,report,sizeof(report)); + afk_recv_compact_message(&ep,3,wire,sizeof(wire)); + assert(hints==2 && hint==123000 && !service.users); + /* The old unconditional descriptor removal loses the serialization header. */ + struct dcp_parse_ctx ctx;assert(parse(report+8,sizeof(report)-8,&ctx)==-EINVAL); + /* Truncated data never creates a takeover hint. */ + memcpy(wire+16,report,8); + afk_recv_compact_message(&ep,3,wire,23); + assert(hints==2 && !service.users); + return 0; +} +''' + + +def keys_and_reports(root): + import display + prefix = display.backlight(root).split('static void completed(')[0] + prefix = prefix.replace('static inline ', 'static inline __attribute__((unused)) ') + return prefix + r''' +static void report(struct dcp_backlight_state *s,u32 nits) +{ + struct dcp_backlight_state before=*s; + assert(!dcp_bl_report(s,nits)); + assert(s->target==before.target && s->level==before.level && s->actual==before.actual); + assert(s->fade_from==before.fade_from && s->fading==before.fading); + assert(s->dirty==before.dirty && s->in_flight==before.in_flight && s->sequence==before.sequence); + assert(s->report_valid && s->reported==nits); +} +static void step(struct dcp_backlight_state *s,u32 elapsed,u32 *id,u32 *previous,bool up) +{ + struct dcp_backlight_swap p; + dcp_bl_fade_step(s,elapsed,250); + report(s,(*id & 1)?50:550); + if(!s->dirty)return; + assert(!dcp_bl_swap_prepare(s,&p,(*id)++,true)); + report(s,(*id & 1)?0:600); + if(*id & 1) { + assert(!dcp_bl_swap_finish(s,&p,p.id,true,true)); + report(s,250); + assert(dcp_bl_swap_finish(s,&p,p.id,false,true)); + } else { + assert(!dcp_bl_swap_finish(s,&p,p.id,false,true)); + report(s,160); + assert(dcp_bl_swap_finish(s,&p,p.id,true,true)); + } + assert(up?s->actual>=*previous:s->actual<=*previous); + *previous=s->actual; +} +int main(void) +{ + for(unsigned direction=0;direction<2;direction++) { + bool up=!direction;u32 initial=up?100:600,previous=initial,id=1; + struct dcp_backlight_state s; + assert(!dcp_bl_init(&s,600,false,0,true,300)); + s.fade=true;s.preserve_inherited=true; + assert(dcp_bl_report(&s,initial) && s.actual==initial && !s.dirty); + report(&s,up?550:50); /* Only the first hint can establish takeover. */ + for(unsigned key=1;key<=10;key++) { + u32 target=up?100+key*50:600-key*50; + assert(!dcp_bl_request(&s,target,false,false)); + report(&s,up?50:550); + /* Key repeat retargets before any fade can finish. */ + step(&s,16,&id,&previous,up);step(&s,32,&id,&previous,up); + assert(s.target==target); + } + u32 last=up?600:100; + for(u32 time=48;time<=256;time+=16)step(&s,time,&id,&previous,up); + assert(s.target==last && s.actual==last && s.level==last && !s.fading && !s.dirty); + for(unsigned late=0;late<64;late++)report(&s,late&1?0:600); + assert(s.target==last && s.actual==last && !s.dirty); + } + return 0; +} +''' + + +def identity_keys(root): + import rate, struct + f = rate.parser_fixture(root) + data = bytearray(struct.pack(' +#include +#include +#include +#include +#include +typedef uint8_t u8;typedef uint32_t u32; +#define U16_MAX UINT16_MAX +#define SZ_16M (16U<<20) +#define ALIGN(x,n) (((x)+(n)-1)&~((n)-1)) +#define ERR_PTR(x) ((void *)(intptr_t)(x)) +#define PTR_ERR(x) ((int)(intptr_t)(x)) +#define IS_ERR(x) ((uintptr_t)(x)>(uintptr_t)-4096) +static u32 get_unaligned_le32(const void *p) {u32 n;memcpy(&n,p,4);return n;} +''' + code+=block((base/'dcp-display-metadata.h').read_text(),'struct dcp_display_metadata {')+';\n' + code+=block(header,'struct dcp_panel_adt {')+';\n'+source + code+='static const u8 fixture[]={'+','.join(map(str,data))+'};\n' + return code+r''' +int main(void) +{ + struct dcp_panel_adt panel={.identity={.manufacturer="APP",.product=0x616}}; + assert(!dcp_panel_adt_parse(fixture,sizeof(fixture),&panel)); + assert(panel.found && panel.width_mm==345 && panel.height_mm==223); + assert(!strcmp(panel.identity.manufacturer,"TST") && panel.identity.product==42); + assert(panel.identity.serial && panel.identity.serial==dcp_adt_serial((const u8 *)"SYNTHETIC-PANEL",15)); + for(unsigned n=0;n +#include +#define READ_ONCE(x) (x) +#define WRITE_ONCE(x,v) ((x)=(v)) +#define DCP_FIRMWARE_V_26_6_DESKTOP 5 +#define spin_lock_irqsave(p,f) do {assert(!*(p));*(p)=true;(f)=0;} while(0) +#define spin_unlock_irqrestore(p,f) do {assert(*(p));*(p)=false;(void)(f);} while(0) +#define container_of(p,t,m) ((t *)((char *)(p)-offsetof(t,m))) +#define KOBJ_CHANGE 1 +#define system_dfl_wq NULL +#define msecs_to_jiffies(x) (x) +struct work_struct {int unused;}; +struct backlight_device {struct {int kobj;} dev;struct {u32 brightness;} props;}; +struct apple_dcp { + int fw_compat;bool quiescing,crashed; + struct {struct backlight_device *bl_dev;} brightness; + struct {bool lock;struct dcp_backlight_state state;struct work_struct notify_work;int fade_work,retry_work;} backlight; + struct {struct dcp_backlight_swap backlight;} *desktop; +}; +static unsigned events,brightness,actual,kicks,cancelled,delayed_cancelled; +static void cancel_delayed_work_sync(int *work) {(void)work;delayed_cancelled++;} +static void cancel_work_sync(struct work_struct *w) {(void)w;cancelled++;} +static void kobject_uevent_env(int *k,int action,char **env) +{(void)k;assert(action==1 && !strcmp(env[0],"SOURCE=unknown") && !env[1]);events++;} +static void sysfs_notify(int *k,void *dir,const char *name) +{(void)k;assert(!dir);if(!strcmp(name,"brightness"))brightness++;else {assert(!strcmp(name,"actual_brightness"));actual++;}} +static void dcp_backlight_notify_work(struct work_struct *); +static void schedule_work(struct work_struct *w) {dcp_backlight_notify_work(w);} +static void dcp_backlight_kick(struct apple_dcp *d) {assert(!d->backlight.lock);kicks++;} +static void mod_delayed_work(void *q,int *w,unsigned delay) {(void)q;(void)w;assert(delay);} +'''+functions+r''' +int main(void) +{ + struct backlight_device bd={.props={500}}; + struct apple_dcp d={.fw_compat=5,.brightness={&bd}}; + __typeof__(*d.desktop) desktop={0};d.desktop=&desktop; + assert(!dcp_bl_init(&d.backlight.state,600,true,100,false,0)); + assert(!dcp_bl_request(&d.backlight.state,500,false,false)); + u32 target=d.backlight.state.target; + /* Accepted/completed presents update userspace once, duplicate callbacks do not. */ + assert(!dcp_bl_swap_prepare(&d.backlight.state,&desktop.backlight,7,true)); + dcp_backlight_desktop_finish(&d,8,false,true);assert(!events); + dcp_backlight_desktop_finish(&d,7,true,true);assert(!events); + dcp_backlight_desktop_finish(&d,7,false,true);assert(events==1 && brightness==1 && actual==1); + dcp_backlight_desktop_finish(&d,7,false,true);assert(events==1); + assert(d.backlight.state.target==target && bd.props.brightness==500); + assert(!dcp_bl_request(&d.backlight.state,250,false,false)); + assert(!dcp_bl_swap_prepare(&d.backlight.state,&desktop.backlight,9,true)); + dcp_backlight_desktop_finish(&d,9,true,false);assert(events==1); + d.quiescing=true;dcp_backlight_notify(&d);assert(events==1); + d.quiescing=false;d.fw_compat=1;dcp_backlight_notify(&d);assert(events==1); + dcp_backlight_stop(&d);assert(!cancelled && delayed_cancelled==2); + d.fw_compat=5;dcp_backlight_notify_release(&d); + assert(!d.brightness.bl_dev && cancelled==1);dcp_backlight_notify(&d);assert(events==1); + dcp_backlight_stop(&d);assert(cancelled==2 && delayed_cancelled==4); + return 0; +} +''' + + +def identity_trace_budget(root): + source=(root/'drivers/gpu/drm/apple/iomfb.c').read_text() + code=r''' +#include +#include +#include +#include +#include +#include +#include +typedef uint32_t u32; +typedef struct {int value;} atomic_t; +static int atomic_read(const atomic_t *n) {return n->value;} +static bool atomic_add_unless(atomic_t *n,int add,int unless) +{if(n->value==unless)return false;n->value+=add;return true;} +#define READ_ONCE(x) (x) +#define min_t(t,a,b) ((t)(a)<(t)(b)?(t)(a):(t)(b)) +#define DCP_FIRMWARE_V_26_6_DESKTOP 5 +struct apple_dcp {int fw_compat;bool has_panel;void *dev;struct {atomic_t identity_log_count[4];} *desktop;}; +static bool kms_debug; +#define DRM_UT_KMS 4 +#define fallthrough __attribute__((fallthrough)) +typedef uint8_t u8; +static bool drm_debug_enabled(int category) {assert(category==DRM_UT_KMS);return kms_debug;} +static unsigned messages; +static char output[256]; +static bool dcp_has_panel(struct apple_dcp *d) {return d->has_panel;} +static void dev_info(void *dev,const char *fmt,...) +{ + (void)dev;va_list args;va_start(args,fmt);vsnprintf(output,sizeof(output),fmt,args);va_end(args);messages++; +} +typedef void (*dcp_identity_key_fn)(void *,const char *,u32,u32,u32,u32); +static int parse_identity_keys(const void *blob,size_t len,dcp_identity_key_fn emit,void *data) +{ + (void)blob;(void)len; + for(unsigned i=0;i<70;i++)emit(data,"Key\n",4,9,25,0); + return 0; +} +''' + code+=block(source,'struct dcp_identity_trace {')+';\n' + code+=block(source,'static void dcp_identity_key_log(') + code+=block(source,'void dcp_trace_panel_property(') + code+=block(source,'static void dcp_trace_panel_setter(') + return code+r''' +int main(void) +{ + struct apple_dcp d={.fw_compat=5,.has_panel=true}; + __typeof__(*d.desktop) desktop={0};d.desktop=&desktop; + const char secret[]="SYNTHETIC_VALUE_NOT_FOR_LOGS"; + dcp_trace_panel_property(&d,561,"Property",8,secret,sizeof(secret));assert(!messages); + dcp_trace_panel_setter(&d,563,NULL,76);assert(!messages); + kms_debug=true;d.fw_compat=1; + dcp_trace_panel_property(&d,561,"Property",8,secret,sizeof(secret));assert(!messages); + d.fw_compat=5;d.has_panel=false; + dcp_trace_panel_property(&d,561,"Property",8,secret,sizeof(secret));assert(!messages); + d.has_panel=true; + u8 setter[4168]={0};strcpy((char *)setter,"ScalarKey"); + memcpy(setter+64,secret,sizeof(secret));setter[72]=0; + dcp_trace_panel_setter(&d,563,setter,75);assert(!messages); + dcp_trace_panel_setter(&d,563,setter,76); + assert(strstr(output,"ScalarKey type=4 size=8") && !strstr(output,secret)); + dcp_trace_panel_setter(&d,565,setter,72);assert(strstr(output,"type=11")); + dcp_trace_panel_setter(&d,567,setter,128);assert(strstr(output,"type=9")); + setter[72]=1;dcp_trace_panel_setter(&d,563,setter,76);assert(strstr(output,"size=0")); + memset(setter,0,sizeof(setter));strcpy((char *)setter+4,"ServiceKey"); + dcp_trace_panel_setter(&d,414,setter,80);assert(strstr(output,"ServiceKey type=4")); + dcp_trace_panel_setter(&d,415,setter,76);assert(strstr(output,"ServiceKey type=11")); + dcp_trace_panel_setter(&d,102,setter,68);dcp_trace_panel_setter(&d,104,setter,68); + for(unsigned i=0;i<100;i++)dcp_trace_panel_property(&d,114,"Statistics",10,secret,sizeof(secret)); + assert(atomic_read(&desktop.identity_log_count[2])==128 && !atomic_read(&desktop.identity_log_count[0]) && !atomic_read(&desktop.identity_log_count[1])); + unsigned before=messages; + dcp_trace_panel_property(&d,552,"Panel",5,secret,sizeof(secret));assert(messages>before); + dcp_trace_panel_property(&d,561,"Panel",5,secret,sizeof(secret)); + assert(atomic_read(&desktop.identity_log_count[0]) && atomic_read(&desktop.identity_log_count[1])); + assert(strstr(output,"Key?") && !strstr(output,secret)); + for(unsigned i=0;i<100;i++) { + dcp_trace_panel_property(&d,552,"Panel",5,secret,sizeof(secret)); + dcp_trace_panel_property(&d,561,"Panel",5,secret,sizeof(secret)); + dcp_trace_panel_property(&d,0,"Chunks",6,secret,sizeof(secret)); + } + for(unsigned i=0;i<4;i++)assert(atomic_read(&desktop.identity_log_count[i])==128); + before=messages;dcp_trace_panel_property(&d,561,"Panel",5,NULL,0);assert(messages==before); + assert(messages<=520); + return 0; +} +''' diff --git a/tools/testing/selftests/drivers/gpu/apple-dcp/display.py b/tools/testing/selftests/drivers/gpu/apple-dcp/display.py new file mode 100644 index 0000000000000..83f29c1ad7be1 --- /dev/null +++ b/tools/testing/selftests/drivers/gpu/apple-dcp/display.py @@ -0,0 +1,383 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright 2026 Ryan Murray +"""Internal panel policy and real TimingElements parser, without hardware.""" +import copy +import struct +from common import block +import rate + + +def backlight(root): + header = (root / 'drivers/gpu/drm/apple/dcp_backlight.h').read_text() + header = '\n'.join(line for line in header.splitlines() + if not line.startswith('#include')) + header = header.replace('static inline ', 'static inline __attribute__((unused)) ') + return r''' +#include +#include +#include +#include +#include +typedef uint32_t u32; +typedef uint64_t u64; +typedef uint8_t u8; +#define min(a,b) ((a)<(b)?(a):(b)) +#define mul_u32_u32(a,b) ((u64)(a)*(b)) +#define div_u64(a,b) ((a)/(b)) +''' + header + r''' +static void completed(struct dcp_backlight_state *s, struct dcp_backlight_swap *p, + u32 id, bool early) +{ + u32 old=s->actual; + if(early) { + assert(!dcp_bl_swap_finish(s,p,id,false,true)); + assert(s->actual==old && s->in_flight); + assert(dcp_bl_swap_finish(s,p,id,true,true)); + } else { + assert(!dcp_bl_swap_finish(s,p,id,true,true)); + assert(s->actual==old && s->in_flight); + assert(dcp_bl_swap_finish(s,p,id,false,true)); + } + assert(!dcp_bl_swap_finish(s,p,id,false,true)); +} +int main(void) +{ + struct dcp_backlight_state s; + struct dcp_backlight_swap p={0}; + u32 id=1, nits; + assert(!dcp_bl_takeover_nits(600,600000,&nits) && nits==600); + /* A registration default must never change the loader's visible level. */ + assert(!dcp_bl_init(&s,600,false,0,true,300)); s.fade=true;s.preserve_inherited=true; + assert(!s.dirty && !s.level_known && dcp_bl_actual(&s)==300); + assert(!dcp_bl_request(&s,300,true,true)); + assert(!dcp_bl_request(&s,300,false,false)); + assert(!s.controlled && !s.dirty && dcp_bl_actual(&s)==300); + assert(dcp_bl_swap_prepare(&s,&p,id,true)==-EALREADY); + assert(!dcp_bl_request(&s,250,false,false)); + assert(s.fading && !s.dirty); + assert(dcp_bl_fade_step(&s,125,250) && s.level==275); + assert(!dcp_bl_fade_step(&s,250,250) && s.level==250); + assert(dcp_bl_swap_prepare(&s,&p,id,false)==-ENODATA); + assert(!dcp_bl_swap_prepare(&s,&p,id,true)); + completed(&s,&p,id++,true); + assert(s.actual==250 && s.level_known && dcp_bl_actual(&s)==250); + assert(!dcp_bl_seed(&s,300)); + /* Same state as a slider request or an ALS retarget through backlight. */ + assert(!dcp_bl_request(&s,600,false,false) && s.fading); + for(u32 t=16;t<250;t+=16) { + u32 before=s.level; + assert(dcp_bl_fade_step(&s,t,250)); + assert(s.level>=before && s.level<600); + if(t==16) { + assert(!dcp_bl_swap_prepare(&s,&p,id,true)); + assert(dcp_bl_swap_finish(&s,&p,id++,true,false)); + assert(s.actual==250 && s.dirty && dcp_bl_retry_delay(&s)==100); + } + } + assert(!dcp_bl_fade_step(&s,250,250)); + assert(!dcp_bl_swap_prepare(&s,&p,id,true)); + assert(!dcp_bl_swap_finish(&s,&p,id+100,false,true)); + completed(&s,&p,id++,false); assert(s.actual==600); + assert(dcp_bl_request(&s,601,false,false)==-ERANGE); + assert(!dcp_bl_request(&s,0,false,false)); + assert(dcp_bl_fade_step(&s,125,250) && s.level==300); + assert(!dcp_bl_request(&s,100,false,false) && s.fade_from==300); + assert(dcp_bl_fade_step(&s,125,250) && s.level==200); + assert(!dcp_bl_request(&s,100,true,false)); + assert(!s.fading && dcp_bl_effective(&s)==0); + assert(!dcp_bl_request(&s,450,true,false) && dcp_bl_effective(&s)==0); + assert(!dcp_bl_request(&s,450,false,false) && s.level==450); + assert(!dcp_bl_request(&s,0,false,true) && dcp_bl_effective(&s)==0); + assert(!dcp_bl_request(&s,450,false,false) && s.level==450); + assert(!dcp_bl_dpms(&s,false) && dcp_bl_effective(&s)==0); + assert(!dcp_bl_dpms(&s,true) && dcp_bl_effective(&s)==450); + assert(!dcp_bl_resend(&s)); + return 0; +} +''' + + +def modes(root): + f = rate.parser_fixture(root) + # Use the exact production parser and public serializer already used by rate. + parser = f['parser'] + prefix = (f['prefix'] + f['epoch'] + f['metadata'] + f['header']).replace('static inline ', 'static inline __attribute__((unused)) ') + baseline = copy.deepcopy(f['timing_fixture'][0]) + checks = [] + blobs = [] + + def case(elements, expected, ranges=None, desktop=True): + raw = bytearray(struct.pack('= 119.88: + check += f'assert(m[{j}].needs_timestamps); ' + if ranges: + for j, (lo, hi) in enumerate(ranges): + check += f'assert(m[{j}].vrr=={int(bool(lo))}); ' + if lo: + check += f'assert(m[{j}].min_vrr=={lo} && m[{j}].max_vrr=={hi}); ' + checks.append(check + 'free(m); }') + + supported = [] + # Synthetic rates exercise catalog handling, not a claim about a panel dump. + for i, hz in enumerate((120, 60, 59.94, 50, 48, 47.952)): + m = copy.deepcopy(baseline) + m['ID'] = 100+i + m['VerticalAttributes']['PreciseSyncRate'] = round(hz*65536) + supported.append(m) + case(supported, (120, 60, 59.94, 50, 48, 47.952), + [(24<<16, 120<<16)] + [(0,0)]*5) + for hz in (119.88, 120, 120.01): + m=copy.deepcopy(supported[0]); m['VerticalAttributes']['PreciseSyncRate']=round(hz*65536) + case([m], [hz], [(24<<16, round(hz*65536))]) + for lo, hi, valid in ((48<<16,120<<16,True), (0,120<<16,False), + (60<<16,0,False), (0,0,True), (120<<16,24<<16,False), + (24<<16,144<<16,False), (120<<16,120<<16,False)): + m=copy.deepcopy(supported[0]); m['MinimumVariableRefreshRate']=lo; m['MaximumVariableRefreshRate']=hi + case([m],[120],[(lo or 24<<16,hi or 120<<16) if valid else (0,0)]) + m=copy.deepcopy(supported[0]);m['MinimumVariableRefreshRate']=0;m['MaximumVariableRefreshRate']=0 + case([m],[120],[(0,0)],desktop=False) + for key in ('MinimumVariableRefreshRate', 'MaximumVariableRefreshRate'): + m=copy.deepcopy(supported[0]);m[key]=0 + case([m],[120],[(0,0)]) + m=copy.deepcopy(supported[0]);m['MinimumVariableRefreshRate']=24<<16 + m=f['ObjectPairs'](list(m.items())+[('MinimumVariableRefreshRate',48<<16)]) + # Check duplicates against the parser directly without dict indexing in case(). + raw=bytearray(struct.pack('','') + return r''' +#include +#include +#include +typedef uint32_t u32; +typedef uint64_t u64; +''' + h + r''' +int main(void) { + u32 lo=0,hi=0; + assert(dcp_refresh_range(120U<<16,0,0,true,&lo,&hi)); + assert(lo==24U<<16 && hi==120U<<16); + assert(!dcp_refresh_range(120U<<16,0,0,false,&lo,&hi)); + assert(!dcp_refresh_range(60U<<16,0,0,true,&lo,&hi)); + assert(!dcp_refresh_range(120U<<16,0,120U<<16,true,&lo,&hi)); + assert(!dcp_refresh_range(120U<<16,120U<<16,24U<<16,true,&lo,&hi)); + assert(!dcp_refresh_range(120U<<16,24U<<16,144U<<16,true,&lo,&hi)); + assert(dcp_desktop_present_deadline(true,true)==120); + assert(dcp_desktop_present_deadline(true,false)==0); + assert(dcp_desktop_present_deadline(false,true)==0); + return 0; +} +''' + + +def takeover(root): + prefix=backlight(root).split('static void completed(')[0] + prefix=prefix.replace('static inline ', 'static inline __attribute__((unused)) ') + function=block((root/'drivers/gpu/drm/apple/dcp_backlight.c').read_text(), + 'int dcp_backlight_takeover(') + return prefix + r''' +#define DCP_FIRMWARE_V_26_6_DESKTOP 5 +#define READ_ONCE(x) (x) +#define spin_lock_irqsave(p,f) do {assert(!*(p));*(p)=true;(f)=0;} while(0) +#define spin_unlock_irqrestore(p,f) do {assert(*(p));*(p)=false;(void)(f);} while(0) +struct apple_dcp { + int fw_compat; + struct {u32 maximum;void *bl_dev;} brightness; + struct {bool lock;struct dcp_backlight_state state;} backlight; + struct {bool inherited_brightness_valid;u32 inherited_brightness;} *desktop; +}; +static unsigned legacy,notifications; +static void dcp_backlight_sync_hint(struct apple_dcp *d) +{assert(d->brightness.bl_dev);notifications++;} +bool dcp_backlight_active(struct apple_dcp *d) {return d->fw_compat==1 && d->backlight.state.ready;} +static int iomfb_configure_backlight_h17p(struct apple_dcp *d,u32 max,bool valid,u32 nits,bool fallback,u32 guessed) +{assert(max==600 && valid && nits==100 && !fallback && !guessed);legacy++;return 0;} +''' + function + r''' +int main(void) { + struct apple_dcp d={.fw_compat=5,.brightness={600}}; + __typeof__(*d.desktop) saved={0};d.desktop=&saved; + assert(!dcp_backlight_takeover(&d,0)); + assert(saved.inherited_brightness_valid && !saved.inherited_brightness && !legacy); + assert(!dcp_backlight_takeover(&d,500000) && !saved.inherited_brightness); + saved.inherited_brightness_valid=false; + assert(dcp_backlight_takeover(&d,1300000)==-ERANGE && !saved.inherited_brightness_valid); + assert(!dcp_backlight_takeover(&d,700000) && saved.inherited_brightness==600); + assert(!dcp_bl_init(&d.backlight.state,600,false,0,true,300)); + d.brightness.bl_dev=&d;saved.inherited_brightness_valid=false; + assert(!dcp_backlight_takeover(&d,100000) && saved.inherited_brightness_valid); + assert(dcp_bl_actual(&d.backlight.state)==100 && !d.backlight.state.dirty && notifications==1); + assert(!dcp_backlight_takeover(&d,200000) && d.backlight.state.target==100 && notifications==1); + saved.inherited_brightness_valid=false; + assert(!dcp_bl_request(&d.backlight.state,250,false,false)); + assert(!dcp_backlight_takeover(&d,100000) && !saved.inherited_brightness_valid); + d.fw_compat=1;d.backlight.state.ready=false; + assert(!dcp_backlight_takeover(&d,100000) && legacy==1); + return 0; +} +''' + + +def watchdog(root): + source=(root/'drivers/gpu/drm/apple/dcp.c').read_text() + production='\n'.join(block(source, marker) for marker in ( + 'bool dcp_desktop_present_complete(', + 'VISIBLE_IF_KUNIT void dcp_swap_watchdog(', + 'void dcp_swap_watchdog_arm(', + 'void dcp_swap_watchdog_complete(')) + definitions='\n'.join(line for line in source.splitlines() + if line.startswith('#define DCP_SWAP_WATCHDOG_')) + return r''' +#include +#include +#include +#include +typedef uint32_t u32; +#define READ_ONCE(x) (x) +#define WRITE_ONCE(x,v) ((x)=(v)) +#define xchg(p,v) __atomic_exchange_n((p),(v),__ATOMIC_SEQ_CST) +#define VISIBLE_IF_KUNIT +#define DCP_FIRMWARE_V_26_6_DESKTOP 5 +#define dev_warn(...) ((void)0) +#define container_of(p,t,m) ((t *)((char *)(p)-offsetof(t,m))) +#define to_delayed_work(p) container_of(p,struct delayed_work,work) +#define msecs_to_jiffies(n) (n) +#define time_before(a,b) ((long)((a)-(b))<0) +#define system_dfl_wq NULL +static unsigned long jiffies; +struct work_struct {unsigned unused;}; +struct delayed_work {struct work_struct work;}; +struct apple_dcp { + int fw_compat;bool vrr_enabled,typec; + struct {u32 present_id;bool present_pending,vrr_failed;} *desktop; + struct {bool valid;} mode_state; + struct delayed_work swap_watchdog_wq; + unsigned long swap_watchdog_deadline; + unsigned swap_watchdog_retrains; + void *crtc,*connector,*dev; +}; +static unsigned scheduled,cancelled,vblanks,hotplugs,unarmed,rejected; +static bool allow_retire=true; +static bool iomfb_retire_dropped_present_h17p(struct apple_dcp *d) {return allow_retire;} +static void dcp_unarm_retained_framebuffers(struct apple_dcp *d,u32 id) +{assert(id==d->desktop->present_id);unarmed++;} +static void dcp_backlight_desktop_finish(struct apple_dcp *d,u32 id,bool reply,bool accepted) +{assert(id==d->desktop->present_id && reply && !accepted);rejected++;} +static void dcp_mode_invalidate(__typeof__(((struct apple_dcp *)0)->mode_state) *m) {m->valid=false;} +static void dcp_drm_crtc_vblank(void *c) {vblanks++;} +static void dcp_queue_hotplug(void *c) {hotplugs++;} +static bool dcp_is_typec_output(struct apple_dcp *d) {return d->typec;} +static void mod_delayed_work(void *q,struct delayed_work *w,unsigned long delay) +{assert(delay==1000);scheduled++;} +static void cancel_delayed_work(struct delayed_work *w) {cancelled++;} +''' + definitions + '\n' + production + r''' +int main(void) { + struct apple_dcp d={.fw_compat=5,.vrr_enabled=true,.mode_state={true},.connector=(void *)1}; + __typeof__(*d.desktop) state={.present_id=42,.present_pending=true};d.desktop=&state; + dcp_swap_watchdog_arm(&d);assert(scheduled==1 && d.swap_watchdog_deadline==1000); + jiffies=999;dcp_swap_watchdog(&d.swap_watchdog_wq.work); + assert(d.mode_state.valid && !hotplugs && !unarmed && state.present_pending); + assert(!dcp_desktop_present_complete(&d,41) && state.present_pending); + assert(dcp_desktop_present_complete(&d,42) && !state.present_pending); + assert(!dcp_desktop_present_complete(&d,42)); + dcp_swap_watchdog_complete(&d);assert(cancelled==1); + jiffies=1000;dcp_swap_watchdog(&d.swap_watchdog_wq.work);assert(!hotplugs); + /* Expiry owns this dropped swap, retains its buffers and triggers reset. */ + state.present_id=43;state.present_pending=true; + dcp_swap_watchdog(&d.swap_watchdog_wq.work); + assert(state.vrr_failed && !d.vrr_enabled && !state.present_pending); + assert(!d.mode_state.valid && hotplugs==1 && vblanks==1 && unarmed==1 && rejected==1); + assert(!dcp_desktop_present_complete(&d,43)); + /* A late completion cannot finish the new fixed-refresh present. */ + state.present_id=44;state.present_pending=true; + assert(!dcp_desktop_present_complete(&d,43) && state.present_pending); + assert(dcp_desktop_present_complete(&d,44)); + unsigned before=scheduled;dcp_swap_watchdog_arm(&d);assert(scheduled==before); + /* The established external watchdog remains armed on a Type-C output. */ + d.fw_compat=1;d.typec=true;dcp_swap_watchdog_arm(&d);assert(scheduled==before+1); + allow_retire=false;d.mode_state.valid=true; + dcp_swap_watchdog(&d.swap_watchdog_wq.work);assert(d.mode_state.valid && hotplugs==1); + return 0; +} +''' + + +def brightness_commit(root): + source=(root/'drivers/gpu/drm/apple/dcp_backlight.c').read_text() + production=block(source,'static int drm_crtc_set_brightness(') + return r''' +#include +#include +#include +#include +#define DRM_MODESET_ACQUIRE_INTERRUPTIBLE 0 +#define IS_ERR(p) false +#define PTR_ERR(p) (-EINVAL) +struct drm_crtc_state {bool active,color_mgmt_changed,planes_changed;void *gamma_lut;}; +struct drm_crtc {void *dev;int mutex;}; +struct drm_modeset_acquire_ctx {int unused;}; +struct drm_atomic_commit {struct drm_modeset_acquire_ctx *acquire_ctx;}; +struct apple_dcp {bool desktop,pending;struct {bool update;} brightness;struct {struct drm_crtc base;} *crtc;}; +static struct drm_atomic_commit commit; +static struct drm_crtc_state state; +static unsigned allocations,commits,planes; +static bool fail_alloc; +static bool dcp_backlight_desktop(struct apple_dcp *d) {return d->desktop;} +static bool dcp_backlight_pending(struct apple_dcp *d) {return d->pending;} +static struct drm_atomic_commit *drm_atomic_commit_alloc(void *dev) +{allocations++;return fail_alloc?NULL:&commit;} +static void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *c,int flags) {} +static int drm_modeset_lock(int *m,struct drm_modeset_acquire_ctx *c) {assert(!*m);*m=1;return 0;} +static struct drm_crtc_state *drm_atomic_get_crtc_state(struct drm_atomic_commit *a,struct drm_crtc *c) +{assert(c->mutex);return &state;} +static int drm_atomic_add_affected_planes(struct drm_atomic_commit *a,struct drm_crtc *c) +{assert(c->mutex);planes++;return 0;} +static int drm_atomic_commit(struct drm_atomic_commit *a) {commits++;return 0;} +static void drm_atomic_commit_clear(struct drm_atomic_commit *a) {assert(false);} +static int drm_modeset_backoff(struct drm_modeset_acquire_ctx *c) {assert(false);return 0;} +static void drm_atomic_commit_put(struct drm_atomic_commit *a) {} +static void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *c) {} +static void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *c) {} +''' + production + r''' +int main(void) { + struct apple_dcp d={.desktop=true,.pending=true,.brightness={true}}; + __typeof__(*d.crtc) crtc={0};d.crtc=&crtc; + state.active=true;state.gamma_lut=&d; + assert(!drm_crtc_set_brightness(&d)); + assert(commits==1 && planes==1 && state.planes_changed && !state.color_mgmt_changed); + crtc.base.mutex=0;state=(struct drm_crtc_state){.active=true};d.desktop=false; + assert(!drm_crtc_set_brightness(&d)); + assert(commits==2 && planes==2 && state.color_mgmt_changed); + crtc.base.mutex=0;state=(struct drm_crtc_state){.active=false};d.desktop=true; + assert(!drm_crtc_set_brightness(&d) && commits==2); + crtc.base.mutex=0;state.active=true;d.pending=false; + assert(!drm_crtc_set_brightness(&d) && commits==2); + fail_alloc=true;assert(drm_crtc_set_brightness(&d)==-ENOMEM && commits==2); + assert(allocations==5); + return 0; +} +''' diff --git a/tools/testing/selftests/drivers/gpu/apple-dcp/j616s.py b/tools/testing/selftests/drivers/gpu/apple-dcp/j616s.py new file mode 100644 index 0000000000000..45af01294a55e --- /dev/null +++ b/tools/testing/selftests/drivers/gpu/apple-dcp/j616s.py @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +"""Panel identity with absent ADT panel nodes and notch-hidden timings.""" +import re +from common import block +import desktop + + +def identity(root, output): + base = root / 'drivers/gpu/drm/apple' + unit = desktop.panel_identity(root).split('int main(int argc, char **argv)')[0] + unit = unit.replace('int fw_compat;bool has_panel;', + 'int fw_compat;unsigned notch_height;bool has_panel;') + unit += r''' +#define ALIGN(x,n) (((x)+(n)-1)&~((n)-1)) +#define SZ_16M (16U<<20) +static u32 get_unaligned_le32(const void *p) {u32 n;memcpy(&n,p,4);return n;} +''' + unit += block((base / 'dcp-panel-adt.h').read_text(), 'struct dcp_panel_adt {') + ';\n' + unit += '\n'.join(line for line in (base / 'dcp-panel-adt.c').read_text().splitlines() + if not line.startswith('#include')) + unit += (base / 'dcp-panel-test.h').read_text() + source = (base / 'dcp.c').read_text() + # Also exercise the pre-fix property read, so the regression fails on r5g. + if 'void dcp_panel_read_dimensions(' in source: + reader = block(source, 'void dcp_panel_read_dimensions(') + else: + start = source.index('\t\tof_property_read_u32(panel_np, "width-mm"') + end = source.index('\n\n', start) + reader = ('void dcp_panel_read_dimensions(struct apple_dcp *dcp, ' + 'struct device_node *panel_np) {\n' + 'const char height_prop[2][16] = {"adj-height-mm", "height-mm"};\n' + + source[start:end] + '\n}') + dts = (root / 'arch/arm64/boot/dts/apple/t6040-j616s.dts').read_text() + values = [] + for name in ('width-mm', 'height-mm', 'adj-height-mm'): + match = re.search(r'^\s*' + name + r'\s*=\s*<(\d+)>;', dts, re.M) + values.append(int(match[1]) if match else 0) + unit += r''' +struct device_node { u32 width, height, adjusted; }; +static int of_property_read_u32(struct device_node *np, const char *name, u32 *out) +{ + u32 value = !strcmp(name,"width-mm") ? np->width : + !strcmp(name,"height-mm") ? np->height : np->adjusted; + if (!value) return -EINVAL; + *out=value; + return 0; +} +''' + reader + '\n' + r''' +int main(int argc, char **argv) +{ + assert(argc==3); + struct apple_dcp d={.fw_compat=5,.has_panel=true}; + __typeof__(*d.desktop) state={0};d.desktop=&state; + struct device_node np={DTS_VALUES}; + d.notch_height=74; + if (!strcmp(argv[1],"dimensions")) { + dcp_panel_read_dimensions(&d,&np); + assert(d.panel.width_mm==345 && d.panel.height_mm==216); + /* A missing optional adjusted height must retain the full height. */ + np.adjusted=0;d.panel.height_mm=0; + dcp_panel_read_dimensions(&d,&np); + assert(d.panel.width_mm==345 && d.panel.height_mm==223); + d.notch_height=0;np.adjusted=216; + dcp_panel_read_dimensions(&d,&np); + assert(d.panel.height_mm==223); + return 0; + } + struct dcp_panel_adt panel={ + .identity={.manufacturer="APP",.product=0x616,.name="Apple Panel"}, + .width_mm=345,.height_mm=216, + }, before=panel; + assert(!dcp_panel_adt_parse(panel_test_adt,sizeof(panel_test_adt),&panel)); + assert(!panel.found && !memcmp(&panel,&before,sizeof(panel))); + for (unsigned n=0;nraw; + assert(!memcmp(raw+77,"Apple Panel",11)); + assert(!memcmp(raw+95,"unspecified",11)); + assert((raw[66] | (raw[68]>>4)<<8)==345); + assert((raw[67] | (raw[68]&15)<<8)==216); + FILE *file=fopen(argv[2],"wb");assert(file); + assert(fwrite(raw,128,1,file)==1);fclose(file); + drm_edid_free(edid); + /* Invalid geometry must still be refused rather than repaired. */ + mode.mode.vsync_end=mode.mode.vtotal+1; + assert(IS_ERR(dcp_panel_edid_snapshot(&d,&mode,1,&generation))); + return 0; +} +'''.replace('DTS_VALUES', ','.join(map(str, values))) + return unit, (("dimensions", str(output / 'j616s-panel.edid')), + ("identity", str(output / 'j616s-panel.edid'))) diff --git a/tools/testing/selftests/drivers/gpu/apple-dcp/lifecycle.py b/tools/testing/selftests/drivers/gpu/apple-dcp/lifecycle.py index 37d7784aee33d..f27c5b243d32f 100644 --- a/tools/testing/selftests/drivers/gpu/apple-dcp/lifecycle.py +++ b/tools/testing/selftests/drivers/gpu/apple-dcp/lifecycle.py @@ -192,8 +192,9 @@ def function(file, name): struct cd321x_status { u32 status, data_status, status_changed, data_status_changed, pwr_status; struct usb_pd_identity partner_identity; bool dp_sid_event,dp_sid_hpd_low,dp_sid_irq; - struct { u32 status_rx,configure; } dp_sid_status; }; -struct tps6598x { void *partner, *dev, *port, *psy; int lock; u32 data_status; enum usb_role *role_sw; }; + struct { u32 status_rx,configure,mode_data; } dp_sid_status; }; +struct tps6598x { void *partner, *dev, *port, *psy; int lock; u32 data_status; enum usb_role *role_sw; + void *partner_altmode_dp; }; struct cd321x { struct tps6598x tps; struct cd321x_status update_status; struct cd321x_pm_state pm; struct delayed_work update_work, resume_work; struct typec_mux_state state; bool state_valid, display_route_active, cur_partner_is_pd; @@ -236,6 +237,20 @@ def function(file, name): static void *typec_register_partner(void *p, struct typec_partner_desc *d) { (void)p; (void)d; return (void *)1; } static void typec_partner_set_identity(void *p) { (void)p; } +#ifndef USB_TYPEC_DP_SID +#define USB_TYPEC_DP_SID 0xff01 +#endif +#ifndef USB_TYPEC_DP_MODE +#define USB_TYPEC_DP_MODE 1 +#endif +struct typec_altmode_desc { unsigned short svid; unsigned char mode; u32 vdo; bool mode_selection; }; +/* The partner DP altmode the firmware entered: registered and dropped in pairs. */ +static int partner_altmodes; +static void *typec_partner_register_altmode(void *p, const struct typec_altmode_desc *d) +{ assert(p && d->svid == USB_TYPEC_DP_SID); partner_altmodes++; return &partner_altmodes; } +static void typec_altmode_update_active(void *a, bool active) { assert(a); (void)active; } +static void __attribute__((unused)) typec_unregister_altmode(void *a) +{ if (a == &partner_altmodes) { assert(partner_altmodes > 0); partner_altmodes--; } } #define typec_set_pwr_opmode(...) ((void)0) #define typec_set_pwr_role(...) ((void)0) #define typec_set_vconn_role(...) ((void)0) @@ -1886,14 +1901,17 @@ def dispatcher(root): typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; +#define READ_ONCE(x) (x) +#define WRITE_ONCE(x,v) ((x)=(v)) #define DCP_FIRMWARE_H17P 1 -#define DCP_FIRMWARE_V_26_6_DESKTOP 100 +#define DCP_FIRMWARE_V_26_6_DESKTOP 5 #define DCP_IOMFB_METHODS_H17P_EXTERNAL 2 #define DCP_IOMFB_METHODS_H17G 3 #define DCP_MAX_CALL_DEPTH 8 #define IOMFB_MAX_CB 600 #define DCP_PACKET_ALIGNMENT 64 #define ALIGN(n, a) (((n) + (a) - 1) & ~((a) - 1)) +#define dcp_trace_panel_setter(...) ((void)0) #define dev_err(...) ((void)dev) #define dev_info(...) ((void)dev) #define dev_warn(...) ((void)dev) @@ -1910,6 +1928,7 @@ def dispatcher(root): bool (**cb_handlers)(struct apple_dcp *, int, void *, void *); unsigned long unknown_callbacks[16]; int start_done; + struct {bool modeset_active,modeset_callback_error;} *desktop; struct dcp_channel channel; }; static bool machine; @@ -1917,9 +1936,6 @@ def dispatcher(root): static struct dcp_channel *dcp_get_channel(struct apple_dcp *d, enum dcp_context_id c) { (void)c; return &d->channel; } static int dcp_parse_tag(char *p) { (void)p; return case_tag; } -/* A desktop 26.6 callback is not under test here: no scenario selects that firmware. */ -static bool __attribute__((unused)) iomfb_check_callback_v26_6_desktop(int tag, u32 in_len, u32 out_len) -{ (void)tag; (void)in_len; (void)out_len; assert(!"desktop 26.6 callback in an H17P scenario"); return false; } static bool of_machine_is_compatible(const char *n) { (void)n; return machine; } static bool of_device_is_compatible(struct device_node *n, const char *s) { (void)s; return n->external; } @@ -1972,4 +1988,7 @@ def dispatcher(root): } """ - return prefix + source[start:end] + suffix + desktop = (root / "drivers/gpu/drm/apple/iomfb_v26_6_desktop.c").read_text() + checker = block(desktop, 'static bool iomfb_check_callback_exact_v26_6_desktop(') + alias = '\n#define iomfb_check_callback_v26_6_desktop iomfb_check_callback_exact_v26_6_desktop\n' + return prefix + checker + alias + source[start:end] + suffix diff --git a/tools/testing/selftests/drivers/gpu/apple-dcp/pm.py b/tools/testing/selftests/drivers/gpu/apple-dcp/pm.py index 1475451c1cef1..3c2f2b8df2bd5 100644 --- a/tools/testing/selftests/drivers/gpu/apple-dcp/pm.py +++ b/tools/testing/selftests/drivers/gpu/apple-dcp/pm.py @@ -513,6 +513,8 @@ def function(text, name): static struct fwnode_handle *fwnode_find_reference(struct fwnode_handle *f,const char *s,int n) { (void)f;(void)s;(void)n;connector.refs++;return &connector; } +static bool fwnode_property_present(struct fwnode_handle *f,const char *s) { (void)f;(void)s;return true; } +static struct fwnode_handle *fwnode_handle_get(struct fwnode_handle *f) { assert(!"connector is a displayport reference");return f; } static void __attribute__((unused)) fwnode_handle_put(struct fwnode_handle *f) { if(f){assert(f->refs>0);f->refs--;} } @@ -551,7 +553,7 @@ def function(text, name): int main(void) { (void)cd321x_pm_notifications; struct cd321x c; - struct cd321x_pm_state budget; + struct cd321x_pm_state budget={0}; cd321x_pm_init(&budget);assert(cd321x_pm_ready(&budget)); assert(cd321x_display_taken(-ENOSPC,true)&&!cd321x_display_taken(-EBUSY,true)&&!cd321x_display_taken(0,true)&&cd321x_display_taken(-EBUSY,false)&&!cd321x_display_taken(-ENOSPC,false)&&!cd321x_display_taken(0,false)); for(unsigned int i=0;i', '') + header = refresh + '\n' + header # Header helpers stay header-like: unused here, as in any includer. header = header.replace('static inline ', 'static inline __attribute__((unused)) ') edid_header = (root / 'include/drm/drm_edid.h').read_text() @@ -142,6 +144,15 @@ def wire(value, out): #define mul_u32_u32(a, b) ((u64)(u32)(a) * (u32)(b)) #define DIV_ROUND_CLOSEST_ULL(n, d) (((n) + (d) / 2) / (d)) #define DIV_ROUND_CLOSEST(n, d) (((n) + (d) / 2) / (d)) +#ifndef min +#define min(a,b) ((a)<(b)?(a):(b)) +#endif +#ifndef max +#define max(a,b) ((a)>(b)?(a):(b)) +#endif +#define max_t(t,a,b) max((t)(a),(t)(b)) +#define div_u64(n,d) ((n)/(d)) +#define DIV_ROUND_UP_ULL(n,d) (((u64)(n)+(d)-1)/(d)) #define trace_iomfb_color_mode(...) ((void)0) #define trace_iomfb_parse_mode_fail(...) ((void)0) #define trace_iomfb_parse_mode_success(...) ((void)0) @@ -360,7 +371,10 @@ def case(value, automatic=True, internal=False, external=True, admitted=True): callback_types = '\n'.join(block(dptx, marker)+' __packed;' for marker in ('struct dptxport_apcall_link_rate {', 'struct dptxport_apcall_set_active_lane_count {')) callback = block(dptx, 'static int dptxport_call_link_config(') dcp = (base/'dcp.c').read_text() - connection = '\n'.join(block(dcp, marker) for marker in ( + # The modelled DCP is the H17P external one, never a desktop 26.x processor. + connection = 'static bool dcp_sink_metadata(struct apple_dcp *d) { (void)d; return false; }\n' + \ + 'static void dcp_desktop_metadata_new_sink(struct apple_dcp *d) { (void)d; assert(0); }\n' + \ + '\n'.join(block(dcp, marker) for marker in ( 'void dcp_firmware_metadata_connection(', 'static void dcp_firmware_metadata_present_locked(', 'static void dcp_announce_kick(', 'void dcp_firmware_metadata_announced(', 'void dcp_firmware_metadata_hotplug(')) @@ -636,6 +650,12 @@ def publication(root): } static const struct drm_edid *dcp_firmware_edid(struct apple_dcp *d,u64 *generation) { (void)d; (void)generation; return ERR_PTR(-ENODATA); } static bool dcp_firmware_metadata_current(struct apple_dcp *d,u64 generation) { (void)d; (void)generation; return false; } +static const struct drm_edid *dcp_panel_edid_snapshot(struct apple_dcp *d,const struct dcp_display_mode *m,unsigned n,u64 *generation) { (void)d;(void)m;(void)n;(void)generation;assert(!"panel identity in external fixture");return ERR_PTR(-ENODATA); } +static bool dcp_panel_metadata_current(struct apple_dcp *d,u64 generation) { (void)d;(void)generation;assert(!"panel identity in external fixture");return false; } +static void dcp_panel_metadata_replace(struct apple_dcp *d,const struct dcp_display_metadata *m) { (void)d;(void)m;assert(!"panel identity in external receipt"); } +static void dcp_panel_edid_receive(struct apple_dcp *d,const void *p,size_t n) { (void)d;(void)p;(void)n;assert(!"panel EDID in external receipt"); } +#define dcp_trace_panel_property(...) ((void)0) + static struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev,const struct drm_display_mode *m) { assert(dev->mode_config.mutex);struct drm_display_mode *copy=malloc(sizeof(*copy));assert(copy);*copy=*m; if(invalidate_during_duplicate) { invalidate_during_duplicate=false;dcp_180hz_invalidate(probing); } @@ -679,6 +699,9 @@ def publication(root): #endif /* Nor the desktop 26.6 firmware. */ #define DCP_FIRMWARE_V_26_6_DESKTOP (DCP_FIRMWARE_H17P + 3) +#define DIV_ROUND_UP(n,d) (((n)+(d)-1)/(d)) +static void dcp_update_connector_dimensions(struct apple_dcp *d,struct drm_connector *c) +{ (void)c;assert(d->fw_compat != DCP_FIRMWARE_V_26_6_DESKTOP); } '''+firmware_lineage(root)+internal_inline(root,'dcp_fw_is_14_7')+r'''/* Off T6030 machines the direct crossbar output is not clocked separately. */ static int __attribute__((unused)) dcp_direct_crossbar_link(struct apple_dcp *d,bool up) { (void)d;(void)up;return 0; } ''' @@ -702,7 +725,7 @@ def publication(root): + mode_helpers) # The desktop 26.6 VRR filter is gone once that firmware takes adaptive sync. vrr_filter = block(iomfb,'static bool dcp_mode_vrr_supported(')+'\n' if 'static bool dcp_mode_vrr_supported(' in iomfb else '' - publication = block(iomfb,'static void dcp_clear_connector_edid(')+'\n'+block(iomfb,'static void dcp_consume_edid_invalidation(')+mode_helpers+vrr_filter+block(iomfb,'int dcp_get_modes(')+block(iomfb,re.search(r'^[^\n;(]*\blookup_mode\(',iomfb,re.M).start()) + publication = block(iomfb,'static void dcp_clear_connector_edid(')+'\n'+block(iomfb,'static void dcp_consume_edid_invalidation(')+mode_helpers+vrr_filter+block(iomfb,'int dcp_get_modes(')+block(iomfb,'bool lookup_mode(') # Drive malformed replacements through the same chunk/end ingress as firmware. # The dictionary cache is a boundary substitute and takes ownership of the blob. receipt = r''' diff --git a/tools/testing/selftests/drivers/gpu/apple-dcp/run.py b/tools/testing/selftests/drivers/gpu/apple-dcp/run.py index 54bcad84819d2..efc79d1345f7a 100644 --- a/tools/testing/selftests/drivers/gpu/apple-dcp/run.py +++ b/tools/testing/selftests/drivers/gpu/apple-dcp/run.py @@ -26,9 +26,12 @@ import transaction import accounting import idle +import desktop +import j616s import rate import boot import m4 +import display def parking_unit(root): @@ -40,7 +43,7 @@ def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--source', type=Path, default=Path(__file__).resolve().parents[6]) parser.add_argument('--output', type=Path, help='retain generated C, commands and results here') - parser.add_argument('--group', choices=('all', 'edid', 'rate', 'phy', 'lifecycle', 'boot', 'quiesce', 'm4'), default='all') + parser.add_argument('--group', choices=('all', 'edid', 'rate', 'phy', 'lifecycle', 'boot', 'quiesce', 'm4', 'display'), default='all') args = parser.parse_args() root = args.source.resolve() output = args.output or Path(tempfile.mkdtemp(prefix='apple-dcp-selftest-')) @@ -51,6 +54,23 @@ def main(): 'orientation-faults', 'backend-change', 'safe-first', 'failed-flip-return', 'live-park')) tests = [ + ('lifecycle', 'desktop-modeset-unmap', desktop.modeset_unmap, ((),), ()), + ('lifecycle', 'desktop-unknown-modeset-recovery', desktop.modeset_unknown, ((),), ()), + ('display', 'panel-backlight-ramp', display.backlight, ((),), ()), + ('display', 'panel-takeover-registration', desktop.brightness_takeover, ((),), ()), + ('display', 'panel-brightness-commit', display.brightness_commit, ((),), ()), + ('display', 'panel-brightness-takeover', display.takeover, ((),), ()), + ('display', 'panel-mode-catalog', display.modes, ((),), ()), + ('display', 'desktop-brightness-notifications', desktop.brightness_notifications, ((),), ()), + ('display', 'j616s-panel-identity', lambda r: j616s.identity(r, output), ((),), ('-Wno-unused-function', '-Wno-unused-const-variable')), + ('display', 'desktop-adt-identity', desktop.adt_identity, ((),), ()), + ('display', 'desktop-identity-trace-budget', desktop.identity_trace_budget, ((),), ()), + ('display', 'desktop-identity-keys', desktop.identity_keys, ((),), ('-Wno-unused-function',)), + ('display', 'desktop-panel-identity', lambda r: desktop.panel_identity(r, output), ((),), ()), + ('display', 'desktop-powerlog-framing', desktop.powerlog_framing, ((),), ()), + ('display', 'desktop-brightness-keys-reports', desktop.keys_and_reports, ((),), ()), + ('display', 'panel-refresh-range', display.refresh, ((),), ()), + ('display', 'panel-adaptive-watchdog', display.watchdog, ((),), ()), ('m4', 'm4-wire-golden', lambda r: m4.golden_check(r, output), (), ()), ('m4', 'm4-protocol', m4.protocol, ((),), ('-Wno-unused-function', '-Wno-unused-const-variable')), ('m4', 'm4-common-plane', m4.plane, ((),), ('-Wno-unused-function', '-Wno-unused-const-variable')), diff --git a/tools/testing/selftests/drivers/gpu/asahi-m4/README b/tools/testing/selftests/drivers/gpu/asahi-m4/README index 9f7859015a19e..c88abd122428e 100644 --- a/tools/testing/selftests/drivers/gpu/asahi-m4/README +++ b/tools/testing/selftests/drivers/gpu/asahi-m4/README @@ -39,6 +39,11 @@ queue/context/notifier records and TTBAT generation words. Comparisons include are compared against the actual kernel header by compiling complete C and Rust definitions and measuring sizes, alignments, member offsets and constants. Comments and documentation can change without invalidating that ABI check. +The current global-parameters reply has a documented additive capacity tail. +The frozen 600-byte prefix is compared unchanged; independent C assertions +require the full reply to be exactly 616 bytes, with two eight-byte fields at +600 and 608. Older Mesa headers can still match the shared prefix. Firmware +encoder golden data and its fixture manifest are never regenerated. No Mesa headers are vendored. Set ASAHI_M4_MESA=/path/to/mesa, or run `test-uapi.py --mesa /path/to/mesa`, for a compatibility check against the real userspace source; a default host run reports that Mesa comparison was omitted. diff --git a/tools/testing/selftests/drivers/gpu/asahi-m4/abi.py b/tools/testing/selftests/drivers/gpu/asahi-m4/abi.py index dd4391a686a14..38c6d7806eabd 100644 --- a/tools/testing/selftests/drivers/gpu/asahi-m4/abi.py +++ b/tools/testing/selftests/drivers/gpu/asahi-m4/abi.py @@ -61,12 +61,16 @@ def run_measurements(command, source, binary): def c_layout(header, bindings, output, tag): keys, _ = expressions(bindings) + capacity_tail = 'client_memory_limit;' in header.read_text() # Linux 7.2 folds the historical extension structs into the base payloads. # Compare the same wire prefixes and offsets while keeping the C oracle frozen. if 'struct drm_asahi_cmd_render_with_prior {' not in header.read_text(): translated = [] for key, expr in keys: kind, _, field = key.partition(':') + if capacity_tail and key == 'drm_asahi_params_global:size': + # Compare the frozen reply prefix; validate the entire known additive tail below. + expr = 'offsetof(struct drm_asahi_params_global, memory_limit)' if key == "drm_asahi_vm_create:pad": expr = "offsetof(struct drm_asahi_vm_create, flags)" base = None @@ -89,6 +93,14 @@ def c_layout(header, bindings, output, tag): source = output / (tag + '.c') lines = ['#include ', '#include ', '#include ' + json.dumps(str(header)), 'int main(void) {'] + if capacity_tail: + lines[3:3] = [ + '_Static_assert(sizeof(struct drm_asahi_params_global) == 616, "capacity reply size");', + '_Static_assert(offsetof(struct drm_asahi_params_global, memory_limit) == 600, "capacity prefix");', + '_Static_assert(offsetof(struct drm_asahi_params_global, client_memory_limit) == 608, "client capacity offset");', + '_Static_assert(sizeof(((struct drm_asahi_params_global *)0)->memory_limit) == 8, "capacity width");', + '_Static_assert(sizeof(((struct drm_asahi_params_global *)0)->client_memory_limit) == 8, "client capacity width");', + ] lines += [f'printf("{key}=%llu\\n", (unsigned long long)({expr}));' for key, expr in keys] source.write_text('\n'.join(lines + ['return 0;', '}']) + '\n') return run_measurements(shlex.split(os.environ.get('CC', 'cc')) + diff --git a/tools/testing/selftests/drivers/gpu/asahi-m4/check-channels.rs b/tools/testing/selftests/drivers/gpu/asahi-m4/check-channels.rs index 52461a122b89d..925fb72a49931 100644 --- a/tools/testing/selftests/drivers/gpu/asahi-m4/check-channels.rs +++ b/tools/testing/selftests/drivers/gpu/asahi-m4/check-channels.rs @@ -4,6 +4,9 @@ mod channels; #[path = "@KERNEL@/drivers/gpu/drm/asahi/g16/fw/graph.rs"] mod graph; +mod fwid { +@FWID_BUILD@ +} mod hw {pub enum GpuVariant {G}} struct Config { chip_id:u32,gpu_variant:hw::GpuVariant,gpu_revision:u32,uat_ias:u32,uat_oas:u32, diff --git a/tools/testing/selftests/drivers/gpu/asahi-m4/run-golden-tests.py b/tools/testing/selftests/drivers/gpu/asahi-m4/run-golden-tests.py index 7d3a06dac82c0..962713941338b 100644 --- a/tools/testing/selftests/drivers/gpu/asahi-m4/run-golden-tests.py +++ b/tools/testing/selftests/drivers/gpu/asahi-m4/run-golden-tests.py @@ -15,10 +15,14 @@ def main(): manifest = unpack_fixtures(golden) (out / 'public-uapi.rs').write_bytes(frozen_bindings().read_bytes()) backend = root / 'drivers/gpu/drm/asahi/g16' + identity = (root / 'drivers/gpu/drm/asahi/fwid.rs').read_text() + first = identity.index('pub(crate) struct FirmwareBuild {') + build_type = identity[first:identity.index('\n}', first) + 2] logs = [] for name in ['initdata', 'compute', 'render', 'channels', 'initdata-bounds']: source = (HERE / f'check-{name}.rs').read_text() source = source.replace('@KERNEL@', str(root)).replace('@OUTPUT@', str(out)) + source = source.replace('@FWID_BUILD@', build_type) test = out / f'check-{name}.rs' test.write_text(source) binary = out / f'check-{name}' diff --git a/tools/testing/selftests/drivers/gpu/asahi-m4/test-mmu-retention.py b/tools/testing/selftests/drivers/gpu/asahi-m4/test-mmu-retention.py index 03184a58211cf..787992e9b4c24 100755 --- a/tools/testing/selftests/drivers/gpu/asahi-m4/test-mmu-retention.py +++ b/tools/testing/selftests/drivers/gpu/asahi-m4/test-mmu-retention.py @@ -19,7 +19,7 @@ def body(source,marker): mm=(ROOT/'drivers/gpu/drm/asahi/mmu.rs').read_text();pt=(ROOT/'drivers/gpu/drm/asahi/pgtable.rs').read_text() pre=r''' #![allow(dead_code,unused_variables)] -use std::{cell::RefCell,rc::Rc,mem::ManuallyDrop,ops::{Deref,DerefMut,Range},num::NonZeroUsize,sync::{Mutex as StdMutex,atomic::{AtomicBool as StdAtomicBool,AtomicU8,AtomicU32,AtomicU64,AtomicUsize,Ordering,fence}}}; +use std::{cell::RefCell,rc::Rc,mem::{ManuallyDrop,size_of},ops::{Deref,DerefMut,Range},num::NonZeroUsize,sync::{Mutex as StdMutex,atomic::{AtomicBool as StdAtomicBool,AtomicU8,AtomicU32,AtomicU64,AtomicUsize,Ordering,fence}}}; type Result = std::result::Result;const EIO:i32=5;const ETIMEDOUT:i32=110;const EOVERFLOW:i32=75; static TRIP_LOAD:AtomicUsize=AtomicUsize::new(0); struct AtomicBool(StdAtomicBool);impl AtomicBool{fn new(v:bool)->Self{Self(StdAtomicBool::new(v))}fn store(&self,v:bool,o:Ordering){self.0.store(v,o);}fn load(&self,o:Ordering)->bool{let n=TRIP_LOAD.load(Ordering::Relaxed);if n!=0&&TRIP_LOAD.fetch_sub(1,Ordering::Relaxed)==1{self.0.store(true,Ordering::Release);}self.0.load(o)}} @@ -29,6 +29,7 @@ def body(source,marker): struct MutexBackend;struct Mutex(StdMutex);implMutex{fn new(v:T)->Self{Self(StdMutex::new(v))}fn lock(&self)->Guard<'_,T,MutexBackend>{Guard(self.0.lock().unwrap(),std::marker::PhantomData)}} struct Guard<'a,T,M>(std::sync::MutexGuard<'a,T>,std::marker::PhantomData);implDeref for Guard<'_,T,M>{type Target=T;fn deref(&self)->&T{&self.0}}implDerefMut for Guard<'_,T,M>{fn deref_mut(&mut self)->&mut T{&mut self.0}} struct Monotonic;struct Delta;impl Delta{fn from_millis(n:u64)->u64{n}}struct Instant{n:std::cell::Cell,p:std::marker::PhantomData}implInstant{fn now()->Self{Self{n:std::cell::Cell::new(0),p:std::marker::PhantomData}}fn elapsed(&self)->u64{let n=self.n.get();self.n.set(n+1);n}} +mod pgtable{pub fn clean_poc(_p:*const u8,_n:usize){super::BARRIERS.fetch_add(1,super::Ordering::Relaxed);}pub fn poc_barrier(){super::BARRIERS.fetch_add(1,super::Ordering::Relaxed);}} mod mem{pub fn sync(){super::BARRIERS.fetch_add(1,super::Ordering::Relaxed);}pub fn tlbi_asid(_:u8){}} #[derive(Clone,Copy,PartialEq,Eq)]enum UatFirmware{Handoff,PublishedRoots} struct Handoff{lock_ap:AtomicU8,lock_fw:AtomicU8,turn:AtomicU32,cur_slot:AtomicU32}impl Handoff{fn new()->Self{Self{lock_ap:AtomicU8::new(0),lock_fw:AtomicU8::new(0),turn:AtomicU32::new(0),cur_slot:AtomicU32::new(0xffff)}} @@ -36,17 +37,19 @@ def body(source,marker): handoff='\n'.join(body(mm,m) for m in ['fn lock(&self) {\n self.lock_ap', 'fn unlock(&self) {\n self.turn', 'fn current_slot(']) mid=r''' } -struct SlotTTBS{ttb0:AtomicU64,ttb1:AtomicU64}struct UatShared{handoff:Handoff,ttbs:[SlotTTBS;64],map_kernel_to_user:bool,kernel_ttb1:u64,binding_generations:[u64;64],binding_published:[bool;64],owned_user_roots:[u64;64]} +struct Quirks{handoff_lock:bool,clean_tables:bool} +struct SlotTTBS{ttb0:AtomicU64,ttb1:AtomicU64}struct UatShared{handoff:Handoff,ttbs:[SlotTTBS;64],map_kernel_to_user:bool,kernel_ttb1:u64,binding_generations:[u64;64],binding_published:[bool;64],owned_user_roots:[u64;64],quirks:Quirks} impl UatShared{fn handoff(&self)->&Handoff{&self.handoff}fn ttbs(&self)->&[SlotTTBS;64]{&self.ttbs}} struct UatInner{firmware:UatFirmware,m4_adopted:bool,failed:AtomicBool,shared:Mutex} ''' +mid=mid.replace('impl UatShared{','impl UatShared{'+''.join(body(mm,m) for m in ['fn handoff_lock(', 'fn handoff_unlock(', 'fn publish_slot('])) raii='#[path="'+str(ROOT/'drivers/gpu/drm/asahi/mmu/ppl.rs')+'"]mod ppl;\n'+'\n'.join(body(mm,m) for m in [ 'struct M4Transport<', 'impl ppl::Memory for M4Transport', 'enum HandoffGuard<', 'impl HandoffGuard<', 'impl Drop for HandoffGuard<' ]) inner='impl UatInner{\n'+'\n'.join(body(mm,m) for m in ['fn lock(&self) -> Guard', 'fn check_m4(', 'fn lock_handoff(', 'fn lock_m4_upper('])+'\n}\n' models=r''' -fn uat(m4:bool,firmware:UatFirmware)->Rc{Rc::new(UatInner{firmware,m4_adopted:m4,failed:AtomicBool::new(false),shared:Mutex::new(UatShared{handoff:Handoff::new(),ttbs:std::array::from_fn(|_|SlotTTBS{ttb0:AtomicU64::new(0),ttb1:AtomicU64::new(0)}),map_kernel_to_user:!m4,kernel_ttb1:0x800000000,binding_generations:[0;64],binding_published:[false;64],owned_user_roots:[0;64]})})} +fn uat(m4:bool,firmware:UatFirmware)->Rc{Rc::new(UatInner{firmware,m4_adopted:m4,failed:AtomicBool::new(false),shared:Mutex::new(UatShared{handoff:Handoff::new(),ttbs:std::array::from_fn(|_|SlotTTBS{ttb0:AtomicU64::new(0),ttb1:AtomicU64::new(0)}),map_kernel_to_user:!m4,kernel_ttb1:0x800000000,binding_generations:[0;64],binding_published:[false;64],owned_user_roots:[0;64],quirks:Quirks{handoff_lock:true,clean_tables:false}})})} #[derive(Clone,Copy)]struct Prot{cached:bool}impl Prot{fn is_cached_noncoherent(&self)->bool{self.cached}} struct Mutation;impl Drop for Mutation{fn drop(&mut self){MUTATIONS.fetch_sub(1,Ordering::Relaxed);}} struct MapTable{fail:bool}impl MapTable{fn unmap_pages(&mut self,_r:Range)->Result{LEAF_CLEARS.fetch_add(1,Ordering::Relaxed);if self.fail{Err(EIO)}else{Ok(())}}} @@ -72,7 +75,7 @@ def body(source,marker): #[derive(Clone)]struct Vm{id:u64,binding:Rc>} struct VmBind(Vm,u32); struct Uat{inner:Rc,slots:Allocator} -mod module_parameters{pub struct Param;impl Param{pub fn value(&self)->&u32{&0}}pub static robust_isolation:Param=Param;} +mod module_parameters{pub struct Param;impl Param{pub fn value(&self)->u32{0}}pub static robust_isolation:Param=Param;} ''' helpers='\n'.join(body(mm,m) for m in ['const fn binding_wire_generation(', 'const fn application_ttb1(']) bind='impl Uat{'+body(mm,'pub(crate) fn bind(&self, vm: &Vm)')+'}\n' diff --git a/tools/testing/selftests/drivers/gpu/asahi-m4/test-timestamps.py b/tools/testing/selftests/drivers/gpu/asahi-m4/test-timestamps.py index 842ca85c090fb..9a159e849e86b 100755 --- a/tools/testing/selftests/drivers/gpu/asahi-m4/test-timestamps.py +++ b/tools/testing/selftests/drivers/gpu/asahi-m4/test-timestamps.py @@ -24,6 +24,7 @@ def function(source, marker): poll = (backend / 'g16/poll.rs').read_text() cache_policy = function((backend / 'gem.rs').read_text(), 'pub(crate) fn is_writeback(') store = function(mmu, 'pub(crate) fn write(&self, value: u64)') +vmap_store = function(mmu, 'pub(crate) fn write(&self, offset: usize, value: u64)') write = function(shared, 'fn write(&self, ticks: u64, frequency_hz: u64)') checked_offset = function(shared, 'fn checked_offset(') publish = function(shared, 'pub(crate) fn publish(&self, ticks: [u64; 4], frequency_hz: u64)') @@ -50,7 +51,12 @@ def function(source, marker): unsafe impl Send for Map {} unsafe impl Sync for Map {} struct View(*mut u8);impl View{fn as_mut_ptr(&self)->*mut u8{self.0}} impl Map{fn get(&self)->View{View(self.backing.words.as_ptr().cast_mut().cast())}fn owner(&self)->&Backing{&self.backing}} -struct TimestampMapping{mapping:Map,offset:usize} +struct TimestampVmap(Map); +impl TimestampVmap { + fn size(&self)->usize{core::mem::size_of_val(&self.0.backing.words)} + __VMAP_STORE__ +} +struct TimestampMapping{mapping:TimestampVmap,offset:usize} impl TimestampMapping { __STORE__ } __CHECKED_OFFSET__ struct Destination{cpu:TimestampMapping} @@ -63,7 +69,7 @@ def function(source, marker): fn backing()->Arc{backing_with_flags(0)} fn backing_with_flags(flags:u32)->Arc{Arc::new(Backing{flags,words:[const{AtomicU64::new(u64::MAX)};4],timestamp_lock:std::sync::Mutex::new(())})} fn destination(memory:&Arc,offset:usize)->Destination{ - Destination{cpu:TimestampMapping{mapping:Map{backing:memory.clone(),addr:memory.words.as_ptr().cast_mut().cast()},offset}} + Destination{cpu:TimestampMapping{mapping:TimestampVmap(Map{backing:memory.clone(),addr:memory.words.as_ptr().cast_mut().cast()}),offset}} } fn outputs(memory:&Arc)->Destinations{ Destinations(core::array::from_fn(|i|Some(destination(memory,i*8)))) @@ -191,7 +197,7 @@ def function(source, marker): assert_eq!(firmware().publish_timestamps(&flight,&claims),Err(EINVAL)); assert_eq!(memory.words[0].load(Ordering::Relaxed),u64::MAX); } -'''.replace('__CHECKED_OFFSET__', checked_offset).replace('__STORE__', store).replace('__CACHE_POLICY__', cache_policy).replace('__WRITE__', write).replace('__PUBLISH__', publish).replace('__RETIRE__', retire.replace('fn publish_timestamps', 'pub(crate) fn publish_timestamps')) +'''.replace('__CHECKED_OFFSET__', checked_offset).replace('__STORE__', store).replace('__VMAP_STORE__', vmap_store.replace('.timestamp_lock.lock()', '.timestamp_lock.lock().unwrap()')).replace('__CACHE_POLICY__', cache_policy).replace('__WRITE__', write).replace('__PUBLISH__', publish).replace('__RETIRE__', retire.replace('fn publish_timestamps', 'pub(crate) fn publish_timestamps')) path = out / 'timestamps-host.rs' path.write_text(source) subprocess.run(rustc() + ['--edition=2021', '--test', str(path), '-o', str(out / 'timestamps-host')], check=True) diff --git a/tools/testing/selftests/drivers/gpu/asahi-m4/test-uapi.py b/tools/testing/selftests/drivers/gpu/asahi-m4/test-uapi.py index 39d2500f60b19..a6c936f3c0b4a 100755 --- a/tools/testing/selftests/drivers/gpu/asahi-m4/test-uapi.py +++ b/tools/testing/selftests/drivers/gpu/asahi-m4/test-uapi.py @@ -27,7 +27,7 @@ def main(): if args.mesa: mesa = args.mesa.resolve() / 'include/drm-uapi/asahi_drm.h' errors += differences(kernel, c_layout(mesa, bindings, out, 'mesa-layout')) - report = {'passed': not errors, 'scope': 'Compiled public UAPI structures, field offsets and constants; no kernel parser or feature-semantic coverage', + report = {'passed': not errors, 'scope': 'Compiled public UAPI structures, field offsets and constants; frozen params prefix plus exact known capacity-tail assertions; no parser or feature-semantic coverage', 'kernel': str(header), 'bindings': str(bindings), 'comparisons': len(kernel), 'mesa': str(mesa) if mesa else None, 'mesa_comparison': 'performed' if mesa else 'not requested; set ASAHI_M4_MESA or --mesa', diff --git a/tools/testing/selftests/drivers/gpu/asahi-m4/test-vm-registry.py b/tools/testing/selftests/drivers/gpu/asahi-m4/test-vm-registry.py index d7750aab9e147..add96b48bb335 100644 --- a/tools/testing/selftests/drivers/gpu/asahi-m4/test-vm-registry.py +++ b/tools/testing/selftests/drivers/gpu/asahi-m4/test-vm-registry.py @@ -20,7 +20,7 @@ def item(marker): implstd::ops::Deref for KVec{type Target=Vec;fn deref(&self)->&Vec{&self.0}} implstd::ops::DerefMut for KVec{fn deref_mut(&mut self)->&mut Vec{&mut self.0}} mod hw{pub mod sizes{pub const MAX_VMS:u32=63;}} -mod module_parameters{pub struct Param;impl Param{pub fn value(&self)->&u32{&0}}pub static robust_isolation:Param=Param;} +mod module_parameters{pub struct Param;impl Param{pub fn value(&self)->u32{0}}pub static robust_isolation:Param=Param;} mod types{pub struct ScopeGuard(Option);implScopeGuard{pub fn new(f:F)->Self{Self(Some(f))}pub fn dismiss(mut self){self.0.take();}}implDrop for ScopeGuard{fn drop(&mut self){if let Some(f)=self.0.take(){f()}}}} struct Handle;impl Handle{fn is_closed(&self)->bool{false}} struct VmState{id:u64,vm:Handle,drops:Arc}impl Drop for VmState{fn drop(&mut self){self.drops.fetch_add(1,Ordering::AcqRel);}} diff --git a/tools/testing/selftests/media/Makefile b/tools/testing/selftests/media/Makefile new file mode 100644 index 0000000000000..b63cb0f120e7e --- /dev/null +++ b/tools/testing/selftests/media/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +CFLAGS += -O2 -Wall -Wextra -Werror +TEST_GEN_PROGS_EXTENDED := apple-isp-stress +include ../lib.mk diff --git a/tools/testing/selftests/media/README.apple-isp b/tools/testing/selftests/media/README.apple-isp new file mode 100644 index 0000000000000..57b95740120c0 --- /dev/null +++ b/tools/testing/selftests/media/README.apple-isp @@ -0,0 +1,34 @@ +Apple ISP capture delivery stress +================================= + +Build with make -C tools/testing/selftests/media. This is a manual hardware +runner; run_tests does not start the camera automatically. No image files are +written. Run only on the agreed J616s test kernel while audio is idle. + +Example for configurations whose expected rate has been established as 30 fps: + + ./apple-isp-stress /dev/video0 600 10 30 | tee capture-results.jsonl + +It enumerates every offered single-planar format and discrete size. Each gets +a ten-minute run, then ten five-second start/stop cycles, switching formats and +sizes between streams. A substituted format/rate, poll timeout, queue error, +V4L2 buffer error, sequence discontinuity or delivery gap exceeding 1.5 nominal +frame periods fails. Delivered FPS must reach 99.8% of the supplied rate, allowing +29.97/30 rounding. The rate argument is an explicit test expectation, not a +rate inferred from the sensor clock or set by the tool as an alleged sensor fact. +For modes with a different nominal rate, filter the run explicitly, for example +`./apple-isp-stress /dev/video0 600 10 25 P010 1920 1080`. A filtered run tests +that mode's restart cycles; the unfiltered run also exercises format switches. This runner stops +on the first failure to preserve evidence and avoid retrying ambiguous firmware. + +The driver's sequence increments for received reports, so consecutive sequence +values alone cannot establish zero sensor/firmware drops. The timing checks +measure delivery gaps and completed frames. A true zero-drop sensor claim also +needs a validated firmware frame counter or an independent sensor-rate oracle. +No RAW packing, Bayer order or nominal per-config FPS is guessed by this tool. +It tests RAW automatically if a verified RAW format is added to ENUM_FMT later. + +Preserve the current firmware config dump, v4l2-ctl --all/--list-formats-ext, +the JSONL output, dmesg before/after, exact kernel and source hashes. Check both +P010 presets for correct image content separately; this runner deliberately +avoids a converter or disk writer in the capture loop. diff --git a/tools/testing/selftests/media/apple-isp-stress.c b/tools/testing/selftests/media/apple-isp-stress.c new file mode 100644 index 0000000000000..cd85e51ecba09 --- /dev/null +++ b/tools/testing/selftests/media/apple-isp-stress.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* V4L2 delivery stress test. Does not infer sensor FPS from a driver counter. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_MODES 256 +#define MAX_BUFFERS 16 + +struct mode { + uint32_t fourcc, width, height; +}; + +struct mapping { + void *address; + size_t size; +}; + +static int xioctl(int fd, unsigned long cmd, void *arg) +{ + int ret; + + do { + ret = ioctl(fd, cmd, arg); + } while (ret < 0 && errno == EINTR); + return ret; +} + +static double monotonic(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec + ts.tv_nsec / 1000000000.0; +} + +static int capture(int fd, struct mode mode, unsigned int seconds, + unsigned int fps, unsigned int cycle) +{ + struct v4l2_format format = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE }; + struct v4l2_streamparm parm = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE }; + struct v4l2_requestbuffers req = { + .count = 4, .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, .memory = V4L2_MEMORY_MMAP, + }; + struct mapping maps[MAX_BUFFERS] = {}; + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + unsigned int frames = 0, bad = 0, gaps = 0, sequence_gaps = 0, mapped = 0; + unsigned int last_sequence = 0; + double start, end, first = 0, last = 0, max_gap = 0, measured_fps = 0; + bool streaming = false; + int ret = -1; + + format.fmt.pix.width = mode.width; + format.fmt.pix.height = mode.height; + format.fmt.pix.pixelformat = mode.fourcc; + format.fmt.pix.field = V4L2_FIELD_NONE; + if (xioctl(fd, VIDIOC_S_FMT, &format) < 0) + goto error; + /* Fail instead of counting a silently substituted mode as tested. */ + if (format.fmt.pix.width != mode.width || format.fmt.pix.height != mode.height || + format.fmt.pix.pixelformat != mode.fourcc) { + fprintf(stderr, "requested mode was substituted\n"); + goto out; + } + parm.parm.capture.timeperframe.numerator = 1; + parm.parm.capture.timeperframe.denominator = fps; + if (xioctl(fd, VIDIOC_S_PARM, &parm) < 0) + goto error; + if (parm.parm.capture.timeperframe.numerator != 1 || + parm.parm.capture.timeperframe.denominator != fps) { + fprintf(stderr, "requested rate was substituted\n"); + goto out; + } + if (xioctl(fd, VIDIOC_REQBUFS, &req) < 0) + goto error; + if (req.count < 2 || req.count > MAX_BUFFERS) + goto out; + for (unsigned int i = 0; i < req.count; i++) { + struct v4l2_buffer buf = { + .type = type, .memory = V4L2_MEMORY_MMAP, .index = i, + }; + + if (xioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) + goto error; + maps[i].size = buf.length; + maps[i].address = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, buf.m.offset); + if (maps[i].address == MAP_FAILED) + goto error; + mapped++; + if (xioctl(fd, VIDIOC_QBUF, &buf) < 0) + goto error; + } + if (xioctl(fd, VIDIOC_STREAMON, &type) < 0) + goto error; + streaming = true; + start = monotonic(); + end = start + seconds; + while (monotonic() < end) { + struct pollfd pfd = { .fd = fd, .events = POLLIN }; + struct v4l2_buffer buf = { .type = type, .memory = V4L2_MEMORY_MMAP }; + double timestamp, gap; + int ready = poll(&pfd, 1, 2000); + + if (ready < 0 && errno == EINTR) + continue; + if (ready <= 0 || (pfd.revents & (POLLERR | POLLHUP | POLLNVAL))) { + fprintf(stderr, "stream stalled or queue failed\n"); + goto out; + } + if (xioctl(fd, VIDIOC_DQBUF, &buf) < 0) + goto error; + if (buf.index >= req.count || buf.bytesused > maps[buf.index].size) + goto out; + if (buf.flags & V4L2_BUF_FLAG_ERROR) + bad++; + timestamp = buf.timestamp.tv_sec + buf.timestamp.tv_usec / 1000000.0; + if (frames) { + gap = timestamp - last; + if (gap > max_gap) + max_gap = gap; + if (gap <= 0 || gap > 1.5 / fps) + gaps++; + if ((uint32_t)(buf.sequence - last_sequence) != 1) + sequence_gaps++; + } else { + first = timestamp; + } + last = timestamp; + last_sequence = buf.sequence; + frames++; + /* Requeue immediately; image copies/conversion do not limit the test. */ + if (xioctl(fd, VIDIOC_QBUF, &buf) < 0) + goto error; + } + if (frames > 1 && last > first) + measured_fps = (frames - 1) / (last - first); + /* Allow clock/29.97-vs-30 rounding; gaps and buffer errors must be zero. */ + ret = bad || gaps || sequence_gaps || measured_fps < fps * 0.998 ? 1 : 0; + goto out; +error: + perror("V4L2 ioctl"); +out: + if (streaming && xioctl(fd, VIDIOC_STREAMOFF, &type) < 0) { + perror("STREAMOFF"); + ret = -1; + } + for (unsigned int i = 0; i < mapped; i++) + munmap(maps[i].address, maps[i].size); + req.count = 0; + if (xioctl(fd, VIDIOC_REQBUFS, &req) < 0) + ret = -1; + printf("{\"cycle\":%u,\"fourcc\":\"%.4s\",\"width\":%u,\"height\":%u,", + cycle, (char *)&mode.fourcc, mode.width, mode.height); + printf("\"stride\":%u,\"sizeimage\":%u,\"requested_fps\":%u,\"frames\":%u,", + format.fmt.pix.bytesperline, format.fmt.pix.sizeimage, fps, frames); + printf("\"delivered_fps\":%.6f,\"max_gap_ms\":%.3f,\"timing_gaps\":%u,", + measured_fps, max_gap * 1000, gaps); + printf("\"buffer_errors\":%u,\"sequence_gaps\":%u,\"pass\":%s}\n", + bad, sequence_gaps, ret ? "false" : "true"); + fflush(stdout); + return ret; +} + +int main(int argc, char **argv) +{ + struct mode modes[MAX_MODES]; + unsigned int count = 0, seconds, cycles, fps; + int fd, failed = 0; + char *end; + unsigned long values[3]; + + if (argc != 5 && argc != 8) { + fprintf(stderr, "usage: %s device seconds cycles fps [FOURCC width height]\n", + argv[0]); + return 2; + } + for (int i = 0; i < 3; i++) { + errno = 0; + values[i] = strtoul(argv[i + 2], &end, 10); + if (errno || *end || !values[i] || values[i] > 86400) + return 2; + } + seconds = values[0]; + cycles = values[1]; + fps = values[2]; + if (fps > 240 || cycles > 1000 || seconds < 5) + return 2; + fd = open(argv[1], O_RDWR | O_NONBLOCK); + if (fd < 0) { + perror(argv[1]); + return 2; + } + for (unsigned int i = 0; ; i++) { + struct v4l2_fmtdesc format = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, .index = i }; + + if (xioctl(fd, VIDIOC_ENUM_FMT, &format) < 0) { + if (errno != EINVAL) + failed = 1; + break; + } + /* This harness exercises the driver's contiguous, single-planar API. */ + if (format.pixelformat == V4L2_PIX_FMT_NV12M) + continue; + for (unsigned int j = 0; ; j++) { + struct v4l2_frmsizeenum size = { + .pixel_format = format.pixelformat, .index = j, + }; + + if (xioctl(fd, VIDIOC_ENUM_FRAMESIZES, &size) < 0) { + if (errno != EINVAL) + failed = 1; + break; + } + if (size.type != V4L2_FRMSIZE_TYPE_DISCRETE || count == MAX_MODES) { + failed = 1; + break; + } + modes[count++] = (struct mode){format.pixelformat, size.discrete.width, + size.discrete.height}; + } + } + if (argc == 8) { + uint32_t fourcc; + unsigned long width, height; + bool found = false; + + if (strlen(argv[5]) != 4) { + close(fd); + return 2; + } + memcpy(&fourcc, argv[5], sizeof(fourcc)); + errno = 0; + width = strtoul(argv[6], &end, 10); + if (errno || *end || !width || width > UINT32_MAX) { + close(fd); + return 2; + } + errno = 0; + height = strtoul(argv[7], &end, 10); + if (errno || *end || !height || height > UINT32_MAX) { + close(fd); + return 2; + } + for (unsigned int i = 0; i < count; i++) { + if (modes[i].fourcc == fourcc && modes[i].width == width && + modes[i].height == height) { + modes[0] = modes[i]; + found = true; + break; + } + } + count = found ? 1 : 0; + } + if (!count) + failed = 1; + /* Long runs first, then repeated stops/restarts and format/size switches. */ + for (unsigned int cycle = 0; cycle <= cycles && !failed; cycle++) { + for (unsigned int i = 0; i < count; i++) { + if (capture(fd, modes[i], cycle ? 5 : seconds, fps, cycle)) { + failed = 1; + break; + } + } + } + close(fd); + return failed ? 1 : 0; +}