1/*
2 * Copyright 2005-2009, Axel D��rfler, axeld@pinc-software.de.
3 * Copyright 2016, Jessica Hamilton, jessica.l.hamilton@gmail.com.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include "framebuffer_private.h"
9#include "vesa.h"
10
11#include <string.h>
12
13#include <drivers/bios.h>
14
15#include <boot_item.h>
16#include <frame_buffer_console.h>
17#include <util/kernel_cpp.h>
18#include <vm/vm.h>
19
20#include "driver.h"
21#include "utility.h"
22#include "vesa_info.h"
23
24
25static status_t
26find_graphics_card(addr_t frameBuffer, addr_t& base, size_t& size)
27{
28	// TODO: when we port this over to the new driver API, this mechanism can be
29	// used to find the right device_node
30	pci_module_info* pci;
31	if (get_module(B_PCI_MODULE_NAME, (module_info**)&pci) != B_OK)
32		return B_ERROR;
33
34	pci_info info;
35	for (int32 index = 0; pci->get_nth_pci_info(index, &info) == B_OK; index++) {
36		if (info.class_base != PCI_display)
37			continue;
38
39		// check PCI BARs
40		for (uint32 i = 0; i < 6; i++) {
41			phys_addr_t addr = info.u.h0.base_registers[i];
42			uint64 barSize = info.u.h0.base_register_sizes[i];
43			if (i < 5
44				&& (info.u.h0.base_register_flags[i] & PCI_address_type) == PCI_address_type_64) {
45				addr |= (uint64)info.u.h0.base_registers[i + 1] << 32;
46				barSize |= (uint64)info.u.h0.base_register_sizes[i + 1] << 32;
47				i++;
48			}
49			if (addr <= frameBuffer && addr + barSize > frameBuffer) {
50				// found it!
51				base = addr;
52				size = barSize;
53				dprintf(DEVICE_NAME " find_graphics_card: found base 0x%lx size %" B_PRIuSIZE "\n",
54					base, size);
55
56				put_module(B_PCI_MODULE_NAME);
57				return B_OK;
58			}
59		}
60	}
61
62	dprintf(DEVICE_NAME " find_graphics_card: no entry found for 0x%lx\n", frameBuffer);
63	put_module(B_PCI_MODULE_NAME);
64	return B_ENTRY_NOT_FOUND;
65}
66
67
68static uint32
69get_color_space_for_depth(uint32 depth)
70{
71	switch (depth) {
72		case 1:
73			return B_GRAY1;
74		case 4:
75			return B_GRAY8;
76				// the app_server is smart enough to translate this to VGA mode
77		case 8:
78			return B_CMAP8;
79		case 15:
80			return B_RGB15;
81		case 16:
82			return B_RGB16;
83		case 24:
84			return B_RGB24;
85		case 32:
86			return B_RGB32;
87	}
88
89	return 0;
90}
91
92
93/*!	Remaps the frame buffer if necessary; if we've already mapped the complete
94	frame buffer, there is no need to map it again.
95*/
96static status_t
97remap_frame_buffer(framebuffer_info& info, addr_t physicalBase, uint32 width,
98	uint32 height, int8 depth, uint32 bytesPerRow, bool initializing)
99{
100	vesa_shared_info& sharedInfo = *info.shared_info;
101	addr_t frameBuffer = info.frame_buffer;
102
103	if (!info.complete_frame_buffer_mapped) {
104		addr_t base = physicalBase;
105		size_t size = bytesPerRow * height;
106		// TODO: this logic looks suspicious and may need refactoring
107		bool remap = !initializing || frameBuffer == 0;
108
109		if (info.physical_frame_buffer_size != 0) {
110			// we can map the complete frame buffer
111			base = info.physical_frame_buffer;
112			size = info.physical_frame_buffer_size;
113			remap = true;
114		}
115
116		if (remap) {
117			area_id area = map_physical_memory("framebuffer buffer", base,
118				size, B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_WRITE_AREA,
119				(void**)&frameBuffer);
120			if (area < 0)
121				return area;
122
123			if (initializing) {
124				// We need to manually update the kernel's frame buffer address,
125				// since this frame buffer remapping has not been issued by the
126				// app_server (which would otherwise take care of this)
127				frame_buffer_update(frameBuffer, width, height, depth,
128					bytesPerRow);
129			}
130
131			delete_area(info.shared_info->frame_buffer_area);
132
133			info.frame_buffer = frameBuffer;
134			sharedInfo.frame_buffer_area = area;
135
136			// Turn on write combining for the area
137			vm_set_area_memory_type(area, base, B_MTR_WC);
138
139			if (info.physical_frame_buffer_size != 0)
140				info.complete_frame_buffer_mapped = true;
141		}
142	}
143
144	if (info.complete_frame_buffer_mapped)
145		frameBuffer += physicalBase - info.physical_frame_buffer;
146
147	// Update shared frame buffer information
148	sharedInfo.frame_buffer = (uint8*)frameBuffer;
149	sharedInfo.physical_frame_buffer = (uint8*)physicalBase;
150	sharedInfo.bytes_per_row = bytesPerRow;
151
152	return B_OK;
153}
154
155
156//	#pragma mark -
157
158
159status_t
160framebuffer_init(framebuffer_info& info)
161{
162	frame_buffer_boot_info* bufferInfo
163		= (frame_buffer_boot_info*)get_boot_item(FRAME_BUFFER_BOOT_INFO, NULL);
164	if (bufferInfo == NULL)
165		return B_ERROR;
166
167	info.complete_frame_buffer_mapped = false;
168
169	// Find out which PCI device we belong to, so that we know its frame buffer
170	// size
171	find_graphics_card(bufferInfo->physical_frame_buffer,
172		info.physical_frame_buffer, info.physical_frame_buffer_size);
173
174	size_t sharedSize = (sizeof(vesa_shared_info) + 7) & ~7;
175
176	info.shared_area = create_area("framebuffer shared info",
177		(void**)&info.shared_info, B_ANY_KERNEL_ADDRESS,
178		ROUND_TO_PAGE_SIZE(sharedSize), B_FULL_LOCK,
179		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA);
180	if (info.shared_area < 0)
181		return info.shared_area;
182
183	vesa_shared_info& sharedInfo = *info.shared_info;
184
185	memset(&sharedInfo, 0, sizeof(vesa_shared_info));
186
187	sharedInfo.frame_buffer_area = bufferInfo->area;
188
189	remap_frame_buffer(info, bufferInfo->physical_frame_buffer,
190		bufferInfo->width, bufferInfo->height, bufferInfo->depth,
191		bufferInfo->bytes_per_row, true);
192		// Does not matter if this fails - the frame buffer was already mapped
193		// before.
194
195	sharedInfo.current_mode.virtual_width = bufferInfo->width;
196	sharedInfo.current_mode.virtual_height = bufferInfo->height;
197	sharedInfo.current_mode.space = get_color_space_for_depth(
198		bufferInfo->depth);
199
200	edid1_info* edidInfo = (edid1_info*)get_boot_item(VESA_EDID_BOOT_INFO,
201		NULL);
202	if (edidInfo != NULL) {
203		sharedInfo.has_edid = true;
204		memcpy(&sharedInfo.edid_info, edidInfo, sizeof(edid1_info));
205	}
206
207	dprintf(DEVICE_NAME ": framebuffer_init() completed successfully!\n");
208	return B_OK;
209}
210
211
212void
213framebuffer_uninit(framebuffer_info& info)
214{
215	dprintf(DEVICE_NAME": framebuffer_uninit()\n");
216
217	delete_area(info.shared_info->frame_buffer_area);
218	delete_area(info.shared_area);
219}
220