| // 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 <ddk/debug.h> |
| #include <ddk/device.h> |
| #include <ddk/driver.h> |
| #include <ddk/protocol/pci.h> |
| #include <ddktl/protocol/display-controller.h> |
| #include <hw/pci.h> |
| |
| #include <assert.h> |
| #include <zircon/pixelformat.h> |
| #include <zircon/process.h> |
| #include <zircon/syscalls.h> |
| #include <zircon/types.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include "simple-display.h" |
| |
| // implement display controller protocol |
| static constexpr uint64_t kDisplayId = 1; |
| |
| static constexpr uint64_t kImageHandle = 0xdecafc0ffee; |
| |
| void SimpleDisplay::DisplayControllerSetDisplayControllerInterface( |
| const display_controller_interface_t* intf) { |
| intf_ = ddk::DisplayControllerInterfaceProxy(intf); |
| |
| 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 = 3000; // Just guess that it's 30fps |
| args.pixel_format_list = &format_; |
| args.pixel_format_count = 1; |
| |
| intf_.OnDisplaysChanged(&args, 1, nullptr, 0, nullptr, 0, nullptr); |
| } |
| |
| zx_status_t SimpleDisplay::DisplayControllerImportVmoImage(image_t* image, zx_handle_t vmo, |
| size_t offset) { |
| zx_info_handle_basic_t import_info; |
| size_t actual, avail; |
| zx_status_t status = zx::unowned_vmo(vmo)->get_info(ZX_INFO_HANDLE_BASIC, |
| &import_info, sizeof(import_info), &actual, |
| &avail); |
| if (status != ZX_OK) { |
| return status; |
| } |
| if (import_info.koid != framebuffer_koid_) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (image->width != width_ || image->height != height_ |
| || image->pixel_format != format_ || offset != 0) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| image->handle = kImageHandle; |
| return ZX_OK; |
| } |
| |
| void SimpleDisplay::DisplayControllerReleaseImage(image_t* image) { |
| // noop |
| } |
| |
| uint32_t SimpleDisplay::DisplayControllerCheckConfiguration( |
| 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 { |
| 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 SimpleDisplay::DisplayControllerApplyConfiguration(const display_config_t** display_config, |
| size_t display_count) { |
| bool has_image = display_count != 0 && display_config[0]->layer_count != 0; |
| uint64_t handles[] = { kImageHandle }; |
| if (intf_.is_valid()) { |
| intf_.OnDisplayVsync(kDisplayId, zx_clock_get(ZX_CLOCK_MONOTONIC), handles, has_image); |
| } |
| } |
| |
| uint32_t SimpleDisplay::DisplayControllerComputeLinearStride(uint32_t width, |
| zx_pixel_format_t format) { |
| return (width == width_ && format == format_) ? stride_ : 0; |
| } |
| |
| zx_status_t SimpleDisplay::DisplayControllerAllocateVmo(uint64_t size, zx_handle_t* vmo_out) { |
| zx_info_handle_count handle_count; |
| size_t actual, avail; |
| zx_status_t status = framebuffer_handle_.get_info(ZX_INFO_HANDLE_COUNT, &handle_count, |
| sizeof(handle_count), &actual, &avail); |
| if (status != ZX_OK) { |
| return status; |
| } |
| if (handle_count.handle_count != 1) { |
| return ZX_ERR_NO_RESOURCES; |
| } |
| if (size > height_ * stride_ * ZX_PIXEL_FORMAT_BYTES(format_)) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| return zx_handle_duplicate(framebuffer_handle_.get(), ZX_RIGHT_SAME_RIGHTS, vmo_out); |
| } |
| |
| // implement device protocol |
| |
| void SimpleDisplay::DdkUnbind() { |
| DdkRemove(); |
| } |
| |
| void SimpleDisplay::DdkRelease() { |
| delete this; |
| } |
| |
| // implement driver object: |
| |
| zx_status_t SimpleDisplay::Bind(const char* name, fbl::unique_ptr<SimpleDisplay>* vbe_ptr) { |
| zx_info_handle_basic_t framebuffer_info; |
| size_t actual, avail; |
| zx_status_t status = framebuffer_handle_.get_info( |
| ZX_INFO_HANDLE_BASIC, &framebuffer_info, sizeof(framebuffer_info), &actual, &avail); |
| if (status != ZX_OK) { |
| printf("%s: failed to id framebuffer: %d\n", name, status); |
| return status; |
| } |
| framebuffer_koid_ = framebuffer_info.koid; |
| |
| status = DdkAdd(name); |
| if (status != ZX_OK) { |
| return status; |
| } |
| // DevMgr now owns this pointer, release it to avoid destroying the object |
| // when device goes out of scope. |
| __UNUSED auto ptr = vbe_ptr->release(); |
| |
| zxlogf(INFO, "%s: initialized display, %u x %u (stride=%u format=%08x)\n", |
| name, width_, height_, stride_, format_); |
| |
| return ZX_OK; |
| } |
| |
| SimpleDisplay::SimpleDisplay(zx_device_t* parent, zx_handle_t vmo, |
| uintptr_t framebuffer, uint64_t framebuffer_size, |
| uint32_t width, uint32_t height, |
| uint32_t stride, zx_pixel_format_t format) |
| : DeviceType(parent), framebuffer_handle_(vmo), |
| framebuffer_(framebuffer), framebuffer_size_(framebuffer_size), |
| width_(width), height_(height), stride_(stride), format_(format) { } |
| |
| SimpleDisplay::~SimpleDisplay() { |
| zx_vmar_unmap(zx_vmar_root_self(), framebuffer_, framebuffer_size_); |
| } |
| |
| zx_status_t bind_simple_pci_display_bootloader(zx_device_t* dev, const char* name, uint32_t bar) { |
| uint32_t format, width, height, stride; |
| zx_status_t status = zx_framebuffer_get_info(get_root_resource(), &format, |
| &width, &height, &stride); |
| if (status != ZX_OK) { |
| printf("%s: failed to get bootloader dimensions: %d\n", name, status); |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| return bind_simple_pci_display(dev, name, bar, width, height, stride, 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, zx_pixel_format_t format) { |
| pci_protocol_t pci; |
| zx_status_t status; |
| if (device_get_protocol(dev, ZX_PROTOCOL_PCI, &pci)) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| void* framebuffer; |
| uint64_t framebuffer_size; |
| zx_handle_t framebuffer_handle; |
| // map framebuffer window |
| status = pci_map_bar(&pci, bar, ZX_CACHE_POLICY_WRITE_COMBINING, |
| &framebuffer, &framebuffer_size, &framebuffer_handle); |
| if (status != ZX_OK) { |
| printf("%s: failed to map pci bar %d: %d\n", name, bar, status); |
| return status; |
| } |
| |
| fbl::AllocChecker ac; |
| fbl::unique_ptr<SimpleDisplay> display(new (&ac) SimpleDisplay( |
| dev, framebuffer_handle, (uintptr_t) framebuffer, |
| framebuffer_size, width, height, stride, format)); |
| if (!ac.check()) { |
| zx_handle_close(framebuffer_handle); |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| return display->Bind(name, &display); |
| } |