1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <lib/zx/vmo.h> 6#include <float.h> 7#include <math.h> 8#include <zircon/device/backlight.h> 9 10#include "display-device.h" 11#include "intel-i915.h" 12#include "macros.h" 13#include "pipe.h" 14#include "registers.h" 15#include "registers-dpll.h" 16#include "registers-transcoder.h" 17#include "tiling.h" 18 19namespace { 20 21uint32_t float_to_i915_csc_offset(float f) { 22 ZX_DEBUG_ASSERT(0 <= f && f < 1.0f); // Controller::CheckConfiguration validates this 23 24 // f is in [0, 1). Multiply by 2^12 to convert to a 12-bit fixed-point fraction. 25 return static_cast<uint32_t>(f * pow(FLT_RADIX, 12)); 26} 27 28uint32_t float_to_i915_csc_coefficient(float f) { 29 registers::CscCoeffFormat res; 30 if (f < 0) { 31 f *= -1; 32 res.set_sign(1); 33 } 34 35 if (f < .125) { 36 res.set_exponent(res.kExponent0125); 37 f /= .125f; 38 } else if (f < .25) { 39 res.set_exponent(res.kExponent025); 40 f /= .25f; 41 } else if (f < .5) { 42 res.set_exponent(res.kExponent05); 43 f /= .5f; 44 } else if (f < 1) { 45 res.set_exponent(res.kExponent1); 46 } else if (f < 2) { 47 res.set_exponent(res.kExponent2); 48 f /= 2.0f; 49 } else { 50 res.set_exponent(res.kExponent4); 51 f /= 4.0f; 52 } 53 f = (f * 512) + .5f; 54 55 if (f >= 512) { 56 res.set_mantissa(0x1ff); 57 } else { 58 res.set_mantissa(static_cast<uint16_t>(f)); 59 } 60 61 return res.reg_value(); 62} 63 64uint32_t encode_pipe_color_component(uint8_t component) { 65 // Convert to unsigned .10 fixed point format 66 return component << 2; 67} 68 69} // namespace 70 71namespace i915 { 72 73Pipe::Pipe(Controller* controller, registers::Pipe pipe) 74 : controller_(controller), pipe_(pipe) {} 75 76hwreg::RegisterIo* Pipe::mmio_space() const { 77 return controller_->mmio_space(); 78} 79 80void Pipe::Init() { 81 pipe_power_ = controller_->power()->GetPipePowerWellRef(pipe_); 82 controller_->interrupts()->EnablePipeVsync(pipe_, true); 83} 84 85void Pipe::Resume() { 86 controller_->interrupts()->EnablePipeVsync(pipe_, true); 87} 88 89void Pipe::Reset() { 90 controller_->ResetPipe(pipe_); 91 controller_->ResetTrans(transcoder()); 92} 93 94void Pipe::Detach() { 95 attached_display_ = INVALID_DISPLAY_ID; 96} 97 98void Pipe::AttachToDisplay(uint64_t id, bool is_edp) { 99 attached_display_ = id; 100 attached_edp_ = is_edp; 101} 102 103void Pipe::ApplyModeConfig(const display_mode_t& mode) { 104 registers::TranscoderRegs trans_regs(transcoder()); 105 106 // Configure the rest of the transcoder 107 uint32_t h_active = mode.h_addressable - 1; 108 uint32_t h_sync_start = h_active + mode.h_front_porch; 109 uint32_t h_sync_end = h_sync_start + mode.h_sync_pulse; 110 uint32_t h_total = h_active + mode.h_blanking; 111 112 uint32_t v_active = mode.v_addressable - 1; 113 uint32_t v_sync_start = v_active + mode.v_front_porch; 114 uint32_t v_sync_end = v_sync_start + mode.v_sync_pulse; 115 uint32_t v_total = v_active + mode.v_blanking; 116 117 auto h_total_reg = trans_regs.HTotal().FromValue(0); 118 h_total_reg.set_count_total(h_total); 119 h_total_reg.set_count_active(h_active); 120 h_total_reg.WriteTo(mmio_space()); 121 auto v_total_reg = trans_regs.VTotal().FromValue(0); 122 v_total_reg.set_count_total(v_total); 123 v_total_reg.set_count_active(v_active); 124 v_total_reg.WriteTo(mmio_space()); 125 126 auto h_sync_reg = trans_regs.HSync().FromValue(0); 127 h_sync_reg.set_sync_start(h_sync_start); 128 h_sync_reg.set_sync_end(h_sync_end); 129 h_sync_reg.WriteTo(mmio_space()); 130 auto v_sync_reg = trans_regs.VSync().FromValue(0); 131 v_sync_reg.set_sync_start(v_sync_start); 132 v_sync_reg.set_sync_end(v_sync_end); 133 v_sync_reg.WriteTo(mmio_space()); 134 135 // The Intel docs say that H/VBlank should be programmed with the same H/VTotal 136 trans_regs.HBlank().FromValue(h_total_reg.reg_value()).WriteTo(mmio_space()); 137 trans_regs.VBlank().FromValue(v_total_reg.reg_value()).WriteTo(mmio_space()); 138 139 registers::PipeRegs pipe_regs(pipe()); 140 auto pipe_size = pipe_regs.PipeSourceSize().FromValue(0); 141 pipe_size.set_horizontal_source_size(mode.h_addressable - 1); 142 pipe_size.set_vertical_source_size(mode.v_addressable - 1); 143 pipe_size.WriteTo(mmio_space()); 144} 145 146void Pipe::LoadActiveMode(display_mode_t* mode) { 147 registers::TranscoderRegs trans_regs(transcoder()); 148 149 auto h_total_reg = trans_regs.HTotal().ReadFrom(mmio_space()); 150 uint32_t h_total = h_total_reg.count_total(); 151 uint32_t h_active = h_total_reg.count_active(); 152 auto v_total_reg = trans_regs.VTotal().ReadFrom(mmio_space()); 153 uint32_t v_total = v_total_reg.count_total(); 154 uint32_t v_active = v_total_reg.count_active(); 155 156 auto h_sync_reg = trans_regs.HSync().ReadFrom(mmio_space()); 157 uint32_t h_sync_start = h_sync_reg.sync_start(); 158 uint32_t h_sync_end = h_sync_reg.sync_end(); 159 auto v_sync_reg = trans_regs.VSync().ReadFrom(mmio_space()); 160 uint32_t v_sync_start = v_sync_reg.sync_start(); 161 uint32_t v_sync_end = v_sync_reg.sync_end(); 162 163 mode->h_addressable = h_active + 1; 164 mode->h_front_porch = h_sync_start - h_active; 165 mode->h_sync_pulse = h_sync_end - h_sync_start; 166 mode->h_blanking = h_total - h_active; 167 168 mode->v_addressable = v_active + 1; 169 mode->v_front_porch = v_sync_start - v_active; 170 mode->v_sync_pulse = v_sync_end - v_sync_start; 171 mode->v_blanking = v_total - v_active; 172 173 mode->flags = 0; 174 auto ddi_func_ctrl = trans_regs.DdiFuncControl().ReadFrom(mmio_space()); 175 if (ddi_func_ctrl.sync_polarity() & 0x2) { 176 mode->flags |= MODE_FLAG_VSYNC_POSITIVE; 177 } 178 if (ddi_func_ctrl.sync_polarity() & 0x1) { 179 mode->flags |= MODE_FLAG_HSYNC_POSITIVE; 180 } 181 if (trans_regs.Conf().ReadFrom(mmio_space()).interlaced_mode()) { 182 mode->flags |= MODE_FLAG_INTERLACED; 183 } 184 185 // If we're reusing hardware state, make sure the pipe source size matches 186 // the display mode size, since we never scale pipes. 187 registers::PipeRegs pipe_regs(pipe_); 188 auto pipe_size = pipe_regs.PipeSourceSize().FromValue(0); 189 pipe_size.set_horizontal_source_size(mode->h_addressable - 1); 190 pipe_size.set_vertical_source_size(mode->v_addressable - 1); 191 pipe_size.WriteTo(mmio_space()); 192} 193 194void Pipe::ApplyConfiguration(const display_config_t* config) { 195 ZX_ASSERT(config); 196 197 registers::pipe_arming_regs_t regs; 198 registers::PipeRegs pipe_regs(pipe_); 199 200 if (config->cc_flags) { 201 float zero_offset[3] = {}; 202 SetColorConversionOffsets(true, config->cc_flags & COLOR_CONVERSION_PREOFFSET ? 203 config->cc_preoffsets : zero_offset); 204 SetColorConversionOffsets(false, config->cc_flags & COLOR_CONVERSION_POSTOFFSET ? 205 config->cc_postoffsets : zero_offset); 206 207 float identity[3][3] = { 208 { 1, 0, 0, }, 209 { 0, 1, 0, }, 210 { 0, 0, 1, }, 211 }; 212 for (uint32_t i = 0; i < 3; i++) { 213 for (uint32_t j = 0; j < 3; j++) { 214 float val = config->cc_flags & COLOR_CONVERSION_COEFFICIENTS ? 215 config->cc_coefficients[i][j] : identity[i][j]; 216 217 auto reg = pipe_regs.CscCoeff(i, j).ReadFrom(mmio_space()); 218 reg.coefficient(i, j).set( float_to_i915_csc_coefficient(val)); 219 reg.WriteTo(mmio_space()); 220 } 221 } 222 } 223 regs.csc_mode = pipe_regs.CscMode().ReadFrom(mmio_space()).reg_value(); 224 225 auto bottom_color = pipe_regs.PipeBottomColor().FromValue(0); 226 bottom_color.set_csc_enable(!!config->cc_flags); 227 bool has_color_layer = config->layer_count && config->layers[0]->type == LAYER_COLOR; 228 if (has_color_layer) { 229 color_layer_t* layer = &config->layers[0]->cfg.color; 230 ZX_DEBUG_ASSERT(layer->format == ZX_PIXEL_FORMAT_RGB_x888 231 || layer->format == ZX_PIXEL_FORMAT_ARGB_8888); 232 uint32_t color = *reinterpret_cast<uint32_t*>(layer->color); 233 234 bottom_color.set_r(encode_pipe_color_component(static_cast<uint8_t>(color >> 16))); 235 bottom_color.set_g(encode_pipe_color_component(static_cast<uint8_t>(color >> 8))); 236 bottom_color.set_b(encode_pipe_color_component(static_cast<uint8_t>(color))); 237 } 238 regs.pipe_bottom_color = bottom_color.reg_value(); 239 240 bool scaler_1_claimed = false; 241 for (unsigned plane = 0; plane < 3; plane++) { 242 primary_layer_t* primary = nullptr; 243 for (unsigned j = 0; j < config->layer_count; j++) { 244 layer_t* layer = config->layers[j]; 245 if (layer->type == LAYER_PRIMARY && (layer->z_index - has_color_layer) == plane) { 246 primary = &layer->cfg.primary; 247 break; 248 } 249 } 250 ConfigurePrimaryPlane(plane, primary, !!config->cc_flags, &scaler_1_claimed, ®s); 251 } 252 cursor_layer_t* cursor = nullptr; 253 if (config->layer_count && config->layers[config->layer_count - 1]->type == LAYER_CURSOR) { 254 cursor = &config->layers[config->layer_count - 1]->cfg.cursor; 255 } 256 ConfigureCursorPlane(cursor, !!config->cc_flags, ®s); 257 258 pipe_regs.CscMode().FromValue(regs.csc_mode).WriteTo(mmio_space()); 259 pipe_regs.PipeBottomColor().FromValue(regs.pipe_bottom_color).WriteTo(mmio_space()); 260 pipe_regs.CursorBase().FromValue(regs.cur_base).WriteTo(mmio_space()); 261 pipe_regs.CursorPos().FromValue(regs.cur_pos).WriteTo(mmio_space()); 262 for (unsigned i = 0; i < registers::kImagePlaneCount; i++) { 263 pipe_regs.PlaneSurface(i).FromValue(regs.plane_surf[i]).WriteTo(mmio_space()); 264 } 265 pipe_regs.PipeScalerWinSize(0).FromValue(regs.ps_win_sz[0]).WriteTo(mmio_space()); 266 if (pipe_ != registers::PIPE_C) { 267 pipe_regs.PipeScalerWinSize(1) 268 .FromValue(regs.ps_win_sz[1]).WriteTo(mmio_space()); 269 } 270} 271 272void Pipe::ConfigurePrimaryPlane(uint32_t plane_num, const primary_layer_t* primary, 273 bool enable_csc, bool* scaler_1_claimed, 274 registers::pipe_arming_regs_t* regs) { 275 registers::PipeRegs pipe_regs(pipe()); 276 277 auto plane_ctrl = pipe_regs.PlaneControl(plane_num).ReadFrom(controller_->mmio_space()); 278 if (primary == nullptr) { 279 plane_ctrl.set_plane_enable(0).WriteTo(mmio_space()); 280 regs->plane_surf[plane_num] = 0; 281 return; 282 } 283 284 const image_t* image = &primary->image; 285 286 const fbl::unique_ptr<GttRegion>& region = controller_->GetGttRegion(image->handle); 287 region->SetRotation(primary->transform_mode, *image); 288 289 uint32_t plane_width; 290 uint32_t plane_height; 291 uint32_t stride; 292 uint32_t x_offset; 293 uint32_t y_offset; 294 if (primary->transform_mode == FRAME_TRANSFORM_IDENTITY 295 || primary->transform_mode == FRAME_TRANSFORM_ROT_180) { 296 plane_width = primary->src_frame.width; 297 plane_height = primary->src_frame.height; 298 stride = width_in_tiles(image->type, image->width, image->pixel_format); 299 x_offset = primary->src_frame.x_pos; 300 y_offset = primary->src_frame.y_pos; 301 } else { 302 uint32_t tile_height = height_in_tiles(image->type, image->height, image->pixel_format); 303 uint32_t tile_px_height = get_tile_px_height(image->type, image->pixel_format); 304 uint32_t total_height = tile_height * tile_px_height; 305 306 plane_width = primary->src_frame.height; 307 plane_height = primary->src_frame.width; 308 stride = tile_height; 309 x_offset = total_height - primary->src_frame.y_pos - primary->src_frame.height; 310 y_offset = primary->src_frame.x_pos; 311 } 312 313 if (plane_width == primary->dest_frame.width 314 && plane_height == primary->dest_frame.height) { 315 auto plane_pos = pipe_regs.PlanePosition(plane_num).FromValue(0); 316 plane_pos.set_x_pos(primary->dest_frame.x_pos); 317 plane_pos.set_y_pos(primary->dest_frame.y_pos); 318 plane_pos.WriteTo(mmio_space()); 319 320 // If there's a scaler pointed at this plane, immediately disable it 321 // in case there's nothing else that will claim it this frame. 322 if (scaled_planes_[pipe()][plane_num]) { 323 uint32_t scaler_idx = scaled_planes_[pipe()][plane_num] - 1; 324 pipe_regs.PipeScalerCtrl(scaler_idx) 325 .ReadFrom(mmio_space()).set_enable(0).WriteTo(mmio_space()); 326 scaled_planes_[pipe()][plane_num] = 0; 327 regs->ps_win_sz[scaler_idx] = 0; 328 } 329 } else { 330 pipe_regs.PlanePosition(plane_num).FromValue(0).WriteTo(mmio_space()); 331 332 auto ps_ctrl = pipe_regs.PipeScalerCtrl(*scaler_1_claimed).ReadFrom(mmio_space()); 333 ps_ctrl.set_mode(ps_ctrl.kDynamic); 334 if (primary->src_frame.width > 2048) { 335 float max_dynamic_height = static_cast<float>(plane_height) 336 * registers::PipeScalerCtrl::kDynamicMaxVerticalRatio2049; 337 if (static_cast<uint32_t>(max_dynamic_height) < primary->dest_frame.height) { 338 // TODO(stevensd): This misses some cases where 7x5 can be used. 339 ps_ctrl.set_enable(ps_ctrl.k7x5); 340 } 341 } 342 ps_ctrl.set_binding(plane_num + 1); 343 ps_ctrl.set_enable(1); 344 ps_ctrl.WriteTo(mmio_space()); 345 346 auto ps_win_pos = pipe_regs.PipeScalerWinPosition(*scaler_1_claimed).FromValue(0); 347 ps_win_pos.set_x_pos(primary->dest_frame.x_pos); 348 ps_win_pos.set_x_pos(primary->dest_frame.y_pos); 349 ps_win_pos.WriteTo(mmio_space()); 350 351 auto ps_win_size = pipe_regs.PipeScalerWinSize(*scaler_1_claimed).FromValue(0); 352 ps_win_size.set_x_size(primary->dest_frame.width); 353 ps_win_size.set_y_size(primary->dest_frame.height); 354 regs->ps_win_sz[*scaler_1_claimed] = ps_win_size.reg_value(); 355 356 scaled_planes_[pipe()][plane_num] = (*scaler_1_claimed) + 1; 357 *scaler_1_claimed = true; 358 } 359 360 auto plane_size = pipe_regs.PlaneSurfaceSize(plane_num).FromValue(0); 361 plane_size.set_width_minus_1(plane_width - 1); 362 plane_size.set_height_minus_1(plane_height - 1); 363 plane_size.WriteTo(mmio_space()); 364 365 auto plane_offset = pipe_regs.PlaneOffset(plane_num).FromValue(0); 366 plane_offset.set_start_x(x_offset); 367 plane_offset.set_start_y(y_offset); 368 plane_offset.WriteTo(mmio_space()); 369 370 auto stride_reg = pipe_regs.PlaneSurfaceStride(plane_num).FromValue(0); 371 stride_reg.set_stride(stride); 372 stride_reg.WriteTo(controller_->mmio_space()); 373 374 auto plane_key_mask = pipe_regs.PlaneKeyMask(plane_num).FromValue(0); 375 if (primary->alpha_mode != ALPHA_DISABLE && !isnan(primary->alpha_layer_val)) { 376 plane_key_mask.set_plane_alpha_enable(1); 377 378 uint8_t alpha = static_cast<uint8_t>(round(primary->alpha_layer_val * 255)); 379 380 auto plane_key_max = pipe_regs.PlaneKeyMax(plane_num).FromValue(0); 381 plane_key_max.set_plane_alpha_value(alpha); 382 plane_key_max.WriteTo(mmio_space()); 383 } 384 plane_key_mask.WriteTo(mmio_space()); 385 if (primary->alpha_mode == ALPHA_DISABLE 386 || primary->image.pixel_format == ZX_PIXEL_FORMAT_RGB_x888) { 387 plane_ctrl.set_alpha_mode(plane_ctrl.kAlphaDisable); 388 } else if (primary->alpha_mode == ALPHA_PREMULTIPLIED) { 389 plane_ctrl.set_alpha_mode(plane_ctrl.kAlphaPreMultiply); 390 } else { 391 ZX_ASSERT(primary->alpha_mode == ALPHA_HW_MULTIPLY); 392 plane_ctrl.set_alpha_mode(plane_ctrl.kAlphaHwMultiply); 393 } 394 395 plane_ctrl.set_plane_enable(1); 396 plane_ctrl.set_pipe_csc_enable(enable_csc); 397 plane_ctrl.set_source_pixel_format(plane_ctrl.kFormatRgb8888); 398 if (primary->image.type == IMAGE_TYPE_SIMPLE) { 399 plane_ctrl.set_tiled_surface(plane_ctrl.kLinear); 400 } else if (primary->image.type == IMAGE_TYPE_X_TILED) { 401 plane_ctrl.set_tiled_surface(plane_ctrl.kTilingX); 402 } else if (primary->image.type == IMAGE_TYPE_Y_LEGACY_TILED) { 403 plane_ctrl.set_tiled_surface(plane_ctrl.kTilingYLegacy); 404 } else { 405 ZX_ASSERT(primary->image.type == IMAGE_TYPE_YF_TILED); 406 plane_ctrl.set_tiled_surface(plane_ctrl.kTilingYF); 407 } 408 if (primary->transform_mode == FRAME_TRANSFORM_IDENTITY) { 409 plane_ctrl.set_plane_rotation(plane_ctrl.kIdentity); 410 } else if (primary->transform_mode == FRAME_TRANSFORM_ROT_90) { 411 plane_ctrl.set_plane_rotation(plane_ctrl.k90deg); 412 } else if (primary->transform_mode == FRAME_TRANSFORM_ROT_180) { 413 plane_ctrl.set_plane_rotation(plane_ctrl.k180deg); 414 } else { 415 ZX_ASSERT(primary->transform_mode == FRAME_TRANSFORM_ROT_270); 416 plane_ctrl.set_plane_rotation(plane_ctrl.k270deg); 417 } 418 plane_ctrl.WriteTo(controller_->mmio_space()); 419 420 uint32_t base_address = static_cast<uint32_t>(region->base()); 421 422 auto plane_surface = pipe_regs.PlaneSurface(plane_num).ReadFrom(controller_->mmio_space()); 423 plane_surface.set_surface_base_addr(base_address >> plane_surface.kRShiftCount); 424 regs->plane_surf[plane_num] = plane_surface.reg_value(); 425} 426 427void Pipe::ConfigureCursorPlane(const cursor_layer_t* cursor, bool enable_csc, 428 registers::pipe_arming_regs_t* regs) { 429 registers::PipeRegs pipe_regs(pipe()); 430 431 auto cursor_ctrl = pipe_regs.CursorCtrl().ReadFrom(controller_->mmio_space()); 432 // The hardware requires that the cursor has at least one pixel on the display, 433 // so disable the plane if there is no overlap. 434 if (cursor == nullptr) { 435 cursor_ctrl.set_mode_select(cursor_ctrl.kDisabled).WriteTo(mmio_space()); 436 regs->cur_base = regs->cur_pos = 0; 437 return; 438 } 439 440 if (cursor->image.width == 64) { 441 cursor_ctrl.set_mode_select(cursor_ctrl.kArgb64x64); 442 } else if (cursor->image.width == 128) { 443 cursor_ctrl.set_mode_select(cursor_ctrl.kArgb128x128); 444 } else if (cursor->image.width == 256) { 445 cursor_ctrl.set_mode_select(cursor_ctrl.kArgb256x256); 446 } else { 447 // The configuration was not properly validated 448 ZX_ASSERT(false); 449 } 450 cursor_ctrl.set_pipe_csc_enable(enable_csc); 451 cursor_ctrl.WriteTo(mmio_space()); 452 453 auto cursor_pos = pipe_regs.CursorPos().FromValue(0); 454 if (cursor->x_pos < 0) { 455 cursor_pos.set_x_sign(1); 456 cursor_pos.set_x_pos(-cursor->x_pos); 457 } else { 458 cursor_pos.set_x_pos(cursor->x_pos); 459 } 460 if (cursor->y_pos < 0) { 461 cursor_pos.set_y_sign(1); 462 cursor_pos.set_y_pos(-cursor->y_pos); 463 } else { 464 cursor_pos.set_y_pos(cursor->y_pos); 465 } 466 regs->cur_pos = cursor_pos.reg_value(); 467 468 uint32_t base_address = 469 static_cast<uint32_t>(reinterpret_cast<uint64_t>(cursor->image.handle)); 470 auto cursor_base = pipe_regs.CursorBase().ReadFrom(controller_->mmio_space()); 471 cursor_base.set_cursor_base(base_address >> cursor_base.kPageShift); 472 regs->cur_base = cursor_base.reg_value(); 473} 474 475void Pipe::SetColorConversionOffsets(bool preoffsets, const float vals[3]) { 476 registers::PipeRegs pipe_regs(pipe()); 477 478 for (uint32_t i = 0; i < 3; i++) { 479 float offset = vals[i]; 480 auto offset_reg = pipe_regs.CscOffset(preoffsets, i).FromValue(0); 481 if (offset < 0) { 482 offset_reg.set_sign(1); 483 offset *= -1; 484 } 485 offset_reg.set_magnitude(float_to_i915_csc_offset(offset)); 486 offset_reg.WriteTo(mmio_space()); 487 } 488} 489 490} // namespace i915 491