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..cc0378aa47d5a --- /dev/null +++ b/drivers/media/platform/apple/avd/H264-FRAME-OPS.rst @@ -0,0 +1,56 @@ +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 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. + +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..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; @@ -674,6 +682,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 +703,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 +764,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 +822,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..5dfafdc759a9f 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,13 +589,14 @@ 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 */ - 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) + @@ -534,14 +605,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 +620,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 +686,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) +static void avd_h264_free_picture(struct avd_ctx *ctx, + struct avd_h264_ctx *h264_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) -{ - 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 +736,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 +752,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 +772,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 +779,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 +867,45 @@ 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 -EINVAL; + return -EOPNOTSUPP; } - - 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; + if (!data || data_offset > payload_len || payload_len - data_offset < 3) { + 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->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; } - 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..1456ee52b48ff --- /dev/null +++ b/drivers/media/platform/apple/avd/tests/builder-main.c @@ -0,0 +1,123 @@ +/* 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); +#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 + 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..f642292edf308 --- /dev/null +++ b/drivers/media/platform/apple/avd/tests/fp-main.c @@ -0,0 +1,64 @@ +/* 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); + 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); + 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)