| // 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 "src/camera/drivers/controller/ge2d_node.h" |
| |
| #include <zircon/errors.h> |
| #include <zircon/types.h> |
| |
| #include <ddk/trace/event.h> |
| #include <fbl/auto_call.h> |
| |
| #include "src/camera/drivers/controller/graph_utils.h" |
| #include "src/camera/drivers/controller/stream_pipeline_info.h" |
| #include "src/lib/syslog/cpp/logger.h" |
| |
| namespace camera { |
| |
| constexpr auto kTag = "camera_controller_ge2d_node"; |
| |
| void OnGe2dFrameAvailable(void* ctx, const frame_available_info_t* info) { |
| static_cast<camera::Ge2dNode*>(ctx)->OnFrameAvailable(info); |
| } |
| |
| // TODO(41730): Implement this. |
| void OnGe2dResChange(void* ctx, const frame_available_info_t* info) {} |
| |
| void OnGe2dTaskRemoved(void* ctx, task_remove_status_t status) { |
| static_cast<Ge2dNode*>(ctx)->OnTaskRemoved(status); |
| } |
| |
| fit::result<ProcessNode*, zx_status_t> Ge2dNode::CreateGe2dNode( |
| const ControllerMemoryAllocator& memory_allocator, async_dispatcher_t* dispatcher, |
| zx_device_t* device, const ddk::Ge2dProtocolClient& ge2d, StreamCreationData* info, |
| ProcessNode* parent_node, const InternalConfigNode& internal_ge2d_node) { |
| auto& input_buffers_hlcpp = parent_node->output_buffer_collection(); |
| auto result = GetBuffers(memory_allocator, internal_ge2d_node, info, parent_node); |
| if (result.is_error()) { |
| FX_LOGST(ERROR, kTag) << "Failed to get buffers"; |
| return fit::error(result.error()); |
| } |
| |
| auto output_buffers_hlcpp = std::move(result.value()); |
| |
| BufferCollectionHelper output_buffer_collection_helper(output_buffers_hlcpp); |
| BufferCollectionHelper input_buffer_collection_helper(input_buffers_hlcpp); |
| |
| std::vector<fuchsia_sysmem_ImageFormat_2> output_image_formats_c; |
| for (auto& format : internal_ge2d_node.image_formats) { |
| output_image_formats_c.push_back(GetImageFormatFromBufferCollection( |
| *output_buffer_collection_helper.GetC(), format.coded_width, format.coded_height)); |
| } |
| |
| std::vector<fuchsia_sysmem_ImageFormat_2> input_image_formats_c; |
| for (auto& format : parent_node->output_image_formats()) { |
| input_image_formats_c.push_back(GetImageFormatFromBufferCollection( |
| *input_buffer_collection_helper.GetC(), format.coded_width, format.coded_height)); |
| } |
| |
| auto ge2d_node = std::make_unique<camera::Ge2dNode>( |
| dispatcher, ge2d, parent_node, internal_ge2d_node.image_formats, |
| std::move(output_buffers_hlcpp), info->stream_config->properties.stream_type(), |
| internal_ge2d_node.supported_streams, internal_ge2d_node.output_frame_rate, |
| internal_ge2d_node.ge2d_info.resize); |
| if (!ge2d_node) { |
| FX_LOGST(ERROR, kTag) << "Failed to create GE2D node"; |
| return fit::error(ZX_ERR_NO_MEMORY); |
| } |
| |
| // Initialize the GE2D to get a unique task index. |
| uint32_t ge2d_task_index = 0; |
| switch (internal_ge2d_node.ge2d_info.config_type) { |
| case Ge2DConfig::GE2D_RESIZE: { |
| auto status = ge2d.InitTaskResize( |
| input_buffer_collection_helper.GetC(), output_buffer_collection_helper.GetC(), |
| ge2d_node->resize_info(), input_image_formats_c.data(), output_image_formats_c.data(), |
| output_image_formats_c.size(), info->image_format_index, ge2d_node->frame_callback(), |
| ge2d_node->res_callback(), ge2d_node->remove_task_callback(), &ge2d_task_index); |
| if (status != ZX_OK) { |
| FX_PLOGST(ERROR, kTag, status) << "Failed to initialize GE2D resize task"; |
| return fit::error(status); |
| } |
| break; |
| } |
| case Ge2DConfig::GE2D_WATERMARK: { |
| std::vector<zx::vmo> watermark_vmos; |
| for (auto watermark : internal_ge2d_node.ge2d_info.watermark) { |
| size_t size; |
| zx::vmo vmo; |
| auto status = load_firmware(device, watermark.filename, vmo.reset_and_get_address(), &size); |
| if (status != ZX_OK || size == 0) { |
| FX_PLOGST(ERROR, kTag, status) << "Failed to load the watermark image"; |
| return fit::error(status); |
| } |
| watermark_vmos.push_back(std::move(vmo)); |
| } |
| |
| std::vector<water_mark_info> watermarks_info; |
| for (uint32_t i = 0; i < internal_ge2d_node.ge2d_info.watermark.size(); i++) { |
| water_mark_info info; |
| info.loc_x = internal_ge2d_node.ge2d_info.watermark[i].loc_x; |
| info.loc_y = internal_ge2d_node.ge2d_info.watermark[i].loc_y; |
| info.wm_image_format = |
| ConvertHlcppImageFormat2toCType(internal_ge2d_node.ge2d_info.watermark[i].image_format); |
| info.watermark_vmo = watermark_vmos[i].release(); |
| constexpr float kGlobalAlpha = 200.f / 255; |
| info.global_alpha = kGlobalAlpha; |
| watermarks_info.push_back(std::move(info)); |
| } |
| |
| auto cleanup = fbl::MakeAutoCall([watermarks_info]() { |
| for (auto info : watermarks_info) { |
| ZX_ASSERT_MSG(ZX_OK == zx_handle_close(info.watermark_vmo), |
| "Failed to free up watermark VMOs"); |
| } |
| }); |
| |
| auto status = ge2d.InitTaskWaterMark( |
| input_buffer_collection_helper.GetC(), output_buffer_collection_helper.GetC(), |
| watermarks_info.data(), watermarks_info.size(), input_image_formats_c.data(), |
| input_image_formats_c.size(), info->image_format_index, ge2d_node->frame_callback(), |
| ge2d_node->res_callback(), ge2d_node->remove_task_callback(), &ge2d_task_index); |
| if (status != ZX_OK) { |
| FX_PLOGST(ERROR, kTag, status) << "Failed to initialize GE2D watermark task"; |
| return fit::error(status); |
| } |
| break; |
| } |
| default: { |
| FX_LOGST(ERROR, kTag) << "Unkwon config type"; |
| return fit::error(ZX_ERR_INVALID_ARGS); |
| } |
| } |
| |
| ge2d_node->set_task_index(ge2d_task_index); |
| |
| auto return_value = fit::ok(ge2d_node.get()); |
| parent_node->AddChildNodeInfo(std::move(ge2d_node)); |
| return return_value; |
| } |
| |
| void Ge2dNode::OnFrameAvailable(const frame_available_info_t* info) { |
| TRACE_DURATION("camera", "Ge2dNode::OnFrameAvailable", "buffer_index", info->buffer_id); |
| // Once shutdown is requested no calls should be made to the driver. |
| if (!shutdown_requested_) { |
| UpdateFrameCounterForAllChildren(); |
| |
| if (NeedToDropFrame()) { |
| parent_node_->OnReleaseFrame(info->metadata.input_buffer_index); |
| ge2d_.ReleaseFrame(task_index_, info->buffer_id); |
| } else { |
| ProcessNode::OnFrameAvailable(info); |
| } |
| } |
| } |
| |
| void Ge2dNode::OnReleaseFrame(uint32_t buffer_index) { |
| TRACE_DURATION("camera", "Ge2dNode::OnReleaseFrame", "buffer_index", buffer_index); |
| fbl::AutoLock guard(&in_use_buffer_lock_); |
| ZX_ASSERT(buffer_index < in_use_buffer_count_.size()); |
| in_use_buffer_count_[buffer_index]--; |
| if (in_use_buffer_count_[buffer_index] != 0) { |
| return; |
| } |
| if (!shutdown_requested_) { |
| ge2d_.ReleaseFrame(task_index_, buffer_index); |
| } |
| } |
| |
| void Ge2dNode::OnReadyToProcess(const frame_available_info_t* info) { |
| async::PostTask(dispatcher_, [this, buffer_index = info->buffer_id]() { |
| TRACE_DURATION("camera", "Ge2dNode::OnReadyToProcess", "buffer_index", buffer_index); |
| if (enabled_) { |
| ZX_ASSERT(ZX_OK == ge2d_.ProcessFrame(task_index_, buffer_index)); |
| } else { |
| // Since streaming is disabled the incoming frame is released |
| // so it gets added back to the pool. |
| parent_node_->OnReleaseFrame(buffer_index); |
| } |
| }); |
| } |
| |
| void Ge2dNode::OnTaskRemoved(zx_status_t status) { |
| ZX_ASSERT(status == ZX_OK); |
| |
| async::PostTask(dispatcher_, [this]() { |
| node_callback_received_ = true; |
| OnCallbackReceived(); |
| }); |
| } |
| |
| void Ge2dNode::OnShutdown(fit::function<void(void)> shutdown_callback) { |
| shutdown_callback_ = std::move(shutdown_callback); |
| |
| // After a shutdown request has been made, |
| // no other calls should be made to the GE2D driver. |
| shutdown_requested_ = true; |
| |
| // Request GE2D to shutdown. |
| ge2d_.RemoveTask(task_index_); |
| |
| auto child_shutdown_completion_callback = [this]() { |
| child_node_callback_received_ = true; |
| OnCallbackReceived(); |
| }; |
| |
| ZX_ASSERT_MSG(configured_streams().size() == 1, |
| "Cannot shutdown a stream which supports multiple streams"); |
| |
| // Forward the shutdown request to child node. |
| child_nodes().at(0)->OnShutdown(child_shutdown_completion_callback); |
| } |
| |
| } // namespace camera |