blob: 49f22920e81fed628259ccb21708a0a8bd6ec439 [file] [log] [blame]
// Copyright 2018 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 "astro-display.h"
#include <ddk/binding.h>
#include <ddk/platform-defs.h>
#include <ddk/protocol/composite.h>
#include <fbl/algorithm.h>
#include <fbl/auto_call.h>
#include <fbl/vector.h>
#include <fuchsia/sysmem/c/fidl.h>
#include <lib/image-format/image_format.h>
namespace astro_display {
namespace {
// List of supported pixel formats
zx_pixel_format_t kSupportedPixelFormats[] = { ZX_PIXEL_FORMAT_RGB_x888 };
constexpr uint64_t kDisplayId = PANEL_DISPLAY_ID;
// Astro/Sherlock Display Configuration. These configuration comes directly from
// from LCD vendor and hardware team.
constexpr display_setting_t kDisplaySettingTV070WSM_FT = {
.lane_num = 4,
.bit_rate_max = 360,
.clock_factor = 8,
.lcd_clock = 44250000,
.h_active = 600,
.v_active = 1024,
.h_period = 700,
.v_period = 1053,
.hsync_width = 24,
.hsync_bp = 36,
.hsync_pol = 0,
.vsync_width = 2,
.vsync_bp = 8,
.vsync_pol = 0,
};
constexpr display_setting_t kDisplaySettingP070ACB_FT = {
.lane_num = 4,
.bit_rate_max = 400,
.clock_factor = 8,
.lcd_clock = 49434000,
.h_active = 600,
.v_active = 1024,
.h_period = 770,
.v_period = 1070,
.hsync_width = 10,
.hsync_bp = 80,
.hsync_pol = 0,
.vsync_width = 6,
.vsync_bp = 20,
.vsync_pol = 0,
};
constexpr display_setting_t kDisplaySettingG101B158_FT = {
.lane_num = 4,
.bit_rate_max = 566,
.clock_factor = 8,
.lcd_clock = 70701600,
.h_active = 800,
.v_active = 1280,
.h_period = 890,
.v_period = 1324,
.hsync_width = 24,
.hsync_bp = 20,
.hsync_pol = 0,
.vsync_width = 4,
.vsync_bp = 20,
.vsync_pol = 0,
};
constexpr display_setting_t kDisplaySettingTV101WXM_FT = {
.lane_num = 4,
.bit_rate_max = 566,
.clock_factor = 8,
.lcd_clock = 70701600,
.h_active = 800,
.v_active = 1280,
.h_period = 890,
.v_period = 1324,
.hsync_width = 20,
.hsync_bp = 50,
.hsync_pol = 0,
.vsync_width = 4,
.vsync_bp = 20,
.vsync_pol = 0,
};
} // namespace
// This function copies the display settings into our internal structure
void AstroDisplay::CopyDisplaySettings() {
ZX_DEBUG_ASSERT(init_disp_table_);
disp_setting_.h_active = init_disp_table_->h_active;
disp_setting_.v_active = init_disp_table_->v_active;
disp_setting_.h_period = init_disp_table_->h_period;
disp_setting_.v_period = init_disp_table_->v_period;
disp_setting_.hsync_width = init_disp_table_->hsync_width;
disp_setting_.hsync_bp = init_disp_table_->hsync_bp;
disp_setting_.hsync_pol = init_disp_table_->hsync_pol;
disp_setting_.vsync_width = init_disp_table_->vsync_width;
disp_setting_.vsync_bp = init_disp_table_->vsync_bp;
disp_setting_.vsync_pol = init_disp_table_->vsync_pol;
disp_setting_.lcd_clock = init_disp_table_->lcd_clock;
disp_setting_.clock_factor = init_disp_table_->clock_factor;
disp_setting_.lane_num = init_disp_table_->lane_num;
disp_setting_.bit_rate_max = init_disp_table_->bit_rate_max;
}
void AstroDisplay::PopulateAddedDisplayArgs(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 = kSupportedPixelFormats;
args->pixel_format_count = countof(kSupportedPixelFormats);
args->cursor_info_count = 0;
}
zx_status_t AstroDisplay::DisplayInit() {
zx_status_t status;
fbl::AllocChecker ac;
if (!skip_disp_init_) {
// Detect panel type
PopulatePanelType();
if (panel_type_ == PANEL_TV070WSM_FT) {
init_disp_table_ = &kDisplaySettingTV070WSM_FT;
} else if (panel_type_ == PANEL_P070ACB_FT) {
init_disp_table_ = &kDisplaySettingP070ACB_FT;
} else if (panel_type_ == PANEL_TV101WXM_FT) {
init_disp_table_ = &kDisplaySettingTV101WXM_FT;
} else if (panel_type_ == PANEL_G101B158_FT) {
init_disp_table_ = &kDisplaySettingG101B158_FT;
} else {
DISP_ERROR("Unsupported panel detected!\n");
status = ZX_ERR_NOT_SUPPORTED;
return status;
}
// Populated internal structures based on predefined tables
CopyDisplaySettings();
// Ensure Max Bit Rate / pixel clock ~= 8 (8.xxx). This is because the clock calculation
// part of code assumes a clock factor of 1. All the LCD tables from Astro have this
// relationship established. We'll have to revisit the calculation if this ratio cannot
// be met.
if (init_disp_table_->bit_rate_max / (init_disp_table_->lcd_clock / 1000 / 1000) != 8) {
DISP_ERROR("Max Bit Rate / pixel clock != 8\n");
status = ZX_ERR_INVALID_ARGS;
return status;
}
// Setup VPU and VPP units first
vpu_ = fbl::make_unique_checked<astro_display::Vpu>(&ac);
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
status = vpu_->Init(components_[COMPONENT_PDEV]);
if (status != ZX_OK) {
DISP_ERROR("Could not initialize VPU object\n");
return status;
}
vpu_->PowerOff();
vpu_->PowerOn();
vpu_->VppInit();
clock_ = fbl::make_unique_checked<astro_display::AstroDisplayClock>(&ac);
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
status = clock_->Init(components_[COMPONENT_PDEV]);
if (status != ZX_OK) {
DISP_ERROR("Could not initialize Clock object\n");
return status;
}
// Enable all display related clocks
status = clock_->Enable(disp_setting_);
if (status != ZX_OK) {
DISP_ERROR("Could not enable display clocks!\n");
return status;
}
// Program and Enable DSI Host Interface
dsi_host_ = fbl::make_unique_checked<astro_display::AmlDsiHost>(&ac,
components_[COMPONENT_PDEV],
components_[COMPONENT_DSI],
components_[COMPONENT_LCD_GPIO],
clock_->GetBitrate(),
panel_type_);
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
status = dsi_host_->Init();
if (status != ZX_OK) {
DISP_ERROR("Could not initialize DSI Host\n");
return status;
}
status = dsi_host_->HostOn(disp_setting_);
if (status != ZX_OK) {
DISP_ERROR("DSI Host On failed! %d\n", status);
return status;
}
}
osd_ = fbl::make_unique_checked<astro_display::Osd>(&ac,
width_,
height_,
disp_setting_.h_active,
disp_setting_.v_active);
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
// Initialize osd object
status = osd_->Init(components_[COMPONENT_PDEV]);
if (status != ZX_OK) {
DISP_ERROR("Could not initialize OSD object\n");
return status;
}
if (!skip_disp_init_) {
osd_->HwInit();
}
// Configure osd layer
current_image_valid_= false;
status = osd_->Configure();
if (status != ZX_OK) {
DISP_ERROR("OSD configuration failed!\n");
return status;
}
return ZX_OK;
}
// part of ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL ops
uint32_t AstroDisplay::DisplayControllerImplComputeLinearStride(uint32_t width,
zx_pixel_format_t format) {
// The astro display controller needs buffers with a stride that is an even
// multiple of 32.
return ROUNDUP(width, 32 / ZX_PIXEL_FORMAT_BYTES(format));
}
// part of ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL ops
void AstroDisplay::DisplayControllerImplSetDisplayControllerInterface(
const display_controller_interface_protocol_t* intf) {
fbl::AutoLock lock(&display_lock_);
dc_intf_ = ddk::DisplayControllerInterfaceProtocolClient(intf);
added_display_args_t args;
PopulateAddedDisplayArgs(&args);
dc_intf_.OnDisplaysChanged(&args, 1, nullptr, 0, nullptr, 0, nullptr);
}
// part of ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL ops
zx_status_t AstroDisplay::DisplayControllerImplImportVmoImage(image_t* image, zx::vmo vmo,
size_t offset) {
zx_status_t status = ZX_OK;
auto import_info = std::make_unique<ImageInfo>();
if (import_info == nullptr) {
return ZX_ERR_NO_MEMORY;
}
fbl::AutoLock lock(&image_lock_);
if (image->type != IMAGE_TYPE_SIMPLE || image->pixel_format != format_) {
status = ZX_ERR_INVALID_ARGS;
return status;
}
uint32_t stride = DisplayControllerImplComputeLinearStride(image->width, image->pixel_format);
canvas_info_t canvas_info;
canvas_info.height = image->height;
canvas_info.stride_bytes = stride * ZX_PIXEL_FORMAT_BYTES(image->pixel_format);
canvas_info.wrap = 0;
canvas_info.blkmode = 0;
canvas_info.endianness = 0;
canvas_info.flags = CANVAS_FLAGS_READ;
uint8_t local_canvas_idx;
status = amlogic_canvas_config(&canvas_, vmo.release(), offset, &canvas_info,
&local_canvas_idx);
if (status != ZX_OK) {
DISP_ERROR("Could not configure canvas: %d\n", status);
status = ZX_ERR_NO_RESOURCES;
return status;
}
import_info->canvas_idx = local_canvas_idx;
import_info->image_height = image->height;
import_info->image_width = image->width;
import_info->image_stride = stride * ZX_PIXEL_FORMAT_BYTES(image->pixel_format);
image->handle = reinterpret_cast<uint64_t>(import_info.get());
imported_images_.push_back(std::move(import_info));
return status;
}
// part of ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL ops
zx_status_t AstroDisplay::DisplayControllerImplImportImage(image_t* image,
zx_unowned_handle_t handle,
uint32_t index) {
zx_status_t status = ZX_OK;
auto import_info = std::make_unique<ImageInfo>();
if (import_info == nullptr) {
return ZX_ERR_NO_MEMORY;
}
if (image->type != IMAGE_TYPE_SIMPLE || image->pixel_format != format_) {
status = ZX_ERR_INVALID_ARGS;
return status;
}
zx_status_t 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;
}
fbl::Vector<zx::vmo> vmos;
for (uint32_t i = 0; i < collection_info.buffer_count; ++i) {
vmos.push_back(zx::vmo(collection_info.buffers[i].vmo));
}
if (!collection_info.settings.has_image_format_constraints || index >= vmos.size()) {
return ZX_ERR_OUT_OF_RANGE;
}
ZX_DEBUG_ASSERT(collection_info.settings.image_format_constraints.pixel_format.type ==
fuchsia_sysmem_PixelFormatType_BGRA32);
ZX_DEBUG_ASSERT(
!collection_info.settings.image_format_constraints.pixel_format.has_format_modifier);
uint32_t minimum_row_bytes;
if (!ImageFormatMinimumRowBytes(&collection_info.settings.image_format_constraints,
image->width, &minimum_row_bytes)) {
DISP_ERROR("Invalid image width %d for collection\n", image->width);
return ZX_ERR_INVALID_ARGS;
}
canvas_info_t canvas_info;
canvas_info.height = image->height;
canvas_info.stride_bytes = minimum_row_bytes;
canvas_info.wrap = 0;
canvas_info.blkmode = 0;
canvas_info.endianness = 0;
canvas_info.flags = CANVAS_FLAGS_READ;
uint8_t local_canvas_idx;
status = amlogic_canvas_config(&canvas_, vmos[index].release(),
collection_info.buffers[index].vmo_usable_start, &canvas_info,
&local_canvas_idx);
if (status != ZX_OK) {
DISP_ERROR("Could not configure canvas: %d\n", status);
status = ZX_ERR_NO_RESOURCES;
return status;
}
fbl::AutoLock lock(&image_lock_);
import_info->canvas_idx = local_canvas_idx;
import_info->image_height = image->height;
import_info->image_stride = minimum_row_bytes;
image->handle = reinterpret_cast<uint64_t>(import_info.get());
imported_images_.push_back(std::move(import_info));
return status;
}
// part of ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL ops
void AstroDisplay::DisplayControllerImplReleaseImage(image_t* image) {
fbl::AutoLock lock(&image_lock_);
auto info = reinterpret_cast<ImageInfo*>(image->handle);
auto local_canvas_idx = info->canvas_idx;
if(imported_images_.erase(*info) != nullptr) {
amlogic_canvas_free(&canvas_, local_canvas_idx);
}
}
// part of ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL ops
uint32_t AstroDisplay::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 == PANEL_DISPLAY_ID);
fbl::AutoLock lock(&display_lock_);
bool success = true;
if (display_configs[0]->layer_count > 1) {
// We only support 1 layer
success = false;
}
if (success && display_configs[0]->cc_flags) {
// Make sure cc values are correct
if (display_configs[0]->cc_flags & COLOR_CONVERSION_PREOFFSET) {
for (int i = 0; i < 3; i++) {
success = success && display_configs[0]->cc_preoffsets[i] > -1;
success = success && display_configs[0]->cc_preoffsets[i] < 1;
}
}
if (success && display_configs[0]->cc_flags & COLOR_CONVERSION_POSTOFFSET) {
for (int i = 0; i < 3; i++) {
success = success && display_configs[0]->cc_postoffsets[i] > -1;
success = success && display_configs[0]->cc_postoffsets[i] < 1;
}
}
}
if (success) {
// Make sure ther layer configuration is supported
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.width == width_
&& layer.image.height == height_
&& memcmp(&layer.dest_frame, &frame, sizeof(frame_t)) == 0
&& memcmp(&layer.src_frame, &frame, sizeof(frame_t)) == 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;
}
}
return CONFIG_DISPLAY_OK;
}
// part of ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL ops
void AstroDisplay::DisplayControllerImplApplyConfiguration(const display_config_t** display_configs,
size_t display_count) {
ZX_DEBUG_ASSERT(display_configs);
fbl::AutoLock lock(&display_lock_);
if (display_count == 1 && display_configs[0]->layer_count) {
if (!full_init_done_) {
zx_status_t status;
if ((status = DisplayInit()) != ZX_OK) {
DISP_ERROR("Display Hardware Initialization failed! %d\n", status);
ZX_ASSERT(0);
}
full_init_done_ = true;
}
// Since Astro does not support plug'n play (fixed display), there is no way
// a checked configuration could be invalid at this point.
auto info = reinterpret_cast<ImageInfo*>(display_configs[0]
->layer_list[0]->cfg.primary.image.handle);
current_image_valid_= true;
current_image_ = display_configs[0]->layer_list[0]->cfg.primary.image.handle;
osd_->FlipOnVsync(info->canvas_idx, display_configs[0]);
} else {
current_image_valid_= false;
if (full_init_done_) {
osd_->Disable();
}
}
// If bootloader does not enable any of the display hardware, no vsync will be generated.
// This fakes a vsync to let clients know we are ready until we actually initialize hardware
if (!full_init_done_) {
if (dc_intf_.is_valid()) {
if (display_count == 0 || display_configs[0]->layer_count == 0) {
dc_intf_.OnDisplayVsync(kDisplayId, zx_clock_get_monotonic(), nullptr, 0);
}
}
}
}
// part of ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL ops
zx_status_t AstroDisplay::DisplayControllerImplAllocateVmo(uint64_t size, zx::vmo* vmo_out) {
return zx::vmo::create_contiguous(bti_, size, 0, vmo_out);
}
void AstroDisplay::DdkUnbind() {
DdkRemove();
}
void AstroDisplay::DdkRelease() {
if (osd_) {
osd_->Release();
}
vsync_irq_.destroy();
thrd_join(vsync_thread_, NULL);
delete this;
}
// This function detect the panel type based.
void AstroDisplay::PopulatePanelType() {
uint8_t pt;
if ((gpio_config_in(&gpio_, GPIO_NO_PULL) == ZX_OK) &&
(gpio_read(&gpio_, &pt) == ZX_OK)) {
panel_type_ = pt;
if (board_info_.pid == PDEV_PID_ASTRO) {
DISP_INFO("Detected panel type = %s (%d)\n",
panel_type_ ? "P070ACB_FT" : "TV070WSM_FT", panel_type_);
} else if (board_info_.pid == PDEV_PID_SHERLOCK) {
DISP_INFO("Detected panel type = %s (%d)\n",
panel_type_ ? "G101B158_FT" : "TV101WXM_FT", panel_type_);
panel_type_ = static_cast<uint8_t>(pt + PANEL_TV101WXM_FT);
} else {
DISP_ERROR("Panel detection attempted on Unsupported hardware\n");
ZX_ASSERT(0);
}
} else {
panel_type_ = PANEL_UNKNOWN;
DISP_ERROR("Failed to detect a valid panel\n");
}
}
zx_status_t AstroDisplay::SetupDisplayInterface() {
fbl::AutoLock lock(&display_lock_);
// Figure out board rev and panel type for Astro only
if (board_info_.pid == PDEV_PID_ASTRO) {
panel_type_ = PANEL_UNKNOWN;
skip_disp_init_ = false;
if (board_info_.board_revision < BOARD_REV_EVT_1) {
DISP_INFO("Unsupported Board REV (%d). Will skip display driver initialization\n",
board_info_.board_revision);
skip_disp_init_ = true;
}
} else if (board_info_.pid == PDEV_PID_SHERLOCK) {
panel_type_ = PANEL_UNKNOWN;
skip_disp_init_ = false;
} else {
skip_disp_init_ = true;
}
format_ = ZX_PIXEL_FORMAT_RGB_x888;
stride_ = DisplayControllerImplComputeLinearStride(width_, format_);
if (dc_intf_.is_valid()) {
added_display_args_t args;
PopulateAddedDisplayArgs(&args);
dc_intf_.OnDisplaysChanged(&args, 1, nullptr, 0, nullptr, 0, nullptr);
}
return ZX_OK;
}
zx_status_t AstroDisplay::DisplayControllerImplGetSysmemConnection(zx::channel connection) {
zx_status_t status = sysmem_connect(&sysmem_, connection.release());
if (status != ZX_OK) {
DISP_ERROR("Could not connect to sysmem\n");
return status;
}
return ZX_OK;
}
zx_status_t AstroDisplay::DisplayControllerImplSetBufferCollectionConstraints(
const image_t* config, zx_unowned_handle_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.heap_permitted_count = 2;
buffer_constraints.heap_permitted[0] = fuchsia_sysmem_HeapType_SYSTEM_RAM;
buffer_constraints.heap_permitted[1] = fuchsia_sysmem_HeapType_AMLOGIC_SECURE;
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 = 32;
image_constraints.start_offset_divisor = 32;
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) {
DISP_ERROR("Failed to set constraints");
return status;
}
return ZX_OK;
}
zx_status_t AstroDisplay::DisplayControllerImplCaptureDisplayOutput(zx::vmo* out_vmo) {
// Make sure this lock is enough to ensure images are not discarded during capture
fbl::AutoLock lock(&display_lock_);
// First obtain the information regarding the current image being display
if (!current_image_valid_) {
DISP_ERROR("Not valid image being displayed\n");
return ZX_ERR_UNAVAILABLE;
}
auto image_info = reinterpret_cast<ImageInfo*>(current_image_);
//FIXME: make sure it's valid
//Create VMO
uint64_t size = image_info->image_height * image_info->image_stride;
zx_status_t status = zx::vmo::create_contiguous(bti_, size, 0, out_vmo);
if (status != ZX_OK) {
DISP_ERROR("Could not allocate vmo %d\n", status);
return status;
}
// duplicate the vmo. One for the canvas driver. the other goes back to client
zx_handle_t vmo_dup;
if ((status = zx_handle_duplicate(out_vmo->get(), ZX_RIGHT_SAME_RIGHTS, &vmo_dup)) != ZX_OK) {
DISP_ERROR("Couldn't duplicate vmo %d\n", status);
return status;
}
canvas_info_t canvas_info;
canvas_info.height = image_info->image_height;
// canvas_info.stride_bytes = image_info->image_stride;
canvas_info.stride_bytes = (ALIGN(600 * 3, 64)); //image_info->image_stride;
canvas_info.wrap = 0;
canvas_info.blkmode = 0;
canvas_info.endianness = 7; // little endian 64bit
canvas_info.flags = CANVAS_FLAGS_READ | CANVAS_FLAGS_WRITE;
uint8_t canvas_idx;
status = amlogic_canvas_config(&canvas_, vmo_dup, 0, &canvas_info, &canvas_idx);
if (status != ZX_OK) {
DISP_ERROR("Could not configure canvas %d\n", status);
status = ZX_ERR_NO_RESOURCES;
return status;
}
// At this point, we have a canvas configured with the same settings as the canvas used by
// the currently displayed image. Now, we can start to capture a frame of image
DISP_INFO("canvas: index = %d, height = %d, stride = %d, width = %d\n",
canvas_idx,
image_info->image_height,
image_info->image_stride,
image_info->image_width);
// Start the capture
return vpu_->Capture(canvas_idx, image_info->image_height, 600);
}
int AstroDisplay::VSyncThread() {
zx_status_t status;
while (1) {
status = vsync_irq_.wait(nullptr);
if (status != ZX_OK) {
DISP_ERROR("VSync Interrupt Wait failed\n");
break;
}
fbl::AutoLock lock(&display_lock_);
uint64_t live[] = { current_image_ };
bool current_image_valid = current_image_valid_;
if (dc_intf_.is_valid()) {
dc_intf_.OnDisplayVsync(kDisplayId, zx_clock_get_monotonic(),
live, current_image_valid);
}
}
return status;
}
// TODO(payamm): make sure unbind/release are called if we return error
zx_status_t AstroDisplay::Bind() {
composite_protocol_t composite;
auto status = device_get_protocol(parent_, ZX_PROTOCOL_COMPOSITE, &composite);
if (status != ZX_OK) {
DISP_ERROR("Could not get composite protocol\n");
return status;
}
size_t actual;
composite_get_components(&composite, components_, fbl::count_of(components_), &actual);
if (actual != fbl::count_of(components_)) {
DISP_ERROR("could not get components\n");
return ZX_ERR_NOT_SUPPORTED;
}
status = device_get_protocol(components_[COMPONENT_PDEV], ZX_PROTOCOL_PDEV, &pdev_);
if (status != ZX_OK) {
DISP_ERROR("Could not get PDEV protocol\n");
return status;
}
dsi_impl_protocol_t dsi;
status = device_get_protocol(components_[COMPONENT_DSI], ZX_PROTOCOL_DSI_IMPL, &dsi);
if (status != ZX_OK) {
DISP_ERROR("Could not get DSI_IMPL protocol\n");
return status;
}
dsiimpl_ = &dsi;
// Get board info
status = pdev_get_board_info(&pdev_, &board_info_);
if (status != ZX_OK) {
DISP_ERROR("Could not obtain board info\n");
return status;
}
if (board_info_.pid == PDEV_PID_ASTRO) {
width_ = ASTRO_DISPLAY_WIDTH;
height_ = ASTRO_DISPLAY_HEIGHT;
} else if (board_info_.pid == PDEV_PID_SHERLOCK) {
width_ = SHERLOCK_DISPLAY_WIDTH;
height_ = SHERLOCK_DISPLAY_HEIGHT;
} else {
DISP_ERROR("Running on Unsupported hardware. Use at your own risk\n");
}
// Obtain GPIO Protocol for Panel reset
status = device_get_protocol(components_[COMPONENT_PANEL_GPIO], ZX_PROTOCOL_GPIO, &gpio_);
if (status != ZX_OK) {
DISP_ERROR("Could not obtain GPIO protocol.\n");
return status;
}
status = device_get_protocol(components_[COMPONENT_SYSMEM], ZX_PROTOCOL_SYSMEM, &sysmem_);
if (status != ZX_OK) {
DISP_ERROR("Could not get Display SYSMEM protocol\n");
return status;
}
status = device_get_protocol(components_[COMPONENT_CANVAS], ZX_PROTOCOL_AMLOGIC_CANVAS,
&canvas_);
if (status != ZX_OK) {
DISP_ERROR("Could not obtain CANVAS protocol\n");
return status;
}
status = pdev_get_bti(&pdev_, 0, bti_.reset_and_get_address());
if (status != ZX_OK) {
DISP_ERROR("Could not get BTI handle\n");
return status;
}
// Setup Display Interface
status = SetupDisplayInterface();
if (status != ZX_OK) {
DISP_ERROR("Astro display setup failed! %d\n", status);
return status;
}
// Map VSync Interrupt
status = pdev_get_interrupt(&pdev_, IRQ_VSYNC, 0, vsync_irq_.reset_and_get_address());
if (status != ZX_OK) {
DISP_ERROR("Could not map vsync interrupt\n");
return status;
}
auto start_thread = [](void* arg) { return static_cast<AstroDisplay*>(arg)->VSyncThread(); };
status = thrd_create_with_name(&vsync_thread_, start_thread, this, "vsync_thread");
if (status != ZX_OK) {
DISP_ERROR("Could not create vsync_thread\n");
return status;
}
auto cleanup = fbl::MakeAutoCall([&]() { DdkRelease(); });
status = DdkAdd("astro-display");
if (status != ZX_OK) {
DISP_ERROR("Could not add device\n");
return status;
}
cleanup.cancel();
return ZX_OK;
}
void AstroDisplay::Dump() {
DISP_INFO("#############################\n");
DISP_INFO("Dumping disp_setting structure:\n");
DISP_INFO("#############################\n");
DISP_INFO("h_active = 0x%x (%u)\n", disp_setting_.h_active,
disp_setting_.h_active);
DISP_INFO("v_active = 0x%x (%u)\n", disp_setting_.v_active,
disp_setting_.v_active);
DISP_INFO("h_period = 0x%x (%u)\n", disp_setting_.h_period,
disp_setting_.h_period);
DISP_INFO("v_period = 0x%x (%u)\n", disp_setting_.v_period,
disp_setting_.v_period);
DISP_INFO("hsync_width = 0x%x (%u)\n", disp_setting_.hsync_width,
disp_setting_.hsync_width);
DISP_INFO("hsync_bp = 0x%x (%u)\n", disp_setting_.hsync_bp,
disp_setting_.hsync_bp);
DISP_INFO("hsync_pol = 0x%x (%u)\n", disp_setting_.hsync_pol,
disp_setting_.hsync_pol);
DISP_INFO("vsync_width = 0x%x (%u)\n", disp_setting_.vsync_width,
disp_setting_.vsync_width);
DISP_INFO("vsync_bp = 0x%x (%u)\n", disp_setting_.vsync_bp,
disp_setting_.vsync_bp);
DISP_INFO("vsync_pol = 0x%x (%u)\n", disp_setting_.vsync_pol,
disp_setting_.vsync_pol);
DISP_INFO("lcd_clock = 0x%x (%u)\n", disp_setting_.lcd_clock,
disp_setting_.lcd_clock);
DISP_INFO("lane_num = 0x%x (%u)\n", disp_setting_.lane_num,
disp_setting_.lane_num);
DISP_INFO("bit_rate_max = 0x%x (%u)\n", disp_setting_.bit_rate_max,
disp_setting_.bit_rate_max);
DISP_INFO("clock_factor = 0x%x (%u)\n", disp_setting_.clock_factor,
disp_setting_.clock_factor);
}
// main bind function called from dev manager
zx_status_t astro_display_bind(void* ctx, zx_device_t* parent) {
fbl::AllocChecker ac;
auto dev = fbl::make_unique_checked<astro_display::AstroDisplay>(&ac, parent);
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
auto status = dev->Bind();
if (status == ZX_OK) {
// devmgr is now in charge of the memory for dev
__UNUSED auto ptr = dev.release();
}
return status;
}
static zx_driver_ops_t astro_display_ops = [](){
zx_driver_ops_t ops;
ops.version = DRIVER_OPS_VERSION;
ops.bind = astro_display_bind;
return ops;
}();
} // namespace astro_display
ZIRCON_DRIVER_BEGIN(astro_display, astro_display::astro_display_ops, "zircon", "0.1", 4)
BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_COMPOSITE),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_AMLOGIC),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_AMLOGIC_S905D2),
BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_AMLOGIC_DISPLAY),
ZIRCON_DRIVER_END(astro_display)