| // 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 |