blob: c4ee9ce65dd456a39a1ba2b88bb1f7515e5d8d25 [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.
#include "src/graphics/display/drivers/intel-i915/pipe-manager.h"
#include <lib/mmio/mmio-buffer.h>
#include <zircon/assert.h>
#include <algorithm>
#include <optional>
#include <utility>
#include "src/graphics/display/drivers/intel-i915/hardware-common.h"
#include "src/graphics/display/drivers/intel-i915/intel-i915.h"
#include "src/graphics/display/drivers/intel-i915/pipe.h"
#include "src/graphics/display/drivers/intel-i915/registers-ddi.h"
#include "src/graphics/display/drivers/intel-i915/registers-pipe.h"
#include "src/graphics/display/drivers/intel-i915/registers-transcoder.h"
namespace i915 {
PipeManager::PipeManager(std::vector<std::unique_ptr<Pipe>> pipes) : pipes_(std::move(pipes)) {}
Pipe* PipeManager::RequestPipe(DisplayDevice& display) {
Pipe* new_pipe = GetAvailablePipe();
if (new_pipe) {
new_pipe->AttachToDisplay(display.id(), display.type() == DisplayDevice::Type::kEdp);
pipes_reallocated_ = true;
}
return new_pipe;
}
Pipe* PipeManager::RequestPipeFromHardwareState(DisplayDevice& display,
fdf::MmioBuffer* mmio_space) {
Pipe* hw_state_pipe = GetPipeFromHwState(display.ddi_id(), mmio_space);
if (hw_state_pipe) {
hw_state_pipe->AttachToDisplay(display.id(), display.type() == DisplayDevice::Type::kEdp);
pipes_reallocated_ = true;
}
return hw_state_pipe;
}
void PipeManager::ReturnPipe(Pipe* pipe) {
bool has_pipe =
std::any_of(pipes_.begin(), pipes_.end(), [pipe](const auto& p) { return p.get() == pipe; });
ZX_DEBUG_ASSERT(has_pipe && pipe->in_use());
pipe->Reset();
pipe->Detach();
pipes_reallocated_ = true;
}
bool PipeManager::PipeReallocated() {
bool result = pipes_reallocated_;
pipes_reallocated_ = false;
return result;
}
Pipe* PipeManager::operator[](PipeId idx) const {
ZX_DEBUG_ASSERT(idx < pipes_.size());
return idx < pipes_.size() ? pipes_.at(idx).get() : nullptr;
}
Pipe* PipeManager::At(PipeId idx) const {
return idx < pipes_.size() ? pipes_.at(idx).get() : nullptr;
}
PipeManager::PipeIterator PipeManager::begin() { return PipeIterator(pipes_.begin()); }
PipeManager::PipeIterator PipeManager::end() { return PipeIterator(pipes_.end()); }
PipeManager::PipeConstIterator PipeManager::begin() const {
return PipeConstIterator(pipes_.cbegin());
}
PipeManager::PipeConstIterator PipeManager::end() const { return PipeConstIterator(pipes_.cend()); }
PipeManagerSkylake::PipeManagerSkylake(Controller* controller)
: PipeManager(GetPipes(controller->mmio_space(), controller->power())),
mmio_space_(controller->mmio_space()) {
ZX_DEBUG_ASSERT(controller);
}
void PipeManagerSkylake::ResetInactiveTranscoders() {
bool edp_transcoder_in_use = false;
for (Pipe* pipe : *this) {
if (pipe->in_use()) {
if (pipe->connected_transcoder_id() == TranscoderId::TRANSCODER_EDP) {
edp_transcoder_in_use = true;
const TranscoderId unused_transcoder_id = pipe->tied_transcoder_id();
Pipe::ResetTranscoder(unused_transcoder_id, registers::Platform::kSkylake, mmio_space_);
zxlogf(
DEBUG,
"Reset unused transcoder %d tied to pipe %d, which is connected to the EDP transcoder",
unused_transcoder_id, pipe->pipe_id());
}
} else {
Pipe::ResetTranscoder(pipe->tied_transcoder_id(), registers::Platform::kSkylake, mmio_space_);
zxlogf(DEBUG, "Reset unused transcoder %d tied to inactive pipe %d",
pipe->tied_transcoder_id(), pipe->pipe_id());
}
}
if (!edp_transcoder_in_use) {
Pipe::ResetTranscoder(TranscoderId::TRANSCODER_EDP, registers::Platform::kSkylake, mmio_space_);
zxlogf(DEBUG, "Reset unused transcoder TRANSCODER_EDP (not used by any pipe)");
}
}
Pipe* PipeManagerSkylake::GetAvailablePipe() {
for (Pipe* pipe : *this) {
if (!pipe->in_use()) {
return pipe;
}
}
return nullptr;
}
Pipe* PipeManagerSkylake::GetPipeFromHwState(DdiId ddi_id, fdf::MmioBuffer* mmio_space) {
// On Kaby Lake and Skylake, DDI_A is attached to the EDP transcoder.
if (ddi_id == DdiId::DDI_A) {
registers::TranscoderRegs transcoder_regs(TranscoderId::TRANSCODER_EDP);
auto transcoder_ddi_control = transcoder_regs.DdiControl().ReadFrom(mmio_space);
const PipeId pipe_id = transcoder_ddi_control.input_pipe_id();
if (pipe_id == PipeId::PIPE_INVALID) {
// The transcoder DDI control register is configured incorrectly.
return nullptr;
}
return At(pipe_id);
}
for (Pipe* pipe : *this) {
const TranscoderId tied_transcoder = pipe->tied_transcoder_id();
ZX_DEBUG_ASSERT_MSG(tied_transcoder != TranscoderId::TRANSCODER_EDP,
"The EDP transcoder is not tied to a pipe");
registers::TranscoderRegs transcoder_regs(tied_transcoder);
if (transcoder_regs.ClockSelect().ReadFrom(mmio_space).ddi_clock_kaby_lake() == ddi_id &&
transcoder_regs.DdiControl().ReadFrom(mmio_space).ddi_kaby_lake() == ddi_id) {
return pipe;
}
}
return nullptr;
}
// static
std::vector<std::unique_ptr<Pipe>> PipeManagerSkylake::GetPipes(fdf::MmioBuffer* mmio_space,
Power* power) {
std::vector<std::unique_ptr<Pipe>> pipes;
for (const auto pipe_enum : PipeIds<registers::Platform::kSkylake>()) {
pipes.push_back(std::make_unique<PipeSkylake>(mmio_space, pipe_enum,
power->GetPipePowerWellRef(pipe_enum)));
}
return pipes;
}
PipeManagerTigerLake::PipeManagerTigerLake(Controller* controller)
: PipeManager(GetPipes(controller->mmio_space(), controller->power())),
mmio_space_(controller->mmio_space()) {
ZX_DEBUG_ASSERT(controller);
}
Pipe* PipeManagerTigerLake::GetAvailablePipe() {
for (Pipe* pipe : *this) {
if (!pipe->in_use()) {
return pipe;
}
}
return nullptr;
}
Pipe* PipeManagerTigerLake::GetPipeFromHwState(DdiId ddi_id, fdf::MmioBuffer* mmio_space) {
for (Pipe* pipe : *this) {
auto transcoder_id = static_cast<TranscoderId>(pipe->pipe_id());
registers::TranscoderRegs regs(transcoder_id);
if (regs.ClockSelect().ReadFrom(mmio_space).ddi_clock_tiger_lake() == ddi_id &&
regs.DdiControl().ReadFrom(mmio_space).ddi_tiger_lake() == ddi_id) {
return pipe;
}
}
return nullptr;
}
void PipeManagerTigerLake::ResetInactiveTranscoders() {
for (Pipe* pipe : *this) {
if (!pipe->in_use()) {
Pipe::ResetTranscoder(pipe->connected_transcoder_id(), registers::Platform::kTigerLake,
mmio_space_);
zxlogf(DEBUG, "Reset unused transcoder %d for pipe %d (pipe inactive)",
pipe->connected_transcoder_id(), pipe->pipe_id());
}
}
}
// static
std::vector<std::unique_ptr<Pipe>> PipeManagerTigerLake::GetPipes(fdf::MmioBuffer* mmio_space,
Power* power) {
std::vector<std::unique_ptr<Pipe>> pipes;
for (const auto pipe_enum : PipeIds<registers::Platform::kTigerLake>()) {
pipes.push_back(std::make_unique<PipeTigerLake>(mmio_space, pipe_enum,
power->GetPipePowerWellRef(pipe_enum)));
}
return pipes;
}
} // namespace i915