blob: bd179d86c1fe6932da87d3308087f54f89b6c8e1 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "h264_multi_decoder.h"
#include <lib/media/codec_impl/codec_buffer.h>
#include <cmath>
#include "registers.h"
#include "util.h"
using InitFlagReg = AvScratch2;
using HeadPaddingReg = AvScratch3;
using H264DecodeModeReg = AvScratch4;
using H264DecodeSeqInfo = AvScratch5;
using NalSearchCtl = AvScratch9;
using H264AuxAddr = AvScratchC;
using H264DecodeSizeReg = AvScratchE;
using H264AuxDataSize = AvScratchH;
using FrameCounterReg = AvScratchI;
using DpbStatusReg = AvScratchJ;
using LmemDumpAddr = AvScratchL;
using DebugReg1 = AvScratchM;
using DebugReg2 = AvScratchN;
using H264DecodeInfo = M4ControlReg;
// AvScratch1
class StreamInfo : public TypedRegisterBase<DosRegisterIo, StreamInfo, uint32_t> {
public:
DEF_FIELD(7, 0, width_in_mbs);
DEF_FIELD(23, 8, total_mbs);
static auto Get() { return AddrType(0x09c1 * 4); }
};
// AvScratch2
class SequenceInfo : public TypedRegisterBase<DosRegisterIo, SequenceInfo, uint32_t> {
public:
DEF_BIT(0, aspect_ratio_info_present_flag);
DEF_BIT(1, timing_info_present_flag);
DEF_BIT(4, pic_struct_present_flag);
// relatively lower-confidence vs. other bits - not confirmed
DEF_BIT(6, fixed_frame_rate_flag);
DEF_FIELD(14, 13, chroma_format_idc);
DEF_BIT(15, frame_mbs_only_flag);
DEF_FIELD(23, 16, aspect_ratio_idc);
static auto Get() { return AddrType(0x09c2 * 4); }
};
// AvScratchB
class StreamInfo2 : public TypedRegisterBase<DosRegisterIo, StreamInfo2, uint32_t> {
public:
DEF_FIELD(7, 0, level_idc);
DEF_FIELD(15, 8, max_reference_size);
static auto Get() { return AddrType(0x09cb * 4); }
};
// AvScratchF
class CodecSettings : public TypedRegisterBase<DosRegisterIo, CodecSettings, uint32_t> {
public:
DEF_BIT(1, trickmode_i);
DEF_BIT(2, zeroed0);
DEF_BIT(3, drop_b_frames);
DEF_BIT(4, error_recovery_mode);
DEF_BIT(5, zeroed1);
DEF_BIT(6, ip_frames_only);
DEF_BIT(7, disable_fast_poc);
static auto Get() { return AddrType(0x09cf * 4); }
};
enum DecodeMode {
// Mode where multiple streams can be decoded, and input doesn't have to be
// broken into frame-sized chunks.
kDecodeModeMultiStreamBased = 0x2
};
// Actions written by CPU into DpbStatusReg to tell the firmware what to do.
enum H264Action {
// Start searching for the head of a frame to decode.
kH264ActionSearchHead = 0xf0,
// Done responding to a config request.
kH264ActionConfigDone = 0xf2,
// Decode the first slice in a new picture.
kH264ActionDecodeNewpic = 0xf3,
};
// Actions written by the firmware into DpbStatusReg before an interrupt to tell
// the CPU what to do.
enum H264Status {
// Configure the DPB.
kH264ConfigRequest = 0x11,
// Out of input data, so get more.
kH264DataRequest = 0x12,
// Initialize the current set of reference frames and output buffer to be
// decoded into.
kH264SliceHeadDone = 0x1,
// Store the current frame into the DPB, or output it.
kH264PicDataDone = 0x2,
};
H264MultiDecoder::H264MultiDecoder(Owner* owner, Client* client)
: VideoDecoder(owner, client, /*is_secure=*/false) {}
H264MultiDecoder::~H264MultiDecoder() {
if (owner_->IsDecoderCurrent(this)) {
owner_->core()->StopDecoding();
owner_->core()->WaitForIdle();
}
}
zx_status_t H264MultiDecoder::Initialize() {
zx_status_t status = InitializeBuffers();
if (status != ZX_OK) {
DECODE_ERROR("Failed to initialize buffers");
return status;
}
return InitializeHardware();
}
zx_status_t H264MultiDecoder::LoadSecondaryFirmware(const uint8_t* data, uint32_t firmware_size) {
// For some reason, some portions of the firmware aren't loaded into the
// hardware directly, but are kept in main memory.
constexpr uint32_t kSecondaryFirmwareSize = 4 * 1024;
// Some sections of the input firmware are copied into multiple places in the output buffer, and 1
// part of the output buffer seems to be unused.
constexpr uint32_t kFirmwareSectionCount = 9;
constexpr uint32_t kSecondaryFirmwareBufferSize = kSecondaryFirmwareSize * kFirmwareSectionCount;
constexpr uint32_t kBufferAlignShift = 16;
{
zx_status_t status = io_buffer_init_aligned(&secondary_firmware_, owner_->bti()->get(),
kSecondaryFirmwareBufferSize, kBufferAlignShift,
IO_BUFFER_RW | IO_BUFFER_CONTIG);
if (status != ZX_OK) {
DECODE_ERROR("Failed to make second firmware buffer: %d", status);
return status;
}
SetIoBufferName(&secondary_firmware_, "H264SecondaryFirmware");
auto addr = static_cast<uint8_t*>(io_buffer_virt(&secondary_firmware_));
// The secondary firmware is in a different order in the file than the main
// firmware expects it to have.
memcpy(addr + 0, data + 0x4000, kSecondaryFirmwareSize); // header
memcpy(addr + 0x1000, data + 0x2000, kSecondaryFirmwareSize); // data
memcpy(addr + 0x2000, data + 0x6000, kSecondaryFirmwareSize); // mmc
memcpy(addr + 0x3000, data + 0x3000, kSecondaryFirmwareSize); // list
memcpy(addr + 0x4000, data + 0x5000, kSecondaryFirmwareSize); // slice
memcpy(addr + 0x5000, data + 0x4000, kSecondaryFirmwareSize); // main
memcpy(addr + 0x5000 + 0x2000, data + 0x2000, kSecondaryFirmwareSize); // data copy 2
memcpy(addr + 0x5000 + 0x3000, data + 0x5000, kSecondaryFirmwareSize); // slice copy 2
ZX_DEBUG_ASSERT(0x5000 + 0x3000 + kSecondaryFirmwareSize == kSecondaryFirmwareBufferSize);
}
io_buffer_cache_flush(&secondary_firmware_, 0, kSecondaryFirmwareBufferSize);
return ZX_OK;
}
constexpr uint32_t kAuxBufPrefixSize = 16 * 1024;
constexpr uint32_t kAuxBufSuffixSize = 0;
zx_status_t H264MultiDecoder::InitializeBuffers() {
constexpr uint32_t kBufferAlignment = 1 << 16;
constexpr uint32_t kCodecDataSize = 0x200000;
auto codec_data_create_result =
InternalBuffer::CreateAligned("H264MultiCodecData", &owner_->SysmemAllocatorSyncPtr(),
owner_->bti(), kCodecDataSize, kBufferAlignment, is_secure(),
/*is_writable=*/true, /*is_mapping_needed*/ false);
if (!codec_data_create_result.is_ok()) {
LOG(ERROR, "Failed to make codec data buffer - status: %d", codec_data_create_result.error());
return codec_data_create_result.error();
}
codec_data_.emplace(codec_data_create_result.take_value());
// Aux buf seems to be used for reading SEI data.
constexpr uint32_t kAuxBufSize = kAuxBufPrefixSize + kAuxBufSuffixSize;
auto aux_buf_create_result =
InternalBuffer::CreateAligned("H264AuxBuf", &owner_->SysmemAllocatorSyncPtr(), owner_->bti(),
kAuxBufSize, kBufferAlignment, /*is_secure=*/false,
/*is_writable=*/true, /*is_mapping_needed*/ false);
if (!aux_buf_create_result.is_ok()) {
LOG(ERROR, "Failed to make aux buffer - status: %d", aux_buf_create_result.error());
return aux_buf_create_result.error();
}
aux_buf_.emplace(aux_buf_create_result.take_value());
// Lmem is used to dump the AMRISC's local memory, which is needed for updating the DPB.
constexpr uint32_t kLmemBufSize = 4096;
auto lmem_create_result =
InternalBuffer::CreateAligned("H264AuxBuf", &owner_->SysmemAllocatorSyncPtr(), owner_->bti(),
kLmemBufSize, kBufferAlignment, /*is_secure=*/false,
/*is_writable=*/true, /*is_mapping_needed*/ true);
if (!lmem_create_result.is_ok()) {
LOG(ERROR, "Failed to make lmem buffer - status: %d", lmem_create_result.error());
return lmem_create_result.error();
}
lmem_.emplace(lmem_create_result.take_value());
return ZX_OK;
}
void H264MultiDecoder::ResetHardware() {
DosSwReset0::Get().FromValue(0).set_vdec_mc(1).set_vdec_iqidct(1).set_vdec_vld_part(1).WriteTo(
owner_->dosbus());
DosSwReset0::Get().FromValue(0).WriteTo(owner_->dosbus());
// Reads are used for delaying running later code.
for (uint32_t i = 0; i < 3; i++) {
DosSwReset0::Get().ReadFrom(owner_->dosbus());
}
DosSwReset0::Get().FromValue(0).set_vdec_mc(1).set_vdec_iqidct(1).set_vdec_vld_part(1).WriteTo(
owner_->dosbus());
DosSwReset0::Get().FromValue(0).WriteTo(owner_->dosbus());
DosSwReset0::Get().FromValue(0).set_vdec_pic_dc(1).set_vdec_dblk(1).WriteTo(owner_->dosbus());
DosSwReset0::Get().FromValue(0).WriteTo(owner_->dosbus());
// Reads are used for delaying running later code.
for (uint32_t i = 0; i < 3; i++) {
DosSwReset0::Get().ReadFrom(owner_->dosbus());
}
auto temp = PowerCtlVld::Get().ReadFrom(owner_->dosbus());
temp.set_reg_value(temp.reg_value() | (1 << 9) | (1 << 6));
temp.WriteTo(owner_->dosbus());
}
zx_status_t H264MultiDecoder::InitializeHardware() {
if (is_secure()) {
DECODE_ERROR("is_secure() == true not yet supported by H264MultiDecoder");
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t status =
owner_->SetProtected(VideoDecoder::Owner::ProtectableHardwareUnit::kVdec, is_secure());
if (status != ZX_OK)
return status;
FirmwareBlob::FirmwareType firmware_type = FirmwareBlob::FirmwareType::kDec_H264_Multi_Gxm;
// Don't use the TEE to load the firmware, since the version we're using on astro and sherlock
// doesn't support H264_Multi_Gxm.
uint8_t* data;
uint32_t firmware_size;
status = owner_->firmware_blob()->GetFirmwareData(firmware_type, &data, &firmware_size);
if (status != ZX_OK)
return status;
status = owner_->core()->LoadFirmware(data, firmware_size);
if (status != ZX_OK)
return status;
status = LoadSecondaryFirmware(data, firmware_size);
if (status != ZX_OK)
return status;
BarrierAfterFlush(); // After secondary_firmware_ cache is flushed to RAM.
ResetHardware();
AvScratchG::Get()
.FromValue(truncate_to_32(io_buffer_phys(&secondary_firmware_)))
.WriteTo(owner_->dosbus());
PscaleCtrl::Get().FromValue(0).WriteTo(owner_->dosbus());
VdecAssistMbox1ClrReg::Get().FromValue(1).WriteTo(owner_->dosbus());
VdecAssistMbox1Mask::Get().FromValue(1).WriteTo(owner_->dosbus());
{
auto temp = MdecPicDcCtrl::Get().ReadFrom(owner_->dosbus()).set_nv12_output(true);
temp.set_reg_value(temp.reg_value() | (0xbf << 24));
temp.WriteTo(owner_->dosbus());
temp.set_reg_value(temp.reg_value() & ~(0xbf << 24));
temp.WriteTo(owner_->dosbus());
}
MdecPicDcMuxCtrl::Get().ReadFrom(owner_->dosbus()).set_bit31(0).WriteTo(owner_->dosbus());
MdecExtIfCfg0::Get().FromValue(0).WriteTo(owner_->dosbus());
MdecPicDcThresh::Get().FromValue(0x404038aa).WriteTo(owner_->dosbus());
// Signal that the DPB hasn't been initialized yet.
// TODO(fxb/13483): Initialize DPB when this is called a second time.
AvScratch0::Get().FromValue(0).WriteTo(owner_->dosbus());
AvScratch9::Get().FromValue(0).WriteTo(owner_->dosbus());
DpbStatusReg::Get().FromValue(0).WriteTo(owner_->dosbus());
FrameCounterReg::Get().FromValue(0).WriteTo(owner_->dosbus());
constexpr uint32_t kBufferStartAddressOffset = 0x1000000;
constexpr uint32_t kDcacReadMargin = 64 * 1024;
uint32_t buffer_offset =
truncate_to_32(codec_data_->phys_base()) - kBufferStartAddressOffset + kDcacReadMargin;
AvScratch8::Get().FromValue(buffer_offset).WriteTo(owner_->dosbus());
CodecSettings::Get()
.ReadFrom(owner_->dosbus())
.set_drop_b_frames(0)
.set_zeroed0(0)
.set_error_recovery_mode(1)
.set_zeroed1(0)
.set_ip_frames_only(0)
.WriteTo(owner_->dosbus());
LmemDumpAddr::Get().FromValue(truncate_to_32(lmem_->phys_base())).WriteTo(owner_->dosbus());
DebugReg1::Get().FromValue(0).WriteTo(owner_->dosbus());
DebugReg2::Get().FromValue(0).WriteTo(owner_->dosbus());
H264DecodeInfo::Get().FromValue(1 << 13).WriteTo(owner_->dosbus());
// TODO(fxb/13483): Use real values.
constexpr uint32_t kBytesToDecode = 2000;
H264DecodeSizeReg::Get().FromValue(kBytesToDecode).WriteTo(owner_->dosbus());
ViffBitCnt::Get().FromValue(kBytesToDecode * 8).WriteTo(owner_->dosbus());
H264AuxAddr::Get().FromValue(truncate_to_32(aux_buf_->phys_base())).WriteTo(owner_->dosbus());
H264AuxDataSize::Get()
.FromValue(((kAuxBufPrefixSize / 16) << 16) | (kAuxBufSuffixSize / 16))
.WriteTo(owner_->dosbus());
H264DecodeModeReg::Get().FromValue(kDecodeModeMultiStreamBased).WriteTo(owner_->dosbus());
H264DecodeSeqInfo::Get().FromValue(0).WriteTo(owner_->dosbus());
HeadPaddingReg::Get().FromValue(0).WriteTo(owner_->dosbus());
// TODO(fxb/13483): Set to 1 on second initialization.
InitFlagReg::Get().FromValue(0).WriteTo(owner_->dosbus());
// TODO(fxb/13483): Set to 1 when SEI is supported.
NalSearchCtl::Get().FromValue(0).WriteTo(owner_->dosbus());
return ZX_OK;
}
void H264MultiDecoder::UpdateDecodeSize() {
// For now, just use the decode size from InitializeHardware.
owner_->core()->StartDecoding();
DpbStatusReg::Get().FromValue(kH264ActionSearchHead).WriteTo(owner_->dosbus());
}
void H264MultiDecoder::ConfigureDpb() {
// StreamInfo AKA AvScratch1.
auto stream_info = StreamInfo::Get().ReadFrom(owner_->dosbus());
// SequenceInfo AKA AvScratch2.
auto sequence_info = SequenceInfo::Get().ReadFrom(owner_->dosbus());
uint32_t mb_width = stream_info.width_in_mbs();
if (!mb_width && stream_info.total_mbs())
mb_width = 256;
if (!mb_width) {
// Not returning ZX_ERR_IO_DATA_INTEGRITY, because this isn't an explicit
// integrity check.
return;
}
uint32_t mb_height = stream_info.total_mbs() / mb_width;
DLOG("Got width: %d height: %d, mbs_only %d info: %x\n", mb_width, mb_height,
sequence_info.frame_mbs_only_flag(), stream_info.reg_value());
auto info2 = StreamInfo2::Get().ReadFrom(owner_->dosbus());
DLOG("Size: %d bits: %d\n", H264DecodeSizeReg::Get().ReadFrom(owner_->dosbus()).reg_value(),
ViffBitCnt::Get().ReadFrom(owner_->dosbus()).reg_value());
constexpr uint32_t kReferenceBufMargin = 4;
next_max_reference_size_ = info2.max_reference_size() + kReferenceBufMargin;
zx::bti bti;
zx_status_t status = owner_->bti()->duplicate(ZX_RIGHT_SAME_RIGHTS, &bti);
if (status != ZX_OK) {
DECODE_ERROR("bti duplicate failed, status: %d\n", status);
return;
}
constexpr uint32_t kMacroblockSize = 16;
// TODO(fxb/13483): Calculate real values, taking into account more sequence
// info.
NalSearchCtl::Get().FromValue(0).WriteTo(owner_->dosbus());
display_width_ = mb_width * kMacroblockSize;
display_height_ = mb_width * kMacroblockSize;
uint32_t min_frame_count = 22;
uint32_t max_frame_count = 24;
uint32_t coded_width = mb_width * kMacroblockSize;
uint32_t coded_height = mb_height * kMacroblockSize;
uint32_t stride = coded_width;
bool has_sar = false;
uint32_t sar_width = 0;
uint32_t sar_height = 0;
client_->InitializeFrames(std::move(bti), min_frame_count, max_frame_count, coded_width,
coded_height, stride, display_width_, display_height_, has_sar,
sar_width, sar_height);
mb_width_ = mb_width;
mb_height_ = mb_height;
}
// This struct contains parameters for the current frame that are dumped from
// lmem
struct HardwareRenderParams {
uint16_t data[0x400];
static constexpr uint32_t kNalUnitType = 0x80;
static constexpr uint32_t kNalRefIdc = 0x81;
static constexpr uint32_t kSliceType = 0x82;
static constexpr uint32_t kLog2MaxFrameNum = 0x83;
static constexpr uint32_t kPicOrderCntType = 0x85;
static constexpr uint32_t kLog2MaxPicOrderCntLsb = 0x86;
static constexpr uint32_t kEntropyCodingModeFlag = 0x8d;
static constexpr uint32_t kProfileIdcMmco = 0xe7;
// offset to dpb_max_buffer_frame.
static constexpr uint32_t kDpbStructStart = 0x100 + 24 * 8;
static constexpr uint32_t kPicOrderCntLsb = kDpbStructStart + 14;
static constexpr uint32_t kDeltaPicOrderCntBottom0 = kDpbStructStart + 19;
static constexpr uint32_t kDeltaPicOrderCntBottom1 = kDpbStructStart + 20;
// Read a pair of entries starting at |offset| as a 32-bit number.
uint32_t Read32(uint32_t offset) {
// Little endian.
return data[offset] | (static_cast<uint32_t>(data[offset + 1]) << 16);
}
void ReadFromLmem(InternalBuffer* lmem) {
lmem->CacheFlushInvalidate(0, sizeof(data));
uint16_t* input_params = reinterpret_cast<uint16_t*>(lmem->virt_base());
// Convert from middle-endian.
for (uint32_t i = 0; i < fbl::count_of(data); i += 4) {
for (uint32_t j = 0; j < 4; j++) {
data[i + j] = input_params[i + (3 - j)];
}
}
}
};
void H264MultiDecoder::HandleSliceHeadDone() {
// Setup reference frames and output buffers before decoding.
HardwareRenderParams params;
params.ReadFromLmem(&*lmem_);
DLOG("NAL unit type: %d\n", params.data[HardwareRenderParams::kNalUnitType]);
DLOG("NAL ref_idc: %d\n", params.data[HardwareRenderParams::kNalRefIdc]);
DLOG("NAL slice_type: %d\n", params.data[HardwareRenderParams::kSliceType]);
DLOG("pic order cnt type: %d\n", params.data[HardwareRenderParams::kPicOrderCntType]);
DLOG("log2_max_frame_num: %d\n", params.data[HardwareRenderParams::kLog2MaxFrameNum]);
DLOG("log2_max_pic_order_cnt: %d\n", params.data[HardwareRenderParams::kLog2MaxPicOrderCntLsb]);
DLOG("entropy coding mode flag: %d\n", params.data[HardwareRenderParams::kEntropyCodingModeFlag]);
DLOG("profile idc mmc0: %d\n", params.data[HardwareRenderParams::kProfileIdcMmco]);
current_frame_ = &video_frames_[0];
// Calculate the pic order count. This currently is good enough for the first
// frame of bear.h264.
// TODO(fxb/13483): Implement other types of calculations.
ZX_DEBUG_ASSERT(params.data[HardwareRenderParams::kPicOrderCntType] == 0);
uint32_t prevPicOrderCntMsb = 0;
uint32_t prevPicOrderCntLsb = 0;
uint32_t pic_order_cnt_lsb = params.data[HardwareRenderParams::kPicOrderCntLsb];
uint32_t PicOrderCntMsb;
uint32_t MaxPicOrderCntLsb = 1 << params.data[HardwareRenderParams::kLog2MaxPicOrderCntLsb];
// H.264 8.2.1.1
if (pic_order_cnt_lsb < prevPicOrderCntLsb &&
(prevPicOrderCntLsb - pic_order_cnt_lsb >= (MaxPicOrderCntLsb / 2))) {
PicOrderCntMsb = prevPicOrderCntMsb + MaxPicOrderCntLsb;
} else if (pic_order_cnt_lsb > prevPicOrderCntLsb &&
(pic_order_cnt_lsb - prevPicOrderCntLsb > (MaxPicOrderCntLsb / 2))) {
PicOrderCntMsb = prevPicOrderCntMsb - MaxPicOrderCntLsb;
} else {
PicOrderCntMsb = prevPicOrderCntMsb;
}
uint32_t TopFieldOrderCnt = PicOrderCntMsb + pic_order_cnt_lsb;
// Assume field_pic_flag = 0
uint32_t BottomFieldOrderCnt =
TopFieldOrderCnt + params.Read32(HardwareRenderParams::kDeltaPicOrderCntBottom0);
uint32_t FramePicOrderCnt = std::min(TopFieldOrderCnt, BottomFieldOrderCnt);
DLOG("Got frame pic order cnt: %d, lsb %d\n", FramePicOrderCnt, pic_order_cnt_lsb);
H264CurrentPocIdxReset::Get().FromValue(0).WriteTo(owner_->dosbus());
H264CurrentPoc::Get().FromValue(FramePicOrderCnt).WriteTo(owner_->dosbus());
H264CurrentPoc::Get().FromValue(TopFieldOrderCnt).WriteTo(owner_->dosbus());
H264CurrentPoc::Get().FromValue(BottomFieldOrderCnt).WriteTo(owner_->dosbus());
CurrCanvasCtrl::Get()
.FromValue(0)
.set_canvas_index(current_frame_->index)
.WriteTo(owner_->dosbus());
// Unclear if reading from the register is actually necessary, or if this
// would always be the same as above.
uint32_t curr_canvas_index =
CurrCanvasCtrl::Get().ReadFrom(owner_->dosbus()).lower_canvas_index();
RecCanvasCtrl::Get().FromValue(curr_canvas_index).WriteTo(owner_->dosbus());
DbkrCanvasCtrl::Get().FromValue(curr_canvas_index).WriteTo(owner_->dosbus());
DbkwCanvasCtrl::Get().FromValue(curr_canvas_index).WriteTo(owner_->dosbus());
// TODO(fxb/13483): BUFFER INFO data
//
// TODO(fxb/13483): Offset for multiple slices in same picture.
H264CoMbWrAddr::Get()
.FromValue(truncate_to_32(current_frame_->reference_mv_buffer.phys_base()))
.WriteTo(owner_->dosbus());
// TODO(fxb/13483) Initialize colocate mv read
// TODO(fxb/13483): new slice, same pic:
DpbStatusReg::Get().FromValue(kH264ActionDecodeNewpic).WriteTo(owner_->dosbus());
}
void H264MultiDecoder::HandlePicDataDone() {
ZX_DEBUG_ASSERT(current_frame_);
// TODO(fxb/13483): Get PTS
// TODO(fxb/13483): Output frame only when past max_num_reorder_frames (or equivalent).
client_->OnFrameReady(current_frame_->frame);
// TODO(fxb/13483): store in DPB
current_frame_ = nullptr;
DpbStatusReg::Get().FromValue(kH264ActionSearchHead).WriteTo(owner_->dosbus());
}
void H264MultiDecoder::HandleInterrupt() {
// Clear interrupt
VdecAssistMbox1ClrReg::Get().FromValue(1).WriteTo(owner_->dosbus());
uint32_t decode_status = DpbStatusReg::Get().ReadFrom(owner_->dosbus()).reg_value();
DLOG("Got H264MultiDecoder::HandleInterrupt, decode status: %x", decode_status);
switch (decode_status) {
case kH264ConfigRequest: {
DpbStatusReg::Get().FromValue(kH264ActionConfigDone).WriteTo(owner_->dosbus());
ConfigureDpb();
break;
}
case kH264DataRequest:
DECODE_ERROR("Got unhandled data request");
break;
case kH264SliceHeadDone: {
HandleSliceHeadDone();
break;
}
case kH264PicDataDone: {
HandlePicDataDone();
break;
}
}
}
void H264MultiDecoder::ReturnFrame(std::shared_ptr<VideoFrame> frame) {
DLOG("Not implemented: %s\n", __func__);
}
void H264MultiDecoder::CallErrorHandler() { DLOG("Not implemented: %s\n", __func__); }
void H264MultiDecoder::InitializedFrames(std::vector<CodecFrame> frames, uint32_t coded_width,
uint32_t coded_height, uint32_t stride) {
uint32_t frame_count = frames.size();
video_frames_.clear();
for (uint32_t i = 0; i < frame_count; ++i) {
auto frame = std::make_shared<VideoFrame>();
// While we'd like to pass in IO_BUFFER_CONTIG, since we know the VMO was
// allocated with zx_vmo_create_contiguous(), the io_buffer_init_vmo()
// treats that flag as an invalid argument, so instead we have to pretend as
// if it's a non-contiguous VMO, then validate that the VMO is actually
// contiguous later in aml_canvas_config() called by
// owner_->ConfigureCanvas() below.
assert(frames[i].codec_buffer_spec.has_data());
assert(frames[i].codec_buffer_spec.data().is_vmo());
assert(frames[i].codec_buffer_spec.data().vmo().has_vmo_handle());
zx_status_t status = io_buffer_init_vmo(
&frame->buffer, owner_->bti()->get(),
frames[i].codec_buffer_spec.data().vmo().vmo_handle().get(), 0, IO_BUFFER_RW);
if (status != ZX_OK) {
DECODE_ERROR("Failed to io_buffer_init_vmo() for frame - status: %d\n", status);
OnFatalError();
return;
}
io_buffer_cache_flush(&frame->buffer, 0, io_buffer_size(&frame->buffer, 0));
BarrierAfterFlush();
frame->hw_width = coded_width;
frame->hw_height = coded_height;
frame->coded_width = coded_width;
frame->coded_height = coded_height;
frame->stride = stride;
frame->uv_plane_offset = stride * coded_height;
frame->display_width = display_width_;
frame->display_height = display_height_;
frame->index = i;
// can be nullptr
frame->codec_buffer = frames[i].codec_buffer_ptr;
if (frames[i].codec_buffer_ptr) {
frames[i].codec_buffer_ptr->SetVideoFrame(frame);
}
// The ConfigureCanvas() calls validate that the VMO is physically
// contiguous, regardless of how the VMO was created.
auto y_canvas =
owner_->ConfigureCanvas(&frame->buffer, 0, frame->stride, frame->coded_height, 0, 0);
auto uv_canvas = owner_->ConfigureCanvas(&frame->buffer, frame->uv_plane_offset, frame->stride,
frame->coded_height / 2, 0, 0);
if (!y_canvas || !uv_canvas) {
OnFatalError();
return;
}
AncNCanvasAddr::Get(i)
.FromValue((uv_canvas->index() << 16) | (uv_canvas->index() << 8) | (y_canvas->index()))
.WriteTo(owner_->dosbus());
constexpr uint32_t kMvRefDataSizePerMb = 96;
uint32_t colocated_buffer_size =
fbl::round_up(mb_width_ * mb_height_ * kMvRefDataSizePerMb, ZX_PAGE_SIZE);
auto create_result =
InternalBuffer::Create("H264ReferenceMvs", &owner_->SysmemAllocatorSyncPtr(), owner_->bti(),
colocated_buffer_size, is_secure_,
/*is_writable=*/true, /*is_mapping_needed*/ false);
if (!create_result.is_ok()) {
LOG(ERROR, "Couldn't allocate reference mv buffer - status: %d", create_result.error());
OnFatalError();
return;
}
video_frames_.push_back({i, std::move(frame), std::move(y_canvas), std::move(uv_canvas),
create_result.take_value()});
}
AvScratch0::Get()
.FromValue((next_max_reference_size_ << 24) | (frame_count << 16) | (frame_count << 8))
.WriteTo(owner_->dosbus());
}
bool H264MultiDecoder::CanBeSwappedIn() {
DLOG("Not implemented: %s\n", __func__);
return true;
}
bool H264MultiDecoder::CanBeSwappedOut() const {
DLOG("Not implemented: %s\n", __func__);
return false;
}
void H264MultiDecoder::SetSwappedOut() { DLOG("Not implemented: %s\n", __func__); }
void H264MultiDecoder::SwappedIn() { DLOG("Not implemented: %s\n", __func__); }
void H264MultiDecoder::OnFatalError() {
if (!fatal_error_) {
fatal_error_ = true;
client_->OnError();
}
}