xref: /haiku/src/add-ons/kernel/drivers/graphics/vesa/vesa.cpp (revision 82a8a20999118b748396cf16a33c47c3b0c0222d)
1 /*
2  * Copyright 2005-2009, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "vesa_private.h"
8 #include "vesa.h"
9 
10 #include <string.h>
11 
12 #include <boot_item.h>
13 #include <frame_buffer_console.h>
14 #include <util/kernel_cpp.h>
15 #include <arch/x86/vm86.h>
16 #include <vm.h>
17 
18 #include "driver.h"
19 #include "utility.h"
20 #include "vesa_info.h"
21 
22 
23 static uint32
24 get_color_space_for_depth(uint32 depth)
25 {
26 	switch (depth) {
27 		case 4:
28 			return B_GRAY8;
29 				// the app_server is smart enough to translate this to VGA mode
30 		case 8:
31 			return B_CMAP8;
32 		case 15:
33 			return B_RGB15;
34 		case 16:
35 			return B_RGB16;
36 		case 24:
37 			return B_RGB24;
38 		case 32:
39 			return B_RGB32;
40 	}
41 
42 	return 0;
43 }
44 
45 
46 static status_t
47 vbe_get_mode_info(struct vm86_state& vmState, uint16 mode,
48 	struct vbe_mode_info* modeInfo)
49 {
50 	struct vbe_mode_info* vbeModeInfo = (struct vbe_mode_info*)0x1000;
51 
52 	memset(vbeModeInfo, 0, sizeof(vbe_mode_info));
53 	vmState.regs.eax = 0x4f01;
54 	vmState.regs.ecx = mode;
55 	vmState.regs.es  = 0x1000 >> 4;
56 	vmState.regs.edi = 0x0000;
57 
58 	status_t status = vm86_do_int(&vmState, 0x10);
59 	if (status != B_OK) {
60 		dprintf(DEVICE_NAME ": vbe_get_mode_info(%u): vm86 failed\n", mode);
61 		return status;
62 	}
63 
64 	if ((vmState.regs.eax & 0xffff) != 0x4f) {
65 		dprintf(DEVICE_NAME ": vbe_get_mode_info(): BIOS returned 0x%04lx\n",
66 			vmState.regs.eax & 0xffff);
67 		return B_ENTRY_NOT_FOUND;
68 	}
69 
70 	memcpy(modeInfo, vbeModeInfo, sizeof(struct vbe_mode_info));
71 	return B_OK;
72 }
73 
74 
75 static status_t
76 vbe_set_mode(struct vm86_state& vmState, uint16 mode)
77 {
78 	vmState.regs.eax = 0x4f02;
79 	vmState.regs.ebx = (mode & SET_MODE_MASK) | SET_MODE_LINEAR_BUFFER;
80 
81 	status_t status = vm86_do_int(&vmState, 0x10);
82 	if (status != B_OK) {
83 		dprintf(DEVICE_NAME ": vbe_set_mode(%u): vm86 failed\n", mode);
84 		return status;
85 	}
86 
87 	if ((vmState.regs.eax & 0xffff) != 0x4f) {
88 		dprintf(DEVICE_NAME ": vbe_set_mode(): BIOS returned 0x%04lx\n",
89 			vmState.regs.eax & 0xffff);
90 		return B_ERROR;
91 	}
92 
93 	return B_OK;
94 }
95 
96 
97 static uint32
98 vbe_to_system_dpms(uint8 vbeMode)
99 {
100 	uint32 mode = 0;
101 	if ((vbeMode & (DPMS_OFF | DPMS_REDUCED_ON)) != 0)
102 		mode |= B_DPMS_OFF;
103 	if ((vbeMode & DPMS_STANDBY) != 0)
104 		mode |= B_DPMS_STAND_BY;
105 	if ((vbeMode & DPMS_SUSPEND) != 0)
106 		mode |= B_DPMS_SUSPEND;
107 
108 	return mode;
109 }
110 
111 
112 static status_t
113 vbe_get_dpms_capabilities(uint32& vbeMode, uint32& mode)
114 {
115 	// we always return a valid mode
116 	vbeMode = 0;
117 	mode = B_DPMS_ON;
118 
119 	// Prepare vm86 mode environment
120 	struct vm86_state vmState;
121 	status_t status = vm86_prepare(&vmState, 0x20000);
122 	if (status != B_OK) {
123 		dprintf(DEVICE_NAME": vbe_get_dpms_capabilities(): vm86_prepare "
124 			"failed: %s\n", strerror(status));
125 		return status;
126 	}
127 
128 	vmState.regs.eax = 0x4f10;
129 	vmState.regs.ebx = 0;
130 	vmState.regs.esi = 0;
131 	vmState.regs.edi = 0;
132 
133 	status = vm86_do_int(&vmState, 0x10);
134 	if (status != B_OK) {
135 		dprintf(DEVICE_NAME ": vbe_get_dpms_capabilities(): vm86 failed\n");
136 		goto out;
137 	}
138 
139 	if ((vmState.regs.eax & 0xffff) != 0x4f) {
140 		dprintf(DEVICE_NAME ": vbe_get_dpms_capabilities(): BIOS returned "
141 			"0x%04lx\n", vmState.regs.eax & 0xffff);
142 		status = B_ERROR;
143 		goto out;
144 	}
145 
146 	vbeMode = vmState.regs.ebx >> 8;
147 	mode = vbe_to_system_dpms(vbeMode);
148 
149 out:
150 	vm86_cleanup(&vmState);
151 	return status;
152 }
153 
154 
155 //	#pragma mark -
156 
157 
158 status_t
159 vesa_init(vesa_info& info)
160 {
161 	frame_buffer_boot_info* bufferInfo
162 		= (frame_buffer_boot_info*)get_boot_item(FRAME_BUFFER_BOOT_INFO, NULL);
163 	if (bufferInfo == NULL)
164 		return B_ERROR;
165 
166 	size_t modesSize = 0;
167 	vesa_mode* modes = (vesa_mode*)get_boot_item(VESA_MODES_BOOT_INFO,
168 		&modesSize);
169 	info.modes = modes;
170 
171 	size_t sharedSize = (sizeof(vesa_shared_info) + 7) & ~7;
172 
173 	info.shared_area = create_area("vesa shared info",
174 		(void**)&info.shared_info, B_ANY_KERNEL_ADDRESS,
175 		ROUND_TO_PAGE_SIZE(sharedSize + modesSize), B_FULL_LOCK,
176 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_USER_CLONEABLE_AREA);
177 	if (info.shared_area < B_OK)
178 		return info.shared_area;
179 
180 	vesa_shared_info &sharedInfo = *info.shared_info;
181 
182 	memset(&sharedInfo, 0, sizeof(vesa_shared_info));
183 
184 	if (modes != NULL) {
185 		sharedInfo.vesa_mode_offset = sharedSize;
186 		sharedInfo.vesa_mode_count = modesSize / sizeof(vesa_mode);
187 
188 		memcpy((uint8*)&sharedInfo + sharedSize, modes, modesSize);
189 	}
190 
191 	sharedInfo.frame_buffer_area = bufferInfo->area;
192 	sharedInfo.frame_buffer = (uint8*)bufferInfo->frame_buffer;
193 
194 	sharedInfo.current_mode.virtual_width = bufferInfo->width;
195 	sharedInfo.current_mode.virtual_height = bufferInfo->height;
196 	sharedInfo.current_mode.space = get_color_space_for_depth(
197 		bufferInfo->depth);
198 	sharedInfo.bytes_per_row = bufferInfo->bytes_per_row;
199 
200 	// TODO: we might want to do this via vm86 instead
201 	edid1_info* edidInfo = (edid1_info*)get_boot_item(VESA_EDID_BOOT_INFO,
202 		NULL);
203 	if (edidInfo != NULL) {
204 		sharedInfo.has_edid = true;
205 		memcpy(&sharedInfo.edid_info, edidInfo, sizeof(edid1_info));
206 	}
207 
208 	vbe_get_dpms_capabilities(info.vbe_dpms_capabilities,
209 		sharedInfo.dpms_capabilities);
210 
211 	physical_entry mapping;
212 	get_memory_map((void*)sharedInfo.frame_buffer, B_PAGE_SIZE, &mapping, 1);
213 	sharedInfo.physical_frame_buffer = (uint8*)mapping.address;
214 
215 	dprintf(DEVICE_NAME ": vesa_init() completed successfully!\n");
216 	return B_OK;
217 }
218 
219 
220 void
221 vesa_uninit(vesa_info& info)
222 {
223 	dprintf(DEVICE_NAME": vesa_uninit()\n");
224 
225 	delete_area(info.shared_info->frame_buffer_area);
226 	delete_area(info.shared_area);
227 }
228 
229 
230 status_t
231 vesa_set_display_mode(vesa_info& info, uint32 mode)
232 {
233 	if (mode >= info.shared_info->vesa_mode_count)
234 		return B_ENTRY_NOT_FOUND;
235 
236 	// Prepare vm86 mode environment
237 	struct vm86_state vmState;
238 	status_t status = vm86_prepare(&vmState, 0x20000);
239 	if (status != B_OK) {
240 		dprintf(DEVICE_NAME": vesa_set_display_mode(): vm86_prepare failed\n");
241 		return status;
242 	}
243 
244 	area_id frameBufferArea;
245 	frame_buffer_boot_info* bufferInfo;
246 	struct vbe_mode_info modeInfo;
247 
248 	// Get mode information
249 	status = vbe_get_mode_info(vmState, info.modes[mode].mode, &modeInfo);
250 	if (status != B_OK) {
251 		dprintf(DEVICE_NAME": vesa_set_display_mode(): cannot get mode info\n");
252 		goto out;
253 	}
254 
255 	// Set mode
256 	status = vbe_set_mode(vmState, info.modes[mode].mode);
257 	if (status != B_OK) {
258 		dprintf(DEVICE_NAME": vesa_set_display_mode(): cannot set mode\n");
259 		goto out;
260 	}
261 
262 	// Map new frame buffer
263 	void* frameBuffer;
264 	frameBufferArea = map_physical_memory("vesa_fb",
265 		(void*)modeInfo.physical_base, modeInfo.bytes_per_row * modeInfo.height,
266 		B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_WRITE_AREA, &frameBuffer);
267 	if (frameBufferArea < B_OK) {
268 		status = (status_t)frameBufferArea;
269 		goto out;
270 	}
271 	delete_area(info.shared_info->frame_buffer_area);
272 
273 	// Turn on write combining for the area
274 	vm_set_area_memory_type(frameBufferArea, modeInfo.physical_base, B_MTR_WC);
275 
276 	// Update shared frame buffer information
277 	info.shared_info->frame_buffer_area = frameBufferArea;
278 	info.shared_info->frame_buffer = (uint8*)frameBuffer;
279 	info.shared_info->physical_frame_buffer = (uint8*)modeInfo.physical_base;
280 	info.shared_info->bytes_per_row = modeInfo.bytes_per_row;
281 	info.shared_info->current_mode.virtual_width = modeInfo.width;
282 	info.shared_info->current_mode.virtual_height = modeInfo.height;
283 	info.shared_info->current_mode.space = get_color_space_for_depth(
284 		modeInfo.bits_per_pixel);
285 
286 	// Update boot item as it's used in vesa_init()
287 	bufferInfo
288 		= (frame_buffer_boot_info*)get_boot_item(FRAME_BUFFER_BOOT_INFO, NULL);
289 	bufferInfo->area = frameBufferArea;
290 	bufferInfo->frame_buffer = (addr_t)frameBuffer;
291 	bufferInfo->width = modeInfo.width;
292 	bufferInfo->height = modeInfo.height;
293 	bufferInfo->depth = modeInfo.bits_per_pixel;
294 	bufferInfo->bytes_per_row = modeInfo.bytes_per_row;
295 
296 out:
297 	vm86_cleanup(&vmState);
298 	return status;
299 }
300 
301 
302 status_t
303 vesa_get_dpms_mode(vesa_info& info, uint32& mode)
304 {
305 	mode = B_DPMS_ON;
306 		// we always return a valid mode
307 
308 	// Prepare vm86 mode environment
309 	struct vm86_state vmState;
310 	status_t status = vm86_prepare(&vmState, 0x20000);
311 	if (status != B_OK) {
312 		dprintf(DEVICE_NAME": vesa_get_dpms_mode(): vm86_prepare failed: %s\n",
313 			strerror(status));
314 		return status;
315 	}
316 
317 	vmState.regs.eax = 0x4f10;
318 	vmState.regs.ebx = 2;
319 	vmState.regs.esi = 0;
320 	vmState.regs.edi = 0;
321 
322 	status = vm86_do_int(&vmState, 0x10);
323 	if (status != B_OK) {
324 		dprintf(DEVICE_NAME ": vesa_get_dpms_mode(): vm86 failed: %s\n",
325 			strerror(status));
326 		goto out;
327 	}
328 
329 	if ((vmState.regs.eax & 0xffff) != 0x4f) {
330 		dprintf(DEVICE_NAME ": vesa_get_dpms_mode(): BIOS returned 0x%04lx\n",
331 			vmState.regs.eax & 0xffff);
332 		status = B_ERROR;
333 		goto out;
334 	}
335 
336 	mode = vbe_to_system_dpms(vmState.regs.ebx >> 8);
337 
338 out:
339 	vm86_cleanup(&vmState);
340 	return status;
341 }
342 
343 
344 status_t
345 vesa_set_dpms_mode(vesa_info& info, uint32 mode)
346 {
347 	// Only let supported modes through
348 	mode &= info.shared_info->dpms_capabilities;
349 
350 	uint8 vbeMode = 0;
351 	if ((mode & B_DPMS_OFF) != 0)
352 		vbeMode |= DPMS_OFF | DPMS_REDUCED_ON;
353 	if ((mode & B_DPMS_STAND_BY) != 0)
354 		vbeMode |= DPMS_STANDBY;
355 	if ((mode & B_DPMS_SUSPEND) != 0)
356 		vbeMode |= DPMS_SUSPEND;
357 
358 	vbeMode &= info.vbe_dpms_capabilities;
359 
360 	// Prepare vm86 mode environment
361 	struct vm86_state vmState;
362 	status_t status = vm86_prepare(&vmState, 0x20000);
363 	if (status != B_OK) {
364 		dprintf(DEVICE_NAME": vesa_set_dpms_mode(): vm86_prepare failed: %s\n",
365 			strerror(status));
366 		return status;
367 	}
368 
369 	vmState.regs.eax = 0x4f10;
370 	vmState.regs.ebx = (vbeMode << 8) | 1;
371 	vmState.regs.esi = 0;
372 	vmState.regs.edi = 0;
373 
374 	status = vm86_do_int(&vmState, 0x10);
375 	if (status != B_OK) {
376 		dprintf(DEVICE_NAME ": vesa_set_dpms_mode(): vm86 failed: %s\n",
377 			strerror(status));
378 		goto out;
379 	}
380 
381 	if ((vmState.regs.eax & 0xffff) != 0x4f) {
382 		dprintf(DEVICE_NAME ": vesa_set_dpms_mode(): BIOS returned 0x%04lx\n",
383 			vmState.regs.eax & 0xffff);
384 		status = B_ERROR;
385 		goto out;
386 	}
387 
388 out:
389 	vm86_cleanup(&vmState);
390 	return status;
391 }
392