blob: 3e7ad6d56d1834a26c615e036872e5bbbd1617e1 [file] [log] [blame]
// Copyright 2019 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 "dma-mgr.h"
#include <lib/syslog/global.h>
#include <cstdint>
#include "../mali-009/pingpong_regs.h"
#include "dma-format.h"
namespace camera {
zx_status_t DmaManager::Create(const zx::bti& bti, const ddk::MmioView& isp_mmio_local,
DmaManager::Stream stream_type, std::unique_ptr<DmaManager>* out) {
*out = std::make_unique<DmaManager>(stream_type, isp_mmio_local);
zx_status_t status = bti.duplicate(ZX_RIGHT_SAME_RIGHTS, &(*out)->bti_);
if (status != ZX_OK) {
FX_LOGF(ERROR, "", "%s: Unable to duplicate bti for DmaManager \n", __func__);
return status;
}
return ZX_OK;
}
auto DmaManager::GetPrimaryMisc() {
if (stream_type_ == Stream::Downscaled) {
return ping::DownScaled::Primary::DmaWriter_Misc::Get();
}
return ping::FullResolution::Primary::DmaWriter_Misc::Get();
}
auto DmaManager::GetUvMisc() {
if (stream_type_ == Stream::Downscaled) {
return ping::DownScaled::Uv::DmaWriter_Misc::Get();
}
return ping::FullResolution::Uv::DmaWriter_Misc::Get();
}
auto DmaManager::GetPrimaryBank0() {
if (stream_type_ == Stream::Downscaled) {
return ping::DownScaled::Primary::DmaWriter_Bank0Base::Get();
}
return ping::FullResolution::Primary::DmaWriter_Bank0Base::Get();
}
auto DmaManager::GetUvBank0() {
if (stream_type_ == Stream::Downscaled) {
return ping::DownScaled::Uv::DmaWriter_Bank0Base::Get();
}
return ping::FullResolution::Uv::DmaWriter_Bank0Base::Get();
}
auto DmaManager::GetPrimaryLineOffset() {
if (stream_type_ == Stream::Downscaled) {
return ping::DownScaled::Primary::DmaWriter_LineOffset::Get();
}
return ping::FullResolution::Primary::DmaWriter_LineOffset::Get();
}
auto DmaManager::GetUvLineOffset() {
if (stream_type_ == Stream::Downscaled) {
return ping::DownScaled::Uv::DmaWriter_LineOffset::Get();
}
return ping::FullResolution::Uv::DmaWriter_LineOffset::Get();
}
auto DmaManager::GetPrimaryActiveDim() {
if (stream_type_ == Stream::Downscaled) {
return ping::DownScaled::Primary::DmaWriter_ActiveDim::Get();
}
return ping::FullResolution::Primary::DmaWriter_ActiveDim::Get();
}
auto DmaManager::GetUvActiveDim() {
if (stream_type_ == Stream::Downscaled) {
return ping::DownScaled::Uv::DmaWriter_ActiveDim::Get();
}
return ping::FullResolution::Uv::DmaWriter_ActiveDim::Get();
}
auto DmaManager::GetPrimaryFrameCount() {
if (stream_type_ == Stream::Downscaled) {
return ping::DownScaled::Primary::DmaWriter_FrameCount::Get();
}
return ping::FullResolution::Primary::DmaWriter_FrameCount::Get();
}
auto DmaManager::GetUvFrameCount() {
if (stream_type_ == Stream::Downscaled) {
return ping::DownScaled::Uv::DmaWriter_FrameCount::Get();
}
return ping::FullResolution::Uv::DmaWriter_FrameCount::Get();
}
auto DmaManager::GetPrimaryFailures() {
if (stream_type_ == Stream::Downscaled) {
return ping::DownScaled::Primary::DmaWriter_Failures::Get();
}
return ping::FullResolution::Primary::DmaWriter_Failures::Get();
}
auto DmaManager::GetUvFailures() {
if (stream_type_ == Stream::Downscaled) {
return ping::DownScaled::Uv::DmaWriter_Failures::Get();
}
return ping::FullResolution::Uv::DmaWriter_Failures::Get();
}
void DmaManager::PrintStatus(ddk::MmioBuffer* mmio) {
printf("%s DMA Status:\n Primary:\n",
(stream_type_ == Stream::Downscaled) ? "Downscaled" : "Full Resolution");
GetPrimaryFrameCount().ReadFrom(mmio).Print();
GetPrimaryFailures().ReadFrom(mmio).Print();
printf(" Secondary:\n");
GetUvFrameCount().ReadFrom(mmio).Print();
GetUvFailures().ReadFrom(mmio).Print();
}
zx_status_t DmaManager::Configure(
fuchsia_sysmem_BufferCollectionInfo buffer_collection,
fit::function<void(fuchsia_camera_common_FrameAvailableEvent)> frame_available_callback) {
current_format_ = DmaFormat(buffer_collection.format.image);
// TODO(CAM-54): Provide a way to dump the previous set of write locked
// buffers.
write_locked_buffers_.clear();
if (current_format_->GetImageSize() > buffer_collection.vmo_size) {
FX_LOGF(ERROR, "", "%s: Buffer size (%lu) is less than image size (%lu)!\n", __func__,
buffer_collection.vmo_size, current_format_->GetImageSize());
return ZX_ERR_INTERNAL;
}
if (buffer_collection.buffer_count > countof(buffer_collection.vmos)) {
return ZX_ERR_INVALID_ARGS;
}
zx::vmo vmos[countof(buffer_collection.vmos)];
for (uint32_t i = 0; i < buffer_collection.buffer_count; ++i) {
vmos[i] = zx::vmo(buffer_collection.vmos[i]);
}
// Pin the buffers
zx_status_t status = buffers_.Init(vmos, buffer_collection.buffer_count);
if (status != ZX_OK) {
FX_LOGF(ERROR, "", "%s: Unable to initialize buffers for DmaManager \n", __func__);
return status;
}
// Release the vmos so that the buffer collection could be reused.
for (uint32_t i = 0; i < buffer_collection.buffer_count; ++i) {
buffer_collection.vmos[i] = vmos[i].release();
}
status =
buffers_.PinVmos(bti_, fzl::VmoPool::RequireContig::Yes, fzl::VmoPool::RequireLowMem::Yes);
if (status != ZX_OK) {
FX_LOGF(ERROR, "", "%s: Unable to pin buffers for DmaManager \n", __func__);
return status;
}
frame_available_callback_ = std::move(frame_available_callback);
return ZX_OK;
}
void DmaManager::Enable() {
ZX_ASSERT(frame_available_callback_ != nullptr);
enabled_ = true;
}
void DmaManager::Disable() {
enabled_ = false;
// TODO(CAM-54): Provide a way to dump the previous set of write locked
// buffers.
write_locked_buffers_.clear();
}
void DmaManager::OnFrameWritten() {
// If we have not started streaming, just skip.
if (!enabled_) {
return;
}
ZX_ASSERT(frame_available_callback_ != nullptr);
ZX_ASSERT(!write_locked_buffers_.empty());
fuchsia_camera_common_FrameAvailableEvent event;
event.buffer_id = write_locked_buffers_.back().ReleaseWriteLockAndGetIndex();
event.frame_status = fuchsia_camera_common_FrameStatus_OK;
// TODO(garratt): set metadata
event.metadata.timestamp = 0;
frame_available_callback_(event);
ZX_ASSERT(!enabled_ || !write_locked_buffers_.empty());
// An additional check is needed here as the callback may have disabled us.
if (!enabled_) {
return;
}
write_locked_buffers_.pop_back();
}
// Called as one of the later steps when a new frame arrives.
bool DmaManager::OnNewFrame() {
// If we have not initialized yet with a format, just skip.
if (!enabled_) {
return false;
}
// 1) Get another buffer
auto buffer = buffers_.LockBufferForWrite();
if (!buffer) {
FX_LOG(ERROR, "", "Failed to get buffer\n");
// TODO(garratt): what should we do when we run out of buffers?
// If we run out of buffers, disable write and send the callback for
// out of buffers:
// clang-format off
GetPrimaryMisc().ReadFrom(&isp_mmio_local_)
.set_frame_write_on(0)
.WriteTo(&isp_mmio_local_);
if (current_format_->HasSecondaryChannel()) {
GetUvMisc().ReadFrom(&isp_mmio_local_)
.set_frame_write_on(0)
.WriteTo(&isp_mmio_local_);
}
// clang-format on
// Send callback:
fuchsia_camera_common_FrameAvailableEvent event;
event.buffer_id = 0;
event.frame_status = fuchsia_camera_common_FrameStatus_ERROR_BUFFER_FULL;
event.metadata.timestamp = 0;
frame_available_callback_(event);
return false;
}
// 2) Optional? Set the DMA settings again... seems unnecessary
// 3) Set the DMA address
auto memory_address = static_cast<uint32_t>(buffer->physical_address());
// clang-format off
GetPrimaryBank0().FromValue(0)
.set_value(memory_address + current_format_->GetBank0Offset())
.WriteTo(&isp_mmio_local_);
if (current_format_->HasSecondaryChannel()) {
GetUvBank0().FromValue(0)
.set_value(memory_address + current_format_->GetBank0OffsetUv())
.WriteTo(&isp_mmio_local_);
}
// 4) Optional? Enable Write_on
GetPrimaryMisc().ReadFrom(&isp_mmio_local_)
.set_frame_write_on(1)
.WriteTo(&isp_mmio_local_);
if (current_format_->HasSecondaryChannel()) {
GetUvMisc().ReadFrom(&isp_mmio_local_)
.set_frame_write_on(1)
.WriteTo(&isp_mmio_local_);
}
// clang-format on
WriteFormat();
// Add buffer to queue of buffers we are writing:
write_locked_buffers_.push_front(std::move(*buffer));
return true;
}
zx_status_t DmaManager::ReleaseFrame(uint32_t buffer_index) {
return buffers_.ReleaseBuffer(buffer_index);
}
void DmaManager::WriteFormat() {
// Write format to registers
// clang-format off
GetPrimaryMisc().ReadFrom(&isp_mmio_local_)
.set_base_mode(current_format_->GetBaseMode())
.set_plane_select(current_format_->GetPlaneSelect())
.WriteTo(&isp_mmio_local_);
GetPrimaryActiveDim().ReadFrom(&isp_mmio_local_)
.set_active_width(current_format_->width())
.set_active_height(current_format_->height())
.WriteTo(&isp_mmio_local_);
GetPrimaryLineOffset().ReadFrom(&isp_mmio_local_)
.set_value(current_format_->GetLineOffset())
.WriteTo(&isp_mmio_local_);
if (current_format_->HasSecondaryChannel()) {
GetUvMisc().ReadFrom(&isp_mmio_local_)
.set_base_mode(current_format_->GetBaseMode())
.set_plane_select(current_format_->GetPlaneSelectUv())
.WriteTo(&isp_mmio_local_);
GetUvActiveDim().ReadFrom(&isp_mmio_local_)
.set_active_width(current_format_->width())
.set_active_height(current_format_->height())
.WriteTo(&isp_mmio_local_);
GetUvLineOffset().ReadFrom(&isp_mmio_local_)
.set_value(current_format_->GetLineOffset())
.WriteTo(&isp_mmio_local_);
}
// clang-format on
}
} // namespace camera