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 #pragma once
6 
7 #include <ddk/protocol/pci.h>
8 
9 #include <hwreg/bitfields.h>
10 #include <inttypes.h>
11 #include <stddef.h>
12 #include <string.h>
13 #include <zircon/assert.h>
14 #include <zircon/types.h>
15 #include <lib/zx/vmo.h>
16 
17 #include "registers-ddi.h"
18 
19 namespace i915 {
20 // Various definitions from IGD OpRegion/Software SCI documentation.
21 
22 // Offsets into the PCI configuration space of IGD registers
23 constexpr uint16_t kIgdSwSciReg = 0xe8;
24 constexpr uint16_t kIgdOpRegionAddrReg = 0xfc;
25 
26 // Length of the igd opregion
27 constexpr uint32_t kIgdOpRegionLen = 0x2000;
28 
29 constexpr uint32_t kMaxVbtSize = 6144;
30 
31 typedef struct igd_opregion {
32     uint8_t signature[16];
33     uint32_t kb_size;
34     uint32_t version;
35     uint8_t system_bios_build_version[32];
36     uint8_t video_bios_build_version[16];
37     uint8_t graphics_bios_build_version[16];
38     uint32_t supported_mailboxes;
39     uint32_t driver_model;
40     uint32_t pcon;
41     uint8_t gop_version[32];
42     uint8_t rsvd[124];
43 
44     uint8_t mailbox1[256];
45     uint8_t mailbox2[256];
46     uint8_t mailbox3[256];
47     uint8_t mailbox4[kMaxVbtSize];
48     uint8_t mailbox5[1024];
49 
validateigd_opregion50     bool validate() {
51         const char* sig = "IntelGraphicsMem";
52         return !memcmp(signature, reinterpret_cast<const void*>(sig), 16)
53                 && kb_size >= (sizeof(struct igd_opregion) >> 10);
54     }
55 } igd_opregion_t;
56 
57 static_assert(sizeof(igd_opregion_t) == 0x2000, "Bad igd opregion len");
58 static_assert(offsetof(igd_opregion_t, mailbox4) == 1024, "Bad mailbox4 offset");
59 
60 typedef struct sci_interface {
61     uint32_t entry_and_exit_params;
62     uint32_t additional_params;
63     uint32_t driver_sleep_timeout;
64     uint8_t rsvd[240];
65 } sci_interface_t;
66 
67 static_assert(sizeof(sci_interface_t) == 252, "Bad sci_interface_t size");
68 
69 // Header for each bios data block.
70 typedef struct block_header {
71     uint8_t type;
72     // Size of the block, not including the header
73     uint8_t size_low;
74     uint8_t size_high;
75 } block_header_t;
76 static_assert(sizeof(block_header_t) == 3, "Bad block_header size");
77 
78 typedef struct bios_data_blocks_header {
79     uint8_t signature[16];
80     uint16_t version;
81     // Size of the header by itself
82     uint16_t header_size;
83     // Size of the header + all the blocks
84     uint16_t bios_data_blocks_size;
85 
validatebios_data_blocks_header86     bool validate() {
87         const char* sig = "BIOS_DATA_BLOCK";
88         return !memcmp(signature, sig, 15) && bios_data_blocks_size >= sizeof(block_header_t);
89     }
90 } bios_data_blocks_header_t;
91 static_assert(sizeof(bios_data_blocks_header_t) == 22, "Bad bios_data_blocks_header size");
92 
93 typedef struct vbt_header {
94     uint8_t signature[20];
95     uint16_t version;
96     uint16_t header_size;
97     uint16_t vbt_size;
98     uint8_t checksum;
99     uint8_t rsvd;
100     uint32_t bios_data_blocks_offset;
101     uint32_t aim_offset[4];
102 
validatevbt_header103     bool validate() {
104         const char* sig_prefix = "$VBT";
105         return !memcmp(signature, sig_prefix, 4)
106             && sizeof(bios_data_blocks_header_t) < vbt_size && vbt_size <= kMaxVbtSize
107             && bios_data_blocks_offset < vbt_size - sizeof(bios_data_blocks_header_t);
108     }
109 } vbt_header_t;
110 static_assert(sizeof(vbt_header_t) == 48, "Bad vbt_header size");
111 
112 typedef struct general_definitions {
113     static constexpr uint32_t kBlockType = 2;
114 
115     uint8_t unused[4];
116     // Contains the length of each entry in ddis.
117     uint8_t ddi_config_size;
118     // Array of ddi_config structures.
119     uint8_t ddis[0];
120 } general_definitions_t;
121 
122 // Bitfield for ddi_config's ddi_flags register
123 class DdiFlags : public hwreg::RegisterBase<DdiFlags, uint16_t> {
124 public:
125     DEF_BIT(12, internal);
126     DEF_BIT(11, not_hdmi);
127     DEF_BIT(4, tmds);
128     DEF_BIT(2, dp);
129 
Get()130     static auto Get() { return hwreg::RegisterAddr<DdiFlags>(0); }
131 };
132 
133 typedef struct ddi_config {
134     uint8_t unused1[2];
135     // See DdiFlags class
136     uint16_t ddi_flags;
137     uint8_t unused2[3];
138 
139     uint8_t hdmi_cfg;
140     // Index into the recommended buffer translation table to use when
141     // configuring DDI_BUF_TRANS[9] for HDMI/DVI.
142     DEF_SUBFIELD(hdmi_cfg, 3, 0, ddi_buf_trans_idx);
143 
144     uint8_t unused3[8];
145     // Specifies the DDI this config this corresponds to as well as type of DDI.
146     uint8_t port_type;
147     uint8_t unused4[6];
148 
149     uint8_t flags;
150     // Flag that indicates that there is an iboost override. An override enables
151     // iboost for all DDI_BUF_TRANS values and overrides the recommended iboost.
152     DEF_SUBBIT(flags, 3, has_iboost_override);
153 
154     uint8_t unused5[13];
155 
156     uint8_t iboost_levels;
157     // The iboost override level, if has_iboost_override is set.
158     DEF_SUBFIELD(iboost_levels, 7, 4, hdmi_iboost_override);
159     DEF_SUBFIELD(iboost_levels, 3, 0, dp_iboost_override);
160 } ddi_config_t;
161 static_assert(offsetof(ddi_config_t, ddi_flags) == 2, "Bad ddi_flags offset");
162 static_assert(offsetof(ddi_config_t, hdmi_cfg) == 7, "Bad hdmi_cfg offset");
163 static_assert(offsetof(ddi_config_t, port_type) == 16, "Bad port_type offset");
164 static_assert(offsetof(ddi_config_t, flags) == 23, "Bad flags offset");
165 static_assert(offsetof(ddi_config_t, iboost_levels) == 37, "Bad iboost_levels offset");
166 static_assert(sizeof(ddi_config_t) == 38, "Bad ddi_config_t size");
167 
168 typedef struct edp_config {
169     static constexpr uint32_t kBlockType = 27;
170 
171     uint8_t unused[204];
172     // Contains 16 nibbles, one for each panel type 0x0-0xf. If the value
173     // is 0, then the panel is a low voltage panel.
174     uint8_t vswing_preemphasis[8];
175     // A bunch of other unused stuff
176 } edp_config_t;
177 static_assert(offsetof(edp_config_t, vswing_preemphasis) == 204, "Bad vswing_preemphasis offset");
178 
179 typedef struct lvds_config {
180     static constexpr uint32_t kBlockType = 40;
181 
182     // The default panel type for the hardware. Can be overridden by the IGD
183     // SCI panel details function.
184     uint8_t panel_type;
185     // A bunch of other unused stuff
186 } lvds_config_t;
187 
188 typedef struct lfp_backlight_entry {
189     uint8_t flags;
190     uint8_t pwm_freq_hz_low;
191     uint8_t pwm_freq_hz_high;
192     uint8_t min_brightness;
193     uint8_t unused[2];
194 } lfp_backlight_entry_t;
195 static_assert(sizeof(lfp_backlight_entry_t) == 6, "Bad struct size");
196 
197 typedef struct lfp_backlight {
198     static constexpr uint32_t kBlockType = 43;
199 
200     uint8_t entry_size;
201     lfp_backlight_entry_t entries[16];
202     uint8_t level[16];
203 } lfp_backlight_t;
204 static_assert(sizeof(lfp_backlight_t) == 113, "Bad struct size");
205 
206 class IgdOpRegion {
207 public:
208     IgdOpRegion();
209     ~IgdOpRegion();
210     zx_status_t Init(pci_protocol_t* pci);
211 
SupportsHdmi(registers::Ddi ddi)212     bool SupportsHdmi(registers::Ddi ddi) const {
213         return ddi_supports_hdmi_[ddi];
214     }
SupportsDvi(registers::Ddi ddi)215     bool SupportsDvi(registers::Ddi ddi) const {
216         return ddi_supports_dvi_[ddi];
217     }
SupportsDp(registers::Ddi ddi)218     bool SupportsDp(registers::Ddi ddi) const {
219         return ddi_supports_dp_[ddi];
220     }
IsEdp(registers::Ddi ddi)221     bool IsEdp(registers::Ddi ddi) const {
222         return ddi_is_edp_[ddi];
223     }
224 
IsLowVoltageEdp(registers::Ddi ddi)225     bool IsLowVoltageEdp(registers::Ddi ddi) const {
226         ZX_DEBUG_ASSERT(SupportsDp(ddi));
227         // TODO(stevensd): Support the case where more than one type of edp panel is present.
228         return ddi_is_edp_[ddi] && edp_is_low_voltage_;
229     }
230 
GetIBoost(registers::Ddi ddi,bool is_dp)231     uint8_t GetIBoost(registers::Ddi ddi, bool is_dp) const {
232         return is_dp ? iboosts_[ddi].dp_iboost : iboosts_[ddi].hdmi_iboost;
233     }
234 
235     static constexpr uint8_t kUseDefaultIdx = 0xff;
GetHdmiBufferTranslationIndex(registers::Ddi ddi)236     uint8_t GetHdmiBufferTranslationIndex(registers::Ddi ddi) const {
237         ZX_DEBUG_ASSERT(SupportsHdmi(ddi) || SupportsDvi(ddi));
238         return hdmi_buffer_translation_idx_[ddi];
239     }
240 
GetMinBacklightBrightness()241     double GetMinBacklightBrightness() const {
242         return min_backlight_brightness_;
243     }
244 
245 private:
246     template<typename T> T* GetSection(uint16_t* size);
247     uint8_t* GetSection(uint8_t tag, uint16_t* size);
248     bool ProcessDdiConfigs();
249     bool CheckForLowVoltageEdp(pci_protocol_t* pci);
250     bool GetPanelType(pci_protocol_t* pci, uint8_t* type);
251     bool Swsci(pci_protocol_t* pci, uint16_t function, uint16_t subfunction,
252                uint32_t additional_param, uint16_t* exit_param, uint32_t* additional_res);
253     void ProcessBacklightData();
254 
255     zx::vmo igd_opregion_pages_;
256     uintptr_t igd_opregion_pages_base_;
257     uintptr_t igd_opregion_pages_len_;
258     igd_opregion_t* igd_opregion_;
259     bios_data_blocks_header_t* bdb_;
260 
261     bool ddi_supports_hdmi_[registers::kDdiCount] = {};
262     bool ddi_supports_dvi_[registers::kDdiCount] = {};
263     bool ddi_supports_dp_[registers::kDdiCount] = {};
264     bool ddi_is_edp_[registers::kDdiCount] = {};
265 
266     bool edp_is_low_voltage_;
267     uint8_t panel_type_;
268     double min_backlight_brightness_;
269 
270     struct {
271         uint8_t hdmi_iboost;
272         uint8_t dp_iboost;
273     } iboosts_[registers::kDdiCount];
274     uint8_t hdmi_buffer_translation_idx_[registers::kDdiCount];
275 };
276 
277 } // namespace i915
278