blob: 4107f2d2af72538264c2a9920b8c8b8d2a474c3b [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_REGISTERS_TRANSCODER_H_
#define SRC_GRAPHICS_DISPLAY_DRIVERS_INTEL_I915_REGISTERS_TRANSCODER_H_
#include <zircon/assert.h>
#include <cstdint>
#include <optional>
#include <hwreg/bitfields.h>
#include "src/graphics/display/drivers/intel-i915/hardware-common.h"
namespace registers {
// TRANS_HTOTAL, TRANS_HBLANK,
// TRANS_VTOTAL, TRANS_VBLANK
class TransHVTotal : public hwreg::RegisterBase<TransHVTotal, uint32_t> {
public:
DEF_FIELD(29, 16, count_total); // same as blank_end
DEF_FIELD(13, 0, count_active); // same as blank_start
};
// TRANS_HSYNC, TRANS_VSYNC
class TransHVSync : public hwreg::RegisterBase<TransHVSync, uint32_t> {
public:
DEF_FIELD(29, 16, sync_end);
DEF_FIELD(13, 0, sync_start);
};
// TRANS_VSYNCSHIFT
class TransVSyncShift : public hwreg::RegisterBase<TransVSyncShift, uint32_t> {
public:
DEF_FIELD(12, 0, second_field_vsync_shift);
};
// TRANS_DDI_FUNC_CTL (Transcoder DDI Function Control)
//
// This register has reserved bits that are not documented as MBZ (must be
// zero), so it should be accessed using read-modify-write.
//
// Tiger Lake: IHD-OS-TGL-Vol 2c-1.22-Rev2.0 Part 2 pages 1370-1375
// Kaby Lake: IHD-OS-KBL-Vol 2c-1.17 Part 2 pages 952-956
// Skylake: IHD-OS-SKL-Vol 2c-05.16 Part 2 pages 926-930
class TranscoderDdiControl : public hwreg::RegisterBase<TranscoderDdiControl, uint32_t> {
public:
// Enables the transcoder's DDI functionality.
DEF_BIT(31, enabled);
// Selects the DDI that the transcoder will connect to.
//
// This field has a non-trivial value encoding. The ddi_*() and set_ddi_*()
// helpers should be preferred to accessing the field directly.
//
// This field is tagged `_subtle` because the definition matches the bits used
// on Tiger Lake, but it's used on all supported models. Kaby Lake and Skylake
// have a very similar field, which only takes up bits 30-28. Fortunately, bit
// 27 is reserved MBZ (must be zero). So, there's still a 1:1 mapping between
// DDI selection and the values of bits 30-27.
//
// We take advantage of this to avoid forking the entire (fairly large)
// register definition by papering over this difference in the helpers
// `ddi_kaby_lake()` and `set_ddi_kaby_lake()`.
DEF_FIELD(30, 27, ddi_select_subtle);
// The DDI that the transcoder will connect to.
//
// This helper works for Kaby Lake and Skylake.
//
// This field must not be changed while `enabled` is true. Directing multiple
// transcoders to the same DDI is only valid for DisplayPort Multi-Streaming.
//
// The underlying field is ignored by the EDP transcoder, which is attached to
// DDI A.
std::optional<i915::DdiId> ddi_kaby_lake() const {
// The cast is lossless because `ddi_select_subtle()` is a 4-bit field.
const int8_t ddi_select_raw_value = static_cast<int8_t>(ddi_select_subtle());
if (ddi_select_raw_value == 0) {
return std::nullopt;
}
// Convert from the Tiger Lake field representation.
const int ddi_index = (ddi_select_raw_value >> 1);
return static_cast<i915::DdiId>(ddi_index);
}
// The DDI that the transcoder will connect to.
//
// This helper works for Tiger Lake.
//
// This field must not be changed while `enabled` is true. Directing multiple
// transcoders to the same DDI is only valid for DisplayPort Multi-Streaming.
//
// The underlying field is ignored by the DSI transcoders. Each DSI transcoder
// is attached to a DDI.
std::optional<i915::DdiId> ddi_tiger_lake() const {
// The cast is lossless because `ddi_select_subtle()` is a 4-bit field.
const int8_t ddi_select_raw_value = static_cast<int8_t>(ddi_select_subtle());
if (ddi_select_raw_value == 0) {
return std::nullopt;
}
const int ddi_index = ddi_select_raw_value - 1;
return static_cast<i915::DdiId>(ddi_index);
}
// See `ddi_kaby_lake()` for details.
TranscoderDdiControl& set_ddi_kaby_lake(std::optional<i915::DdiId> ddi_id) {
if (!ddi_id.has_value()) {
return set_ddi_select_subtle(0);
}
ZX_DEBUG_ASSERT_MSG(*ddi_id != i915::DdiId::DDI_A,
"DDI A cannot be explicitly connected to a transcoder");
const int ddi_index = *ddi_id - i915::DdiId::DDI_A;
// Convert to the Tiger Lake field representation.
return set_ddi_select_subtle(ddi_index << 1);
}
// See `ddi_tiger_lake()` for details.
TranscoderDdiControl& set_ddi_tiger_lake(std::optional<i915::DdiId> ddi_id) {
if (!ddi_id.has_value()) {
return set_ddi_select_subtle(0);
}
const int ddi_index = *ddi_id - i915::DdiId::DDI_A;
return set_ddi_select_subtle(ddi_index + 1);
}
// The transcoder's mode of operation.
//
// This field must not be changed while `enabled` is true.
//
// This field must be changed in the same MMIO write as the
// `display_port_transport_tiger_lake` field.
//
// In HDMI mode, the transcoder sends a null packet (32 zero bytes) when
// Vsync is asserted. The transcoder also sends a preamble and guardband
// before each null packet. These behaviors match the HDMI specification.
//
// In DVI mode, enabling DIP (Data Island Packets) or audio causes the
// transcoder to adopt the HDMI behavior described above.
//
// DisplayPort modes SST (Single Stream) or MST (Multi-Stream) must match the
// mode selected in the `DpTransportControl` register.
//
// On Tiger Lake, the DSI transcoders ignore this field.
//
// On Kaby Lake, transcoder EDP (and therefore DDI A) must be in the
// DisplayPort SST (Single Stream) mode.
DEF_FIELD(26, 24, ddi_mode);
// TODO(https://fxbug.dev/42062082): Move the constants below into an enum class once we
// figure out how to handle invalid field values.
static constexpr uint32_t kModeHdmi = 0;
static constexpr uint32_t kModeDvi = 1;
static constexpr uint32_t kModeDisplayPortSingleStream = 2;
static constexpr uint32_t kModeDisplayPortMultiStream = 3;
// Selects the bpc (number of bits per color) output on the connected DDI.
//
// This field must not be changed while `enabled` is true.
//
// HDMI and DSC (Display Stream Compression) don't support 6bpc.
//
// On Tiger Lake, the DSI transcoder ignores this field, and uses the pixel
// format in the TRANS_DSI_FUNC_CONF register.
DEF_FIELD(22, 20, bits_per_color);
// TODO(https://fxbug.dev/42062082): Move the constants below into an enum class once we
// figure out how to handle invalid field values.
static constexpr uint32_t k8bpc = 0;
static constexpr uint32_t k10bpc = 1;
static constexpr uint32_t k6bpc = 2;
static constexpr uint32_t k12bpc = 3;
// When operating as a port sync secondary, selects the primary transcoder.
//
// This field has a non-trivial value encoding. The
// `port_sync_primary_transcoder_kaby_lake()` and
// `set_port_sync_primary_transcoder_kaby_lake()` helpers should be preferred
// to accessing the field directly.
DEF_FIELD(19, 18, port_sync_primary_transcoder_select_kaby_lake);
// When operating as a port sync secondary, selects the primary transcoder.
//
// This field is ignored by the EDP transcoder, because it cannot function as
// a port sync secondary.
//
// This field's bits are reserved MBZ (must be zero) on Tiger Lake. The field
// was moved to the TRANS_DDI_FUNC_CTL2 register and widened.
i915::TranscoderId port_sync_primary_transcoder_kaby_lake() const {
// The cast is lossless because `port_sync_primary_select_kaby_lake()` is a
// 2-bit field.
const int8_t raw_port_sync_primary_select =
static_cast<int8_t>(port_sync_primary_transcoder_select_kaby_lake());
if (raw_port_sync_primary_select == 0) {
return i915::TranscoderId::TRANSCODER_EDP;
}
// The subtraction result is non-negative, because we checked for zero
// above. The addition will not overflow because
// `port_sync_primary_select_kaby_lake()` is a 2-bit field.
return static_cast<i915::TranscoderId>(i915::TranscoderId::TRANSCODER_A +
(raw_port_sync_primary_select - 1));
}
// See `port_sync_primary_kaby_lake()`.
TranscoderDdiControl& set_port_sync_primary_kaby_lake(i915::TranscoderId transcoder_id) {
if (transcoder_id == i915::TranscoderId::TRANSCODER_EDP) {
return set_port_sync_primary_transcoder_select_kaby_lake(0);
}
ZX_ASSERT(transcoder_id >= i915::TranscoderId::TRANSCODER_A);
ZX_ASSERT(transcoder_id <= i915::TranscoderId::TRANSCODER_C);
return set_port_sync_primary_transcoder_select_kaby_lake(
(transcoder_id - i915::TranscoderId::TRANSCODER_A) + 1);
}
// If true, VSync is active high. If false, VSync is active low.
//
// On Tiger Lake, the DSI transcoders ignore this field.
//
// Active high is the default, and considered the standard polarity. Active
// low is considered an inverted polarity.
DEF_BIT(17, vsync_polarity_not_inverted);
// If true, HSync is active high. If false, HSync is active low.
//
// On Tiger Lake, the DSI transcoders ignore this field.
//
// Active high is the default, and considered the standard polarity. Active
// low is considered an inverted polarity.
DEF_BIT(16, hsync_polarity_not_inverted);
// If true, this transcoder operates as a port sync secondary transcoder.
//
// Only the secondary transcoders must be explicitly configured for port sync.
// This is set to false for the port sync primary transcoder.
//
// This field is ignored by the EDP transcoder, because it cannot function as
// a port sync secondary.
//
// This field's bits are reserved MBZ (must be zero) on Tiger Lake. The field
// was moved to the TRANS_DDI_FUNC_CTL2 register.
DEF_BIT(15, is_port_sync_secondary_kaby_lake);
// Selects the input pipe, for transcoders that are not attached to pipes.
//
// This field has a non-trivial value encoding. The input_pipe_*() and
// set_input_pipe_*() helpers should be preferred to accessing the field
// directly.
DEF_FIELD(14, 12, input_pipe_select);
// Selects the input pipe, for transcoders that are not attached to pipes.
//
// On Tiger Lake, this field is only used by the DSI transcoders. On Kaby
// Lake, the field is only used by the EDP transcoder. These are the
// transcoders that are not attached to pipes.
//
// This field is not documented on Skylake, and its bits are documented as
// reserved. However, several PRM locations (IHD-OS-SKL-Vol 12-05.16 section
// "Display Connections" pages 103, section "Pipe to Transcoder to DDI
// Mappings" page 107) mention that the EDP transcoder can connect to pipes
// A-C. So, the field likely works the same way as on Kaby Lake.
i915::PipeId input_pipe_id() const {
switch (input_pipe_select()) {
case kInputSelectPipeA:
return i915::PipeId::PIPE_A;
case kInputSelectPipeB:
return i915::PipeId::PIPE_B;
case kInputSelectPipeC:
return i915::PipeId::PIPE_C;
// TODO(https://fxbug.dev/42060657): Add pipe D, once we support it.
};
return i915::PipeId::PIPE_INVALID;
}
// See `input_pipe()` for details.
TranscoderDdiControl& set_input_pipe_id(i915::PipeId input_pipe_id) {
switch (input_pipe_id) {
case i915::PipeId::PIPE_A:
return set_input_pipe_select(kInputSelectPipeA);
case i915::PipeId::PIPE_B:
return set_input_pipe_select(kInputSelectPipeB);
case i915::PipeId::PIPE_C:
return set_input_pipe_select(kInputSelectPipeC);
// TODO(https://fxbug.dev/42060657): Add pipe D, once we support it.
case i915::PipeId::PIPE_INVALID:
// The code handling the explicit invalid pipe value is outside the
// switch() so it also applies to values that aren't Pipe enum members,
// which are also invalid.
;
};
ZX_DEBUG_ASSERT_MSG(false, "Invalid pipe: %d", input_pipe_id);
return *this;
}
// Values for `display_port_transport_tiger_lake`.
enum class DisplayPortTransportTigerLake {
kA = 0,
kB = 1,
kC = 2,
kD = 3,
};
// Selects the DisplayPort transport that receives this transcoder's data.
//
// This field is only used when DisplayPort MST (multi-streaming) is enabled.
//
// This must be changed in the same MMIO operation as `ddi_mode`.
DEF_ENUM_FIELD(DisplayPortTransportTigerLake, 11, 10, display_port_transport_tiger_lake);
// If true, VC (Virtual Channel) payload allocation is enabled.
//
// This field is ignored by the transcoders attached to DDIs that don't
// support multi-streaming. These are the DSI transcoders On Tiger Lake, and
// the EDP transcoder on Kaby Lake and Skylake.
DEF_BIT(8, allocate_display_port_virtual_circuit_payload);
// If true, the HDMI scrambler is in CTS (Compliance Test Specification) mode.
//
// This field must not be changed while `hdmi_scrambler_enabled` is true.
//
// This field is not documented on Kaby Lake and Skylake. The bit is reserved
// MBZ (must be zero). This extends the good read semantics of `hdmi_enabled_`
// -- reading zero means that the CTS mode is disabled, which makes perfect
// sense while the HDMI scrambler is disabled.
DEF_BIT(7, hdmi_scrambler_cts_mode);
// If false, the HDMI scrambler is reset on every line.
//
// This field is only used when the HDMI scrambler is in CTS mode. In that
// case, it determines whether the transcoder sends a SSCP (Scrambler
// Synchronization Control Period) during HSync for every line, or for every
// other line.
//
// This field must be not be set while `hdmi_scrambler_cts_mode` is true.
//
// This field is not documented on Kaby Lake and Skylake. The bit is reserved
// MBZ (must be zero). This extends the good read semantics of
// `hdmi_scrambler_cts_mode` -- the CTS mode is never enabled, and this field
// can always be ignored.
DEF_BIT(6, hdmi_scrambler_resets_every_other_line);
// If true, the high TMDS character rate is enabled over the HDMI link.
//
// This field must be set to true if and only if the HDMI link symbol rate is
// greater than 340 MHz.
//
// This field is not documented on Kaby Lake and Skylake. The bits are
// reserved MBZ (must be zero), which makes for good read semantics -- reading
// zero means that the high TMDS character rate is not enabled.
DEF_BIT(4, high_tmds_character_rate_tiger_lake);
// Selects the number of DisplayPort or DSI lanes enabled.
//
// This field has a non-trivial value encoding. The
// `display_port_lane_count()` and `set_display_port_lane_count()` helpers
// should be preferred to accessing the field directly.
DEF_FIELD(3, 1, display_port_lane_count_selection);
// The number of DisplayPort lanes enabled.
//
// This field is ignored for HDMI or DVI, as these modes always use 4 lanes.
// Only the DSI transcoders support using 3 lanes.
//
// When the transcoder mode is a DisplayPort mode, the field must match the
// `display_port_lane_count` in the attached DDI's DdiBufferControl register.
uint8_t display_port_lane_count() const {
// The addition will not overflow and the cast is lossless because
// display_port_lane_count_selection() is a 3-bit field.
return static_cast<int8_t>(display_port_lane_count_selection() + 1);
}
// See `display_port_lane_count()` for details.
TranscoderDdiControl set_display_port_lane_count(uint8_t lane_count) {
ZX_DEBUG_ASSERT(lane_count >= 1);
ZX_DEBUG_ASSERT(lane_count <= 4);
return set_display_port_lane_count_selection(lane_count - 1);
}
// If true, scrambling is enabled over the HDMI link.
//
// Scrambling must be enabled for HDMI link symbol rates above 340 MHz.
// Scrambling should also be enabled at lower speeds, when the receiver
// supports scrambling at those speeds.
//
// This field is not documented on Kaby Lake and Skylake. The bits are
// reserved MBZ (must be zero), which makes for good read semantics -- reading
// zero means that no HDMI scrambler is enabled.
DEF_BIT(0, hdmi_scrambler_enabled_tiger_lake);
static auto GetForKabyLakeTranscoder(i915::TranscoderId transcoder_id) {
if (transcoder_id == i915::TranscoderId::TRANSCODER_EDP) {
return hwreg::RegisterAddr<TranscoderDdiControl>(0x6f400);
}
ZX_ASSERT(transcoder_id >= i915::TranscoderId::TRANSCODER_A);
ZX_ASSERT(transcoder_id <= i915::TranscoderId::TRANSCODER_C);
const int transcoder_index = transcoder_id - i915::TranscoderId::TRANSCODER_A;
return hwreg::RegisterAddr<TranscoderDdiControl>(0x60400 + 0x1000 * transcoder_index);
}
static auto GetForTigerLakeTranscoder(i915::TranscoderId transcoder_id) {
ZX_ASSERT(transcoder_id >= i915::TranscoderId::TRANSCODER_A);
// TODO(https://fxbug.dev/42060657): Allow transcoder D, once we support it.
ZX_ASSERT(transcoder_id <= i915::TranscoderId::TRANSCODER_C);
const int transcoder_index = transcoder_id - i915::TranscoderId::TRANSCODER_A;
return hwreg::RegisterAddr<TranscoderDdiControl>(0x60400 + 0x1000 * transcoder_index);
}
private:
static constexpr uint32_t kInputSelectPipeA = 0;
static constexpr uint32_t kInputSelectPipeB = 5;
static constexpr uint32_t kInputSelectPipeC = 6;
// TODO(https://fxbug.dev/42060657): Add pipe D, once we support it. The value is 7.
};
// TRANS_CONF (Transcoder Configuration)
//
// This register has reserved bits that are not documented as MBZ (must be
// zero), so it should be accessed using read-modify-write.
//
// Tiger Lake: IHD-OS-TGL-Vol 2c-1.22-Rev2.0 Part 2 pages 1365-1366
// Kaby Lake: IHD-OS-KBL-Vol 2c-1.17 Part 2 pages 949-951
// Skylake: IHD-OS-SKL-Vol 2c-05.16 Part 2 pages 924-925
class TranscoderConfig : public hwreg::RegisterBase<TranscoderConfig, uint32_t> {
public:
// Set to true/false to eventually enable/disable the transcoder.
//
// Turning off the transcoder disables the timing generator and the
// synchronization pulses to the display.
//
// Timing registers must be set to valid values before this field is enabled.
DEF_BIT(31, enabled_target);
// Read-only, reflects the current state.
DEF_BIT(30, enabled);
// If false, the transcoder operates in Progressive Fetch mode.
//
// The following features are not supported with Interlaced Fetch mode:
// * Y tiling
// * 90 or 270 rotation
// * scaling
// * YUV 4:2:0 hybrid planar source pixel formats
DEF_BIT(22, interlaced_fetch);
// If false, the transcoder operates in Progressive Display mode.
//
// Must be true if `interlaced_fetch` is true.
//
// When `interlaced_fetch` is false and `interlaced_display` is true:
// * Pipe scaling is required
// * The vertical resolution doubles
// * The maximum supported pixel rate is cut down in half
DEF_BIT(21, interlaced_display);
// The number of symbols that must be in the DisplayPort audio symbol RAM
// before it starts to drain during horizontal blank.
//
// The value must be between 2 and 64.
//
// This field does not exist (must be zero) on Kaby Lake or Skylake.
DEF_FIELD(6, 0, display_port_audio_symbol_watermark_tiger_lake);
static auto GetForKabyLakeTranscoder(i915::TranscoderId transcoder_id) {
if (transcoder_id == i915::TranscoderId::TRANSCODER_EDP) {
return hwreg::RegisterAddr<TranscoderConfig>(0x7f008);
}
ZX_ASSERT(transcoder_id >= i915::TranscoderId::TRANSCODER_A);
ZX_ASSERT(transcoder_id <= i915::TranscoderId::TRANSCODER_C);
const int transcoder_index = transcoder_id - i915::TranscoderId::TRANSCODER_A;
return hwreg::RegisterAddr<TranscoderConfig>(0x70008 + 0x1000 * transcoder_index);
}
static auto GetForTigerLakeTranscoder(i915::TranscoderId transcoder_id) {
ZX_ASSERT(transcoder_id >= i915::TranscoderId::TRANSCODER_A);
// TODO(https://fxbug.dev/42060657): Allow transcoder D, once we support it.
ZX_ASSERT(transcoder_id <= i915::TranscoderId::TRANSCODER_C);
const int transcoder_index = transcoder_id - i915::TranscoderId::TRANSCODER_A;
return hwreg::RegisterAddr<TranscoderConfig>(0x70008 + 0x1000 * transcoder_index);
}
};
// TRANS_CLK_SEL (Transcoder Clock Select).
//
// On Kaby Lake and Skylake, the EDP transcoder always uses the DDI A clock, so
// it doesn't have a Clock Select register.
//
// On Tiger Lake, all reserved bits are MBZ (must be zero), so this register can
// be safely written without reading it first. On Kaby Lake and Skylake, the
// reserved bits are not documented as MBZ, so this register should be accessed
// using read-modify-write.
//
// Tiger Lake: IHD-OS-TGL-Vol 2c-1.22-Rev2.0 Part 2 pages 1365-1366
// Kaby Lake: IHD-OS-KBL-Vol 2c-1.17 Part 2 pages 947-948
// Skylake: IHD-OS-SKL-Vol 2c-05.16 Part 2 pages 922-923
class TranscoderClockSelect : public hwreg::RegisterBase<TranscoderClockSelect, uint32_t> {
public:
// Selects the DDI whose port clock is used by this transcoder.
//
// This field has a non-trivial value encoding. The ddi_*() and set_ddi_*()
// helpers should be preferred to accessing the field directly.
//
// This field is tagged `_subtle` because the definition matches the bits used
// on Tiger Lake, but it's used on all supported models. Kaby Lake and Skylake
// have a very similar field, which only takes up bits 30-28. Fortunately,
// bit 27 is reserved, and we can still paper over the field width difference
// in the helpers `ddi_clock_kaby_lake()` and `set_ddi_clock_kaby_lake()`.
DEF_FIELD(31, 28, ddi_clock_select_subtle);
// The DDI whose port clock is used by the transcoder.
//
// This helper works for Kaby Lake and Skylake.
//
// This field must not be changed while the transcoder is enabled.
std::optional<i915::DdiId> ddi_clock_kaby_lake() const {
// Shifting converts from the Tiger Lake field width. The cast is lossless
// because `ddi_clock_select_subtle()` is a 4-bit field.
const int8_t ddi_clock_select_raw_value = static_cast<int8_t>(ddi_clock_select_subtle() >> 1);
if (ddi_clock_select_raw_value == 0) {
return std::nullopt;
}
const int ddi_index = ddi_clock_select_raw_value - 1;
return static_cast<i915::DdiId>(ddi_index);
}
// The DDI whose port clock is used by the transcoder.
//
// This helper works for Tiger Lake.
//
// This field must not be changed while the transcoder is enabled.
std::optional<i915::DdiId> ddi_clock_tiger_lake() const {
// The cast is lossless because `ddi_clock_select_subtle()` is a 4-bit field.
const int8_t ddi_select_raw_value = static_cast<int8_t>(ddi_clock_select_subtle());
if (ddi_select_raw_value == 0) {
return std::nullopt;
}
const int ddi_index = ddi_select_raw_value - 1;
return static_cast<i915::DdiId>(ddi_index);
}
// See `ddi_clock_kaby_lake()` for details.
TranscoderClockSelect& set_ddi_clock_kaby_lake(std::optional<i915::DdiId> ddi_id) {
ZX_DEBUG_ASSERT_MSG(!ddi_id.has_value() || ddi_id != i915::DdiId::DDI_A,
"DDI A cannot be explicitly connected to a transcoder");
const int8_t ddi_select_raw = RawDdiClockSelect(ddi_id);
// Convert to the Tiger Lake field representation.
const uint32_t reserved_bit = (ddi_clock_select_subtle() & 1);
return set_ddi_clock_select_subtle((ddi_select_raw << 1) | reserved_bit);
}
// See `ddi_clock_tiger_lake()` for details.
TranscoderClockSelect& set_ddi_clock_tiger_lake(std::optional<i915::DdiId> ddi_id) {
return set_ddi_clock_select_subtle(RawDdiClockSelect(ddi_id));
}
static auto GetForTranscoder(i915::TranscoderId transcoder_id) {
ZX_ASSERT(transcoder_id >= i915::TranscoderId::TRANSCODER_A);
// TODO(https://fxbug.dev/42060657): Allow transcoder D, once we support it.
ZX_ASSERT(transcoder_id <= i915::TranscoderId::TRANSCODER_C);
const int transcoder_index = transcoder_id - i915::TranscoderId::TRANSCODER_A;
return hwreg::RegisterAddr<TranscoderClockSelect>(0x46140 + 4 * transcoder_index);
}
private:
static int8_t RawDdiClockSelect(std::optional<i915::DdiId> ddi) {
if (!ddi.has_value()) {
return 0;
}
// The cast is lossless because DDI indices fit in 4 bits.
const int8_t ddi_index = static_cast<int8_t>(*ddi - i915::DdiId::DDI_A);
// The addition doesn't overflow and the cast is lossless because DDI
// indices fit in 4 bits.
return static_cast<int8_t>(ddi_index + 1);
}
};
// DATAM / TRANS_DATAM1 (Transcoder Data M Value 1)
//
// This register is double-buffered and the update triggers when the first
// MSA (Main Stream Attributes packet) that is sent after LINKN is modified.
//
// All unassigned bits in this register are MBZ (must be zero), so it's safe to
// assign this register without reading its old value.
//
// Tiger Lake: IHD-OS-TGL-Vol 2c-1.22-Rev2.0 Part 1 pages 328-329
// Kaby Lake: IHD-OS-KBL-Vol 2c-1.17 Part 1 pages 427-428
// Skylake: IHD-OS-SKL-Vol 2c-05.16 Part 1 pages 422-423
class TranscoderDataM : public hwreg::RegisterBase<TranscoderDataM, uint32_t> {
public:
DEF_RSVDZ_BIT(31);
// Selects the TU (transfer unit) or VC (Virtual Channel) payload size.
//
// This field has a non-trivial value encoding. The `payload_size()` and
// `set_payload_size()` helpers should be preferred to accessing the field
// directly.
DEF_FIELD(30, 25, payload_size_select);
// Selects the TU (transfer unit) or VC (Virtual Channel) payload size.
//
// In DisplayPort SST (Single Stream) mode, this field represents the TU
// (transfer unit size), which is typically set to 64.
//
// In DisplayPort MST (Multi-Stream) mode, this field represents the Virtual
// Channel payload size, which must be at most 63. This field must not be
// changed while the transcoder is in MST mode, even if the transcoder is
// disabled.
int32_t payload_size() const {
// The cast is lossless and the addition does not overflow (which would be
// UB) because `payload_size_select()` is a 24-bit field.
return static_cast<int32_t>(static_cast<int32_t>(payload_size_select()) + 1);
}
// See `payload_size()`.
TranscoderDataM& set_payload_size(int payload_size) {
ZX_DEBUG_ASSERT(payload_size > 0);
return set_payload_size_select(payload_size - 1);
}
DEF_RSVDZ_BIT(24);
// The M value in the data M/N ratio, which is used by the transcoder.
DEF_FIELD(23, 0, m);
static auto GetForKabyLakeTranscoder(i915::TranscoderId transcoder_id) {
if (transcoder_id == i915::TranscoderId::TRANSCODER_EDP) {
return hwreg::RegisterAddr<TranscoderDataM>(0x6f030);
}
ZX_ASSERT(transcoder_id >= i915::TranscoderId::TRANSCODER_A);
ZX_ASSERT(transcoder_id <= i915::TranscoderId::TRANSCODER_C);
const int transcoder_index = transcoder_id - i915::TranscoderId::TRANSCODER_A;
return hwreg::RegisterAddr<TranscoderDataM>(0x60030 + 0x1000 * transcoder_index);
}
static auto GetForTigerLakeTranscoder(i915::TranscoderId transcoder_id) {
ZX_ASSERT(transcoder_id >= i915::TranscoderId::TRANSCODER_A);
// TODO(https://fxbug.dev/42060657): Allow transcoder D, once we support it.
ZX_ASSERT(transcoder_id <= i915::TranscoderId::TRANSCODER_C);
const int transcoder_index = transcoder_id - i915::TranscoderId::TRANSCODER_A;
return hwreg::RegisterAddr<TranscoderDataM>(0x60030 + 0x1000 * transcoder_index);
}
};
// DATAN / TRANS_DATAN1 (Transcoder Data N Value 1)
//
// This register is double-buffered and the update triggers when the first
// MSA (Main Stream Attributes packet) that is sent after LINKN is modified.
//
// All unassigned bits in this register are MBZ (must be zero), so it's safe to
// assign this register without reading its old value.
//
// Tiger Lake: IHD-OS-TGL-Vol 2c-1.22-Rev2.0 Part 1 page 330
// Kaby Lake: IHD-OS-KBL-Vol 2c-1.17 Part 1 page 429
// Skylake: IHD-OS-SKL-Vol 2c-05.16 Part 1 pages 424-425
class TranscoderDataN : public hwreg::RegisterBase<TranscoderDataN, uint32_t> {
public:
DEF_RSVDZ_FIELD(31, 24);
// The N value in the data M/N ratio, which is used by the transcoder.
DEF_FIELD(23, 0, n);
static auto GetForKabyLakeTranscoder(i915::TranscoderId transcoder_id) {
if (transcoder_id == i915::TranscoderId::TRANSCODER_EDP) {
return hwreg::RegisterAddr<TranscoderDataN>(0x6f034);
}
ZX_ASSERT(transcoder_id >= i915::TranscoderId::TRANSCODER_A);
ZX_ASSERT(transcoder_id <= i915::TranscoderId::TRANSCODER_C);
const int transcoder_index = transcoder_id - i915::TranscoderId::TRANSCODER_A;
return hwreg::RegisterAddr<TranscoderDataN>(0x60034 + 0x1000 * transcoder_index);
}
static auto GetForTigerLakeTranscoder(i915::TranscoderId transcoder_id) {
ZX_ASSERT(transcoder_id >= i915::TranscoderId::TRANSCODER_A);
// TODO(https://fxbug.dev/42060657): Allow transcoder D, once we support it.
ZX_ASSERT(transcoder_id <= i915::TranscoderId::TRANSCODER_C);
const int transcoder_index = transcoder_id - i915::TranscoderId::TRANSCODER_A;
return hwreg::RegisterAddr<TranscoderDataN>(0x60034 + 0x1000 * transcoder_index);
}
};
// LINKM / TRANS_LINKM1 (Transcoder Link M Value 1)
//
// This register is double-buffered and the update triggers when the first
// MSA (Main Stream Attributes packet) that is sent after LINKN is modified.
//
// All unassigned bits in this register are MBZ (must be zero), so it's safe to
// assign this register without reading its old value.
//
// Tiger Lake: IHD-OS-TGL-Vol 2c-1.22-Rev2.0 Part 1 page 1300
// Kaby Lake: IHD-OS-KBL-Vol 2c-1.17 Part 1 page 1123
// Skylake: IHD-OS-SKL-Vol 2c-05.16 Part 1 pages 1112-1113
class TranscoderLinkM : public hwreg::RegisterBase<TranscoderLinkM, uint32_t> {
public:
DEF_RSVDZ_FIELD(31, 24);
// The M value in the link M/N ratio transmitted in the MSA packet.
DEF_FIELD(23, 0, m);
static auto GetForKabyLakeTranscoder(i915::TranscoderId transcoder_id) {
if (transcoder_id == i915::TranscoderId::TRANSCODER_EDP) {
return hwreg::RegisterAddr<TranscoderLinkM>(0x6f040);
}
ZX_ASSERT(transcoder_id >= i915::TranscoderId::TRANSCODER_A);
ZX_ASSERT(transcoder_id <= i915::TranscoderId::TRANSCODER_C);
const int transcoder_index = transcoder_id - i915::TranscoderId::TRANSCODER_A;
return hwreg::RegisterAddr<TranscoderLinkM>(0x60040 + 0x1000 * transcoder_index);
}
static auto GetForTigerLakeTranscoder(i915::TranscoderId transcoder_id) {
ZX_ASSERT(transcoder_id >= i915::TranscoderId::TRANSCODER_A);
// TODO(https://fxbug.dev/42060657): Allow transcoder D, once we support it.
ZX_ASSERT(transcoder_id <= i915::TranscoderId::TRANSCODER_C);
const int transcoder_index = transcoder_id - i915::TranscoderId::TRANSCODER_A;
return hwreg::RegisterAddr<TranscoderLinkM>(0x60040 + 0x1000 * transcoder_index);
}
};
// LINKN / TRANS_LINKN1 (Transcoder Link N Value 1)
//
// Updating this register triggers an update of all double-buffered M/N
// registers (DATAM, DATAN, LINKM, LINKN) for the transcoder.
//
// All unassigned bits in this register are MBZ (must be zero), so it's safe to
// assign this register without reading its old value.
//
// Tiger Lake: IHD-OS-TGL-Vol 2c-1.22-Rev2.0 Part 1 page 1301
// Kaby Lake: IHD-OS-KBL-Vol 2c-1.17 Part 1 page 1124
// Skylake: IHD-OS-SKL-Vol 2c-05.16 Part 1 pages 1114-1115
class TranscoderLinkN : public hwreg::RegisterBase<TranscoderLinkN, uint32_t> {
public:
DEF_RSVDZ_FIELD(31, 24);
// The N value in the link M/N ratio transmitted in the MSA packet. This is
// also transmitted in the VB-ID (Vertical Blanking ID).
DEF_FIELD(23, 0, n);
static auto GetForKabyLakeTranscoder(i915::TranscoderId transcoder_id) {
if (transcoder_id == i915::TranscoderId::TRANSCODER_EDP) {
return hwreg::RegisterAddr<TranscoderLinkN>(0x6f044);
}
ZX_ASSERT(transcoder_id >= i915::TranscoderId::TRANSCODER_A);
ZX_ASSERT(transcoder_id <= i915::TranscoderId::TRANSCODER_C);
const int transcoder_index = transcoder_id - i915::TranscoderId::TRANSCODER_A;
return hwreg::RegisterAddr<TranscoderLinkN>(0x60044 + 0x1000 * transcoder_index);
}
static auto GetForTigerLakeTranscoder(i915::TranscoderId transcoder_id) {
ZX_ASSERT(transcoder_id >= i915::TranscoderId::TRANSCODER_A);
// TODO(https://fxbug.dev/42060657): Allow transcoder D, once we support it.
ZX_ASSERT(transcoder_id <= i915::TranscoderId::TRANSCODER_C);
const int transcoder_index = transcoder_id - i915::TranscoderId::TRANSCODER_A;
return hwreg::RegisterAddr<TranscoderLinkN>(0x60044 + 0x1000 * transcoder_index);
}
};
// Documented values for the DisplayPort MSA MISC0 field's bits 7:5.
//
// The values come from the VESA DisplayPort Standard Version 2.0, Table 2-96
// "MSA MISC1 and MISC0 Fields for Pixel Encoding/Colorimetry Format Indication"
// at page 158. The table belongs to Section 2.2.4 "MSA Data Transport".
//
// The encoding here is correct for all modes except for RAW, which uses a
// different encoding.
//
// TODO(https://fxbug.dev/42056427): This covers a general DisplayPort concept, so it
// belongs in a general-purpose DisplayPort support library.
enum class DisplayPortMsaBitsPerComponent {
k6Bpc = 0,
k8Bpc = 1,
k10Bpc = 2,
k12Bpc = 3,
k16Bpc = 4,
};
// Documented values for the DisplayPort MSA MISC0 field's bits 4:1.
//
// The values come from the VESA DisplayPort Standard Version 2.0, Table 2-96
// "MSA MISC1 and MISC0 Fields for Pixel Encoding/Colorimetry Format Indication"
// at page 158. The table belongs to Section 2.2.4 "MSA Data Transport".
//
// TODO(https://fxbug.dev/42056427): This covers a general DisplayPort concept, so it
// belongs in a general-purpose DisplayPort support library.
enum class DisplayPortMsaColorimetry {
kRgbUnspecifiedLegacy = 0b0'0'00,
kCtaSrgb = 0b0'1'00,
kRgbWideGamutFixed = 0b0'0'11,
kRgbWideGamutFloating = 0b1'0'00,
kYCbCr422Bt601 = 0b0'1'01,
kYCbCr422Bt709 = 0b1'1'01,
kYCbCr444Bt601 = 0b0'1'10,
kYCbCr444Bt709 = 0b1'1'10,
kAdobeRgb = 0b1'1'00,
kDciP3 = 0b0'1'11,
// The color profile will be sent as a MCCS (VESA Monitor Control Command)
// VCP (Virtual Control Panel).
kVcpColorProfile = 0b0'1'11,
};
// TRANS_MSA_MISC (Transcoder Main Stream Attribute Miscellaneous)
//
// All reserved fields in this register are MBZ (must be zero), so it can be
// safely written without a prior read.
//
// Tiger Lake: IHD-OS-TGL-Vol 2c-1.22-Rev2.0 Part 2 pages 1394-1395
// Kaby Lake: IHD-OS-KBL-Vol 2c-1.17 Part 2 pages 947-948
// Skylake: IHD-OS-SKL-Vol 2c-05.16 Part 2 pages 922-923
//
// MISC fields: VESA DisplayPort Standard Version 2.0, Section 2.2.4
// "MSA Data Transport", pages 149-151 and 157-158.
class TranscoderMainStreamAttributeMisc
: public hwreg::RegisterBase<TranscoderMainStreamAttributeMisc, uint32_t> {
public:
// TODO(https://fxbug.dev/42056427): The MSA field definitions are a general DisplayPort
// concept, and belong in a general-purpose DisplayPort support library. Once
// we have that, this register's definition should only map MSA fields to
// register bytes, matching the PRM.
// Bits 31:16 are document as the value transmitted in the MSA unused fields.
//
// The VESA DisplayPort Standard Version 2.0, Figure 2-18 "DP MSA Packet
// Transport Mapping over Main-Link", page 152 states this field must be zero.
DEF_RSVDZ_FIELD(31, 16);
// Bits 15:8 are the MISC1 MSA field from the DisplayPort standard.
// True for Y (luminance)-only and RAW formats.
//
// We don't currently support these color formats.
DEF_BIT(15, colorimetry_top_bit);
// If true, the colorimetry information is sent separately, in a VSC SDP.
//
// This must only be used if the sink's DPRX_FEATURE_ENUMERATION_LIST register
// has VSC_SDP_EXTENSION_FOR_COLORIMETRY_SUPPORTED set.
//
// Including colorimetry information in the VSC (Video Stream Configuration)
// SDP (Secondary Data Packet) is described in the VESA DisplayPort Standard
// Version 2.0, Section 2.2.5.6.5 "VSC SDP Payload for Pixel
// Encoding/Colorimetry Format", pages 203-205
//
// This field was introduced in DisplayPort 1.3. Prior to that, the underlying
// bit was MBZ (must be zero).
//
// We don't currently support this feature.
DEF_BIT(14, colorimetry_in_vsc_sdp);
// Reserved in the DisplayPort 2.0 standard, must be zero.
DEF_RSVDZ_FIELD(13, 11);
// If the "FS MSA MISC1 Drive Enable" field in the TRANS_STEREO3D_CTL register
// is true, this field is ignored, and the display hardware drives the
// corresponding MSA bits.
DEF_FIELD(10, 9, stereo_video);
// True iff the number of lines per interlaced frame (two fields) is even.
DEF_BIT(8, interlaced_vertical_total_even);
// Bits 7:0 are the MSA MISC0 field from the DisplayPort standard.
// The bpc (number of bits per color component) for the selected format.
//
// Some bpc values are not supported by some colorimetry modes. For example,
// the RGB wide gamut fixed point only supports 8, 10, and 12bpc.
DEF_ENUM_FIELD(DisplayPortMsaBitsPerComponent, 7, 5, bits_per_component_select);
// Selects the pixel encoding and colorimetry format.
//
// See the `DisplayPortMainStreamAttributeColorimetry` comments for details.
DEF_ENUM_FIELD(DisplayPortMsaColorimetry, 4, 1, colorimetry_select);
// If true, the main link clock and video stream clock are synchronous.
//
// Before DisplayPort is enabled, this field must be set to true.
DEF_BIT(0, video_stream_clock_sync_with_link_clock);
static auto GetForKabyLakeTranscoder(i915::TranscoderId transcoder_id) {
if (transcoder_id == i915::TranscoderId::TRANSCODER_EDP) {
return hwreg::RegisterAddr<TranscoderMainStreamAttributeMisc>(0x6f410);
}
ZX_ASSERT(transcoder_id >= i915::TranscoderId::TRANSCODER_A);
ZX_ASSERT(transcoder_id <= i915::TranscoderId::TRANSCODER_C);
const int transcoder_index = transcoder_id - i915::TranscoderId::TRANSCODER_A;
return hwreg::RegisterAddr<TranscoderMainStreamAttributeMisc>(0x60410 +
0x1000 * transcoder_index);
}
static auto GetForTigerLakeTranscoder(i915::TranscoderId transcoder_id) {
ZX_ASSERT(transcoder_id >= i915::TranscoderId::TRANSCODER_A);
// TODO(https://fxbug.dev/42060657): Allow transcoder D, once we support it.
ZX_ASSERT(transcoder_id <= i915::TranscoderId::TRANSCODER_C);
const int transcoder_index = transcoder_id - i915::TranscoderId::TRANSCODER_A;
return hwreg::RegisterAddr<TranscoderClockSelect>(0x60410 + 0x1000 * transcoder_index);
}
};
// TRANS_VRR_CTL (VRR Control Register Transcoder)
//
// This register is not documented for Kaby Lake or Skylake. These display
// engines do not support the VRR (Variable Refresh Rate) feature.
//
// Tiger Lake: IHD-OS-TGL-Vol 2c-1.22-Rev2.0 Part 2 pages 1406-1407
class TranscoderVariableRateRefreshControl
: public hwreg::RegisterBase<TranscoderVariableRateRefreshControl, uint32_t> {
public:
// If true, VRR (Variable Rate Refresh) is enabled.
DEF_BIT(31, enabled);
// If true, hardware varies Vblank.
//
// If this field is true, Vblank (the Vertical Blank period) varies between
// the minimum set in the TRANS_VRR_VMIN register and the maximum set in the
// TRANS_VRR_VMAX register.
//
// If this field is false, the Vblank (Vertical Blank period) in the
// TRANS_VBLANK register is used.
DEF_BIT(30, vblank_max_shift_ignored);
// If true, the Flip Line feature is enabled.
//
// Changes to this field take effect at the next vertical blank.
//
// This field must be set to true before `enabled` is true. If this field is
// true, `vblank_max_shift_ignored` and `use_pipeline_full_line_count_delay`
// must also be true.
DEF_BIT(29, flip_line_enabled);
DEF_RSVDZ_FIELD(28, 11);
// Delay from frame start to Pipeline Full Line Count signal generation.
//
// When `use_pipeline_full_line_count_delay` is true, this field indicates the
// delay (in number of scanlines) from the start of Vblank (Vertical Blank)
// start until the Pipeline Full Line Count signal is triggered. This signal
// causes the start of Vactive (Vertical Active).
//
// This field must be set to VRR Vmin - Vblank start - 4.
DEF_FIELD(10, 3, pipeline_full_line_count_delay_from_frame_start);
DEF_RSVDZ_FIELD(2, 1);
// If true, Vertical Active starts at a programmed delay from frame start.
//
// If this field is false, Vactive (Vertical Active) starts when a
// hardware-generated Pipeline Full Line Count signal is triggered.
//
// If this field is true, `use_pipeline_full_line_count_delay` must be
// programmed correctly.
DEF_BIT(0, use_pipeline_full_line_count_delay);
static auto GetForTigerLakeTranscoder(i915::TranscoderId transcoder_id) {
ZX_ASSERT(transcoder_id >= i915::TranscoderId::TRANSCODER_A);
// TODO(https://fxbug.dev/42060657): Allow transcoder D, once we support it.
ZX_ASSERT(transcoder_id <= i915::TranscoderId::TRANSCODER_C);
const int transcoder_index = transcoder_id - i915::TranscoderId::TRANSCODER_A;
return hwreg::RegisterAddr<TranscoderVariableRateRefreshControl>(0x60420 +
0x1000 * transcoder_index);
}
};
// Per-transcoder chicken register.
//
// This register is not officially documented in any register listing. It is
// implicitly documented in display engine PRMs and workaround PRMs, via
// instructions to flip specific bits at transcoder-dependent MMIO addresses.
//
// On Kaby Lake, the transcoder chicken registers also store some DDI-specific
// chicken bits. The GetForKabyLakeDdi() helper will retrieve the register that
// contains the chicken bits for a specific DDI.
class TranscoderChicken : public hwreg::RegisterBase<TranscoderChicken, uint32_t> {
public:
// FEC (Forward Error Correction) workaround.
//
// This field must be set for correct functioning in DisplayPort 1.4 MST
// (Multi-Stream) mode with FEC (Forward Error Correction), before the
// `enabled_target` field in the TranscoderConfig register is set to true.
//
// This field must be set to false before the `enabled_target` field in the
// TranscoderConfig register is set back to false.
//
// This bit is only indirectly docuumented in IHD-OS-TGL-Vol 12-1.22-Rev2.0
// sections "Sequences for DisplayPort" > "Enable Sequence" (page 144) and
// "Disable Sequence" (page 147). The register is mentioned by its MMIO
// address.
DEF_BIT(23, override_forward_error_correction_tiger_lake);
// HDMI port voltage swing programming workaround.
//
// By default, each HDMI port in Kaby Lake display engines uses the voltage
// swing setting specified when the port is first enabled. A new voltage swing
// setting can be programmed by setting this field to 3 (0b11) right before
// setting the `enabled` field in the DdiBufferControl register, waiting for
// 1us, and then setting this field to zero (0b00).
//
// This field is scoped to DDIs, not to transcoders. GetForKabyLakeDdi() will
// return the correct register for applying this workaround to a DDI.
//
// IHD-OS-KBL-Vol 16-1.17 workaround BSpec ID 1143, pages 29-30
DEF_FIELD(19, 18, override_ddi_hdmi_voltage_swing_kaby_lake);
// DisplayPort audio corruption or video underflow workaround.
//
// This field must be set to true for DisplayPort x4 (4 main link lanes) ports
// that use the HBR2 rate, when the CDCLK (core display clock) frequency is
// below 432 MHz. This workaround is only valid for audio clock frequencies <=
// 96 KHz, and fewer than 8 audio channels -- audio must not be used over
// DisplayPort otherwise.
//
// IHD-OS-KBL-Vol 16-1.17 workaround BSpec ID 1144, pages 30-31
DEF_BIT(13, override_display_port_audio_island_kaby_lake);
// Returns the register that holds DDI-scoped chicken bits for `ddi`.
//
// On Kaby Lake, the transcoder chicken registers are also used for DDI-scoped
// chicken bits. The mapping of DDIs to registers is not straightforward. This
// method returns the correct register for accessing a DDI's chicken bits.
static auto GetForKabyLakeDdi(i915::DdiId ddi_id) {
ZX_ASSERT(ddi_id >= i915::DdiId::DDI_A);
ZX_ASSERT(ddi_id <= i915::DdiId::DDI_D);
// The DDI-to-MMIO address mapping is specified implicitly in
// IHD-OS-KBL-Vol 16-1.17 BSpec (workaround) ID 1143, page 30.
static constexpr uint32_t kMmioAddress[] = {0x420cc, 0x420c0, 0x420c4, 0x420c8};
const int ddi_index = ddi_id - i915::DdiId::DDI_A;
return hwreg::RegisterAddr<TranscoderChicken>(kMmioAddress[ddi_index]);
}
static auto GetForKabyLakeTranscoder(i915::TranscoderId transcoder_id) {
ZX_ASSERT(transcoder_id >= i915::TranscoderId::TRANSCODER_A);
ZX_ASSERT(transcoder_id <= i915::TranscoderId::TRANSCODER_C);
// The transcoder-to-MMIO address mapping is specified implicitly in
// IHD-OS-KBL-Vol 16-1.17 BSpec (workaround) ID 1144, page 31.
const int transcoder_index = transcoder_id - i915::TranscoderId::TRANSCODER_A;
return hwreg::RegisterAddr<TranscoderChicken>(0x420c0 + transcoder_index * 4);
}
static auto GetForTigerLakeTranscoder(i915::TranscoderId transcoder_id) {
ZX_ASSERT(transcoder_id >= i915::TranscoderId::TRANSCODER_A);
// TODO(https://fxbug.dev/42060657): Allow transcoder D, once we support it.
ZX_ASSERT(transcoder_id <= i915::TranscoderId::TRANSCODER_C);
// The transcoder-to-MMIO address mapping is presented in section "Variable
// Refresh Rate" in the display engine PRMs.
// Tiger Lake: IHD-OS-TGL-Vol 12-1.22-Rev2.0 page 240
// DG1: IHD-OS-DG1-Vol 12-2.21 page 192
static constexpr uint32_t kMmioAddress[] = {0x420c0, 0x420c4, 0x420c8, 0x420d8};
const int transcoder_index = transcoder_id - i915::TranscoderId::TRANSCODER_A;
return hwreg::RegisterAddr<TranscoderChicken>(kMmioAddress[transcoder_index]);
}
};
class TranscoderRegs {
public:
explicit TranscoderRegs(i915::TranscoderId transcoder_id)
: transcoder_id_(transcoder_id),
offset_(transcoder_id_ == i915::TranscoderId::TRANSCODER_EDP ? 0xf000
: (transcoder_id_ * 0x1000)) {}
hwreg::RegisterAddr<TransHVTotal> HTotal() { return GetReg<TransHVTotal>(0x60000); }
hwreg::RegisterAddr<TransHVTotal> HBlank() { return GetReg<TransHVTotal>(0x60004); }
hwreg::RegisterAddr<TransHVSync> HSync() { return GetReg<TransHVSync>(0x60008); }
hwreg::RegisterAddr<TransHVTotal> VTotal() { return GetReg<TransHVTotal>(0x6000c); }
hwreg::RegisterAddr<TransHVTotal> VBlank() { return GetReg<TransHVTotal>(0x60010); }
hwreg::RegisterAddr<TransHVSync> VSync() { return GetReg<TransHVSync>(0x60014); }
hwreg::RegisterAddr<TransVSyncShift> VSyncShift() { return GetReg<TransVSyncShift>(0x60028); }
hwreg::RegisterAddr<TranscoderDdiControl> DdiControl() {
// This works for Tiger Lake too, because the supported transcoders are a
// subset of the Kaby Lake transcoders, and the MMIO addresses for these
// transcoders are the same.
// TODO(https://fxbug.dev/42060657): This won't be true once we support transcoder D.
return TranscoderDdiControl::GetForKabyLakeTranscoder(transcoder_id_);
}
hwreg::RegisterAddr<TranscoderConfig> Config() {
// This works for Tiger Lake too, because the supported transcoders are a
// subset of the Kaby Lake transcoders, and the MMIO addresses for these
// transcoders are the same.
// TODO(https://fxbug.dev/42060657): This won't be true once we support transcoder D.
return TranscoderConfig::GetForKabyLakeTranscoder(transcoder_id_);
}
hwreg::RegisterAddr<TranscoderClockSelect> ClockSelect() {
return TranscoderClockSelect::GetForTranscoder(transcoder_id_);
}
hwreg::RegisterAddr<TranscoderMainStreamAttributeMisc> MainStreamAttributeMisc() {
// This works for Tiger Lake too, because the supported transcoders are a
// subset of the Kaby Lake transcoders, and the MMIO addresses for these
// transcoders are the same.
return TranscoderMainStreamAttributeMisc::GetForKabyLakeTranscoder(transcoder_id_);
}
hwreg::RegisterAddr<TranscoderChicken> Chicken() {
// This works for Tiger Lake too, because the supported transcoders are a
// subset of the Kaby Lake transcoders, and the MMIO addresses for these
// transcoders are the same.
// TODO(https://fxbug.dev/42060657): This won't be true once we support transcoder D.
return TranscoderChicken::GetForKabyLakeTranscoder(transcoder_id_);
}
hwreg::RegisterAddr<TranscoderVariableRateRefreshControl> VariableRateRefreshControl() {
// We should only be using this code on Tiger Lake.
return TranscoderVariableRateRefreshControl::GetForTigerLakeTranscoder(transcoder_id_);
}
hwreg::RegisterAddr<TranscoderDataM> DataM() {
// This works for Tiger Lake too, because the supported transcoders are a
// subset of the Kaby Lake transcoders, and the MMIO addresses for these
// transcoders are the same.
// TODO(https://fxbug.dev/42060657): This won't be true once we support transcoder D.
return TranscoderDataM::GetForKabyLakeTranscoder(transcoder_id_);
}
hwreg::RegisterAddr<TranscoderDataN> DataN() {
// This works for Tiger Lake too, because the supported transcoders are a
// subset of the Kaby Lake transcoders, and the MMIO addresses for these
// transcoders are the same.
// TODO(https://fxbug.dev/42060657): This won't be true once we support transcoder D.
return TranscoderDataN::GetForKabyLakeTranscoder(transcoder_id_);
}
hwreg::RegisterAddr<TranscoderLinkM> LinkM() {
// This works for Tiger Lake too, because the supported transcoders are a
// subset of the Kaby Lake transcoders, and the MMIO addresses for these
// transcoders are the same.
// TODO(https://fxbug.dev/42060657): This won't be true once we support transcoder D.
return TranscoderLinkM::GetForKabyLakeTranscoder(transcoder_id_);
}
hwreg::RegisterAddr<TranscoderLinkN> LinkN() {
// This works for Tiger Lake too, because the supported transcoders are a
// subset of the Kaby Lake transcoders, and the MMIO addresses for these
// transcoders are the same.
// TODO(https://fxbug.dev/42060657): This won't be true once we support transcoder D.
return TranscoderLinkN::GetForKabyLakeTranscoder(transcoder_id_);
}
private:
template <class RegType>
hwreg::RegisterAddr<RegType> GetReg(uint32_t base_addr) {
return hwreg::RegisterAddr<RegType>(base_addr + offset_);
}
i915::TranscoderId transcoder_id_;
uint32_t offset_;
};
} // namespace registers
#endif // SRC_GRAPHICS_DISPLAY_DRIVERS_INTEL_I915_REGISTERS_TRANSCODER_H_