1 // Copyright 2018 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 <limits.h>
6 #include <ddk/protocol/pci-lib.h>
7 #include <hwreg/bitfields.h>
8 #include <lib/zx/object.h>
9 #include <lib/zx/vmar.h>
10 
11 #include "igd.h"
12 #include "intel-i915.h"
13 #include "macros.h"
14 
15 namespace {
16 
17 // Register definitions from IGD OpRegion/Software SCI documentation. Section
18 // numbers reference Skylake Sept 2016 rev 0.5.
19 
20 // The number of eDP panel types supported by the IGD API
21 static const uint32_t kNumPanelTypes = 16;
22 
23 // GMCH SWSCI Register - 5.1.1
24 class GmchSwsciRegister : public hwreg::RegisterBase<GmchSwsciRegister, uint16_t> {
25 public:
26     DEF_BIT(15, sci_event_select);
27     DEF_BIT(0, gmch_sw_sci_trigger);
28 
Get()29     static auto Get() { return hwreg::RegisterAddr<GmchSwsciRegister>(0); }
30 };
31 
32 // Entry half of Software SCI Entry/Exit Parameters - 3.3.1
33 class SciEntryParam : public hwreg::RegisterBase<SciEntryParam, uint32_t> {
34 public:
35     DEF_RSVDZ_FIELD(31, 16);
36     DEF_FIELD(15, 8, subfunction);
37     DEF_RSVDZ_FIELD(7, 5);
38     DEF_FIELD(4, 1, function);
39     DEF_BIT(0, swsci_indicator);
40 
41     // Main function codes
42     static const uint32_t kFuncGetBiosData = 4;
43 
44     // GetBiosData sub-function codes
45     static const uint32_t kGbdaSupportedCalls = 0;
46     static const uint32_t kGbdaPanelDetails = 5;
47 
Get()48     static auto Get() { return hwreg::RegisterAddr<SciEntryParam>(0); }
49 };
50 
51 // Exit half of Software SCI Entry/Exit Parameters - 3.3.1
52 class SciExitParam : public hwreg::RegisterBase<SciExitParam, uint32_t> {
53 public:
54     DEF_RSVDZ_FIELD(31, 16);
55     DEF_FIELD(15, 8, exit_param);
56     DEF_FIELD(7, 5, exit_result);
57     DEF_RSVDZ_FIELD(4, 1);
58     DEF_BIT(0, swsci_indicator);
59 
60     constexpr static uint32_t kResultOk = 1;
61 
Get()62     static auto Get() { return hwreg::RegisterAddr<SciExitParam>(0); }
63 };
64 
65 // Additional param return value for GetBiosData supported calls function - 4.2.2
66 class GbdaSupportedCalls : public hwreg::RegisterBase<GbdaSupportedCalls, uint32_t> {
67 public:
68     DEF_RSVDZ_FIELD(31, 11);
69     DEF_BIT(10, get_aksv);
70     DEF_BIT(9, spread_spectrum_clocks);
71     DEF_RSVDZ_FIELD(8, 7);
72     DEF_BIT(6, internal_graphics);
73     DEF_BIT(5, tv_std_video_connector_info);
74     DEF_BIT(4, get_panel_details);
75     DEF_BIT(3, get_boot_display_preference);
76     DEF_RSVDZ_FIELD(2, 1);
77     DEF_BIT(0, requested_system_callbacks);
78 
Get()79     static auto Get() { return hwreg::RegisterAddr<GbdaSupportedCalls>(0); }
80 };
81 
82 // Additional param return value for GetBiosData panel details function - 4.2.5
83 class GbdaPanelDetails : public hwreg::RegisterBase<GbdaPanelDetails, uint32_t> {
84 public:
85     DEF_RSVDZ_FIELD(31, 23);
86     DEF_FIELD(22, 20, bia_ctrl);
87     DEF_FIELD(19, 18, blc_support);
88     DEF_RSVDZ_BIT(17);
89     DEF_BIT(16, lid_state);
90     DEF_FIELD(15, 8, panel_type_plus1);
91     DEF_FIELD(7, 0, panel_scaling);
92 
Get()93     static auto Get() { return hwreg::RegisterAddr<GbdaPanelDetails>(0); }
94 };
95 
iboost_idx_to_level(uint8_t iboost_idx)96 static uint8_t iboost_idx_to_level(uint8_t iboost_idx) {
97     switch (iboost_idx) {
98         case 0: return 1;
99         case 1: return 3;
100         case 2: return 7;
101         default:
102             LOG_INFO("Invalid iboost override\n");
103             return 0;
104     }
105 }
106 
107 } // namespace
108 
109 namespace i915 {
110 
IgdOpRegion()111 IgdOpRegion::IgdOpRegion() {}
112 
~IgdOpRegion()113 IgdOpRegion::~IgdOpRegion() {
114     if (igd_opregion_pages_base_) {
115         zx::vmar::root_self()->unmap(igd_opregion_pages_base_, igd_opregion_pages_len_);
116     }
117 }
118 
GetSection(uint16_t * size)119 template<typename T> T* IgdOpRegion::GetSection(uint16_t* size) {
120     return reinterpret_cast<T*>(GetSection(T::kBlockType, size));
121 }
122 
GetSection(uint8_t type,uint16_t * size)123 uint8_t* IgdOpRegion::GetSection(uint8_t type, uint16_t* size) {
124     uint8_t* data = reinterpret_cast<uint8_t*>(bdb_);
125     uint16_t idx = bdb_->header_size;
126 
127     while (idx < bdb_->bios_data_blocks_size - sizeof(block_header_t)) {
128         block_header_t* header = reinterpret_cast<block_header_t*>(data + idx);
129         uint16_t block_size = static_cast<uint16_t>(header->size_low | (header->size_high << 8));
130         if (block_size > bdb_->bios_data_blocks_size) {
131             return nullptr;
132         }
133         uint16_t new_idx = static_cast<uint16_t>(idx + block_size + sizeof(block_header_t));
134         if (new_idx <= bdb_->bios_data_blocks_size && header->type == type) {
135             *size = block_size;
136             return data + idx + sizeof(block_header_t);
137         }
138         idx = new_idx;
139     }
140 
141     return nullptr;
142 }
143 
ProcessDdiConfigs()144 bool IgdOpRegion::ProcessDdiConfigs() {
145     uint16_t size;
146     general_definitions_t* defs = GetSection<general_definitions_t>(&size);
147     if (defs == nullptr) {
148         LOG_ERROR("Couldn't find vbt general definitions\n");
149         return false;
150     }
151     if (size < sizeof(general_definitions_t)) {
152         LOG_ERROR("Bad size in vbt general definitions\n");
153         return false;
154     }
155     uint16_t num_configs = static_cast<uint16_t>(
156             (size - sizeof(general_definitions_t)) / defs->ddi_config_size);
157     for (int i = 0; i < num_configs; i++) {
158         ddi_config_t* cfg = reinterpret_cast<ddi_config_t*>(defs->ddis + i * defs->ddi_config_size);
159         if (!cfg->ddi_flags) {
160             continue;
161         }
162 
163         auto ddi_flags = DdiFlags::Get().FromValue(cfg->ddi_flags);
164         uint8_t idx;
165         if (cfg->port_type < 4 || cfg->port_type == 12) {
166             // Types 0, 1, 2, 3, and 12 are HDMI ports A, B, C, D, and E
167             if (!ddi_flags.tmds()) {
168                 LOG_WARN("Malformed hdmi config\n");
169                 continue;
170             }
171 
172             idx = cfg->port_type < 4 ? static_cast<registers::Ddi>(cfg->port_type)
173                                      : registers::DDI_E;
174         } else if (7 <= cfg->port_type && cfg->port_type <= 11) {
175             // Types 7, 8, 9, 10, 11 are DP ports B, C, D, A, E
176             if (!ddi_flags.dp()) {
177                 LOG_WARN("Malformed dp config\n");
178                 continue;
179             }
180 
181             if (cfg->port_type <= 9) {
182                 idx = static_cast<uint8_t>(cfg->port_type - 6);
183             } else if (cfg->port_type == 10) {
184                 idx = registers::DDI_A;
185             } else {
186                 ZX_DEBUG_ASSERT(cfg->port_type == 11);
187                 idx = registers::DDI_E;
188             }
189         } else {
190             continue;
191         }
192 
193         if (ddi_supports_dvi_[idx] || ddi_supports_dp_[idx]) {
194             LOG_WARN("Duplicate ddi config\n");
195             continue;
196         }
197         ddi_supports_dvi_[idx] = ddi_flags.tmds();
198         ddi_supports_hdmi_[idx] = ddi_flags.tmds() && !ddi_flags.not_hdmi();
199         ddi_supports_dp_[idx] = ddi_flags.dp();
200         ddi_is_edp_[idx] = ddi_flags.dp() && ddi_flags.internal();
201 
202         hdmi_buffer_translation_idx_[idx] = cfg->ddi_buf_trans_idx();
203         if (cfg->has_iboost_override()) {
204             iboosts_[idx].dp_iboost = iboost_idx_to_level(cfg->dp_iboost_override());
205             iboosts_[idx].hdmi_iboost = iboost_idx_to_level(cfg->hdmi_iboost_override());
206         } else {
207             iboosts_[idx].dp_iboost = 0;
208             iboosts_[idx].hdmi_iboost = 0;
209         }
210     }
211 
212     return true;
213 }
214 
Swsci(pci_protocol_t * pci,uint16_t function,uint16_t subfunction,uint32_t additional_param,uint16_t * exit_param,uint32_t * additional_res)215 bool IgdOpRegion::Swsci(pci_protocol_t* pci,
216                         uint16_t function, uint16_t subfunction, uint32_t additional_param,
217                         uint16_t* exit_param, uint32_t* additional_res) {
218     uint16_t val;
219     if (pci_config_read16(pci, kIgdSwSciReg, &val) != ZX_OK) {
220         LOG_WARN("Failed to read SWSCI register\n");
221         return false;
222     }
223     auto gmch_swsci_reg = GmchSwsciRegister::Get().FromValue(val);
224     if (!gmch_swsci_reg.sci_event_select() || gmch_swsci_reg.gmch_sw_sci_trigger()) {
225         LOG_WARN("Bad GMCH SWSCI register value (%04x)\n", val);
226         return false;
227     }
228 
229     sci_interface_t* sci_interface = reinterpret_cast<sci_interface_t*>(igd_opregion_->mailbox2);
230 
231     auto sci_entry_param = SciEntryParam::Get().FromValue(0);
232     sci_entry_param.set_function(function);
233     sci_entry_param.set_subfunction(subfunction);
234     sci_entry_param.set_swsci_indicator(1);
235     sci_interface->entry_and_exit_params = sci_entry_param.reg_value();
236     sci_interface->additional_params = additional_param;
237 
238     if (pci_config_write16(pci, kIgdSwSciReg,
239                            gmch_swsci_reg.set_gmch_sw_sci_trigger(1).reg_value()) != ZX_OK) {
240         LOG_WARN("Failed to write SWSCI register\n");
241         return false;
242     }
243 
244     // The spec says to wait for 2ms if driver_sleep_timeout isn't set, but that's not
245     // long enough. I've seen delays as long as 10ms, so use 50ms to be safe.
246     int timeout_ms = sci_interface->driver_sleep_timeout ? sci_interface->driver_sleep_timeout : 50;
247     while (timeout_ms-- > 0) {
248         auto sci_exit_param = SciExitParam::Get().FromValue(sci_interface->entry_and_exit_params);
249         if (!sci_exit_param.swsci_indicator()) {
250             if (sci_exit_param.exit_result() == SciExitParam::kResultOk) {
251                 *exit_param = static_cast<uint16_t>(sci_exit_param.exit_param());
252                 *additional_res = sci_interface->additional_params;
253                 return true;
254             } else {
255                 LOG_WARN("SWSCI failed (%x)\n", sci_exit_param.exit_result());
256                 return false;
257             }
258         }
259         zx_nanosleep(zx_deadline_after(ZX_MSEC(1)));
260     }
261     LOG_WARN("SWSCI timeout\n");
262     return false;
263 }
264 
GetPanelType(pci_protocol_t * pci,uint8_t * type)265 bool IgdOpRegion::GetPanelType(pci_protocol_t* pci, uint8_t* type) {
266     uint16_t exit_param;
267     uint32_t additional_res;
268     // TODO(stevensd): cache the supported calls when we nede to use Swsci more than once
269     if (Swsci(pci, SciEntryParam::kFuncGetBiosData, SciEntryParam::kGbdaSupportedCalls,
270               0 /* unused additional_param */, &exit_param, &additional_res)) {
271         auto support = GbdaSupportedCalls::Get().FromValue(additional_res);
272         if (support.get_panel_details()) {
273             // TODO(stevensd): Support the case where there is >1 eDP panel
274             uint32_t panel_number = 0;
275             if (Swsci(pci, SciEntryParam::kFuncGetBiosData, SciEntryParam::kGbdaPanelDetails,
276                       panel_number, &exit_param, &additional_res)) {
277                 auto details = GbdaPanelDetails::Get().FromValue(additional_res);
278                 if (details.panel_type_plus1()
279                         && details.panel_type_plus1() < (kNumPanelTypes + 1)) {
280                     *type = static_cast<uint8_t>(details.panel_type_plus1() - 1);
281                     LOG_SPEW("SWSCI panel type %d\n", *type);
282                     return true;
283                 }
284             }
285 
286         }
287     }
288 
289     uint16_t size;
290     lvds_config_t* cfg = GetSection<lvds_config_t>(&size);
291     if (!cfg || cfg->panel_type >= kNumPanelTypes) {
292         return false;
293     }
294     *type = cfg->panel_type;
295 
296     return true;
297 }
298 
CheckForLowVoltageEdp(pci_protocol_t * pci)299 bool IgdOpRegion::CheckForLowVoltageEdp(pci_protocol_t* pci) {
300     bool has_edp = true;
301     for (unsigned i = 0; i < registers::kDdiCount; i++) {
302         has_edp |= ddi_is_edp_[i];
303     }
304     if (!has_edp) {
305         LOG_SPEW("No edp found\n");
306         return true;
307     }
308 
309     uint16_t size;
310     edp_config_t* edp = GetSection<edp_config_t>(&size);
311     if (edp == nullptr) {
312         LOG_WARN("Couldn't find edp general definitions\n");
313         return false;
314     }
315 
316     if (!GetPanelType(pci, &panel_type_)) {
317         LOG_TRACE("No panel type\n");
318         return false;
319     }
320     edp_is_low_voltage_ =
321             !((edp->vswing_preemphasis[panel_type_ / 2] >> (4 * panel_type_ % 2)) & 0xf);
322 
323     LOG_TRACE("Is low voltage edp? %d\n", edp_is_low_voltage_);
324 
325     return true;
326 }
327 
ProcessBacklightData()328 void IgdOpRegion::ProcessBacklightData() {
329     uint16_t size;
330     lfp_backlight_t* data = GetSection<lfp_backlight_t>(&size);
331 
332     if (data) {
333         lfp_backlight_entry_t* e = &data->entries[panel_type_];
334         min_backlight_brightness_ = e->min_brightness / 255.0;
335     }
336 }
337 
Init(pci_protocol_t * pci)338 zx_status_t IgdOpRegion::Init(pci_protocol_t* pci) {
339     uint32_t igd_addr;
340     zx_status_t status = pci_config_read32(pci, kIgdOpRegionAddrReg, &igd_addr);
341     if (status != ZX_OK || !igd_addr) {
342         LOG_ERROR("Failed to locate IGD OpRegion (%d)\n", status);
343         return status;
344     }
345 
346     // TODO(stevensd): This is directly mapping a physical address into our address space, which
347     // is not something we'll be able to do forever. At some point, there will need to be an
348     // actual API (probably in ACPI) to do this.
349     zx_handle_t vmo;
350     uint32_t igd_opregion_pages_len_ = kIgdOpRegionLen + (igd_addr & PAGE_SIZE);
351     status = zx_vmo_create_physical(get_root_resource(),
352                                     igd_addr & ~(PAGE_SIZE - 1),
353                                     igd_opregion_pages_len_, &vmo);
354     if (status != ZX_OK) {
355         LOG_ERROR("Failed to access IGD OpRegion (%d)\n", status);
356         return status;
357     }
358     igd_opregion_pages_ = zx::vmo(vmo);
359 
360     status = zx::vmar::root_self()->map(0, igd_opregion_pages_, 0, igd_opregion_pages_len_,
361                                        ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
362                                        &igd_opregion_pages_base_);
363     if (status != ZX_OK) {
364         LOG_ERROR("Failed to map IGD OpRegion (%d)\n", status);
365         return status;
366     }
367 
368     igd_opregion_ = reinterpret_cast<igd_opregion_t*>(
369             igd_opregion_pages_base_ + (igd_addr % PAGE_SIZE));
370     if (!igd_opregion_->validate()) {
371         LOG_ERROR("Failed to validate IGD OpRegion\n");
372         return ZX_ERR_INTERNAL;
373     }
374 
375     vbt_header_t* vbt_header = reinterpret_cast<vbt_header_t*>(&igd_opregion_->mailbox4);
376     if (!vbt_header->validate()) {
377         LOG_ERROR("Failed to validate vbt header\n");
378         return ZX_ERR_INTERNAL;
379     }
380 
381     bdb_ = reinterpret_cast<bios_data_blocks_header_t*>(
382             igd_opregion_->mailbox4 + vbt_header->bios_data_blocks_offset);
383     uint16_t vbt_size = vbt_header->vbt_size;
384     if (!bdb_->validate()
385             || bdb_->bios_data_blocks_size > vbt_size
386             || vbt_header->bios_data_blocks_offset + bdb_->bios_data_blocks_size > vbt_size) {
387         LOG_ERROR("Failed to validate bdb header\n");
388         return ZX_ERR_INTERNAL;
389     }
390 
391     // TODO(stevensd): 196 seems old enough that all gen9 processors will have it. If we want to
392     // support older hardware, we'll need to handle missing data.
393     if (bdb_->version < 196) {
394         LOG_ERROR("Out of date vbt (%d)\n", bdb_->version);
395         return ZX_ERR_INTERNAL;
396     }
397 
398     if (!ProcessDdiConfigs() || !CheckForLowVoltageEdp(pci)) {
399         return ZX_ERR_INTERNAL;
400     }
401 
402     ProcessBacklightData();
403 
404     return ZX_OK;
405 }
406 
407 }
408