1 /*
2 * Copyright 2005-2015, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include <stdlib.h>
8 #include <string.h>
9
10 #include <compute_display_timing.h>
11 #include <create_display_modes.h>
12
13 #include "accelerant_protos.h"
14 #include "accelerant.h"
15 #include "utility.h"
16 #include "vesa_info.h"
17
18
19 //#define TRACE_MODE
20 #ifdef TRACE_MODE
21 extern "C" void _sPrintf(const char* format, ...);
22 # define TRACE(x) _sPrintf x
23 #else
24 # define TRACE(x) ;
25 #endif
26
27
28 struct nvidia_resolution {
29 int width;
30 int height;
31 };
32
33 static const nvidia_resolution kNVidiaAllowedResolutions[] = {
34 { 1280, 720 },
35 { 1280, 800 },
36 { 1360, 768 },
37 { 1400, 1050 },
38 { 1440, 900 },
39 { 1600, 900 },
40 { 1600, 1200 },
41 { 1680, 1050 },
42 { 1920, 1080 },
43 { 1920, 1200 },
44 { 2048, 1536 },
45 };
46
47
48 static uint32
get_color_space_for_depth(uint32 depth)49 get_color_space_for_depth(uint32 depth)
50 {
51 switch (depth) {
52 case 4:
53 return B_GRAY8;
54 // the app_server is smart enough to translate this to VGA mode
55 case 8:
56 return B_CMAP8;
57 case 15:
58 return B_RGB15;
59 case 16:
60 return B_RGB16;
61 case 24:
62 return B_RGB24;
63 case 32:
64 return B_RGB32;
65 }
66
67 return 0;
68 }
69
70
71 /*! Checks if the specified \a mode can be set using VESA. */
72 static bool
is_mode_supported(display_mode * mode)73 is_mode_supported(display_mode* mode)
74 {
75 vesa_mode* modes = gInfo->vesa_modes;
76
77 bool colorspaceSupported = false;
78
79 for (uint32 i = gInfo->shared_info->vesa_mode_count; i-- > 0;) {
80 // search mode in VESA mode list
81 // TODO: list is ordered, we could use binary search
82 if (modes[i].width == mode->virtual_width
83 && modes[i].height == mode->virtual_height
84 && get_color_space_for_depth(modes[i].bits_per_pixel)
85 == mode->space)
86 return true;
87
88 if (get_color_space_for_depth(modes[i].bits_per_pixel) == mode->space)
89 colorspaceSupported = true;
90 }
91
92 bios_type_enum type = gInfo->shared_info->bios_type;
93 if (type == kIntelBiosType || type == kAtomBiosType1 || type == kAtomBiosType2) {
94 // We know how to patch the BIOS, so we can set any mode we want
95 return colorspaceSupported;
96 }
97
98 if (type == kNVidiaBiosType) {
99 for (size_t i = 0; i < B_COUNT_OF(kNVidiaAllowedResolutions); i++) {
100 if (mode->virtual_width == kNVidiaAllowedResolutions[i].width
101 && mode->virtual_height == kNVidiaAllowedResolutions[i].height)
102 return colorspaceSupported;
103 }
104 }
105
106 return false;
107 }
108
109
110 /*! Creates the initial mode list of the primary accelerant.
111 It's called from vesa_init_accelerant().
112 */
113 status_t
create_mode_list(void)114 create_mode_list(void)
115 {
116 const color_space kVesaSpaces[] = {B_RGB32_LITTLE, B_RGB24_LITTLE,
117 B_RGB16_LITTLE, B_RGB15_LITTLE, B_CMAP8};
118
119 uint32 initialModesCount = 0;
120
121 // Add initial VESA modes.
122 display_mode* initialModes = (display_mode*)malloc(
123 sizeof(display_mode) * gInfo->shared_info->vesa_mode_count);
124 if (initialModes != NULL) {
125 initialModesCount = gInfo->shared_info->vesa_mode_count;
126 vesa_mode* vesaModes = gInfo->vesa_modes;
127
128 for (uint32 i = 0; i < initialModesCount; i++) {
129 compute_display_timing(vesaModes[i].width, vesaModes[i].height,
130 60, false, &initialModes[i].timing);
131 fill_display_mode(vesaModes[i].width, vesaModes[i].height,
132 &initialModes[i]);
133 }
134 }
135
136 gInfo->mode_list_area = create_display_modes("vesa modes",
137 gInfo->shared_info->has_edid ? &gInfo->shared_info->edid_info : NULL,
138 initialModes, initialModesCount,
139 kVesaSpaces, sizeof(kVesaSpaces) / sizeof(kVesaSpaces[0]),
140 is_mode_supported, &gInfo->mode_list, &gInfo->shared_info->mode_count);
141
142 free(initialModes);
143
144 if (gInfo->mode_list_area < 0)
145 return gInfo->mode_list_area;
146
147 gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
148 return B_OK;
149 }
150
151
152 // #pragma mark -
153
154
155 uint32
vesa_accelerant_mode_count(void)156 vesa_accelerant_mode_count(void)
157 {
158 TRACE(("vesa_accelerant_mode_count() = %d\n", gInfo->shared_info->mode_count));
159 return gInfo->shared_info->mode_count;
160 }
161
162
163 status_t
vesa_get_mode_list(display_mode * modeList)164 vesa_get_mode_list(display_mode* modeList)
165 {
166 TRACE(("vesa_get_mode_info()\n"));
167 memcpy(modeList, gInfo->mode_list,
168 gInfo->shared_info->mode_count * sizeof(display_mode));
169 return B_OK;
170 }
171
172
173 status_t
vesa_propose_display_mode(display_mode * target,const display_mode * low,const display_mode * high)174 vesa_propose_display_mode(display_mode* target, const display_mode* low,
175 const display_mode* high)
176 {
177 TRACE(("vesa_propose_display_mode()\n"));
178
179 // Search for the specified mode in the list. If it's in there, we don't need a custom mode and
180 // we just normalize it to the info provided by the VESA BIOS.
181
182 for (uint32 i = 0; i < gInfo->shared_info->mode_count; i++) {
183 display_mode* current = &gInfo->mode_list[i];
184
185 if (target->virtual_width != current->virtual_width
186 || target->virtual_height != current->virtual_height
187 || target->space != current->space)
188 continue;
189
190 *target = *current;
191 return B_OK;
192 }
193
194 bios_type_enum type = gInfo->shared_info->bios_type;
195 if (type == kIntelBiosType || type == kAtomBiosType1 || type == kAtomBiosType2) {
196 // The driver says it knows the BIOS type, and therefore how to patch it to apply custom
197 // modes.
198 return B_OK;
199 }
200
201 if (type == kNVidiaBiosType) {
202 // For NVidia there is only a limited set of extra resolutions we know how to set
203 for (size_t i = 0; i < B_COUNT_OF(kNVidiaAllowedResolutions); i++) {
204 if (target->virtual_width == kNVidiaAllowedResolutions[i].width
205 && target->virtual_height == kNVidiaAllowedResolutions[i].height)
206 return B_OK;
207 }
208 }
209
210 return B_BAD_VALUE;
211 }
212
213
214 status_t
vesa_set_display_mode(display_mode * _mode)215 vesa_set_display_mode(display_mode* _mode)
216 {
217 TRACE(("vesa_set_display_mode()\n"));
218
219 display_mode mode = *_mode;
220 if (vesa_propose_display_mode(&mode, &mode, &mode) != B_OK)
221 return B_BAD_VALUE;
222
223 vesa_mode* modes = gInfo->vesa_modes;
224 for (int32 i = gInfo->shared_info->vesa_mode_count; i-- > 0;) {
225 // search mode in VESA mode list
226 // TODO: list is ordered, we could use binary search
227 if (modes[i].width == mode.virtual_width
228 && modes[i].height == mode.virtual_height
229 && get_color_space_for_depth(modes[i].bits_per_pixel)
230 == mode.space) {
231 if (gInfo->current_mode == i)
232 return B_OK;
233 status_t result = ioctl(gInfo->device, VESA_SET_DISPLAY_MODE, &i, sizeof(i));
234 if (result == B_OK)
235 gInfo->current_mode = i;
236 return result;
237 }
238 }
239
240 // If the mode is not found in the list of standard mode, live patch the BIOS to get it anyway
241 status_t result = ioctl(gInfo->device, VESA_SET_CUSTOM_DISPLAY_MODE,
242 &mode, sizeof(display_mode));
243 if (result == B_OK) {
244 gInfo->current_mode = -1;
245 }
246
247 return result;
248 }
249
250
251 status_t
vesa_get_display_mode(display_mode * _currentMode)252 vesa_get_display_mode(display_mode* _currentMode)
253 {
254 TRACE(("vesa_get_display_mode()\n"));
255 *_currentMode = gInfo->shared_info->current_mode;
256 return B_OK;
257 }
258
259
260 status_t
vesa_get_edid_info(void * info,size_t size,uint32 * _version)261 vesa_get_edid_info(void* info, size_t size, uint32* _version)
262 {
263 TRACE(("vesa_get_edid_info()\n"));
264
265 if (!gInfo->shared_info->has_edid)
266 return B_ERROR;
267 if (size < sizeof(struct edid1_info))
268 return B_BUFFER_OVERFLOW;
269
270 memcpy(info, &gInfo->shared_info->edid_info, sizeof(struct edid1_info));
271 *_version = EDID_VERSION_1;
272 return B_OK;
273 }
274
275
276 status_t
vesa_get_frame_buffer_config(frame_buffer_config * config)277 vesa_get_frame_buffer_config(frame_buffer_config* config)
278 {
279 TRACE(("vesa_get_frame_buffer_config()\n"));
280
281 config->frame_buffer = gInfo->shared_info->frame_buffer;
282 config->frame_buffer_dma = gInfo->shared_info->physical_frame_buffer;
283 config->bytes_per_row = gInfo->shared_info->bytes_per_row;
284
285 return B_OK;
286 }
287
288
289 status_t
vesa_get_pixel_clock_limits(display_mode * mode,uint32 * _low,uint32 * _high)290 vesa_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high)
291 {
292 TRACE(("vesa_get_pixel_clock_limits()\n"));
293
294 // TODO: do some real stuff here (taken from radeon driver)
295 uint32 totalPixel = (uint32)mode->timing.h_total
296 * (uint32)mode->timing.v_total;
297 uint32 clockLimit = 2000000;
298
299 // lower limit of about 48Hz vertical refresh
300 *_low = totalPixel * 48L / 1000L;
301 if (*_low > clockLimit)
302 return B_ERROR;
303
304 *_high = clockLimit;
305 return B_OK;
306 }
307
308
309 status_t
vesa_move_display(uint16 h_display_start,uint16 v_display_start)310 vesa_move_display(uint16 h_display_start, uint16 v_display_start)
311 {
312 TRACE(("vesa_move_display()\n"));
313 return B_ERROR;
314 }
315
316
317 status_t
vesa_get_timing_constraints(display_timing_constraints * constraints)318 vesa_get_timing_constraints(display_timing_constraints* constraints)
319 {
320 TRACE(("vesa_get_timing_constraints()\n"));
321 return B_ERROR;
322 }
323
324
325 void
vesa_set_indexed_colors(uint count,uint8 first,uint8 * colors,uint32 flags)326 vesa_set_indexed_colors(uint count, uint8 first, uint8* colors, uint32 flags)
327 {
328 TRACE(("vesa_set_indexed_colors()\n"));
329
330 vesa_set_indexed_colors_args args;
331 args.first = first;
332 args.count = count;
333 args.colors = colors;
334 ioctl(gInfo->device, VESA_SET_INDEXED_COLORS, &args, sizeof(args));
335 }
336
337