xref: /haiku/src/add-ons/accelerants/vesa/mode.cpp (revision c0936b5a0384bc6fe654d296ee54222a0f45d2b6)
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 
17 
18 //#define TRACE_MODE
19 #ifdef TRACE_MODE
20 extern "C" void _sPrintf(const char* format, ...);
21 #	define TRACE(x) _sPrintf x
22 #else
23 #	define TRACE(x) ;
24 #endif
25 
26 
27 static uint32
28 get_color_space_for_depth(uint32 depth)
29 {
30 	switch (depth) {
31 		case 4:
32 			return B_GRAY8;
33 				// the app_server is smart enough to translate this to VGA mode
34 		case 8:
35 			return B_CMAP8;
36 		case 15:
37 			return B_RGB15;
38 		case 16:
39 			return B_RGB16;
40 		case 24:
41 			return B_RGB24;
42 		case 32:
43 			return B_RGB32;
44 	}
45 
46 	return 0;
47 }
48 
49 
50 /*!	Checks if the specified \a mode can be set using VESA. */
51 static bool
52 is_mode_supported(display_mode* mode)
53 {
54 	vesa_mode* modes = gInfo->vesa_modes;
55 
56 	if (modes == NULL) {
57 		// we're a UEFI framebuffer, just confirm it's our current mode
58 		const display_mode &current = gInfo->shared_info->current_mode;
59 		return mode->virtual_width == current.virtual_width
60 			&& mode->virtual_height == current.virtual_height
61 			&& mode->h_display_start == current.h_display_start
62 			&& mode->v_display_start == current.v_display_start
63 			&& mode->space == current.space;
64 	}
65 
66 	for (uint32 i = gInfo->shared_info->vesa_mode_count; i-- > 0;) {
67 		// search mode in VESA mode list
68 		// TODO: list is ordered, we could use binary search
69 		if (modes[i].width == mode->virtual_width
70 			&& modes[i].height == mode->virtual_height
71 			&& get_color_space_for_depth(modes[i].bits_per_pixel)
72 				== mode->space)
73 			return true;
74 	}
75 
76 	return false;
77 }
78 
79 
80 /*!	Creates the initial mode list of the primary accelerant.
81 	It's called from vesa_init_accelerant().
82 */
83 status_t
84 create_mode_list(void)
85 {
86 	const color_space kVesaSpaces[] = {B_RGB32_LITTLE, B_RGB24_LITTLE,
87 		B_RGB16_LITTLE, B_RGB15_LITTLE, B_CMAP8};
88 	const color_space kUefiSpaces[] = {
89 		(color_space)gInfo->shared_info->current_mode.space
90 	};
91 
92 	uint32 initialModesCount = 0;
93 	bool vesaAvailable = gInfo->vesa_modes != NULL;
94 
95 	// Add initial VESA modes.
96 	display_mode* initialModes = NULL;
97 	if (vesaAvailable) {
98 		initialModes = (display_mode*)malloc(
99 			sizeof(display_mode) * gInfo->shared_info->vesa_mode_count);
100 	}
101 
102 	if (initialModes != NULL) {
103 		initialModesCount = gInfo->shared_info->vesa_mode_count;
104 		vesa_mode* vesaModes = gInfo->vesa_modes;
105 
106 		for (uint32 i = 0; i < initialModesCount; i++) {
107 			compute_display_timing(vesaModes[i].width, vesaModes[i].height,
108 				60, false, &initialModes[i].timing);
109 			fill_display_mode(vesaModes[i].width, vesaModes[i].height,
110 				&initialModes[i]);
111 		}
112 	} else {
113 		// UEFI doesn't give us any VESA modes
114 		initialModes = (display_mode*)malloc(sizeof(display_mode));
115 		if (initialModes != NULL) {
116 			initialModesCount = 1;
117 
118 			compute_display_timing(initialModes[0].virtual_width,
119 				initialModes[0].virtual_height, 60, false,
120 				&initialModes[0].timing);
121 			fill_display_mode(initialModes[0].virtual_width,
122 				initialModes[0].virtual_height, &initialModes[0]);
123 		}
124 	}
125 
126 	const color_space *colorSpaces = vesaAvailable ? kVesaSpaces : kUefiSpaces;
127 	size_t colorSpaceCount = vesaAvailable ?
128 		sizeof(kVesaSpaces) / sizeof(kVesaSpaces[0]) : 1;
129 
130 	gInfo->mode_list_area = create_display_modes("vesa modes",
131 		gInfo->shared_info->has_edid ? &gInfo->shared_info->edid_info : NULL,
132 		initialModes, initialModesCount, colorSpaces, colorSpaceCount,
133 		is_mode_supported, &gInfo->mode_list, &gInfo->shared_info->mode_count);
134 
135 	free(initialModes);
136 
137 	if (gInfo->mode_list_area < 0)
138 		return gInfo->mode_list_area;
139 
140 	gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
141 	return B_OK;
142 }
143 
144 
145 //	#pragma mark -
146 
147 
148 uint32
149 vesa_accelerant_mode_count(void)
150 {
151 	TRACE(("vesa_accelerant_mode_count() = %d\n", gInfo->shared_info->mode_count));
152 	return gInfo->shared_info->mode_count;
153 }
154 
155 
156 status_t
157 vesa_get_mode_list(display_mode* modeList)
158 {
159 	TRACE(("vesa_get_mode_info()\n"));
160 	memcpy(modeList, gInfo->mode_list,
161 		gInfo->shared_info->mode_count * sizeof(display_mode));
162 	return B_OK;
163 }
164 
165 
166 status_t
167 vesa_propose_display_mode(display_mode* target, const display_mode* low,
168 	const display_mode* high)
169 {
170 	TRACE(("vesa_propose_display_mode()\n"));
171 
172 	// just search for the specified mode in the list
173 
174 	for (uint32 i = 0; i < gInfo->shared_info->mode_count; i++) {
175 		display_mode* current = &gInfo->mode_list[i];
176 
177 		if (target->virtual_width != current->virtual_width
178 			|| target->virtual_height != current->virtual_height
179 			|| target->space != current->space)
180 			continue;
181 
182 		*target = *current;
183 		return B_OK;
184 	}
185 	return B_BAD_VALUE;
186 }
187 
188 
189 status_t
190 vesa_set_display_mode(display_mode* _mode)
191 {
192 	TRACE(("vesa_set_display_mode()\n"));
193 
194 	display_mode mode = *_mode;
195 	if (vesa_propose_display_mode(&mode, &mode, &mode) != B_OK)
196 		return B_BAD_VALUE;
197 
198 	vesa_mode* modes = gInfo->vesa_modes;
199 	if (modes == NULL)
200 		return B_BAD_VALUE;
201 			// UEFI has no VESA modes
202 
203 	for (uint32 i = gInfo->shared_info->vesa_mode_count; i-- > 0;) {
204 		// search mode in VESA mode list
205 		// TODO: list is ordered, we could use binary search
206 		if (modes[i].width == mode.virtual_width
207 			&& modes[i].height == mode.virtual_height
208 			&& get_color_space_for_depth(modes[i].bits_per_pixel)
209 				== mode.space) {
210 			if (gInfo->current_mode == i)
211 				return B_OK;
212 			status_t result = ioctl(gInfo->device, VESA_SET_DISPLAY_MODE, &i, sizeof(i));
213 			if (result == B_OK)
214 				gInfo->current_mode = i;
215 			return result;
216 		}
217 	}
218 
219 	return B_UNSUPPORTED;
220 }
221 
222 
223 status_t
224 vesa_get_display_mode(display_mode* _currentMode)
225 {
226 	TRACE(("vesa_get_display_mode()\n"));
227 	*_currentMode = gInfo->shared_info->current_mode;
228 	return B_OK;
229 }
230 
231 
232 status_t
233 vesa_get_edid_info(void* info, size_t size, uint32* _version)
234 {
235 	TRACE(("vesa_get_edid_info()\n"));
236 
237 	if (!gInfo->shared_info->has_edid)
238 		return B_ERROR;
239 	if (size < sizeof(struct edid1_info))
240 		return B_BUFFER_OVERFLOW;
241 
242 	memcpy(info, &gInfo->shared_info->edid_info, sizeof(struct edid1_info));
243 	*_version = EDID_VERSION_1;
244 	return B_OK;
245 }
246 
247 
248 status_t
249 vesa_get_frame_buffer_config(frame_buffer_config* config)
250 {
251 	TRACE(("vesa_get_frame_buffer_config()\n"));
252 
253 	config->frame_buffer = gInfo->shared_info->frame_buffer;
254 	config->frame_buffer_dma = gInfo->shared_info->physical_frame_buffer;
255 	config->bytes_per_row = gInfo->shared_info->bytes_per_row;
256 
257 	return B_OK;
258 }
259 
260 
261 status_t
262 vesa_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high)
263 {
264 	TRACE(("vesa_get_pixel_clock_limits()\n"));
265 
266 	// TODO: do some real stuff here (taken from radeon driver)
267 	uint32 totalPixel = (uint32)mode->timing.h_total
268 		* (uint32)mode->timing.v_total;
269 	uint32 clockLimit = 2000000;
270 
271 	// lower limit of about 48Hz vertical refresh
272 	*_low = totalPixel * 48L / 1000L;
273 	if (*_low > clockLimit)
274 		return B_ERROR;
275 
276 	*_high = clockLimit;
277 	return B_OK;
278 }
279 
280 
281 status_t
282 vesa_move_display(uint16 h_display_start, uint16 v_display_start)
283 {
284 	TRACE(("vesa_move_display()\n"));
285 	return B_ERROR;
286 }
287 
288 
289 status_t
290 vesa_get_timing_constraints(display_timing_constraints* constraints)
291 {
292 	TRACE(("vesa_get_timing_constraints()\n"));
293 	return B_ERROR;
294 }
295 
296 
297 void
298 vesa_set_indexed_colors(uint count, uint8 first, uint8* colors, uint32 flags)
299 {
300 	TRACE(("vesa_set_indexed_colors()\n"));
301 
302 	vesa_set_indexed_colors_args args;
303 	args.first = first;
304 	args.count = count;
305 	args.colors = colors;
306 	ioctl(gInfo->device, VESA_SET_INDEXED_COLORS, &args, sizeof(args));
307 }
308 
309