blob: 39c89b45ea449c93ef22fe7901afb13dab514c75 [file] [log] [blame]
// Copyright 2016 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 "simple-display.h"
#include <fidl/fuchsia.hardware.pci/cpp/wire.h>
#include <fidl/fuchsia.images2/cpp/wire.h>
#include <fidl/fuchsia.sysmem/cpp/wire.h>
#include <lib/async-loop/default.h>
#include <lib/ddk/debug.h>
#include <lib/ddk/device.h>
#include <lib/ddk/driver.h>
#include <lib/device-protocol/pci.h>
#include <lib/image-format/image_format.h>
#include <lib/sysmem-version/sysmem-version.h>
#include <lib/zbi-format/graphics.h>
#include <unistd.h>
#include <zircon/process.h>
#include <zircon/syscalls.h>
#include <zircon/types.h>
#include <atomic>
#include <cstdlib>
#include <cstring>
#include <memory>
#include <utility>
#include <bind/fuchsia/sysmem/heap/cpp/bind.h>
#include <fbl/alloc_checker.h>
#include <fbl/auto_lock.h>
#include "src/graphics/display/lib/api-types-cpp/config-stamp.h"
#include "src/graphics/display/lib/api-types-cpp/display-id.h"
#include "src/graphics/display/lib/api-types-cpp/display-timing.h"
namespace {
static constexpr display::DisplayId kDisplayId(1);
static constexpr uint64_t kImageHandle = 0xdecafc0ffee;
// Just guess that it's 30fps
static constexpr int kRefreshRateHz = 30;
static constexpr auto kVSyncInterval = zx::usec(1000000 / kRefreshRateHz);
fuchsia_hardware_sysmem::wire::HeapProperties GetHeapProperties(fidl::AnyArena& arena) {
fuchsia_hardware_sysmem::wire::CoherencyDomainSupport coherency_domain_support =
fuchsia_hardware_sysmem::wire::CoherencyDomainSupport::Builder(arena)
.cpu_supported(false)
.ram_supported(true)
.inaccessible_supported(false)
.Build();
fuchsia_hardware_sysmem::wire::HeapProperties heap_properties =
fuchsia_hardware_sysmem::wire::HeapProperties::Builder(arena)
.coherency_domain_support(std::move(coherency_domain_support))
.need_clear(false)
.Build();
return heap_properties;
}
void OnHeapServerClose(fidl::UnbindInfo info, zx::channel channel) {
if (info.is_dispatcher_shutdown()) {
// Pending wait is canceled because the display device that the heap belongs
// to has been destroyed.
zxlogf(INFO, "Simple display destroyed: status: %s", info.status_string());
return;
}
if (info.is_peer_closed()) {
zxlogf(INFO, "Client closed heap connection");
return;
}
zxlogf(ERROR, "Channel internal error: status: %s", info.FormatDescription().c_str());
}
zx_koid_t GetCurrentProcessKoid() {
zx_handle_t handle = zx_process_self();
zx_info_handle_basic_t info;
zx_status_t status =
zx_object_get_info(handle, ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
return status == ZX_OK ? info.koid : ZX_KOID_INVALID;
}
} // namespace
// implement display controller protocol:
void SimpleDisplay::DisplayControllerImplSetDisplayControllerInterface(
const display_controller_interface_protocol_t* intf) {
intf_ = ddk::DisplayControllerInterfaceProtocolClient(intf);
added_display_args_t args = {};
args.display_id = display::ToBanjoDisplayId(kDisplayId);
args.panel_capabilities_source = PANEL_CAPABILITIES_SOURCE_DISPLAY_MODE;
const int64_t pixel_clock_hz = int64_t{width_} * height_ * kRefreshRateHz;
ZX_DEBUG_ASSERT(pixel_clock_hz >= 0);
ZX_DEBUG_ASSERT(pixel_clock_hz <= display::kMaxPixelClockHz);
const display::DisplayTiming timing = {
.horizontal_active_px = static_cast<int32_t>(width_),
.horizontal_front_porch_px = 0,
.horizontal_sync_width_px = 0,
.horizontal_back_porch_px = 0,
.vertical_active_lines = static_cast<int32_t>(height_),
.vertical_front_porch_lines = 0,
.vertical_sync_width_lines = 0,
.vertical_back_porch_lines = 0,
.pixel_clock_frequency_hz = pixel_clock_hz,
.fields_per_frame = display::FieldsPerFrame::kProgressive,
.hsync_polarity = display::SyncPolarity::kNegative,
.vsync_polarity = display::SyncPolarity::kNegative,
.vblank_alternates = false,
.pixel_repetition = 0,
};
args.panel.mode = display::ToBanjoDisplayMode(timing);
// fuchsia.images2.PixelFormat can always cast to AnyPixelFormat safely.
fuchsia_images2_pixel_format_enum_value_t pixel_format =
static_cast<fuchsia_images2_pixel_format_enum_value_t>(format_);
args.pixel_format_list = &pixel_format;
args.pixel_format_count = 1;
intf_.OnDisplaysChanged(&args, 1, nullptr, 0);
}
void SimpleDisplay::DisplayControllerImplResetDisplayControllerInterface() {
intf_ = ddk::DisplayControllerInterfaceProtocolClient();
}
zx_status_t SimpleDisplay::DisplayControllerImplImportBufferCollection(
uint64_t banjo_driver_buffer_collection_id, zx::channel collection_token) {
const display::DriverBufferCollectionId driver_buffer_collection_id =
display::ToDriverBufferCollectionId(banjo_driver_buffer_collection_id);
if (buffer_collections_.find(driver_buffer_collection_id) != buffer_collections_.end()) {
zxlogf(ERROR, "Buffer Collection (id=%lu) already exists", driver_buffer_collection_id.value());
return ZX_ERR_ALREADY_EXISTS;
}
ZX_DEBUG_ASSERT_MSG(sysmem_.is_valid(), "sysmem allocator is not initialized");
auto [collection_client_endpoint, collection_server_endpoint] =
fidl::Endpoints<fuchsia_sysmem2::BufferCollection>::Create();
fidl::Arena arena;
fuchsia_sysmem2::wire::AllocatorBindSharedCollectionRequest bind_request =
fuchsia_sysmem2::wire::AllocatorBindSharedCollectionRequest::Builder(arena)
.token(
fidl::ClientEnd<fuchsia_sysmem2::BufferCollectionToken>(std::move(collection_token)))
.buffer_collection_request(std::move(collection_server_endpoint))
.Build();
auto bind_result = sysmem_->BindSharedCollection(std::move(bind_request));
if (!bind_result.ok()) {
zxlogf(ERROR, "Cannot complete FIDL call BindSharedCollection: %s",
bind_result.status_string());
return ZX_ERR_INTERNAL;
}
buffer_collections_[driver_buffer_collection_id] =
fidl::WireSyncClient(std::move(collection_client_endpoint));
return ZX_OK;
}
zx_status_t SimpleDisplay::DisplayControllerImplReleaseBufferCollection(
uint64_t banjo_driver_buffer_collection_id) {
const display::DriverBufferCollectionId driver_buffer_collection_id =
display::ToDriverBufferCollectionId(banjo_driver_buffer_collection_id);
if (buffer_collections_.find(driver_buffer_collection_id) == buffer_collections_.end()) {
const display::DriverBufferCollectionId driver_buffer_collection_id =
display::ToDriverBufferCollectionId(banjo_driver_buffer_collection_id);
zxlogf(ERROR, "Cannot release buffer collection %lu: buffer collection doesn't exist",
driver_buffer_collection_id.value());
return ZX_ERR_NOT_FOUND;
}
buffer_collections_.erase(driver_buffer_collection_id);
return ZX_OK;
}
zx_status_t SimpleDisplay::DisplayControllerImplImportImage(
const image_metadata_t* image_metadata, uint64_t banjo_driver_buffer_collection_id,
uint32_t index, uint64_t* out_image_handle) {
const display::DriverBufferCollectionId driver_buffer_collection_id =
display::ToDriverBufferCollectionId(banjo_driver_buffer_collection_id);
const auto it = buffer_collections_.find(driver_buffer_collection_id);
if (it == buffer_collections_.end()) {
zxlogf(ERROR, "ImportImage: Cannot find imported buffer collection (id=%lu)",
driver_buffer_collection_id.value());
return ZX_ERR_NOT_FOUND;
}
const fidl::WireSyncClient<fuchsia_sysmem2::BufferCollection>& collection = it->second;
fidl::WireResult check_result = collection->CheckAllBuffersAllocated();
// TODO(https://fxbug.dev/42072690): The sysmem FIDL error logging patterns are
// inconsistent across drivers. The FIDL error handling and logging should be
// unified.
if (!check_result.ok()) {
zxlogf(ERROR, "failed to check buffers allocated, %s",
check_result.FormatDescription().c_str());
return check_result.status();
}
const auto& check_response = check_result.value();
if (check_response.is_error()) {
if (check_response.error_value() == fuchsia_sysmem2::Error::kPending) {
return ZX_ERR_SHOULD_WAIT;
}
return sysmem::V1CopyFromV2Error(check_response.error_value());
}
fidl::WireResult wait_result = collection->WaitForAllBuffersAllocated();
// TODO(https://fxbug.dev/42072690): The sysmem FIDL error logging patterns are
// inconsistent across drivers. The FIDL error handling and logging should be
// unified.
if (!wait_result.ok()) {
zxlogf(ERROR, "failed to wait for buffers allocated, %s",
wait_result.FormatDescription().c_str());
return wait_result.status();
}
auto& wait_response = wait_result.value();
if (wait_response.is_error()) {
return sysmem::V1CopyFromV2Error(wait_response.error_value());
}
fuchsia_sysmem2::wire::BufferCollectionInfo& collection_info =
wait_response->buffer_collection_info();
if (!collection_info.settings().has_image_format_constraints()) {
zxlogf(ERROR, "no image format constraints");
return ZX_ERR_INVALID_ARGS;
}
if (index > 0) {
zxlogf(ERROR, "invalid index %d, greater than 0", index);
return ZX_ERR_OUT_OF_RANGE;
}
auto sysmem2_collection_format =
collection_info.settings().image_format_constraints().pixel_format();
if (sysmem2_collection_format != format_) {
zxlogf(ERROR, "Image format from sysmem (%u) doesn't match expected format (%u)",
static_cast<uint32_t>(sysmem2_collection_format), static_cast<uint32_t>(format_));
return ZX_ERR_INVALID_ARGS;
}
// We only need the VMO temporarily to get the BufferKey. The BufferCollection client_end in
// buffer_collections_ is not SetWeakOk (and therefore is known to be strong at this point), so
// it's not necessary to keep this VMO for the buffer to remain alive.
zx::vmo vmo = std::move(collection_info.buffers()[0].vmo());
fidl::Arena arena;
auto vmo_info_result =
sysmem_->GetVmoInfo(fuchsia_sysmem2::wire::AllocatorGetVmoInfoRequest::Builder(arena)
.vmo(std::move(vmo))
.Build());
if (!vmo_info_result.ok()) {
return vmo_info_result.error().status();
}
if (!vmo_info_result->is_ok()) {
return sysmem::V1CopyFromV2Error(vmo_info_result->error_value());
}
auto& vmo_info = vmo_info_result->value();
BufferKey buffer_key(vmo_info->buffer_collection_id(), vmo_info->buffer_index());
bool key_matched;
{
fbl::AutoLock lock(&framebuffer_key_mtx_);
key_matched = framebuffer_key_.has_value() && (*framebuffer_key_ == buffer_key);
}
if (!key_matched) {
return ZX_ERR_INVALID_ARGS;
}
if (image_metadata->width != width_ || image_metadata->height != height_) {
return ZX_ERR_INVALID_ARGS;
}
*out_image_handle = kImageHandle;
return ZX_OK;
}
void SimpleDisplay::DisplayControllerImplReleaseImage(uint64_t image_handle) {
// noop
}
config_check_result_t SimpleDisplay::DisplayControllerImplCheckConfiguration(
const display_config_t* display_configs, size_t display_count,
client_composition_opcode_t* out_client_composition_opcodes_list,
size_t client_composition_opcodes_count, size_t* out_client_composition_opcodes_actual) {
if (out_client_composition_opcodes_actual != nullptr) {
*out_client_composition_opcodes_actual = 0;
}
if (display_count != 1) {
ZX_DEBUG_ASSERT(display_count == 0);
return CONFIG_CHECK_RESULT_OK;
}
ZX_DEBUG_ASSERT(display::ToDisplayId(display_configs[0].display_id) == kDisplayId);
ZX_DEBUG_ASSERT(client_composition_opcodes_count >= display_configs[0].layer_count);
cpp20::span<client_composition_opcode_t> client_composition_opcodes(
out_client_composition_opcodes_list, display_configs[0].layer_count);
std::fill(client_composition_opcodes.begin(), client_composition_opcodes.end(), 0);
if (out_client_composition_opcodes_actual != nullptr) {
*out_client_composition_opcodes_actual = client_composition_opcodes.size();
}
bool success;
if (display_configs[0].layer_count != 1) {
success = false;
} else {
const primary_layer_t* layer = &display_configs[0].layer_list[0].cfg.primary;
frame_t frame = {
.x_pos = 0,
.y_pos = 0,
.width = width_,
.height = height_,
};
success = display_configs[0].layer_list[0].type == LAYER_TYPE_PRIMARY &&
layer->transform_mode == FRAME_TRANSFORM_IDENTITY &&
layer->image_metadata.width == width_ && layer->image_metadata.height == height_ &&
memcmp(&layer->dest_frame, &frame, sizeof(frame_t)) == 0 &&
memcmp(&layer->src_frame, &frame, sizeof(frame_t)) == 0 &&
display_configs[0].cc_flags == 0 && layer->alpha_mode == ALPHA_DISABLE;
}
if (!success) {
client_composition_opcodes[0] = CLIENT_COMPOSITION_OPCODE_MERGE_BASE;
for (unsigned i = 1; i < display_configs[0].layer_count; i++) {
client_composition_opcodes[i] = CLIENT_COMPOSITION_OPCODE_MERGE_SRC;
}
}
return CONFIG_CHECK_RESULT_OK;
}
void SimpleDisplay::DisplayControllerImplApplyConfiguration(
const display_config_t* display_config, size_t display_count,
const config_stamp_t* banjo_config_stamp) {
ZX_DEBUG_ASSERT(banjo_config_stamp != nullptr);
has_image_ = display_count != 0 && display_config[0].layer_count != 0;
{
fbl::AutoLock lock(&mtx_);
config_stamp_ = display::ToConfigStamp(*banjo_config_stamp);
}
}
zx_status_t SimpleDisplay::DisplayControllerImplSetBufferCollectionConstraints(
const image_buffer_usage_t* usage, uint64_t banjo_driver_buffer_collection_id) {
const display::DriverBufferCollectionId driver_buffer_collection_id =
display::ToDriverBufferCollectionId(banjo_driver_buffer_collection_id);
const auto it = buffer_collections_.find(driver_buffer_collection_id);
if (it == buffer_collections_.end()) {
zxlogf(ERROR, "SetBufferCollectionConstraints: Cannot find imported buffer collection (id=%lu)",
driver_buffer_collection_id.value());
return ZX_ERR_NOT_FOUND;
}
const fidl::WireSyncClient<fuchsia_sysmem2::BufferCollection>& collection = it->second;
const uint32_t bytes_per_pixel =
ImageFormatStrideBytesPerWidthPixel(PixelFormatAndModifier(format_, kFormatModifier));
uint32_t bytes_per_row = stride_ * bytes_per_pixel;
fidl::Arena arena;
auto constraints = fuchsia_sysmem2::wire::BufferCollectionConstraints::Builder(arena);
auto buffer_usage = fuchsia_sysmem2::wire::BufferUsage::Builder(arena);
buffer_usage.display(fuchsia_sysmem2::wire::kDisplayUsageLayer);
constraints.usage(buffer_usage.Build());
auto buffer_constraints = fuchsia_sysmem2::wire::BufferMemoryConstraints::Builder(arena);
buffer_constraints.min_size_bytes(0);
buffer_constraints.max_size_bytes(height_ * bytes_per_row);
buffer_constraints.physically_contiguous_required(false);
buffer_constraints.secure_required(false);
buffer_constraints.ram_domain_supported(true);
buffer_constraints.cpu_domain_supported(true);
auto heap = fuchsia_sysmem2::wire::Heap::Builder(arena);
heap.heap_type(bind_fuchsia_sysmem_heap::HEAP_TYPE_FRAMEBUFFER);
heap.id(0);
buffer_constraints.permitted_heaps(std::array{heap.Build()});
constraints.buffer_memory_constraints(buffer_constraints.Build());
auto image_constraints = fuchsia_sysmem2::wire::ImageFormatConstraints::Builder(arena);
image_constraints.pixel_format(format_);
image_constraints.pixel_format_modifier(kFormatModifier);
image_constraints.color_spaces(std::array{fuchsia_images2::ColorSpace::kSrgb});
image_constraints.min_size({width_, height_});
image_constraints.max_size({width_, height_});
image_constraints.min_bytes_per_row(bytes_per_row);
image_constraints.max_bytes_per_row(bytes_per_row);
constraints.image_format_constraints(std::array{image_constraints.Build()});
auto set_request = fuchsia_sysmem2::wire::BufferCollectionSetConstraintsRequest::Builder(arena);
set_request.constraints(constraints.Build());
auto result = collection->SetConstraints(set_request.Build());
if (!result.ok()) {
zxlogf(ERROR, "failed to set constraints, %s", result.FormatDescription().c_str());
return result.status();
}
return ZX_OK;
}
// implement device protocol:
void SimpleDisplay::DdkRelease() { delete this; }
// implement sysmem heap protocol:
void SimpleDisplay::AllocateVmo(AllocateVmoRequestView request,
AllocateVmoCompleter::Sync& completer) {
BufferKey buffer_key(request->buffer_collection_id, request->buffer_index);
zx_info_handle_count handle_count;
zx_status_t status = framebuffer_mmio_.get_vmo()->get_info(
ZX_INFO_HANDLE_COUNT, &handle_count, sizeof(handle_count), nullptr, nullptr);
if (status != ZX_OK) {
completer.ReplyError(status);
return;
}
if (handle_count.handle_count != 1) {
completer.ReplyError(ZX_ERR_NO_RESOURCES);
return;
}
zx::vmo vmo;
status = framebuffer_mmio_.get_vmo()->duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo);
if (status != ZX_OK) {
completer.ReplyError(status);
}
bool had_framebuffer_key;
{
fbl::AutoLock lock(&framebuffer_key_mtx_);
had_framebuffer_key = framebuffer_key_.has_value();
if (!had_framebuffer_key) {
framebuffer_key_ = buffer_key;
}
}
if (had_framebuffer_key) {
completer.ReplyError(ZX_ERR_NO_RESOURCES);
return;
}
completer.ReplySuccess(std::move(vmo));
}
void SimpleDisplay::DeleteVmo(DeleteVmoRequestView request, DeleteVmoCompleter::Sync& completer) {
{
fbl::AutoLock lock(&framebuffer_key_mtx_);
framebuffer_key_.reset();
}
// Semantics of DeleteVmo are to recycle all resources tied to the sysmem allocation before
// replying, so we close the VMO handle here before replying. Even if it shares an object and
// pages with a VMO handle we're not closing, this helps clarify wrt semantics of DeleteVmo.
request->vmo.reset();
completer.Reply();
}
// implement driver object:
zx_status_t SimpleDisplay::Bind(const char* name, std::unique_ptr<SimpleDisplay>* vbe_ptr) {
zx_status_t status;
zx::channel heap_request, heap_connection;
if ((status = zx::channel::create(0, &heap_request, &heap_connection)) != ZX_OK) {
return ZX_ERR_NOT_SUPPORTED;
}
auto result = hardware_sysmem_->RegisterHeap(
static_cast<uint64_t>(fuchsia_sysmem::wire::HeapType::kFramebuffer),
fidl::ClientEnd<fuchsia_hardware_sysmem::Heap>(std::move(heap_connection)));
if (!result.ok()) {
printf("%s: failed to register sysmem heap: %s\n", name, result.status_string());
return result.status();
}
status = DdkAdd(name);
if (status != ZX_OK) {
return status;
}
// Start heap server.
auto arena = std::make_unique<fidl::Arena<512>>();
fuchsia_hardware_sysmem::wire::HeapProperties heap_properties = GetHeapProperties(*arena.get());
async::PostTask(
loop_.dispatcher(),
[server_end = fidl::ServerEnd<fuchsia_hardware_sysmem::Heap>(std::move(heap_request)),
arena = std::move(arena), heap_properties = std::move(heap_properties), this]() mutable {
auto binding =
fidl::BindServer(loop_.dispatcher(), std::move(server_end), this,
[](SimpleDisplay* self, fidl::UnbindInfo info,
fidl::ServerEnd<fuchsia_hardware_sysmem::Heap> server_end) {
OnHeapServerClose(info, server_end.TakeChannel());
});
auto result = fidl::WireSendEvent(binding)->OnRegister(std::move(heap_properties));
if (!result.ok()) {
zxlogf(ERROR, "OnRegister() failed: %s", result.FormatDescription().c_str());
}
});
// Start vsync loop.
async::PostTask(loop_.dispatcher(), [this]() { OnPeriodicVSync(); });
// DevMgr now owns this pointer, release it to avoid destroying the object
// when device goes out of scope.
[[maybe_unused]] auto ptr = vbe_ptr->release();
zxlogf(INFO, "%s: initialized display, %u x %u (stride=%u format=%u)", name, width_, height_,
stride_, static_cast<uint32_t>(format_));
return ZX_OK;
}
SimpleDisplay::SimpleDisplay(zx_device_t* parent,
fidl::WireSyncClient<fuchsia_hardware_sysmem::Sysmem> hardware_sysmem,
fidl::WireSyncClient<fuchsia_sysmem2::Allocator> sysmem,
fdf::MmioBuffer framebuffer_mmio, uint32_t width, uint32_t height,
uint32_t stride, fuchsia_images2::wire::PixelFormat format)
: DeviceType(parent),
hardware_sysmem_(std::move(hardware_sysmem)),
sysmem_(std::move(sysmem)),
loop_(&kAsyncLoopConfigNoAttachToCurrentThread),
has_image_(false),
framebuffer_mmio_(std::move(framebuffer_mmio)),
width_(width),
height_(height),
stride_(stride),
format_(format),
next_vsync_time_(zx::clock::get_monotonic()) {
// Start thread. Heap server must be running on a separate
// thread as sysmem might be making synchronous allocation requests
// from the main thread.
loop_.StartThread("simple-display");
if (sysmem_) {
zx_koid_t current_process_koid = GetCurrentProcessKoid();
std::string debug_name = "simple-display[" + std::to_string(current_process_koid) + "]";
fidl::Arena arena;
auto set_debug_request =
fuchsia_sysmem2::wire::AllocatorSetDebugClientInfoRequest::Builder(arena);
set_debug_request.name(debug_name);
set_debug_request.id(current_process_koid);
auto set_debug_status = sysmem_->SetDebugClientInfo(set_debug_request.Build());
if (!set_debug_status.ok()) {
zxlogf(ERROR, "Cannot set sysmem allocator debug info: %s", set_debug_status.status_string());
}
}
}
void SimpleDisplay::OnPeriodicVSync() {
if (intf_.is_valid()) {
fbl::AutoLock lock(&mtx_);
const uint64_t banjo_display_id = display::ToBanjoDisplayId(kDisplayId);
const config_stamp_t banjo_config_stamp = display::ToBanjoConfigStamp(config_stamp_);
intf_.OnDisplayVsync(banjo_display_id, next_vsync_time_.get(), &banjo_config_stamp);
}
next_vsync_time_ += kVSyncInterval;
async::PostTaskForTime(loop_.dispatcher(), [this]() { OnPeriodicVSync(); }, next_vsync_time_);
}
zx_status_t bind_simple_pci_display_bootloader(zx_device_t* dev, const char* name, uint32_t bar,
bool use_fidl) {
zbi_pixel_format_t format;
uint32_t width, height, stride;
zx_status_t status =
zx_framebuffer_get_info(get_framebuffer_resource(dev), &format, &width, &height, &stride);
if (status != ZX_OK) {
printf("%s: failed to get bootloader dimensions: %d\n", name, status);
return ZX_ERR_NOT_SUPPORTED;
}
auto sysmem2_format_type_result = ImageFormatConvertZbiToSysmemPixelFormat_v2(format);
if (!sysmem2_format_type_result.is_ok()) {
zxlogf(ERROR, "%s: failed to convert framebuffer format: %u", name, format);
return ZX_ERR_NOT_SUPPORTED;
}
fuchsia_images2::wire::PixelFormat sysmem2_format = sysmem2_format_type_result.take_value();
if (use_fidl) {
return bind_simple_fidl_pci_display(dev, name, bar, width, height, stride, sysmem2_format);
}
return bind_simple_pci_display(dev, name, bar, width, height, stride, sysmem2_format);
}
zx_status_t bind_simple_pci_display(zx_device_t* dev, const char* name, uint32_t bar,
uint32_t width, uint32_t height, uint32_t stride,
fuchsia_images2::wire::PixelFormat format) {
ddk::Pci pci(dev, "pci");
if (!pci.is_valid()) {
zxlogf(ERROR, "%s: could not get PCI protocol", name);
return ZX_ERR_INTERNAL;
}
// Since this function is used by multiple drivers with different bind rules,
// the fragment name here must be the same as both the simple-display
// composite fragment defined in this directory and the PCI sysmem
// fragment defined elsewhere.
zx::result hardware_sysmem_result =
ddk::Device<void>::DdkConnectFragmentFidlProtocol<fuchsia_hardware_sysmem::Service::Sysmem>(
dev, "sysmem");
if (hardware_sysmem_result.is_error()) {
zxlogf(ERROR, "%s: could not get SYSMEM protocol: %s", name,
hardware_sysmem_result.status_string());
return hardware_sysmem_result.status_value();
}
fidl::WireSyncClient hardware_sysmem{std::move(*hardware_sysmem_result)};
zx::result sysmem_result = ddk::Device<void>::DdkConnectFragmentFidlProtocol<
fuchsia_hardware_sysmem::Service::AllocatorV2>(dev, "sysmem");
if (sysmem_result.is_error()) {
zxlogf(ERROR, "%s: could not get fuchsia.sysmem2.Allocator protocol: %s", name,
sysmem_result.status_string());
return sysmem_result.status_value();
}
fidl::WireSyncClient sysmem(std::move(*sysmem_result));
std::optional<fdf::MmioBuffer> framebuffer_mmio;
// map framebuffer window
zx_status_t status = pci.MapMmio(bar, ZX_CACHE_POLICY_WRITE_COMBINING, &framebuffer_mmio);
if (status != ZX_OK) {
printf("%s: failed to map pci bar %d: %d\n", name, bar, status);
return status;
}
fbl::AllocChecker ac;
std::unique_ptr<SimpleDisplay> display(
new (&ac) SimpleDisplay(dev, std::move(hardware_sysmem), std::move(sysmem),
std::move(*framebuffer_mmio), width, height, stride, format));
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
return display->Bind(name, &display);
}
zx_status_t bind_simple_fidl_pci_display(zx_device_t* dev, const char* name, uint32_t bar,
uint32_t width, uint32_t height, uint32_t stride,
fuchsia_images2::wire::PixelFormat format) {
zx::result client =
ddk::Device<void>::DdkConnectFragmentFidlProtocol<fuchsia_hardware_pci::Service::Device>(
dev, "pci");
if (client.is_error()) {
zxlogf(ERROR, "%s: could not get PCI protocol: %s", name, client.status_string());
return ZX_ERR_NOT_SUPPORTED;
}
fidl::WireSyncClient<fuchsia_hardware_pci::Device> pci(std::move(*client));
// For important information about the fragment name, see the note in bind_simple_pci_display
// above.
zx::result hardware_sysmem_result =
ddk::Device<void>::DdkConnectFragmentFidlProtocol<fuchsia_hardware_sysmem::Service::Sysmem>(
dev, "sysmem");
if (hardware_sysmem_result.is_error()) {
zxlogf(ERROR, "%s: could not get SYSMEM protocol: %s", name,
hardware_sysmem_result.status_string());
return hardware_sysmem_result.status_value();
}
fidl::WireSyncClient hardware_sysmem{std::move(*hardware_sysmem_result)};
zx::result sysmem_result = ddk::Device<void>::DdkConnectFragmentFidlProtocol<
fuchsia_hardware_sysmem::Service::AllocatorV2>(dev, "sysmem");
if (sysmem_result.is_error()) {
zxlogf(ERROR, "%s: could not get fuchsia.sysmem2.Allocator protocol: %s", name,
sysmem_result.status_string());
return sysmem_result.status_value();
}
fidl::WireSyncClient sysmem(std::move(*sysmem_result));
fidl::WireResult<fuchsia_hardware_pci::Device::GetBar> bar_result = pci->GetBar(bar);
if (!bar_result.ok()) {
zxlogf(ERROR, "Failed to send map PCI bar %d: %s", bar, bar_result.FormatDescription().data());
return bar_result.status();
}
if (bar_result.value().is_error()) {
zxlogf(ERROR, "Failed to map PCI bar %d: %s", bar,
zx_status_get_string(bar_result.value().error_value()));
return bar_result.value().error_value();
}
if (!bar_result.value().value()->result.result.is_vmo()) {
zxlogf(ERROR, "PCI bar %u is not an MMIO BAR!", bar);
return ZX_ERR_WRONG_TYPE;
}
// map framebuffer window
auto mmio = fdf::MmioBuffer::Create(0, bar_result.value().value()->result.size,
std::move(bar_result.value().value()->result.result.vmo()),
ZX_CACHE_POLICY_WRITE_COMBINING);
if (mmio.is_error()) {
printf("%s: failed to map pci bar %d: %s\n", name, bar, mmio.status_string());
return mmio.status_value();
}
auto display = std::make_unique<SimpleDisplay>(dev, std::move(hardware_sysmem), std::move(sysmem),
std::move(*mmio), width, height, stride, format);
return display->Bind(name, &display);
}