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