blob: f44041f8b6d0b52839cfb6a4c9e34d85d5b90847 [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.
#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_