| // 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. |
| |
| #ifndef SRC_GRAPHICS_DISPLAY_DRIVERS_INTEL_I915_DP_DISPLAY_H_ |
| #define SRC_GRAPHICS_DISPLAY_DRIVERS_INTEL_I915_DP_DISPLAY_H_ |
| |
| #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 { |
| public: |
| 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> { |
| public: |
| // `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_; } |
| |
| private: |
| 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) |
| __TA_REQUIRES(lock_); |
| 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) |
| __TA_REQUIRES(lock_); |
| |
| zx::result<DdiAuxChannel::ReplyInfo> DoTransaction(const DdiAuxChannel::Request& request, |
| cpp20::span<uint8_t> reply_data_buffer) |
| __TA_REQUIRES(lock_); |
| }; |
| |
| // 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 { |
| public: |
| // Initializes the DPCD capability array with all zeros and the EDP DPCD capabilities as |
| // non-present. |
| DpCapabilities(); |
| |
| // 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 { |
| ZX_ASSERT(address < dpcd::DPCD_SUPPORTED_LINK_RATE_START); |
| 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(edp_dpcd_.has_value()); |
| ZX_ASSERT(address < dpcd::DPCD_EDP_RESERVED); |
| 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; |
| reg.set_reg_value(dpcd_at(A)); |
| return reg; |
| } |
| |
| // Asserts if eDP capabilities are not available. |
| template <typename T, dpcd::EdpRegister A> |
| T edp_dpcd_reg() const { |
| T reg; |
| reg.set_reg_value(edp_dpcd_at(A)); |
| 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 2.2.1.2). |
| 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 |
| // dpcd::DPCD_EDP_BACKLIGHT_BRIGHTNESS_[MSB|LSB] registers. |
| 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_; } |
| |
| private: |
| // DpCapabilities that are only present in eDP displays. |
| struct Edp { |
| 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 { |
| public: |
| 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_; } |
| |
| private: |
| // 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(fxbug.dev/114460): 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 |
| |
| #endif // SRC_GRAPHICS_DISPLAY_DRIVERS_INTEL_I915_DP_DISPLAY_H_ |