blob: 639b21105fff3d1b7c3efa989bc7c9ce8e9ce21f [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 <lib/zx/vmo.h>
#include "display-device.h"
#include "intel-i915.h"
#include "macros.h"
#include "registers.h"
#include "registers-dpll.h"
#include "registers-transcoder.h"
#include "tiling.h"
namespace i915 {
DisplayDevice::DisplayDevice(Controller* controller, uint64_t id,
registers::Ddi ddi, registers::Trans trans, registers::Pipe pipe)
: controller_(controller), id_(id), ddi_(ddi), trans_(trans), pipe_(pipe) {}
DisplayDevice::~DisplayDevice() {
if (inited_) {
ResetPipe();
ResetTrans();
ResetDdi();
}
}
hwreg::RegisterIo* DisplayDevice::mmio_space() const {
return controller_->mmio_space();
}
void DisplayDevice::ResetPipe() {
controller_->ResetPipe(pipe_);
}
bool DisplayDevice::ResetTrans() {
return controller_->ResetTrans(trans_);
}
bool DisplayDevice::ResetDdi() {
return controller_->ResetDdi(ddi_);
}
bool DisplayDevice::Init() {
ddi_power_ = controller_->power()->GetDdiPowerWellRef(ddi_);
pipe_power_ = controller_->power()->GetPipePowerWellRef(pipe_);
if (!QueryDevice(&edid_)) {
return false;
}
auto preferred_timing = edid_.begin();
if (!(preferred_timing != edid_.end())) {
return false;
}
info_.pixel_clock_10khz = (*preferred_timing).pixel_freq_10khz;
info_.h_addressable = (*preferred_timing).horizontal_addressable;
info_.h_front_porch = (*preferred_timing).horizontal_front_porch;
info_.h_sync_pulse = (*preferred_timing).horizontal_sync_pulse;
info_.h_blanking = (*preferred_timing).horizontal_blanking;
info_.v_addressable = (*preferred_timing).vertical_addressable;
info_.v_front_porch = (*preferred_timing).vertical_front_porch;
info_.v_sync_pulse = (*preferred_timing).vertical_sync_pulse;
info_.v_blanking = (*preferred_timing).vertical_blanking;
info_.mode_flags = ((*preferred_timing).vertical_sync_pulse ? MODE_FLAG_VSYNC_POSITIVE : 0)
| ((*preferred_timing).horizontal_sync_pulse ? MODE_FLAG_HSYNC_POSITIVE : 0)
| ((*preferred_timing).interlaced ? MODE_FLAG_INTERLACED : 0);
ResetPipe();
if (!ResetTrans() || !ResetDdi()) {
return false;
}
if (!ConfigureDdi()) {
return false;
}
controller_->interrupts()->EnablePipeVsync(pipe_, true);
inited_ = true;
return true;
}
bool DisplayDevice::Resume() {
if (!ConfigureDdi()) {
return false;
}
controller_->interrupts()->EnablePipeVsync(pipe_, true);
return true;
}
void DisplayDevice::ApplyConfiguration(const display_config_t* config) {
if (config == nullptr) {
ResetPipe();
return;
}
if (memcmp(&config->mode, &info_, sizeof(display_mode_t)) != 0) {
ResetPipe();
ResetTrans();
ResetDdi();
info_ = config->mode;
ConfigureDdi();
}
registers::PipeRegs pipe_regs(pipe());
auto pipe_size = pipe_regs.PipeSourceSize().FromValue(0);
pipe_size.set_horizontal_source_size(info_.h_addressable - 1);
pipe_size.set_vertical_source_size(info_.v_addressable - 1);
pipe_size.WriteTo(mmio_space());
for (unsigned i = 0; i < 3; i++) {
primary_layer_t* primary = nullptr;
for (unsigned j = 0; j < config->layer_count; j++) {
layer_t* layer = config->layers[j];
if (layer->z_index == i) {
primary = &layer->cfg.primary;
break;
}
}
if (!primary) {
auto plane_ctrl = pipe_regs.PlaneControl(i).ReadFrom(controller_->mmio_space());
plane_ctrl.set_plane_enable(0);
plane_ctrl.WriteTo(controller_->mmio_space());
auto plane_surface = pipe_regs.PlaneSurface(i).ReadFrom(controller_->mmio_space());
plane_surface.set_surface_base_addr(0);
plane_surface.WriteTo(controller_->mmio_space());
continue;
}
image_t* image = &primary->image;
const fbl::unique_ptr<GttRegion>& region = controller_->GetGttRegion(image->handle);
region->SetRotation(primary->transform_mode, *image);
uint32_t plane_width;
uint32_t plane_height;
uint32_t stride;
uint32_t x_offset;
uint32_t y_offset;
if (primary->transform_mode == FRAME_TRANSFORM_IDENTITY
|| primary->transform_mode == FRAME_TRANSFORM_ROT_180) {
plane_width = primary->src_frame.width;
plane_height = primary->src_frame.height;
stride = width_in_tiles(image->type, image->width, image->pixel_format);
x_offset = primary->src_frame.x_pos;
y_offset = primary->src_frame.y_pos;
} else {
uint32_t tile_height = height_in_tiles(image->type, image->height, image->pixel_format);
uint32_t tile_px_height = get_tile_px_height(image->type, image->pixel_format);
uint32_t total_height = tile_height * tile_px_height;
plane_width = primary->src_frame.height;
plane_height = primary->src_frame.width;
stride = tile_height;
x_offset = total_height - primary->src_frame.y_pos - primary->src_frame.height;
y_offset = primary->src_frame.x_pos;
}
auto plane_size = pipe_regs.PlaneSurfaceSize(i).FromValue(0);
plane_size.set_width_minus_1(plane_width - 1);
plane_size.set_height_minus_1(plane_height - 1);
plane_size.WriteTo(mmio_space());
auto plane_pos = pipe_regs.PlanePosition(i).FromValue(0);
plane_pos.set_x_pos(primary->dest_frame.x_pos);
plane_pos.set_y_pos(primary->dest_frame.y_pos);
plane_pos.WriteTo(mmio_space());
auto plane_offset = pipe_regs.PlaneOffset(i).FromValue(0);
plane_offset.set_start_x(x_offset);
plane_offset.set_start_y(y_offset);
plane_offset.WriteTo(mmio_space());
auto stride_reg = pipe_regs.PlaneSurfaceStride(i).FromValue(0);
stride_reg.set_stride(stride);
stride_reg.WriteTo(controller_->mmio_space());
auto plane_ctrl = pipe_regs.PlaneControl(i).ReadFrom(controller_->mmio_space());
plane_ctrl.set_plane_enable(1);
plane_ctrl.set_source_pixel_format(plane_ctrl.kFormatRgb8888);
if (primary->image.type == IMAGE_TYPE_SIMPLE) {
plane_ctrl.set_tiled_surface(plane_ctrl.kLinear);
} else if (primary->image.type == IMAGE_TYPE_X_TILED) {
plane_ctrl.set_tiled_surface(plane_ctrl.kTilingX);
} else if (primary->image.type == IMAGE_TYPE_Y_LEGACY_TILED) {
plane_ctrl.set_tiled_surface(plane_ctrl.kTilingYLegacy);
} else {
ZX_ASSERT(primary->image.type == IMAGE_TYPE_YF_TILED);
plane_ctrl.set_tiled_surface(plane_ctrl.kTilingYF);
}
if (primary->transform_mode == FRAME_TRANSFORM_IDENTITY) {
plane_ctrl.set_plane_rotation(plane_ctrl.kIdentity);
} else if (primary->transform_mode == FRAME_TRANSFORM_ROT_90) {
plane_ctrl.set_plane_rotation(plane_ctrl.k90deg);
} else if (primary->transform_mode == FRAME_TRANSFORM_ROT_180) {
plane_ctrl.set_plane_rotation(plane_ctrl.k180deg);
} else {
ZX_ASSERT(primary->transform_mode == FRAME_TRANSFORM_ROT_270);
plane_ctrl.set_plane_rotation(plane_ctrl.k270deg);
}
plane_ctrl.WriteTo(controller_->mmio_space());
uint32_t base_address = static_cast<uint32_t>(region->base());
auto plane_surface = pipe_regs.PlaneSurface(i).ReadFrom(controller_->mmio_space());
plane_surface.set_surface_base_addr(base_address >> plane_surface.kRShiftCount);
plane_surface.WriteTo(controller_->mmio_space());
}
}
} // namespace i915