blob: cf9cf936a0f08595c490c689ddfbec775adc46b6 [file] [log] [blame] [edit]
// 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.
#ifndef SRC_GRAPHICS_DISPLAY_DRIVERS_AMLOGIC_DISPLAY_AMLOGIC_DISPLAY_H_
#define SRC_GRAPHICS_DISPLAY_DRIVERS_AMLOGIC_DISPLAY_AMLOGIC_DISPLAY_H_
#include <fidl/fuchsia.hardware.amlogiccanvas/cpp/wire.h>
#include <fidl/fuchsia.hardware.gpio/cpp/wire.h>
#include <fidl/fuchsia.hardware.sysmem/cpp/wire.h>
#include <fidl/fuchsia.images2/cpp/wire.h>
#include <fidl/fuchsia.sysmem/cpp/wire.h>
#include <fuchsia/hardware/display/clamprgb/cpp/banjo.h>
#include <fuchsia/hardware/display/controller/cpp/banjo.h>
#include <fuchsia/hardware/platform/device/cpp/banjo.h>
#include <lib/ddk/debug.h>
#include <lib/ddk/device.h>
#include <lib/ddk/driver.h>
#include <lib/inspect/cpp/inspect.h>
#include <lib/sysmem-version/sysmem-version.h>
#include <lib/zircon-internal/thread_annotations.h>
#include <lib/zx/bti.h>
#include <lib/zx/interrupt.h>
#include <unistd.h>
#include <zircon/compiler.h>
#include <zircon/types.h>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <ddktl/device.h>
#include <fbl/auto_lock.h>
#include <fbl/intrusive_double_list.h>
#include <fbl/mutex.h>
#include "src/graphics/display/drivers/amlogic-display/common.h"
#include "src/graphics/display/drivers/amlogic-display/osd.h"
#include "src/graphics/display/drivers/amlogic-display/vout.h"
#include "src/graphics/display/drivers/amlogic-display/vpu.h"
#include "src/graphics/display/lib/api-types-cpp/driver-buffer-collection-id.h"
namespace amlogic_display {
struct ImageInfo : public fbl::DoublyLinkedListable<std::unique_ptr<ImageInfo>> {
~ImageInfo() {
zxlogf(INFO, "Destroying image on canvas %d", canvas_idx);
if (canvas.has_value()) {
fidl::WireResult result = fidl::WireCall(canvas.value())->Free(canvas_idx);
if (!result.ok()) {
zxlogf(WARNING, "Failed to call Canvas Free: %s",
result.error().FormatDescription().c_str());
} else if (result->is_error()) {
zxlogf(WARNING, "Canvas Free failed: %s", zx_status_get_string(result->error_value()));
}
}
if (pmt) {
pmt.unpin();
}
}
std::optional<fidl::UnownedClientEnd<fuchsia_hardware_amlogiccanvas::Device>> canvas;
uint8_t canvas_idx;
uint32_t image_height;
uint32_t image_width;
PixelFormatAndModifier pixel_format;
bool is_afbc;
zx::pmt pmt;
zx_paddr_t paddr;
};
class AmlogicDisplay;
class ClampRgb;
// AmlogicDisplay will implement only a few subset of Device.
using DeviceType = ddk::Device<AmlogicDisplay, ddk::GetProtocolable, ddk::Suspendable,
ddk::Resumable, ddk::ChildPreReleaseable>;
class AmlogicDisplay
: public DeviceType,
public ddk::DisplayControllerImplProtocol<AmlogicDisplay, ddk::base_protocol>,
public ddk::DisplayClampRgbImplProtocol<AmlogicDisplay> {
public:
// Factory method used by the device manager glue code.
static zx_status_t Create(zx_device_t* parent);
explicit AmlogicDisplay(zx_device_t* parent);
AmlogicDisplay(const AmlogicDisplay&) = delete;
AmlogicDisplay(AmlogicDisplay&&) = delete;
AmlogicDisplay& operator=(const AmlogicDisplay&) = delete;
AmlogicDisplay& operator=(AmlogicDisplay&&) = delete;
~AmlogicDisplay();
// Acquires parent resources, sets up display submodules, and binds itself to
// the device node.
//
// Must be called once and only once by the device manager (via the Create()
// factory method) during the driver lifetime.
zx_status_t Bind();
// Required functions needed to implement Display Controller Protocol
void DisplayControllerImplSetDisplayControllerInterface(
const display_controller_interface_protocol_t* intf);
zx_status_t DisplayControllerImplImportBufferCollection(
uint64_t banjo_driver_buffer_collection_id, zx::channel collection_token);
zx_status_t DisplayControllerImplReleaseBufferCollection(
uint64_t banjo_driver_buffer_collection_id);
zx_status_t DisplayControllerImplImportImage(image_t* image,
uint64_t banjo_driver_buffer_collection_id,
uint32_t index);
void DisplayControllerImplReleaseImage(image_t* image);
config_check_result_t 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);
void DisplayControllerImplApplyConfiguration(const display_config_t** display_config,
size_t display_count,
const config_stamp_t* banjo_config_stamp);
void DisplayControllerImplSetEld(uint64_t display_id, const uint8_t* raw_eld_list,
size_t raw_eld_count) {} // No ELD required for non-HDA systems.
zx_status_t DisplayControllerImplGetSysmemConnection(zx::channel connection);
zx_status_t DisplayControllerImplSetBufferCollectionConstraints(
const image_t* config, uint64_t banjo_driver_buffer_collection_id);
zx_status_t DisplayControllerImplSetDisplayPower(uint64_t display_id, bool power_on);
zx_status_t DisplayControllerImplSetDisplayCaptureInterface(
const display_capture_interface_protocol_t* intf);
zx_status_t DisplayControllerImplImportImageForCapture(uint64_t banjo_driver_buffer_collection_id,
uint32_t index,
uint64_t* out_capture_handle);
zx_status_t DisplayControllerImplStartCapture(uint64_t capture_handle);
zx_status_t DisplayControllerImplReleaseCapture(uint64_t capture_handle);
bool DisplayControllerImplIsCaptureCompleted() __TA_EXCLUDES(capture_mutex_);
zx_status_t DisplayClampRgbImplSetMinimumRgb(uint8_t minimum_rgb);
// Required functions for DeviceType
void DdkSuspend(ddk::SuspendTxn txn);
void DdkResume(ddk::ResumeTxn txn);
void DdkRelease();
zx_status_t DdkGetProtocol(uint32_t proto_id, void* out_protocol);
void DdkChildPreRelease(void* child_ctx) {
fbl::AutoLock lock(&display_mutex_);
dc_intf_ = ddk::DisplayControllerInterfaceProtocolClient();
}
void Dump() { vout_->Dump(); }
void SetFormatSupportCheck(fit::function<bool(fuchsia_images2::wire::PixelFormat)> fn) {
format_support_check_ = std::move(fn);
}
void SetCanvasForTesting(fidl::ClientEnd<fuchsia_hardware_amlogiccanvas::Device> canvas) {
canvas_.Bind(std::move(canvas));
}
void SetVoutForTesting(std::unique_ptr<Vout> vout) { vout_ = std::move(vout); }
void SetSysmemAllocatorForTesting(
fidl::WireSyncClient<fuchsia_sysmem::Allocator> sysmem_allocator_client) {
sysmem_allocator_client_ = std::move(sysmem_allocator_client);
}
private:
void VSyncThreadEntryPoint();
void CaptureThreadEntryPoint();
void HpdThreadEntryPoint();
void PopulatePanelType() TA_REQ(display_mutex_);
// TODO(fxbug.dev/132267): Currently, AmlogicDisplay has a multi-step
// initialization procedure when the device manager binds the driver to the
// device node. This makes the initialization stateful and hard to maintain
// (submodules may have dependencies on initialization). Instead, it's
// preferred to turn the initialization procedure into a builder pattern,
// where resources are acquired before the device is created, and the device
// is guaranteed to be ready at creation time.
// Acquires the common protocols (pdev, sysmem and amlogic-canvas) and
// resources (Vsync interrupts, capture interrupts and BTIs) from the parent
// nodes.
//
// Must be called once and only once per driver binding procedure, before
// the protocols / resources mentioned above being used.
zx_status_t GetCommonProtocolsAndResources();
// Must be called once and only once per driver binding procedure.
//
// `GetCommonProtocolsAndResources()` must be called to acquire sysmem
// protocol before this is called.
zx_status_t InitializeSysmemAllocator();
// Initializes the video output (Vout) hardware submodule of the Video
// Processing Unit. The type of display (MIPI-DSI or HDMI) is based on the
// panel metadata provided by the board driver.
//
// Must be called once and only once per driver binding procedure.
// `vout_` must be already allocated and not yet initialized.
zx_status_t InitializeVout();
// A helper initializing the video output (Vout) hardware submodule for
// MIPI-DSI displays.
//
// `vout_` must be already allocated and not yet initialized.
zx_status_t InitializeMipiDsiVout(display_panel_t panel_info);
// A helper initializing the video output (Vout) hardware submodule for HDMI
// displays.
//
// `vout_` must be already allocated and not yet initialized.
zx_status_t InitializeHdmiVout();
// Starts the Vsync interrupt handler thread.
//
// Must be called once and only once per driver binding procedure.
// `GetCommonProtocolsAndResources()` must be called to acquire the Vsync
// interrupt before this is called.
zx_status_t StartVsyncInterruptHandlerThread();
// Starts the display capture interrupt handler thread.
//
// Must be called once and only once per driver binding procedure.
// `GetCommonProtocolsAndResources()` must be called to acquire the display
// capture interrupt before this is called.
zx_status_t StartDisplayCaptureInterruptHandlerThread();
// Acquires the hotplug display detection hardware resources (GPIO protocol
// and interrupt for the hotplug detection GPIO pin) from parent nodes, and
// starts the interrupt handler thread.
//
// Must be called once and only once per driver binding procedure, if display
// hotplug is supported.
zx_status_t SetupHotplugDisplayDetection();
// This function enables the display hardware. This function is disruptive and causes
// unexpected pixels to be visible on the screen.
zx_status_t DisplayInit() TA_REQ(display_mutex_);
// Power cycle the device and bring up clocks. Only needed when resuming the
// driver, as the bootloader will initialize the display when the machine is
// powered on.
zx_status_t RestartDisplay() TA_REQ(display_mutex_);
bool fully_initialized() const { return full_init_done_.load(std::memory_order_relaxed); }
void set_fully_initialized() { full_init_done_.store(true, std::memory_order_release); }
// Zircon handles
zx::bti bti_;
zx::interrupt inth_;
// Thread handles
std::optional<thrd_t> vsync_thread_;
std::optional<thrd_t> capture_thread_;
// Protocol handles used in by this driver
ddk::PDevFidl pdev_;
fidl::WireSyncClient<fuchsia_hardware_amlogiccanvas::Device> canvas_;
fidl::WireSyncClient<fuchsia_hardware_sysmem::Sysmem> sysmem_;
// Interrupts
zx::interrupt vsync_irq_;
zx::interrupt capture_finished_irq_;
// Locks used by the display driver
fbl::Mutex display_mutex_; // general display state (i.e. display_id)
fbl::Mutex image_mutex_; // used for accessing imported_images_
fbl::Mutex capture_mutex_; // general capture state
// Relaxed is safe because full_init_done_ only ever moves from false to true.
std::atomic<bool> full_init_done_ = false;
// Display controller related data
ddk::DisplayControllerInterfaceProtocolClient dc_intf_ TA_GUARDED(display_mutex_);
// Display Capture interface protocol
ddk::DisplayCaptureInterfaceProtocolClient capture_intf_ TA_GUARDED(capture_mutex_);
// Points to the next capture target image to capture displayed contents into.
// Stores nullptr if capture is not going to be performed.
ImageInfo* current_capture_target_image_ TA_GUARDED(capture_mutex_);
// The sysmem allocator client used to bind incoming buffer collection tokens.
fidl::WireSyncClient<fuchsia_sysmem::Allocator> sysmem_allocator_client_;
// Imported sysmem buffer collections.
std::unordered_map<display::DriverBufferCollectionId,
fidl::WireSyncClient<fuchsia_sysmem::BufferCollection>>
buffer_collections_;
// Imported Images
fbl::DoublyLinkedList<std::unique_ptr<ImageInfo>> imported_images_ TA_GUARDED(image_mutex_);
fbl::DoublyLinkedList<std::unique_ptr<ImageInfo>> imported_captures_ TA_GUARDED(capture_mutex_);
// Objects: only valid if fully_initialized()
std::unique_ptr<Vpu> vpu_;
std::unique_ptr<Osd> osd_;
std::unique_ptr<Vout> vout_;
// Monitoring. We create a named "amlogic-display" node to allow for easier filtering
// of inspect tree when defining selectors and metrics.
inspect::Inspector inspector_;
inspect::Node root_node_;
inspect::Node osd_node_;
display::DisplayId display_id_ = kPanelDisplayId;
bool display_attached_ TA_GUARDED(display_mutex_) = false;
// Hot Plug Detection
fidl::WireSyncClient<fuchsia_hardware_gpio::Gpio> hpd_gpio_;
zx::interrupt hpd_irq_;
std::optional<thrd_t> hpd_thread_;
fit::function<bool(fuchsia_images2::wire::PixelFormat format)> format_support_check_ = nullptr;
};
} // namespace amlogic_display
#endif // SRC_GRAPHICS_DISPLAY_DRIVERS_AMLOGIC_DISPLAY_AMLOGIC_DISPLAY_H_