blob: 9113352c6fe340798352123979482f7b560bc68d [file] [log] [blame]
// Copyright 2022 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 <fuchsia/hardware/i2cimpl/cpp/banjo.h>
#include <lib/inspect/cpp/inspect.h>
#include <lib/zx/result.h>
#include <threads.h>
#include <zircon/compiler.h>
#include <zircon/types.h>
#include <cstdint>
#include "src/graphics/display/drivers/intel-i915/ddi-aux-channel.h"
#include "src/graphics/display/drivers/intel-i915/ddi-physical-layer.h"
#include "src/graphics/display/drivers/intel-i915/display-device.h"
#include "src/graphics/display/drivers/intel-i915/dpcd.h"
#include "src/graphics/display/drivers/intel-i915/pch-engine.h"
#include "src/graphics/display/lib/api-types-cpp/display-id.h"
namespace i915 {
// Abstraction over the DPCD register transactions that are performed over the DisplayPort Auxiliary
// channel.
class DpcdChannel {
virtual ~DpcdChannel() = default;
virtual ddk::I2cImplProtocolClient i2c() = 0;
virtual bool DpcdRead(uint32_t addr, uint8_t* buf, size_t size) = 0;
virtual bool DpcdWrite(uint32_t addr, const uint8_t* buf, size_t size) = 0;
class DpAux : public DpcdChannel, public ddk::I2cImplProtocol<DpAux> {
// `mmio_buffer` must outlive this instance.
DpAux(fdf::MmioBuffer* mmio_buffer, DdiId ddi_id, uint16_t device_id);
zx_status_t I2cImplGetMaxTransferSize(uint64_t* out_size);
zx_status_t I2cImplSetBitrate(uint32_t bitrate);
zx_status_t I2cImplTransact(const i2c_impl_op_t* ops, size_t count);
// DpcdChannel overrides:
ddk::I2cImplProtocolClient i2c() final;
bool DpcdRead(uint32_t addr, uint8_t* buf, size_t size) final;
bool DpcdWrite(uint32_t addr, const uint8_t* buf, size_t size) final;
// Exposed for configuration logging.
DdiAuxChannel& aux_channel() { return aux_channel_; }
DdiAuxChannel aux_channel_ __TA_GUARDED(lock_);
mtx_t lock_;
zx_status_t DpAuxRead(uint32_t dp_cmd, uint32_t addr, uint8_t* buf, size_t size)
zx_status_t DpAuxReadChunk(uint32_t dp_cmd, uint32_t addr, uint8_t* buf, uint32_t size_in,
size_t* size_out) __TA_REQUIRES(lock_);
zx_status_t DpAuxWrite(uint32_t dp_cmd, uint32_t addr, const uint8_t* buf, size_t size)
zx::result<DdiAuxChannel::ReplyInfo> DoTransaction(const DdiAuxChannel::Request& request,
cpp20::span<uint8_t> reply_data_buffer)
// DpCapabilities is a utility for reading and storing DisplayPort capabilities
// supported by the display based on a copy of read-only DPCD capability
// registers. Drivers can also use PublishToInspect() to publish the data to
// inspect.
class DpCapabilities final {
// Initializes the DPCD capability array with all zeros and the EDP DPCD capabilities as
// non-present.
// Allow copy.
DpCapabilities(const DpCapabilities&) = default;
DpCapabilities& operator=(const DpCapabilities&) = default;
// Allow move.
DpCapabilities(DpCapabilities&&) = default;
DpCapabilities& operator=(DpCapabilities&&) = default;
// Read and parse DPCD capabilities. Clears any previously initialized content
static fpromise::result<DpCapabilities> Read(DpcdChannel* dp_aux);
// Publish the capabilities fields to inspect node `caps_node`.
void PublishToInspect(inspect::Node* caps_node) const;
// Get the cached value of a DPCD register using its DPCD address.
uint8_t dpcd_at(dpcd::Register address) const {
return dpcd_[address - dpcd::DPCD_CAP_START];
// Get the cached value of a EDP DPCD register using its address. Asserts if the eDP capabilities
// are not available.
uint8_t edp_dpcd_at(dpcd::EdpRegister address) const {
ZX_ASSERT(address >= dpcd::DPCD_EDP_CAP_START);
return edp_dpcd_->bytes[address - dpcd::DPCD_EDP_CAP_START];
template <typename T, dpcd::Register A>
T dpcd_reg() const {
T reg;
return reg;
// Asserts if eDP capabilities are not available.
template <typename T, dpcd::EdpRegister A>
T edp_dpcd_reg() const {
T reg;
return reg;
dpcd::Revision dpcd_revision() const {
return static_cast<dpcd::Revision>(dpcd_[dpcd::DPCD_REV]);
std::optional<dpcd::EdpRevision> edp_revision() const {
if (edp_dpcd_) {
return edp_dpcd_->revision;
return std::nullopt;
// Total number of stream sinks within this Sink device.
size_t sink_count() const { return sink_count_.count(); }
// Maximum number of DisplayPort lanes.
uint8_t max_lane_count() const { return max_lane_count_.lane_count_set(); }
// True for SST mode displays that support the Enhanced Framing symbol sequence (see DP v1.4a
// Section
bool enhanced_frame_capability() const { return max_lane_count_.enhanced_frame_enabled(); }
// True for eDP displays that support the `backlight_enable` bit in the
// dpcd::DPCD_EDP_DISPLAY_CTRL register (see dpcd.h).
bool backlight_aux_power() const { return edp_dpcd_ && edp_dpcd_->backlight_aux_power; }
// True for eDP displays that support backlight adjustment through the
bool backlight_aux_brightness() const { return edp_dpcd_ && edp_dpcd_->backlight_aux_brightness; }
// The list of supported link rates in ascending order, measured in units of Mbps/lane.
const std::vector<uint32_t>& supported_link_rates_mbps() const {
return supported_link_rates_mbps_;
// True if the contents of vector returned by `supported_link_rates_mbps()` was populated using
// the "Link Rate Table" method. If true, the link rate must be selected by writing the vector
// index to the DPCD LINK_RATE_SET register. Otherwise, the selected link rate must be programmed
// using the DPCD LINK_BW_SET register.
bool use_link_rate_table() const { return use_link_rate_table_; }
// DpCapabilities that are only present in eDP displays.
struct Edp {
std::array<uint8_t, dpcd::DPCD_EDP_RESERVED - dpcd::DPCD_EDP_CAP_START> bytes;
dpcd::EdpRevision revision;
bool backlight_aux_power = false;
bool backlight_aux_brightness = false;
bool ProcessEdp(DpcdChannel* dp_aux);
bool ProcessSupportedLinkRates(DpcdChannel* dp_aux);
std::array<uint8_t, dpcd::DPCD_SUPPORTED_LINK_RATE_START - dpcd::DPCD_CAP_START> dpcd_;
dpcd::SinkCount sink_count_;
dpcd::LaneCount max_lane_count_;
std::vector<uint32_t> supported_link_rates_mbps_;
bool use_link_rate_table_ = false;
std::optional<Edp> edp_dpcd_;
class DpDisplay : public DisplayDevice {
DpDisplay(Controller* controller, display::DisplayId id, DdiId ddi_id, DpcdChannel* dp_aux,
PchEngine* pch_engine, DdiReference ddi_reference, inspect::Node* parent_node);
DpDisplay(const DpDisplay&) = delete;
DpDisplay(DpDisplay&&) = delete;
DpDisplay& operator=(const DpDisplay&) = delete;
DpDisplay& operator=(DpDisplay&&) = delete;
~DpDisplay() override;
// Gets the backlight brightness as a coefficient on the maximum brightness,
// between the minimum brightness and 1.
double GetBacklightBrightness();
// DisplayDevice overrides:
bool Query() final;
bool InitWithDdiPllConfig(const DdiPllConfig& pll_config) final;
uint8_t lane_count() const { return dp_lane_count_; }
uint32_t link_rate_mhz() const { return dp_link_rate_mhz_; }
// DisplayDevice overrides:
bool InitDdi() final;
bool DdiModeset(const display_mode_t& mode) final;
bool PipeConfigPreamble(const display_mode_t& mode, PipeId pipe_id,
TranscoderId transcoder_id) final;
bool PipeConfigEpilogue(const display_mode_t& mode, PipeId pipe_id,
TranscoderId transcoder_id) final;
DdiPllConfig ComputeDdiPllConfig(int32_t pixel_clock_10khz) final;
uint32_t LoadClockRateForTranscoder(TranscoderId transcoder_id) final;
bool CheckPixelRate(uint64_t pixel_rate) final;
ddk::I2cImplProtocolClient i2c() final { return i2c_; }
// Returns true if the eDP panel is powered on.
// This method performs any configuration and power sequencing needed to get
// the eDP panel powered on, which may include waiting for a significant
// amount of time.
// This method returns fairly quickly if the panel is already configured and
// powered on. It is almost idempotent, modulo the panel changing power
// states independently.
bool EnsureEdpPanelIsPoweredOn();
bool DpcdWrite(uint32_t addr, const uint8_t* buf, size_t size);
bool DpcdRead(uint32_t addr, uint8_t* buf, size_t size);
bool DpcdRequestLinkTraining(const dpcd::TrainingPatternSet& tp_set,
const dpcd::TrainingLaneSet lanes[]);
bool DpcdUpdateLinkTraining(const dpcd::TrainingLaneSet lanes[]);
template <uint32_t addr, typename T>
bool DpcdReadPairedRegs(hwreg::RegisterBase<T, typename T::ValueType>* status);
bool DpcdHandleAdjustRequest(dpcd::TrainingLaneSet* training, dpcd::AdjustRequestLane* adjust);
bool ProgramDpModeTigerLake();
bool DoLinkTraining();
// TODO( Move voltage swing configuration logic to a
// DDI-specific class.
void ConfigureVoltageSwingKabyLake(size_t phy_config_index);
void ConfigureVoltageSwingTigerLake(size_t phy_config_index);
void ConfigureVoltageSwingTypeCTigerLake(size_t phy_config_index);
void ConfigureVoltageSwingComboTigerLake(size_t phy_config_index);
bool LinkTrainingSetupKabyLake();
bool LinkTrainingSetupTigerLake();
// For locking Clock Recovery Circuit of the DisplayPort receiver
bool LinkTrainingStage1(dpcd::TrainingPatternSet* tp_set, dpcd::TrainingLaneSet* lanes);
// For optimizing equalization, determining symbol boundary, and achieving inter-lane alignment
bool LinkTrainingStage2(dpcd::TrainingPatternSet* tp_set, dpcd::TrainingLaneSet* lanes);
bool SetBacklightOn(bool on);
bool InitBacklightHw() override;
bool IsBacklightOn();
// Sets the backlight brightness with |val| as a coefficient on the maximum
// brightness. |val| must be in [0, 1]. If the panel has a minimum fractional
// brightness, then |val| will be clamped to [min, 1].
bool SetBacklightBrightness(double val);
bool HandleHotplug(bool long_pulse) override;
bool HasBacklight() override;
zx::result<> SetBacklightState(bool power, double brightness) override;
zx::result<FidlBacklight::wire::State> GetBacklightState() override;
void SetLinkRate(uint32_t value);
// The object referenced by this pointer must outlive the DpDisplay.
DpcdChannel* dp_aux_; // weak
// Used by eDP displays.
PchEngine* pch_engine_;
// Contains a value only if successfully initialized via Query().
std::optional<DpCapabilities> capabilities_;
// The current lane count and link rate. 0 if invalid/uninitialized.
uint8_t dp_lane_count_ = 0;
// The current per-lane link rate configuration. Use SetLinkRate to mutate the value which also
// updates the related inspect properties.
// These values can be initialized by:
// 1. InitWithDdiPllConfig based on an the current DPLL state
// 2. Init, which selects the highest supported link rate
// The lane count is always initialized to the maximum value that the device can support in
// Query().
uint32_t dp_link_rate_mhz_ = 0;
std::optional<uint8_t> dp_link_rate_table_idx_;
// The backlight brightness coefficient, in the range [min brightness, 1].
double backlight_brightness_ = 1.0f;
const ddk::I2cImplProtocolClient i2c_;
// Debug
inspect::Node inspect_node_;
inspect::Node dp_capabilities_node_;
inspect::UintProperty dp_lane_count_inspect_;
inspect::UintProperty dp_link_rate_mhz_inspect_;
} // namespace i915