| // 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 "display.h" |
| |
| #include <ddk/binding.h> |
| #include <ddk/debug.h> |
| #include <ddk/trace/event.h> |
| #include <fbl/auto_lock.h> |
| #include <fbl/unique_ptr.h> |
| #include <fuchsia/sysmem/c/fidl.h> |
| #include <zircon/pixelformat.h> |
| #include <zircon/threads.h> |
| |
| #include <memory> |
| |
| namespace goldfish { |
| namespace { |
| |
| const char* kTag = "goldfish-display"; |
| |
| const char* kPipeName = "pipe:opengles"; |
| |
| constexpr uint32_t kRefreshRateHz = 60; |
| |
| constexpr uint64_t kDisplayId = 1; |
| |
| constexpr uint32_t kClientFlags = 0; |
| |
| constexpr zx_pixel_format_t kPixelFormats[] = { |
| ZX_PIXEL_FORMAT_RGB_x888, |
| ZX_PIXEL_FORMAT_ARGB_8888, |
| }; |
| |
| constexpr uint32_t FB_WIDTH = 1; |
| constexpr uint32_t FB_HEIGHT = 2; |
| |
| constexpr uint32_t GL_RGBA = 0x1908; |
| constexpr uint32_t GL_UNSIGNED_BYTE = 0x1401; |
| |
| constexpr uint32_t IMAGE_TYPE_OPTIMAL = 1; |
| |
| struct GetFbParamCmd { |
| uint32_t op; |
| uint32_t size; |
| uint32_t param; |
| }; |
| constexpr uint32_t kOP_rcGetFbParam = 10007; |
| constexpr uint32_t kSize_rcGetFbParam = 12; |
| |
| struct CreateColorBufferCmd { |
| uint32_t op; |
| uint32_t size; |
| uint32_t width; |
| uint32_t height; |
| uint32_t internalformat; |
| }; |
| constexpr uint32_t kOP_rcCreateColorBuffer = 10012; |
| constexpr uint32_t kSize_rcCreateColorBuffer = 20; |
| |
| struct OpenColorBufferCmd { |
| uint32_t op; |
| uint32_t size; |
| uint32_t id; |
| }; |
| constexpr uint32_t kOP_rcOpenColorBuffer = 10013; |
| constexpr uint32_t kSize_rcOpenColorBuffer = 12; |
| |
| struct CloseColorBufferCmd { |
| uint32_t op; |
| uint32_t size; |
| uint32_t id; |
| }; |
| constexpr uint32_t kOP_rcCloseColorBuffer = 10014; |
| constexpr uint32_t kSize_rcCloseColorBuffer = 12; |
| |
| struct UpdateColorBufferCmd { |
| uint32_t op; |
| uint32_t size; |
| uint32_t id; |
| uint32_t x; |
| uint32_t y; |
| uint32_t width; |
| uint32_t height; |
| uint32_t format; |
| uint32_t type; |
| }; |
| constexpr uint32_t kOP_rcUpdateColorBuffer = 10024; |
| constexpr uint32_t kSize_rcUpdateColorBuffer = 36; |
| |
| struct FbPostCmd { |
| uint32_t op; |
| uint32_t size; |
| uint32_t id; |
| }; |
| constexpr uint32_t kOP_rcFbPost = 10018; |
| constexpr uint32_t kSize_rcFbPost = 12; |
| |
| } // namespace |
| |
| // static |
| zx_status_t Display::Create(void* ctx, zx_device_t* device) { |
| auto display = std::make_unique<Display>(device); |
| |
| zx_status_t status = display->Bind(); |
| if (status == ZX_OK) { |
| // devmgr now owns device. |
| __UNUSED auto* dev = display.release(); |
| } |
| return status; |
| } |
| |
| Display::Display(zx_device_t* parent) |
| : DisplayType(parent), control_(parent), pipe_(parent) {} |
| |
| Display::~Display() { |
| if (id_) { |
| fbl::AutoLock lock(&lock_); |
| if (cmd_buffer_.is_valid()) { |
| auto buffer = static_cast<pipe_cmd_buffer_t*>(cmd_buffer_.virt()); |
| buffer->id = id_; |
| buffer->cmd = PIPE_CMD_CODE_CLOSE; |
| buffer->status = PIPE_ERROR_INVAL; |
| |
| pipe_.Exec(id_); |
| ZX_DEBUG_ASSERT(!buffer->status); |
| } |
| pipe_.Destroy(id_); |
| } |
| |
| { |
| fbl::AutoLock lock(&flush_lock_); |
| shutdown_ = true; |
| } |
| |
| thrd_join(flush_thread_, NULL); |
| } |
| |
| zx_status_t Display::Bind() { |
| fbl::AutoLock lock(&lock_); |
| |
| if (!control_.is_valid()) { |
| zxlogf(ERROR, "%s: no control protocol\n", kTag); |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| if (!pipe_.is_valid()) { |
| zxlogf(ERROR, "%s: no pipe protocol\n", kTag); |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| zx_status_t status = pipe_.GetBti(&bti_); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s: GetBti failed: %d\n", kTag, status); |
| return status; |
| } |
| |
| status = |
| io_buffer_.Init(bti_.get(), PAGE_SIZE, IO_BUFFER_RW | IO_BUFFER_CONTIG); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s: io_buffer_init failed: %d\n", kTag, status); |
| return status; |
| } |
| |
| zx::vmo vmo; |
| goldfish_pipe_signal_value_t signal_cb = {Display::OnSignal, this}; |
| status = pipe_.Create(&signal_cb, &id_, &vmo); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s: Create failed: %d\n", kTag, status); |
| return status; |
| } |
| |
| status = cmd_buffer_.InitVmo(bti_.get(), vmo.get(), 0, IO_BUFFER_RW); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s: io_buffer_init_vmo failed: %d\n", kTag, status); |
| return status; |
| } |
| |
| auto buffer = static_cast<pipe_cmd_buffer_t*>(cmd_buffer_.virt()); |
| buffer->id = id_; |
| buffer->cmd = PIPE_CMD_CODE_OPEN; |
| buffer->status = PIPE_ERROR_INVAL; |
| |
| pipe_.Open(id_); |
| if (buffer->status) { |
| zxlogf(ERROR, "%s: Open failed: %d\n", kTag, buffer->status); |
| cmd_buffer_.release(); |
| return ZX_ERR_INTERNAL; |
| } |
| |
| size_t length = strlen(kPipeName) + 1; |
| memcpy(io_buffer_.virt(), kPipeName, length); |
| WriteLocked(static_cast<uint32_t>(length)); |
| |
| memcpy(io_buffer_.virt(), &kClientFlags, sizeof(kClientFlags)); |
| WriteLocked(sizeof(kClientFlags)); |
| |
| int rc = thrd_create_with_name( |
| &flush_thread_, |
| [](void* arg) { return static_cast<Display*>(arg)->FlushHandler(); }, |
| this, "goldfish_display_flush_thread"); |
| if (rc != thrd_success) { |
| return thrd_status_to_zx_status(rc); |
| } |
| |
| return DdkAdd("goldfish-display"); |
| } |
| |
| void Display::DdkUnbind() { |
| DdkRemove(); |
| } |
| |
| void Display::DdkRelease() { |
| delete this; |
| } |
| |
| void Display::DisplayControllerImplSetDisplayControllerInterface( |
| const display_controller_interface_protocol_t* interface) { |
| fbl::AutoLock lock(&flush_lock_); |
| dc_intf_ = ddk::DisplayControllerInterfaceProtocolClient(interface); |
| { |
| fbl::AutoLock lock(&lock_); |
| width_ = GetFbParamLocked(FB_WIDTH, 1); |
| height_ = GetFbParamLocked(FB_HEIGHT, 1); |
| } |
| |
| added_display_args_t args = {}; |
| args.display_id = kDisplayId; |
| args.edid_present = false; |
| args.panel.params.height = height_; |
| args.panel.params.width = width_; |
| args.panel.params.refresh_rate_e2 = kRefreshRateHz * 100; |
| args.pixel_format_list = kPixelFormats; |
| args.pixel_format_count = sizeof(kPixelFormats) / sizeof(kPixelFormats[0]); |
| |
| dc_intf_.OnDisplaysChanged(&args, 1, nullptr, 0, nullptr, 0, nullptr); |
| } |
| |
| zx_status_t Display::DisplayControllerImplImportVmoImage(image_t* image, |
| zx::vmo vmo, |
| size_t offset) { |
| if (image->type != IMAGE_TYPE_SIMPLE) { |
| zxlogf(ERROR, "%s: invalid image type\n", kTag); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| auto color_buffer = std::make_unique<ColorBuffer>(); |
| |
| // Linear images must be pinned. |
| unsigned pixel_size = ZX_PIXEL_FORMAT_BYTES(image->pixel_format); |
| color_buffer->size = |
| ROUNDUP(image->width * image->height * pixel_size, PAGE_SIZE); |
| zx_status_t status = bti_.pin(ZX_BTI_PERM_READ | ZX_BTI_CONTIGUOUS, vmo, |
| offset, color_buffer->size, |
| &color_buffer->paddr, 1, &color_buffer->pmt); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s: failed to pin VMO: %d\n", kTag, status); |
| return status; |
| } |
| |
| color_buffer->vmo = std::move(vmo); |
| color_buffer->width = image->width; |
| color_buffer->height = image->height; |
| |
| { |
| fbl::AutoLock lock(&lock_); |
| status = CreateColorBufferLocked(image->width, image->height, |
| &color_buffer->id); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s: failed to create color buffer\n", kTag); |
| return status; |
| } |
| } |
| |
| image->handle = reinterpret_cast<uint64_t>(color_buffer.release()); |
| return ZX_OK; |
| } |
| |
| zx_status_t Display::DisplayControllerImplImportImage( |
| image_t* image, zx_unowned_handle_t handle, uint32_t index) { |
| if (image->type != IMAGE_TYPE_OPTIMAL) { |
| zxlogf(ERROR, "%s: invalid image type\n", kTag); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| auto color_buffer = std::make_unique<ColorBuffer>(); |
| |
| zx_status_t status, status2; |
| fuchsia_sysmem_BufferCollectionInfo_2 collection_info; |
| status = fuchsia_sysmem_BufferCollectionWaitForBuffersAllocated( |
| handle, &status2, &collection_info); |
| if (status != ZX_OK) { |
| return status; |
| } |
| if (status2 != ZX_OK) { |
| return status2; |
| } |
| |
| if (index < collection_info.buffer_count) { |
| color_buffer->vmo = zx::vmo(collection_info.buffers[index].vmo); |
| collection_info.buffers[index].vmo = ZX_HANDLE_INVALID; |
| } |
| for (uint32_t i = 0; i < collection_info.buffer_count; ++i) { |
| zx_handle_close(collection_info.buffers[i].vmo); |
| } |
| |
| if (!collection_info.settings.has_image_format_constraints || |
| !color_buffer->vmo.is_valid()) { |
| zxlogf(ERROR, "%s: invalid image format or index\n", kTag); |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| uint64_t offset = collection_info.buffers[index].vmo_usable_start; |
| if (offset) { |
| zxlogf(ERROR, "%s: invalid offset\n", kTag); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| image->handle = reinterpret_cast<uint64_t>(color_buffer.release()); |
| return ZX_OK; |
| } |
| |
| void Display::DisplayControllerImplReleaseImage(image_t* image) { |
| auto color_buffer = reinterpret_cast<ColorBuffer*>(image->handle); |
| |
| // Color buffer is owned by image in the linear case. |
| if (image->type == IMAGE_TYPE_SIMPLE) { |
| fbl::AutoLock lock(&lock_); |
| CloseColorBufferLocked(color_buffer->id); |
| } |
| |
| delete color_buffer; |
| } |
| |
| uint32_t Display::DisplayControllerImplCheckConfiguration( |
| const display_config_t** display_configs, size_t display_count, |
| uint32_t** layer_cfg_results, size_t* layer_cfg_result_count) { |
| if (display_count != 1) { |
| ZX_DEBUG_ASSERT(display_count == 0); |
| return CONFIG_DISPLAY_OK; |
| } |
| ZX_DEBUG_ASSERT(display_configs[0]->display_id == kDisplayId); |
| bool success; |
| if (display_configs[0]->layer_count != 1) { |
| success = false; |
| } else { |
| fbl::AutoLock lock(&flush_lock_); |
| |
| 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.width == width_ && layer->image.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) { |
| layer_cfg_results[0][0] = CLIENT_MERGE_BASE; |
| for (unsigned i = 1; i < display_configs[0]->layer_count; i++) { |
| layer_cfg_results[0][i] = CLIENT_MERGE_SRC; |
| } |
| layer_cfg_result_count[0] = display_configs[0]->layer_count; |
| } |
| return CONFIG_DISPLAY_OK; |
| } |
| |
| void Display::DisplayControllerImplApplyConfiguration( |
| const display_config_t** display_configs, size_t display_count) { |
| uint64_t handle = 0; |
| if (display_count && display_configs[0]->layer_count) { |
| handle = display_configs[0]->layer_list[0]->cfg.primary.image.handle; |
| } |
| |
| auto color_buffer = reinterpret_cast<ColorBuffer*>(handle); |
| if (color_buffer && !color_buffer->id) { |
| zx::vmo vmo; |
| |
| zx_status_t status = |
| color_buffer->vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s: failed to duplicate vmo: %d\n", kTag, status); |
| } else { |
| fbl::AutoLock lock(&lock_); |
| |
| status = control_.GetColorBuffer(std::move(vmo), &color_buffer->id); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s: failed to get color buffer: %d\n", kTag, |
| status); |
| } |
| } |
| } |
| |
| { |
| fbl::AutoLock lock(&flush_lock_); |
| current_fb_ = color_buffer; |
| } |
| } |
| |
| uint32_t |
| Display::DisplayControllerImplComputeLinearStride(uint32_t width, |
| zx_pixel_format_t format) { |
| return width; |
| } |
| |
| zx_status_t Display::DisplayControllerImplAllocateVmo(uint64_t size, |
| zx::vmo* vmo_out) { |
| return zx_vmo_create_contiguous(bti_.get(), size, 0, |
| vmo_out->reset_and_get_address()); |
| } |
| |
| zx_status_t |
| Display::DisplayControllerImplGetSysmemConnection(zx::channel connection) { |
| fbl::AutoLock lock(&lock_); |
| |
| zx_status_t status = pipe_.ConnectSysmem(std::move(connection)); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s: failed to connect to sysmem: %d\n", kTag, status); |
| return status; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t Display::DisplayControllerImplSetBufferCollectionConstraints( |
| const image_t* config, uint32_t collection) { |
| fuchsia_sysmem_BufferCollectionConstraints constraints = {}; |
| constraints.usage.display = fuchsia_sysmem_displayUsageLayer; |
| constraints.has_buffer_memory_constraints = true; |
| fuchsia_sysmem_BufferMemoryConstraints& buffer_constraints = |
| constraints.buffer_memory_constraints; |
| buffer_constraints.min_size_bytes = 0; |
| buffer_constraints.max_size_bytes = 0xffffffff; |
| buffer_constraints.physically_contiguous_required = true; |
| buffer_constraints.secure_required = false; |
| buffer_constraints.ram_domain_supported = true; |
| buffer_constraints.cpu_domain_supported = true; |
| buffer_constraints.inaccessible_domain_supported = true; |
| buffer_constraints.heap_permitted_count = 2; |
| buffer_constraints.heap_permitted[0] = fuchsia_sysmem_HeapType_SYSTEM_RAM; |
| buffer_constraints.heap_permitted[1] = |
| fuchsia_sysmem_HeapType_GOLDFISH_DEVICE_LOCAL; |
| constraints.image_format_constraints_count = 1; |
| fuchsia_sysmem_ImageFormatConstraints& image_constraints = |
| constraints.image_format_constraints[0]; |
| image_constraints.pixel_format.type = fuchsia_sysmem_PixelFormatType_BGRA32; |
| image_constraints.color_spaces_count = 1; |
| image_constraints.color_space[0].type = fuchsia_sysmem_ColorSpaceType_SRGB; |
| image_constraints.min_coded_width = 0; |
| image_constraints.max_coded_width = 0xffffffff; |
| image_constraints.min_coded_height = 0; |
| image_constraints.max_coded_height = 0xffffffff; |
| image_constraints.min_bytes_per_row = 0; |
| image_constraints.max_bytes_per_row = 0xffffffff; |
| image_constraints.max_coded_width_times_coded_height = 0xffffffff; |
| image_constraints.layers = 1; |
| image_constraints.coded_width_divisor = 1; |
| image_constraints.coded_height_divisor = 1; |
| image_constraints.bytes_per_row_divisor = 1; |
| image_constraints.start_offset_divisor = 1; |
| image_constraints.display_width_divisor = 1; |
| image_constraints.display_height_divisor = 1; |
| |
| zx_status_t status = fuchsia_sysmem_BufferCollectionSetConstraints( |
| collection, true, &constraints); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s: failed to set constraints\n", kTag); |
| return status; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t |
| Display::DisplayControllerImplGetSingleBufferFramebuffer(zx::vmo* out_vmo, |
| uint32_t* out_stride) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| void Display::OnSignal(void* ctx, int32_t flags) { |
| TRACE_DURATION("gfx", "Display::OnSignal", "flags", flags); |
| |
| if (flags & (PIPE_WAKE_FLAG_READ | PIPE_WAKE_FLAG_CLOSED)) { |
| static_cast<Display*>(ctx)->OnReadable(); |
| } |
| } |
| |
| void Display::OnReadable() { |
| TRACE_DURATION("gfx", "Display::OnReadable"); |
| |
| fbl::AutoLock lock(&lock_); |
| readable_cvar_.Signal(); |
| } |
| |
| void Display::WriteLocked(uint32_t cmd_size) { |
| TRACE_DURATION("gfx", "Display::Write", "cmd_size", cmd_size); |
| |
| auto buffer = static_cast<pipe_cmd_buffer_t*>(cmd_buffer_.virt()); |
| buffer->id = id_; |
| buffer->cmd = PIPE_CMD_CODE_WRITE; |
| buffer->status = PIPE_ERROR_INVAL; |
| buffer->rw_params.ptrs[0] = io_buffer_.phys(); |
| buffer->rw_params.sizes[0] = cmd_size; |
| buffer->rw_params.buffers_count = 1; |
| buffer->rw_params.consumed_size = 0; |
| pipe_.Exec(id_); |
| ZX_DEBUG_ASSERT(buffer->rw_params.consumed_size == |
| static_cast<int32_t>(cmd_size)); |
| } |
| |
| zx_status_t Display::ReadResultLocked(uint32_t* result) { |
| TRACE_DURATION("gfx", "Display::ReadResult"); |
| |
| while (1) { |
| auto buffer = static_cast<pipe_cmd_buffer_t*>(cmd_buffer_.virt()); |
| buffer->id = id_; |
| buffer->cmd = PIPE_CMD_CODE_READ; |
| buffer->status = PIPE_ERROR_INVAL; |
| buffer->rw_params.ptrs[0] = io_buffer_.phys(); |
| buffer->rw_params.sizes[0] = sizeof(*result); |
| buffer->rw_params.buffers_count = 1; |
| buffer->rw_params.consumed_size = 0; |
| pipe_.Exec(id_); |
| |
| // Positive consumed size always indicate a successful transfer. |
| if (buffer->rw_params.consumed_size) { |
| ZX_DEBUG_ASSERT(buffer->rw_params.consumed_size == sizeof(*result)); |
| *result = *static_cast<uint32_t*>(io_buffer_.virt()); |
| return ZX_OK; |
| } |
| |
| // Early out if error is not because of back-pressure. |
| if (buffer->status != PIPE_ERROR_AGAIN) { |
| zxlogf(ERROR, "%s: reading result failed: %d\n", kTag, |
| buffer->status); |
| return ZX_ERR_INTERNAL; |
| } |
| |
| buffer->id = id_; |
| buffer->cmd = PIPE_CMD_CODE_WAKE_ON_READ; |
| buffer->status = PIPE_ERROR_INVAL; |
| pipe_.Exec(id_); |
| ZX_DEBUG_ASSERT(!buffer->status); |
| |
| // Wait for pipe to become readable. |
| readable_cvar_.Wait(&lock_); |
| } |
| } |
| |
| zx_status_t Display::ExecuteCommandLocked(uint32_t cmd_size, uint32_t* result) { |
| TRACE_DURATION("gfx", "Display::ExecuteCommand", "cnd_size", cmd_size); |
| |
| WriteLocked(cmd_size); |
| return ReadResultLocked(result); |
| } |
| |
| int32_t Display::GetFbParamLocked(uint32_t param, int32_t default_value) { |
| TRACE_DURATION("gfx", "Display::GetFbParam", "param", param); |
| |
| auto cmd = static_cast<GetFbParamCmd*>(io_buffer_.virt()); |
| cmd->op = kOP_rcGetFbParam; |
| cmd->size = kSize_rcGetFbParam; |
| cmd->param = param; |
| |
| uint32_t result; |
| zx_status_t status = ExecuteCommandLocked(kSize_rcGetFbParam, &result); |
| return status == ZX_OK ? result : default_value; |
| } |
| |
| zx_status_t Display::CreateColorBufferLocked(uint32_t width, uint32_t height, |
| uint32_t* id) { |
| TRACE_DURATION("gfx", "Display::CreateColorBuffer", "width", width, |
| "height", height); |
| |
| auto cmd = static_cast<CreateColorBufferCmd*>(io_buffer_.virt()); |
| cmd->op = kOP_rcCreateColorBuffer; |
| cmd->size = kSize_rcCreateColorBuffer; |
| cmd->width = width; |
| cmd->height = height; |
| cmd->internalformat = GL_RGBA; |
| |
| return ExecuteCommandLocked(kSize_rcCreateColorBuffer, id); |
| } |
| |
| void Display::OpenColorBufferLocked(uint32_t id) { |
| TRACE_DURATION("gfx", "Display::OpenColorBuffer", "id", id); |
| |
| auto cmd = static_cast<OpenColorBufferCmd*>(io_buffer_.virt()); |
| cmd->op = kOP_rcOpenColorBuffer; |
| cmd->size = kSize_rcOpenColorBuffer; |
| cmd->id = id; |
| |
| WriteLocked(kSize_rcOpenColorBuffer); |
| } |
| |
| void Display::CloseColorBufferLocked(uint32_t id) { |
| TRACE_DURATION("gfx", "Display::CloseColorBuffer", "id", id); |
| |
| auto cmd = static_cast<CloseColorBufferCmd*>(io_buffer_.virt()); |
| cmd->op = kOP_rcCloseColorBuffer; |
| cmd->size = kSize_rcCloseColorBuffer; |
| cmd->id = id; |
| |
| WriteLocked(kSize_rcCloseColorBuffer); |
| } |
| |
| zx_status_t Display::UpdateColorBufferLocked(uint32_t id, zx_paddr_t paddr, |
| uint32_t width, uint32_t height, |
| size_t size, uint32_t* result) { |
| TRACE_DURATION("gfx", "Display::UpdateColorBuffer", "size", size); |
| |
| auto cmd = static_cast<UpdateColorBufferCmd*>(io_buffer_.virt()); |
| cmd->op = kOP_rcUpdateColorBuffer; |
| cmd->size = kSize_rcUpdateColorBuffer + static_cast<uint32_t>(size); |
| cmd->id = id; |
| cmd->x = 0; |
| cmd->y = 0; |
| cmd->width = width; |
| cmd->height = height; |
| cmd->format = GL_RGBA; |
| cmd->type = GL_UNSIGNED_BYTE; |
| |
| auto buffer = static_cast<pipe_cmd_buffer_t*>(cmd_buffer_.virt()); |
| buffer->id = id_; |
| buffer->cmd = PIPE_CMD_CODE_WRITE; |
| buffer->status = PIPE_ERROR_INVAL; |
| buffer->rw_params.ptrs[0] = io_buffer_.phys(); |
| buffer->rw_params.ptrs[1] = paddr; |
| buffer->rw_params.sizes[0] = kSize_rcUpdateColorBuffer; |
| buffer->rw_params.sizes[1] = static_cast<uint32_t>(size); |
| buffer->rw_params.buffers_count = 2; |
| buffer->rw_params.consumed_size = 0; |
| |
| pipe_.Exec(id_); |
| ZX_DEBUG_ASSERT(buffer->rw_params.consumed_size == |
| static_cast<int32_t>(kSize_rcUpdateColorBuffer + size)); |
| |
| return ReadResultLocked(result); |
| } |
| |
| void Display::FbPostLocked(uint32_t id) { |
| TRACE_DURATION("gfx", "Display::FbPost", "id", id); |
| |
| auto cmd = static_cast<FbPostCmd*>(io_buffer_.virt()); |
| cmd->op = kOP_rcFbPost; |
| cmd->size = kSize_rcFbPost; |
| cmd->id = id; |
| |
| WriteLocked(kSize_rcFbPost); |
| } |
| |
| int Display::FlushHandler() { |
| zx_time_t next_deadline = zx_clock_get_monotonic(); |
| zx_time_t period = ZX_SEC(1) / kRefreshRateHz; |
| |
| while (1) { |
| zx_nanosleep(next_deadline); |
| |
| ColorBuffer* displayed_fb; |
| { |
| fbl::AutoLock lock(&flush_lock_); |
| |
| if (shutdown_) |
| break; |
| |
| displayed_fb = current_fb_; |
| } |
| |
| if (displayed_fb) { |
| fbl::AutoLock lock(&lock_); |
| |
| if (displayed_fb->paddr) { |
| uint32_t result; |
| zx_status_t status = UpdateColorBufferLocked( |
| displayed_fb->id, displayed_fb->paddr, |
| displayed_fb->width, displayed_fb->height, |
| displayed_fb->size, &result); |
| if (status != ZX_OK || result) { |
| zxlogf(ERROR, "%s: color buffer update failed\n", kTag); |
| continue; |
| } |
| } |
| |
| FbPostLocked(displayed_fb->id); |
| } |
| |
| { |
| fbl::AutoLock lock(&flush_lock_); |
| |
| if (dc_intf_.is_valid()) { |
| uint64_t handles[] = {reinterpret_cast<uint64_t>(displayed_fb)}; |
| dc_intf_.OnDisplayVsync(kDisplayId, next_deadline, handles, |
| displayed_fb ? 1 : 0); |
| } |
| } |
| |
| next_deadline = zx_time_add_duration(next_deadline, period); |
| } |
| |
| return 0; |
| } |
| |
| } // namespace goldfish |
| |
| static zx_driver_ops_t goldfish_display_driver_ops = []() -> zx_driver_ops_t { |
| zx_driver_ops_t ops; |
| ops.version = DRIVER_OPS_VERSION; |
| ops.bind = goldfish::Display::Create; |
| return ops; |
| }(); |
| |
| // clang-format off |
| ZIRCON_DRIVER_BEGIN(goldfish_display, goldfish_display_driver_ops, "zircon", |
| "0.1", 1) |
| BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_GOLDFISH_CONTROL), |
| ZIRCON_DRIVER_END(goldfish_display) |
| // clang-format on |