blob: 0d81e6d29ef256f38c4c943277fb31dfe81ca390 [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_POWER_CONTROLLER_H_
#define SRC_GRAPHICS_DISPLAY_DRIVERS_INTEL_I915_POWER_CONTROLLER_H_
#include <lib/mmio/mmio-buffer.h>
#include <lib/zx/result.h>
#include <cstdint>
#include "src/graphics/display/drivers/intel-i915/scoped-value-change.h"
namespace i915 {
// Command sent to the PCU (power controller)'s firmware.
struct PowerControllerCommand {
uint8_t command;
uint8_t param1 = 0;
uint8_t param2 = 0;
uint64_t data;
// The amount of time to wait for the PCU firmware to complete the command.
//
// This time is measured from the moment the command is submitted to the PCU
// firmware via the GT Driver Mailbox. Consequently,
// PowerController::Transact() execution may take longer than this timeout.
// See the method-level comments for details.
//
// If this is zero, the GT Driver Mailbox state will not be consulted at all
// after the command is posted.
int timeout_us;
};
// Memory information reported by the PCU.
//
// Tiger Lake: IHD-OS-TGL-Vol 12-1.22-Rev2.0 pages 212-213
// DG1: IHD-OS-TGL-Vol 12-1.22-Rev2.0 pages 169-170
struct MemorySubsystemInfo {
// Documented values for the `ram_type` field.
enum class RamType {
kDoubleDataRam4 = 0, // DDRAM 4
kDoubleDataRam5 = 1, // DDRAM 5
kLowPowerDoubleDataRam5 = 2, // LPDDRAM5
kLowPowerDoubleDataRam4 = 3, // LPDDRAM4
kDoubleDataRam3 = 4, // DDRAM 3
kLowPowerDoubleDataRam3 = 5, // LPDDRAM3
};
struct GlobalInfo {
RamType ram_type;
int memory_channel_count; // Number of populated DDRAM channels.
int agent_point_count; // Number of enabled QGV points.
// `mailbox_data` should be the mailbox data contents after a successful
// MAILBOX_GTRDIVER_CMD_MEM_SS_INFO_SUBCOMMAND_READ_GLOBAL_INFO command.
static GlobalInfo CreateFromMailboxDataTigerLake(uint64_t mailbox_data);
};
struct AgentPoint {
// DRAM clock, in kHz.
//
// All inter-command latencies below are specified in terms of this clock.
int32_t dram_clock_khz;
// tRP: Latency from a precharge to the next row open.
int16_t row_precharge_to_open_cycles;
// tRCD: Latency from a row access to the next column access.
int16_t row_access_to_column_access_delay_cycles;
// tRDPRE / tRTP: Latency from a read to the next precharge.
int16_t read_to_precharge_cycles;
// tRAS: Latency from a row active to the next row precharge.
int16_t row_activate_to_precharge_cycles;
// `mailbox_data` should be the mailbox data contents after a successful
// MAILBOX_GTRDIVER_CMD_MEM_SS_INFO_SUBCOMMAND_READ_QGV_POINT_INFO command.
static AgentPoint CreateFromMailboxDataTigerLake(uint64_t mailbox_data);
};
GlobalInfo global_info;
static constexpr int kMaxPointCount = 16;
AgentPoint points[kMaxPointCount];
};
// Communicates with the firmware on the PCU (power controller).
//
// The PCU firmware is also called PCODE (power microcontroller microcode) in
// Intel's documentation. The avenue for communication is called the GT Driver
// Mailbox (sometimes abbreviated to "GT Mailbox") in Intel's documentation.
//
// All higher-level commands are built on top of Transact(). See the Transact()
// comments for low-level details on communicating with the PCU firmware.
class PowerController {
public:
// Behavior when the PCU-reported state doesn't match the requested state.
enum class RetryBehavior {
// Issue the state change request once. The caller will recover from
// ZX_ERR_IO_REFUSED errors, which indicate that the current state doesn't
// match the requested state.
kNoRetry = 0,
// Repeat the state change request until the PCU firmware reports that the
// current state matches the request. Give up when it becomes highly likely
// that an external factor is preventing the PCU's current state from
// matching the requested state. The caller cannot recover from
// ZX_ERR_IO_REFUSED errors.
kRetryUntilStateChanges = 1,
};
explicit PowerController(fdf::MmioBuffer* mmio_buffer);
PowerController(const PowerController&) = delete;
PowerController(PowerController&&) = delete;
PowerController& operator=(const PowerController&) = delete;
PowerController& operator=(PowerController&&) = delete;
// Trivially destructible.
~PowerController() = default;
// Performs a command-response exchange with the PCU firmware.
//
// Returns ZX_ERR_IO_MISSED_DEADLINE if a timeout occurs while waiting for the
// PCU firmware. This usually happens if the PCU does not complete `command`
// in time, but can also indicate that the PCU firmware was already performing
// on a different command, and did not become available in a reasonable amount
// of time.
//
// In case of success, returns the 64-bit value in the GT Mailbox Data
// Low/High registers.
//
// Before submitting `command` to the PCU firmware via the GT Mailbox
// registers, this method waits (for quite a while) for any ongoing command to
// finish executing. We adopted this strategy because successful execution of
// PCU commands is usually critical to the driver's operation, so we trade off
// some waiting time in return for maximizing the odds of successful
// execution. The consequence of this approach is that Transact() may take
// more than `command.timeout_us` to complete.
zx::result<uint64_t> Transact(PowerControllerCommand command);
// Informs the PCU of the display engine's voltage requirements.
//
// Returns ZX_ERR_IO_MISSED_DEADLINE if a timeout occurs while communicating
// with the the PCU firmware. This indicates a problem in the PCU firmware. We
// should not make any clocking changes if this happens.
//
// Returns ZX_ERR_IO_REFUSED if the PCU firmware did not set the voltage to
// the requested level. This is an acceptable outcome when `voltage_level` is
// not the maximum level. For example, another consumer (device that shares
// the voltage rail with the display engine) may have requested a higher
// voltage level.
//
// `voltage_level` must be a valid display engine voltage level. All known
// display engines use levels 0-3.
zx::result<> RequestDisplayVoltageLevel(int voltage_level, RetryBehavior retry_behavior);
// Sets the display engine's block TCCOLD (Type C Cold power state) flag.
//
// Returns ZX_ERR_IO_MISSED_DEADLINE if a timeout occurs while communicating
// with the the PCU firmware. This indicates a problem in the PCU firmware. We
// should stop using Type C ports if this happens.
//
// Returns ZX_ERR_IO_REFUSED if the PCU firmware did not bring the Type C
// subsystem into the state implied by the blocking request. This is an
// acceptable outcome when `blocked` is false. For example, the Type C ports
// may be used by another client.
//
// The Type C system must be brought out of the cold power state before
// accessing any registers in the FIA (Flexible IO Adapter) or in the Type C
// PHYs. The cold power state must remain blocked as long as the display
// engine uses any main link or AUX channel in a Type C connector.
//
// This method implements the communication protocol for Tiger Lake's PCU
// firmware. Other processors use different protocols.
zx::result<> SetDisplayTypeCColdBlockingTigerLake(bool blocked, RetryBehavior retry_behavior);
// Sets the display engine's SAGV (System Agent Geyserville) enabled flag.
//
// Returns ZX_ERR_IO_MISSED_DEADLINE if a timeout occurs while communicating
// with the the PCU firmware. This indicates a problem in the PCU firmware. We
// should assume that the SAGV is stuck enabled and configure the display
// engine's pipes and planes accordingly.
//
// Returns ZX_ERR_IO_REFUSED if the PCU firmware did not bring the system
// agent subsystem into the state implied by the enablement request. This is
// an acceptable outcome when `enabled` is true.
//
// This method implements the communication protocol for Kaby Lake and Skylake
// PCUs. The protocol is supported by Tiger Lake PCUs, but has been superseded
// by a more fine-grained version.
zx::result<> SetSystemAgentGeyservilleEnabled(bool enabled, RetryBehavior retry_behavior);
// Reads the SAGV (System Agent Geyserville) blocking time.
//
// Returns the SAGV Block Time, in microseconds.
//
// Returns ZX_ERR_IO_MISSED_DEADLINE if a timeout occurs while communicating
// with the the PCU firmware. Returns ZX_ERR_IO_REFUSED if the PCU firmware
// reports an error. In either case, the display engine's planes cannot be
// used safely.
//
// This method implements the communication protocol for the Tiger Lake PCU.
// The protocol is not supported on Kaby Lake and Skylake PCUs.
zx::result<uint32_t> GetSystemAgentBlockTimeUsTigerLake();
// Reads the SAGV (System Agent Geyserville) blocking time.
//
// Returns the SAGV Block Time, in microseconds.
//
// This method has the same signature as GetSystemAgentBlockTimeUsTigerLake()
// for programming convenience. On Kaby Lake and Skylake PCUs, the SAGV
// blocking time is constant.
zx::result<uint32_t> GetSystemAgentBlockTimeUsKabyLake();
// Reads the PCU's memory latency data.
//
// Returns the raw memory latency data, as it is returned by the PCU firmware.
// Each entry in the returned array represents a memory latency level, in
// microseconds. The data may have to be adjusted based on the display engine
// hardware and on extra information from the memory controller about the
// installed DRAM.
//
// Returns ZX_ERR_IO_MISSED_DEADLINE if a timeout occurs while communicating
// with the the PCU firmware. Returns ZX_ERR_IO_REFUSED if the PCU firmware
// reports an error. In either case, the display engine's planes cannot be
// used safely.
zx::result<std::array<uint8_t, 8>> GetRawMemoryLatencyDataUs();
// Reads MemSS (Memory Subsystem) information from the PCU.
//
// Returns ZX_ERR_IO_MISSED_DEADLINE if a timeout occurs while communicating
// with the the PCU firmware. Returns ZX_ERR_IO_REFUSED if the PCU firmware
// reports an error. In either case, SAGV (System Agent Geyserville) cannot be
// enabled safely.
zx::result<MemorySubsystemInfo> GetMemorySubsystemInfoTigerLake();
// Tests that simulate retries must use the overrides below to avoid flakiness
// stemming from scheduling variability. Tests that simulate timeouts should
// use the overrides below to get the PowerController to issue a deterministic
// MMIO access pattern.
static ScopedValueChange<int> OverridePreviousCommandTimeoutUsForTesting(int timeout_us);
static ScopedValueChange<int> OverrideVoltageLevelRequestReplyTimeoutUsForTesting(int timeout_us);
static ScopedValueChange<int> OverrideVoltageLevelRequestTotalTimeoutUsForTesting(int timeout_us);
static ScopedValueChange<int> OverrideTypeCColdBlockingChangeReplyTimeoutUsForTesting(
int timeout_us);
static ScopedValueChange<int> OverrideTypeCColdBlockingChangeTotalTimeoutUsForTesting(
int timeout_us);
static ScopedValueChange<int> OverrideSystemAgentEnablementChangeReplyTimeoutUsForTesting(
int timeout_us);
static ScopedValueChange<int> OverrideSystemAgentEnablementChangeTotalTimeoutUsForTesting(
int timeout_us);
static ScopedValueChange<int> OverrideGetMemorySubsystemInfoReplyTimeoutUsForTesting(
int timeout_us);
static ScopedValueChange<int> OverrideGetMemoryLatencyReplyTimeoutUsForTesting(int timeout_us);
private:
fdf::MmioBuffer* mmio_buffer_;
};
} // namespace i915
#endif // SRC_GRAPHICS_DISPLAY_DRIVERS_INTEL_I915_POWER_CONTROLLER_H_