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 <float.h>
6 #include <fuchsia/hardware/backlight/c/fidl.h>
7 #include <lib/zx/vmo.h>
8 #include <math.h>
9 
10 #include "display-device.h"
11 #include "intel-i915.h"
12 #include "macros.h"
13 #include "registers.h"
14 #include "registers-dpll.h"
15 #include "registers-transcoder.h"
16 #include "tiling.h"
17 
18 namespace {
19 
get_state(void * ctx,fidl_txn_t * txn)20 zx_status_t get_state(void* ctx, fidl_txn_t* txn) {
21     fuchsia_hardware_backlight_State state;
22     {
23         fbl::AutoLock lock(&static_cast<i915::display_ref_t*>(ctx)->mtx);
24         static_cast<i915::display_ref_t*>(ctx)->display_device
25                 ->GetBacklightState(&state.on, &state.brightness);
26     }
27     return fuchsia_hardware_backlight_DeviceGetState_reply(txn, &state);
28 }
29 
set_state(void * ctx,const fuchsia_hardware_backlight_State * state)30 zx_status_t set_state(void* ctx, const fuchsia_hardware_backlight_State* state) {
31     fbl::AutoLock lock(&static_cast<i915::display_ref_t*>(ctx)->mtx);
32 
33     static_cast<i915::display_ref_t*>(ctx)->display_device
34             ->SetBacklightState(state->on, state->brightness);
35     return ZX_OK;
36 }
37 
38 static fuchsia_hardware_backlight_Device_ops_t fidl_ops = {
39     .GetState = get_state,
40     .SetState = set_state,
41 };
42 
backlight_message(void * ctx,fidl_msg_t * msg,fidl_txn_t * txn)43 zx_status_t backlight_message(void* ctx, fidl_msg_t* msg, fidl_txn_t* txn) {
44     return fuchsia_hardware_backlight_Device_dispatch(ctx, txn, msg, &fidl_ops);
45 }
46 
backlight_release(void * ctx)47 void backlight_release(void* ctx) {
48     delete static_cast<i915::display_ref_t*>(ctx);
49 }
50 
51 static zx_protocol_device_t backlight_ops = {};
52 
float_to_i915_csc_offset(float f)53 uint32_t float_to_i915_csc_offset(float f) {
54     ZX_DEBUG_ASSERT(0 <= f && f < 1.0f); // Controller::CheckConfiguration validates this
55 
56     // f is in [0, 1). Multiply by 2^12 to convert to a 12-bit fixed-point fraction.
57     return static_cast<uint32_t>(f * pow(FLT_RADIX, 12));
58 }
59 
float_to_i915_csc_coefficient(float f)60 uint32_t float_to_i915_csc_coefficient(float f) {
61     registers::CscCoeffFormat res;
62     if (f < 0) {
63         f *= -1;
64         res.set_sign(1);
65     }
66 
67     if (f < .125) {
68         res.set_exponent(res.kExponent0125);
69         f /= .125f;
70     } else if (f < .25) {
71         res.set_exponent(res.kExponent025);
72         f /= .25f;
73     } else if (f < .5) {
74         res.set_exponent(res.kExponent05);
75         f /= .5f;
76     } else if (f < 1) {
77         res.set_exponent(res.kExponent1);
78     } else if (f < 2) {
79         res.set_exponent(res.kExponent2);
80         f /= 2.0f;
81     } else {
82         res.set_exponent(res.kExponent4);
83         f /= 4.0f;
84     }
85     f = (f * 512) + .5f;
86 
87     if (f >= 512) {
88         res.set_mantissa(0x1ff);
89     } else {
90         res.set_mantissa(static_cast<uint16_t>(f));
91     }
92 
93     return res.reg_value();
94 }
95 
encode_pipe_color_component(uint8_t component)96 uint32_t encode_pipe_color_component(uint8_t component) {
97     // Convert to unsigned .10 fixed point format
98     return component << 2;
99 }
100 
101 } // namespace
102 
103 namespace i915 {
104 
DisplayDevice(Controller * controller,uint64_t id,registers::Ddi ddi)105 DisplayDevice::DisplayDevice(Controller* controller, uint64_t id, registers::Ddi ddi)
106         : controller_(controller), id_(id), ddi_(ddi) {}
107 
~DisplayDevice()108 DisplayDevice::~DisplayDevice() {
109     if (pipe_) {
110         pipe_->Reset();
111         pipe_->Detach();
112     }
113     if (inited_) {
114         controller_->ResetDdi(ddi());
115     }
116     if (display_ref_) {
117         fbl::AutoLock lock(&display_ref_->mtx);
118         device_remove(backlight_device_);
119         display_ref_->display_device = nullptr;
120     }
121 }
122 
mmio_space() const123 ddk::MmioBuffer* DisplayDevice::mmio_space() const {
124     return controller_->mmio_space();
125 }
126 
Init()127 bool DisplayDevice::Init() {
128     ddi_power_ = controller_->power()->GetDdiPowerWellRef(ddi_);
129 
130     if (!InitDdi()) {
131         return false;
132     }
133 
134     inited_ = true;
135 
136     InitBacklight();
137 
138     return true;
139 }
140 
InitBacklight()141 void DisplayDevice::InitBacklight() {
142     if (HasBacklight() && InitBacklightHw()) {
143         fbl::AllocChecker ac;
144         auto display_ref = fbl::make_unique_checked<display_ref_t>(&ac);
145         zx_status_t status = ZX_ERR_NO_MEMORY;
146         if (ac.check()) {
147             mtx_init(&display_ref->mtx, mtx_plain);
148             {
149                 fbl::AutoLock lock(&display_ref->mtx);
150                 display_ref->display_device = this;
151             }
152 
153             backlight_ops.version = DEVICE_OPS_VERSION;
154             backlight_ops.message = backlight_message;
155             backlight_ops.release = backlight_release;
156 
157             device_add_args_t args = {};
158             args.version = DEVICE_ADD_ARGS_VERSION;
159             args.name = "backlight";
160             args.ctx = display_ref.get();
161             args.ops = &backlight_ops;
162             args.proto_id = ZX_PROTOCOL_BACKLIGHT;
163 
164             if ((status = device_add(controller_->zxdev(), &args, &backlight_device_)) == ZX_OK) {
165                 display_ref_ = display_ref.release();
166             }
167         }
168         if (display_ref_ == nullptr) {
169             LOG_WARN("Failed to add backlight (%d)\n", status);
170         }
171 
172         SetBacklightState(true, 255);
173     }
174 }
175 
Resume()176 bool DisplayDevice::Resume() {
177     if (!DdiModeset(info_, pipe_->pipe(), pipe_->transcoder())) {
178         return false;
179     }
180     if (pipe_) {
181         pipe_->Resume();
182     }
183     return true;
184 }
185 
LoadActiveMode()186 void DisplayDevice::LoadActiveMode() {
187     pipe_->LoadActiveMode(&info_);
188     info_.pixel_clock_10khz = LoadClockRateForTranscoder(pipe_->transcoder());
189 }
190 
AttachPipe(Pipe * pipe)191 bool DisplayDevice::AttachPipe(Pipe* pipe) {
192     if (pipe == pipe_) {
193         return false;
194     }
195 
196     if (pipe_) {
197         pipe_->Reset();
198         pipe_->Detach();
199     }
200     if (pipe) {
201         pipe->AttachToDisplay(id_, controller()->igd_opregion().IsEdp(ddi()));
202 
203         if (info_.h_addressable) {
204             PipeConfigPreamble(info_, pipe->pipe(), pipe->transcoder());
205             pipe->ApplyModeConfig(info_);
206             PipeConfigEpilogue(info_, pipe->pipe(), pipe->transcoder());
207         }
208     }
209     pipe_ = pipe;
210     return true;
211 }
212 
CheckNeedsModeset(const display_mode_t * mode)213 bool DisplayDevice::CheckNeedsModeset(const display_mode_t* mode) {
214     // Check the clock and the flags later
215     size_t cmp_start = offsetof(display_mode_t, h_addressable);
216     size_t cmp_end = offsetof(display_mode_t, flags);
217     if (memcmp(&mode->h_addressable, &info_.h_addressable, cmp_end - cmp_start)) {
218         // Modeset is necessary if display params other than the clock frequency differ
219         LOG_SPEW("Modeset necessary for display params");
220         return true;
221     }
222 
223     // TODO(stevensd): There are still some situations where the BIOS is better at setting up
224     // the display than we are. The BIOS seems to not always set the hsync/vsync polarity, so
225     // don't include that in the check for already initialized displays. Once we're better at
226     // initializing displays, merge the flags check back into the above memcmp.
227     if ((mode->flags & MODE_FLAG_INTERLACED) != (info_.flags & MODE_FLAG_INTERLACED)) {
228         LOG_SPEW("Modeset necessary for display flags");
229         return true;
230     }
231 
232     if (mode->pixel_clock_10khz == info_.pixel_clock_10khz) {
233         // Modeset is necessary not necessary if all display params are the same
234         return false;
235     }
236 
237     // Check to see if the hardware was already configured properly. The is primarily to
238     // prevent unnecessary modesetting at startup. The extra work this adds to regular
239     // modesetting is negligible.
240     auto dpll_ctrl2 = registers::DpllControl2::Get().ReadFrom(mmio_space());
241     const dpll_state_t* current_state = nullptr;
242     if (!dpll_ctrl2.ddi_clock_off(ddi()).get()) {
243         current_state = controller_->GetDpllState(
244                 static_cast<registers::Dpll>(dpll_ctrl2.ddi_clock_select(ddi()).get()));
245     }
246 
247     if (current_state == nullptr) {
248         LOG_SPEW("Modeset necessary for clock");
249         return true;
250     }
251 
252     dpll_state_t new_state;
253     if (!ComputeDpllState(mode->pixel_clock_10khz, &new_state)) {
254         // ComputeDpllState should be validated in the display's CheckDisplayMode
255         ZX_ASSERT(false);
256     }
257 
258     // Modesetting is necessary if the states are not equal
259     bool res = !Controller::CompareDpllStates(*current_state, new_state);
260     if (res) {
261         LOG_SPEW("Modeset necessary for clock state");
262     }
263     return res;
264 }
265 
ApplyConfiguration(const display_config_t * config)266 void DisplayDevice::ApplyConfiguration(const display_config_t* config) {
267     ZX_ASSERT(config);
268 
269     if (CheckNeedsModeset(&config->mode)) {
270         info_ = config->mode;
271 
272         DdiModeset(info_, pipe_->pipe(), pipe_->transcoder());
273 
274         PipeConfigPreamble(info_, pipe_->pipe(), pipe_->transcoder());
275         pipe_->ApplyModeConfig(info_);
276         PipeConfigEpilogue(info_, pipe_->pipe(), pipe_->transcoder());
277     }
278 
279     pipe_->ApplyConfiguration(config);
280 }
281 
282 } // namespace i915
283