blob: 7839f7875c1d3c25e30e5e3b46ef0f822e9ea7a1 [file] [log] [blame]
// Copyright 2017 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_INTEL_I915_INTEL_I915_H_
#define SRC_GRAPHICS_DISPLAY_DRIVERS_INTEL_I915_INTEL_I915_H_
#include <fuchsia/hardware/display/controller/cpp/banjo.h>
#include <fuchsia/hardware/i2cimpl/c/banjo.h>
#include <fuchsia/hardware/intelgpucore/cpp/banjo.h>
#include <fuchsia/hardware/pci/c/banjo.h>
#include <fuchsia/hardware/sysmem/c/banjo.h>
#include <lib/inspect/cpp/inspect.h>
#include <lib/mmio/mmio.h>
#include <lib/zx/channel.h>
#include <threads.h>
#include <memory>
#include <optional>
#include <fbl/vector.h>
#include "src/graphics/display/drivers/intel-i915/display-device.h"
#include "src/graphics/display/drivers/intel-i915/dp-display.h"
#include "src/graphics/display/drivers/intel-i915/dpll.h"
#include "src/graphics/display/drivers/intel-i915/gtt.h"
#include "src/graphics/display/drivers/intel-i915/hdmi-display.h"
#include "src/graphics/display/drivers/intel-i915/igd.h"
#include "src/graphics/display/drivers/intel-i915/interrupts.h"
#include "src/graphics/display/drivers/intel-i915/pipe.h"
#include "src/graphics/display/drivers/intel-i915/power.h"
#include "src/graphics/display/drivers/intel-i915/registers-ddi.h"
#include "src/graphics/display/drivers/intel-i915/registers-dpll.h"
#include "src/graphics/display/drivers/intel-i915/registers-pipe.h"
#include "src/graphics/display/drivers/intel-i915/registers-transcoder.h"
#include "src/graphics/display/drivers/intel-i915/registers.h"
namespace i915 {
typedef struct buffer_allocation {
uint16_t start;
uint16_t end;
} buffer_allocation_t;
class Controller;
using DeviceType = ddk::Device<Controller, ddk::Initializable, ddk::Unbindable, ddk::Suspendable,
ddk::Resumable, ddk::GetProtocolable, ddk::ChildPreReleaseable>;
class Controller : public DeviceType,
public ddk::DisplayControllerImplProtocol<Controller, ddk::base_protocol>,
public ddk::IntelGpuCoreProtocol<Controller> {
public:
explicit Controller(zx_device_t* parent);
~Controller();
// Perform short-running initialization of all subcomponents and instruct the DDK to publish the
// device. On success, returns ZX_OK and the owernship of the Controller instance is claimed by
// the DDK.
//
// Long-running initialization is performed in the DdkInit hook.
static zx_status_t Create(zx_device_t* parent);
// DDK ops
void DdkInit(ddk::InitTxn txn);
void DdkUnbind(ddk::UnbindTxn txn);
void DdkRelease();
zx_status_t DdkGetProtocol(uint32_t proto_id, void* out);
void DdkSuspend(ddk::SuspendTxn txn);
void DdkResume(ddk::ResumeTxn txn);
void DdkChildPreRelease(void* child_ctx) {
fbl::AutoLock lock(&display_lock_);
if (dc_intf_.is_valid()) {
display_controller_interface_protocol_t proto;
dc_intf_.GetProto(&proto);
if (proto.ctx == child_ctx) {
dc_intf_ = ddk::DisplayControllerInterfaceProtocolClient();
}
}
}
// display controller protocol ops
void DisplayControllerImplSetDisplayControllerInterface(
const display_controller_interface_protocol* intf);
zx_status_t DisplayControllerImplImportImage(image_t* image, zx_unowned_handle_t handle,
uint32_t index);
void DisplayControllerImplReleaseImage(image_t* image);
uint32_t DisplayControllerImplCheckConfiguration(const display_config_t** display_config,
size_t display_count,
uint32_t** layer_cfg_result,
size_t* layer_cfg_result_count);
void DisplayControllerImplApplyConfiguration(const display_config_t** display_config,
size_t display_count,
const config_stamp_t* config_stamp);
void DisplayControllerImplSetEld(uint64_t display_id, const uint8_t* raw_eld_list,
size_t raw_eld_count);
zx_status_t DisplayControllerImplGetSysmemConnection(zx::channel connection);
zx_status_t DisplayControllerImplSetBufferCollectionConstraints(const image_t* config,
uint32_t collection);
zx_status_t DisplayControllerImplGetSingleBufferFramebuffer(zx::vmo* out_vmo,
uint32_t* out_stride) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t DisplayControllerImplSetDisplayPower(uint64_t display_id, bool power_on) {
return ZX_ERR_NOT_SUPPORTED;
}
// gpu core ops
zx_status_t IntelGpuCoreReadPciConfig16(uint16_t addr, uint16_t* value_out);
zx_status_t IntelGpuCoreMapPciMmio(uint32_t pci_bar, uint8_t** addr_out, uint64_t* size_out);
zx_status_t IntelGpuCoreUnmapPciMmio(uint32_t pci_bar);
zx_status_t IntelGpuCoreGetPciBti(uint32_t index, zx::bti* bti_out);
zx_status_t IntelGpuCoreRegisterInterruptCallback(const intel_gpu_core_interrupt_t* callback,
uint32_t interrupt_mask);
zx_status_t IntelGpuCoreUnregisterInterruptCallback();
uint64_t IntelGpuCoreGttGetSize();
zx_status_t IntelGpuCoreGttAlloc(uint64_t page_count, uint64_t* addr_out);
zx_status_t IntelGpuCoreGttFree(uint64_t addr);
zx_status_t IntelGpuCoreGttClear(uint64_t addr);
zx_status_t IntelGpuCoreGttInsert(uint64_t addr, zx::vmo buffer, uint64_t page_offset,
uint64_t page_count);
void GpuRelease();
// i2c ops
uint32_t GetBusCount();
zx_status_t GetMaxTransferSize(uint32_t bus_id, size_t* out_size);
zx_status_t SetBitrate(uint32_t bus_id, uint32_t bitrate);
zx_status_t Transact(uint32_t bus_id, const i2c_impl_op_t* ops, size_t count);
fdf::MmioBuffer* mmio_space() { return mmio_space_.has_value() ? &*mmio_space_ : nullptr; }
Interrupts* interrupts() { return &interrupts_; }
uint16_t device_id() const { return device_id_; }
const IgdOpRegion& igd_opregion() const { return igd_opregion_; }
Power* power() { return power_.get(); }
DisplayPllManager* dpll_manager() { return dpll_manager_.get(); }
// Non-const getter to allow unit tests to modify the IGD.
// TODO(fxbug.dev/83998): Consider making a fake IGD object injectable as allowing mutable access
// to internal state that is intended to be externally immutable can be source of bugs if used
// incorrectly. The various "ForTesting" methods are a typical anti-pattern that exposes internal
// state and makes the class state machine harder to reason about.
IgdOpRegion* igd_opregion_for_testing() { return &igd_opregion_; }
void HandleHotplug(registers::Ddi ddi, bool long_pulse);
void HandlePipeVsync(registers::Pipe pipe, zx_time_t timestamp);
void ResetPipe(registers::Pipe pipe) __TA_NO_THREAD_SAFETY_ANALYSIS;
bool ResetTrans(registers::Trans trans);
bool ResetDdi(registers::Ddi ddi);
void SetDpllManagerForTesting(std::unique_ptr<DisplayPllManager> dpll_manager) {
dpll_manager_ = std::move(dpll_manager);
}
void SetPowerWellForTesting(std::unique_ptr<Power> power_well) { power_ = std::move(power_well); }
void SetMmioForTesting(fdf::MmioBuffer mmio_space) { mmio_space_ = std::move(mmio_space); }
void ResetMmioSpaceForTesting() { mmio_space_.reset(); }
// For every frame, in order to use the imported image, it is required to set
// up the image based on given rotation in GTT and use the handle offset in
// GTT. Returns the image base address used for display registers.
uint64_t SetupGttImage(const image_t* image, uint32_t rotation);
private:
// Perform short-running initialization of all subcomponents and instruct the DDK to publish the
// device. On success, returns ZX_OK and the ownership of the Controller instance is claimed by
// the DDK.
//
// Long-running initialization is performed in the DdkInit hook.
zx_status_t Init();
const std::unique_ptr<GttRegion>& GetGttRegion(uint64_t handle);
void InitDisplays();
std::unique_ptr<DisplayDevice> QueryDisplay(registers::Ddi ddi) __TA_REQUIRES(display_lock_);
bool LoadHardwareState(registers::Ddi ddi, DisplayDevice* device) __TA_REQUIRES(display_lock_);
zx_status_t AddDisplay(std::unique_ptr<DisplayDevice> display) __TA_REQUIRES(display_lock_);
void RemoveDisplay(std::unique_ptr<DisplayDevice> display) __TA_REQUIRES(display_lock_);
bool BringUpDisplayEngine(bool resume) __TA_REQUIRES(display_lock_);
void InitDisplayBuffers();
DisplayDevice* FindDevice(uint64_t display_id) __TA_REQUIRES(display_lock_);
void CallOnDisplaysChanged(DisplayDevice** added, size_t added_count, uint64_t* removed,
size_t removed_count) __TA_REQUIRES(display_lock_);
// Gets the layer_t* config for the given pipe/plane. Return false if there is no layer.
bool GetPlaneLayer(registers::Pipe pipe, uint32_t plane, const display_config_t** configs,
size_t display_count, const layer_t** layer_out) __TA_REQUIRES(display_lock_);
uint16_t CalculateBuffersPerPipe(size_t display_count);
// Returns false if no allocation is possible. When that happens,
// plane 0 of the failing displays will be set to UINT16_MAX.
bool CalculateMinimumAllocations(
const display_config_t** display_configs, size_t display_count,
uint16_t min_allocs[registers::kPipeCount][registers::kImagePlaneCount])
__TA_REQUIRES(display_lock_);
// Updates plane_buffers_ based pipe_buffers_ and the given parameters
void UpdateAllocations(
const uint16_t min_allocs[registers::kPipeCount][registers::kImagePlaneCount],
const uint64_t display_rate[registers::kPipeCount][registers::kImagePlaneCount])
__TA_REQUIRES(display_lock_);
// Reallocates the pipe buffers when a pipe comes online/goes offline. This is a
// long-running operation, as shifting allocations between pipes requires waiting
// for vsync.
void DoPipeBufferReallocation(buffer_allocation_t active_allocation[registers::kPipeCount])
__TA_REQUIRES(display_lock_);
// Reallocates plane buffers based on the given layer config.
void ReallocatePlaneBuffers(const display_config_t** display_configs, size_t display_count,
bool reallocate_pipes) __TA_REQUIRES(display_lock_);
// Validates that a basic layer configuration can be supported for the
// given modes of the displays.
bool CheckDisplayLimits(const display_config_t** display_configs, size_t display_count,
uint32_t** layer_cfg_results) __TA_REQUIRES(display_lock_);
bool CalculatePipeAllocation(const display_config_t** display_config, size_t display_count,
uint64_t alloc[registers::kPipeCount]) __TA_REQUIRES(display_lock_);
bool ReallocatePipes(const display_config_t** display_config, size_t display_count)
__TA_REQUIRES(display_lock_);
zx_device_t* zx_gpu_dev_ = nullptr;
zx_device_t* display_controller_dev_ = nullptr;
bool gpu_released_ = false;
bool display_released_ = false;
sysmem_protocol_t sysmem_;
ddk::DisplayControllerInterfaceProtocolClient dc_intf_ __TA_GUARDED(display_lock_);
bool ready_for_callback_ __TA_GUARDED(display_lock_) = false;
Gtt gtt_ __TA_GUARDED(gtt_lock_);
mtx_t gtt_lock_;
// These regions' VMOs are not owned
fbl::Vector<std::unique_ptr<GttRegion>> imported_images_ __TA_GUARDED(gtt_lock_);
// These regions' VMOs are owned
fbl::Vector<std::unique_ptr<GttRegion>> imported_gtt_regions_ __TA_GUARDED(gtt_lock_);
IgdOpRegion igd_opregion_; // Read only, no locking
Interrupts interrupts_; // Internal locking
pci_protocol_t pci_;
struct {
mmio_buffer_t mmio;
int32_t count = 0;
} mapped_bars_[PCI_MAX_BAR_COUNT] __TA_GUARDED(bar_lock_);
mtx_t bar_lock_;
// The mmio_space_ is read only. The internal registers are guarded by various locks where
// appropriate.
std::optional<fdf::MmioBuffer> mmio_space_;
std::unique_ptr<Power> power_;
// References to displays. References are owned by devmgr, but will always
// be valid while they are in this vector.
fbl::Vector<std::unique_ptr<DisplayDevice>> display_devices_ __TA_GUARDED(display_lock_);
uint64_t next_id_ __TA_GUARDED(display_lock_) = 1; // id can't be INVALID_DISPLAY_ID == 0
mtx_t display_lock_;
fbl::Vector<Pipe> pipes_ __TA_GUARDED(display_lock_);
PowerWellRef cd_clk_power_well_;
std::unique_ptr<DisplayPllManager> dpll_manager_;
cpp20::span<const registers::Ddi> ddis_;
fbl::Vector<GMBusI2c> gmbus_i2cs_;
fbl::Vector<DpAux> dp_auxs_;
// Plane buffer allocation. If no alloc, start == end == registers::PlaneBufCfg::kBufferCount.
buffer_allocation_t plane_buffers_[registers::kPipeCount]
[registers::kImagePlaneCount] __TA_GUARDED(display_lock_) = {};
// Buffer allocations for pipes
buffer_allocation_t pipe_buffers_[registers::kPipeCount] __TA_GUARDED(display_lock_) = {};
bool initial_alloc_ = true;
uint16_t device_id_;
uint32_t flags_;
// Various configuration values set by the BIOS which need to be carried across suspend.
uint32_t pp_divisor_val_;
uint32_t pp_off_delay_val_;
uint32_t pp_on_delay_val_;
uint32_t sblc_ctrl2_val_;
uint32_t schicken1_val_;
bool ddi_a_lane_capability_control_;
bool sblc_polarity_;
std::optional<uint64_t> eld_display_id_;
// Debug
inspect::Inspector inspector_;
inspect::Node root_node_;
};
} // namespace i915
#endif // SRC_GRAPHICS_DISPLAY_DRIVERS_INTEL_I915_INTEL_I915_H_