From f2b0fa518172d60cac526cf3bc91afb31e33d8f4 Mon Sep 17 00:00:00 2001 From: Ryan Murray Date: Sat, 31 Oct 2026 22:39:59 +1100 Subject: [PATCH 1/2] media: apple: avd: stage H.264 frame operations and layout projections Add CPU frame operations, bounded instruction emission and independent slot scratch. Address coded data directly from OUTPUT DMA mappings and retain the licensed builder for host regression comparisons. Keep Apple transport fields and HOLD source-lifetime gaps explicit; this integration draft does not enable Apple decoding or multi-buffer HOLD pictures. Assisted-by: LLM Signed-off-by: Ryan Murray --- .../platform/apple/avd/H264-FRAME-OPS.rst | 55 ++ drivers/media/platform/apple/avd/Makefile | 2 +- drivers/media/platform/apple/avd/avd-drv.c | 4 + .../media/platform/apple/avd/avd-h264-fp.c | 215 +++++++ .../media/platform/apple/avd/avd-h264-fp.h | 81 +++ drivers/media/platform/apple/avd/avd-h264.c | 540 +++++++++++------- drivers/media/platform/apple/avd/avd-queue.h | 87 +++ drivers/media/platform/apple/avd/avd-v4l2.c | 1 + drivers/media/platform/apple/avd/avd.h | 6 + .../platform/apple/avd/tests/builder-main.c | 116 ++++ .../media/platform/apple/avd/tests/fp-main.c | 61 ++ .../apple/avd/tests/h264-builder-baseline.c | 534 +++++++++++++++++ .../platform/apple/avd/tests/host-compat.h | 67 +++ .../apple/avd/tests/run-host-tests.py | 37 ++ 14 files changed, 1609 insertions(+), 197 deletions(-) create mode 100644 drivers/media/platform/apple/avd/H264-FRAME-OPS.rst create mode 100644 drivers/media/platform/apple/avd/avd-h264-fp.c create mode 100644 drivers/media/platform/apple/avd/avd-h264-fp.h create mode 100644 drivers/media/platform/apple/avd/avd-queue.h create mode 100644 drivers/media/platform/apple/avd/tests/builder-main.c create mode 100644 drivers/media/platform/apple/avd/tests/fp-main.c create mode 100644 drivers/media/platform/apple/avd/tests/h264-builder-baseline.c create mode 100644 drivers/media/platform/apple/avd/tests/host-compat.h create mode 100644 drivers/media/platform/apple/avd/tests/run-host-tests.py diff --git a/drivers/media/platform/apple/avd/H264-FRAME-OPS.rst b/drivers/media/platform/apple/avd/H264-FRAME-OPS.rst new file mode 100644 index 0000000000000..44eb38f65b99f --- /dev/null +++ b/drivers/media/platform/apple/avd/H264-FRAME-OPS.rst @@ -0,0 +1,55 @@ +H.264 frame operations (integration draft) +========================================= + +The descriptor exports ``avd_h264_frame_ops`` alongside the legacy callbacks so +this codec branch builds on aurora-wip while the queue core is developed. The +legacy entry points must be removed when integrating the frame queue. Neither +prepare nor retire operates hardware or removes mem2mem buffers. The queue owns +request control setup/completion, source/destination lifetime and error retire. +The core must call retire after prepare failures that acquired codec_priv. + +Open prepare writes little-endian instruction words directly into frame->prog, +with a capacity check before every word. Coded data uses the OUTPUT DMA address +plus the plane's data_offset and the validated emulation-prevention-aware header +offset. No coherent allocation or copy of coded slice bytes remains. Scratch is +cached separately for each of the sixteen frame indices and retained until the +core drains the context. Motion vectors remain in each CAPTURE allocation's +reserved tail, so references retain their own vectors. H.264 declares DEP_NONE. + +Unresolved contract and protocol facts +-------------------------------------- + +A multi-buffer HOLD_CAPTURE_BUF picture cannot safely return intermediate +OUTPUT buffers before the final decode. Userspace may immediately overwrite a +returned OUTPUT, even if a kernel reference keeps its allocation alive. The +current shared contract says to return those buffers immediately. Until core +retains every source through the picture's completion, prepare and the legacy +path reject HOLD_CAPTURE_BUF with EOPNOTSUPP. This is an explicit limitation, +not successful multi-slice support. + +The V3 payload projection uses the MIT public layout in eiln/avd +``avid/h264/fp.py`` and its ``halv3.py`` field encoding. Transport PIODMA words +and FIFO configuration remain transport-owned and are not fabricated. The +public sources do not establish a Borage syntax layout. SPEC 6.2 establishes +only part of the command: the exact +0x3c/+0x44 packed-address rules and both +work-count bounds are still absent. The known Borage command projection returns +EOPNOTSUPP; it must never be published. V3 commands also return EOPNOTSUPP because +they cannot use the Borage command ABI. Apple decoding is not yet enabled. +T8112/T8140 are explicit TODO rows tied to per-SoC ledger task t-3862. + +Verification +------------ + +Run on a Linux build host:: + + python3 drivers/media/platform/apple/avd/tests/run-host-tests.py + +The host harness compiles the production builder and parameter writer with +kernel plumbing replaced by a small shim. It compares the word stream against +a frozen, licensed aurora-wip builder for synthetic V4L2 I/P/B controls, +1/4-slice programs, CABAC/CAVLC, scaling, weights and both address formats. +Four-slice program equivalence does not imply that the queue lifetime problem +above has been solved. Parameter tests check V3 offsets, address alignment, +array capacity, known Borage command offsets and unsupported-layout rejection. +These tests do not feed an encoded ffmpeg clip through GStreamer or compare +against an execution of eiln's bitstream decoder; those remain integration work. diff --git a/drivers/media/platform/apple/avd/Makefile b/drivers/media/platform/apple/avd/Makefile index 4d1877c414d41..9750a2b8558e8 100644 --- a/drivers/media/platform/apple/avd/Makefile +++ b/drivers/media/platform/apple/avd/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only -apple-avd-y := avd-drv.o avd-v4l2.o avd-hw.o avd-h264.o avd-vp9.o avd-hevc.o \ +apple-avd-y := avd-drv.o avd-v4l2.o avd-hw.o avd-h264.o avd-h264-fp.o avd-vp9.o avd-hevc.o \ avd-av1.o avd-av1-entropymode.o apple-avd-$(CONFIG_VIDEO_APPLE_AVD_KUNIT_TEST) += avd-h264-test.o obj-$(CONFIG_VIDEO_APPLE_AVD) += apple-avd.o diff --git a/drivers/media/platform/apple/avd/avd-drv.c b/drivers/media/platform/apple/avd/avd-drv.c index bf5bf96f4c62a..aef1b52bd513b 100644 --- a/drivers/media/platform/apple/avd/avd-drv.c +++ b/drivers/media/platform/apple/avd/avd-drv.c @@ -674,6 +674,7 @@ static const struct avd_variant avd_t8103_variant = { }; static const struct avd_variant avd_t6000_variant = { + .h264_fp_layout = AVD_H264_FP_V3, .vp_slots = { [AVD_CODEC_HEVC] = 4, [AVD_CODEC_H264] = 4, @@ -694,6 +695,7 @@ static const struct avd_variant avd_t6000_variant = { }; static const struct avd_variant avd_t8112_variant = { + .h264_fp_layout = AVD_H264_FP_T8112_TODO, .vp_slots = { [AVD_CODEC_HEVC] = 4, [AVD_CODEC_H264] = 4, @@ -754,6 +756,7 @@ static const struct avd_variant avd_t8122_variant = { }; static const struct avd_variant avd_t8140_variant = { + .h264_fp_layout = AVD_H264_FP_T8140_TODO, .vp_slots = { [AVD_CODEC_HEVC] = 2, [AVD_CODEC_H264] = 1, @@ -811,6 +814,7 @@ static const struct avd_variant avd_t8132_variant = { * verified on the hardware. */ static const struct avd_variant avd_t6040_variant = { + .h264_fp_layout = AVD_H264_FP_BORAGE, .vp_slots = { [AVD_CODEC_HEVC] = 4, [AVD_CODEC_H264] = 4, diff --git a/drivers/media/platform/apple/avd/avd-h264-fp.c b/drivers/media/platform/apple/avd/avd-h264-fp.c new file mode 100644 index 0000000000000..d3a6531640f22 --- /dev/null +++ b/drivers/media/platform/apple/avd/avd-h264-fp.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* H.264 frame-parameter facts. No firmware or captured frame images. */ +#include +#include +#include +#include + +#include "avd.h" +#include "avd-queue.h" +#include "avd-h264-fp.h" + +/* + * Each generation has an independent provenance. Register revisions or firmware + * tiers do not imply a frame layout. Select this enum from OF match data only. + * + * V3: https://github.com/eiln/avd/blob/main/avid/h264/fp.py (MIT), with + * avid/h264/halv3.py for the address shifts and instruction fields. + * Borage: clean-room protocol SPEC (2026-10-31), sections 4.1, 6.1, 6.2. + * TODO rows: Daybreak per-SoC ledger task t-3862; no V3/Borage fallback. + */ +struct avd_h264_fp_layout_info { + enum avd_h264_fp_layout layout; + size_t size; + const char *source; +}; + +static const struct avd_h264_fp_layout_info avd_h264_fp_layouts[] = { + { AVD_H264_FP_V3, 0x8b4e0, "MIT eiln/avd avid/h264/fp.py: V3, T6000" }, + { AVD_H264_FP_BORAGE, 0x1130d0, "SPEC 6.2: T6040/Borage" }, + { AVD_H264_FP_T8112_TODO, 0, "TODO t-3862: T8112 per-SoC ledger" }, + { AVD_H264_FP_T8140_TODO, 0, "TODO t-3862: T8140 per-SoC ledger" }, +}; + +/* Offsets and array capacities from the public V3 layout, not Borage. */ +static const struct { + u16 offset; + u16 count; +} v3_fields[AVD_H264_FP_NONE] = { + [AVD_H264_FP_MV_ABOVE_INFO] = { 0xb8, 1 }, + [AVD_H264_FP_MV_COLOR] = { 0xbc, 1 }, + [AVD_H264_FP_HDR_D0_REF_HDR] = { 0xd0, 16 }, + [AVD_H264_FP_HDR_4C_PIC_SCALING_LIST_DIMS] = { 0x4c, 1 }, + [AVD_H264_FP_SCL_46C_PIC_SCALING_MATRIX_4X4] = { 0x46c, 24 }, + [AVD_H264_FP_SCL_4CC_PIC_SCALING_MATRIX_8X8] = { 0x4cc, 96 }, + [AVD_H264_FP_HDR_34_START_HDR] = { 0x34, 1 }, + [AVD_H264_FP_HDR_38_MODE] = { 0x38, 1 }, + [AVD_H264_FP_HDR_3C_HEIGHT_WIDTH] = { 0x3c, 1 }, + [AVD_H264_FP_HDR_40_ZERO] = { 0x40, 1 }, + [AVD_H264_FP_HDR_28_HEIGHT_WIDTH_SHIFT3] = { 0x28, 1 }, + [AVD_H264_FP_HDR_2C_SPS_PARAM] = { 0x2c, 1 }, + [AVD_H264_FP_HDR_44_FLAGS] = { 0x44, 1 }, + [AVD_H264_FP_HDR_48_CHROMA_QP_INDEX_OFFSET] = { 0x48, 1 }, + [AVD_H264_FP_HDR_58_CONST_3A] = { 0x58, 1 }, + [AVD_H264_FP_HDR_9C_PPS_TILE_ADDR_LSB8] = { 0x9c, 1 }, + [AVD_H264_FP_IP_ABOVE] = { 0xa0, 1 }, + [AVD_H264_FP_LF_ABOVE] = { 0xa4, 1 }, + [AVD_H264_FP_LF_ABOVE_INFO] = { 0xa8, 1 }, + [AVD_H264_FP_HDR_210_Y_ADDR_LSB8] = { 0x210, 1 }, + [AVD_H264_FP_HDR_218_WIDTH_ALIGN] = { 0x218, 1 }, + [AVD_H264_FP_HDR_214_UV_ADDR_LSB8] = { 0x214, 1 }, + [AVD_H264_FP_HDR_21C_WIDTH_ALIGN] = { 0x21c, 1 }, + [AVD_H264_FP_HDR_54_HEIGHT_WIDTH] = { 0x54, 1 }, + [AVD_H264_FP_SLC_76C_CMD_WEIGHTS_DENOM] = { 0x76c, 1 }, + [AVD_H264_FP_SLC_LUMA_WEIGHTS] = { 0x770, 96 }, + [AVD_H264_FP_SLC_LUMA_OFFSETS] = { 0x8f0, 96 }, + [AVD_H264_FP_SLC_CHROMA_WEIGHTS_0_] = { 0x770, 96 }, + [AVD_H264_FP_SLC_CHROMA_OFFSETS_0_] = { 0x8f0, 96 }, + [AVD_H264_FP_SLC_CHROMA_WEIGHTS_1_] = { 0x770, 96 }, + [AVD_H264_FP_SLC_CHROMA_OFFSETS_1_] = { 0x8f0, 96 }, + [AVD_H264_FP_SLC_A7C_CMD_SET_CODED_SLICE] = { 0xa7c, 1 }, + [AVD_H264_FP_SLC_A84_SLICE_ADDR_LOW] = { 0xa84, 1 }, + [AVD_H264_FP_SLC_A88_SLICE_HDR_SIZE] = { 0xa88, 1 }, + [AVD_H264_FP_SLC_A70_CMD_QUANT_PARAM] = { 0xa70, 1 }, + [AVD_H264_FP_SLC_A74_CMD_DEBLOCKING_FILTER] = { 0xa74, 1 }, + [AVD_H264_FP_SLC_6E8_CMD_REF_LIST_0] = { 0x6e8, 32 }, + [AVD_H264_FP_SLC_6E4_CMD_REF_TYPE] = { 0x6e4, 1 }, + [AVD_H264_FP_SLC_A78_SPS_TILE_ADDR2_LSB8] = { 0xa78, 1 }, +}; + +static int fp_word(struct avd_buf *buf, size_t offset, u32 word) +{ + if (!buf || !buf->cpu || buf->size < sizeof(u32) || + offset > buf->size - sizeof(u32)) + return -ENOSPC; + put_unaligned_le32(word, (u8 *)buf->cpu + offset); + return 0; +} + +int avd_h264_fp_begin(struct avd_h264_fp_cursor *cursor, struct avd_buf *buf, + enum avd_h264_fp_layout layout) +{ + unsigned int i; + size_t size = 0; + + memset(cursor, 0, sizeof(*cursor)); + for (i = 0; i < ARRAY_SIZE(avd_h264_fp_layouts); i++) + if (avd_h264_fp_layouts[i].layout == layout) + size = avd_h264_fp_layouts[i].size; + if (layout != AVD_H264_FP_V3 || !size) + return -EOPNOTSUPP; + if (!buf || !buf->cpu || buf->size < size) + return -ENOSPC; + memset(buf->cpu, 0, size); + cursor->buf = buf; + /* V3 piodma words are transport-owned; do not invent their encodings. */ + fp_word(buf, 0x04, 1); + fp_word(buf, 0x14, 0xdeadcafe); + fp_word(buf, 0x18, 0x101); + fp_word(buf, 0x1c, 1); + fp_word(buf, 0x20, 0x8b4c0); + /* Public DFW metadata in AVDH264V3DFWScalingList. */ + fp_word(buf, 0x284, 0x1de28b5); + fp_word(buf, 0x468, 0x1de28b5); + fp_word(buf, 0x64c, 0x764099); + fp_word(buf, 0x6c8, 0x124111); + return 0; +} + +int avd_h264_fp_emit(struct avd_h264_fp_cursor *cursor, + enum avd_h264_fp_field field, u32 word) +{ + unsigned int index; + + if (field == AVD_H264_FP_NONE) + return 0; + if (field >= AVD_H264_FP_NONE) + return -EINVAL; + /* Luma and both chroma components share one ordered weights array. */ + if (v3_fields[field].offset == 0x770) + index = cursor->weights++; + else if (v3_fields[field].offset == 0x8f0) + index = cursor->offsets++; + else + index = cursor->count[field]++; + if (index >= v3_fields[field].count) + return -E2BIG; + return fp_word(cursor->buf, v3_fields[field].offset + index * 4, word); +} + +int avd_h264_fp_address(struct avd_h264_fp_cursor *cursor, + enum avd_h264_fp_field field, unsigned int index, + dma_addr_t addr) +{ + if (field == AVD_H264_FP_NONE) + return 0; + /* Builder scratch annotations already identify distinct fields. */ + if (field != AVD_H264_FP_HDR_9C_PPS_TILE_ADDR_LSB8) + index = 0; + if (field >= AVD_H264_FP_NONE || index > 7 || + (addr & 0xff) || (addr >> 8) > U32_MAX) + return -ERANGE; + return fp_word(cursor->buf, v3_fields[field].offset + index * 4, addr >> 8); +} + +int avd_h264_fp_ref(struct avd_h264_fp_cursor *cursor, bool reference, + unsigned int index, unsigned int plane, dma_addr_t addr) +{ + if (plane >= 4 || index >= 16 || (addr & 0x7f) || (addr >> 7) > U32_MAX) + return -ERANGE; + return fp_word(cursor->buf, + reference ? 0x110 + plane * 0x40 + index * 4 : 0xc0 + plane * 4, + addr >> 7); +} + +/* + * [C] Borage fp+0x3d8 bits 0:6 -> cmd+0x00 bits 2:8 (SPEC 6.2). + * [C] Borage fp+0x18/+0x19 -> cmd+0x00 bits 0/1 (SPEC 6.1). + * [C] cmd+0x3c/+0x44 combine fp+0x3dc/+0x5c0/+0x7a4 and + * MMIO 0x01482220; the shift/mask rules are absent from SPEC 6.2. + * The two +0x1c work counts' exact bounds are also not specified there. + * Keep these fields unnamed; no guessed command may reach firmware. + */ +int avd_h264_borage_cmd_fields(struct avd_frame *f) +{ + const u8 *fp; + u32 work, slot, flags; + u16 count; + + if (!f || !f->fp || !f->fp->cpu || f->fp->size < 0x1130d0 || + f->idx >= AVD_MAX_FRAMES) + return -EINVAL; + fp = f->fp->cpu; + count = get_unaligned_le16(fp + 0x1a); + /* validateAvcFields' independently established slice-count bound. */ + if (count >= 0x385) + return -EINVAL; + work = (f->cmd[1] >> 16) & 0xffff; + if (work >= 4) + return -EINVAL; + work = 0x01483120 + 0x1814 * work; + slot = 0x1130d0 * f->idx; + flags = (fp[0x18] & 1) | ((fp[0x19] & 1) << 1) | + ((get_unaligned_le32(fp + 0x3d8) & 0x7f) << 2) | ((u32)count << 16); + /* Preserve the common prefix outside the codec's documented masks. */ + f->cmd[0] = (f->cmd[0] & ~0xffff01ffU) | flags; + f->cmd[1] = (f->cmd[1] & 0xffff0000U) | get_unaligned_le16(fp + 0x1c); + f->cmd[3] = work; + f->cmd[4] = slot + 0x3dc; + f->cmd[5] = work + 0x0c; + f->cmd[6] = slot + 0x5c0; + f->cmd[7] = slot + 0x8a0; + f->cmd[10] = work + 0x34; + f->cmd[11] = work + 0x350; + f->cmd[12] = work + 0x3ac; + /* Projection is reviewable/testable, but not a publishable descriptor. */ + return -EOPNOTSUPP; +} + +int avd_h264_fp_pack_cmd(enum avd_h264_fp_layout layout, struct avd_frame *f) +{ + /* V3 command transport is not the Borage 0x60 command (SPEC 6.2). */ + if (layout != AVD_H264_FP_BORAGE) + return -EOPNOTSUPP; + return avd_h264_borage_cmd_fields(f); +} diff --git a/drivers/media/platform/apple/avd/avd-h264-fp.h b/drivers/media/platform/apple/avd/avd-h264-fp.h new file mode 100644 index 0000000000000..724e578e86738 --- /dev/null +++ b/drivers/media/platform/apple/avd/avd-h264-fp.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +#ifndef AVD_H264_FP_H_ +#define AVD_H264_FP_H_ + +#include + +struct avd_buf; +struct avd_frame; + +enum avd_h264_fp_layout { + AVD_H264_FP_UNKNOWN, + AVD_H264_FP_V3, + AVD_H264_FP_BORAGE, + AVD_H264_FP_T8112_TODO, + AVD_H264_FP_T8140_TODO, +}; + +enum avd_h264_fp_field { + AVD_H264_FP_MV_ABOVE_INFO, + AVD_H264_FP_MV_COLOR, + AVD_H264_FP_HDR_D0_REF_HDR, + AVD_H264_FP_HDR_4C_PIC_SCALING_LIST_DIMS, + AVD_H264_FP_SCL_46C_PIC_SCALING_MATRIX_4X4, + AVD_H264_FP_SCL_4CC_PIC_SCALING_MATRIX_8X8, + AVD_H264_FP_HDR_34_START_HDR, + AVD_H264_FP_HDR_38_MODE, + AVD_H264_FP_HDR_3C_HEIGHT_WIDTH, + AVD_H264_FP_HDR_40_ZERO, + AVD_H264_FP_HDR_28_HEIGHT_WIDTH_SHIFT3, + AVD_H264_FP_HDR_2C_SPS_PARAM, + AVD_H264_FP_HDR_44_FLAGS, + AVD_H264_FP_HDR_48_CHROMA_QP_INDEX_OFFSET, + AVD_H264_FP_HDR_58_CONST_3A, + AVD_H264_FP_HDR_9C_PPS_TILE_ADDR_LSB8, + AVD_H264_FP_IP_ABOVE, + AVD_H264_FP_LF_ABOVE, + AVD_H264_FP_LF_ABOVE_INFO, + AVD_H264_FP_HDR_210_Y_ADDR_LSB8, + AVD_H264_FP_HDR_218_WIDTH_ALIGN, + AVD_H264_FP_HDR_214_UV_ADDR_LSB8, + AVD_H264_FP_HDR_21C_WIDTH_ALIGN, + AVD_H264_FP_HDR_54_HEIGHT_WIDTH, + AVD_H264_FP_SLC_76C_CMD_WEIGHTS_DENOM, + AVD_H264_FP_SLC_LUMA_WEIGHTS, + AVD_H264_FP_SLC_LUMA_OFFSETS, + AVD_H264_FP_SLC_CHROMA_WEIGHTS_0_, + AVD_H264_FP_SLC_CHROMA_OFFSETS_0_, + AVD_H264_FP_SLC_CHROMA_WEIGHTS_1_, + AVD_H264_FP_SLC_CHROMA_OFFSETS_1_, + AVD_H264_FP_SLC_A7C_CMD_SET_CODED_SLICE, + AVD_H264_FP_SLC_A84_SLICE_ADDR_LOW, + AVD_H264_FP_SLC_A88_SLICE_HDR_SIZE, + AVD_H264_FP_SLC_A70_CMD_QUANT_PARAM, + AVD_H264_FP_SLC_A74_CMD_DEBLOCKING_FILTER, + AVD_H264_FP_SLC_6E8_CMD_REF_LIST_0, + AVD_H264_FP_SLC_6E4_CMD_REF_TYPE, + AVD_H264_FP_SLC_A78_SPS_TILE_ADDR2_LSB8, + AVD_H264_FP_NONE, +}; + +struct avd_h264_fp_cursor { + struct avd_buf *buf; + u16 count[AVD_H264_FP_NONE]; + u16 weights; + u16 offsets; +}; + +int avd_h264_fp_begin(struct avd_h264_fp_cursor *cursor, struct avd_buf *buf, + enum avd_h264_fp_layout layout); +int avd_h264_fp_emit(struct avd_h264_fp_cursor *cursor, + enum avd_h264_fp_field field, u32 word); +int avd_h264_fp_address(struct avd_h264_fp_cursor *cursor, + enum avd_h264_fp_field field, unsigned int index, + dma_addr_t addr); +int avd_h264_fp_ref(struct avd_h264_fp_cursor *cursor, bool reference, + unsigned int index, unsigned int plane, dma_addr_t addr); +int avd_h264_fp_pack_cmd(enum avd_h264_fp_layout layout, struct avd_frame *f); +/* Known Borage projection only; returns -EOPNOTSUPP for unresolved fields. */ +int avd_h264_borage_cmd_fields(struct avd_frame *f); + +#endif /* AVD_H264_FP_H_ */ diff --git a/drivers/media/platform/apple/avd/avd-h264.c b/drivers/media/platform/apple/avd/avd-h264.c index e8bc8d0066ead..65d7994b92678 100644 --- a/drivers/media/platform/apple/avd/avd-h264.c +++ b/drivers/media/platform/apple/avd/avd-h264.c @@ -22,6 +22,8 @@ #include "avd.h" #include "avd-inst.h" +#include "avd-queue.h" +#include "avd-h264-fp.h" #define H264_SCL_DIMS (0x1000000 | ((64 / 4) << 5) | (((16 / 4) << 5) - 1)) @@ -31,13 +33,17 @@ #define H264_TRANSFORM_8X8_MODE(v) FIELD_PREP(BIT(7), !!(v)) -/* - * Most slices held for one picture. This bounds the slice table, the job - * table and the memory that a client can pin. - */ +/* not a hardware constraint */ #define MAX_SLICES 4096 +struct avd_h264_ctx; + struct avd_h264_run { + struct avd_frame *frame; + struct avd_h264_ctx *picture; + struct avd_h264_fp_cursor fp_cursor; + unsigned int ref_index; + int error; struct avd_run base; const struct v4l2_ctrl_h264_decode_params *decode_params; @@ -66,15 +72,14 @@ struct avd_h264_ctx { struct v4l2_h264_reference b1[V4L2_H264_REF_LIST_LEN]; } reflists; - struct avd_buf *slices; + /* CPU view and DMA address of OUTPUT; never owns coded-data memory. */ + struct avd_buf slice; + struct avd_h264_ctx *slots[AVD_MAX_FRAMES]; + bool busy; struct avd_buf *active_slice; size_t slice_num; - size_t alloc_slice_num; - /* allocated size of the held slices */ - size_t slice_bytes; struct avd_h264_bufs { - struct avd_buf inst; struct avd_buf pipe_state; struct avd_buf above_info; struct avd_buf lf_above_info; @@ -107,24 +112,88 @@ static inline u32 mv_color_size(u32 w, u32 h) /* sorry for the formatting */ +static void avd_h264_emit(struct avd_ctx *ctx, struct avd_h264_run *run, + u32 word, enum avd_h264_fp_field field) +{ + struct avd_frame *f = run->frame; + + if (!f) { + (push)(ctx, word); + return; + } + if (run->error) + return; + if (f->prog && f->prog->cpu) { + if (f->prog_words >= f->prog->size / sizeof(__le32)) { + run->error = -ENOSPC; + return; + } + ((__le32 *)f->prog->cpu)[f->prog_words++] = cpu_to_le32(word); + } else if (!run->fp_cursor.buf) { + run->error = -EINVAL; + return; + } + if (run->fp_cursor.buf) + run->error = avd_h264_fp_emit(&run->fp_cursor, field, word); +} + +static void avd_h264_emit_addr(struct avd_ctx *ctx, struct avd_h264_run *run, + dma_addr_t addr, enum avd_h264_fp_field field, + unsigned int index) +{ + /* Open instruction addresses retain the existing per-variant encoding. */ + if (ctx->dev->variant->quirks & AVD_QUIRK_LSR) { + avd_h264_emit(ctx, run, addr >> 8, AVD_H264_FP_NONE); + } else { + avd_h264_emit(ctx, run, lower_32_bits(addr), AVD_H264_FP_NONE); + avd_h264_emit(ctx, run, upper_32_bits(addr), AVD_H264_FP_NONE); + } + if (run->fp_cursor.buf && !run->error) + run->error = avd_h264_fp_address(&run->fp_cursor, field, index, addr); +} + +static void avd_h264_emit_comp(struct avd_ctx *ctx, struct avd_h264_run *run, + dma_addr_t addr, bool reference) +{ + unsigned int i; + + for (i = 0; i < 4; i++) { + dma_addr_t a = addr + ctx->comp.offsets[i]; + + if (ctx->dev->variant->quirks & AVD_QUIRK_LSR) + avd_h264_emit(ctx, run, a >> 7, AVD_H264_FP_NONE); + else + avd_h264_emit_addr(ctx, run, a, AVD_H264_FP_NONE, 0); + if (run->fp_cursor.buf && !run->error) + run->error = avd_h264_fp_ref(&run->fp_cursor, reference, + run->ref_index, i, a); + } +} + +#undef push +#undef pusha +#define push(word, field) avd_h264_emit(ctx, run, (word), (field)) +#define pusha(addr, field, index) \ + avd_h264_emit_addr(ctx, run, (addr), (field), (index)) + static void stream_refs(struct avd_ctx *ctx, struct avd_h264_run *run) { const struct v4l2_ctrl_h264_decode_params *decode = run->decode_params; const struct v4l2_h264_dpb_entry *dpb = decode->dpb; - struct avd_h264_ctx *h264_ctx = ctx->priv; + struct avd_h264_ctx *h264_ctx = run->picture; struct avd_decoded_buffer *dst, *ref; dma_addr_t addr; dst = vb2_to_avd_decoded_buf(&run->base.bufs.dst->vb2_buf); - push(0, ""); - pusha(h264_ctx->bufs.mv_above_info.addr, "mv_above_info", 7); - pusha(run->addresses.mv_color, "mv_color", 0); + push(0, AVD_H264_FP_NONE); + pusha(h264_ctx->bufs.mv_above_info.addr, AVD_H264_FP_MV_ABOVE_INFO, 7); + pusha(run->addresses.mv_color, AVD_H264_FP_MV_COLOR, 0); - push(0, ""); - push(0, ""); - push(0, ""); - push(0, ""); + push(0, AVD_H264_FP_NONE); + push(0, AVD_H264_FP_NONE); + push(0, AVD_H264_FP_NONE); + push(0, AVD_H264_FP_NONE); for (int i = 0; i < ARRAY_SIZE(decode->dpb); i++) { if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_VALID)) @@ -141,9 +210,10 @@ static void stream_refs(struct avd_ctx *ctx, struct avd_h264_run *run) V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM) | AVD_REF_DELTA_POC(run->cur_poc - dpb[i].top_field_order_cnt), - "hdr_d0_ref_hdr"); + AVD_H264_FP_HDR_D0_REF_HDR); - push_comp(ctx, addr, ctx->comp.offsets); + avd_h264_emit_comp(ctx, run, addr, true); + run->ref_index++; } } @@ -153,7 +223,7 @@ static void stream_scaling(struct avd_ctx *ctx, struct avd_h264_run *run) const struct v4l2_ctrl_h264_scaling_matrix *scaling = run->scaling_matrix; - push(H264_SCL_DIMS, "hdr_4c_pic_scaling_list_dims"); + push(H264_SCL_DIMS, AVD_H264_FP_HDR_4C_PIC_SCALING_LIST_DIMS); for (int i = 0; i < 6; i++) for (int j = 0; j < 16; j += 4) @@ -165,7 +235,7 @@ static void stream_scaling(struct avd_ctx *ctx, struct avd_h264_run *run) scaling->scaling_list_4x4[i][j + 2]) | AVD_SCALING_I0( scaling->scaling_list_4x4[i][j + 3]), - "scl_46c_pic_scaling_matrix_4x4"); + AVD_H264_FP_SCL_46C_PIC_SCALING_MATRIX_4X4); /* Instead of 8x8 raster scan order avd expects 4 4x4 subblocks */ static const u8 map[16] = { @@ -190,15 +260,15 @@ static void stream_scaling(struct avd_ctx *ctx, struct avd_h264_run *run) AVD_SCALING_I0( scaling->scaling_list_8x8 [i][map[j] + 3]), - "scl_4cc_pic_scaling_matrix_8x8"); + AVD_H264_FP_SCL_4CC_PIC_SCALING_MATRIX_8X8); } else { for (int i = 0; i < ARRAY_SIZE(default_8x8_intra); i++) push(default_8x8_intra[i], - "scl_4cc_pic_scaling_matrix_8x8"); + AVD_H264_FP_SCL_4CC_PIC_SCALING_MATRIX_8X8); for (int i = 0; i < ARRAY_SIZE(default_8x8_inter); i++) push(default_8x8_inter[i], - "scl_4cc_pic_scaling_matrix_8x8"); + AVD_H264_FP_SCL_4CC_PIC_SCALING_MATRIX_8X8); } } @@ -208,7 +278,7 @@ static void stream_hdr(struct avd_ctx *ctx, struct avd_h264_run *run) const struct v4l2_ctrl_h264_sps *sps = run->sps; const struct v4l2_ctrl_h264_pps *pps = run->pps; struct avd_dev *avd = ctx->dev; - struct avd_h264_ctx *h264_ctx = ctx->priv; + struct avd_h264_ctx *h264_ctx = run->picture; u32 bytesperline; u32 width = (sps->pic_width_in_mbs_minus1 + 1) * 16; u32 height = (sps->pic_height_in_map_units_minus1 + 1) * 16; @@ -220,18 +290,18 @@ static void stream_hdr(struct avd_ctx *ctx, struct avd_h264_run *run) AVD_OP_HDR_CONST | AVD_OP_HDR_FLAG_PIPE_STATE( !(avd->variant->quirks & AVD_QUIRK_NO_PIPE_STATE)), - "hdr_34_start_hdr"); + AVD_H264_FP_HDR_34_START_HDR); - push(AVD_HDR_CODEC_MODE(AVD_CODEC_H264), "hdr_38_mode"); + push(AVD_HDR_CODEC_MODE(AVD_CODEC_H264), AVD_H264_FP_HDR_38_MODE); push(AVD_HDR_HEIGHT(height - 1) | AVD_HDR_WIDTH(width - 1), - "hdr_3c_height_width"); + AVD_H264_FP_HDR_3C_HEIGHT_WIDTH); - push(0, "hdr_40_zero"); + push(0, AVD_H264_FP_HDR_40_ZERO); push(AVD_HDR_HEIGHT((height - 1) >> 3) | AVD_HDR_WIDTH((width - 1) >> 3), - "hdr_28_height_width_shift3"); + AVD_H264_FP_HDR_28_HEIGHT_WIDTH_SHIFT3); /* TODO: did i mix up luma/chroma in hevc or here? */ push(AVD_HDR_COMMON_CHROMA_FORMAT(sps->chroma_format_idc) | @@ -245,7 +315,7 @@ static void stream_hdr(struct avd_ctx *ctx, struct avd_h264_run *run) AVD_HDR_COMMON_FLAG0( sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE), - "hdr_2c_sps_param"); + AVD_H264_FP_HDR_2C_SPS_PARAM); push(H264_FLAG_ENTROPY_CODING_MODE( pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE) | @@ -254,52 +324,52 @@ static void stream_hdr(struct avd_ctx *ctx, struct avd_h264_run *run) h264_FLAG_CI_PRED( pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED), - "hdr_44_flags"); + AVD_H264_FP_HDR_44_FLAGS); push(AVD_HDR_H26X_QP_OFFSET_CB(pps->chroma_qp_index_offset) | AVD_HDR_H26X_QP_OFFSET_CR( pps->second_chroma_qp_index_offset), - "hdr_48_chroma_qp_index_offset"); + AVD_H264_FP_HDR_48_CHROMA_QP_INDEX_OFFSET); push(AVD_HDR_FEAT_H26X | AVD_HDR_FEAT_COMMON | AVD_HDR_FEAT_H264 | AVD_HDR_FEAT_PIPE_STATE_EN( !(avd->variant->quirks & AVD_QUIRK_NO_PIPE_STATE)), - "hdr_58_const_3a"); + AVD_H264_FP_HDR_58_CONST_3A); - push(0, ""); - push(0, ""); + push(0, AVD_H264_FP_NONE); + push(0, AVD_H264_FP_NONE); if (avd->variant->revision == 3) - push(0, "zero"); + push(0, AVD_H264_FP_NONE); - pusha(h264_ctx->bufs.above_info.addr, "hdr_9c_pps_tile_addr_lsb8", 0); + pusha(h264_ctx->bufs.above_info.addr, AVD_H264_FP_HDR_9C_PPS_TILE_ADDR_LSB8, 0); - push(0, ""); - push(0, ""); + push(0, AVD_H264_FP_NONE); + push(0, AVD_H264_FP_NONE); if (avd->variant->revision == 3) - push(0, "zero"); + push(0, AVD_H264_FP_NONE); else if (!(avd->variant->quirks & AVD_QUIRK_NO_PIPE_STATE)) - pusha(h264_ctx->bufs.pipe_state.addr, "pipe_state", 0); + pusha(h264_ctx->bufs.pipe_state.addr, AVD_H264_FP_NONE, 0); - pusha(h264_ctx->bufs.ip_above.addr, "ip_above", 1); - pusha(h264_ctx->bufs.lf_above.addr, "lf_above", 2); - pusha(h264_ctx->bufs.lf_above_info.addr, "lf_above_info", 3); - push(0, ""); + pusha(h264_ctx->bufs.ip_above.addr, AVD_H264_FP_IP_ABOVE, 1); + pusha(h264_ctx->bufs.lf_above.addr, AVD_H264_FP_LF_ABOVE, 2); + pusha(h264_ctx->bufs.lf_above_info.addr, AVD_H264_FP_LF_ABOVE_INFO, 3); + push(0, AVD_H264_FP_NONE); - push_comp(ctx, run->base.comp_out, ctx->comp.offsets); + avd_h264_emit_comp(ctx, run, run->base.comp_out, false); bytesperline = ctx->decoded_fmt.fmt.pix_mp.plane_fmt[0].bytesperline; if (avd->variant->quirks & AVD_QUIRK_LSR) bytesperline = bytesperline >> 4; - pusha(run->base.y_out, "hdr_210_y_addr_lsb8", 0); - push(bytesperline, "hdr_218_width_align"); - pusha(run->base.uv_out, "hdr_214_uv_addr_lsb8", 0); - push(bytesperline, "hdr_21c_width_align"); + pusha(run->base.y_out, AVD_H264_FP_HDR_210_Y_ADDR_LSB8, 0); + push(bytesperline, AVD_H264_FP_HDR_218_WIDTH_ALIGN); + pusha(run->base.uv_out, AVD_H264_FP_HDR_214_UV_ADDR_LSB8, 0); + push(bytesperline, AVD_H264_FP_HDR_21C_WIDTH_ALIGN); - push(0, "cm3_mark_end_section"); - push(((height - 1) << 16) | (width - 1), "hdr_54_height_width"); + push(0, AVD_H264_FP_NONE); + push(((height - 1) << 16) | (width - 1), AVD_H264_FP_HDR_54_HEIGHT_WIDTH); if (!(decode->flags & V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC)) stream_refs(ctx, run); @@ -307,7 +377,7 @@ static void stream_hdr(struct avd_ctx *ctx, struct avd_h264_run *run) if (pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT) stream_scaling(ctx, run); else - push(0, "cm3_mark_end_section_scl"); + push(0, AVD_H264_FP_NONE); } #define DEFAULT_WEIGHT_DENOM \ @@ -336,7 +406,7 @@ static void stream_weights(struct avd_ctx *ctx, struct avd_h264_run *run) weights->chroma_log2_weight_denom : 0) | (default_weights ? DEFAULT_WEIGHT_DENOM : 0), - "slc_76c_cmd_weights_denom"); + AVD_H264_FP_SLC_76C_CMD_WEIGHTS_DENOM); if (!pred_weight_req) return; @@ -361,11 +431,11 @@ static void stream_weights(struct avd_ctx *ctx, struct avd_h264_run *run) AVD_OP_WEIGHTS_INDEX(i) | AVD_OP_WEIGHTS_WEIGHT( factors.luma_weight[i]), - "slc_luma_weights"); + AVD_H264_FP_SLC_LUMA_WEIGHTS); push(AVD_OP_OFFSETS | AVD_OP_OFFSETS_OFFSET( factors.luma_offset[i]), - "slc_luma_offsets"); + AVD_H264_FP_SLC_LUMA_OFFSETS); } if ((factors.chroma_weight[i][0] != chroma_denom) || @@ -377,21 +447,21 @@ static void stream_weights(struct avd_ctx *ctx, struct avd_h264_run *run) AVD_OP_WEIGHTS_INDEX(i) | AVD_OP_WEIGHTS_WEIGHT( factors.chroma_weight[i][0]), - "slc_chroma_weights[0]"); + AVD_H264_FP_SLC_CHROMA_WEIGHTS_0_); push(AVD_OP_OFFSETS | AVD_OP_OFFSETS_OFFSET( factors.chroma_offset[i][0]), - "slc_chroma_offsets[0]"); + AVD_H264_FP_SLC_CHROMA_OFFSETS_0_); push(AVD_OP_WEIGHTS | AVD_OP_WEIGHTS_IDENT(3) | AVD_OP_WEIGHTS_LIST_IDX(y) | AVD_OP_WEIGHTS_INDEX(i) | AVD_OP_WEIGHTS_WEIGHT( factors.chroma_weight[i][1]), - "slc_chroma_weights[1]"); + AVD_H264_FP_SLC_CHROMA_WEIGHTS_1_); push(AVD_OP_OFFSETS | AVD_OP_OFFSETS_OFFSET( factors.chroma_offset[i][1]), - "slc_chroma_offsets[1]"); + AVD_H264_FP_SLC_CHROMA_OFFSETS_1_); } } } @@ -436,7 +506,7 @@ static void stream_slice(struct avd_ctx *ctx, struct avd_h264_run *run) const struct v4l2_ctrl_h264_pps *pps = run->pps; const struct v4l2_ctrl_h264_sps *sps = run->sps; const struct v4l2_ctrl_h264_slice_params *sl = run->slice_params; - struct avd_h264_ctx *h264_ctx = ctx->priv; + struct avd_h264_ctx *h264_ctx = run->picture; u32 payload_len = h264_ctx->active_slice->size; bool en_mode = (pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE) == 0; @@ -448,22 +518,22 @@ static void stream_slice(struct avd_ctx *ctx, struct avd_h264_run *run) AVD_OP_CODED_DATA_BIT_OFF( en_mode ? (sl->header_bit_size % 8) : 0) | AVD_OP_CODED_DATA_ADDR(slc_a84 >> 32), - "slc_a7c_cmd_set_coded_slice"); - push((u32)(slc_a84 & 0xffffffff), "slc_a84_slice_addr_low"); + AVD_H264_FP_SLC_A7C_CMD_SET_CODED_SLICE); + push((u32)(slc_a84 & 0xffffffff), AVD_H264_FP_SLC_A84_SLICE_ADDR_LOW); /* should not include trailing 0? */ - push(payload_len - off, "slc_a88_slice_hdr_size"); + push(payload_len - off, AVD_H264_FP_SLC_A88_SLICE_HDR_SIZE); push(AVD_OP_SL_LOC | AVD_OP_SL_LOC_Y(sl->first_mb_in_slice / (sps->pic_width_in_mbs_minus1 + 1)) | AVD_OP_SL_LOC_X(sl->first_mb_in_slice % (sps->pic_width_in_mbs_minus1 + 1)), - "cm3_cmd_exec_mb_vp"); + AVD_H264_FP_NONE); push(AVD_OP_QP | AVD_OP_QP_VAL(26 + pps->pic_init_qp_minus26 + sl->slice_qp_delta), - "slc_a70_cmd_quant_param"); + AVD_H264_FP_SLC_A70_CMD_QUANT_PARAM); push(AVD_OP_DBLK | AVD_OP_DBLK_FLAG_FULL_EN( @@ -472,7 +542,7 @@ static void stream_slice(struct avd_ctx *ctx, struct avd_h264_run *run) 1) | AVD_OP_DBLK_OFF1(sl->slice_beta_offset_div2) | AVD_OP_DBLK_OFF0(sl->slice_alpha_c0_offset_div2), - "slc_a74_cmd_deblocking_filter"); + AVD_H264_FP_SLC_A74_CMD_DEBLOCKING_FILTER); if (sl->slice_type == V4L2_H264_SLICE_TYPE_P || sl->slice_type == V4L2_H264_SLICE_TYPE_B) { @@ -482,7 +552,7 @@ static void stream_slice(struct avd_ctx *ctx, struct avd_h264_run *run) AVD_OP_REF_LOOP_IDX(i) | AVD_OP_REF_DBP_IDX( sl->ref_pic_list0[i].index), - "slc_6e8_cmd_ref_list_0"); + AVD_H264_FP_SLC_6E8_CMD_REF_LIST_0); if (sl->slice_type == V4L2_H264_SLICE_TYPE_B) { u32 num_ref_idx_active = @@ -492,16 +562,16 @@ static void stream_slice(struct avd_ctx *ctx, struct avd_h264_run *run) AVD_OP_REF_LOOP_IDX(i) | AVD_OP_REF_DBP_IDX( sl->ref_pic_list1[i].index), - "slc_6e8_cmd_ref_list_0"); + AVD_H264_FP_SLC_6E8_CMD_REF_LIST_0); } stream_weights(ctx, run); } if (sl->first_mb_in_slice == 0) { - push(AVD_OP_SL_DIM_START, "cm3_cmd_set_mb_dims"); + push(AVD_OP_SL_DIM_START, AVD_H264_FP_NONE); push(AVD_SL_DIM_END_Y(sps->pic_height_in_map_units_minus1) | AVD_SL_DIM_END_X(sps->pic_width_in_mbs_minus1), - "cm3_set_mb_dims"); + AVD_H264_FP_NONE); } push(AVD_OP_SL_REF | AVD_OP_SL_REF_FLAG_CABAC(sl->cabac_init_idc == 1) | @@ -519,7 +589,7 @@ static void stream_slice(struct avd_ctx *ctx, struct avd_h264_run *run) V4L2_H264_SLICE_TYPE_B) , - "slc_6e4_cmd_ref_type"); + AVD_H264_FP_SLC_6E4_CMD_REF_TYPE); if (sl->slice_type == V4L2_H264_SLICE_TYPE_B) { /* bidirectional reference of previous mv */ @@ -534,14 +604,14 @@ static void stream_slice(struct avd_ctx *ctx, struct avd_h264_run *run) fmt_height(ctx))) : run->addresses.mv_color; - pusha(mv_color_addr, "slc_a78_sps_tile_addr2_lsb8", 0); + pusha(mv_color_addr, AVD_H264_FP_SLC_A78_SPS_TILE_ADDR2_LSB8, 0); } } -static int avd_h264_alloc_bufs(struct avd_ctx *ctx) +static int avd_h264_alloc_bufs(struct avd_ctx *ctx, + struct avd_h264_ctx *h264_ctx) { struct avd_dev *dev = ctx->dev; - struct avd_h264_ctx *h264_ctx = ctx->priv; int ret, w, bit_depth, mb; w = fmt_width(ctx); bit_depth = (ctx->image_fmt == AVD_IMG_FMT_420_10BIT || @@ -549,11 +619,6 @@ static int avd_h264_alloc_bufs(struct avd_ctx *ctx) 10 : 8; - ret = avd_buf_alloc(dev, &h264_ctx->bufs.inst, fifo_size()); - if (ret) { - return ret; - } - if (!(dev->variant->quirks & AVD_QUIRK_NO_PIPE_STATE)) { ret = avd_buf_alloc(dev, &h264_ctx->bufs.pipe_state, 0x200); if (ret) { @@ -620,37 +685,33 @@ static int avd_h264_validate_sps(struct avd_ctx *ctx, return 0; } -/* Free the slices held for the current picture. */ -static void avd_h264_drop_slices(struct avd_ctx *ctx) -{ - struct avd_h264_ctx *h264_ctx = ctx->priv; - - for (size_t i = 0; i < h264_ctx->slice_num; i++) - avd_buf_free(ctx->dev, &h264_ctx->slices[i]); - h264_ctx->slice_num = 0; - h264_ctx->slice_bytes = 0; -} - -static void avd_h264_stop(struct avd_ctx *ctx) +static void avd_h264_free_picture(struct avd_ctx *ctx, + struct avd_h264_ctx *h264_ctx) { - struct avd_h264_ctx *h264_ctx = ctx->priv; struct avd_dev *dev = ctx->dev; if (!h264_ctx) return; - avd_buf_free(dev, &h264_ctx->bufs.pipe_state); - avd_buf_free(dev, &h264_ctx->bufs.inst); avd_buf_free(dev, &h264_ctx->bufs.above_info); avd_buf_free(dev, &h264_ctx->bufs.lf_above_info); avd_buf_free(dev, &h264_ctx->bufs.lf_above); avd_buf_free(dev, &h264_ctx->bufs.ip_above); avd_buf_free(dev, &h264_ctx->bufs.mv_above_info); + kfree(h264_ctx); +} - avd_h264_drop_slices(ctx); - kfree(h264_ctx->slices); +static void avd_h264_stop(struct avd_ctx *ctx) +{ + struct avd_h264_ctx *h264_ctx = ctx->priv; + unsigned int i; - kfree(h264_ctx); + if (!h264_ctx) + return; + /* Core drains every frame before stop; no slot can still be DMA-owned. */ + for (i = 0; i < AVD_MAX_FRAMES; i++) + avd_h264_free_picture(ctx, h264_ctx->slots[i]); + avd_h264_free_picture(ctx, h264_ctx); ctx->priv = NULL; } @@ -674,17 +735,7 @@ static int avd_h264_start(struct avd_ctx *ctx) ctx->priv = h264_ctx; - /* assume one slice per row */ - h264_ctx->alloc_slice_num = - ctrl->p_new.p_h264_sps->pic_height_in_map_units_minus1 + 1; - h264_ctx->slices = kcalloc(h264_ctx->alloc_slice_num, - sizeof(*h264_ctx->slices), GFP_KERNEL); - if (!h264_ctx->slices) { - ret = -ENOMEM; - goto err_free_ctx; - } - - ret = avd_h264_alloc_bufs(ctx); + ret = avd_h264_alloc_bufs(ctx, h264_ctx); if (ret) goto err_free_ctx; @@ -700,6 +751,9 @@ static void avd_h264_run_preamble(struct avd_ctx *ctx, struct avd_h264_run *run) struct v4l2_ctrl *ctrl; u32 dst_len, mv_color_len; + avd_run_preamble(ctx, &run->base); + run->picture = ctx->priv; + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, V4L2_CID_STATELESS_H264_DECODE_PARAMS); run->decode_params = ctrl ? ctrl->p_cur.p : NULL; @@ -717,8 +771,6 @@ static void avd_h264_run_preamble(struct avd_ctx *ctx, struct avd_h264_run *run) V4L2_CID_STATELESS_H264_PRED_WEIGHTS); run->pred_weights = ctrl ? ctrl->p_cur.p : NULL; - avd_run_preamble(ctx, &run->base); - dst_len = run->base.bufs.dst->vb2_buf.planes[0].length; mv_color_len = mv_color_size(fmt_width(ctx), fmt_height(ctx)); @@ -726,49 +778,6 @@ static void avd_h264_run_preamble(struct avd_ctx *ctx, struct avd_h264_run *run) run->addresses.mv_color = run->base.y_out + (dst_len - mv_color_len); } -static int avd_h264_realloc_slices(struct avd_ctx *ctx, size_t max_slices) -{ - struct avd_h264_ctx *h264_ctx = ctx->priv; - void *tmp; - size_t alloc_slice_num; - - tmp = h264_ctx->slices; - /* grow by half, and by at least one for a one-row picture */ - alloc_slice_num = h264_ctx->alloc_slice_num + - max_t(size_t, h264_ctx->alloc_slice_num / 2, 1); - alloc_slice_num = min(alloc_slice_num, max_slices); - h264_ctx->slices = kcalloc(alloc_slice_num, sizeof(*h264_ctx->slices), - GFP_KERNEL); - if (!h264_ctx->slices) { - /* make deallocating a little easier */ - h264_ctx->slices = tmp; - return -ENOMEM; - } - - h264_ctx->alloc_slice_num = alloc_slice_num; - memcpy(h264_ctx->slices, tmp, - sizeof(*h264_ctx->slices) * h264_ctx->slice_num); - kfree(tmp); - - /* a slice that arrives before the frame's header has no job table yet */ - if (!ctx->job.segments) - return 0; - - tmp = ctx->job.segments; - ctx->job.segments = kvcalloc(alloc_slice_num + 1, - sizeof(*ctx->job.segments), GFP_KERNEL); - if (!ctx->job.segments) { - ctx->job.segments = tmp; - return -ENOMEM; - } - - memcpy(ctx->job.segments, tmp, - sizeof(*ctx->job.segments) * (ctx->job.num + 1)); - kvfree(tmp); - - return 0; -} - /* * Check that every reference in the slice's ref lists points at a DPB entry * that is VALID. stream_refs() only pushes a reference header (and its RVRA @@ -857,48 +866,46 @@ static int avd_h264_run(struct avd_ctx *ctx) { struct avd_h264_ctx *h264_ctx = ctx->priv; struct v4l2_h264_reflist_builder reflist_builder; - struct avd_h264_run run; + struct avd_h264_run run = {}; int ret; avd_h264_run_preamble(ctx, &run); + if (h264_ctx->slice_num >= MAX_SLICES) { + dev_err_ratelimited(ctx->dev->dev, + "slice_num > %d, stream was rejected!", + MAX_SLICES); + avd_run_postamble(ctx, &run.base); + return -EINVAL; + } - struct vb2_v4l2_buffer *src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + struct vb2_v4l2_buffer *src = run.base.bufs.src; u32 payload_len = vb2_get_plane_payload(&src->vb2_buf, 0); + u32 data_offset = src->vb2_buf.planes[0].data_offset; const u8 *data = vb2_plane_vaddr(&src->vb2_buf, 0); - if (!data || !avd_h264_find_slice_data(data, payload_len, - avd_h264_header_len(&run), - &run.slice_data_off)) { - dev_dbg_ratelimited(ctx->dev->dev, - "slice has no data after its header\n"); + /* The old one-job core cannot retain intermediate zero-copy sources. */ + if (src->flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF) { + avd_run_postamble(ctx, &run.base); + return -EOPNOTSUPP; + } + if (!data || data_offset > payload_len || payload_len - data_offset < 3) { avd_run_postamble(ctx, &run.base); return -EINVAL; } - - if (!avd_h264_slice_fits(run.sps, h264_ctx->slice_num, - h264_ctx->slice_bytes, payload_len, - ctx->decoded_fmt.fmt.pix_mp.plane_fmt[0].sizeimage)) { - dev_err_ratelimited(ctx->dev->dev, - "too many slices for one picture, dropping it\n"); - /* The unsubmitted job table points at the dropped slices. */ - avd_h264_drop_slices(ctx); - ret = -EINVAL; - goto err_free_jobs; + h264_ctx->slice.cpu = (void *)(data + data_offset); + h264_ctx->slice.addr = run.base.coded_in + data_offset; + h264_ctx->slice.size = payload_len - data_offset; + h264_ctx->active_slice = &h264_ctx->slice; + if (!avd_h264_find_slice_data(h264_ctx->slice.cpu, h264_ctx->slice.size, + avd_h264_header_len(&run), &run.slice_data_off) || + !avd_h264_slice_fits(run.sps, 0, 0, h264_ctx->slice.size, + ctx->decoded_fmt.fmt.pix_mp.plane_fmt[0].sizeimage)) { + avd_run_postamble(ctx, &run.base); + return -EINVAL; } - if (h264_ctx->slice_num >= h264_ctx->alloc_slice_num) { - ret = avd_h264_realloc_slices(ctx, avd_h264_max_slices(run.sps)); - if (ret) - goto err_free_jobs; - } - h264_ctx->active_slice = &h264_ctx->slices[h264_ctx->slice_num]; - ret = avd_buf_alloc(ctx->dev, h264_ctx->active_slice, payload_len); - if (ret) - goto err_free_jobs; - memcpy(h264_ctx->active_slice->cpu, data, payload_len); h264_ctx->slice_num++; - h264_ctx->slice_bytes += PAGE_ALIGN((size_t)payload_len); h264_ctx->monochrome = run.sps->chroma_format_idc == 0; /* Build the P/B{0,1} ref lists. */ @@ -922,8 +929,7 @@ static int avd_h264_run(struct avd_ctx *ctx) if (is_new_frame(run.slice_params)) { /* the header segment plus one per slice */ - ret = avd_init_job(ctx, AVD_CODEC_H264, - h264_ctx->alloc_slice_num + 1); + ret = avd_init_job(ctx, AVD_CODEC_H264, 2); if (ret) return ret; stream_hdr(ctx, &run); @@ -935,18 +941,8 @@ static int avd_h264_run(struct avd_ctx *ctx) ctx->job.num++; stream_slice(ctx, &run); - if (run.base.bufs.src->flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF) { - avd_job_finish(ctx, VB2_BUF_STATE_DONE); - return 0; - } return avd_submit_job(ctx); - -err_free_jobs: - kvfree(ctx->job.segments); - ctx->job.segments = NULL; - avd_run_postamble(ctx, &run.base); - return ret; } static void avd_h264_done(struct avd_ctx *ctx, struct vb2_v4l2_buffer *src_buf, @@ -956,7 +952,7 @@ static void avd_h264_done(struct avd_ctx *ctx, struct vb2_v4l2_buffer *src_buf, struct avd_h264_ctx *h264_ctx = ctx->priv; if (!(src_buf->flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF)) { - avd_h264_drop_slices(ctx); + h264_ctx->slice_num = 0; /* 4:0:0 has no chroma to decode; the picture is grey */ if (h264_ctx->monochrome && dst_buf && result == VB2_BUF_STATE_DONE) { @@ -1026,6 +1022,158 @@ static void avd_h264_submit(struct avd_ctx *ctx) ctx->dev->ctrl + ctx->dev->variant->submit_offset); } +static void avd_h264_frame_controls(struct avd_ctx *ctx, struct avd_h264_run *run) +{ + struct v4l2_ctrl *ctrl; + +#define H264_CTRL(member, id) do { \ + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, id); \ + run->member = ctrl ? ctrl->p_cur.p : NULL; \ +} while (0) + H264_CTRL(decode_params, V4L2_CID_STATELESS_H264_DECODE_PARAMS); + H264_CTRL(sps, V4L2_CID_STATELESS_H264_SPS); + H264_CTRL(pps, V4L2_CID_STATELESS_H264_PPS); + H264_CTRL(slice_params, V4L2_CID_STATELESS_H264_SLICE_PARAMS); + H264_CTRL(scaling_matrix, V4L2_CID_STATELESS_H264_SCALING_MATRIX); + H264_CTRL(pred_weights, V4L2_CID_STATELESS_H264_PRED_WEIGHTS); +#undef H264_CTRL +} + +static int avd_h264_prepare(struct avd_ctx *ctx, struct avd_frame *f) +{ + struct avd_h264_ctx *h264 = ctx->priv, *picture; + struct v4l2_h264_reflist_builder builder; + struct avd_h264_run run = { .frame = f }; + struct avd_decoded_buffer *dst; + struct vb2_buffer *src; + const u8 *data; + size_t payload, offset, mv_len; + bool apple = avd_backend_is_apple(ctx->dev); + int ret; + + f->dep = AVD_DEP_NONE; + f->prog_words = 0; + if (!h264 || f->idx >= AVD_MAX_FRAMES || !f->src || !f->dst || + (!apple && (!f->prog || !f->prog->cpu))) + return -EINVAL; + /* CONTRACT's immediate OUTPUT retire conflicts with zero-copy DMA. + * Do not expose a DMA use-after-requeue until core retains all sources. + */ + if (f->src->flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF) + return -EOPNOTSUPP; + avd_h264_frame_controls(ctx, &run); + if (!run.sps || !run.pps || !run.decode_params || !run.slice_params || + !run.scaling_matrix || !run.pred_weights) + return -EINVAL; + ret = avd_h264_validate_sps(ctx, run.sps); + if (!ret) + ret = avd_h264_validate_pps(ctx, run.pps); + if (ret || !avd_h264_refs_valid(&run)) + return ret ?: -EINVAL; + if (run.slice_params->first_mb_in_slice || + run.slice_params->slice_type > V4L2_H264_SLICE_TYPE_I || + run.pred_weights->luma_log2_weight_denom > 7 || + run.pred_weights->chroma_log2_weight_denom > 7) + return -EINVAL; + if ((run.sps->pic_width_in_mbs_minus1 + 1) * 16 != ALIGN(fmt_width(ctx), 16) || + (run.sps->pic_height_in_map_units_minus1 + 1) * 16 != ALIGN(fmt_height(ctx), 16)) + return -EINVAL; + if (apple && ctx->dev->variant->h264_fp_layout != AVD_H264_FP_V3) + return -EOPNOTSUPP; + src = &f->src->vb2_buf; + data = vb2_plane_vaddr(src, 0); + payload = vb2_get_plane_payload(src, 0); + offset = src->planes[0].data_offset; + if (!data || offset > payload || payload - offset < 2 || + run.slice_params->header_bit_size > (payload - offset) * 8) + return -EINVAL; + picture = h264->slots[f->idx]; + if (!picture) { + picture = kzalloc(sizeof(*picture), GFP_KERNEL); + if (!picture) + return -ENOMEM; + ret = avd_h264_alloc_bufs(ctx, picture); + if (ret) { + avd_h264_free_picture(ctx, picture); + return ret; + } + h264->slots[f->idx] = picture; + } + if (picture->busy) + return -EBUSY; + picture->busy = true; + f->codec_priv = picture; + run.picture = picture; + picture->monochrome = run.sps->chroma_format_idc == 0; + picture->slice = (struct avd_buf) { + .cpu = (void *)(data + offset), + .addr = vb2_dma_contig_plane_dma_addr(src, 0) + offset, + .size = payload - offset, + }; + picture->active_slice = &picture->slice; + if (!avd_h264_find_slice_data(picture->slice.cpu, picture->slice.size, + avd_h264_header_len(&run), &run.slice_data_off)) + return -EINVAL; + run.base.bufs.src = f->src; + run.base.bufs.dst = f->dst; + run.base.y_out = vb2_dma_contig_plane_dma_addr(&f->dst->vb2_buf, 0); + run.base.uv_out = run.base.y_out + + (size_t)ctx->decoded_fmt.fmt.pix_mp.plane_fmt[0].bytesperline * + ctx->decoded_fmt.fmt.pix_mp.height; + run.base.comp_out = run.base.y_out + ctx->comp.start_offset; + mv_len = mv_color_size(fmt_width(ctx), fmt_height(ctx)); + if (mv_len > vb2_plane_size(&f->dst->vb2_buf, 0)) + return -EINVAL; + run.addresses.mv_color = run.base.y_out + + vb2_plane_size(&f->dst->vb2_buf, 0) - mv_len; + dst = vb2_to_avd_decoded_buf(&f->dst->vb2_buf); + dst->comp = ctx->comp; + v4l2_h264_init_reflist_builder(&builder, run.decode_params, + run.sps, run.decode_params->dpb); + run.num_valid = builder.num_valid; + run.cur_poc = builder.cur_pic_order_count; + f->frame_num = run.decode_params->frame_num; + if (apple) { + ret = avd_h264_fp_begin(&run.fp_cursor, f->fp, + ctx->dev->variant->h264_fp_layout); + if (ret) + return ret; + } + ctx->decomp = true; + stream_hdr(ctx, &run); + stream_slice(ctx, &run); + return run.error; +} + +static int avd_h264_pack_cmd(struct avd_ctx *ctx, struct avd_frame *f) +{ + return avd_h264_fp_pack_cmd(ctx->dev->variant->h264_fp_layout, f); +} + +static void avd_h264_retire(struct avd_ctx *ctx, struct avd_frame *f, + enum vb2_buffer_state state) +{ + struct avd_h264_ctx *picture = f->codec_priv; + + if (!picture) + return; + if (picture->monochrome && f->dst && state == VB2_BUF_STATE_DONE) { + struct avd_decoded_buffer *dst = vb2_to_avd_decoded_buf(&f->dst->vb2_buf); + const struct v4l2_pix_format_mplane *pix = &ctx->decoded_fmt.fmt.pix_mp; + + dst->grey_chroma_offset = (size_t)pix->plane_fmt[0].bytesperline * pix->height; + dst->grey_chroma_10bit = ctx->image_fmt == AVD_IMG_FMT_420_10BIT; + } + picture->busy = false; + f->codec_priv = NULL; +} + +const struct avd_codec_frame_ops avd_h264_frame_ops = { + .prepare = avd_h264_prepare, + .pack_cmd = avd_h264_pack_cmd, + .retire = avd_h264_retire, +}; + const struct avd_coded_fmt_ops avd_h264_fmt_ops = { .adjust_decoded_fmt = avd_h264_adjust_decoded_fmt, .start = avd_h264_start, diff --git a/drivers/media/platform/apple/avd/avd-queue.h b/drivers/media/platform/apple/avd/avd-queue.h new file mode 100644 index 0000000000000..ca04f908ef359 --- /dev/null +++ b/drivers/media/platform/apple/avd/avd-queue.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +#ifndef AVD_QUEUE_H_ +#define AVD_QUEUE_H_ + +#include +#include +#include + +struct avd_dev; +struct avd_ctx; +struct avd_buf; /* avd.h: { void *cpu; dma_addr_t addr; size_t size; } */ + +#define AVD_MAX_FRAMES 16 /* SPEC 4.1: 16 frame-param slots */ + +enum avd_frame_state { + AVD_FRAME_FREE = 0, /* SPEC: slot state 0 */ + AVD_FRAME_RESERVED, /* 1: codec is filling it on the CPU */ + AVD_FRAME_SUBMITTED, /* 3: owned by firmware */ + AVD_FRAME_DONE, /* completion seen, waiting for in-order retire */ +}; + +/* Codec says what must finish before this frame may be submitted. */ +enum avd_frame_dep { + AVD_DEP_NONE = 0, /* independent: may overlap the previous frame */ + AVD_DEP_PREV_DONE, /* previous frame of this ctx must be DONE first + * (VP9 backward adaptation, AV1 CDF update, ...) */ +}; + +struct avd_frame { + unsigned int idx; /* 0..AVD_MAX_FRAMES-1, also the fp slot */ + u32 tag; /* (seq << 4) | idx, echoed by completion */ + u64 seq; /* global submission order */ + enum avd_frame_state state; + enum avd_frame_dep dep; + int status; /* 0 or -EIO/-ETIMEDOUT from the backend */ + + struct avd_ctx *ctx; + struct vb2_v4l2_buffer *src; /* OUTPUT, held until retire */ + struct vb2_v4l2_buffer *dst; /* CAPTURE, held until retire */ + u32 frame_num; /* SPEC 5.2 +0x0c */ + + /* open backend: instruction program, DMA-visible, built by the codec */ + struct avd_buf *prog; /* per-slot, allocated by core */ + size_t prog_words; + + /* apple backend: frame-params slot (SPEC 0x1130d0 on Borage) */ + struct avd_buf *fp; /* per-slot, allocated by core, size from match data */ + u32 cmd[24]; /* 0x60 Borage command; core fills the 5.2 prefix, + * codec pack_cmd() fills the codec part */ + + void *codec_priv; /* per-frame codec state, owned by the codec */ + unsigned long deadline; /* jiffies, per-frame watchdog */ +}; + +struct avd_backend_ops { + const char *name; + int (*probe)(struct avd_dev *avd); /* apertures, fw source, version */ + int (*boot)(struct avd_dev *avd); /* power-up: fw present/loaded, link up, ready */ + void (*shutdown)(struct avd_dev *avd); + unsigned int (*max_inflight)(struct avd_dev *avd); + int (*session_start)(struct avd_ctx *ctx); /* apple: cmd 0; open: may be NULL */ + void (*session_stop)(struct avd_ctx *ctx); /* apple: cmd 2 */ + /* Non-blocking. -EBUSY means inbox full: core retries on next completion. */ + int (*submit)(struct avd_dev *avd, struct avd_frame *f); + void (*abort)(struct avd_ctx *ctx); /* apple: cmd 5 */ + irqreturn_t (*irq)(struct avd_dev *avd); /* calls avd_frame_complete() */ + int (*suspend)(struct avd_dev *avd); + int (*resume)(struct avd_dev *avd); +}; + +/* Codec half. Replaces avd_coded_fmt_ops.run/submit/done. CPU only, no MMIO. */ +struct avd_codec_frame_ops { + /* Read the request's controls, set f->dep, fill f->prog (open backend) + * or f->fp (apple backend), allocate f->codec_priv if needed. */ + int (*prepare)(struct avd_ctx *ctx, struct avd_frame *f); + /* Apple backend: fill the codec part of f->cmd (SPEC 6.x). */ + int (*pack_cmd)(struct avd_ctx *ctx, struct avd_frame *f); + /* Called in submission order per ctx, after completion or failure. */ + void (*retire)(struct avd_ctx *ctx, struct avd_frame *f, + enum vb2_buffer_state state); +}; + +/* Core API used by backends and codecs. */ +void avd_frame_complete(struct avd_dev *avd, u32 tag, int status); /* any ctx, IRQ-safe */ +bool avd_backend_is_apple(const struct avd_dev *avd); + +#endif /* AVD_QUEUE_H_ */ diff --git a/drivers/media/platform/apple/avd/avd-v4l2.c b/drivers/media/platform/apple/avd/avd-v4l2.c index 4622e92055486..f01a5a7d475ad 100644 --- a/drivers/media/platform/apple/avd/avd-v4l2.c +++ b/drivers/media/platform/apple/avd/avd-v4l2.c @@ -416,6 +416,7 @@ static const struct avd_coded_fmt_desc avd_coded_fmts[] = { }, .ctrls = &avd_h264_ctrls, .ops = &avd_h264_fmt_ops, + .frame_ops = &avd_h264_frame_ops, .num_decoded_fmts = ARRAY_SIZE(avd_h264_decoded_fmts), .decoded_fmts = avd_h264_decoded_fmts, .subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF, diff --git a/drivers/media/platform/apple/avd/avd.h b/drivers/media/platform/apple/avd/avd.h index ad7accb6e9615..72d8bc3546890 100644 --- a/drivers/media/platform/apple/avd/avd.h +++ b/drivers/media/platform/apple/avd/avd.h @@ -24,6 +24,9 @@ #include #include +#include "avd-queue.h" +#include "avd-h264-fp.h" + #define AVD_CAPABILITY_HEVC BIT(0) #define AVD_CAPABILITY_H264 BIT(1) #define AVD_CAPABILITY_VP9 BIT(2) @@ -173,6 +176,7 @@ struct avd_coded_fmt_desc { struct v4l2_frmsize_stepwise frmsize; const struct avd_ctrls *ctrls; const struct avd_coded_fmt_ops *ops; + const struct avd_codec_frame_ops *frame_ops; unsigned int num_decoded_fmts; const struct avd_decoded_fmt_desc *decoded_fmts; u32 subsystem_flags; @@ -180,6 +184,7 @@ struct avd_coded_fmt_desc { }; struct avd_variant { + enum avd_h264_fp_layout h264_fp_layout; unsigned int vp_slots[4]; unsigned int vp_slot_base[4]; unsigned int fifo_slots; @@ -303,6 +308,7 @@ bool avd_h264_slice_fits(const struct v4l2_ctrl_h264_sps *sps, size_t held, #endif extern const struct avd_coded_fmt_ops avd_h264_fmt_ops; +extern const struct avd_codec_frame_ops avd_h264_frame_ops; extern const struct avd_coded_fmt_ops avd_hevc_fmt_ops; extern const struct avd_coded_fmt_ops avd_vp9_fmt_ops; extern const struct avd_coded_fmt_ops avd_av1_fmt_ops; diff --git a/drivers/media/platform/apple/avd/tests/builder-main.c b/drivers/media/platform/apple/avd/tests/builder-main.c new file mode 100644 index 0000000000000..86c2d271dcbd7 --- /dev/null +++ b/drivers/media/platform/apple/avd/tests/builder-main.c @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +int main(void) +{ + static const u8 data[] = { 0x65, 0, 0, 3, 0x80, 0x42, 0, 0, 3, 4, 5, 6, 7, 8 }; + for (int quirks = 0; quirks < 2; quirks++) + for (int revision = 3; revision <= 4; revision++) + for (int type = 0; type < 3; type++) + for (int slices = 1; slices <= 4; slices += 3) + for (int custom = 0; custom < 2; custom++) + for (int cabac = 0; cabac < 2; cabac++) { + struct avd_variant variant = { .quirks = quirks, .revision = revision }; + struct avd_dev dev = { .variant = &variant }; + struct avd_h264_ctx *picture = calloc(1, sizeof(*picture)); + struct avd_segment segments[5] = {}; + struct avd_ctx ctx = { .dev = &dev, .priv = picture, .decomp = true, + .comp = { .offsets = { 0, 0x100, 0x200, 0x300 } }, + .job = { .segments = segments }, + }; + struct avd_h264_run r = {}; + struct v4l2_ctrl_h264_sps sps = { .pic_width_in_mbs_minus1 = 3, + .pic_height_in_map_units_minus1 = 3, .chroma_format_idc = 1, + .flags = V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY | V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE }; + struct v4l2_ctrl_h264_pps pps = { .flags = V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT | + (custom ? V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE : 0), + .weighted_bipred_idc = custom ? 1 : 2 }; + struct v4l2_ctrl_h264_slice_params sl = { .slice_type = type, + .header_bit_size = 33, .num_ref_idx_l0_active_minus1 = 1, + .num_ref_idx_l1_active_minus1 = 1 }; + struct v4l2_ctrl_h264_decode_params decode = { .flags = type == 2 ? V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC : 0 }; + struct v4l2_ctrl_h264_scaling_matrix scaling; + struct v4l2_ctrl_h264_pred_weights weights = { .luma_log2_weight_denom = 2, .chroma_log2_weight_denom = 2 }; + struct vb2_v4l2_buffer dst = {}; + u32 output[AVD_MAX_INST * 5]; size_t words = 0; +#ifdef TEST_FRAME + struct avd_buf program = { .cpu = output, .size = sizeof(output) }; + struct avd_frame frame = { .prog = &program }; + struct avd_buf fp = { .cpu = calloc(1, 0x8b4e0), .size = 0x8b4e0 }; + if (slices == 1 && quirks) assert(!avd_h264_fp_begin(&r.fp_cursor, &fp, AVD_H264_FP_V3)); + r.frame = &frame; r.picture = picture; +#endif + if (cabac) pps.flags |= V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE; + if (custom) pps.flags |= V4L2_H264_PPS_FLAG_WEIGHTED_PRED; + memset(&scaling, 16, sizeof(scaling)); + for (int l = 0; l < 2; l++) for (int i = 0; i < 32; i++) { + weights.weight_factors[l].luma_weight[i] = custom ? i+1 : 4; + weights.weight_factors[l].luma_offset[i] = custom ? -i : 0; + for (int c = 0; c < 2; c++) { + weights.weight_factors[l].chroma_weight[i][c] = custom ? i+c+1 : 4; + weights.weight_factors[l].chroma_offset[i][c] = custom ? -i-c : 0; + } + } + for (int i = 0; i < 2; i++) { + decode.dpb[i].flags = V4L2_H264_DPB_ENTRY_FLAG_VALID; + decode.dpb[i].reference_ts = i; + decode.dpb[i].top_field_order_cnt = i*2; + sl.ref_pic_list0[i].index = i; sl.ref_pic_list1[i].index = i; + refs[i].base.vb.vb2_buf.dma = 0x600000 + i*0x10000; + refs[i].base.vb.vb2_buf.planes[0].length = 0x10000; + refs[i].comp.start_offset = 0x1000; + } + ctx.decoded_fmt.fmt.pix_mp.width = ctx.decoded_fmt.fmt.pix_mp.height = 64; + ctx.decoded_fmt.fmt.pix_mp.plane_fmt[0].bytesperline = 64; + picture->slice_num = slices; +#ifdef TEST_FRAME + picture->slice = (struct avd_buf) { .cpu = (void *)data, .addr = 0x123450000ULL, .size = sizeof(data) }; + picture->active_slice = &picture->slice; +#else + picture->slices = calloc(4, sizeof(*picture->slices)); + picture->slices[0] = (struct avd_buf) { .cpu = (void *)data, .addr = 0x123450000ULL, .size = sizeof(data) }; + picture->active_slice = &picture->slices[0]; +#endif + picture->bufs.above_info.addr = 0x100000; + picture->bufs.ip_above.addr = 0x110000; + picture->bufs.lf_above.addr = 0x120000; + picture->bufs.lf_above_info.addr = 0x130000; + picture->bufs.mv_above_info.addr = 0x140000; + picture->bufs.pipe_state.addr = 0x150000; + r.base.bufs.dst = &dst; + r.base.y_out = 0x200000; r.base.uv_out = 0x210000; r.base.comp_out = 0x220000; + r.addresses.mv_color = 0x300000; + r.sps = &sps; r.pps = &pps; r.slice_params = &sl; r.decode_params = &decode; + r.scaling_matrix = &scaling; r.pred_weights = &weights; + r.num_valid = 2; r.cur_poc = 6; + assert(avd_h264_find_slice_data(data, sizeof(data), avd_h264_header_len(&r), &r.slice_data_off)); + stream_hdr(&ctx, &r); + for (int i = 0; i < slices; i++) { + ctx.job.num = i+1; sl.first_mb_in_slice = i*4; + stream_slice(&ctx, &r); + } +#ifdef TEST_FRAME + assert(!r.error); words = frame.prog_words; + if (r.fp_cursor.buf) { + const u8 *f = fp.cpu; + assert(get_unaligned_le32(f+0x3c) == 0x003f003f); + assert(get_unaligned_le32(f+0x210) == (r.base.y_out >> 8)); + assert(get_unaligned_le32(f+0xc0) == (r.base.comp_out >> 7)); + assert(get_unaligned_le32(f+0xa84) == (u32)(picture->slice.addr + r.slice_data_off)); + assert(get_unaligned_le32(f+0xa88) == sizeof(data)-r.slice_data_off); + assert(get_unaligned_le32(f+0x4cc) == (custom ? 0x10101010 : 0x060a0d10)); + if (type != 2) assert(get_unaligned_le32(f+0x110) == ((refs[0].base.vb.vb2_buf.dma + 0x1000) >> 7)); + } + free(fp.cpu); +#else + for (int i = 0; i <= slices; i++) { + memcpy(output + words, segments[i].instructions, segments[i].num*4); + words += segments[i].num; + } +#endif + fwrite(&words, sizeof(words), 1, stdout); fwrite(output, 4, words, stdout); +#ifndef TEST_FRAME + free(picture->slices); +#endif + free(picture); + } + return 0; +} diff --git a/drivers/media/platform/apple/avd/tests/fp-main.c b/drivers/media/platform/apple/avd/tests/fp-main.c new file mode 100644 index 0000000000000..5fef6c603af73 --- /dev/null +++ b/drivers/media/platform/apple/avd/tests/fp-main.c @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +int main(void) +{ + struct avd_buf fp = { .cpu = calloc(1, 0x1130d0), .size = 0x1130d0 }; + struct avd_h264_fp_cursor cursor; + struct avd_frame frame = { .idx = 15, .fp = &fp }; + u8 *p = fp.cpu; + assert(avd_h264_fp_begin(&cursor, &fp, AVD_H264_FP_T8112_TODO) == -EOPNOTSUPP); + assert(avd_h264_fp_begin(&cursor, &fp, AVD_H264_FP_T8140_TODO) == -EOPNOTSUPP); + assert(avd_h264_fp_begin(&cursor, &fp, AVD_H264_FP_BORAGE) == -EOPNOTSUPP); + fp.size = 0xa94; + assert(avd_h264_fp_begin(&cursor, &fp, AVD_H264_FP_V3) == -ENOSPC); + fp.size = 0x1130d0; + assert(!avd_h264_fp_begin(&cursor, &fp, AVD_H264_FP_V3)); + assert(get_unaligned_le32(p+0x14) == 0xdeadcafe); + assert(get_unaligned_le32(p+0x1c) == 1); + assert(get_unaligned_le32(p+0x20) == 0x8b4c0); + assert(!avd_h264_fp_emit(&cursor, AVD_H264_FP_HDR_3C_HEIGHT_WIDTH, 0x0437077f)); + assert(get_unaligned_le32(p+0x3c) == 0x0437077f); + assert(avd_h264_fp_emit(&cursor, AVD_H264_FP_HDR_3C_HEIGHT_WIDTH, 0) == -E2BIG); + assert(!avd_h264_fp_address(&cursor, AVD_H264_FP_HDR_210_Y_ADDR_LSB8, 0, 0x123450000ULL)); + assert(get_unaligned_le32(p+0x210) == 0x1234500); + assert(avd_h264_fp_address(&cursor, AVD_H264_FP_HDR_210_Y_ADDR_LSB8, 0, 0x100001) == -ERANGE); + assert(!avd_h264_fp_ref(&cursor, true, 15, 3, 0x400000)); + assert(get_unaligned_le32(p+0x20c) == 0x8000); + assert(avd_h264_fp_ref(&cursor, true, 16, 0, 0) == -ERANGE); + assert(avd_h264_fp_ref(&cursor, false, 0, 4, 0) == -ERANGE); + assert(!avd_h264_fp_emit(&cursor, AVD_H264_FP_SLC_LUMA_WEIGHTS, 0x2de04001)); + assert(!avd_h264_fp_emit(&cursor, AVD_H264_FP_SLC_CHROMA_WEIGHTS_0_, 0x2de08002)); + assert(get_unaligned_le32(p+0x770) == 0x2de04001); + assert(get_unaligned_le32(p+0x774) == 0x2de08002); + memset(p, 0, fp.size); + p[0x18] = 1; p[0x19] = 1; + put_unaligned_le32(0x7f, p+0x3d8); + p[0x1a] = 4; p[0x1c] = 2; + frame.cmd[0] = 0xa5000200; + frame.cmd[1] = 3U << 16; + frame.cmd[18] = 0x12345678; + assert(avd_h264_borage_cmd_fields(&frame) == -EOPNOTSUPP); + assert(frame.cmd[0] == 0x000403ff); + assert(frame.cmd[1] == 0x30002); + assert(frame.cmd[3] == 0x01483120 + 3*0x1814); + assert(frame.cmd[4] == 15*0x1130d0 + 0x3dc); + assert(frame.cmd[5] == frame.cmd[3] + 0xc); + assert(frame.cmd[6] == 15*0x1130d0 + 0x5c0); + assert(frame.cmd[7] == 15*0x1130d0 + 0x8a0); + assert(frame.cmd[10] == 0x01483154 + 3*0x1814); + assert(frame.cmd[11] == 0x01483470 + 3*0x1814); + assert(frame.cmd[12] == frame.cmd[3] + 0x3ac); + assert(frame.cmd[18] == 0x12345678); + p[0x1a] = 0x85; p[0x1b] = 3; + assert(avd_h264_borage_cmd_fields(&frame) == -EINVAL); + p[0x1a] = 0x84; + assert(avd_h264_borage_cmd_fields(&frame) == -EOPNOTSUPP); + frame.cmd[1] = 4U << 16; + assert(avd_h264_borage_cmd_fields(&frame) == -EINVAL); + assert(avd_h264_fp_pack_cmd(AVD_H264_FP_V3, &frame) == -EOPNOTSUPP); + free(fp.cpu); + puts("PASS: V3 field/address/array bounds and Borage known-command projection; unknown layouts/commands rejected"); + return 0; +} diff --git a/drivers/media/platform/apple/avd/tests/h264-builder-baseline.c b/drivers/media/platform/apple/avd/tests/h264-builder-baseline.c new file mode 100644 index 0000000000000..21266e766d626 --- /dev/null +++ b/drivers/media/platform/apple/avd/tests/h264-builder-baseline.c @@ -0,0 +1,534 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Apple AVD VPU codec driver + * + * Copyright The Asahi Linux Contributors + * Copyright 2023 Eileen Yoon + * + * Copyright (c) 2014 Rockchip Electronics Co., Ltd. + * Hertz Wong + * Herman Chen + * + * Copyright (C) 2014 Google, Inc. + * Tomasz Figa + */ + +/* Frozen Linux builder, copyright Asahi Linux Contributors and Eileen Yoon. + * Baseline aurora-wip fbc4e79, before frame_ops; synthetic regression only. */ +#define H264_SCL_DIMS (0x1000000 | ((64 / 4) << 5) | (((16 / 4) << 5) - 1)) + +#define h264_FLAG_CI_PRED(v) FIELD_PREP(BIT(19), !!(v)) +#define H264_FLAG_ENTROPY_CODING_MODE(v) FIELD_PREP(BIT(20), !!(v)) +#define H264_FLAG_NOT_IDR(v) FIELD_PREP(BIT(21), !!(v)) + +#define H264_TRANSFORM_8X8_MODE(v) FIELD_PREP(BIT(7), !!(v)) + +/* + * Most slices held for one picture. This bounds the slice table, the job + * table and the memory that a client can pin. + */ +#define MAX_SLICES 4096 + +struct avd_h264_run { + struct avd_run base; + + const struct v4l2_ctrl_h264_decode_params *decode_params; + const struct v4l2_ctrl_h264_sps *sps; + const struct v4l2_ctrl_h264_pps *pps; + const struct v4l2_ctrl_h264_slice_params *slice_params; + + const struct v4l2_ctrl_h264_scaling_matrix *scaling_matrix; + const struct v4l2_ctrl_h264_pred_weights *pred_weights; + + struct run_addr { + dma_addr_t mv_color; + } addresses; + + s32 cur_poc; + u8 num_valid; + u32 slice_data_off; +}; + +/* state */ +struct avd_h264_ctx { + bool monochrome; + struct avd_h264_reflists { + struct v4l2_h264_reference p[V4L2_H264_REF_LIST_LEN]; + struct v4l2_h264_reference b0[V4L2_H264_REF_LIST_LEN]; + struct v4l2_h264_reference b1[V4L2_H264_REF_LIST_LEN]; + } reflists; + + struct avd_buf *slices; + struct avd_buf *active_slice; + size_t slice_num; + size_t alloc_slice_num; + /* allocated size of the held slices */ + size_t slice_bytes; + + struct avd_h264_bufs { + struct avd_buf inst; + struct avd_buf pipe_state; + struct avd_buf above_info; + struct avd_buf lf_above_info; + struct avd_buf lf_above; + struct avd_buf ip_above; + struct avd_buf mv_above_info; + } bufs; +}; + +/* ffmpeg submits wrong timestamp, use first_mb_in_slice as a workaround */ +#define is_new_frame(sl) \ + (sl->first_mb_in_slice == 0) /* (ctx->fh.m2m_ctx->new_frame) */ + +/* scaling matrix */ +static const u32 default_8x8_intra[] = { + 0x060a0d10, 0x0a0b1012, 0x0d101217, 0x10121719, 0x1217191b, 0x17191b1d, + 0x191b1d1f, 0x1b1d1f21, 0x1217191b, 0x17191b1d, 0x191b1d1f, 0x1b1d1f21, + 0x1d1f2124, 0x1f212426, 0x21242628, 0x2426282a, +}; +static const u32 default_8x8_inter[] = { + 0x090d0f11, 0x0d0d1113, 0x0f111315, 0x11131516, 0x13151618, 0x15161819, + 0x1618191b, 0x18191b1c, 0x13151618, 0x15161819, 0x1618191b, 0x18191b1c, + 0x191b1c1e, 0x1b1c1e20, 0x1c1e2021, 0x1e202123, +}; + +static inline u32 mv_color_size(u32 w, u32 h) +{ + return (DIV_ROUND_UP(w, 16) + 1) * (DIV_ROUND_UP(h, 16) + 1) * 64; +} + +/* sorry for the formatting */ + +static void stream_refs(struct avd_ctx *ctx, struct avd_h264_run *run) +{ + const struct v4l2_ctrl_h264_decode_params *decode = run->decode_params; + const struct v4l2_h264_dpb_entry *dpb = decode->dpb; + struct avd_h264_ctx *h264_ctx = ctx->priv; + struct avd_decoded_buffer *dst, *ref; + dma_addr_t addr; + + dst = vb2_to_avd_decoded_buf(&run->base.bufs.dst->vb2_buf); + + push(0, ""); + pusha(h264_ctx->bufs.mv_above_info.addr, "mv_above_info", 7); + pusha(run->addresses.mv_color, "mv_color", 0); + + push(0, ""); + push(0, ""); + push(0, ""); + push(0, ""); + + for (int i = 0; i < ARRAY_SIZE(decode->dpb); i++) { + if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_VALID)) + continue; + + ref = avd_get_ref_buf(ctx, &dst->base.vb, dpb[i].reference_ts); + + addr = vb2_dma_contig_plane_dma_addr(&ref->base.vb.vb2_buf, 0) + + ref->comp.start_offset; + + push(AVD_REF_NUM(run->num_valid - 1) | AVD_REF_FLAG_CONST | + AVD_REF_FLAG_LONG( + dpb[i].flags & + V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM) | + AVD_REF_DELTA_POC(run->cur_poc - + dpb[i].top_field_order_cnt), + "hdr_d0_ref_hdr"); + + push_comp(ctx, addr, ctx->comp.offsets); + } +} + +static void stream_scaling(struct avd_ctx *ctx, struct avd_h264_run *run) +{ + const struct v4l2_ctrl_h264_pps *pps = run->pps; + const struct v4l2_ctrl_h264_scaling_matrix *scaling = + run->scaling_matrix; + + push(H264_SCL_DIMS, "hdr_4c_pic_scaling_list_dims"); + + for (int i = 0; i < 6; i++) + for (int j = 0; j < 16; j += 4) + push(AVD_SCALING_I3( + scaling->scaling_list_4x4[i][j + 0]) | + AVD_SCALING_I2( + scaling->scaling_list_4x4[i][j + 1]) | + AVD_SCALING_I1( + scaling->scaling_list_4x4[i][j + 2]) | + AVD_SCALING_I0( + scaling->scaling_list_4x4[i][j + 3]), + "scl_46c_pic_scaling_matrix_4x4"); + + /* Instead of 8x8 raster scan order avd expects 4 4x4 subblocks */ + static const u8 map[16] = { + 0, 8, 16, 24, /* top left */ + 4, 12, 20, 28, /* top right */ + 32, 40, 48, 56, /* bottom left */ + 36, 44, 52, 60, /* bottom right */ + }; + + /* 7.3.2.2, only matrix 0 and 1 are used if chroma_format_idc < 3 */ + if (pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE) { + for (int i = 0; i < 2; i++) + for (int j = 0; j < 16; j++) + push(AVD_SCALING_I3(scaling->scaling_list_8x8 + [i][map[j] + 0]) | + AVD_SCALING_I2( + scaling->scaling_list_8x8 + [i][map[j] + 1]) | + AVD_SCALING_I1( + scaling->scaling_list_8x8 + [i][map[j] + 2]) | + AVD_SCALING_I0( + scaling->scaling_list_8x8 + [i][map[j] + 3]), + "scl_4cc_pic_scaling_matrix_8x8"); + + } else { + for (int i = 0; i < ARRAY_SIZE(default_8x8_intra); i++) + push(default_8x8_intra[i], + "scl_4cc_pic_scaling_matrix_8x8"); + for (int i = 0; i < ARRAY_SIZE(default_8x8_inter); i++) + push(default_8x8_inter[i], + "scl_4cc_pic_scaling_matrix_8x8"); + } +} + +static void stream_hdr(struct avd_ctx *ctx, struct avd_h264_run *run) +{ + const struct v4l2_ctrl_h264_decode_params *decode = run->decode_params; + const struct v4l2_ctrl_h264_sps *sps = run->sps; + const struct v4l2_ctrl_h264_pps *pps = run->pps; + struct avd_dev *avd = ctx->dev; + struct avd_h264_ctx *h264_ctx = ctx->priv; + u32 bytesperline; + u32 width = (sps->pic_width_in_mbs_minus1 + 1) * 16; + u32 height = (sps->pic_height_in_map_units_minus1 + 1) * 16; + + push(AVD_OP_HDR | AVD_OP_HDR_FLAG_DECOMP(ctx->decomp) | + AVD_OP_HDR_FLAG_INTRA( + decode->flags & + V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC) | + AVD_OP_HDR_CONST | + AVD_OP_HDR_FLAG_PIPE_STATE( + !(avd->variant->quirks & AVD_QUIRK_NO_PIPE_STATE)), + "hdr_34_start_hdr"); + + push(AVD_HDR_CODEC_MODE(AVD_CODEC_H264), "hdr_38_mode"); + + push(AVD_HDR_HEIGHT(height - 1) | AVD_HDR_WIDTH(width - 1), + "hdr_3c_height_width"); + + push(0, "hdr_40_zero"); + + push(AVD_HDR_HEIGHT((height - 1) >> 3) | + AVD_HDR_WIDTH((width - 1) >> 3), + "hdr_28_height_width_shift3"); + + /* TODO: did i mix up luma/chroma in hevc or here? */ + push(AVD_HDR_COMMON_CHROMA_FORMAT(sps->chroma_format_idc) | + AVD_HDR_COMMON_BIT_DEPTH_L(sps->bit_depth_luma_minus8) | + AVD_HDR_COMMON_BIT_DEPTH_C(sps->bit_depth_chroma_minus8) | + AVD_HDR_COMMON_MIN_LUMA_CBS(1) | + AVD_HDR_COMMON_LUMA_CBS(1) | + H264_TRANSFORM_8X8_MODE( + pps->flags & + V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE) | + AVD_HDR_COMMON_FLAG0( + sps->flags & + V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE), + "hdr_2c_sps_param"); + + push(H264_FLAG_ENTROPY_CODING_MODE( + pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE) | + H264_FLAG_NOT_IDR(!(decode->flags & + V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC)) | + h264_FLAG_CI_PRED( + pps->flags & + V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED), + "hdr_44_flags"); + + push(AVD_HDR_H26X_QP_OFFSET_CB(pps->chroma_qp_index_offset) | + AVD_HDR_H26X_QP_OFFSET_CR( + pps->second_chroma_qp_index_offset), + "hdr_48_chroma_qp_index_offset"); + + push(AVD_HDR_FEAT_H26X | AVD_HDR_FEAT_COMMON | AVD_HDR_FEAT_H264 | + AVD_HDR_FEAT_PIPE_STATE_EN( + !(avd->variant->quirks & AVD_QUIRK_NO_PIPE_STATE)), + "hdr_58_const_3a"); + + push(0, ""); + push(0, ""); + + if (avd->variant->revision == 3) + push(0, "zero"); + + pusha(h264_ctx->bufs.above_info.addr, "hdr_9c_pps_tile_addr_lsb8", 0); + + push(0, ""); + push(0, ""); + + if (avd->variant->revision == 3) + push(0, "zero"); + else if (!(avd->variant->quirks & AVD_QUIRK_NO_PIPE_STATE)) + pusha(h264_ctx->bufs.pipe_state.addr, "pipe_state", 0); + + pusha(h264_ctx->bufs.ip_above.addr, "ip_above", 1); + pusha(h264_ctx->bufs.lf_above.addr, "lf_above", 2); + pusha(h264_ctx->bufs.lf_above_info.addr, "lf_above_info", 3); + push(0, ""); + + push_comp(ctx, run->base.comp_out, ctx->comp.offsets); + + bytesperline = ctx->decoded_fmt.fmt.pix_mp.plane_fmt[0].bytesperline; + if (avd->variant->quirks & AVD_QUIRK_LSR) + bytesperline = bytesperline >> 4; + + pusha(run->base.y_out, "hdr_210_y_addr_lsb8", 0); + push(bytesperline, "hdr_218_width_align"); + pusha(run->base.uv_out, "hdr_214_uv_addr_lsb8", 0); + push(bytesperline, "hdr_21c_width_align"); + + push(0, "cm3_mark_end_section"); + push(((height - 1) << 16) | (width - 1), "hdr_54_height_width"); + + if (!(decode->flags & V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC)) + stream_refs(ctx, run); + + if (pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT) + stream_scaling(ctx, run); + else + push(0, "cm3_mark_end_section_scl"); +} + +#define DEFAULT_WEIGHT_DENOM \ + (AVD_OP_WEIGHTS_HDR_LUMA(5) | AVD_OP_WEIGHTS_HDR_CHROMA(5)) + +static void stream_weights(struct avd_ctx *ctx, struct avd_h264_run *run) +{ + int luma_denom, chroma_denom; + struct v4l2_h264_weight_factors factors; + const struct v4l2_ctrl_h264_pred_weights *weights = run->pred_weights; + const struct v4l2_ctrl_h264_pps *pps = run->pps; + const struct v4l2_ctrl_h264_slice_params *sl = run->slice_params; + + bool pred_weight_req = V4L2_H264_CTRL_PRED_WEIGHTS_REQUIRED(pps, sl); + bool default_weights = pps->weighted_bipred_idc == 2 && + !pred_weight_req; + + push(AVD_OP_WEIGHTS_HDR | AVD_OP_WEIGHTS_HDR_FLAG1(default_weights) | + AVD_OP_WEIGHTS_HDR_FLAG0(pred_weight_req) | + AVD_OP_WEIGHTS_HDR_LUMA( + !default_weights ? + weights->luma_log2_weight_denom : + 0) | + AVD_OP_WEIGHTS_HDR_CHROMA( + !default_weights ? + weights->chroma_log2_weight_denom : + 0) | + (default_weights ? DEFAULT_WEIGHT_DENOM : 0), + "slc_76c_cmd_weights_denom"); + + if (!pred_weight_req) + return; + + luma_denom = 1 << weights->luma_log2_weight_denom; + chroma_denom = 1 << weights->chroma_log2_weight_denom; + + for (int y = 0; y < 2; y++) { + if (y == 1 && sl->slice_type != V4L2_H264_SLICE_TYPE_B) + break; + + factors = weights->weight_factors[y]; + int to = y == 0 ? sl->num_ref_idx_l0_active_minus1 : + sl->num_ref_idx_l1_active_minus1; + for (int i = 0; i < to + 1; i++) { + /* Avd only expects offsets/weights if they are not the default + * ones, otherwise we get artifacts */ + if ((factors.luma_weight[i] != luma_denom) || + (factors.luma_offset[i] != 0)) { + push(AVD_OP_WEIGHTS | AVD_OP_WEIGHTS_IDENT(1) | + AVD_OP_WEIGHTS_LIST_IDX(y) | + AVD_OP_WEIGHTS_INDEX(i) | + AVD_OP_WEIGHTS_WEIGHT( + factors.luma_weight[i]), + "slc_luma_weights"); + push(AVD_OP_OFFSETS | + AVD_OP_OFFSETS_OFFSET( + factors.luma_offset[i]), + "slc_luma_offsets"); + } + + if ((factors.chroma_weight[i][0] != chroma_denom) || + (factors.chroma_offset[i][0] != 0) || + (factors.chroma_weight[i][1] != chroma_denom) || + (factors.chroma_offset[i][1] != 0)) { + push(AVD_OP_WEIGHTS | AVD_OP_WEIGHTS_IDENT(2) | + AVD_OP_WEIGHTS_LIST_IDX(y) | + AVD_OP_WEIGHTS_INDEX(i) | + AVD_OP_WEIGHTS_WEIGHT( + factors.chroma_weight[i][0]), + "slc_chroma_weights[0]"); + push(AVD_OP_OFFSETS | + AVD_OP_OFFSETS_OFFSET( + factors.chroma_offset[i][0]), + "slc_chroma_offsets[0]"); + push(AVD_OP_WEIGHTS | AVD_OP_WEIGHTS_IDENT(3) | + AVD_OP_WEIGHTS_LIST_IDX(y) | + AVD_OP_WEIGHTS_INDEX(i) | + AVD_OP_WEIGHTS_WEIGHT( + factors.chroma_weight[i][1]), + "slc_chroma_weights[1]"); + push(AVD_OP_OFFSETS | + AVD_OP_OFFSETS_OFFSET( + factors.chroma_offset[i][1]), + "slc_chroma_offsets[1]"); + } + } + } +} + +/* + * Find the slice data in a slice: skip the two bytes in front of the slice + * header, then the header_len bytes of the header itself, which do not count + * its emulation prevention bytes. At least one byte of slice data must follow. + */ +VISIBLE_IF_KUNIT bool avd_h264_find_slice_data(const u8 *data, u32 len, + u32 header_len, u32 *off) +{ + u32 pos = 2, bytes_read = 2; + + while (bytes_read < header_len && pos < len) { + if (data[pos - 2] != 0x00 || data[pos - 1] != 0x00 || + data[pos] != 0x03) + bytes_read++; + pos++; + } + + if (pos >= len) + return false; + + *off = pos; + return true; +} + +/* Bytes of slice header in front of the slice data. */ +static u32 avd_h264_header_len(const struct avd_h264_run *run) +{ + /* CABAC slice data starts on a byte boundary. */ + bool cabac = run->pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE; + + return (run->slice_params->header_bit_size + (cabac ? 7 : 0)) / 8; +} + +static void stream_slice(struct avd_ctx *ctx, struct avd_h264_run *run) +{ + const struct v4l2_ctrl_h264_decode_params *decode = run->decode_params; + const struct v4l2_ctrl_h264_pps *pps = run->pps; + const struct v4l2_ctrl_h264_sps *sps = run->sps; + const struct v4l2_ctrl_h264_slice_params *sl = run->slice_params; + struct avd_h264_ctx *h264_ctx = ctx->priv; + u32 payload_len = h264_ctx->active_slice->size; + bool en_mode = (pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE) == + 0; + u32 off = run->slice_data_off; + + dma_addr_t slc_a84 = h264_ctx->active_slice->addr + off; + + push(AVD_OP_CODED_DATA | + AVD_OP_CODED_DATA_BIT_OFF( + en_mode ? (sl->header_bit_size % 8) : 0) | + AVD_OP_CODED_DATA_ADDR(slc_a84 >> 32), + "slc_a7c_cmd_set_coded_slice"); + push((u32)(slc_a84 & 0xffffffff), "slc_a84_slice_addr_low"); + + /* should not include trailing 0? */ + push(payload_len - off, "slc_a88_slice_hdr_size"); + + push(AVD_OP_SL_LOC | + AVD_OP_SL_LOC_Y(sl->first_mb_in_slice / + (sps->pic_width_in_mbs_minus1 + 1)) | + AVD_OP_SL_LOC_X(sl->first_mb_in_slice % + (sps->pic_width_in_mbs_minus1 + 1)), + "cm3_cmd_exec_mb_vp"); + + push(AVD_OP_QP | AVD_OP_QP_VAL(26 + pps->pic_init_qp_minus26 + + sl->slice_qp_delta), + "slc_a70_cmd_quant_param"); + + push(AVD_OP_DBLK | + AVD_OP_DBLK_FLAG_FULL_EN( + sl->disable_deblocking_filter_idc == 0) | + AVD_OP_DBLK_FLAG_EN(sl->disable_deblocking_filter_idc != + 1) | + AVD_OP_DBLK_OFF1(sl->slice_beta_offset_div2) | + AVD_OP_DBLK_OFF0(sl->slice_alpha_c0_offset_div2), + "slc_a74_cmd_deblocking_filter"); + + if (sl->slice_type == V4L2_H264_SLICE_TYPE_P || + sl->slice_type == V4L2_H264_SLICE_TYPE_B) { + u32 num_ref_idx_active = sl->num_ref_idx_l0_active_minus1 + 1; + for (u32 i = 0; i < num_ref_idx_active; i++) + push(AVD_OP_REF | AVD_OP_REF_LIST_IDX(0) | + AVD_OP_REF_LOOP_IDX(i) | + AVD_OP_REF_DBP_IDX( + sl->ref_pic_list0[i].index), + "slc_6e8_cmd_ref_list_0"); + + if (sl->slice_type == V4L2_H264_SLICE_TYPE_B) { + u32 num_ref_idx_active = + sl->num_ref_idx_l1_active_minus1 + 1; + for (u32 i = 0; i < num_ref_idx_active; i++) + push(AVD_OP_REF | AVD_OP_REF_LIST_IDX(1) | + AVD_OP_REF_LOOP_IDX(i) | + AVD_OP_REF_DBP_IDX( + sl->ref_pic_list1[i].index), + "slc_6e8_cmd_ref_list_0"); + } + stream_weights(ctx, run); + } + + if (sl->first_mb_in_slice == 0) { + push(AVD_OP_SL_DIM_START, "cm3_cmd_set_mb_dims"); + push(AVD_SL_DIM_END_Y(sps->pic_height_in_map_units_minus1) | + AVD_SL_DIM_END_X(sps->pic_width_in_mbs_minus1), + "cm3_set_mb_dims"); + } + + push(AVD_OP_SL_REF | AVD_OP_SL_REF_FLAG_CABAC(sl->cabac_init_idc == 1) | + AVD_OP_SL_REF_FLAG1(sl->cabac_init_idc == 2) | + AVD_OP_SL_REF_FLAG2( + !(sl->flags & + V4L2_H264_SLICE_FLAG_DIRECT_SPATIAL_MV_PRED)) | + AVD_OP_SL_REF_NUM_L0(sl->num_ref_idx_l0_active_minus1) | + AVD_OP_SL_REF_NUM_L1(sl->num_ref_idx_l1_active_minus1) | + AVD_OP_SL_REF_SLICE_P(sl->slice_type == + V4L2_H264_SLICE_TYPE_P) | + AVD_OP_SL_REF_SLICE_I(sl->slice_type == + V4L2_H264_SLICE_TYPE_I) | + AVD_OP_SL_REF_SLICE_B(sl->slice_type == + V4L2_H264_SLICE_TYPE_B) + + , + "slc_6e4_cmd_ref_type"); + + if (sl->slice_type == V4L2_H264_SLICE_TYPE_B) { + /* bidirectional reference of previous mv */ + struct vb2_buffer *vb = vb2_find_buffer( + &ctx->fh.m2m_ctx->cap_q_ctx.q, + decode->dpb[sl->ref_pic_list1[0].index].reference_ts); + + dma_addr_t mv_color_addr = + vb ? vb2_dma_contig_plane_dma_addr(vb, 0) + + (vb->planes[0].length - + mv_color_size(fmt_width(ctx), + fmt_height(ctx))) : + run->addresses.mv_color; + + pusha(mv_color_addr, "slc_a78_sps_tile_addr2_lsb8", 0); + } +} + + +static int avd_h264_alloc_bufs; /* extraction end marker */ diff --git a/drivers/media/platform/apple/avd/tests/host-compat.h b/drivers/media/platform/apple/avd/tests/host-compat.h new file mode 100644 index 0000000000000..4a433d58978ef --- /dev/null +++ b/drivers/media/platform/apple/avd/tests/host-compat.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef int32_t s32; +typedef uint64_t u64; +typedef uint32_t __le32; +typedef uint64_t dma_addr_t; +#define BIT(n) (1U << (n)) +#define GENMASK(h,l) ((UINT32_MAX >> (31-(h))) & (UINT32_MAX << (l))) +#define FIELD_PREP(mask,v) (((u32)(v) << __builtin_ctz(mask)) & (mask)) +#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) +#define DIV_ROUND_UP(n,d) (((n)+(d)-1)/(d)) +#define cpu_to_le32(x) (x) +#define lower_32_bits(x) ((u32)(x)) +#define upper_32_bits(x) ((u32)((x)>>32)) +#define U32_MAX UINT32_MAX +#define AVD_QUIRK_LSR BIT(0) +#define AVD_QUIRK_NO_PIPE_STATE BIT(1) +#define AVD_MAX_INST 512 +#define AVD_MAX_FRAMES 16 +#define AVD_H_ 1 +#define AVD_QUEUE_H_ 1 +#define VISIBLE_IF_KUNIT +#define dev_info(...) ((void)0) +struct avd_buf { void *cpu; dma_addr_t addr; size_t size; }; +struct avd_variant { unsigned int quirks; unsigned char revision; }; +struct avd_dev { const struct avd_variant *variant; }; +struct vb2_buffer { struct { size_t length; } planes[1]; dma_addr_t dma; }; +struct vb2_v4l2_buffer { struct vb2_buffer vb2_buf; }; +struct avd_comp { u32 size; size_t start_offset; u32 offsets[4]; }; +struct avd_decoded_buffer { struct { struct vb2_v4l2_buffer vb; } base; struct avd_comp comp; }; +struct avd_segment { size_t num; u32 instructions[AVD_MAX_INST]; }; +struct avd_job { size_t num; struct avd_segment *segments; }; +struct avd_ctx { + struct avd_dev *dev; void *priv; bool decomp; struct avd_comp comp; struct avd_job job; + struct { struct { struct { int q; } cap_q_ctx; } *m2m_ctx; } fh; + struct { struct { struct { u32 width,height; struct { u32 bytesperline; } plane_fmt[1]; } pix_mp; } fmt; } decoded_fmt; +}; +struct avd_run { + struct { struct vb2_v4l2_buffer *src, *dst; } bufs; + dma_addr_t coded_in, y_out, uv_out, comp_out; +}; +struct avd_frame { + unsigned int idx; struct avd_buf *prog, *fp; size_t prog_words; u32 cmd[24]; +}; +enum { AVD_CODEC_H264 = 1 }; +static struct avd_decoded_buffer refs[16]; +#define vb2_to_avd_decoded_buf(vb) ((struct avd_decoded_buffer *)(vb)) +#define vb2_dma_contig_plane_dma_addr(vb, p) ((vb)->dma) +#define avd_get_ref_buf(ctx,dst,ts) (&refs[(ts)]) +#define vb2_find_buffer(q,ts) (&refs[(ts)].base.vb.vb2_buf) +#define fmt_width(ctx) ((ctx)->decoded_fmt.fmt.pix_mp.width) +#define fmt_height(ctx) ((ctx)->decoded_fmt.fmt.pix_mp.height) +static inline void put_unaligned_le32(u32 x, void *p) { memcpy(p, &x, 4); } +static inline u32 get_unaligned_le32(const void *p) { u32 x; memcpy(&x,p,4);return x; } +static inline u16 get_unaligned_le16(const void *p) { u16 x; memcpy(&x,p,2);return x; } diff --git a/drivers/media/platform/apple/avd/tests/run-host-tests.py b/drivers/media/platform/apple/avd/tests/run-host-tests.py new file mode 100644 index 0000000000000..5104a4f14c81a --- /dev/null +++ b/drivers/media/platform/apple/avd/tests/run-host-tests.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only OR MIT +"""Compile actual instruction builders against synthetic V4L2 controls. + +The frozen baseline is Linux's licensed pre-frame_ops H.264 builder; no Apple +images or captured parameters are used. The shim replaces only kernel plumbing. +Run on a Linux host: python3 tests/run-host-tests.py --baseline-file FILE +""" +from pathlib import Path +import argparse, os, subprocess, tempfile +ap=argparse.ArgumentParser(); ap.add_argument('--baseline-file', type=Path, default=Path(__file__).parent/"h264-builder-baseline.c") +args=ap.parse_args() +root=Path(__file__).resolve().parent.parent +tests=root/'tests' +def strip_includes(s): + return '\n'.join(l for l in s.splitlines() if not l.startswith('#include'))+'\n' +def builder(s): + return s[s.index('#define H264_SCL_DIMS'):s.index('static int avd_h264_alloc_bufs')] +with tempfile.TemporaryDirectory(prefix='avd-h264-host-') as td: + td=Path(td) + # Keep the inline instruction encoding functions used by the original builder. + inst=strip_includes((root/'avd-inst.h').read_text()) + fp_h=strip_includes((root/'avd-h264-fp.h').read_text()) + fp_c=strip_includes((root/'avd-h264-fp.c').read_text()) + common=(tests/'host-compat.h').read_text()+'\n'+inst+'\n'+fp_h+'\n'+fp_c + outputs=[] + for name, src in [('baseline',args.baseline_file.read_text()),('frame',(root/'avd-h264.c').read_text())]: + path=td/(name+'.c'); exe=td/name + path.write_text(common+'\n'+builder(src)+'\n'+(tests/'builder-main.c').read_text()) + subprocess.run([os.environ.get('HOSTCC','cc'),'-std=gnu11','-Wall','-Wextra','-Werror','-Wno-unused-function','-Wno-unused-variable','-Wno-unused-parameter','-Wno-sign-compare',*(['-DTEST_FRAME'] if name=='frame' else []),str(path),'-o',str(exe)],check=True) + outputs.append(subprocess.check_output([str(exe)])) + assert outputs[0]==outputs[1], 'frame program differs from baseline' + print('PASS: 96 synthetic I/P/B, 1/4-slice, scaling/weights, address-format cases match baseline byte for byte') + path=td/'fp.c'; exe=td/'fp' + path.write_text(common+'\n'+(tests/'fp-main.c').read_text()) + subprocess.run([os.environ.get('HOSTCC','cc'),'-std=gnu11','-Wall','-Wextra','-Werror','-Wno-unused-function','-Wno-unused-variable','-Wno-unused-parameter',str(path),'-o',str(exe)],check=True) + subprocess.run([str(exe)],check=True) From 4dda29a718751bb184573a03e4fcd783391bdc4b Mon Sep 17 00:00:00 2001 From: Ryan Murray Date: Sat, 31 Oct 2026 22:42:15 +1100 Subject: [PATCH 2/2] media: apple: avd: close standalone frame-op integration edges Route collocated MV lookup through the existing reference helper, add program and parameter overflow cases, and provide an open-only weak backend predicate for the standalone codec branch. The queue core overrides that predicate when integrated. Assisted-by: LLM Signed-off-by: Ryan Murray --- drivers/media/platform/apple/avd/H264-FRAME-OPS.rst | 3 ++- drivers/media/platform/apple/avd/avd-drv.c | 8 ++++++++ drivers/media/platform/apple/avd/avd-h264.c | 6 +++--- drivers/media/platform/apple/avd/tests/builder-main.c | 7 +++++++ drivers/media/platform/apple/avd/tests/fp-main.c | 3 +++ 5 files changed, 23 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/apple/avd/H264-FRAME-OPS.rst b/drivers/media/platform/apple/avd/H264-FRAME-OPS.rst index 44eb38f65b99f..cc0378aa47d5a 100644 --- a/drivers/media/platform/apple/avd/H264-FRAME-OPS.rst +++ b/drivers/media/platform/apple/avd/H264-FRAME-OPS.rst @@ -3,7 +3,8 @@ H.264 frame operations (integration draft) The descriptor exports ``avd_h264_frame_ops`` alongside the legacy callbacks so this codec branch builds on aurora-wip while the queue core is developed. The -legacy entry points must be removed when integrating the frame queue. Neither +legacy entry points and the weak open-backend compatibility predicate must be +removed when integrating the frame queue. Neither prepare nor retire operates hardware or removes mem2mem buffers. The queue owns request control setup/completion, source/destination lifetime and error retire. The core must call retire after prepare failures that acquired codec_priv. diff --git a/drivers/media/platform/apple/avd/avd-drv.c b/drivers/media/platform/apple/avd/avd-drv.c index aef1b52bd513b..30b0cc5a7e95a 100644 --- a/drivers/media/platform/apple/avd/avd-drv.c +++ b/drivers/media/platform/apple/avd/avd-drv.c @@ -138,6 +138,14 @@ static int avd_wait_submission_queue(struct avd_ctx *ctx, int vp) return 0; } +/* Standalone codec branches still have only the legacy open transport. + * The queue core supplies the strong backend implementation when integrated. + */ +bool __weak avd_backend_is_apple(const struct avd_dev *avd) +{ + return false; +} + int avd_init_job(struct avd_ctx *ctx, enum avd_codec codec, size_t segments) { int ret = 0; diff --git a/drivers/media/platform/apple/avd/avd-h264.c b/drivers/media/platform/apple/avd/avd-h264.c index 65d7994b92678..5dfafdc759a9f 100644 --- a/drivers/media/platform/apple/avd/avd-h264.c +++ b/drivers/media/platform/apple/avd/avd-h264.c @@ -593,9 +593,10 @@ static void stream_slice(struct avd_ctx *ctx, struct avd_h264_run *run) if (sl->slice_type == V4L2_H264_SLICE_TYPE_B) { /* bidirectional reference of previous mv */ - struct vb2_buffer *vb = vb2_find_buffer( - &ctx->fh.m2m_ctx->cap_q_ctx.q, + struct avd_decoded_buffer *ref = avd_get_ref_buf( + ctx, run->base.bufs.dst, decode->dpb[sl->ref_pic_list1[0].index].reference_ts); + struct vb2_buffer *vb = &ref->base.vb.vb2_buf; dma_addr_t mv_color_addr = vb ? vb2_dma_contig_plane_dma_addr(vb, 0) + @@ -904,7 +905,6 @@ static int avd_h264_run(struct avd_ctx *ctx) return -EINVAL; } - h264_ctx->slice_num++; h264_ctx->monochrome = run.sps->chroma_format_idc == 0; diff --git a/drivers/media/platform/apple/avd/tests/builder-main.c b/drivers/media/platform/apple/avd/tests/builder-main.c index 86c2d271dcbd7..1456ee52b48ff 100644 --- a/drivers/media/platform/apple/avd/tests/builder-main.c +++ b/drivers/media/platform/apple/avd/tests/builder-main.c @@ -107,6 +107,13 @@ int main(void) } #endif fwrite(&words, sizeof(words), 1, stdout); fwrite(output, 4, words, stdout); +#ifdef TEST_FRAME + r.fp_cursor.buf = NULL; r.error = 0; frame.prog_words = 0; + program.size = sizeof(u32); output[1] = 0xdeadbeef; + stream_hdr(&ctx, &r); + assert(r.error == -ENOSPC && frame.prog_words == 1); + assert(output[1] == 0xdeadbeef); +#endif #ifndef TEST_FRAME free(picture->slices); #endif diff --git a/drivers/media/platform/apple/avd/tests/fp-main.c b/drivers/media/platform/apple/avd/tests/fp-main.c index 5fef6c603af73..f642292edf308 100644 --- a/drivers/media/platform/apple/avd/tests/fp-main.c +++ b/drivers/media/platform/apple/avd/tests/fp-main.c @@ -29,6 +29,9 @@ int main(void) assert(!avd_h264_fp_emit(&cursor, AVD_H264_FP_SLC_CHROMA_WEIGHTS_0_, 0x2de08002)); assert(get_unaligned_le32(p+0x770) == 0x2de04001); assert(get_unaligned_le32(p+0x774) == 0x2de08002); + assert(fp_word(&fp, fp.size-3, 0) == -ENOSPC); + for (int i = 2; i < 96; i++) assert(!avd_h264_fp_emit(&cursor, AVD_H264_FP_SLC_LUMA_WEIGHTS, i)); + assert(avd_h264_fp_emit(&cursor, AVD_H264_FP_SLC_LUMA_WEIGHTS, 0) == -E2BIG); memset(p, 0, fp.size); p[0x18] = 1; p[0x19] = 1; put_unaligned_le32(0x7f, p+0x3d8);