| // 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; |
| 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; |
| } |
| if (imported_images_.GetOne(local_canvas_idx)) { |
| DISP_INFO("Reusing previously allocated canvas (index = %d)\n", local_canvas_idx); |
| } |
| imported_images_.SetOne(local_canvas_idx); |
| image->handle = local_canvas_idx; |
| 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; |
| |
| 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_); |
| if (imported_images_.GetOne(local_canvas_idx)) { |
| DISP_INFO("Reusing previously allocated canvas (index = %d)\n", local_canvas_idx); |
| } |
| imported_images_.SetOne(local_canvas_idx); |
| image->handle = local_canvas_idx; |
| return status; |
| } |
| |
| // part of ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL ops |
| void AstroDisplay::DisplayControllerImplReleaseImage(image_t* image) { |
| fbl::AutoLock lock(&image_lock_); |
| size_t local_canvas_idx = (size_t)image->handle; |
| if (imported_images_.GetOne(local_canvas_idx)) { |
| imported_images_.ClearOne(local_canvas_idx); |
| amlogic_canvas_free(&canvas_, static_cast<uint8_t>(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_); |
| uint8_t addr; |
| 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. |
| addr = (uint8_t) (uint64_t) display_configs[0]->layer_list[0]->cfg.primary.image.handle; |
| current_image_valid_= true; |
| current_image_ = addr; |
| osd_->FlipOnVsync(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_); |
| |
| { |
| // Reset imported_images_ bitmap |
| fbl::AutoLock lock(&image_lock_); |
| imported_images_.Reset(kMaxImportedImages); |
| } |
| |
| 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; |
| } |
| |
| 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) |