blob: a39c2634486675f7df0b5be117f88cb8c2b04065 [file] [log] [blame]
// Copyright 2017 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 "display-device.h"
#include <float.h>
#include <lib/zx/vmo.h>
#include <math.h>
#include <ddktl/fidl.h>
#include "intel-i915.h"
#include "macros.h"
#include "registers-dpll.h"
#include "registers-transcoder.h"
#include "registers.h"
#include "tiling.h"
namespace {
zx_status_t backlight_message(void* ctx, fidl_incoming_msg_t* msg, fidl_txn_t* txn) {
DdkTransaction transaction(txn);
i915::DisplayDevice* ptr;
{
fbl::AutoLock lock(&static_cast<i915::display_ref_t*>(ctx)->mtx);
ptr = static_cast<i915::display_ref_t*>(ctx)->display_device;
}
fidl::WireDispatch<fuchsia_hardware_backlight::Device>(ptr, msg, &transaction);
return transaction.Status();
}
void backlight_release(void* ctx) { delete static_cast<i915::display_ref_t*>(ctx); }
static zx_protocol_device_t backlight_ops = {};
} // namespace
namespace i915 {
DisplayDevice::DisplayDevice(Controller* controller, uint64_t id, registers::Ddi ddi)
: controller_(controller), id_(id), ddi_(ddi) {}
DisplayDevice::~DisplayDevice() {
if (pipe_) {
pipe_->Reset();
pipe_->Detach();
}
if (inited_) {
controller_->ResetDdi(ddi());
}
if (display_ref_) {
fbl::AutoLock lock(&display_ref_->mtx);
display_ref_->display_device = nullptr;
device_async_remove(backlight_device_);
}
}
ddk::MmioBuffer* DisplayDevice::mmio_space() const { return controller_->mmio_space(); }
bool DisplayDevice::Init() {
ddi_power_ = controller_->power()->GetDdiPowerWellRef(ddi_);
if (!InitDdi()) {
return false;
}
inited_ = true;
InitBacklight();
return true;
}
void DisplayDevice::InitBacklight() {
if (HasBacklight() && InitBacklightHw()) {
fbl::AllocChecker ac;
auto display_ref = fbl::make_unique_checked<display_ref_t>(&ac);
zx_status_t status = ZX_ERR_NO_MEMORY;
if (ac.check()) {
mtx_init(&display_ref->mtx, mtx_plain);
{
fbl::AutoLock lock(&display_ref->mtx);
display_ref->display_device = this;
}
backlight_ops.version = DEVICE_OPS_VERSION;
backlight_ops.message = backlight_message;
backlight_ops.release = backlight_release;
device_add_args_t args = {};
args.version = DEVICE_ADD_ARGS_VERSION;
args.name = "backlight";
args.ctx = display_ref.get();
args.ops = &backlight_ops;
args.proto_id = ZX_PROTOCOL_BACKLIGHT;
if ((status = device_add(controller_->zxdev(), &args, &backlight_device_)) == ZX_OK) {
display_ref_ = display_ref.release();
}
}
if (display_ref_ == nullptr) {
zxlogf(WARNING, "Failed to add backlight (%d)", status);
}
SetBacklightState(true, 1.0);
}
}
bool DisplayDevice::Resume() {
if (!DdiModeset(info_, pipe_->pipe(), pipe_->transcoder())) {
return false;
}
if (pipe_) {
pipe_->Resume();
}
return true;
}
void DisplayDevice::LoadActiveMode() {
pipe_->LoadActiveMode(&info_);
info_.pixel_clock_10khz = LoadClockRateForTranscoder(pipe_->transcoder());
}
bool DisplayDevice::AttachPipe(Pipe* pipe) {
if (pipe == pipe_) {
return false;
}
if (pipe_) {
pipe_->Reset();
pipe_->Detach();
}
if (pipe) {
pipe->AttachToDisplay(id_, controller()->igd_opregion().IsEdp(ddi()));
if (info_.h_addressable) {
PipeConfigPreamble(info_, pipe->pipe(), pipe->transcoder());
pipe->ApplyModeConfig(info_);
PipeConfigEpilogue(info_, pipe->pipe(), pipe->transcoder());
}
}
pipe_ = pipe;
return true;
}
bool DisplayDevice::CheckNeedsModeset(const display_mode_t* mode) {
// Check the clock and the flags later
size_t cmp_start = offsetof(display_mode_t, h_addressable);
size_t cmp_end = offsetof(display_mode_t, flags);
if (memcmp(&mode->h_addressable, &info_.h_addressable, cmp_end - cmp_start)) {
// Modeset is necessary if display params other than the clock frequency differ
zxlogf(DEBUG, "Modeset necessary for display params");
return true;
}
// TODO(stevensd): There are still some situations where the BIOS is better at setting up
// the display than we are. The BIOS seems to not always set the hsync/vsync polarity, so
// don't include that in the check for already initialized displays. Once we're better at
// initializing displays, merge the flags check back into the above memcmp.
if ((mode->flags & MODE_FLAG_INTERLACED) != (info_.flags & MODE_FLAG_INTERLACED)) {
zxlogf(DEBUG, "Modeset necessary for display flags");
return true;
}
if (mode->pixel_clock_10khz == info_.pixel_clock_10khz) {
// Modeset is necessary not necessary if all display params are the same
return false;
}
// Check to see if the hardware was already configured properly. The is primarily to
// prevent unnecessary modesetting at startup. The extra work this adds to regular
// modesetting is negligible.
auto dpll_ctrl2 = registers::DpllControl2::Get().ReadFrom(mmio_space());
const dpll_state_t* current_state = nullptr;
if (!dpll_ctrl2.ddi_clock_off(ddi()).get()) {
current_state = controller_->GetDpllState(
static_cast<registers::Dpll>(dpll_ctrl2.ddi_clock_select(ddi()).get()));
}
if (current_state == nullptr) {
zxlogf(DEBUG, "Modeset necessary for clock");
return true;
}
dpll_state_t new_state;
if (!ComputeDpllState(mode->pixel_clock_10khz, &new_state)) {
// ComputeDpllState should be validated in the display's CheckDisplayMode
ZX_ASSERT(false);
}
// Modesetting is necessary if the states are not equal
bool res = !Controller::CompareDpllStates(*current_state, new_state);
if (res) {
zxlogf(DEBUG, "Modeset necessary for clock state");
}
return res;
}
void DisplayDevice::ApplyConfiguration(const display_config_t* config) {
ZX_ASSERT(config);
if (CheckNeedsModeset(&config->mode)) {
info_ = config->mode;
DdiModeset(info_, pipe_->pipe(), pipe_->transcoder());
PipeConfigPreamble(info_, pipe_->pipe(), pipe_->transcoder());
pipe_->ApplyModeConfig(info_);
PipeConfigEpilogue(info_, pipe_->pipe(), pipe_->transcoder());
}
pipe_->ApplyConfiguration(config);
}
void DisplayDevice::GetStateNormalized(GetStateNormalizedCompleter::Sync& completer) {
zx_status_t status = ZX_OK;
FidlBacklight::wire::State state = {};
if (display_ref_ != nullptr) {
fbl::AutoLock lock(&display_ref_->mtx);
if (display_ref_->display_device != nullptr) {
status =
display_ref_->display_device->GetBacklightState(&state.backlight_on, &state.brightness);
}
} else {
status = ZX_ERR_BAD_STATE;
}
FidlBacklight::wire::Device_GetStateNormalized_Result result;
FidlBacklight::wire::Device_GetStateNormalized_Response response{.state = state};
if (status == ZX_OK) {
result.set_response(
fidl::ObjectView<FidlBacklight::wire::Device_GetStateNormalized_Response>::FromExternal(
&response));
} else {
result.set_err(fidl::ObjectView<zx_status_t>::FromExternal(&status));
}
completer.Reply(std::move(result));
}
void DisplayDevice::SetStateNormalized(FidlBacklight::wire::State state,
SetStateNormalizedCompleter::Sync& completer) {
zx_status_t status = ZX_OK;
if (display_ref_ != nullptr) {
fbl::AutoLock lock(&display_ref_->mtx);
if (display_ref_->display_device != nullptr) {
status =
display_ref_->display_device->SetBacklightState(state.backlight_on, state.brightness);
}
} else {
status = ZX_ERR_BAD_STATE;
}
if (status != ZX_OK) {
completer.ReplyError(status);
return;
}
completer.ReplySuccess();
}
void DisplayDevice::GetStateAbsolute(GetStateAbsoluteCompleter::Sync& completer) {
FidlBacklight::wire::Device_GetStateAbsolute_Result result;
zx_status_t status = ZX_ERR_NOT_SUPPORTED;
result.set_err(fidl::ObjectView<zx_status_t>::FromExternal(&status));
completer.Reply(std::move(result));
}
void DisplayDevice::SetStateAbsolute(FidlBacklight::wire::State state,
SetStateAbsoluteCompleter::Sync& completer) {
FidlBacklight::wire::Device_SetStateAbsolute_Result result;
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void DisplayDevice::GetMaxAbsoluteBrightness(GetMaxAbsoluteBrightnessCompleter::Sync& completer) {
FidlBacklight::wire::Device_GetMaxAbsoluteBrightness_Result result;
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void DisplayDevice::SetNormalizedBrightnessScale(
__UNUSED double scale, SetNormalizedBrightnessScaleCompleter::Sync& completer) {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void DisplayDevice::GetNormalizedBrightnessScale(
GetNormalizedBrightnessScaleCompleter::Sync& completer) {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
} // namespace i915