| // Copyright 2018 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 "vdec1.h" |
| |
| #include <lib/ddk/io-buffer.h> |
| #include <lib/trace/event.h> |
| |
| #include <algorithm> |
| |
| #include "device_type.h" |
| #include "macros.h" |
| #include "registers.h" |
| #include "src/media/lib/memory_barriers/memory_barriers.h" |
| #include "util.h" |
| #include "video_decoder.h" |
| |
| namespace amlogic_decoder { |
| |
| namespace { |
| |
| constexpr uint32_t kFirmwareSize = 4 * 4096; |
| |
| constexpr uint32_t kReadOffsetAlignment = 512; |
| |
| } // namespace |
| |
| uint32_t Vdec1::vdec_sleep_bits() { |
| switch (owner_->device_type()) { |
| case DeviceType::kGXM: |
| case DeviceType::kG12A: |
| case DeviceType::kG12B: |
| return 0xc; |
| case DeviceType::kSM1: |
| return 0x2; |
| } |
| } |
| |
| uint32_t Vdec1::vdec_iso_bits() { |
| switch (owner_->device_type()) { |
| case DeviceType::kGXM: |
| case DeviceType::kG12A: |
| case DeviceType::kG12B: |
| return 0xc0; |
| case DeviceType::kSM1: |
| return 0x2; |
| } |
| } |
| |
| std::optional<InternalBuffer> Vdec1::LoadFirmwareToBuffer(const uint8_t* data, uint32_t len) { |
| TRACE_DURATION("media", "Vdec1::LoadFirmwareToBuffer"); |
| const uint32_t kBufferAlignShift = 16; |
| auto create_result = InternalBuffer::CreateAligned( |
| "Vdec1Firmware", &owner_->SysmemAllocatorSyncPtr(), owner_->bti(), kFirmwareSize, |
| 1 << kBufferAlignShift, /*is_secure=*/false, /*is_writable=*/true, |
| /*is_mapping_needed=*/true); |
| if (!create_result.is_ok()) { |
| DECODE_ERROR("Failed to make firmware buffer - %d", create_result.error()); |
| return {}; |
| } |
| auto buffer = create_result.take_value(); |
| memcpy(buffer.virt_base(), data, std::min(len, kFirmwareSize)); |
| buffer.CacheFlush(0, kFirmwareSize); |
| BarrierAfterFlush(); |
| return std::move(buffer); |
| } |
| |
| zx_status_t Vdec1::LoadFirmware(const uint8_t* data, uint32_t len) { |
| auto buffer = LoadFirmwareToBuffer(data, len); |
| if (!buffer) |
| return ZX_ERR_NO_MEMORY; |
| return LoadFirmware(*buffer); |
| } |
| |
| zx_status_t Vdec1::LoadFirmware(InternalBuffer& buffer) { |
| TRACE_DURATION("media", "Vdec1::LoadFirmware"); |
| ZX_DEBUG_ASSERT(buffer.size() == kFirmwareSize); |
| Mpsr::Get().FromValue(0).WriteTo(mmio()->dosbus); |
| Cpsr::Get().FromValue(0).WriteTo(mmio()->dosbus); |
| ImemDmaAdr::Get().FromValue(truncate_to_32(buffer.phys_base())).WriteTo(mmio()->dosbus); |
| ImemDmaCount::Get().FromValue(kFirmwareSize / sizeof(uint32_t)).WriteTo(mmio()->dosbus); |
| ImemDmaCtrl::Get().FromValue(0x8000 | (7 << 16)).WriteTo(mmio()->dosbus); |
| { |
| TRACE_DURATION("media", "SpinWaitForRegister"); |
| |
| // Measured spin wait time is around 5 microseconds on sherlock, so it makes sense to SpinWait. |
| if (!SpinWaitForRegister(std::chrono::milliseconds(100), [this] { |
| return (ImemDmaCtrl::Get().ReadFrom(mmio()->dosbus).reg_value() & 0x8000) == 0; |
| })) { |
| DECODE_ERROR("Failed to load microcode, ImemDmaCtrl %d, ImemDmaAdr 0x%x", |
| ImemDmaCtrl::Get().ReadFrom(mmio()->dosbus).reg_value(), |
| ImemDmaAdr::Get().ReadFrom(mmio()->dosbus).reg_value()); |
| |
| BarrierBeforeRelease(); |
| return ZX_ERR_TIMED_OUT; |
| } |
| } |
| |
| BarrierBeforeRelease(); |
| return ZX_OK; |
| } |
| |
| void Vdec1::PowerOn() { |
| ZX_DEBUG_ASSERT(!powered_on_); |
| // Make sure that the clocks are ungated before we apply power, and reset the |
| // DOS unit. In the past, we have seen a rare issue on ~3 devices where, a |
| // failure to have the clocks running before the DOS unit was powered up and |
| // reset, would result in a system-wide lockup. This was confirmed on only 1 |
| // device. The other two were production devices which could not be |
| // instrumented. |
| // |
| // Experimental evidence seems to suggest that it may have been the main DDR |
| // controller which was locking up, but it is difficult to tell right now as |
| // the available documentation is extremely limited, and provides more or less |
| // no guidance on the subject of a proper power on/reset sequence for this |
| // unit. |
| // |
| // Either way, we currently let the clocks run before even powering up the DOS |
| // unit. The magic "bad" device seems to like this more than doing it after |
| // resetting the DOS unit. |
| owner_->UngateClocks(); |
| |
| { |
| auto temp = AoRtiGenPwrSleep0::Get().ReadFrom(mmio()->aobus); |
| temp.set_reg_value(temp.reg_value() & ~vdec_sleep_bits()); |
| temp.WriteTo(mmio()->aobus); |
| } |
| zx_nanosleep(zx_deadline_after(ZX_USEC(10))); |
| |
| DosSwReset0::Get().FromValue(0xfffffffc).WriteTo(mmio()->dosbus); |
| DosSwReset0::Get().FromValue(0).WriteTo(mmio()->dosbus); |
| |
| enum { |
| kGxmFclkDiv4 = 0, // 500 MHz |
| kGxmFclkDiv3 = 1, // 666 MHz |
| kGxmFclkDiv5 = 2, // 400 MHz |
| kGxmFclkDiv7 = 3, // 285.7 MHz |
| kGxmMp1 = 4, |
| kGxmMp2 = 5, |
| kGxmGp0 = 6, |
| kGxmXtal = 7, // 24 MHz |
| |
| // G12B has the same clock inputs as G12A. |
| kG12xFclkDiv2p5 = 0, // 800 MHz |
| kG12xFclkDiv3 = 1, // 666 MHz |
| kG12xFclkDiv4 = 2, // 500 MHz |
| kG12xFclkDiv5 = 3, // 400 MHz |
| kG12xFclkDiv7 = 4, // 285.7 MHz |
| kG12xHifi = 5, |
| kG12xGp0 = 6, |
| kG12xXtal = 7, // 24 MHz |
| }; |
| |
| // The maximum frequency used in linux is 648 MHz, but that requires using GP0, which is already |
| // being used by the GPU. The linux driver also uses 200MHz in some circumstances for videos <= |
| // 1080p30. |
| // |
| // We can run all the way up at 800 MHz without glitches, after fixing some glitches we were |
| // seeing before especially at higher clock rates. However, 500 MHz is plenty for now, and it |
| // seems prudent to run at <= the max frequency used on linux, just in case. |
| uint32_t clock_sel; |
| switch (owner_->device_type()) { |
| case DeviceType::kG12A: |
| case DeviceType::kG12B: |
| case DeviceType::kSM1: |
| clock_sel = kG12xFclkDiv4; |
| break; |
| case DeviceType::kGXM: |
| clock_sel = kGxmFclkDiv4; |
| break; |
| } |
| |
| HhiVdecClkCntl::Get() |
| .ReadFrom(mmio()->hiubus) |
| .set_vdec_en(true) |
| .set_vdec_sel(clock_sel) |
| .WriteTo(mmio()->hiubus); |
| // This driver doesn't currently reverse this setting during PowerOff(), but perhaps ideally it |
| // would. |
| owner_->ToggleClock(ClockType::kGclkVdec, true); |
| DosMemPdVdec::Get().FromValue(0).WriteTo(mmio()->dosbus); |
| |
| { |
| auto temp = AoRtiGenPwrIso0::Get().ReadFrom(mmio()->aobus); |
| temp.set_reg_value(temp.reg_value() & ~vdec_iso_bits()); |
| temp.WriteTo(mmio()->aobus); |
| } |
| DosVdecMcrccStallCtrl::Get().FromValue(0).WriteTo(mmio()->dosbus); |
| if (IsDeviceAtLeast(owner_->device_type(), DeviceType::kG12A)) { |
| DmcReqCtrl::Get().ReadFrom(mmio()->dmc).set_g12a_vdec(true).WriteTo(mmio()->dmc); |
| } else { |
| DmcReqCtrl::Get().ReadFrom(mmio()->dmc).set_vdec(true).WriteTo(mmio()->dmc); |
| } |
| |
| MdecPicDcCtrl::Get().ReadFrom(mmio()->dosbus).set_bit31(false).WriteTo(mmio()->dosbus); |
| |
| // Reset all the hardware again. Doing it at this time doesn't match the linux driver, but instead |
| // matches the hardware documentation. If we don't do this, restoring the input context or loading |
| // the firmware can hang. |
| DosSwReset0::Get().FromValue(0xfffffffc).WriteTo(mmio()->dosbus); |
| DosSwReset0::Get().FromValue(0).WriteTo(mmio()->dosbus); |
| |
| powered_on_ = true; |
| } |
| |
| void Vdec1::PowerOff() { |
| ZX_DEBUG_ASSERT(powered_on_); |
| powered_on_ = false; |
| if (IsDeviceAtLeast(owner_->device_type(), DeviceType::kG12A)) { |
| DmcReqCtrl::Get().ReadFrom(mmio()->dmc).set_g12a_vdec(false).WriteTo(mmio()->dmc); |
| } else { |
| DmcReqCtrl::Get().ReadFrom(mmio()->dmc).set_vdec(false).WriteTo(mmio()->dmc); |
| } |
| zx_nanosleep(zx_deadline_after(ZX_USEC(10))); |
| { |
| auto temp = AoRtiGenPwrIso0::Get().ReadFrom(mmio()->aobus); |
| temp.set_reg_value(temp.reg_value() | vdec_iso_bits()); |
| temp.WriteTo(mmio()->aobus); |
| } |
| DosMemPdVdec::Get().FromValue(~0u).WriteTo(mmio()->dosbus); |
| HhiVdecClkCntl::Get().ReadFrom(mmio()->hiubus).set_vdec_en(false).WriteTo(mmio()->hiubus); |
| |
| { |
| auto temp = AoRtiGenPwrSleep0::Get().ReadFrom(mmio()->aobus); |
| temp.set_reg_value(temp.reg_value() | vdec_sleep_bits()); |
| temp.WriteTo(mmio()->aobus); |
| } |
| owner_->GateClocks(); |
| } |
| |
| void Vdec1::StartDecoding() { |
| // Delay to ensure previous writes have executed. |
| for (uint32_t i = 0; i < 3; i++) { |
| DosSwReset0::Get().ReadFrom(mmio()->dosbus); |
| } |
| |
| DosSwReset0::Get().FromValue(0).set_vdec_ccpu(1).set_vdec_mcpu(1).WriteTo(mmio()->dosbus); |
| DosSwReset0::Get().FromValue(0).WriteTo(mmio()->dosbus); |
| |
| // Delay to ensure previous writes have executed. |
| for (uint32_t i = 0; i < 3; i++) { |
| DosSwReset0::Get().ReadFrom(mmio()->dosbus); |
| } |
| |
| Mpsr::Get().FromValue(1).WriteTo(mmio()->dosbus); |
| decoding_started_ = true; |
| } |
| |
| void Vdec1::StopDecoding() { |
| if (!decoding_started_) |
| return; |
| decoding_started_ = false; |
| Mpsr::Get().FromValue(0).WriteTo(mmio()->dosbus); |
| Cpsr::Get().FromValue(0).WriteTo(mmio()->dosbus); |
| |
| if (!WaitForRegister(std::chrono::milliseconds(100), [this] { |
| return (ImemDmaCtrl::Get().ReadFrom(mmio()->dosbus).reg_value() & 0x8000) == 0; |
| })) { |
| DECODE_ERROR("Failed to wait for IMEM DMA completion"); |
| return; |
| } |
| |
| if (!WaitForRegister(std::chrono::milliseconds(100), [this] { |
| return (LmemDmaCtrl::Get().ReadFrom(mmio()->dosbus).reg_value() & 0x8000) == 0; |
| })) { |
| DECODE_ERROR("Failed to wait for LMEM DMA completion"); |
| return; |
| } |
| |
| // Delay to ensure previous writes have executed. |
| for (uint32_t i = 0; i < 3; i++) { |
| DosSwReset0::Get().ReadFrom(mmio()->dosbus); |
| } |
| |
| DosSwReset0::Get().FromValue(0).set_vdec_ccpu(1).set_vdec_mcpu(1).WriteTo(mmio()->dosbus); |
| DosSwReset0::Get().FromValue(0).WriteTo(mmio()->dosbus); |
| |
| // Delay to ensure previous write have executed. |
| for (uint32_t i = 0; i < 3; i++) { |
| DosSwReset0::Get().ReadFrom(mmio()->dosbus); |
| } |
| } |
| |
| void Vdec1::WaitForIdle() { |
| auto timeout = std::chrono::milliseconds(100); |
| LOG(DEBUG, "MdecPicDcStatus wait..."); |
| if (!WaitForRegister(timeout, [this] { |
| return MdecPicDcStatus::Get().ReadFrom(mmio()->dosbus).reg_value() == 0; |
| })) { |
| // Forcibly shutoff video output hardware. Probably. |
| LOG(DEBUG, "Forcibly MdecPicDcCtrl..."); |
| auto temp = MdecPicDcCtrl::Get().ReadFrom(mmio()->dosbus); |
| temp.set_reg_value(1 | temp.reg_value()); |
| temp.WriteTo(mmio()->dosbus); |
| temp.set_reg_value(~1 & temp.reg_value()); |
| temp.WriteTo(mmio()->dosbus); |
| for (uint32_t i = 0; i < 3; i++) { |
| MdecPicDcStatus::Get().ReadFrom(mmio()->dosbus); |
| } |
| } |
| LOG(DEBUG, "DblkStatus wait..."); |
| if (!WaitForRegister(timeout, [this] { |
| return !(DblkStatus::Get().ReadFrom(mmio()->dosbus).reg_value() & 1); |
| })) { |
| // Forcibly shutoff deblocking hardware. |
| LOG(DEBUG, "Forcibly DblkCtrl..."); |
| DblkCtrl::Get().FromValue(3).WriteTo(mmio()->dosbus); |
| DblkCtrl::Get().FromValue(0).WriteTo(mmio()->dosbus); |
| for (uint32_t i = 0; i < 3; i++) { |
| DblkStatus::Get().ReadFrom(mmio()->dosbus); |
| } |
| } |
| |
| LOG(DEBUG, "McStatus0 wait..."); |
| if (!WaitForRegister(timeout, [this] { |
| return !(McStatus0::Get().ReadFrom(mmio()->dosbus).reg_value() & 1); |
| })) { |
| // Forcibly shutoff reference frame reading hardware. |
| LOG(DEBUG, "Forcibly McCtrl1..."); |
| auto temp = McCtrl1::Get().ReadFrom(mmio()->dosbus); |
| temp.set_reg_value(0x9 | temp.reg_value()); |
| temp.WriteTo(mmio()->dosbus); |
| temp.set_reg_value(~0x9 & temp.reg_value()); |
| temp.WriteTo(mmio()->dosbus); |
| for (uint32_t i = 0; i < 3; i++) { |
| McStatus0::Get().ReadFrom(mmio()->dosbus); |
| } |
| } |
| LOG(DEBUG, "DcacDmaCtrl wait..."); |
| WaitForRegister(timeout, [this] { |
| return !(DcacDmaCtrl::Get().ReadFrom(mmio()->dosbus).reg_value() & 0x8000); |
| }); |
| LOG(DEBUG, "DcacDmaCtrl wait done."); |
| } |
| |
| void Vdec1::InitializeStreamInput(bool use_parser, uint32_t buffer_address, uint32_t buffer_size) { |
| ZX_DEBUG_ASSERT(buffer_size % kReadOffsetAlignment == 0); |
| |
| VldMemVififoControl::Get().FromValue(0).WriteTo(mmio()->dosbus); |
| VldMemVififoWrapCount::Get().FromValue(0).WriteTo(mmio()->dosbus); |
| |
| // These reset bits avoid the fifo leaking in data. With these bits we can cleanly re-start |
| // decode without stale fifo bits leaking in. This allows using InitializeStreamInput() to |
| // re-start decode almost as if we're restoring a saved input context. |
| DosSwReset0::Get().FromValue(0).set_vdec_vld(1).set_vdec_vld_part(1).set_vdec_vififo(1).WriteTo( |
| mmio()->dosbus); |
| DosSwReset0::Get().FromValue(0).WriteTo(mmio()->dosbus); |
| |
| Reset0Register::Get().ReadFrom(mmio()->reset); |
| |
| auto temp = PowerCtlVld::Get().ReadFrom(mmio()->dosbus); |
| temp.set_reg_value(1 << 4); |
| temp.WriteTo(mmio()->dosbus); |
| temp = PowerCtlVld::Get().ReadFrom(mmio()->dosbus); |
| temp.set_reg_value(temp.reg_value() | (1 << 6) | (1 << 9)); |
| temp.WriteTo(mmio()->dosbus); |
| |
| VldMemVififoStartPtr::Get().FromValue(buffer_address).WriteTo(mmio()->dosbus); |
| VldMemVififoEndPtr::Get().FromValue(buffer_address + buffer_size - 8).WriteTo(mmio()->dosbus); |
| VldMemVififoCurrPtr::Get().FromValue(buffer_address).WriteTo(mmio()->dosbus); |
| VldMemVififoControl::Get().FromValue(0).set_init(true).WriteTo(mmio()->dosbus); |
| VldMemVififoControl::Get().FromValue(0).WriteTo(mmio()->dosbus); |
| VldMemVififoBufCntl::Get().FromValue(0).set_manual(true).WriteTo(mmio()->dosbus); |
| VldMemVififoRP::Get().FromValue(buffer_address).WriteTo(mmio()->dosbus); |
| VldMemVififoWP::Get().FromValue(buffer_address).WriteTo(mmio()->dosbus); |
| VldMemVififoBufCntl::Get().FromValue(0).set_manual(true).set_init(true).WriteTo(mmio()->dosbus); |
| VldMemVififoBufCntl::Get().FromValue(0).set_manual(true).WriteTo(mmio()->dosbus); |
| auto fifo_control = |
| VldMemVififoControl::Get().FromValue(0).set_upper(0x11).set_fill_on_level(true); |
| if (use_parser) { |
| fifo_control.set_fill_en(true).set_empty_en(true); |
| } |
| // Expect input to be in normal byte order. |
| fifo_control.set_endianness(7); |
| fifo_control.WriteTo(mmio()->dosbus); |
| } |
| |
| void Vdec1::InitializeParserInput() { |
| VldMemVififoBufCntl::Get().FromValue(0).set_init(true).WriteTo(mmio()->dosbus); |
| VldMemVififoBufCntl::Get().FromValue(0).WriteTo(mmio()->dosbus); |
| |
| DosGenCtrl0::Get().FromValue(0).WriteTo(mmio()->dosbus); |
| } |
| |
| void Vdec1::InitializeDirectInput() { |
| VldMemVififoBufCntl::Get().FromValue(0).set_init(true).set_manual(true).WriteTo(mmio()->dosbus); |
| VldMemVififoBufCntl::Get().FromValue(0).set_manual(true).WriteTo(mmio()->dosbus); |
| } |
| |
| void Vdec1::UpdateWriteOffset(uint32_t write_offset) { |
| uint32_t buffer_start = VldMemVififoStartPtr::Get().ReadFrom(mmio()->dosbus).reg_value(); |
| assert(buffer_start + write_offset >= buffer_start); |
| UpdateWritePointer(buffer_start + write_offset); |
| } |
| |
| void Vdec1::UpdateWritePointer(uint32_t write_pointer) { |
| VldMemVififoWP::Get().FromValue(write_pointer).WriteTo(mmio()->dosbus); |
| VldMemVififoControl::Get() |
| .ReadFrom(mmio()->dosbus) |
| .set_fill_en(true) |
| .set_empty_en(true) |
| .WriteTo(mmio()->dosbus); |
| } |
| |
| uint32_t Vdec1::GetStreamInputOffset() { |
| uint32_t write_ptr = VldMemVififoWP::Get().ReadFrom(mmio()->dosbus).reg_value(); |
| uint32_t buffer_start = VldMemVififoStartPtr::Get().ReadFrom(mmio()->dosbus).reg_value(); |
| assert(write_ptr >= buffer_start); |
| return write_ptr - buffer_start; |
| } |
| |
| uint32_t Vdec1::GetReadOffset() { |
| uint32_t read_ptr = VldMemVififoRP::Get().ReadFrom(mmio()->dosbus).reg_value(); |
| uint32_t buffer_start = VldMemVififoStartPtr::Get().ReadFrom(mmio()->dosbus).reg_value(); |
| assert(read_ptr >= buffer_start); |
| return read_ptr - buffer_start; |
| } |
| |
| zx_status_t Vdec1::InitializeInputContext(InputContext* context, bool is_secure) { |
| constexpr uint32_t kInputContextSize = 4096; |
| auto create_result = InternalBuffer::Create("VDec1InputCtx", &owner_->SysmemAllocatorSyncPtr(), |
| owner_->bti(), kInputContextSize, is_secure, |
| /*is_writable=*/true, /*is_mapping_needed_=*/false); |
| if (!create_result.is_ok()) { |
| LOG(ERROR, "Failed to allocate input context - status: %d", create_result.error()); |
| return create_result.error(); |
| } |
| // Sysmem has already written zeroes, flushed the zeroes, fenced the flush, to the extent |
| // possible. |
| context->buffer.emplace(create_result.take_value()); |
| return ZX_OK; |
| } |
| |
| zx_status_t Vdec1::SaveInputContext(InputContext* context) { |
| // Not sure why there are dirty cache lines corresponding to the input context. |
| context->buffer->CacheFlush(0, context->buffer->size()); |
| |
| // No idea what this does. |
| VldMemVififoControl::Get().FromValue(1 << 15).WriteTo(mmio()->dosbus); |
| VldMemSwapAddr::Get() |
| .FromValue(truncate_to_32(context->buffer->phys_base())) |
| .WriteTo(mmio()->dosbus); |
| VldMemSwapCtrl::Get().FromValue(0).set_enable(true).set_save(true).WriteTo(mmio()->dosbus); |
| bool finished = SpinWaitForRegister(std::chrono::milliseconds(100), [this]() { |
| return !VldMemSwapCtrl::Get().ReadFrom(mmio()->dosbus).in_progress(); |
| }); |
| if (!finished) { |
| DECODE_ERROR("Timed out in VDec1::SaveInputContext"); |
| return ZX_ERR_TIMED_OUT; |
| } |
| VldMemSwapCtrl::Get().FromValue(0).WriteTo(mmio()->dosbus); |
| return ZX_OK; |
| } |
| |
| zx_status_t Vdec1::RestoreInputContext(InputContext* context) { |
| VldMemVififoControl::Get().FromValue(0).WriteTo(mmio()->dosbus); |
| |
| // Reset the input hardware. |
| DosSwReset0::Get().FromValue(0).set_vdec_vld(1).set_vdec_vld_part(1).set_vdec_vififo(1).WriteTo( |
| mmio()->dosbus); |
| DosSwReset0::Get().FromValue(0).WriteTo(mmio()->dosbus); |
| |
| // Dummy read to give time for the hardware to reset. |
| Reset0Register::Get().ReadFrom(mmio()->reset); |
| |
| // Power on various parts of the VLD hardware. This needs to be done before |
| // swapping in or else some state will remain uninitialized. Bit 9 holds |
| // information related to the escape sequence status. |
| PowerCtlVld::Get().FromValue(0).set_reg_value(1 << 4).WriteTo(mmio()->dosbus); |
| auto temp = PowerCtlVld::Get().ReadFrom(mmio()->dosbus); |
| temp.set_reg_value(temp.reg_value() | (1 << 6) | (1 << 9)); |
| temp.WriteTo(mmio()->dosbus); |
| |
| VldMemVififoControl::Get().FromValue(0).WriteTo(mmio()->dosbus); |
| |
| VldMemSwapAddr::Get() |
| .FromValue(truncate_to_32(context->buffer->phys_base())) |
| .WriteTo(mmio()->dosbus); |
| VldMemSwapCtrl::Get().FromValue(0).set_enable(true).set_save(false).WriteTo(mmio()->dosbus); |
| bool finished = SpinWaitForRegister(std::chrono::milliseconds(100), [this]() { |
| return !VldMemSwapCtrl::Get().ReadFrom(mmio()->dosbus).in_progress(); |
| }); |
| if (!finished) { |
| DECODE_ERROR("Timed out in VDec1::RestoreInputContext"); |
| return ZX_ERR_TIMED_OUT; |
| } |
| VldMemSwapCtrl::Get().FromValue(0).WriteTo(mmio()->dosbus); |
| auto fifo_control = |
| VldMemVififoControl::Get().FromValue(0).set_upper(0x11).set_fill_on_level(true); |
| // Expect input to be in normal byte order. |
| fifo_control.set_endianness(7); |
| fifo_control.WriteTo(mmio()->dosbus); |
| return ZX_OK; |
| } |
| |
| } // namespace amlogic_decoder |