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 <drivers/bios.h>
13
14#include <boot_item.h>
15#include <frame_buffer_console.h>
16#include <util/kernel_cpp.h>
17#include <vm/vm.h>
18
19#include "driver.h"
20#include "utility.h"
21#include "vesa_info.h"
22
23
24static bios_module_info* sBIOSModule;
25
26
27/*!	Loads the BIOS module and sets up a state for it. The BIOS module is only
28	loaded when we need it, as it is quite a large module.
29*/
30static status_t
31vbe_call_prepare(bios_state** state)
32{
33	status_t status;
34
35	status = get_module(B_BIOS_MODULE_NAME, (module_info**)&sBIOSModule);
36	if (status != B_OK) {
37		dprintf(DEVICE_NAME ": failed to get BIOS module: %s\n",
38			strerror(status));
39		return status;
40	}
41
42	status = sBIOSModule->prepare(state);
43	if (status != B_OK) {
44		dprintf(DEVICE_NAME ": failed to prepare BIOS state: %s\n",
45			strerror(status));
46		put_module(B_BIOS_MODULE_NAME);
47	}
48
49	return status;
50}
51
52
53static void
54vbe_call_finish(bios_state* state)
55{
56	sBIOSModule->finish(state);
57	put_module(B_BIOS_MODULE_NAME);
58}
59
60
61static status_t
62find_graphics_card(addr_t frameBuffer, addr_t& base, size_t& size)
63{
64	// TODO: when we port this over to the new driver API, this mechanism can be
65	// used to find the right device_node
66	pci_module_info* pci;
67	if (get_module(B_PCI_MODULE_NAME, (module_info**)&pci) != B_OK)
68		return B_ERROR;
69
70	pci_info info;
71	for (int32 index = 0; pci->get_nth_pci_info(index, &info) == B_OK; index++) {
72		if (info.class_base != PCI_display)
73			continue;
74
75		// check PCI BARs
76		for (uint32 i = 0; i < 6; i++) {
77			if (info.u.h0.base_registers[i] <= frameBuffer
78				&& info.u.h0.base_registers[i] + info.u.h0.base_register_sizes[i]
79					> frameBuffer) {
80				// found it!
81				base = info.u.h0.base_registers[i];
82				size = info.u.h0.base_register_sizes[i];
83
84				put_module(B_PCI_MODULE_NAME);
85				return B_OK;
86			}
87		}
88	}
89
90	put_module(B_PCI_MODULE_NAME);
91	return B_ENTRY_NOT_FOUND;
92}
93
94
95static uint32
96get_color_space_for_depth(uint32 depth)
97{
98	switch (depth) {
99		case 1:
100			return B_GRAY1;
101		case 4:
102			return B_GRAY8;
103				// the app_server is smart enough to translate this to VGA mode
104		case 8:
105			return B_CMAP8;
106		case 15:
107			return B_RGB15;
108		case 16:
109			return B_RGB16;
110		case 24:
111			return B_RGB24;
112		case 32:
113			return B_RGB32;
114	}
115
116	return 0;
117}
118
119
120static status_t
121vbe_get_mode_info(bios_state* state, uint16 mode, struct vbe_mode_info* modeInfo)
122{
123	void* vbeModeInfo = sBIOSModule->allocate_mem(state,
124		sizeof(struct vbe_mode_info));
125	if (vbeModeInfo == NULL)
126		return B_NO_MEMORY;
127	memset(vbeModeInfo, 0, sizeof(vbe_mode_info));
128
129	uint32 physicalAddress = sBIOSModule->physical_address(state, vbeModeInfo);
130	bios_regs regs = {};
131	regs.eax = 0x4f01;
132	regs.ecx = mode;
133	regs.es  = physicalAddress >> 4;
134	regs.edi = physicalAddress - (regs.es << 4);
135
136	status_t status = sBIOSModule->interrupt(state, 0x10, &regs);
137	if (status != B_OK) {
138		dprintf(DEVICE_NAME ": vbe_get_mode_info(%u): BIOS failed: %s\n", mode,
139			strerror(status));
140		return status;
141	}
142
143	if ((regs.eax & 0xffff) != 0x4f) {
144		dprintf(DEVICE_NAME ": vbe_get_mode_info(%u): BIOS returned "
145			"0x%04" B_PRIx32 "\n", mode, regs.eax & 0xffff);
146		return B_ENTRY_NOT_FOUND;
147	}
148
149	memcpy(modeInfo, vbeModeInfo, sizeof(struct vbe_mode_info));
150	return B_OK;
151}
152
153
154static status_t
155vbe_set_mode(bios_state* state, uint16 mode)
156{
157	bios_regs regs = {};
158	regs.eax = 0x4f02;
159	regs.ebx = (mode & SET_MODE_MASK) | SET_MODE_LINEAR_BUFFER;
160
161	status_t status = sBIOSModule->interrupt(state, 0x10, &regs);
162	if (status != B_OK) {
163		dprintf(DEVICE_NAME ": vbe_set_mode(%u): BIOS failed: %s\n", mode,
164			strerror(status));
165		return status;
166	}
167
168	if ((regs.eax & 0xffff) != 0x4f) {
169		dprintf(DEVICE_NAME ": vbe_set_mode(%u): BIOS returned 0x%04" B_PRIx32
170			"\n", mode, regs.eax & 0xffff);
171		return B_ERROR;
172	}
173
174	return B_OK;
175}
176
177
178static uint32
179vbe_to_system_dpms(uint8 vbeMode)
180{
181	uint32 mode = 0;
182	if ((vbeMode & (DPMS_OFF | DPMS_REDUCED_ON)) != 0)
183		mode |= B_DPMS_OFF;
184	if ((vbeMode & DPMS_STANDBY) != 0)
185		mode |= B_DPMS_STAND_BY;
186	if ((vbeMode & DPMS_SUSPEND) != 0)
187		mode |= B_DPMS_SUSPEND;
188
189	return mode;
190}
191
192
193static status_t
194vbe_get_dpms_capabilities(uint32& vbeMode, uint32& mode)
195{
196	// we always return a valid mode
197	vbeMode = 0;
198	mode = B_DPMS_ON;
199
200	// Prepare BIOS environment
201	bios_state* state;
202	status_t status = vbe_call_prepare(&state);
203	if (status != B_OK)
204		return status;
205
206	bios_regs regs = {};
207	regs.eax = 0x4f10;
208	regs.ebx = 0;
209	regs.esi = 0;
210	regs.edi = 0;
211
212	status = sBIOSModule->interrupt(state, 0x10, &regs);
213	if (status != B_OK) {
214		dprintf(DEVICE_NAME ": vbe_get_dpms_capabilities(): BIOS failed: %s\n",
215			strerror(status));
216		goto out;
217	}
218
219	if ((regs.eax & 0xffff) != 0x4f) {
220		dprintf(DEVICE_NAME ": vbe_get_dpms_capabilities(): BIOS returned "
221			"0x%04" B_PRIx32 "\n", regs.eax & 0xffff);
222		status = B_ERROR;
223		goto out;
224	}
225
226	vbeMode = regs.ebx >> 8;
227	mode = vbe_to_system_dpms(vbeMode);
228
229out:
230	vbe_call_finish(state);
231	return status;
232}
233
234
235static status_t
236vbe_set_bits_per_gun(bios_state* state, vesa_info& info, uint8 bits)
237{
238	info.bits_per_gun = 6;
239
240	bios_regs regs = {};
241	regs.eax = 0x4f08;
242	regs.ebx = (bits << 8) | 1;
243
244	status_t status = sBIOSModule->interrupt(state, 0x10, &regs);
245	if (status != B_OK) {
246		dprintf(DEVICE_NAME ": vbe_set_bits_per_gun(): BIOS failed: %s\n",
247			strerror(status));
248		return status;
249	}
250
251	if ((regs.eax & 0xffff) != 0x4f) {
252		dprintf(DEVICE_NAME ": vbe_set_bits_per_gun(): BIOS returned "
253			"0x%04" B_PRIx32 "\n", regs.eax & 0xffff);
254		return B_ERROR;
255	}
256
257	info.bits_per_gun = regs.ebx >> 8;
258	return B_OK;
259}
260
261
262static status_t
263vbe_set_bits_per_gun(vesa_info& info, uint8 bits)
264{
265	info.bits_per_gun = 6;
266
267	bios_state* state;
268	status_t status = vbe_call_prepare(&state);
269	if (status != B_OK)
270		return status;
271
272	status = vbe_set_bits_per_gun(state, info, bits);
273
274	vbe_call_finish(state);
275	return status;
276}
277
278
279/*!	Remaps the frame buffer if necessary; if we've already mapped the complete
280	frame buffer, there is no need to map it again.
281*/
282static status_t
283remap_frame_buffer(vesa_info& info, addr_t physicalBase, uint32 width,
284	uint32 height, int8 depth, uint32 bytesPerRow, bool initializing)
285{
286	vesa_shared_info& sharedInfo = *info.shared_info;
287	addr_t frameBuffer = info.frame_buffer;
288
289	if (!info.complete_frame_buffer_mapped) {
290		addr_t base = physicalBase;
291		size_t size = bytesPerRow * height;
292		bool remap = !initializing;
293
294		if (info.physical_frame_buffer_size != 0) {
295			// we can map the complete frame buffer
296			base = info.physical_frame_buffer;
297			size = info.physical_frame_buffer_size;
298			remap = true;
299		}
300
301		if (remap) {
302			area_id area = map_physical_memory("vesa frame buffer", base,
303				size, B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_WRITE_AREA,
304				(void**)&frameBuffer);
305			if (area < 0)
306				return area;
307
308			if (initializing) {
309				// We need to manually update the kernel's frame buffer address,
310				// since this frame buffer remapping has not been issued by the
311				// app_server (which would otherwise take care of this)
312				frame_buffer_update(frameBuffer, width, height, depth,
313					bytesPerRow);
314			}
315
316			delete_area(info.shared_info->frame_buffer_area);
317
318			info.frame_buffer = frameBuffer;
319			sharedInfo.frame_buffer_area = area;
320
321			// Turn on write combining for the area
322			vm_set_area_memory_type(area, base, B_MTR_WC);
323
324			if (info.physical_frame_buffer_size != 0)
325				info.complete_frame_buffer_mapped = true;
326		}
327	}
328
329	if (info.complete_frame_buffer_mapped)
330		frameBuffer += physicalBase - info.physical_frame_buffer;
331
332	// Update shared frame buffer information
333	sharedInfo.frame_buffer = (uint8*)frameBuffer;
334	sharedInfo.physical_frame_buffer = (uint8*)physicalBase;
335	sharedInfo.bytes_per_row = bytesPerRow;
336
337	return B_OK;
338}
339
340
341//	#pragma mark -
342
343
344status_t
345vesa_init(vesa_info& info)
346{
347	frame_buffer_boot_info* bufferInfo
348		= (frame_buffer_boot_info*)get_boot_item(FRAME_BUFFER_BOOT_INFO, NULL);
349	if (bufferInfo == NULL)
350		return B_ERROR;
351
352	info.vbe_capabilities = bufferInfo->vesa_capabilities;
353	info.complete_frame_buffer_mapped = false;
354
355	// Find out which PCI device we belong to, so that we know its frame buffer
356	// size
357	find_graphics_card(bufferInfo->physical_frame_buffer,
358		info.physical_frame_buffer, info.physical_frame_buffer_size);
359
360	size_t modesSize = 0;
361	vesa_mode* modes = (vesa_mode*)get_boot_item(VESA_MODES_BOOT_INFO,
362		&modesSize);
363	info.modes = modes;
364
365	size_t sharedSize = (sizeof(vesa_shared_info) + 7) & ~7;
366
367	info.shared_area = create_area("vesa shared info",
368		(void**)&info.shared_info, B_ANY_KERNEL_ADDRESS,
369		ROUND_TO_PAGE_SIZE(sharedSize + modesSize), B_FULL_LOCK,
370		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_USER_CLONEABLE_AREA);
371	if (info.shared_area < 0)
372		return info.shared_area;
373
374	vesa_shared_info& sharedInfo = *info.shared_info;
375
376	memset(&sharedInfo, 0, sizeof(vesa_shared_info));
377
378	if (modes != NULL) {
379		sharedInfo.vesa_mode_offset = sharedSize;
380		sharedInfo.vesa_mode_count = modesSize / sizeof(vesa_mode);
381
382		memcpy((uint8*)&sharedInfo + sharedSize, modes, modesSize);
383	}
384
385	sharedInfo.frame_buffer_area = bufferInfo->area;
386
387	remap_frame_buffer(info, bufferInfo->physical_frame_buffer,
388		bufferInfo->width, bufferInfo->height, bufferInfo->depth,
389		bufferInfo->bytes_per_row, true);
390		// Does not matter if this fails - the frame buffer was already mapped
391		// before.
392
393	sharedInfo.current_mode.virtual_width = bufferInfo->width;
394	sharedInfo.current_mode.virtual_height = bufferInfo->height;
395	sharedInfo.current_mode.space = get_color_space_for_depth(
396		bufferInfo->depth);
397
398	edid1_info* edidInfo = (edid1_info*)get_boot_item(VESA_EDID_BOOT_INFO,
399		NULL);
400	if (edidInfo != NULL) {
401		sharedInfo.has_edid = true;
402		memcpy(&sharedInfo.edid_info, edidInfo, sizeof(edid1_info));
403	}
404
405	vbe_get_dpms_capabilities(info.vbe_dpms_capabilities,
406		sharedInfo.dpms_capabilities);
407	if (bufferInfo->depth <= 8)
408		vbe_set_bits_per_gun(info, 8);
409
410	dprintf(DEVICE_NAME ": vesa_init() completed successfully!\n");
411	return B_OK;
412}
413
414
415void
416vesa_uninit(vesa_info& info)
417{
418	dprintf(DEVICE_NAME": vesa_uninit()\n");
419
420	delete_area(info.shared_info->frame_buffer_area);
421	delete_area(info.shared_area);
422}
423
424
425status_t
426vesa_set_display_mode(vesa_info& info, uint32 mode)
427{
428	if (mode >= info.shared_info->vesa_mode_count)
429		return B_ENTRY_NOT_FOUND;
430
431	// Prepare BIOS environment
432	bios_state* state;
433	status_t status = vbe_call_prepare(&state);
434	if (status != B_OK)
435		return status;
436
437	// Get mode information
438	struct vbe_mode_info modeInfo;
439	status = vbe_get_mode_info(state, info.modes[mode].mode, &modeInfo);
440	if (status != B_OK) {
441		dprintf(DEVICE_NAME": vesa_set_display_mode(): cannot get mode info\n");
442		goto out;
443	}
444
445	// Set mode
446	status = vbe_set_mode(state, info.modes[mode].mode);
447	if (status != B_OK) {
448		dprintf(DEVICE_NAME": vesa_set_display_mode(): cannot set mode\n");
449		goto out;
450	}
451
452	if (info.modes[mode].bits_per_pixel <= 8)
453		vbe_set_bits_per_gun(state, info, 8);
454
455	// Map new frame buffer if necessary
456
457	status = remap_frame_buffer(info, modeInfo.physical_base, modeInfo.width,
458		modeInfo.height, modeInfo.bits_per_pixel, modeInfo.bytes_per_row,
459		false);
460	if (status == B_OK) {
461		// Update shared frame buffer information
462		info.shared_info->current_mode.virtual_width = modeInfo.width;
463		info.shared_info->current_mode.virtual_height = modeInfo.height;
464		info.shared_info->current_mode.space = get_color_space_for_depth(
465			modeInfo.bits_per_pixel);
466	}
467
468out:
469	vbe_call_finish(state);
470	return status;
471}
472
473
474status_t
475vesa_get_dpms_mode(vesa_info& info, uint32& mode)
476{
477	mode = B_DPMS_ON;
478		// we always return a valid mode
479
480	// Prepare BIOS environment
481	bios_state* state;
482	status_t status = vbe_call_prepare(&state);
483	if (status != B_OK)
484		return status;
485
486	bios_regs regs = {};
487	regs.eax = 0x4f10;
488	regs.ebx = 2;
489	regs.esi = 0;
490	regs.edi = 0;
491
492	status = sBIOSModule->interrupt(state, 0x10, &regs);
493	if (status != B_OK) {
494		dprintf(DEVICE_NAME ": vesa_get_dpms_mode(): BIOS failed: %s\n",
495			strerror(status));
496		goto out;
497	}
498
499	if ((regs.eax & 0xffff) != 0x4f) {
500		dprintf(DEVICE_NAME ": vesa_get_dpms_mode(): BIOS returned "
501			"0x%" B_PRIx32 "\n", regs.eax & 0xffff);
502		status = B_ERROR;
503		goto out;
504	}
505
506	mode = vbe_to_system_dpms(regs.ebx >> 8);
507
508out:
509	vbe_call_finish(state);
510	return status;
511}
512
513
514status_t
515vesa_set_dpms_mode(vesa_info& info, uint32 mode)
516{
517	// Only let supported modes through
518	mode &= info.shared_info->dpms_capabilities;
519
520	uint8 vbeMode = 0;
521	if ((mode & B_DPMS_OFF) != 0)
522		vbeMode |= DPMS_OFF | DPMS_REDUCED_ON;
523	if ((mode & B_DPMS_STAND_BY) != 0)
524		vbeMode |= DPMS_STANDBY;
525	if ((mode & B_DPMS_SUSPEND) != 0)
526		vbeMode |= DPMS_SUSPEND;
527
528	vbeMode &= info.vbe_dpms_capabilities;
529
530	// Prepare BIOS environment
531	bios_state* state;
532	status_t status = vbe_call_prepare(&state);
533	if (status != B_OK)
534		return status;
535
536	bios_regs regs = {};
537	regs.eax = 0x4f10;
538	regs.ebx = (vbeMode << 8) | 1;
539	regs.esi = 0;
540	regs.edi = 0;
541
542	status = sBIOSModule->interrupt(state, 0x10, &regs);
543	if (status != B_OK) {
544		dprintf(DEVICE_NAME ": vesa_set_dpms_mode(): BIOS failed: %s\n",
545			strerror(status));
546		goto out;
547	}
548
549	if ((regs.eax & 0xffff) != 0x4f) {
550		dprintf(DEVICE_NAME ": vesa_set_dpms_mode(): BIOS returned "
551			"0x%04" B_PRIx32 "\n", regs.eax & 0xffff);
552		status = B_ERROR;
553		goto out;
554	}
555
556out:
557	vbe_call_finish(state);
558	return status;
559}
560
561
562status_t
563vesa_set_indexed_colors(vesa_info& info, uint8 first, uint8* colors,
564	uint16 count)
565{
566	bios_regs regs = {};
567	uint32 shift, physicalAddress;
568
569	if (first + count > 256)
570		count = 256 - first;
571
572	// Prepare BIOS environment
573	bios_state* state;
574	status_t status = vbe_call_prepare(&state);
575	if (status != B_OK)
576		return status;
577
578	uint8* palette = (uint8*)sBIOSModule->allocate_mem(state, 256 * 4);
579	if (palette == NULL) {
580		status = B_NO_MEMORY;
581		goto out;
582	}
583
584	shift = 8 - info.bits_per_gun;
585
586	// convert colors to VESA palette
587	for (int32 i = first; i < count; i++) {
588		uint8 color[3];
589		if (user_memcpy(color, &colors[i * 3], 3) < B_OK) {
590			status = B_BAD_ADDRESS;
591			goto out;
592		}
593
594		// order is BGR-
595		palette[i * 4 + 0] = color[2] >> shift;
596		palette[i * 4 + 1] = color[1] >> shift;
597		palette[i * 4 + 2] = color[0] >> shift;
598		palette[i * 4 + 3] = 0;
599	}
600
601	// set palette
602	physicalAddress = sBIOSModule->physical_address(state, palette);
603	regs.eax = 0x4f09;
604	regs.ebx = 0;
605	regs.ecx = count;
606	regs.edx = first;
607	regs.es  = physicalAddress >> 4;
608	regs.edi = physicalAddress - (regs.es << 4);
609
610	status = sBIOSModule->interrupt(state, 0x10, &regs);
611	if (status != B_OK) {
612		dprintf(DEVICE_NAME ": vesa_set_indexed_colors(): BIOS failed: %s\n",
613			strerror(status));
614		goto out;
615	}
616
617	if ((regs.eax & 0xffff) != 0x4f) {
618		dprintf(DEVICE_NAME ": vesa_set_indexed_colors(): BIOS returned "
619			"0x%04" B_PRIx32 "\n", regs.eax & 0xffff);
620		status = B_ERROR;
621	}
622
623out:
624	vbe_call_finish(state);
625	return status;
626}
627