xref: /haiku/src/add-ons/accelerants/vesa/mode.cpp (revision 52f7c9389475e19fc21487b38064b4390eeb6fea)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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