| // 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_DDI_AUX_CHANNEL_H_ |
| #define SRC_GRAPHICS_DISPLAY_DRIVERS_INTEL_I915_DDI_AUX_CHANNEL_H_ |
| |
| #include <lib/mmio/mmio.h> |
| #include <lib/stdcompat/span.h> |
| #include <lib/zx/result.h> |
| #include <zircon/assert.h> |
| |
| #include <cstdint> |
| |
| #include "src/graphics/display/drivers/intel-i915/registers-ddi.h" |
| |
| namespace i915 { |
| |
| // Low-level channel configuration. |
| struct DdiAuxChannelConfig { |
| // The transaction timeout period. |
| int16_t timeout_us; |
| |
| // Total number of SYNC pulses sent when starting a transaction. |
| // |
| // The number includes the zeros sent as pre-charge pulses and the zeros at |
| // the start of the AUX_SYNC pattern. |
| // |
| // The DisplayPort standard specifies 10-16 pre-charge pulses and 16 |
| // consecutive zeros at the start of the AUX_SYNC pattern. |
| int8_t sync_pulse_count; |
| |
| // Number of SYNC pulses sent when starting a fast-wake transaction. |
| // |
| // The Embedded DisplayPort standard specifies 8 pulses. |
| int8_t fast_wake_sync_pulse_count; |
| |
| // If true, transactions are directed through the Thunderbolt controller. |
| // Otherwise, transactions are directed through the FIA. |
| bool use_thunderbolt; |
| }; |
| |
| // Helper for accessing the DP AUX channel via the DDI registers. |
| // |
| // This class is focused on DDI register management. It has as little knowledge |
| // of the AUX channel protocol as needed to avoid unnecessary copying of the |
| // message data. |
| class DdiAuxChannel { |
| public: |
| // Commands take up a 4-bit field in the request header. |
| static constexpr int32_t kMaxCommand = (1 << 4) - 1; |
| |
| // Addresses take up a 20-bit field in the request header. |
| static constexpr int32_t kMaxAddress = (1 << 20) - 1; |
| |
| // AUX messages store the data length in a byte. However, the DP standard |
| // limits AUX message data to 1-16 bytes, for both requests and replies. |
| static constexpr int8_t kMaxOpSize = 16; |
| |
| // Metadata about a transaction request. |
| struct Request { |
| // The address in the request header. Must be between 0 and `kMaxAddress`. |
| int32_t address; |
| |
| // The command in the request header. Must be between 0 and `kMaxCommand`. |
| int8_t command; |
| |
| // The size of the operation, in bytes. Must be between 1 and `kMaxOpSize`. |
| int8_t op_size; |
| |
| // The data payload attached to the request message. |
| // |
| // Write payloads should have the size stated in `op_size`. Read requests |
| // have empty payloads. |
| cpp20::span<const uint8_t> data; |
| }; |
| |
| // Metadata about a transaction reply. |
| struct ReplyInfo { |
| // The reply header byte. |
| // |
| // DdiAuxChannel methods do not validate this byte. According to the |
| // DisplayPort specification, the upper 4 bits should be the reply command, |
| // and the lower 4 bits should be zero. |
| uint8_t reply_header; |
| |
| // Instances returned by DdiAuxChannel methods are guaranteed to have this |
| // size between 0 and `kMaxOpSize`. |
| int8_t reply_data_size; |
| }; |
| |
| // `mmio_buffer` must outlive this instance. |
| DdiAuxChannel(fdf::MmioBuffer* mmio_buffer, DdiId ddi_id, uint16_t device_id); |
| |
| // No copying. |
| DdiAuxChannel(const DdiAuxChannel&) = delete; |
| DdiAuxChannel& operator=(const DdiAuxChannel&) = delete; |
| |
| // Moving is allowed to facilitate storing in containers. |
| DdiAuxChannel(DdiAuxChannel&&) noexcept = default; |
| DdiAuxChannel& operator=(DdiAuxChannel&&) noexcept = default; |
| |
| // Performs an AUX transaction, exchanging one request and one reply message. |
| // |
| // `reply_data` points to a buffer populated with the data payload of the |
| // reply message. If the buffer is smaller than the reply payload, only the |
| // first `reply_data.size()` bytes of the payload are copied. |
| // |
| // The returned zx::result reflects whether the DDI considers this transaction |
| // successful. If that's the case, the `ReplyInfo` structure has the reply |
| // command byte (which could indicate a NACK or a DEFER), and the size of the |
| // reply payload. |
| zx::result<ReplyInfo> DoTransaction(const Request& request, |
| cpp20::span<uint8_t> reply_data_buffer); |
| |
| // Directs AUX transactions to/away from the Thunderbolt controller. |
| // |
| // This method most only be called on Type C DDIs, to switch between |
| // Thunderbolt connections and Type C (Alt Modes) connections. |
| // |
| // This method must not be called while a transaction is in progress. |
| void SetUseThunderbolt(bool use_thunderbolt); |
| |
| // Reads the configuration in the cached control register. |
| DdiAuxChannelConfig Config() const; |
| |
| // Outputs the current configuration as TRACE entries in the kernel log. |
| void Log(); |
| |
| // Stores an AUX channel request in the DDI's data buffer. |
| // |
| // This is a helper for DoTransaction(). It is only exposed for unit tests. |
| // |
| // After a request is stored in the DDI data buffer using this method, |
| // Transact() should be used to transmit the request to the AUX channel. |
| void WriteRequestForTesting(const Request& request); |
| |
| // Performs an AUX channel transaction, using a populated DDI data buffer. |
| // |
| // This is a helper for DoTransaction(). It is only exposed for unit tests. |
| // |
| // WriteRequest() must be called before this method. |
| // |
| // Returns a status that reflects whether the DDI considers the transaction |
| // successful. If this method reports success, ReadReply() can be called to |
| // retrieve the transaction reply. DDI-level success isn't conditioned on the |
| // reply command, so NACKed and DEFERred transaction will still be considered |
| // successful. |
| zx::result<> TransactForTesting(); |
| |
| // Reads an AUX channel response from the DDI's data buffer. |
| // |
| // This is a helper for DoTransaction(). It is only exposed for unit tests. |
| // |
| // Must only be called after a Transact() call that returns success. |
| // |
| // The response command byte is returned via ReplyInfo. The other response |
| // bytes are copied into `data_buffer`. If the size of `data_buffer` is less |
| // than the response, the buffer is filled with as many response bytes as |
| // possible. |
| // |
| // Callers should pass a non-empty buffer even when performing a writes, so |
| // they can retrieve the partial write size, if the write is NACKed. |
| ReplyInfo ReadReplyForTesting(cpp20::span<uint8_t> data_buffer); |
| |
| private: |
| // The DDI is set to time out after 1,600us (on Kaby Lake and Skylake) or |
| // after 4,000us (on Tiger Lake and DG1). The timeout below gives the DDI a |
| // large margin for reporting the timeout to us. |
| static constexpr int kDdiTransactionTimeoutUs = 10'000; |
| |
| // Returns true if the transaction completes, and false if the wait timed out. |
| bool WaitForTransactionComplete(); |
| |
| // WriteRequest() helpers. |
| void WriteRequestHeader(int8_t command, int32_t address, int8_t op_size); |
| void WriteRequestData(cpp20::span<const uint8_t> data); |
| |
| // Patches up fields in the AUX control reg with obviously incorrect values. |
| // |
| // The fixes apply to the cached version of the AUX control register. The |
| // caller is responsible for issuing a WriteTo() call to the register. |
| void FixConfig(); |
| |
| registers::DdiAuxControl aux_control_; |
| |
| fdf::MmioBuffer* mmio_buffer_; // Non-null. |
| |
| // The value of large timeouts. |
| int16_t large_timeout_us_; |
| |
| #if ZX_DEBUG_ASSERT_IMPLEMENTED |
| // The ZX_DEBUG_ASSERT_IMPLEMENTED block aims to clarify that these members |
| // are only used for consistency checks. It is not intended as a performance |
| // optimization. |
| DdiId ddi_id_; |
| uint16_t device_id_; |
| #else |
| |
| #if defined(ZX_DEBUG_ASSERT_IMPLEMENTED) && defined(ZX_ASSERT_LEVEL) |
| #error "ZX_ASSERT_LEVEL defined but evals to <= 1" |
| #else |
| #error "Else" |
| #endif |
| |
| #endif // ZX_DEBUG_ASSERT_IMPLEMENTED |
| }; |
| |
| } // namespace i915 |
| |
| #endif // SRC_GRAPHICS_DISPLAY_DRIVERS_INTEL_I915_DDI_AUX_CHANNEL_H_ |