blob: 4cac4e68b793be13d9fcfeac3086f11ba5db1744 [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.
#pragma once
#if __cplusplus
#include <ddk/mmio-buffer.h>
#include <ddk/protocol/intelgpucore.h>
#include <ddk/protocol/pci.h>
#include <ddk/protocol/i2cimpl.h>
#include <ddk/protocol/sysmem.h>
#include <lib/mmio/mmio.h>
#include <ddktl/protocol/display/controller.h>
#include <fbl/unique_ptr.h>
#include <fbl/vector.h>
#include <hw/pci.h>
#include <lib/zx/channel.h>
#include <threads.h>
#include <optional>
#include "display-device.h"
#include "dp-display.h"
#include "gtt.h"
#include "igd.h"
#include "interrupts.h"
#include "pipe.h"
#include "power.h"
#include "registers.h"
#include "registers-ddi.h"
#include "registers-dpll.h"
#include "registers-pipe.h"
#include "registers-transcoder.h"
#include "hdmi-display.h"
namespace i915 {
typedef struct buffer_allocation {
uint16_t start;
uint16_t end;
} buffer_allocation_t;
typedef struct dpll_state {
bool is_hdmi;
union {
uint32_t dp_rate;
struct {
uint16_t dco_int;
uint16_t dco_frac;
uint8_t q;
uint8_t q_mode;
uint8_t k;
uint8_t p;
uint8_t cf;
} hdmi;
};
} dpll_state_t;
class Controller;
using DeviceType = ddk::Device<Controller, ddk::Unbindable,
ddk::Suspendable, ddk::Resumable, ddk::GetProtocolable>;
class Controller : public DeviceType,
public ddk::DisplayControllerImplProtocol<Controller, ddk::base_protocol> {
public:
Controller(zx_device_t* parent);
~Controller();
static bool CompareDpllStates(const dpll_state_t& a, const dpll_state_t& b);
// DDK ops
void DdkUnbind();
void DdkRelease();
zx_status_t DdkGetProtocol(uint32_t proto_id, void* out);
zx_status_t DdkSuspend(uint32_t reason);
zx_status_t DdkResume(uint32_t reason);
zx_status_t Bind(fbl::unique_ptr<i915::Controller>* controller_ptr);
// display controller protocol ops
void DisplayControllerImplSetDisplayControllerInterface(
const display_controller_interface_protocol* intf);
zx_status_t DisplayControllerImplImportVmoImage(image_t* image, zx::vmo vmo, size_t offset);
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);
uint32_t DisplayControllerImplComputeLinearStride(uint32_t width, zx_pixel_format_t format);
zx_status_t DisplayControllerImplAllocateVmo(uint64_t size, zx::vmo* vmo_out);
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;
}
// gpu core ops
zx_status_t ReadPciConfig16(uint16_t addr, uint16_t* value_out);
zx_status_t MapPciMmio(uint32_t pci_bar, void** addr_out, uint64_t* size_out);
zx_status_t UnmapPciMmio(uint32_t pci_bar);
zx_status_t GetPciBti(uint32_t index, zx_handle_t* bti_out);
zx_status_t RegisterInterruptCallback(const zx_intel_gpu_core_interrupt_t* callback,
uint32_t interrupt_mask);
zx_status_t UnregisterInterruptCallback();
uint64_t GttGetSize();
zx_status_t GttAlloc(uint64_t page_count, uint64_t* addr_out);
zx_status_t GttFree(uint64_t addr);
zx_status_t GttClear(uint64_t addr);
zx_status_t GttInsert(uint64_t addr, zx_handle_t 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);
bool DpcdRead(registers::Ddi ddi, uint32_t addr, uint8_t* buf, size_t size);
bool DpcdWrite(registers::Ddi ddi, uint32_t addr, const uint8_t* buf, size_t size);
pci_protocol_t* pci() { return &pci_; }
ddk::MmioBuffer* mmio_space() { return &*mmio_space_; }
Gtt* gtt() { return &gtt_; }
Interrupts* interrupts() { return &interrupts_; }
uint16_t device_id() const { return device_id_; }
const IgdOpRegion& igd_opregion() const { return igd_opregion_; }
Power* power() { return &power_; }
void HandleHotplug(registers::Ddi ddi, bool long_pulse);
void HandlePipeVsync(registers::Pipe pipe, zx_time_t timestamp);
void FinishInit();
void ResetPipe(registers::Pipe pipe) __TA_NO_THREAD_SAFETY_ANALYSIS;
bool ResetTrans(registers::Trans trans);
bool ResetDdi(registers::Ddi ddi);
const fbl::unique_ptr<GttRegion>& GetGttRegion(uint64_t handle);
registers::Dpll SelectDpll(bool is_edp, const dpll_state_t& state);
const dpll_state_t* GetDpllState(registers::Dpll dpll);
private:
void EnableBacklight(bool enable);
void InitDisplays();
fbl::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(fbl::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;
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<fbl::unique_ptr<GttRegion>> imported_images_ __TA_GUARDED(gtt_lock_);
// These regions' VMOs are owned
fbl::Vector<fbl::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<ddk::MmioBuffer> mmio_space_;
// References to displays. References are owned by devmgr, but will always
// be valid while they are in this vector.
fbl::Vector<fbl::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_;
Pipe pipes_[registers::kPipeCount] __TA_GUARDED(display_lock_) = {
Pipe(this, registers::PIPE_A), Pipe(this, registers::PIPE_B), Pipe(this, registers::PIPE_C)
};
Power power_;
PowerWellRef cd_clk_power_well_;
struct {
uint8_t use_count = 0;
dpll_state_t state;
} dplls_[registers::kDpllCount] = {};
GMBusI2c gmbus_i2cs_[registers::kDdiCount] = {
GMBusI2c(registers::DDI_A), GMBusI2c(registers::DDI_B), GMBusI2c(registers::DDI_C),
GMBusI2c(registers::DDI_D), GMBusI2c(registers::DDI_E),
};
DpAux dp_auxs_[registers::kDdiCount] = {
DpAux(registers::DDI_A), DpAux(registers::DDI_B), DpAux(registers::DDI_C),
DpAux(registers::DDI_D), DpAux(registers::DDI_E),
};
// 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_;
bool init_thrd_started_ = false;
thrd_t init_thread_;
};
} // namespace i915
#endif // __cplusplus
__BEGIN_CDECLS
zx_status_t intel_i915_bind(void* ctx, zx_device_t* parent);
__END_CDECLS