xref: /haiku/src/add-ons/accelerants/vesa/mode.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 			display_mode &mode = gInfo->shared_info->current_mode;
119 
120 			compute_display_timing(mode.virtual_width, mode.virtual_height,
121 				60, false, &initialModes[0].timing);
122 			fill_display_mode(mode.virtual_width, mode.virtual_height,
123 				&initialModes[0]);
124 		}
125 	}
126 
127 	const color_space *colorSpaces = vesaAvailable ? kVesaSpaces : kUefiSpaces;
128 	size_t colorSpaceCount = vesaAvailable ?
129 		sizeof(kVesaSpaces) / sizeof(kVesaSpaces[0]) : 1;
130 
131 	gInfo->mode_list_area = create_display_modes("vesa modes",
132 		gInfo->shared_info->has_edid ? &gInfo->shared_info->edid_info : NULL,
133 		initialModes, initialModesCount, colorSpaces, colorSpaceCount,
134 		is_mode_supported, &gInfo->mode_list, &gInfo->shared_info->mode_count);
135 
136 	free(initialModes);
137 
138 	if (gInfo->mode_list_area < 0)
139 		return gInfo->mode_list_area;
140 
141 	gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
142 	return B_OK;
143 }
144 
145 
146 //	#pragma mark -
147 
148 
149 uint32
150 vesa_accelerant_mode_count(void)
151 {
152 	TRACE(("vesa_accelerant_mode_count() = %d\n", gInfo->shared_info->mode_count));
153 	return gInfo->shared_info->mode_count;
154 }
155 
156 
157 status_t
158 vesa_get_mode_list(display_mode* modeList)
159 {
160 	TRACE(("vesa_get_mode_info()\n"));
161 	memcpy(modeList, gInfo->mode_list,
162 		gInfo->shared_info->mode_count * sizeof(display_mode));
163 	return B_OK;
164 }
165 
166 
167 status_t
168 vesa_propose_display_mode(display_mode* target, const display_mode* low,
169 	const display_mode* high)
170 {
171 	TRACE(("vesa_propose_display_mode()\n"));
172 
173 	// just search for the specified mode in the list
174 
175 	for (uint32 i = 0; i < gInfo->shared_info->mode_count; i++) {
176 		display_mode* current = &gInfo->mode_list[i];
177 
178 		if (target->virtual_width != current->virtual_width
179 			|| target->virtual_height != current->virtual_height
180 			|| target->space != current->space)
181 			continue;
182 
183 		*target = *current;
184 		return B_OK;
185 	}
186 	return B_BAD_VALUE;
187 }
188 
189 
190 status_t
191 vesa_set_display_mode(display_mode* _mode)
192 {
193 	TRACE(("vesa_set_display_mode()\n"));
194 
195 	display_mode mode = *_mode;
196 	if (vesa_propose_display_mode(&mode, &mode, &mode) != B_OK)
197 		return B_BAD_VALUE;
198 
199 	vesa_mode* modes = gInfo->vesa_modes;
200 	if (modes == NULL)
201 		return B_BAD_VALUE;
202 			// UEFI has no VESA modes
203 
204 	for (uint32 i = gInfo->shared_info->vesa_mode_count; i-- > 0;) {
205 		// search mode in VESA mode list
206 		// TODO: list is ordered, we could use binary search
207 		if (modes[i].width == mode.virtual_width
208 			&& modes[i].height == mode.virtual_height
209 			&& get_color_space_for_depth(modes[i].bits_per_pixel)
210 				== mode.space) {
211 			if (gInfo->current_mode == i)
212 				return B_OK;
213 			status_t result = ioctl(gInfo->device, VESA_SET_DISPLAY_MODE, &i, sizeof(i));
214 			if (result == B_OK)
215 				gInfo->current_mode = i;
216 			return result;
217 		}
218 	}
219 
220 	return B_UNSUPPORTED;
221 }
222 
223 
224 status_t
225 vesa_get_display_mode(display_mode* _currentMode)
226 {
227 	TRACE(("vesa_get_display_mode()\n"));
228 	*_currentMode = gInfo->shared_info->current_mode;
229 	return B_OK;
230 }
231 
232 
233 status_t
234 vesa_get_edid_info(void* info, size_t size, uint32* _version)
235 {
236 	TRACE(("vesa_get_edid_info()\n"));
237 
238 	if (!gInfo->shared_info->has_edid)
239 		return B_ERROR;
240 	if (size < sizeof(struct edid1_info))
241 		return B_BUFFER_OVERFLOW;
242 
243 	memcpy(info, &gInfo->shared_info->edid_info, sizeof(struct edid1_info));
244 	*_version = EDID_VERSION_1;
245 	return B_OK;
246 }
247 
248 
249 status_t
250 vesa_get_frame_buffer_config(frame_buffer_config* config)
251 {
252 	TRACE(("vesa_get_frame_buffer_config()\n"));
253 
254 	config->frame_buffer = gInfo->shared_info->frame_buffer;
255 	config->frame_buffer_dma = gInfo->shared_info->physical_frame_buffer;
256 	config->bytes_per_row = gInfo->shared_info->bytes_per_row;
257 
258 	return B_OK;
259 }
260 
261 
262 status_t
263 vesa_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high)
264 {
265 	TRACE(("vesa_get_pixel_clock_limits()\n"));
266 
267 	// TODO: do some real stuff here (taken from radeon driver)
268 	uint32 totalPixel = (uint32)mode->timing.h_total
269 		* (uint32)mode->timing.v_total;
270 	uint32 clockLimit = 2000000;
271 
272 	// lower limit of about 48Hz vertical refresh
273 	*_low = totalPixel * 48L / 1000L;
274 	if (*_low > clockLimit)
275 		return B_ERROR;
276 
277 	*_high = clockLimit;
278 	return B_OK;
279 }
280 
281 
282 status_t
283 vesa_move_display(uint16 h_display_start, uint16 v_display_start)
284 {
285 	TRACE(("vesa_move_display()\n"));
286 	return B_ERROR;
287 }
288 
289 
290 status_t
291 vesa_get_timing_constraints(display_timing_constraints* constraints)
292 {
293 	TRACE(("vesa_get_timing_constraints()\n"));
294 	return B_ERROR;
295 }
296 
297 
298 void
299 vesa_set_indexed_colors(uint count, uint8 first, uint8* colors, uint32 flags)
300 {
301 	TRACE(("vesa_set_indexed_colors()\n"));
302 
303 	vesa_set_indexed_colors_args args;
304 	args.first = first;
305 	args.count = count;
306 	args.colors = colors;
307 	ioctl(gInfo->device, VESA_SET_INDEXED_COLORS, &args, sizeof(args));
308 }
309 
310