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, &regs);
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, &regs);
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