From 6d181d5fd2d6199dee12a7a552ad597982b1a469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Berkay=20K=C3=B6sebay?= Date: Fri, 13 Nov 2026 05:01:59 +0800 Subject: [PATCH 1/2] kernel: Decode HEVC in hardware on the A9 and A10 iPads The VXD video decoder of the S8000/S8003 (A9) and T8010 (A10) also decodes HEVC (H.265). Add a stateless HEVC decoder beside the H.264 one, with the per-SoC values Apple's driver uses. H.264 stays the first and default format; HEVC shows up as a second one. From orchard PR #121 (commit 4016f5e2): patches 0008-0011 there, the last two adapted to main, which does not carry the VXD sleep changes of PR #117. The kexec firmware-stop patch (0012 there) belongs to the warm restart topic and is not part of this one. --- devices/d10/device.toml | 1 + devices/d101/device.toml | 1 + devices/d11/device.toml | 1 + devices/d111/device.toml | 1 + devices/d20/device.toml | 1 + devices/d201/device.toml | 1 + devices/d21/device.toml | 1 + devices/d211/device.toml | 1 + devices/d22/device.toml | 1 + devices/d221/device.toml | 1 + devices/j1/device.toml | 1 + devices/j120/device.toml | 1 + devices/j121/device.toml | 1 + devices/j127/device.toml | 1 + devices/j128/device.toml | 1 + devices/j171/device.toml | 1 + devices/j172/device.toml | 1 + devices/j207/device.toml | 1 + devices/j208/device.toml | 1 + devices/j71/device.toml | 1 + devices/j71b/device.toml | 1 + devices/j71s/device.toml | 1 + devices/j71t/device.toml | 1 + devices/j72b/device.toml | 1 + devices/j72s/device.toml | 1 + devices/j72t/device.toml | 1 + devices/j73/device.toml | 1 + devices/j81/device.toml | 1 + devices/j82/device.toml | 1 + devices/j85/device.toml | 1 + devices/j85m/device.toml | 1 + devices/j86/device.toml | 1 + devices/j86m/device.toml | 1 + devices/j87/device.toml | 1 + devices/j87m/device.toml | 1 + devices/j96/device.toml | 1 + devices/j97/device.toml | 1 + devices/j98a/device.toml | 1 + devices/j99a/device.toml | 1 + devices/n51/device.toml | 1 + devices/n53/device.toml | 1 + devices/n56/device.toml | 1 + devices/n61/device.toml | 1 + devices/n66/device.toml | 1 + devices/n66m/device.toml | 1 + devices/n69/device.toml | 1 + devices/n69u/device.toml | 1 + devices/n71/device.toml | 1 + devices/n71m/device.toml | 1 + devices/p105/device.toml | 1 + features.toml | 1 + ...xd-Prepare-for-a-second-coded-format.patch | 250 +++ ...-Add-the-HEVC-command-stream-builder.patch | 52 + ...e-vxd-Add-the-stateless-HEVC-decoder.patch | 1616 +++++++++++++++++ ...Enable-HEVC-on-the-S8000-and-T8010-d.patch | 62 + schemas/v1/device.schema.json | 11 +- tools/catalog/drivers.json | 10 +- tools/catalog/feature-drivers.toml | 1 + 58 files changed, 2050 insertions(+), 3 deletions(-) create mode 100644 kernel/patches/drivers/media/0006-media-apple-vxd-Prepare-for-a-second-coded-format.patch create mode 100644 kernel/patches/drivers/media/0007-media-apple-vxd-Add-the-HEVC-command-stream-builder.patch create mode 100644 kernel/patches/drivers/media/0008-media-apple-vxd-Add-the-stateless-HEVC-decoder.patch create mode 100644 kernel/patches/drivers/media/0009-media-apple-vxd-Enable-HEVC-on-the-S8000-and-T8010-d.patch diff --git a/devices/d10/device.toml b/devices/d10/device.toml index 37c7ff01..82a677cf 100644 --- a/devices/d10/device.toml +++ b/devices/d10/device.toml @@ -58,3 +58,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/d101/device.toml b/devices/d101/device.toml index 46b30321..954d19bb 100644 --- a/devices/d101/device.toml +++ b/devices/d101/device.toml @@ -58,3 +58,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/d11/device.toml b/devices/d11/device.toml index 83e1a85d..8704efc0 100644 --- a/devices/d11/device.toml +++ b/devices/d11/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/d111/device.toml b/devices/d111/device.toml index 99c20fbc..0e48e5f2 100644 --- a/devices/d111/device.toml +++ b/devices/d111/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/d20/device.toml b/devices/d20/device.toml index fd995d68..b3cdca42 100644 --- a/devices/d20/device.toml +++ b/devices/d20/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/d201/device.toml b/devices/d201/device.toml index a973d29c..f530c173 100644 --- a/devices/d201/device.toml +++ b/devices/d201/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/d21/device.toml b/devices/d21/device.toml index 9ecaee92..98ce032f 100644 --- a/devices/d21/device.toml +++ b/devices/d21/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/d211/device.toml b/devices/d211/device.toml index af13003d..ad431385 100644 --- a/devices/d211/device.toml +++ b/devices/d211/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/d22/device.toml b/devices/d22/device.toml index 938de5b5..05c9f6c4 100644 --- a/devices/d22/device.toml +++ b/devices/d22/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/d221/device.toml b/devices/d221/device.toml index fe36b4fa..99cfcde0 100644 --- a/devices/d221/device.toml +++ b/devices/d221/device.toml @@ -56,3 +56,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j1/device.toml b/devices/j1/device.toml index 79d533fc..55284f4c 100644 --- a/devices/j1/device.toml +++ b/devices/j1/device.toml @@ -59,3 +59,4 @@ usb_network = "untested" # CDC Ethernet, pinged both ways. audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j120/device.toml b/devices/j120/device.toml index 54bdeeb0..e9ac0a90 100644 --- a/devices/j120/device.toml +++ b/devices/j120/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j121/device.toml b/devices/j121/device.toml index e952846b..0d0dee24 100644 --- a/devices/j121/device.toml +++ b/devices/j121/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j127/device.toml b/devices/j127/device.toml index edfcceea..f0d7fa6b 100644 --- a/devices/j127/device.toml +++ b/devices/j127/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j128/device.toml b/devices/j128/device.toml index eccb2be0..e86b1a68 100644 --- a/devices/j128/device.toml +++ b/devices/j128/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j171/device.toml b/devices/j171/device.toml index 7e949d12..d2aec4d0 100644 --- a/devices/j171/device.toml +++ b/devices/j171/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "working" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j172/device.toml b/devices/j172/device.toml index b51d8510..044d536a 100644 --- a/devices/j172/device.toml +++ b/devices/j172/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "untested" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j207/device.toml b/devices/j207/device.toml index 9f1b8d82..1b3a1bf9 100644 --- a/devices/j207/device.toml +++ b/devices/j207/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j208/device.toml b/devices/j208/device.toml index a05ce52d..0141c6bc 100644 --- a/devices/j208/device.toml +++ b/devices/j208/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j71/device.toml b/devices/j71/device.toml index 4103d1d5..461b7528 100644 --- a/devices/j71/device.toml +++ b/devices/j71/device.toml @@ -56,3 +56,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j71b/device.toml b/devices/j71b/device.toml index c2aa8231..59bd78b6 100644 --- a/devices/j71b/device.toml +++ b/devices/j71b/device.toml @@ -52,3 +52,4 @@ usb_network = "working" audio_output = "working" audio_input = "missing" camera = "missing" +video_decode_hevc = "untested" diff --git a/devices/j71s/device.toml b/devices/j71s/device.toml index 7db1ed12..5f19b54e 100644 --- a/devices/j71s/device.toml +++ b/devices/j71s/device.toml @@ -57,3 +57,4 @@ usb_network = "untested" audio_output = "untested" audio_input = "untested" camera = "missing" +video_decode_hevc = "untested" diff --git a/devices/j71t/device.toml b/devices/j71t/device.toml index c850227b..50c7cd2e 100644 --- a/devices/j71t/device.toml +++ b/devices/j71t/device.toml @@ -54,3 +54,4 @@ usb_network = "untested" audio_output = "working" audio_input = "untested" camera = "missing" +video_decode_hevc = "working" diff --git a/devices/j72b/device.toml b/devices/j72b/device.toml index 00996e6c..defdb532 100644 --- a/devices/j72b/device.toml +++ b/devices/j72b/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "untested" audio_input = "missing" camera = "missing" +video_decode_hevc = "untested" diff --git a/devices/j72s/device.toml b/devices/j72s/device.toml index f64c9e5f..b15dbf38 100644 --- a/devices/j72s/device.toml +++ b/devices/j72s/device.toml @@ -57,3 +57,4 @@ usb_network = "untested" audio_output = "untested" audio_input = "untested" camera = "missing" +video_decode_hevc = "untested" diff --git a/devices/j72t/device.toml b/devices/j72t/device.toml index 38ba1313..6fa8ad18 100644 --- a/devices/j72t/device.toml +++ b/devices/j72t/device.toml @@ -54,3 +54,4 @@ usb_network = "untested" audio_output = "untested" audio_input = "untested" camera = "missing" +video_decode_hevc = "untested" diff --git a/devices/j73/device.toml b/devices/j73/device.toml index 76c7f04a..93881ff2 100644 --- a/devices/j73/device.toml +++ b/devices/j73/device.toml @@ -56,3 +56,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j81/device.toml b/devices/j81/device.toml index 5c750799..ba559d4c 100644 --- a/devices/j81/device.toml +++ b/devices/j81/device.toml @@ -55,3 +55,4 @@ usb_network = "working" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j82/device.toml b/devices/j82/device.toml index 0071e63e..4f8494dc 100644 --- a/devices/j82/device.toml +++ b/devices/j82/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j85/device.toml b/devices/j85/device.toml index 7b2efdb4..d11e8ef1 100644 --- a/devices/j85/device.toml +++ b/devices/j85/device.toml @@ -56,3 +56,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j85m/device.toml b/devices/j85m/device.toml index 512eb26c..64ce961f 100644 --- a/devices/j85m/device.toml +++ b/devices/j85m/device.toml @@ -56,3 +56,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j86/device.toml b/devices/j86/device.toml index 8ceb823b..688b2a1a 100644 --- a/devices/j86/device.toml +++ b/devices/j86/device.toml @@ -56,3 +56,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j86m/device.toml b/devices/j86m/device.toml index b0cecdc6..6ab531bc 100644 --- a/devices/j86m/device.toml +++ b/devices/j86m/device.toml @@ -56,3 +56,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j87/device.toml b/devices/j87/device.toml index efab182e..ce91ec6a 100644 --- a/devices/j87/device.toml +++ b/devices/j87/device.toml @@ -56,3 +56,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j87m/device.toml b/devices/j87m/device.toml index f253466c..35f57773 100644 --- a/devices/j87m/device.toml +++ b/devices/j87m/device.toml @@ -56,3 +56,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j96/device.toml b/devices/j96/device.toml index f73e8a35..f4d6cf26 100644 --- a/devices/j96/device.toml +++ b/devices/j96/device.toml @@ -58,3 +58,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j97/device.toml b/devices/j97/device.toml index f7bddef4..52984e73 100644 --- a/devices/j97/device.toml +++ b/devices/j97/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j98a/device.toml b/devices/j98a/device.toml index 1e4de72c..6dc027e0 100644 --- a/devices/j98a/device.toml +++ b/devices/j98a/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/j99a/device.toml b/devices/j99a/device.toml index ce2762b4..2048f84c 100644 --- a/devices/j99a/device.toml +++ b/devices/j99a/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/n51/device.toml b/devices/n51/device.toml index bd811030..08808d5a 100644 --- a/devices/n51/device.toml +++ b/devices/n51/device.toml @@ -56,3 +56,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/n53/device.toml b/devices/n53/device.toml index 835f3963..0e0e32cb 100644 --- a/devices/n53/device.toml +++ b/devices/n53/device.toml @@ -56,3 +56,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/n56/device.toml b/devices/n56/device.toml index fbe1eb31..2818de3e 100644 --- a/devices/n56/device.toml +++ b/devices/n56/device.toml @@ -56,3 +56,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/n61/device.toml b/devices/n61/device.toml index 462fd978..e528c490 100644 --- a/devices/n61/device.toml +++ b/devices/n61/device.toml @@ -54,3 +54,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/n66/device.toml b/devices/n66/device.toml index 445604e7..d2400599 100644 --- a/devices/n66/device.toml +++ b/devices/n66/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/n66m/device.toml b/devices/n66m/device.toml index 271a9576..aa306b0d 100644 --- a/devices/n66m/device.toml +++ b/devices/n66m/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/n69/device.toml b/devices/n69/device.toml index 1274d806..8c13faf9 100644 --- a/devices/n69/device.toml +++ b/devices/n69/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/n69u/device.toml b/devices/n69u/device.toml index 1b8ad683..2984e963 100644 --- a/devices/n69u/device.toml +++ b/devices/n69u/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/n71/device.toml b/devices/n71/device.toml index 5e65cd00..0a541db0 100644 --- a/devices/n71/device.toml +++ b/devices/n71/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/n71m/device.toml b/devices/n71m/device.toml index ef312d54..120c4ffe 100644 --- a/devices/n71m/device.toml +++ b/devices/n71m/device.toml @@ -55,3 +55,4 @@ usb_network = "untested" audio_output = "missing" audio_input = "missing" camera = "missing" +video_decode_hevc = "missing" diff --git a/devices/p105/device.toml b/devices/p105/device.toml index 5cdcb0c0..c9908c6d 100644 --- a/devices/p105/device.toml +++ b/devices/p105/device.toml @@ -66,3 +66,4 @@ usb_network = "untested" audio_output = "untested" audio_input = "untested" camera = "missing" +video_decode_hevc = "missing" diff --git a/features.toml b/features.toml index adcf0586..98fc2086 100644 --- a/features.toml +++ b/features.toml @@ -31,3 +31,4 @@ usb_network = "USB network" audio_output = "Speakers" audio_input = "Microphone" camera = "Cameras" +video_decode_hevc = "Hardware H.265 video decoding" diff --git a/kernel/patches/drivers/media/0006-media-apple-vxd-Prepare-for-a-second-coded-format.patch b/kernel/patches/drivers/media/0006-media-apple-vxd-Prepare-for-a-second-coded-format.patch new file mode 100644 index 00000000..297352c0 --- /dev/null +++ b/kernel/patches/drivers/media/0006-media-apple-vxd-Prepare-for-a-second-coded-format.patch @@ -0,0 +1,250 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Berkay=20K=C3=B6sebay?= +Date: Tue, 10 Nov 2026 21:24:51 +0100 +Subject: [PATCH] media: apple-vxd: Prepare for a second coded format + +The coded-format table was the H.264 unit's own array, installed whole +by apple_vxd_h264_register(). Make it a table of pointers in the device +that each codec unit appends to with apple_vxd_register_coded_fmt(), so +a second codec can sit next to H.264 without the core knowing about it. +The first unit registered stays the default OUTPUT format. + +Add apple_vxd_buf_aux_cpu() and apple_vxd_buf_aux_size() next to +apple_vxd_buf_aux_va(): a codec that keeps per-picture state in the +CAPTURE side buffer needs to read and write it. + +apple_vxd_cmdbuf_pair_chunk_end() had no user. Make it match the HEVC +per-picture chunk it was written for: AppleD5500 (0xfffffff0062b7ab0) +zeroes 0x40 - (pairs & 7) * 8 bytes past the write pointer and neither +counts them in the packet's byte count nor advances over them. + +H.264 output is unchanged: its host command-stream compare still matches +the reference generator on all 76 pictures. + +[Orchard: from orchard PR #121, commit 4016f5e2; unchanged] + +Assisted-by: LLM +--- +diff --git a/drivers/media/platform/apple/vxd/apple-vxd-cmd.c b/drivers/media/platform/apple/vxd/apple-vxd-cmd.c +index 03340ed..fa93cd9 100644 +--- a/drivers/media/platform/apple/vxd/apple-vxd-cmd.c ++++ b/drivers/media/platform/apple/vxd/apple-vxd-cmd.c +@@ -318,29 +318,30 @@ int apple_vxd_cmdbuf_chunk_end_exact(struct apple_vxd_cmdbuf *cb, + } + + /* +- * Close a chunk made only of 0x000f0000 pairs (the per-picture kind-4 +- * chunk, 0x62b7ab0): pad by 0x40 - (pairs & 7) * 8 bytes, a full 64 when +- * the pair count is a multiple of eight, and report the byte count +- * including that pad (the kext measures it after the pad; the message +- * sends it as a pair count, >> 3). ++ * Close a chunk made only of 0x000f0000 pairs (the HEVC per-picture kind-4 ++ * chunk, 0x62b7ab0): report its byte count (the message sends it as a pair ++ * count, >> 3), then zero 0x40 - (pairs & 7) * 8 bytes past it, a full 64 ++ * when the pair count is a multiple of eight. The kext writes that pad ++ * past its write pointer and neither counts it nor advances over it, so ++ * this must be the buffer's last chunk. + */ + int apple_vxd_cmdbuf_pair_chunk_end(struct apple_vxd_cmdbuf *cb, u32 *offset, + u32 *bytes) + { + size_t pad; +- int ret; + + if (cb->group_open || cb->used - cb->chunk_start != cb->npairs * 8) + cb->error = true; +- if (cb->error) ++ pad = VXD_CMD_CHUNK_ALIGN - (cb->npairs & 7) * 8; ++ if (cb->error || pad > cb->size - cb->used) { ++ cb->error = true; + return -ENOSPC; ++ } + + *offset = cb->chunk_start; +- pad = VXD_CMD_CHUNK_ALIGN - (cb->npairs & 7) * 8; +- ret = vxd_cmd_pad(cb, pad); +- if (!ret) +- *bytes = cb->used - *offset; +- return ret; ++ *bytes = cb->used - cb->chunk_start; ++ memset((u8 *)cb->vaddr + cb->used, 0, pad); ++ return 0; + } + + /* +diff --git a/drivers/media/platform/apple/vxd/apple-vxd-h264.c b/drivers/media/platform/apple/vxd/apple-vxd-h264.c +index 726df75..ca289e1 100644 +--- a/drivers/media/platform/apple/vxd/apple-vxd-h264.c ++++ b/drivers/media/platform/apple/vxd/apple-vxd-h264.c +@@ -557,14 +557,8 @@ static const struct apple_vxd_coded_fmt_desc apple_vxd_h264_fmt = { + .decoded_fmts = apple_vxd_h264_decoded_fmts, + }; + +-static const struct apple_vxd_coded_fmt_desc apple_vxd_coded_fmts[] = { +- apple_vxd_h264_fmt, +-}; +- +-/* Fills the device's coded-format table, before any V4L2 node exists. */ ++/* Adds H.264 to the device's coded formats, before any V4L2 node exists. */ + int apple_vxd_h264_register(struct apple_vxd_dev *vxd) + { +- vxd->coded_fmts = apple_vxd_coded_fmts; +- vxd->num_coded_fmts = ARRAY_SIZE(apple_vxd_coded_fmts); +- return 0; ++ return apple_vxd_register_coded_fmt(vxd, &apple_vxd_h264_fmt); + } +diff --git a/drivers/media/platform/apple/vxd/apple-vxd-v4l2.c b/drivers/media/platform/apple/vxd/apple-vxd-v4l2.c +index 23c051f..9664bb8 100644 +--- a/drivers/media/platform/apple/vxd/apple-vxd-v4l2.c ++++ b/drivers/media/platform/apple/vxd/apple-vxd-v4l2.c +@@ -97,8 +97,8 @@ apple_vxd_find_coded_fmt_desc(struct apple_vxd_ctx *ctx, u32 fourcc) + unsigned int i; + + for (i = 0; i < vxd->num_coded_fmts; i++) +- if (vxd->coded_fmts[i].fourcc == fourcc) +- return &vxd->coded_fmts[i]; ++ if (vxd->coded_fmts[i]->fourcc == fourcc) ++ return vxd->coded_fmts[i]; + return NULL; + } + +@@ -107,7 +107,7 @@ static void apple_vxd_reset_coded_fmt(struct apple_vxd_ctx *ctx) + struct v4l2_format *f = &ctx->coded_fmt; + + /* apple_vxd_ctx_init() checked vxd->coded_fmts before calling us. */ +- ctx->coded_fmt_desc = &ctx->vxd->coded_fmts[0]; ++ ctx->coded_fmt_desc = ctx->vxd->coded_fmts[0]; + apple_vxd_reset_fmt(ctx, f, ctx->coded_fmt_desc->fourcc); + f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + f->fmt.pix_mp.width = ctx->coded_fmt_desc->frmsize.min_width; +@@ -208,12 +208,12 @@ static int apple_vxd_init_ctrls(struct apple_vxd_ctx *ctx) + int ret; + + for (i = 0; i < vxd->num_coded_fmts; i++) +- nctrls += vxd->coded_fmts[i].ctrls->num_ctrls; ++ nctrls += vxd->coded_fmts[i]->ctrls->num_ctrls; + + v4l2_ctrl_handler_init(&ctx->ctrl_hdl, nctrls); + + for (i = 0; i < vxd->num_coded_fmts; i++) { +- ret = apple_vxd_add_ctrls(ctx, vxd->coded_fmts[i].ctrls); ++ ret = apple_vxd_add_ctrls(ctx, vxd->coded_fmts[i]->ctrls); + if (ret) + goto err_free_handler; + } +@@ -297,7 +297,7 @@ static int apple_vxd_do_try_output_fmt(struct apple_vxd_ctx *ctx, + + desc = apple_vxd_find_coded_fmt_desc(ctx, pix_mp->pixelformat); + if (!desc) { +- desc = &ctx->vxd->coded_fmts[0]; ++ desc = ctx->vxd->coded_fmts[0]; + pix_mp->pixelformat = desc->fourcc; + } + +@@ -404,7 +404,7 @@ static int apple_vxd_do_enum_output_fmt(struct apple_vxd_ctx *ctx, + { + if (f->index >= ctx->vxd->num_coded_fmts) + return -EINVAL; +- f->pixelformat = ctx->vxd->coded_fmts[f->index].fourcc; ++ f->pixelformat = ctx->vxd->coded_fmts[f->index]->fourcc; + return 0; + } + +@@ -650,6 +650,18 @@ u32 apple_vxd_buf_aux_va(struct vb2_v4l2_buffer *vbuf) + return vxd_buf(vbuf)->aux.va; + } + ++/* CPU address of a CAPTURE buffer's side buffer, NULL if it has none. */ ++void *apple_vxd_buf_aux_cpu(struct vb2_v4l2_buffer *vbuf) ++{ ++ return vxd_buf(vbuf)->aux.va ? vxd_buf(vbuf)->aux.mem.vaddr : NULL; ++} ++ ++/* Size of a CAPTURE buffer's side buffer, 0 if it has none. */ ++size_t apple_vxd_buf_aux_size(struct vb2_v4l2_buffer *vbuf) ++{ ++ return vxd_buf(vbuf)->aux.va ? vxd_buf(vbuf)->aux.mem.size : 0; ++} ++ + static void apple_vxd_buf_queue(struct vb2_buffer *vb) + { + struct apple_vxd_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); +@@ -930,11 +942,25 @@ static const struct v4l2_file_operations apple_vxd_fops = { + + /* --- registration ------------------------------------------------------ */ + ++/* ++ * Add one coded format to the device's table. The codec units call this ++ * during probe, before any V4L2 node exists; the first one added is the ++ * default OUTPUT format. ++ */ ++int apple_vxd_register_coded_fmt(struct apple_vxd_dev *vxd, ++ const struct apple_vxd_coded_fmt_desc *desc) ++{ ++ if (vxd->num_coded_fmts >= ARRAY_SIZE(vxd->coded_fmts)) ++ return -ENOSPC; ++ vxd->coded_fmts[vxd->num_coded_fmts++] = desc; ++ return 0; ++} ++ + int apple_vxd_v4l2_init(struct apple_vxd_dev *vxd) + { + int ret; + +- if (!vxd->coded_fmts || !vxd->num_coded_fmts) { ++ if (!vxd->num_coded_fmts) { + dev_err(vxd->dev, "v4l2: no coded formats registered, not exposing a node\n"); + return -ENODEV; + } +diff --git a/drivers/media/platform/apple/vxd/apple-vxd.h b/drivers/media/platform/apple/vxd/apple-vxd.h +index bba04f7..5faac08 100644 +--- a/drivers/media/platform/apple/vxd/apple-vxd.h ++++ b/drivers/media/platform/apple/vxd/apple-vxd.h +@@ -10,7 +10,8 @@ + * - host<->firmware mailbox: ring geometry, 0x80 init message, push, + * IRQ drain, fences (apple-vxd-mbox.c) + * - command buffers and their encoders (apple-vxd-cmdbuf.c, -cmd.c) +- * - the V4L2 stateless H.264 decoder (apple-vxd-v4l2.c, -h264*.c) ++ * - the V4L2 stateless node (apple-vxd-v4l2.c) and its codec units: ++ * H.264 (apple-vxd-h264*.c) + * + * [E] marks facts taken from the kext. + */ +@@ -60,6 +61,9 @@ enum apple_vxd_image_fmt { + APPLE_VXD_IMG_FMT_420_8BIT, + }; + ++/* One per codec unit. */ ++#define APPLE_VXD_MAX_CODED_FMTS 2 ++ + /* SoC variant data (compatible table). */ + struct apple_vxd_variant { + unsigned int vxd_version; /* ADT "vxd-version" */ +@@ -342,10 +346,11 @@ struct apple_vxd_dev { + struct v4l2_m2m_dev *m2m_dev; + struct mutex vdev_lock; /* serializes ioctls */ + /* +- * Coded formats, filled by apple_vxd_h264_register() during probe +- * before any V4L2 node exists. ++ * Coded formats, one per codec unit, added with ++ * apple_vxd_register_coded_fmt() during probe before any V4L2 node ++ * exists. + */ +- const struct apple_vxd_coded_fmt_desc *coded_fmts; ++ const struct apple_vxd_coded_fmt_desc *coded_fmts[APPLE_VXD_MAX_CODED_FMTS]; + unsigned int num_coded_fmts; + }; + +@@ -483,6 +488,10 @@ int apple_vxd_packet_msg(struct apple_vxd_dev *vxd, u32 *pkt, u16 fence, + /* v4l2.c */ + u32 apple_vxd_buf_va(struct vb2_v4l2_buffer *vbuf); + u32 apple_vxd_buf_aux_va(struct vb2_v4l2_buffer *vbuf); ++void *apple_vxd_buf_aux_cpu(struct vb2_v4l2_buffer *vbuf); ++size_t apple_vxd_buf_aux_size(struct vb2_v4l2_buffer *vbuf); ++int apple_vxd_register_coded_fmt(struct apple_vxd_dev *vxd, ++ const struct apple_vxd_coded_fmt_desc *desc); + int apple_vxd_v4l2_init(struct apple_vxd_dev *vxd); + void apple_vxd_v4l2_cleanup(struct apple_vxd_dev *vxd); + extern const struct v4l2_ctrl_ops apple_vxd_ctrl_ops; +-- +2.54.0 diff --git a/kernel/patches/drivers/media/0007-media-apple-vxd-Add-the-HEVC-command-stream-builder.patch b/kernel/patches/drivers/media/0007-media-apple-vxd-Add-the-HEVC-command-stream-builder.patch new file mode 100644 index 00000000..33e2d2e4 --- /dev/null +++ b/kernel/patches/drivers/media/0007-media-apple-vxd-Add-the-HEVC-command-stream-builder.patch @@ -0,0 +1,52 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Berkay=20K=C3=B6sebay?= +Date: Tue, 10 Nov 2026 21:25:14 +0100 +Subject: [PATCH] media: apple-vxd: Add the HEVC command-stream builder + +AppleD5500 decodes HEVC with a register program of its own, distinct +from the AVC one: per slice segment a chunk with the 0x90000000 +descriptor, the whole slice NAL by DMA ({0xb0000001, header bits, bytes} +and {0xa.., address}), the 0x30xx/0x34xx command registers and a slice +structure the core copies into the picture's slice parameter buffer +(0xf), each with a kind-1 packet (mailbox 0x81); then one chunk of +single register writes for the picture, the 0x50xx/0x54xx entropy and +0x70xx/0x71xx render registers with the output and reference surfaces +and the scaling lists, with a kind-4 packet (mailbox 0x88) that ends the +job. The layouts come from the iOS 16.7.10 kext (picture path +0xfffffff0062960a0, per slice 0x62b37d4, per picture 0x62b7ab0) and are +listed register by register in the comments. + +This builds that program from the V4L2 HEVC controls. It is pure code, +like the H.264 builder, so it also builds on the host: a harness feeds +it x265 and VideoToolbox test streams and compares every command word, +slice structure and packet against an independent generator written from +the kext. + +Supported: 8-bit 4:2:0, I/P/B, multiple and dependent slice segments, +tiles, WPP, long-term references, weighted prediction, PCM and scaling +lists. Refused: other chroma formats and bit depths (the Main 10 output +layout is not established), more than 15 references. + +The 15 hardware reference slots are named by the V4L2 DPB index; the +kext numbers them in its own DPB order, which never leaves the host. + +Nothing calls the builder yet. + +[Orchard: from orchard PR #121, commit 4016f5e2; unchanged] + +Assisted-by: LLM +--- +diff --git a/drivers/media/platform/apple/vxd/Makefile b/drivers/media/platform/apple/vxd/Makefile +index 7f692f8..0877a60 100644 +--- a/drivers/media/platform/apple/vxd/Makefile ++++ b/drivers/media/platform/apple/vxd/Makefile +@@ -2,6 +2,6 @@ + apple-vxd-y := apple-vxd-core.o apple-vxd-gart.o apple-vxd-fw.o \ + apple-vxd-mbox.o apple-vxd-cmd.o apple-vxd-cmdbuf.o \ + apple-vxd-v4l2.o apple-vxd-h264.o apple-vxd-h264-parse.o \ +- apple-vxd-h264-cmd.o ++ apple-vxd-h264-cmd.o apple-vxd-hevc-cmd.o + + obj-$(CONFIG_VIDEO_APPLE_VXD) += apple-vxd.o +-- +2.54.0 diff --git a/kernel/patches/drivers/media/0008-media-apple-vxd-Add-the-stateless-HEVC-decoder.patch b/kernel/patches/drivers/media/0008-media-apple-vxd-Add-the-stateless-HEVC-decoder.patch new file mode 100644 index 00000000..e2970aea --- /dev/null +++ b/kernel/patches/drivers/media/0008-media-apple-vxd-Add-the-stateless-HEVC-decoder.patch @@ -0,0 +1,1616 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Berkay=20K=C3=B6sebay?= +Date: Tue, 10 Nov 2026 21:25:29 +0100 +Subject: [PATCH] media: apple-vxd: Add the stateless HEVC decoder + +Expose V4L2_PIX_FMT_HEVC_SLICE next to H.264: frame-based decoding with +Annex-B start codes (what FFmpeg's v4l2request hwaccel, GStreamer's +v4l2slh265dec and libva-v4l2-request send), the SPS, PPS, SLICE_PARAMS, +SCALING_MATRIX and DECODE_PARAMS controls, NV12 output. + +Per picture the unit locates the slice NALs in the OUTPUT buffer, +resolves the DPB to CAPTURE buffers by timestamp and runs the builder. +The slice header's length comes from data_byte_offset, which FFmpeg and +libva count from the buffer and GStreamer from the slice's start code; +both are accepted. ENTRY_POINT_OFFSETS is not offered: the hardware +does not take them (the kext never reads them from its slice header). + +Buffers follow CHEVCDecoder: per context the four Genc buffers sized +from the picture, the intra, aux-line and stream-state buffers and the +slice structure buffer; per CAPTURE buffer the temporal MVs, fragment +pointer table, above parameters and the record the co-located picture's +words are read back from. The kext's 1 MiB slice parameter buffer per +picture is one per context here, as it is only read while its picture +decodes. + +The unit registers only when the SoC's match data gives the HEVC +parameters, and not on core revision 0x70600 (S8000 A0/A1), which needs +AppleD5500's BRN50577 dummy-picture workaround. + +[Orchard: from orchard PR #121, commit 4016f5e2; context adapted to main, which does not carry the VXD sleep patches of PR #117] + +Assisted-by: LLM +--- +diff --git a/drivers/media/platform/apple/vxd/Kconfig b/drivers/media/platform/apple/vxd/Kconfig +index c08ae03..ef785fc 100644 +--- a/drivers/media/platform/apple/vxd/Kconfig ++++ b/drivers/media/platform/apple/vxd/Kconfig +@@ -11,7 +11,8 @@ config VIDEO_APPLE_VXD + help + Support for the Apple "VXD" video decoder of the S8000/S8003 (A9) + SoCs: an Imagination PowerVR MSVDX core driven by the DEVA (MTX) +- firmware. It is exposed as a V4L2 stateless H.264 decoder. ++ firmware. It is exposed as a V4L2 stateless H.264 decoder, and as ++ an HEVC (H.265 Main) decoder on the SoCs whose decoder has it. + + The driver needs the DEVA firmware as apple/vxd-deva.bin. + +diff --git a/drivers/media/platform/apple/vxd/Makefile b/drivers/media/platform/apple/vxd/Makefile +index 0877a60..7e4fa0a 100644 +--- a/drivers/media/platform/apple/vxd/Makefile ++++ b/drivers/media/platform/apple/vxd/Makefile +@@ -2,6 +2,6 @@ + apple-vxd-y := apple-vxd-core.o apple-vxd-gart.o apple-vxd-fw.o \ + apple-vxd-mbox.o apple-vxd-cmd.o apple-vxd-cmdbuf.o \ + apple-vxd-v4l2.o apple-vxd-h264.o apple-vxd-h264-parse.o \ +- apple-vxd-h264-cmd.o apple-vxd-hevc-cmd.o ++ apple-vxd-h264-cmd.o apple-vxd-hevc.o apple-vxd-hevc-cmd.o + + obj-$(CONFIG_VIDEO_APPLE_VXD) += apple-vxd.o +diff --git a/drivers/media/platform/apple/vxd/apple-vxd-core.c b/drivers/media/platform/apple/vxd/apple-vxd-core.c +index ab7515f..97d388f 100644 +--- a/drivers/media/platform/apple/vxd/apple-vxd-core.c ++++ b/drivers/media/platform/apple/vxd/apple-vxd-core.c +@@ -354,6 +354,8 @@ static int apple_vxd_probe(struct platform_device *pdev) + goto err_disable_runtime_pm; + + ret = apple_vxd_h264_register(vxd); ++ if (!ret) ++ ret = apple_vxd_hevc_register(vxd); + if (!ret) + ret = apple_vxd_v4l2_init(vxd); + if (ret) +diff --git a/drivers/media/platform/apple/vxd/apple-vxd-hevc-cmd.c b/drivers/media/platform/apple/vxd/apple-vxd-hevc-cmd.c +new file mode 100644 +index 0000000..7b05811 +--- /dev/null ++++ b/drivers/media/platform/apple/vxd/apple-vxd-hevc-cmd.c +@@ -0,0 +1,940 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Apple VXD - HEVC (H.265) command-stream builder ++ * ++ * The HEVC path of the kext (picture path 0xfffffff0062960a0; per slice ++ * 0x62b37d4, per picture 0x62b7ab0; docs/periph/vdec-hevc-20261110.md in ++ * the hardware bring-up records has the word-level map). Per picture: ++ * ++ * per slice segment, one chunk and one kind-1 packet (mailbox 0x81): ++ * 0x90000000 + 9 words slice descriptor ++ * 0xb + 0xa the whole slice NAL from its header byte; ++ * the core skips the header (RBSP bits) ++ * 0x1 register runs CMD block 0x30xx / 0x34xx ++ * 0x3090 / 0x3094 slice structure destination / lines, ++ * written after the structure is built ++ * 0xf slice structure transfer (independent ++ * segments): built in a CPU buffer, copied ++ * by the core into the slice parameter buffer ++ * 0x60000000 ++ * then zero padding to the next 64 bytes, not counted ++ * then one chunk of single register writes (0x000f0000 form) and one ++ * kind-4 packet (mailbox 0x88): entropy registers 0x50xx/0x54xx, the ++ * scaling lists 0x5800.., and the render registers 0x70xx/0x71xx with ++ * the output and reference surfaces. The 0x88 message ends the job. ++ * ++ * Reference slots: the hardware has 15, and every list in the stream ++ * (0x7100 surfaces, 0x5438 long-term bits, 0x5440 POC deltas, the slice ++ * structures' reference indices) names them by number. Apple numbers them ++ * in its own DPB order; this driver uses the V4L2 DPB index. ++ * ++ * Supported: 8-bit 4:2:0 (Main, Main Still Picture), I/P/B, multiple ++ * slices and dependent slice segments, tiles, WPP, long-term references, ++ * weighted prediction, PCM, scaling lists. Refused: other chroma formats ++ * and bit depths (the output layout is not established), range extensions, ++ * more than 15 references. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "apple-vxd-hevc-cmd.h" ++ ++/* Slice-level CMD block (MSVDX command registers). */ ++#define HEVC_CMD_SP_DEST 0x3090 /* slice structure address */ ++#define HEVC_CMD_SP_LINES 0x3094 /* its size in 64-byte lines - 1 */ ++#define HEVC_CMD_FRAGMENT 0x3098 ++#define HEVC_CMD_FORMAT 0x30a0 ++#define HEVC_CMD_SIZE 0x30a8 ++#define HEVC_CMD_SLICE_POS 0x30b0 ++#define HEVC_CMD_RESET 0x3080 ++#define HEVC_CMD_RESET_ZERO 0x3120 /* x4 */ ++#define HEVC_CMD_GENC_SIZE 0x3100 /* x4, interleaved with: */ ++#define HEVC_CMD_GENC_BASE 0x3110 ++#define HEVC_CMD_SPS0 0x3400 ++#define HEVC_CMD_SPS1 0x3404 ++#define HEVC_CMD_PPS0 0x3408 ++#define HEVC_CMD_SEGMENT 0x3410 ++#define HEVC_CMD_SLICE0 0x3414 ++#define HEVC_CMD_TILE_COLS 0x3440 /* x12 */ ++#define HEVC_CMD_TILE_ROWS 0x3480 /* x12 */ ++/* Entropy block. */ ++#define HEVC_ENT_CONTROL 0x5000 ++#define HEVC_ENT_SIZE8 0x5010 ++#define HEVC_ENT_FRAGMENT 0x5030 ++#define HEVC_ENT_GENC_SIZE 0x5040 ++#define HEVC_ENT_GENC_BASE 0x5050 ++#define HEVC_ENT_ABOVE 0x50c0 ++#define HEVC_ENT_SPS0 0x5400 /* .. 0x540c */ ++#define HEVC_ENT_COL_MV 0x5430 ++#define HEVC_ENT_CUR_MV 0x5434 ++#define HEVC_ENT_REFS 0x5438 ++#define HEVC_ENT_COL_REFS 0x543c ++#define HEVC_ENT_POC_DELTA 0x5440 /* x4 */ ++#define HEVC_ENT_COL_POC_DELTA 0x5450 /* x4 */ ++#define HEVC_ENT_TILE_COLS 0x5480 /* x12 */ ++#define HEVC_ENT_TILE_ROWS 0x54c0 /* x12 */ ++#define HEVC_ENT_SCALING 0x5800 ++/* Render block. */ ++#define HEVC_REN_DST 0x700c /* Y, UV */ ++#define HEVC_REN_INTRA 0x7018 ++#define HEVC_REN_CACHE 0x701c ++#define HEVC_REN_WEIGHT_DENOM 0x7024 ++#define HEVC_REN_LAYOUT 0x703c ++#define HEVC_REN_STRIDE 0x7040 ++#define HEVC_REN_PIC_CONFIG 0x70a0 ++#define HEVC_REN_CHROMA_QP 0x70a4 ++#define HEVC_REN_SIZE 0x70b0 ++#define HEVC_REN_SIZE8 0x70b4 ++#define HEVC_REN_REFS 0x7100 /* Y, UV per slot */ ++#define HEVC_REN_CSTRIDE 0x71ac ++#define HEVC_REN_FORMAT 0x71b4 ++#define HEVC_REN_AUXLINE 0x71ec ++#define HEVC_REN_WEIGHTS(l) (0x7200 + 0x100 * (l)) ++#define HEVC_REN_OFFSETS(l) (0x7280 + 0x100 * (l)) ++#define HEVC_REN_7474 0x7474 ++ ++#define HEVC_F_ADDR 0x10 /* 0x000f0000 flag on addresses */ ++ ++/* Largest slice structure: B header, 1 + 2 x 2 x 15 weight pairs, 0x7474 x 2. */ ++#define HEVC_STRUCT_MAX (48 + 8 * 63 + 0x38) ++ ++/* The linear-output layout word (0x62b8b0c with tiling off, aux line on). */ ++#define HEVC_LAYOUT_LINEAR 0x400 ++ ++enum { ++ HEVC_B = V4L2_HEVC_SLICE_TYPE_B, ++ HEVC_P = V4L2_HEVC_SLICE_TYPE_P, ++ HEVC_I = V4L2_HEVC_SLICE_TYPE_I, ++}; ++ ++/* The per-slice parameter block (the kext's P[]), V4L2 names. */ ++struct hevc_slice { ++ bool dependent; ++ u32 type; /* B 0, P 1, I 2 */ ++ u32 address; ++ bool sao_luma, sao_chroma, temporal_mvp, mvd_l1_zero, cabac_init; ++ bool col_from_l0, dbk_disabled, lf_across_slices; ++ u32 l0, l1; /* num_ref_idx_lX_active_minus1 */ ++ u32 col_ref_idx; ++ u32 merge_cand; /* MaxNumMergeCand */ ++ int qp; /* SliceQpY */ ++ int cb_qp, cr_qp, beta, tc; ++ u8 slot[2][15]; /* hardware slot of RefPicListX[i], else 0 */ ++ bool weighted; ++}; ++ ++/* Picture-level state carried across the slices of one picture. */ ++struct hevc_pic { ++ const struct apple_vxd_hevc_frame *f; ++ u32 ctb_mb; /* CtbSizeY / 16 */ ++ u32 w_ctb, h_ctb; /* PicWidth/HeightInCtbsY */ ++ u32 w, h; /* luma size rounded to the minimum CB */ ++ u32 qp_bd; /* QpBdOffsetY */ ++ u32 tile_cols[12], tile_rows[12]; ++ u32 seg_x, seg_y; /* last independent segment, in MBs */ ++ u32 min_type; /* minimum slice type so far (C[13c470]) */ ++ int min_type_q; /* over independent segments, -1 none */ ++ u32 cache_div; /* Q+0x84 */ ++ u32 side_used, transfers, dest; ++ u32 prev_dest, prev_lines; ++ int col_slot; /* -1 none */ ++ bool col_invalid; ++ size_t last_desc; /* word index of the last slice descriptor */ ++}; ++ ++#define SPS_F(sps, n) (!!((sps)->flags & V4L2_HEVC_SPS_FLAG_##n)) ++#define PPS_F(pps, n) (!!((pps)->flags & V4L2_HEVC_PPS_FLAG_##n)) ++#define SLICE_F(s, n) (!!((s)->flags & V4L2_HEVC_SLICE_PARAMS_FLAG_##n)) ++ ++u32 apple_vxd_hevc_genc_size(unsigned int width, unsigned int height) ++{ ++ /* (W/16) * 1152.0 * (H/16) * 0.25, page aligned, capped (0x62b18f8) */ ++ u32 v = DIV_ROUND_UP(width, 16) * 288 * DIV_ROUND_UP(height, 16); ++ ++ return min_t(u32, ALIGN(v, 0x1000), 0x6000000); ++} ++ ++static void hevc_aux_sizes(u32 w, u32 h, struct apple_vxd_hevc_aux_layout *l) ++{ ++ /* CHEVCDecoder::AllocatePictureMemory (0x62b87dc) */ ++ u32 mv = (DIV_ROUND_UP(w, 64) * 0x80 + DIV_ROUND_UP(w, 128) * 0x40) * ++ DIV_ROUND_UP(h, 64); ++ u32 frag = DIV_ROUND_UP(h, 64) * 0x600 + 0x3fc0; ++ u32 above = DIV_ROUND_UP(w, 64) * 0xc0; ++ ++ l->mv = 0; ++ l->frag = ALIGN(mv, 64); ++ l->above = ALIGN(l->frag + frag, 64); ++ l->record = ALIGN(l->above + above, 64); ++ l->size = l->record + ALIGN(sizeof(struct apple_vxd_hevc_record), 64); ++} ++ ++static void hevc_coded_size(const struct v4l2_ctrl_hevc_sps *sps, u32 *w, u32 *h) ++{ ++ u32 mcb = sps->log2_min_luma_coding_block_size_minus3 + 3; ++ ++ *w = (sps->pic_width_in_luma_samples >> mcb) << mcb; ++ *h = (sps->pic_height_in_luma_samples >> mcb) << mcb; ++} ++ ++void apple_vxd_hevc_aux_layout(const struct v4l2_ctrl_hevc_sps *sps, ++ struct apple_vxd_hevc_aux_layout *l) ++{ ++ u32 w, h; ++ ++ hevc_coded_size(sps, &w, &h); ++ hevc_aux_sizes(w, h, l); ++} ++ ++/* An upper bound for any stream that fits a @width x @height CAPTURE. */ ++u32 apple_vxd_hevc_aux_size(unsigned int width, unsigned int height) ++{ ++ struct apple_vxd_hevc_aux_layout l; ++ ++ hevc_aux_sizes(width, height, &l); ++ return l.size; ++} ++ ++int apple_vxd_hevc_validate(const struct v4l2_ctrl_hevc_sps *sps, ++ const struct v4l2_ctrl_hevc_pps *pps, ++ const char **why) ++{ ++ u32 ctb_log2 = sps->log2_min_luma_coding_block_size_minus3 + 3 + ++ sps->log2_diff_max_min_luma_coding_block_size; ++ ++ if (sps->chroma_format_idc != 1) ++ *why = "chroma format other than 4:2:0"; ++ else if (sps->bit_depth_luma_minus8 || sps->bit_depth_chroma_minus8) ++ *why = "bit depth above 8 (Main 10 output layout not established)"; ++ else if (SPS_F(sps, SEPARATE_COLOUR_PLANE)) ++ *why = "separate colour planes"; ++ else if (ctb_log2 < 4 || ctb_log2 > 6 || ++ sps->log2_min_luma_coding_block_size_minus3 > 3 || ++ sps->log2_min_luma_transform_block_size_minus2 > 3 || ++ sps->log2_diff_max_min_luma_transform_block_size > 3 || ++ sps->max_transform_hierarchy_depth_inter > 7 || ++ sps->max_transform_hierarchy_depth_intra > 7) ++ *why = "coding or transform block sizes outside the hardware fields"; ++ else if (!sps->pic_width_in_luma_samples || !sps->pic_height_in_luma_samples || ++ sps->pic_width_in_luma_samples > 4096 || ++ sps->pic_height_in_luma_samples > 4096) ++ *why = "picture size outside the hardware fields"; ++ else if (PPS_F(pps, TILES_ENABLED) && ++ (pps->num_tile_columns_minus1 > 19 || pps->num_tile_rows_minus1 > 21)) ++ *why = "tile counts out of range"; ++ else if (pps->diff_cu_qp_delta_depth > 3 || pps->log2_parallel_merge_level_minus2 > 7) ++ *why = "PPS depth fields out of range"; ++ else ++ return 0; ++ return -EINVAL; ++} ++ ++/* --- locating the slices --------------------------------------------- */ ++ ++static bool hevc_is_sc(const u8 *p, size_t i, size_t len) ++{ ++ return i + 3 <= len && !p[i] && !p[i + 1] && p[i + 2] == 1; ++} ++ ++/* ++ * Frame-based Annex-B: the OUTPUT buffer holds the picture's slice NALs, ++ * each behind a start code, in slice order. The i-th slice NAL is the ++ * i-th VCL NAL found. data_byte_offset counts RBSP header bytes, and ++ * clients disagree on its base: FFmpeg and libva-v4l2-request count from ++ * the buffer start, GStreamer from the slice's start code. Either way the ++ * header ends inside this NAL, which tells them apart. ++ */ ++int apple_vxd_hevc_find_nals(const u8 *src, size_t len, ++ const struct v4l2_ctrl_hevc_slice_params *slices, ++ unsigned int nslices, struct apple_vxd_hevc_nal *nals, ++ const char **why) ++{ ++ size_t i = 0, sc, start, end; ++ unsigned int n = 0; ++ ++ while (n < nslices) { ++ u32 hdr; ++ ++ while (i < len && !hevc_is_sc(src, i, len)) ++ i++; ++ if (i >= len) { ++ *why = "fewer slice NALs in the OUTPUT buffer than SLICE_PARAMS"; ++ return -EINVAL; ++ } ++ sc = i; ++ start = i + 3; ++ for (end = start; end < len && !hevc_is_sc(src, end, len); end++) ++ ; ++ i = end; ++ while (end > start && !src[end - 1]) ++ end--; ++ if (end - start < 3 || ((src[start] >> 1) & 0x3f) > 31) ++ continue; /* not a VCL NAL */ ++ ++ if (slices[n].data_byte_offset > start && ++ slices[n].data_byte_offset < end) { ++ hdr = slices[n].data_byte_offset - start; ++ } else if (slices[n].data_byte_offset >= start - sc + 2 && ++ slices[n].data_byte_offset < end - sc) { ++ hdr = slices[n].data_byte_offset - (start - sc); ++ } else { ++ *why = "data_byte_offset outside its slice NAL"; ++ return -EINVAL; ++ } ++ if (hdr < 3) { ++ *why = "slice header shorter than the NAL header"; ++ return -EINVAL; ++ } ++ nals[n].offset = start; ++ nals[n].size = end - start; ++ nals[n].header_bits = 8 * hdr; ++ n++; ++ } ++ return 0; ++} ++ ++/* --- per-picture words (0x62b2bb8 / 0x62b38d0 / 0x62b7a18) ------------ */ ++ ++static u32 hevc_sps0(const struct v4l2_ctrl_hevc_sps *sps) ++{ ++ return sps->max_transform_hierarchy_depth_intra << 28 | ++ sps->max_transform_hierarchy_depth_inter << 24 | ++ (sps->log2_diff_max_min_luma_transform_block_size & 3) << 20 | ++ (sps->log2_min_luma_transform_block_size_minus2 & 3) << 16 | ++ (sps->log2_diff_max_min_luma_coding_block_size & 3) << 12 | ++ (sps->log2_min_luma_coding_block_size_minus3 & 3) << 8 | ++ (sps->bit_depth_chroma_minus8 & 7) << 4 | ++ (sps->bit_depth_luma_minus8 & 7); ++} ++ ++/* PCM fields are zero without PCM, which makes the depths read 1. */ ++static u32 hevc_sps1(const struct v4l2_ctrl_hevc_sps *sps) ++{ ++ bool pcm = SPS_F(sps, PCM_ENABLED); ++ u32 v; ++ ++ v = ((pcm ? sps->pcm_sample_bit_depth_chroma_minus1 & 15 : 0) + 1) << 12 | ++ ((pcm ? sps->pcm_sample_bit_depth_luma_minus1 & 15 : 0) + 1) << 8; ++ if (pcm) ++ v |= (sps->log2_diff_max_min_pcm_luma_coding_block_size & 3) << 6 | ++ (sps->log2_min_pcm_luma_coding_block_size_minus3 & 3) << 4 | ++ SPS_F(sps, PCM_LOOP_FILTER_DISABLED) << 2; ++ return v | pcm << 1 | SPS_F(sps, AMP_ENABLED); ++} ++ ++static u32 hevc_tiles(const struct v4l2_ctrl_hevc_pps *pps) ++{ ++ bool tiles = PPS_F(pps, TILES_ENABLED); ++ ++ return (tiles ? pps->num_tile_rows_minus1 : 0) << 24 | ++ (tiles ? pps->num_tile_columns_minus1 : 0) << 16 | ++ PPS_F(pps, ENTROPY_CODING_SYNC_ENABLED) << 10 | ++ tiles << 9 | PPS_F(pps, TRANSQUANT_BYPASS_ENABLED) << 8; ++} ++ ++static u32 hevc_pic_config(const struct hevc_pic *p) ++{ ++ const struct v4l2_ctrl_hevc_sps *sps = p->f->sps; ++ ++ return (p->min_type == HEVC_I) << 30 | ++ (sps->log2_min_luma_coding_block_size_minus3 + ++ sps->log2_diff_max_min_luma_coding_block_size) << 28 | ++ 0x1c0000 | (sps->bit_depth_chroma_minus8 & 7) << 8 | ++ (sps->bit_depth_luma_minus8 & 7) << 4 | sps->chroma_format_idc; ++} ++ ++/* ++ * Tile boundaries in MBs, two 16-bit halves per word: cumulative explicit ++ * sizes, or the kext's float32 accumulation of PicSizeInCtbs / (n + 1), ++ * truncated per step. The integer form below is the H.265 6.5.1 one, which ++ * the float form equals whenever it is exact (shift by CtbLog2 - 4). ++ */ ++static void hevc_tile_bounds(const struct v4l2_ctrl_hevc_pps *pps, u32 shift, ++ u32 w_ctb, u32 h_ctb, u32 *cols, u32 *rows) ++{ ++ unsigned int k, n; ++ u32 acc; ++ ++ memset(cols, 0, 12 * sizeof(*cols)); ++ memset(rows, 0, 12 * sizeof(*rows)); ++ if (!PPS_F(pps, TILES_ENABLED)) ++ return; ++ ++ n = pps->num_tile_columns_minus1; ++ for (k = 0, acc = 0; k < n; k++) { ++ if (PPS_F(pps, UNIFORM_SPACING)) ++ acc = (k + 1) * w_ctb / (n + 1); ++ else ++ acc += pps->column_width_minus1[k] + 1; ++ cols[k / 2] |= (acc << shift) << (16 * (k & 1)); ++ } ++ n = pps->num_tile_rows_minus1; ++ for (k = 0, acc = 0; k < n; k++) { ++ if (PPS_F(pps, UNIFORM_SPACING)) ++ acc = (k + 1) * h_ctb / (n + 1); ++ else ++ acc += pps->row_height_minus1[k] + 1; ++ rows[k / 2] |= (acc << shift) << (16 * (k & 1)); ++ } ++} ++ ++/* --- per slice -------------------------------------------------------- */ ++ ++static int hevc_slice_params(const struct apple_vxd_hevc_frame *f, ++ unsigned int idx, struct hevc_slice *s, ++ const char **why) ++{ ++ const struct v4l2_ctrl_hevc_slice_params *sp = &f->slices[idx]; ++ const struct v4l2_ctrl_hevc_pps *pps = f->pps; ++ unsigned int i, lst; ++ ++ memset(s, 0, sizeof(*s)); ++ s->address = sp->slice_segment_addr; ++ s->dependent = SLICE_F(sp, DEPENDENT_SLICE_SEGMENT); ++ if (s->dependent) { ++ /* ++ * The kext parses only the address of a dependent segment and ++ * builds its words from a zeroed record (slice type B, no ++ * references, MaxNumMergeCand 5). The core takes the rest ++ * from the independent segment. ++ */ ++ s->merge_cand = 5; ++ s->qp = 26 + pps->init_qp_minus26; ++ return 0; ++ } ++ ++ s->type = sp->slice_type; ++ if (s->type > HEVC_I) { ++ *why = "invalid slice type"; ++ return -EINVAL; ++ } ++ s->sao_luma = SLICE_F(sp, SLICE_SAO_LUMA); ++ s->sao_chroma = SLICE_F(sp, SLICE_SAO_CHROMA); ++ s->temporal_mvp = SLICE_F(sp, SLICE_TEMPORAL_MVP_ENABLED); ++ s->mvd_l1_zero = SLICE_F(sp, MVD_L1_ZERO); ++ s->cabac_init = SLICE_F(sp, CABAC_INIT); ++ /* Only B slices code it; the kext's default is 1. */ ++ s->col_from_l0 = s->type != HEVC_B || SLICE_F(sp, COLLOCATED_FROM_L0); ++ s->dbk_disabled = SLICE_F(sp, SLICE_DEBLOCKING_FILTER_DISABLED); ++ s->lf_across_slices = SLICE_F(sp, SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED); ++ /* I slices carry the PPS defaults, P slices the PPS L1 default. */ ++ s->l0 = s->type == HEVC_I ? pps->num_ref_idx_l0_default_active_minus1 : ++ sp->num_ref_idx_l0_active_minus1; ++ s->l1 = s->type == HEVC_B ? sp->num_ref_idx_l1_active_minus1 : ++ pps->num_ref_idx_l1_default_active_minus1; ++ if (s->l0 > 14 || s->l1 > 14) { ++ *why = "more than 15 active references"; ++ return -EINVAL; ++ } ++ s->col_ref_idx = s->temporal_mvp ? sp->collocated_ref_idx : 0; ++ s->merge_cand = 5 - (s->type == HEVC_I ? 0 : sp->five_minus_max_num_merge_cand); ++ s->qp = 26 + pps->init_qp_minus26 + sp->slice_qp_delta; ++ s->cb_qp = sp->slice_cb_qp_offset; ++ s->cr_qp = sp->slice_cr_qp_offset; ++ s->beta = sp->slice_beta_offset_div2; ++ s->tc = sp->slice_tc_offset_div2; ++ s->weighted = (PPS_F(pps, WEIGHTED_PRED) && s->type == HEVC_P) || ++ (PPS_F(pps, WEIGHTED_BIPRED) && s->type == HEVC_B); ++ ++ for (lst = 0; s->type != HEVC_I && lst < (s->type == HEVC_B ? 2 : 1); lst++) { ++ const u8 *ref = lst ? sp->ref_idx_l1 : sp->ref_idx_l0; ++ u32 n = lst ? s->l1 : s->l0; ++ ++ for (i = 0; i <= n; i++) { ++ if (ref[i] >= f->dp->num_active_dpb_entries) { ++ *why = "a reference index names no DPB entry"; ++ return -EINVAL; ++ } ++ s->slot[lst][i] = ref[i]; ++ } ++ } ++ if (s->type != HEVC_I && s->col_ref_idx > (s->col_from_l0 ? s->l0 : s->l1)) { ++ *why = "collocated_ref_idx out of range"; ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/* The slice structure and its 0xf transfer (0x62b6010). */ ++static void hevc_slice_struct(struct apple_vxd_cmdbuf *cb, struct hevc_pic *p, ++ const struct hevc_slice *s, ++ const struct v4l2_ctrl_hevc_slice_params *sp, ++ size_t w3090, size_t w3094) ++{ ++ const struct apple_vxd_hevc_frame *f = p->f; ++ const struct v4l2_ctrl_hevc_sps *sps = f->sps; ++ const struct v4l2_ctrl_hevc_pps *pps = f->pps; ++ const struct v4l2_hevc_pred_weight_table *pw = &sp->pred_weight_table; ++ u32 hdr = s->type == HEVC_B ? 48 : s->type == HEVC_P ? 32 : 16; ++ u32 *w = (u32 *)(f->side_cpu + p->side_used); ++ u32 *pairs = w + hdr / 4; ++ u32 npairs = 0, pad, unpadded, transfer, lines, c320, c321; ++ unsigned int i, lst; ++ ++ memset(w, 0, hdr); ++#define PAIR(r, v) \ ++ do { \ ++ pairs[2 * npairs] = VXD_CMD_REG1 | (r); \ ++ pairs[2 * npairs + 1] = (v); \ ++ npairs++; \ ++ } while (0) ++ if (s->weighted) { ++ u32 cden = pw->luma_log2_weight_denom + pw->delta_chroma_log2_weight_denom; ++ ++ PAIR(HEVC_REN_WEIGHT_DENOM, pw->luma_log2_weight_denom | cden << 4); ++ for (lst = 0; lst < (s->type == HEVC_B ? 2 : 1); lst++) { ++ const s8 *dlw = lst ? pw->delta_luma_weight_l1 : pw->delta_luma_weight_l0; ++ const s8 *lo = lst ? pw->luma_offset_l1 : pw->luma_offset_l0; ++ const s8 (*dcw)[2] = lst ? pw->delta_chroma_weight_l1 : ++ pw->delta_chroma_weight_l0; ++ const s8 (*co)[2] = lst ? pw->chroma_offset_l1 : pw->chroma_offset_l0; ++ ++ for (i = 0; i <= (lst ? s->l1 : s->l0); i++) { ++ PAIR(HEVC_REN_WEIGHTS(lst) + 4 * i, ++ ((u32)dlw[i] & 0x1ff) | ((u32)dcw[i][0] & 0x1ff) << 9 | ++ ((u32)dcw[i][1] & 0x1ff) << 18); ++ PAIR(HEVC_REN_OFFSETS(lst) + 4 * i, ++ (u8)lo[i] | (u32)(u8)co[i][0] << 8 | ++ (u32)(u8)co[i][1] << 16); ++ } ++ } ++ } ++ if (f->caps->tail_7474) { ++ PAIR(HEVC_REN_7474, 0); ++ PAIR(HEVC_REN_7474, 0); ++ } ++#undef PAIR ++ ++ c320 = PPS_F(pps, CONSTRAINED_INTRA_PRED) << 7 | ++ SPS_F(sps, STRONG_INTRA_SMOOTHING_ENABLED) << 6 | ++ SPS_F(sps, SAMPLE_ADAPTIVE_OFFSET) << 5 | s->sao_chroma << 3 | ++ s->sao_luma << 2 | ++ (PPS_F(pps, WEIGHTED_BIPRED) && s->type == HEVC_B) << 1 | ++ (PPS_F(pps, WEIGHTED_PRED) && s->type == HEVC_P); ++ c321 = PPS_F(pps, LOOP_FILTER_ACROSS_TILES_ENABLED) << 2 | ++ s->lf_across_slices << 1 | s->dbk_disabled; ++ ++ w[0] = s->type | s->sao_luma << 2 | s->sao_chroma << 3 | s->temporal_mvp << 4 | ++ s->l0 << 8 | s->l1 << 12 | s->mvd_l1_zero << 16 | s->col_from_l0 << 18 | ++ s->merge_cand << 20; ++ w[1] = s->lf_across_slices << 24 | ((p->qp_bd + s->qp) & 0x7f) | ++ ((u32)s->cb_qp & 31) << 8 | ((u32)s->cr_qp & 31) << 16; ++ w[2] = p->seg_y << 10 | p->seg_x | npairs << 20; ++ w[3] = c320 << 16 | c321 << 12 | ((u32)s->tc & 15) << 8 | ++ ((u32)s->beta & 15) << 4 | 8 | (2 - s->type); ++ for (lst = 0; s->type != HEVC_I && lst < (s->type == HEVC_B ? 2 : 1); lst++) ++ for (i = 0; i < 15; i++) ++ w[4 + 4 * lst + i / 4] |= (u32)s->slot[lst][i] << (8 * (i % 4)); ++ ++ pad = (npairs * 0x38 - hdr) & 0x38; ++ unpadded = hdr + 8 * npairs; ++ memset((u8 *)w + unpadded, 0, pad); ++ transfer = unpadded + pad; ++ lines = unpadded - 1 > 0x3bf ? 15 : (unpadded - 1) >> 6; ++ ++ /* Q+0x7c (minimum independent slice type) and Q+0x84 (cache hint). */ ++ if (!s->address || p->min_type_q < 0 || (int)s->type < p->min_type_q) { ++ u32 d = 0; ++ ++ if (s->type == HEVC_P && pps->num_ref_idx_l0_default_active_minus1) ++ d = abs((int)s->slot[0][0] - (int)s->slot[0][1]); ++ else if (s->type == HEVC_B) ++ d = abs((int)s->slot[0][0] - (int)s->slot[1][0]); ++ p->cache_div = max(p->cache_div, d); ++ p->min_type_q = s->type; ++ } ++ ++ if (!p->transfers) ++ p->dest = f->spdest; ++ apple_vxd_cmd_table_xfer(cb, transfer, f->side + p->side_used, p->dest); ++ cb->vaddr[w3090] = p->dest; ++ cb->vaddr[w3094] = lines; ++ p->prev_dest = p->dest; ++ p->prev_lines = lines; ++ p->dest += transfer; ++ p->transfers++; ++ p->side_used += transfer; ++ ++ /* The co-located picture: the first P/B slice decides (L28666). */ ++ if (SPS_F(sps, SPS_TEMPORAL_MVP_ENABLED) && p->col_slot < 0 && ++ !p->col_invalid && s->type != HEVC_I) { ++ u8 slot = s->slot[s->col_from_l0 ? 0 : 1][s->col_ref_idx]; ++ ++ if (f->refs[slot].rec && f->refs[slot].rec->valid) ++ p->col_slot = slot; ++ else ++ p->col_invalid = true; ++ } ++} ++ ++static int hevc_emit_slice(struct apple_vxd_cmdbuf *cb, struct hevc_pic *p, ++ unsigned int idx, const char **why) ++{ ++ const struct apple_vxd_hevc_frame *f = p->f; ++ const struct v4l2_ctrl_hevc_sps *sps = f->sps; ++ const struct v4l2_ctrl_hevc_pps *pps = f->pps; ++ const struct v4l2_ctrl_hevc_slice_params *sp = &f->slices[idx]; ++ const struct apple_vxd_hevc_nal *nal = &f->nals[idx]; ++ u32 hdr[VXD_CMD_SLICE_BLOCK_WORDS - 1] = { }; ++ u32 row, col_mb, row_mb, w3410, w3414; ++ size_t w3090, w3094; ++ struct hevc_slice s; ++ unsigned int k; ++ int ret; ++ ++ ret = hevc_slice_params(f, idx, &s, why); ++ if (ret) ++ return ret; ++ if (!s.dependent && p->side_used + HEVC_STRUCT_MAX > f->side_size) { ++ *why = "slice structure buffer full"; ++ return -ENOSPC; ++ } ++ ++ p->min_type = idx ? min(p->min_type, s.type) : s.type; ++ ++ /* Segment position in MBs (0x62b55f0). */ ++ row = p->w_ctb ? s.address / p->w_ctb : 0; ++ col_mb = (s.address - row * p->w_ctb) * p->ctb_mb; ++ row_mb = row * p->ctb_mb; ++ if (!s.dependent) { ++ p->seg_x = col_mb; ++ p->seg_y = row_mb; ++ } ++ w3410 = s.dependent << 31 | (p->seg_y & 0xfff) << 16 | (p->seg_x & 0xfff); ++ w3414 = ((p->qp_bd + s.qp) & 0x7f) << 24 | s.merge_cand << 20 | ++ s.cabac_init << 17 | s.mvd_l1_zero << 16 | s.l1 << 12 | s.l0 << 8 | ++ s.sao_chroma << 3 | s.sao_luma << 2 | s.type; ++ ++ p->last_desc = cb->used / 4; ++ hdr[3] = row_mb << 16 | col_mb; ++ hdr[4] = (DIV_ROUND_UP(p->w, 16) - 1) | (DIV_ROUND_UP(p->h, 16) - 1) << 16; ++ apple_vxd_cmd_ctrl_alloc(cb, hdr); ++ ++ apple_vxd_cmd_slice_desc(cb, nal->header_bits, nal->size); ++ apple_vxd_cmd_load(cb, f->src + nal->offset, nal->size); ++ ++ apple_vxd_cmd_group_open(cb, 0); ++ apple_vxd_cmd_reg(cb, HEVC_CMD_FRAGMENT, f->frag & ~63U); ++ apple_vxd_cmd_reg(cb, HEVC_CMD_FORMAT, sps->chroma_format_idc << 16 | 0x10c); ++ apple_vxd_cmd_reg(cb, HEVC_CMD_SIZE, (p->h - 1) << 16 | (p->w - 1)); ++ apple_vxd_cmd_group_close(cb); ++ apple_vxd_cmd_group_open(cb, 0); ++ apple_vxd_cmd_reg(cb, HEVC_CMD_SLICE_POS, row_mb << 16 | col_mb); ++ apple_vxd_cmd_reg(cb, HEVC_CMD_SEGMENT, w3410); ++ apple_vxd_cmd_group_close(cb); ++ if (f->first_slice_of_session && !idx) { ++ apple_vxd_cmd_group_open(cb, 0); ++ for (k = 0; k < 4; k++) ++ apple_vxd_cmd_reg(cb, HEVC_CMD_RESET_ZERO + 4 * k, 0); ++ apple_vxd_cmd_reg(cb, HEVC_CMD_RESET, 1); ++ apple_vxd_cmd_group_close(cb); ++ } ++ apple_vxd_cmd_group_open(cb, 0); ++ apple_vxd_cmd_reg(cb, HEVC_CMD_SPS0, hevc_sps0(sps)); ++ apple_vxd_cmd_reg(cb, HEVC_CMD_SPS1, hevc_sps1(sps)); ++ apple_vxd_cmd_reg(cb, HEVC_CMD_PPS0, hevc_tiles(pps) | ++ (pps->diff_cu_qp_delta_depth & 3) << 4 | ++ PPS_F(pps, CU_QP_DELTA_ENABLED) << 3 | ++ PPS_F(pps, TRANSFORM_SKIP_ENABLED) << 2 | ++ PPS_F(pps, SIGN_DATA_HIDING_ENABLED)); ++ apple_vxd_cmd_group_close(cb); ++ apple_vxd_cmd_group_open(cb, 0); ++ for (k = 0; k < 12; k++) ++ apple_vxd_cmd_reg(cb, HEVC_CMD_TILE_COLS + 4 * k, p->tile_cols[k]); ++ for (k = 0; k < 12; k++) ++ apple_vxd_cmd_reg(cb, HEVC_CMD_TILE_ROWS + 4 * k, p->tile_rows[k]); ++ apple_vxd_cmd_group_close(cb); ++ apple_vxd_cmd_group_open(cb, 0); ++ apple_vxd_cmd_reg(cb, HEVC_CMD_SLICE0, w3414); ++ apple_vxd_cmd_group_close(cb); ++ apple_vxd_cmd_group_open(cb, 0); ++ for (k = 0; k < APPLE_VXD_HEVC_GENC_BUFS; k++) { ++ apple_vxd_cmd_reg(cb, HEVC_CMD_GENC_SIZE + 4 * k, f->genc_size & ~0xfffU); ++ apple_vxd_cmd_reg(cb, HEVC_CMD_GENC_BASE + 4 * k, f->genc[k] & ~0xfffU); ++ } ++ apple_vxd_cmd_group_close(cb); ++ ++ /* 0x3090/0x3094 first, filled in once the structure exists. */ ++ apple_vxd_cmd_group_open(cb, 0); ++ apple_vxd_cmd_reg(cb, HEVC_CMD_SP_DEST, 0); ++ w3090 = cb->used / 4 - 1; ++ apple_vxd_cmd_reg(cb, HEVC_CMD_SP_LINES, 0); ++ w3094 = cb->used / 4 - 1; ++ apple_vxd_cmd_group_close(cb); ++ if (cb->error) ++ return -ENOSPC; ++ if (s.dependent) { ++ cb->vaddr[w3090] = p->prev_dest; ++ cb->vaddr[w3094] = p->prev_lines; ++ } else { ++ hevc_slice_struct(cb, p, &s, sp, w3090, w3094); ++ } ++ apple_vxd_cmd_end_slice(cb); ++ return cb->error ? -ENOSPC : 0; ++} ++ ++/* --- per picture: the kind-4 chunk (0x62b7ab0, 0x62b859c) -------------- */ ++ ++/* ++ * Scaling lists: the 8x8 base lists one byte per coefficient, four per ++ * register; register i takes coefficients 4i..4i+3 of a de-interleaved ++ * order in which the even index bits give the row and the odd bits the ++ * column (the kext's gather tables at 0xfffffff005595040/0x5595050). ++ */ ++static u32 hevc_scl_word(const u8 *raster, unsigned int n, unsigned int i) ++{ ++ unsigned int k, l, row, col, side = n == 16 ? 8 : 4; ++ u32 v = 0; ++ ++ for (k = 0; k < 4; k++) { ++ l = 4 * i + k; ++ row = (l & 1) | (l >> 1 & 2) | (l >> 2 & 4); ++ col = (l >> 1 & 1) | (l >> 2 & 2) | (l >> 3 & 4); ++ if (side == 4) { ++ row &= 3; ++ col &= 3; ++ } ++ v |= (u32)raster[row * side + col] << (8 * k); ++ } ++ return v; ++} ++ ++static void hevc_scl_list(struct apple_vxd_cmdbuf *cb, u32 reg, const u8 *raster, ++ unsigned int n) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < n; i++) ++ apple_vxd_cmd_reg1(cb, reg + 4 * i, hevc_scl_word(raster, n, i), 0); ++} ++ ++static void hevc_emit_scaling(struct apple_vxd_cmdbuf *cb, ++ const struct v4l2_ctrl_hevc_scaling_matrix *m) ++{ ++ unsigned int i; ++ ++ hevc_scl_list(cb, HEVC_ENT_SCALING + 0x000, m->scaling_list_32x32[0], 16); ++ hevc_scl_list(cb, HEVC_ENT_SCALING + 0x040, m->scaling_list_32x32[1], 16); ++ for (i = 0; i < 6; i++) ++ hevc_scl_list(cb, HEVC_ENT_SCALING + 0x080 + 0x40 * i, ++ m->scaling_list_16x16[i], 16); ++ for (i = 0; i < 6; i++) ++ hevc_scl_list(cb, HEVC_ENT_SCALING + 0x200 + 0x40 * i, ++ m->scaling_list_8x8[i], 16); ++ for (i = 0; i < 6; i++) ++ hevc_scl_list(cb, HEVC_ENT_SCALING + 0x380 + 0x10 * i, ++ m->scaling_list_4x4[i], 4); ++ apple_vxd_cmd_reg1(cb, HEVC_ENT_SCALING + 0x3e0, m->scaling_list_dc_coef_32x32[0], 0); ++ apple_vxd_cmd_reg1(cb, HEVC_ENT_SCALING + 0x3e4, m->scaling_list_dc_coef_32x32[1], 0); ++ for (i = 0; i < 6; i++) ++ apple_vxd_cmd_reg1(cb, HEVC_ENT_SCALING + 0x3e8 + 4 * i, ++ m->scaling_list_dc_coef_16x16[i], 0); ++} ++ ++/* 0x701c: Apple's line-buffer split for 4:2:0 (13d684 set at create). */ ++static u32 hevc_cache_policy(const struct hevc_pic *p) ++{ ++ u32 t = (p->f->caps->cache_base * 0x15556) >> 17; ++ ++ t = (t * 15) >> 5; ++ if (p->cache_div > 1) ++ t /= p->cache_div; ++ return 1 << 24 | ALIGN(t, 8) << 12 | 12; ++} ++ ++static void hevc_emit_picture(struct apple_vxd_cmdbuf *cb, struct hevc_pic *p) ++{ ++ const struct apple_vxd_hevc_frame *f = p->f; ++ const struct v4l2_ctrl_hevc_sps *sps = f->sps; ++ const struct v4l2_ctrl_hevc_pps *pps = f->pps; ++ const struct v4l2_ctrl_hevc_decode_params *dp = f->dp; ++ u32 min_type = p->min_type_q < 0 ? 0 : p->min_type_q; ++ u32 lt = 0, delta[4] = { }, size8, w5400; ++ unsigned int k; ++ ++ size8 = ((ALIGN(p->h, 8) - 1) << 16) | (ALIGN(p->w, 8) - 1); ++ w5400 = hevc_sps0(sps) | SPS_F(sps, SCALING_LIST_ENABLED) << 31; ++ ++ apple_vxd_cmd_reg1(cb, HEVC_ENT_ABOVE, f->above & ~63U, HEVC_F_ADDR); ++ apple_vxd_cmd_reg1(cb, HEVC_ENT_SIZE8, size8, 0); ++ apple_vxd_cmd_reg1(cb, HEVC_ENT_SPS0, w5400, 0); ++ apple_vxd_cmd_reg1(cb, HEVC_ENT_SPS0 + 4, hevc_sps1(sps) | ++ SPS_F(sps, SPS_TEMPORAL_MVP_ENABLED) << 16, 0); ++ apple_vxd_cmd_reg1(cb, HEVC_ENT_SPS0 + 8, hevc_tiles(pps) | ++ PPS_F(pps, TRANSFORM_SKIP_ENABLED) << 2, 0); ++ apple_vxd_cmd_reg1(cb, HEVC_ENT_SPS0 + 12, ++ (pps->log2_parallel_merge_level_minus2 & 7) << 16 | ++ ((u32)pps->pps_cr_qp_offset & 31) << 8 | ++ ((u32)pps->pps_cb_qp_offset & 31), 0); ++ for (k = 0; k < APPLE_VXD_HEVC_GENC_BUFS; k++) { ++ apple_vxd_cmd_reg1(cb, HEVC_ENT_GENC_SIZE + 4 * k, f->genc_size & ~0xfffU, 0); ++ apple_vxd_cmd_reg1(cb, HEVC_ENT_GENC_BASE + 4 * k, f->genc[k] & ~0xfffU, ++ HEVC_F_ADDR); ++ } ++ apple_vxd_cmd_reg1(cb, HEVC_ENT_FRAGMENT, f->frag & ~63U, HEVC_F_ADDR); ++ for (k = 0; k < 12; k++) ++ apple_vxd_cmd_reg1(cb, HEVC_ENT_TILE_COLS + 4 * k, p->tile_cols[k], 0); ++ for (k = 0; k < 12; k++) ++ apple_vxd_cmd_reg1(cb, HEVC_ENT_TILE_ROWS + 4 * k, p->tile_rows[k], 0); ++ ++ for (k = 0; k < dp->num_active_dpb_entries; k++) { ++ const struct v4l2_hevc_dpb_entry *e = &dp->dpb[k]; ++ s32 d = clamp_t(s32, dp->pic_order_cnt_val - e->pic_order_cnt_val, -128, 127); ++ ++ if (e->flags & V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE) ++ lt |= BIT(k); ++ delta[k / 4] |= (u32)(u8)d << (8 * (k % 4)); ++ } ++ f->record->refs = lt | (2 - min_type) << 16; ++ memcpy(f->record->poc_delta, delta, sizeof(delta)); ++ f->record->mv = f->mv; ++ f->record->valid = 1; ++ apple_vxd_cmd_reg1(cb, HEVC_ENT_REFS, f->record->refs, 0); ++ for (k = 0; k < 4; k++) ++ apple_vxd_cmd_reg1(cb, HEVC_ENT_POC_DELTA + 4 * k, delta[k], 0); ++ apple_vxd_cmd_reg1(cb, HEVC_ENT_CUR_MV, f->mv, HEVC_F_ADDR); ++ if (p->col_slot < 0) { ++ apple_vxd_cmd_reg1(cb, HEVC_ENT_COL_REFS, 0, 0); ++ } else { ++ const struct apple_vxd_hevc_record *r = f->refs[p->col_slot].rec; ++ ++ apple_vxd_cmd_reg1(cb, HEVC_ENT_COL_REFS, r->refs, 0); ++ for (k = 0; k < 4; k++) ++ apple_vxd_cmd_reg1(cb, HEVC_ENT_COL_POC_DELTA + 4 * k, ++ r->poc_delta[k], 0); ++ apple_vxd_cmd_reg1(cb, HEVC_ENT_COL_MV, r->mv, HEVC_F_ADDR); ++ } ++ apple_vxd_cmd_reg1(cb, HEVC_ENT_CONTROL, ++ sps->chroma_format_idc << 28 | 0x10c0000, 0); ++ if (SPS_F(sps, SCALING_LIST_ENABLED)) ++ hevc_emit_scaling(cb, f->sm); ++ ++ apple_vxd_cmd_reg1(cb, HEVC_REN_PIC_CONFIG, hevc_pic_config(p), 0); ++ apple_vxd_cmd_reg1(cb, HEVC_REN_CHROMA_QP, ((u32)pps->pps_cb_qp_offset & 31) | ++ ((u32)pps->pps_cr_qp_offset & 31) << 8, 0); ++ apple_vxd_cmd_reg1(cb, HEVC_REN_SIZE, (p->h - 1) << 16 | (p->w - 1), 0); ++ apple_vxd_cmd_reg1(cb, HEVC_REN_SIZE8, size8, 0); ++ apple_vxd_cmd_reg1(cb, HEVC_REN_AUXLINE, f->auxline, HEVC_F_ADDR); ++ apple_vxd_cmd_reg1(cb, HEVC_REN_CACHE, hevc_cache_policy(p), 0); ++ apple_vxd_cmd_reg1(cb, HEVC_REN_STRIDE, f->dst_stride & ~63U, 0); ++ apple_vxd_cmd_reg1(cb, HEVC_REN_FORMAT, (sps->bit_depth_chroma_minus8 & 7) << 12 | ++ (sps->bit_depth_luma_minus8 & 7) << 8 | sps->chroma_format_idc, 0); ++ apple_vxd_cmd_reg1(cb, HEVC_REN_LAYOUT, HEVC_LAYOUT_LINEAR, 0); ++ apple_vxd_cmd_reg1(cb, HEVC_REN_CSTRIDE, f->dst_cstride | f->dst_cstride << 16, 0); ++ apple_vxd_cmd_reg1(cb, HEVC_REN_DST, f->dst_luma, HEVC_F_ADDR); ++ apple_vxd_cmd_reg1(cb, HEVC_REN_DST + 4, f->dst_chroma, HEVC_F_ADDR); ++ if (min_type < HEVC_I) { ++ for (k = 0; k < dp->num_active_dpb_entries; k++) { ++ apple_vxd_cmd_reg1(cb, HEVC_REN_REFS + 8 * k, f->refs[k].luma, ++ HEVC_F_ADDR); ++ apple_vxd_cmd_reg1(cb, HEVC_REN_REFS + 8 * k + 4, f->refs[k].chroma, ++ HEVC_F_ADDR); ++ } ++ } ++ apple_vxd_cmd_reg1(cb, HEVC_REN_INTRA, f->intra, HEVC_F_ADDR); ++ ++ /* The last slice descriptor learns the strides and the layout. */ ++ cb->vaddr[p->last_desc + 3] = f->dst_cstride | f->dst_cstride << 16; ++ cb->vaddr[p->last_desc + 6] = 0; ++ cb->vaddr[p->last_desc + 7] = 0; ++ cb->vaddr[p->last_desc + 8] = HEVC_LAYOUT_LINEAR; ++} ++ ++/* ++ * One picture: the slice chunks with their kind-1 packets, then the kind-4 ++ * chunk and packet. Returns 0, or -EINVAL / -ENOSPC with @why set. ++ */ ++int apple_vxd_hevc_build(struct apple_vxd_cmdbuf *cb, ++ const struct apple_vxd_hevc_frame *f, ++ const char **why) ++{ ++ const struct v4l2_ctrl_hevc_sps *sps = f->sps; ++ struct hevc_pic p = { .f = f, .col_slot = -1, .min_type_q = -1 }; ++ u32 ctb_log2, mcb, off, bytes, *pkt; ++ unsigned int i, k; ++ int ret; ++ ++ *why = NULL; ++ ret = apple_vxd_hevc_validate(sps, f->pps, why); ++ if (ret) ++ return ret; ++ if (!f->nslices || f->nslices > APPLE_VXD_HEVC_MAX_SLICES) { ++ *why = "no slices, or more than a job holds"; ++ return -EINVAL; ++ } ++ if (f->dp->num_active_dpb_entries > APPLE_VXD_HEVC_MAX_REFS) { ++ *why = "more than 15 reference pictures"; ++ return -EINVAL; ++ } ++ for (k = 0; k < f->dp->num_active_dpb_entries; k++) { ++ if (!f->refs[k].luma) { ++ *why = "a DPB entry has no CAPTURE buffer"; ++ return -EINVAL; ++ } ++ } ++ if (SPS_F(sps, SCALING_LIST_ENABLED) && (!f->sm || !f->sm->scaling_list_4x4[0][0])) { ++ *why = "scaling lists enabled but the SCALING_MATRIX control is not set"; ++ return -EINVAL; ++ } ++ ++ mcb = sps->log2_min_luma_coding_block_size_minus3 + 3; ++ ctb_log2 = mcb + sps->log2_diff_max_min_luma_coding_block_size; ++ hevc_coded_size(sps, &p.w, &p.h); ++ p.ctb_mb = 1 << (ctb_log2 - 4); ++ p.w_ctb = DIV_ROUND_UP(p.w, 1 << ctb_log2); ++ p.h_ctb = DIV_ROUND_UP(p.h, 1 << ctb_log2); ++ p.qp_bd = 6 * sps->bit_depth_luma_minus8; ++ hevc_tile_bounds(f->pps, ctb_log2 - 4, p.w_ctb, p.h_ctb, p.tile_cols, p.tile_rows); ++ ++ for (i = 0; i < f->nslices; i++) { ++ if (!i && SLICE_F(&f->slices[0], DEPENDENT_SLICE_SEGMENT)) { ++ *why = "picture starts with a dependent slice segment"; ++ return -EINVAL; ++ } ++ ret = hevc_emit_slice(cb, &p, i, why); ++ if (ret) { ++ if (!*why) ++ *why = "command buffer full"; ++ return ret; ++ } ++ ret = apple_vxd_cmdbuf_chunk_end(cb, &off, &bytes); ++ if (ret) { ++ *why = "command buffer full"; ++ return ret; ++ } ++ pkt = apple_vxd_cmdbuf_add_packet(cb, VXD_PKT_MAGIC0, VXD_PKT_H264_PAYLOAD); ++ if (!pkt) { ++ *why = "packet list full"; ++ return -ENOSPC; ++ } ++ pkt[VXD_PKT_FLAGS / 4] = (i ? 0 : 1) | ++ (!i && f->first_picture_of_session ? 0x10 : 0); ++ pkt[VXD_PKT_W4 / 4] = hevc_pic_config(&p); ++ pkt[VXD_PKT_CMD_BYTES / 4] = bytes; ++ pkt[VXD_PKT_W2 / 4] = cb->va + off; ++ pkt[VXD_PKT_W5 / 4] = f->stream_state; ++ pkt[VXD_PKT_KIND / 4] = VXD_PKT_KIND_PICTURE; ++ } ++ ++ hevc_emit_picture(cb, &p); ++ ret = apple_vxd_cmdbuf_pair_chunk_end(cb, &off, &bytes); ++ if (ret) { ++ *why = "command buffer full"; ++ return ret; ++ } ++ pkt = apple_vxd_cmdbuf_add_packet(cb, VXD_PKT_MAGIC0, VXD_PKT_H264_PAYLOAD); ++ if (!pkt) { ++ *why = "packet list full"; ++ return -ENOSPC; ++ } ++ pkt[VXD_PKT_W4 / 4] = hevc_pic_config(&p); ++ pkt[VXD_PKT_CMD_BYTES / 4] = bytes; ++ pkt[VXD_PKT_W2 / 4] = cb->va + off; ++ pkt[VXD_PKT_W5 / 4] = f->stream_state; ++ pkt[VXD_PKT_KIND / 4] = VXD_PKT_KIND_RENDER_TARGET; ++ return 0; ++} +diff --git a/drivers/media/platform/apple/vxd/apple-vxd-hevc-cmd.h b/drivers/media/platform/apple/vxd/apple-vxd-hevc-cmd.h +new file mode 100644 +index 0000000..8be0b1b +--- /dev/null ++++ b/drivers/media/platform/apple/vxd/apple-vxd-hevc-cmd.h +@@ -0,0 +1,109 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Apple VXD - HEVC (H.265) command-stream builder ++ * ++ * Pure code: no MMIO, no DMA API, no locking, so it also builds on the host ++ * (scripts/vdec/host/vdec-hevc-host.c) and its words can be compared with ++ * the reference generator. ++ */ ++#ifndef __APPLE_VXD_HEVC_CMD_H__ ++#define __APPLE_VXD_HEVC_CMD_H__ ++ ++#include ++#include ++#include ++ ++#include "apple-vxd-cmd.h" ++ ++#define APPLE_VXD_HEVC_MAX_SLICES 127 /* + the kind-4 packet <= 128 */ ++#define APPLE_VXD_HEVC_MAX_REFS 15 /* hardware reference slots */ ++#define APPLE_VXD_HEVC_SIDE_SIZE SZ_256K /* slice structure buffer */ ++#define APPLE_VXD_HEVC_SPDEST_SIZE SZ_1M /* slice parameter buffer */ ++#define APPLE_VXD_HEVC_INTRA_SIZE SZ_64K ++#define APPLE_VXD_HEVC_GENC_BUFS 4 ++ ++/* Per-SoC HEVC parameters (match data). */ ++struct apple_vxd_hevc_caps { ++ u8 buf_mult; /* 1, or 2 from vxd-version 9 on */ ++ bool tail_7474; /* two 0x7474 writes after every slice structure */ ++ u16 cache_base; /* 0x701c line split base */ ++}; ++ ++/* ++ * What a decoded picture keeps for later use as the co-located picture ++ * (the kext's 0x1c-byte picture record): its 0x5438 and 0x5440..0x544c ++ * words and its temporal MV buffer. It lives in the CAPTURE buffer's ++ * side buffer, so it follows the picture. ++ */ ++struct apple_vxd_hevc_record { ++ u32 refs; /* 0x5438 */ ++ u32 poc_delta[4]; /* 0x5440..0x544c */ ++ u32 mv; /* 0x5434 */ ++ u32 valid; ++}; ++ ++/* Per-CAPTURE side buffer layout (AllocatePictureMemory sizes). */ ++struct apple_vxd_hevc_aux_layout { ++ u32 mv, frag, above, record, size; ++}; ++ ++struct apple_vxd_hevc_ref { ++ u32 luma; ++ u32 chroma; ++ const struct apple_vxd_hevc_record *rec; ++}; ++ ++/* One located slice NAL in the OUTPUT buffer. */ ++struct apple_vxd_hevc_nal { ++ u32 offset; /* first NAL header byte */ ++ u32 size; /* through the last byte before the next start code */ ++ u32 header_bits; /* RBSP bits from the NAL header to slice_data() */ ++}; ++ ++struct apple_vxd_hevc_frame { ++ const struct v4l2_ctrl_hevc_sps *sps; ++ const struct v4l2_ctrl_hevc_pps *pps; ++ const struct v4l2_ctrl_hevc_decode_params *dp; ++ const struct v4l2_ctrl_hevc_scaling_matrix *sm; /* NULL: none set */ ++ const struct v4l2_ctrl_hevc_slice_params *slices; ++ const struct apple_vxd_hevc_nal *nals; ++ unsigned int nslices; ++ const struct apple_vxd_hevc_caps *caps; ++ ++ /* Device addresses. */ ++ u32 src; /* OUTPUT buffer */ ++ u32 dst_luma, dst_chroma, dst_stride, dst_cstride; ++ u32 mv, frag, above; /* this picture's side buffer */ ++ struct apple_vxd_hevc_ref refs[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]; ++ u32 genc[APPLE_VXD_HEVC_GENC_BUFS]; ++ u32 genc_size; ++ u32 side; /* slice structures, CPU-written */ ++ u8 *side_cpu; ++ size_t side_size; ++ u32 spdest; /* their transfer destination */ ++ u32 intra, auxline, stream_state; ++ ++ /* Session state (C[13c469], C[136254]); the caller updates both. */ ++ bool first_slice_of_session; ++ bool first_picture_of_session; ++ ++ /* Output: this picture's record (in its side buffer). */ ++ struct apple_vxd_hevc_record *record; ++}; ++ ++u32 apple_vxd_hevc_genc_size(unsigned int width, unsigned int height); ++void apple_vxd_hevc_aux_layout(const struct v4l2_ctrl_hevc_sps *sps, ++ struct apple_vxd_hevc_aux_layout *l); ++u32 apple_vxd_hevc_aux_size(unsigned int width, unsigned int height); ++int apple_vxd_hevc_validate(const struct v4l2_ctrl_hevc_sps *sps, ++ const struct v4l2_ctrl_hevc_pps *pps, ++ const char **why); ++int apple_vxd_hevc_find_nals(const u8 *src, size_t len, ++ const struct v4l2_ctrl_hevc_slice_params *slices, ++ unsigned int nslices, struct apple_vxd_hevc_nal *nals, ++ const char **why); ++int apple_vxd_hevc_build(struct apple_vxd_cmdbuf *cb, ++ const struct apple_vxd_hevc_frame *f, ++ const char **why); ++ ++#endif /* __APPLE_VXD_HEVC_CMD_H__ */ +diff --git a/drivers/media/platform/apple/vxd/apple-vxd-hevc.c b/drivers/media/platform/apple/vxd/apple-vxd-hevc.c +new file mode 100644 +index 0000000..d3f628d +--- /dev/null ++++ b/drivers/media/platform/apple/vxd/apple-vxd-hevc.c +@@ -0,0 +1,428 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Apple VXD - HEVC (H.265) backend (V4L2 stateless, frame based) ++ * ++ * Userspace parses the parameter sets, the slice headers and the DPB ++ * (V4L2 stateless contract); the OUTPUT buffer carries the picture's slice ++ * NALs behind Annex-B start codes, and SLICE_PARAMS one entry per slice. ++ * Per picture this file ++ * ++ * 1. validates SPS/PPS against what the command recipe covers, ++ * 2. locates the slice NALs (apple_vxd_hevc_find_nals()), ++ * 3. resolves the DPB to CAPTURE buffers by timestamp, with their side ++ * buffers (temporal MVs and the co-located picture record), ++ * 4. builds the command stream (apple-vxd-hevc-cmd.c) and submits one ++ * 0x81 message per slice and the 0x88 message that ends the job. ++ * ++ * The context owns the buffers CHEVCDecoder::VACommandBufferInit allocates ++ * per decoder (0xfffffff0062b18f8): four Genc buffers sized from the ++ * picture, the intra and aux-line buffers, the stream state, and the slice ++ * structure buffer. Each CAPTURE buffer carries what AllocatePictureMemory ++ * allocates per picture: temporal MVs, the fragment pointer table and the ++ * above-parameter buffer. The kext also gives every picture its own 1 MiB ++ * slice parameter buffer; it is only read while that picture decodes, so ++ * one per context serves here, decodes being serialized. ++ */ ++#include ++#include ++ ++#include "apple-vxd.h" ++#include "apple-vxd-hevc-cmd.h" ++ ++/* AppleD5500 BRN50577: S8000 A0/A1 cores need a dummy picture first. */ ++#define APPLE_VXD_CORE_REV_BRN50577 0x70600 ++ ++struct apple_vxd_hevc_ctx { ++ struct apple_vxd_dev_buf genc[APPLE_VXD_HEVC_GENC_BUFS]; ++ u32 genc_size; ++ struct apple_vxd_dev_buf side; ++ struct apple_vxd_dev_buf spdest; ++ struct apple_vxd_dev_buf intra; ++ struct apple_vxd_dev_buf auxline; ++ struct apple_vxd_dev_buf stream_state; ++ bool first_slice; ++ bool first_picture; ++ struct apple_vxd_hevc_nal nals[APPLE_VXD_HEVC_MAX_SLICES]; ++}; ++ ++static const void *apple_vxd_hevc_ctrl(struct apple_vxd_ctx *ctx, u32 id, ++ unsigned int *elems) ++{ ++ struct v4l2_ctrl *ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, id); ++ ++ if (ctrl && elems) ++ *elems = ctrl->elems; ++ return ctrl ? ctrl->p_cur.p : NULL; ++} ++ ++static int apple_vxd_hevc_try_ctrl(struct apple_vxd_ctx *ctx, ++ struct v4l2_ctrl *ctrl) ++{ ++ const struct v4l2_ctrl_hevc_sps *sps = ctrl->p_new.p_hevc_sps; ++ ++ if (ctrl->id != V4L2_CID_STATELESS_HEVC_SPS) ++ return 0; ++ /* The default (all zero, before userspace sets an SPS) must pass. */ ++ if (!sps->pic_width_in_luma_samples) ++ return 0; ++ if (sps->chroma_format_idc != 1 || sps->bit_depth_luma_minus8 || ++ sps->bit_depth_chroma_minus8) { ++ dev_dbg(ctx->vxd->dev, "hevc sps: only 8-bit 4:2:0 is supported\n"); ++ return -EINVAL; ++ } ++ if (sps->pic_width_in_luma_samples > APPLE_VXD_MAX_PIC_W || ++ sps->pic_height_in_luma_samples > APPLE_VXD_MAX_PIC_H) { ++ dev_dbg(ctx->vxd->dev, "hevc sps: %ux%u too large\n", ++ sps->pic_width_in_luma_samples, sps->pic_height_in_luma_samples); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static void apple_vxd_hevc_free(struct apple_vxd_dev *vxd, ++ struct apple_vxd_hevc_ctx *h) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < APPLE_VXD_HEVC_GENC_BUFS; i++) ++ apple_vxd_dev_buf_free(vxd, &h->genc[i]); ++ apple_vxd_dev_buf_free(vxd, &h->side); ++ apple_vxd_dev_buf_free(vxd, &h->spdest); ++ apple_vxd_dev_buf_free(vxd, &h->intra); ++ apple_vxd_dev_buf_free(vxd, &h->auxline); ++ apple_vxd_dev_buf_free(vxd, &h->stream_state); ++} ++ ++static int apple_vxd_hevc_start(struct apple_vxd_ctx *ctx) ++{ ++ const struct v4l2_pix_format_mplane *pix = &ctx->decoded_fmt.fmt.pix_mp; ++ struct apple_vxd_dev *vxd = ctx->vxd; ++ const struct apple_vxd_hevc_caps *caps = &vxd->variant->hevc; ++ struct apple_vxd_hevc_ctx *h; ++ unsigned int i; ++ int ret; ++ ++ h = kvzalloc_obj(*h); ++ if (!h) ++ return -ENOMEM; ++ ++ /* VACommandBufferInit sizes; M is the per-SoC buffer multiplier. */ ++ h->genc_size = apple_vxd_hevc_genc_size(pix->width, pix->height); ++ for (i = 0; i < APPLE_VXD_HEVC_GENC_BUFS; i++) { ++ ret = apple_vxd_dev_buf_alloc(vxd, h->genc_size, &h->genc[i]); ++ if (ret) ++ goto err; ++ } ++ ret = apple_vxd_dev_buf_alloc(vxd, APPLE_VXD_HEVC_SIDE_SIZE, &h->side); ++ if (!ret) ++ ret = apple_vxd_dev_buf_alloc(vxd, APPLE_VXD_HEVC_SPDEST_SIZE, &h->spdest); ++ if (!ret) ++ ret = apple_vxd_dev_buf_alloc(vxd, APPLE_VXD_HEVC_INTRA_SIZE, &h->intra); ++ if (!ret) ++ ret = apple_vxd_dev_buf_alloc(vxd, caps->buf_mult << 21, &h->auxline); ++ if (!ret) ++ ret = apple_vxd_dev_buf_alloc(vxd, caps->buf_mult << 12, &h->stream_state); ++ if (ret) ++ goto err; ++ ++ h->first_slice = true; ++ h->first_picture = true; ++ ctx->priv = h; ++ dev_dbg(vxd->dev, ++ "hevc: context buffers: genc 4 x %u bytes, aux line %u, stream state %u\n", ++ h->genc_size, caps->buf_mult << 21, caps->buf_mult << 12); ++ return 0; ++ ++err: ++ apple_vxd_hevc_free(vxd, h); ++ kvfree(h); ++ return ret; ++} ++ ++static void apple_vxd_hevc_stop(struct apple_vxd_ctx *ctx) ++{ ++ struct apple_vxd_hevc_ctx *h = ctx->priv; ++ ++ if (!h) ++ return; ++ apple_vxd_hevc_free(ctx->vxd, h); ++ kvfree(h); ++ ctx->priv = NULL; ++} ++ ++static enum apple_vxd_image_fmt ++apple_vxd_hevc_get_image_fmt(struct apple_vxd_ctx *ctx, struct v4l2_ctrl *ctrl) ++{ ++ if (ctrl->id != V4L2_CID_STATELESS_HEVC_SPS) ++ return APPLE_VXD_IMG_FMT_ANY; ++ ++ return APPLE_VXD_IMG_FMT_420_8BIT; ++} ++ ++static size_t apple_vxd_hevc_capture_aux_size(struct apple_vxd_ctx *ctx) ++{ ++ const struct v4l2_pix_format_mplane *pix = &ctx->decoded_fmt.fmt.pix_mp; ++ ++ return apple_vxd_hevc_aux_size(pix->width, pix->height); ++} ++ ++/* A CAPTURE buffer's side-buffer device and CPU addresses for @l. */ ++static bool apple_vxd_hevc_aux(struct vb2_v4l2_buffer *vbuf, ++ const struct apple_vxd_hevc_aux_layout *l, ++ u32 *va, struct apple_vxd_hevc_record **rec) ++{ ++ u8 *cpu = apple_vxd_buf_aux_cpu(vbuf); ++ ++ if (!cpu || apple_vxd_buf_aux_size(vbuf) < l->size) ++ return false; ++ *va = apple_vxd_buf_aux_va(vbuf); ++ *rec = (struct apple_vxd_hevc_record *)(cpu + l->record); ++ return true; ++} ++ ++static int apple_vxd_hevc_run(struct apple_vxd_ctx *ctx) ++{ ++ const struct v4l2_pix_format_mplane *pix = &ctx->decoded_fmt.fmt.pix_mp; ++ struct apple_vxd_hevc_ctx *h = ctx->priv; ++ struct apple_vxd_dev *vxd = ctx->vxd; ++ struct apple_vxd_hevc_frame f = { }; ++ struct apple_vxd_hevc_aux_layout l; ++ struct apple_vxd_cmdbuf *cb = NULL; ++ struct apple_vxd_hevc_record *rec; ++ struct apple_vxd_run run; ++ const char *why = NULL; ++ struct vb2_queue *cap_q; ++ unsigned int i, nslices = 0; ++ u32 stride, aux; ++ const u8 *src; ++ int ret; ++ ++ apple_vxd_run_preamble(ctx, &run); ++ ++ if (!vxd->hw_ready || vxd->fw_panicked) { ++ dev_dbg(vxd->dev, "decode job with no usable firmware\n"); ++ ret = -EAGAIN; ++ goto out; ++ } ++ if (!h) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ f.sps = apple_vxd_hevc_ctrl(ctx, V4L2_CID_STATELESS_HEVC_SPS, NULL); ++ f.pps = apple_vxd_hevc_ctrl(ctx, V4L2_CID_STATELESS_HEVC_PPS, NULL); ++ f.dp = apple_vxd_hevc_ctrl(ctx, V4L2_CID_STATELESS_HEVC_DECODE_PARAMS, NULL); ++ f.sm = apple_vxd_hevc_ctrl(ctx, V4L2_CID_STATELESS_HEVC_SCALING_MATRIX, NULL); ++ f.slices = apple_vxd_hevc_ctrl(ctx, V4L2_CID_STATELESS_HEVC_SLICE_PARAMS, &nslices); ++ if (!f.sps || !f.pps || !f.dp || !f.slices) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ret = apple_vxd_hevc_validate(f.sps, f.pps, &why); ++ if (ret) ++ goto out_why; ++ if (f.sps->pic_width_in_luma_samples > pix->width || ++ f.sps->pic_height_in_luma_samples > pix->height) { ++ why = "picture larger than the CAPTURE format"; ++ ret = -EINVAL; ++ goto out_why; ++ } ++ ++ src = vb2_plane_vaddr(&run.src->vb2_buf, 0); ++ if (!src) { ++ why = "OUTPUT buffer has no kernel mapping"; ++ ret = -EINVAL; ++ goto out_why; ++ } ++ ret = apple_vxd_hevc_find_nals(src, vb2_get_plane_payload(&run.src->vb2_buf, 0), ++ f.slices, min(nslices, APPLE_VXD_HEVC_MAX_SLICES), ++ h->nals, &why); ++ if (ret) ++ goto out_why; ++ ++ apple_vxd_hevc_aux_layout(f.sps, &l); ++ stride = pix->plane_fmt[0].bytesperline; ++ f.nals = h->nals; ++ f.nslices = nslices; ++ f.caps = &vxd->variant->hevc; ++ f.src = apple_vxd_buf_va(run.src); ++ f.dst_luma = apple_vxd_buf_va(run.dst); ++ f.dst_chroma = f.dst_luma + stride * pix->height; ++ f.dst_stride = stride; ++ f.dst_cstride = stride; ++ if (!apple_vxd_hevc_aux(run.dst, &l, &aux, &rec)) { ++ why = "CAPTURE side buffer missing or too small"; ++ ret = -EINVAL; ++ goto out_why; ++ } ++ f.mv = aux + l.mv; ++ f.frag = aux + l.frag; ++ f.above = aux + l.above; ++ f.record = rec; ++ rec->valid = 0; ++ ++ cap_q = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); ++ for (i = 0; i < f.dp->num_active_dpb_entries && i < V4L2_HEVC_DPB_ENTRIES_NUM_MAX; i++) { ++ struct vb2_buffer *ref = vb2_find_buffer(cap_q, f.dp->dpb[i].timestamp); ++ struct apple_vxd_hevc_record *rrec; ++ u32 raux; ++ ++ if (!ref || !apple_vxd_hevc_aux(to_vb2_v4l2_buffer(ref), &l, &raux, &rrec)) { ++ dev_dbg(vxd->dev, "hevc: dpb[%u] ts %llu has no CAPTURE buffer\n", ++ i, f.dp->dpb[i].timestamp); ++ continue; ++ } ++ f.refs[i].luma = apple_vxd_buf_va(to_vb2_v4l2_buffer(ref)); ++ f.refs[i].chroma = f.refs[i].luma + stride * pix->height; ++ f.refs[i].rec = rrec; ++ } ++ for (i = 0; i < APPLE_VXD_HEVC_GENC_BUFS; i++) ++ f.genc[i] = h->genc[i].va; ++ f.genc_size = h->genc_size; ++ f.side = h->side.va; ++ f.side_cpu = h->side.mem.vaddr; ++ f.side_size = APPLE_VXD_HEVC_SIDE_SIZE; ++ f.spdest = h->spdest.va; ++ f.intra = h->intra.va; ++ f.auxline = h->auxline.va; ++ f.stream_state = h->stream_state.va; ++ f.first_slice_of_session = h->first_slice; ++ f.first_picture_of_session = h->first_picture; ++ ++ cb = apple_vxd_cmdbuf_get(vxd); ++ if (!cb) { ++ why = "no free command buffer"; ++ ret = -EBUSY; ++ goto out_why; ++ } ++ ret = apple_vxd_hevc_build(cb, &f, &why); ++ if (ret) ++ goto out_why; ++ ++ dev_dbg(vxd->dev, ++ "hevc decode: src %u va %#x, %u slices (first NAL %#x+%u, header %u bits), dst %u Y %#x UV %#x stride %u, %zu command bytes in buf %u (va %#x), %u packets\n", ++ run.src->vb2_buf.index, f.src, nslices, h->nals[0].offset, ++ h->nals[0].size, h->nals[0].header_bits, run.dst->vb2_buf.index, ++ f.dst_luma, f.dst_chroma, stride, cb->used, cb->index, cb->va, ++ cb->npackets); ++ ++ /* Controls were only read while building; the job may finish any time now. */ ++ apple_vxd_run_postamble(ctx, &run); ++ ret = apple_vxd_submit(ctx, cb); ++ if (ret) { ++ dev_warn_ratelimited(vxd->dev, "hevc decode: submit failed: %d\n", ret); ++ if (cb->state == APPLE_VXD_CMDBUF_HOST) ++ apple_vxd_cmdbuf_put(vxd, cb); ++ return ret; ++ } ++ h->first_slice = false; ++ h->first_picture = false; ++ return 0; ++ ++out_why: ++ dev_warn_ratelimited(vxd->dev, "hevc decode: picture refused (%d): %s\n", ++ ret, why ? why : "?"); ++out: ++ if (cb) ++ apple_vxd_cmdbuf_put(vxd, cb); ++ apple_vxd_run_postamble(ctx, &run); ++ return ret; ++} ++ ++static const struct apple_vxd_ctrl_desc apple_vxd_hevc_ctrl_descs[] = { ++ { ++ .cfg.id = V4L2_CID_STATELESS_HEVC_SPS, ++ .cfg.ops = &apple_vxd_ctrl_ops, ++ }, ++ { ++ .cfg.id = V4L2_CID_STATELESS_HEVC_PPS, ++ }, ++ { ++ .cfg.id = V4L2_CID_STATELESS_HEVC_SLICE_PARAMS, ++ .cfg.flags = V4L2_CTRL_FLAG_DYNAMIC_ARRAY, ++ .cfg.dims = { APPLE_VXD_HEVC_MAX_SLICES }, ++ }, ++ { ++ .cfg.id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX, ++ }, ++ { ++ .cfg.id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS, ++ }, ++ { ++ .cfg.id = V4L2_CID_STATELESS_HEVC_DECODE_MODE, ++ .cfg.min = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, ++ .cfg.max = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, ++ .cfg.def = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, ++ }, ++ { ++ .cfg.id = V4L2_CID_STATELESS_HEVC_START_CODE, ++ .cfg.min = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, ++ .cfg.max = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, ++ .cfg.def = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, ++ }, ++ { ++ .cfg.id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, ++ .cfg.min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, ++ .cfg.max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE, ++ .cfg.def = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, ++ }, ++ { ++ .cfg.id = V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, ++ .cfg.min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1, ++ .cfg.max = V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1, ++ .cfg.def = V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1, ++ }, ++}; ++ ++static const struct apple_vxd_ctrls apple_vxd_hevc_ctrls = { ++ .ctrls = apple_vxd_hevc_ctrl_descs, ++ .num_ctrls = ARRAY_SIZE(apple_vxd_hevc_ctrl_descs), ++}; ++ ++static const struct apple_vxd_decoded_fmt_desc apple_vxd_hevc_decoded_fmts[] = { ++ { ++ .fourcc = V4L2_PIX_FMT_NV12, ++ .image_fmt = APPLE_VXD_IMG_FMT_420_8BIT, ++ }, ++}; ++ ++static const struct apple_vxd_coded_fmt_desc apple_vxd_hevc_fmt = { ++ .fourcc = V4L2_PIX_FMT_HEVC_SLICE, ++ .frmsize = { ++ .min_width = APPLE_VXD_MIN_PIC_W, ++ .max_width = APPLE_VXD_MAX_PIC_W, ++ .step_width = 16, ++ .min_height = APPLE_VXD_MIN_PIC_H, ++ .max_height = APPLE_VXD_MAX_PIC_H, ++ .step_height = 16, ++ }, ++ .ctrls = &apple_vxd_hevc_ctrls, ++ .ops = &(const struct apple_vxd_coded_fmt_ops){ ++ .start = apple_vxd_hevc_start, ++ .stop = apple_vxd_hevc_stop, ++ .run = apple_vxd_hevc_run, ++ .try_ctrl = apple_vxd_hevc_try_ctrl, ++ .get_image_fmt = apple_vxd_hevc_get_image_fmt, ++ .capture_aux_size = apple_vxd_hevc_capture_aux_size, ++ }, ++ .num_decoded_fmts = ARRAY_SIZE(apple_vxd_hevc_decoded_fmts), ++ .decoded_fmts = apple_vxd_hevc_decoded_fmts, ++}; ++ ++/* ++ * Adds HEVC to the device's coded formats when the SoC's match data has ++ * it: AppleD5500 advertises HEVCSupported for vxd-version 7, 9 and 10. ++ */ ++int apple_vxd_hevc_register(struct apple_vxd_dev *vxd) ++{ ++ const struct apple_vxd_variant *v = vxd->variant; ++ ++ if (!v->hevc_supported || !v->hevc.buf_mult) ++ return 0; ++ if (vxd->core_rev == APPLE_VXD_CORE_REV_BRN50577) { ++ dev_info(vxd->dev, "hevc: core revision %#x needs BRN50577, not implemented\n", ++ vxd->core_rev); ++ return 0; ++ } ++ return apple_vxd_register_coded_fmt(vxd, &apple_vxd_hevc_fmt); ++} +diff --git a/drivers/media/platform/apple/vxd/apple-vxd.h b/drivers/media/platform/apple/vxd/apple-vxd.h +index 5faac08..9ac2689 100644 +--- a/drivers/media/platform/apple/vxd/apple-vxd.h ++++ b/drivers/media/platform/apple/vxd/apple-vxd.h +@@ -11,7 +11,7 @@ + * IRQ drain, fences (apple-vxd-mbox.c) + * - command buffers and their encoders (apple-vxd-cmdbuf.c, -cmd.c) + * - the V4L2 stateless node (apple-vxd-v4l2.c) and its codec units: +- * H.264 (apple-vxd-h264*.c) ++ * H.264 (apple-vxd-h264*.c) and HEVC (apple-vxd-hevc*.c) + * + * [E] marks facts taken from the kext. + */ +@@ -39,6 +39,7 @@ + #include + + #include "apple-vxd-cmd.h" ++#include "apple-vxd-hevc-cmd.h" + #include "apple-vxd-regs.h" + + #define APPLE_VXD_DRV_NAME "apple-vxd" +@@ -61,7 +62,7 @@ enum apple_vxd_image_fmt { + APPLE_VXD_IMG_FMT_420_8BIT, + }; + +-/* One per codec unit. */ ++/* One per codec unit (H.264, HEVC). */ + #define APPLE_VXD_MAX_CODED_FMTS 2 + + /* SoC variant data (compatible table). */ +@@ -70,6 +71,8 @@ struct apple_vxd_variant { + u32 num_gart_pts; /* mapped page tables (512 = 2 GiB) */ + bool hevc_supported; + bool has_integration; /* second reg range, see VXD_INTEG_CTRL */ ++ /* HEVC buffer multiplier and command quirks; zero leaves HEVC off. */ ++ struct apple_vxd_hevc_caps hevc; + }; + + /* +@@ -499,4 +502,7 @@ extern const struct v4l2_ctrl_ops apple_vxd_ctrl_ops; + /* h264.c */ + int apple_vxd_h264_register(struct apple_vxd_dev *vxd); + ++/* hevc.c */ ++int apple_vxd_hevc_register(struct apple_vxd_dev *vxd); ++ + #endif /* __APPLE_VXD_H__ */ +-- +2.54.0 diff --git a/kernel/patches/drivers/media/0009-media-apple-vxd-Enable-HEVC-on-the-S8000-and-T8010-d.patch b/kernel/patches/drivers/media/0009-media-apple-vxd-Enable-HEVC-on-the-S8000-and-T8010-d.patch new file mode 100644 index 00000000..bdb51871 --- /dev/null +++ b/kernel/patches/drivers/media/0009-media-apple-vxd-Enable-HEVC-on-the-S8000-and-T8010-d.patch @@ -0,0 +1,62 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Berkay=20K=C3=B6sebay?= +Date: Tue, 10 Nov 2026 21:25:37 +0100 +Subject: [PATCH] media: apple-vxd: Enable HEVC on the S8000 and T8010 decoders + +AppleD5500 advertises HEVCSupported for vxd-version 7, 9 and 10 and keys +its HEVC choices on that version (VACreateDecodeDeviceHEVC, +0xfffffff006293f2c, called with the ADT vxd-version): the aux-line and +stream-state buffers are scaled by 2 from version 9 on, versions 7 and 9 +write 0x7474 twice after every slice structure, and the 0x701c line +split starts from 0x1800 instead of 0xc00 from version 10 on. Give the +S8000 (vxd-version 7, A9) and T8010 (10, A10) variants those values, +which turns the HEVC unit on for both. + +The J71t decodes HEVC bit-exact against software decoding; the T8010 +has not been tried yet. + +[Orchard: from orchard PR #121, commit 4016f5e2; the S8000 variant without the sleep_stop flag of PR #117 (not on main); message updated: HEVC decodes on the J71t] + +Assisted-by: LLM +--- +diff --git a/drivers/media/platform/apple/vxd/apple-vxd-core.c b/drivers/media/platform/apple/vxd/apple-vxd-core.c +index 97d388f..2940978 100644 +--- a/drivers/media/platform/apple/vxd/apple-vxd-core.c ++++ b/drivers/media/platform/apple/vxd/apple-vxd-core.c +@@ -249,11 +249,22 @@ static const struct dev_pm_ops apple_vxd_pm_ops = { + RUNTIME_PM_OPS(apple_vxd_runtime_suspend, apple_vxd_runtime_resume, NULL) + }; + ++/* ++ * HEVC: AppleD5500 keys its per-SoC HEVC choices on the ADT vxd-version ++ * (VACreateDecodeDeviceHEVC 0xfffffff006293f2c): the buffer multiplier ++ * (2 for versions 9 and 10), the 0x7474 pair after each slice structure ++ * (versions 7 and 9) and the 0x701c line split base (0x1800 from 10 on). ++ */ + static const struct apple_vxd_variant s8000_vxd_variant = { + .vxd_version = 7, + .num_gart_pts = 512, /* "setting 2GB mapping" [E] */ + /* HEVCSupported = ((rev - 7) < 4) && ((0xD >> (rev - 7)) & 1) [E] */ + .hevc_supported = true, ++ .hevc = { ++ .buf_mult = 1, ++ .tail_7474 = true, ++ .cache_base = 0xc00, ++ }, + }; + + /* The T8010 core has two pixel pipes; it reports them in CORE_ID. */ +@@ -262,6 +273,11 @@ static const struct apple_vxd_variant t8010_vxd_variant = { + .num_gart_pts = 512, + .hevc_supported = true, + .has_integration = true, ++ .hevc = { ++ .buf_mult = 2, ++ .tail_7474 = false, ++ .cache_base = 0x1800, ++ }, + }; + + static const struct of_device_id apple_vxd_of_match[] = { +-- +2.54.0 diff --git a/schemas/v1/device.schema.json b/schemas/v1/device.schema.json index 77db1dee..e1d0a630 100644 --- a/schemas/v1/device.schema.json +++ b/schemas/v1/device.schema.json @@ -361,6 +361,14 @@ "missing", "none" ] + }, + "video_decode_hevc": { + "enum": [ + "working", + "untested", + "missing", + "none" + ] } }, "required": [ @@ -388,7 +396,8 @@ "usb_network", "audio_output", "audio_input", - "camera" + "camera", + "video_decode_hevc" ], "additionalProperties": false }, diff --git a/tools/catalog/drivers.json b/tools/catalog/drivers.json index 5b000d53..5c7b9db0 100644 --- a/tools/catalog/drivers.json +++ b/tools/catalog/drivers.json @@ -193,6 +193,7 @@ "touch", "usb_network", "usb_serial", + "video_decode_hevc", "wifi" ], "j71s": [ @@ -206,7 +207,8 @@ "light_sensor", "touch", "usb_network", - "usb_serial" + "usb_serial", + "video_decode_hevc" ], "j71t": [ "audio_output", @@ -222,6 +224,7 @@ "touch", "usb_network", "usb_serial", + "video_decode_hevc", "wifi" ], "j72b": [ @@ -238,6 +241,7 @@ "touch", "usb_network", "usb_serial", + "video_decode_hevc", "wifi" ], "j72s": [ @@ -251,7 +255,8 @@ "light_sensor", "touch", "usb_network", - "usb_serial" + "usb_serial", + "video_decode_hevc" ], "j72t": [ "audio_output", @@ -267,6 +272,7 @@ "touch", "usb_network", "usb_serial", + "video_decode_hevc", "wifi" ], "j73": [ diff --git a/tools/catalog/feature-drivers.toml b/tools/catalog/feature-drivers.toml index 9616d549..e8cb9f07 100644 --- a/tools/catalog/feature-drivers.toml +++ b/tools/catalog/feature-drivers.toml @@ -17,3 +17,4 @@ charging = ["apple_d2365_charger", "apple_tristar_charger"] usb_serial = ["dwc2"] usb_network = ["dwc2"] audio_output = ["snd_soc_apple_s8000_mca"] +video_decode_hevc = ["apple_vxd"] From 88c3c8391041090bc40209e36058d4fad4b35f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Berkay=20K=C3=B6sebay?= Date: Fri, 13 Nov 2026 05:01:59 +0800 Subject: [PATCH 2/2] catalog: Add hardware HEVC decoding as a feature J71t decodes HEVC bit-exact against software decoding (tested by hand on 2026-11-11 and 2026-11-12). The other iPads with the decoder enabled (J71s, J72s, J72t, and the iPad 6 J71b/J72b) are untested; every other board has no decoder node and is missing it. From orchard PR #121 (commit 4016f5e2), device data part. --- docs/compatibility.md | 204 +++++++++++++++++++++--------------------- 1 file changed, 102 insertions(+), 102 deletions(-) diff --git a/docs/compatibility.md b/docs/compatibility.md index bb495d0a..a9730828 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -136,113 +136,113 @@ Not generated while in review: `j72` (PR #59). Boards Orchard builds for. States: working, untested, missing, none (no such hardware). -| Board | Display | Screen on/off | Backlight | Touchscreen | Buttons | Accelerometer | Gyroscope | Light sensor | Touch ID | Internal storage read | Internal storage write | Wi-Fi | Bluetooth input devices | Bluetooth audio | Cellular | Battery status | Charging | Charging control | Suspend | Resume | USB serial | USB network | Speakers | Microphone | Cameras | -| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| `p105` | untested | untested | untested | untested | untested | missing | missing | missing | missing | untested | untested | untested | untested | untested | none | untested | untested | untested | missing | missing | untested | untested | untested | untested | missing | -| `j1` | untested | missing | missing | untested | missing | missing | missing | missing | none | missing | missing | untested | missing | missing | none | missing | missing | missing | missing | missing | untested | untested | missing | missing | missing | -| `j71` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `j73` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `j85` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `j85m` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `j86` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `j86m` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `j87` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `j87m` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `n51` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `n53` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `j96` | untested | missing | untested | untested | untested | missing | missing | missing | missing | missing | missing | untested | missing | missing | none | untested | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `j97` | untested | missing | untested | untested | untested | missing | missing | missing | missing | missing | missing | untested | missing | missing | missing | untested | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `n56` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `n61` | working | untested | untested | working | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | missing | missing | untested | untested | working | untested | missing | missing | missing | -| `j81` | working | untested | working | working | working | missing | missing | missing | missing | missing | missing | working | untested | untested | none | working | missing | missing | untested | untested | working | working | missing | missing | missing | -| `j82` | untested | untested | untested | untested | untested | missing | missing | missing | missing | missing | missing | untested | untested | untested | missing | untested | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `j71s` | untested | untested | untested | untested | untested | missing | missing | untested | missing | missing | missing | missing | untested | untested | none | untested | untested | untested | untested | untested | untested | untested | untested | untested | missing | -| `j72s` | untested | untested | untested | untested | untested | missing | missing | untested | missing | missing | missing | missing | untested | untested | missing | untested | untested | untested | untested | untested | untested | untested | untested | untested | missing | -| `n66` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `n69u` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `n71` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `j71t` | untested | untested | untested | untested | untested | missing | missing | untested | missing | untested | untested | untested | untested | untested | none | untested | untested | untested | untested | untested | untested | untested | working | untested | missing | -| `j72t` | untested | untested | untested | untested | untested | missing | missing | untested | missing | untested | untested | untested | untested | untested | missing | untested | untested | untested | untested | untested | untested | untested | untested | untested | missing | -| `n66m` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `n69` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `n71m` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `j127` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `j128` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `j98a` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `j99a` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `d10` | untested | untested | untested | missing | untested | untested | missing | missing | missing | untested | untested | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `d101` | untested | untested | untested | missing | untested | untested | missing | missing | missing | untested | untested | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `d11` | untested | untested | untested | untested | untested | untested | missing | untested | missing | untested | untested | untested | missing | missing | missing | untested | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `d111` | untested | untested | untested | untested | untested | untested | missing | untested | missing | untested | untested | untested | missing | missing | missing | untested | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `j171` | working | untested | working | working | working | untested | missing | untested | missing | working | working | working | missing | missing | none | working | untested | missing | untested | untested | untested | untested | working | missing | missing | -| `j172` | untested | untested | untested | untested | untested | untested | missing | untested | missing | untested | untested | untested | missing | missing | missing | untested | untested | missing | untested | untested | untested | untested | untested | missing | missing | -| `j71b` | working | working | working | working | working | working | missing | missing | missing | working | working | working | working | working | none | working | working | working | working | working | working | working | working | missing | missing | -| `j72b` | untested | untested | untested | untested | untested | untested | missing | missing | missing | untested | untested | untested | missing | missing | missing | untested | untested | untested | untested | untested | untested | untested | untested | missing | missing | -| `j120` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `j121` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `j207` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `j208` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `d20` | untested | missing | untested | missing | missing | missing | missing | missing | missing | untested | untested | missing | missing | missing | missing | untested | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `d201` | untested | missing | untested | missing | missing | missing | missing | missing | missing | untested | untested | missing | missing | missing | missing | untested | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `d21` | untested | missing | untested | missing | missing | missing | missing | missing | missing | untested | untested | missing | missing | missing | missing | untested | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `d211` | untested | missing | untested | missing | missing | missing | missing | missing | missing | untested | untested | missing | missing | missing | missing | untested | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `d22` | untested | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | missing | missing | missing | missing | untested | missing | missing | untested | untested | untested | untested | missing | missing | missing | -| `d221` | working | working | working | working | working | missing | missing | missing | none | working | working | untested | missing | missing | missing | working | missing | missing | untested | untested | working | untested | missing | missing | missing | +| Board | Display | Screen on/off | Backlight | Touchscreen | Buttons | Accelerometer | Gyroscope | Light sensor | Touch ID | Internal storage read | Internal storage write | Wi-Fi | Bluetooth input devices | Bluetooth audio | Cellular | Battery status | Charging | Charging control | Suspend | Resume | USB serial | USB network | Speakers | Microphone | Cameras | Hardware H.265 video decoding | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| `p105` | untested | untested | untested | untested | untested | missing | missing | missing | missing | untested | untested | untested | untested | untested | none | untested | untested | untested | missing | missing | untested | untested | untested | untested | missing | missing | +| `j1` | untested | missing | missing | untested | missing | missing | missing | missing | none | missing | missing | untested | missing | missing | none | missing | missing | missing | missing | missing | untested | untested | missing | missing | missing | missing | +| `j71` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `j73` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `j85` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `j85m` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `j86` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `j86m` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `j87` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `j87m` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `n51` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `n53` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `j96` | untested | missing | untested | untested | untested | missing | missing | missing | missing | missing | missing | untested | missing | missing | none | untested | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `j97` | untested | missing | untested | untested | untested | missing | missing | missing | missing | missing | missing | untested | missing | missing | missing | untested | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `n56` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `n61` | working | untested | untested | working | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | missing | missing | untested | untested | working | untested | missing | missing | missing | missing | +| `j81` | working | untested | working | working | working | missing | missing | missing | missing | missing | missing | working | untested | untested | none | working | missing | missing | untested | untested | working | working | missing | missing | missing | missing | +| `j82` | untested | untested | untested | untested | untested | missing | missing | missing | missing | missing | missing | untested | untested | untested | missing | untested | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `j71s` | untested | untested | untested | untested | untested | missing | missing | untested | missing | missing | missing | missing | untested | untested | none | untested | untested | untested | untested | untested | untested | untested | untested | untested | missing | untested | +| `j72s` | untested | untested | untested | untested | untested | missing | missing | untested | missing | missing | missing | missing | untested | untested | missing | untested | untested | untested | untested | untested | untested | untested | untested | untested | missing | untested | +| `n66` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `n69u` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `n71` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `j71t` | untested | untested | untested | untested | untested | missing | missing | untested | missing | untested | untested | untested | untested | untested | none | untested | untested | untested | untested | untested | untested | untested | working | untested | missing | working | +| `j72t` | untested | untested | untested | untested | untested | missing | missing | untested | missing | untested | untested | untested | untested | untested | missing | untested | untested | untested | untested | untested | untested | untested | untested | untested | missing | untested | +| `n66m` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `n69` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `n71m` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `j127` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `j128` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `j98a` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `j99a` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `d10` | untested | untested | untested | missing | untested | untested | missing | missing | missing | untested | untested | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `d101` | untested | untested | untested | missing | untested | untested | missing | missing | missing | untested | untested | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `d11` | untested | untested | untested | untested | untested | untested | missing | untested | missing | untested | untested | untested | missing | missing | missing | untested | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `d111` | untested | untested | untested | untested | untested | untested | missing | untested | missing | untested | untested | untested | missing | missing | missing | untested | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `j171` | working | untested | working | working | working | untested | missing | untested | missing | working | working | working | missing | missing | none | working | untested | missing | untested | untested | untested | untested | working | missing | missing | missing | +| `j172` | untested | untested | untested | untested | untested | untested | missing | untested | missing | untested | untested | untested | missing | missing | missing | untested | untested | missing | untested | untested | untested | untested | untested | missing | missing | missing | +| `j71b` | working | working | working | working | working | working | missing | missing | missing | working | working | working | working | working | none | working | working | working | working | working | working | working | working | missing | missing | untested | +| `j72b` | untested | untested | untested | untested | untested | untested | missing | missing | missing | untested | untested | untested | missing | missing | missing | untested | untested | untested | untested | untested | untested | untested | untested | missing | missing | untested | +| `j120` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `j121` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `j207` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `j208` | untested | missing | untested | missing | untested | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `d20` | untested | missing | untested | missing | missing | missing | missing | missing | missing | untested | untested | missing | missing | missing | missing | untested | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `d201` | untested | missing | untested | missing | missing | missing | missing | missing | missing | untested | untested | missing | missing | missing | missing | untested | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `d21` | untested | missing | untested | missing | missing | missing | missing | missing | missing | untested | untested | missing | missing | missing | missing | untested | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `d211` | untested | missing | untested | missing | missing | missing | missing | missing | missing | untested | untested | missing | missing | missing | missing | untested | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `d22` | untested | missing | missing | missing | missing | missing | missing | missing | missing | untested | untested | missing | missing | missing | missing | untested | missing | missing | untested | untested | untested | untested | missing | missing | missing | missing | +| `d221` | working | working | working | working | working | missing | missing | missing | none | working | working | untested | missing | missing | missing | working | missing | missing | untested | untested | working | untested | missing | missing | missing | missing | ## Drivers A yes means an enabled device tree node binds a driver in `7.3.0-rc1-orchard-apple-16k`, `7.3.0-rc1-orchard-apple-4k`. It does not mean the feature works. Read by `tools/catalog/generate kernel`. -| Board | Status | Backlight | Touchscreen | Buttons | Accelerometer | Light sensor | Internal storage read | Internal storage write | Wi-Fi | Bluetooth input devices | Bluetooth audio | Battery status | Charging | USB serial | USB network | Speakers | -| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| `j71` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `j73` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `j85` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `j85m` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `j86` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `j86m` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `j87` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `j87m` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `n51` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `n53` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `j96` | experimental | yes | yes | yes | – | – | – | – | yes | – | – | yes | – | yes | yes | – | -| `j97` | experimental | yes | yes | yes | – | – | – | – | yes | – | – | yes | – | yes | yes | – | -| `n56` | experimental | yes | – | yes | – | – | – | – | – | – | – | yes | – | yes | yes | – | -| `n61` | experimental | yes | yes | yes | – | – | – | – | – | – | – | yes | – | yes | yes | – | -| `j81` | experimental | yes | yes | yes | – | – | – | – | yes | yes | yes | yes | – | yes | yes | – | -| `j82` | experimental | yes | yes | yes | – | – | – | – | yes | yes | yes | yes | – | yes | yes | – | -| `j71s` | experimental | yes | yes | yes | – | yes | – | – | – | yes | yes | yes | yes | yes | yes | yes | -| `j72s` | experimental | yes | yes | yes | – | yes | – | – | – | yes | yes | yes | yes | yes | yes | yes | -| `n66` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `n69u` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `n71` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `j71t` | experimental | yes | yes | yes | – | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | -| `j72t` | experimental | yes | yes | yes | – | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | -| `n66m` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `n69` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `n71m` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `j127` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `j128` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `j98a` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `j99a` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `d10` | experimental | yes | – | yes | yes | – | yes | yes | – | yes | yes | – | – | yes | yes | – | -| `d101` | experimental | yes | – | yes | yes | – | yes | yes | – | yes | yes | – | – | yes | yes | – | -| `d11` | experimental | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | – | yes | yes | – | -| `d111` | experimental | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | – | yes | yes | – | -| `j171` | experimental | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | -| `j172` | experimental | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | -| `j71b` | supported | yes | yes | yes | yes | – | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | -| `j72b` | experimental | yes | yes | yes | yes | – | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | -| `j120` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `j121` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `j207` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `j208` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | -| `d20` | experimental | yes | – | – | – | – | yes | yes | – | – | – | yes | – | yes | yes | – | -| `d201` | experimental | yes | – | – | – | – | yes | yes | – | – | – | yes | – | yes | yes | – | -| `d21` | experimental | yes | – | – | – | – | yes | yes | – | – | – | yes | – | yes | yes | – | -| `d211` | experimental | yes | – | – | – | – | yes | yes | – | – | – | yes | – | yes | yes | – | -| `d22` | experimental | yes | yes | – | – | – | yes | yes | – | – | – | yes | – | yes | yes | – | -| `d221` | experimental | yes | yes | – | – | – | yes | yes | yes | – | – | yes | – | yes | yes | – | +| Board | Status | Backlight | Touchscreen | Buttons | Accelerometer | Light sensor | Internal storage read | Internal storage write | Wi-Fi | Bluetooth input devices | Bluetooth audio | Battery status | Charging | USB serial | USB network | Speakers | Hardware H.265 video decoding | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| `j71` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `j73` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `j85` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `j85m` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `j86` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `j86m` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `j87` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `j87m` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `n51` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `n53` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `j96` | experimental | yes | yes | yes | – | – | – | – | yes | – | – | yes | – | yes | yes | – | – | +| `j97` | experimental | yes | yes | yes | – | – | – | – | yes | – | – | yes | – | yes | yes | – | – | +| `n56` | experimental | yes | – | yes | – | – | – | – | – | – | – | yes | – | yes | yes | – | – | +| `n61` | experimental | yes | yes | yes | – | – | – | – | – | – | – | yes | – | yes | yes | – | – | +| `j81` | experimental | yes | yes | yes | – | – | – | – | yes | yes | yes | yes | – | yes | yes | – | – | +| `j82` | experimental | yes | yes | yes | – | – | – | – | yes | yes | yes | yes | – | yes | yes | – | – | +| `j71s` | experimental | yes | yes | yes | – | yes | – | – | – | yes | yes | yes | yes | yes | yes | yes | yes | +| `j72s` | experimental | yes | yes | yes | – | yes | – | – | – | yes | yes | yes | yes | yes | yes | yes | yes | +| `n66` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `n69u` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `n71` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `j71t` | experimental | yes | yes | yes | – | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | +| `j72t` | experimental | yes | yes | yes | – | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | +| `n66m` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `n69` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `n71m` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `j127` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `j128` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `j98a` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `j99a` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `d10` | experimental | yes | – | yes | yes | – | yes | yes | – | yes | yes | – | – | yes | yes | – | – | +| `d101` | experimental | yes | – | yes | yes | – | yes | yes | – | yes | yes | – | – | yes | yes | – | – | +| `d11` | experimental | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | – | yes | yes | – | – | +| `d111` | experimental | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | – | yes | yes | – | – | +| `j171` | experimental | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | – | +| `j172` | experimental | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | – | +| `j71b` | supported | yes | yes | yes | yes | – | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | +| `j72b` | experimental | yes | yes | yes | yes | – | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | +| `j120` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `j121` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `j207` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `j208` | experimental | yes | – | yes | – | – | – | – | – | – | – | – | – | yes | yes | – | – | +| `d20` | experimental | yes | – | – | – | – | yes | yes | – | – | – | yes | – | yes | yes | – | – | +| `d201` | experimental | yes | – | – | – | – | yes | yes | – | – | – | yes | – | yes | yes | – | – | +| `d21` | experimental | yes | – | – | – | – | yes | yes | – | – | – | yes | – | yes | yes | – | – | +| `d211` | experimental | yes | – | – | – | – | yes | yes | – | – | – | yes | – | yes | yes | – | – | +| `d22` | experimental | yes | yes | – | – | – | yes | yes | – | – | – | yes | – | yes | yes | – | – | +| `d221` | experimental | yes | yes | – | – | – | yes | yes | yes | – | – | yes | – | yes | yes | – | – | ### Where feature states and drivers disagree