xref: /haiku/src/add-ons/kernel/drivers/graphics/framebuffer/framebuffer.cpp (revision 9e25244c5e9051f6cd333820d6332397361abd6c)
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 
25 static status_t
26 find_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 
68 static uint32
69 get_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 */
96 static status_t
97 remap_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 		bool remap = !initializing;
107 
108 		if (info.physical_frame_buffer_size != 0) {
109 			// we can map the complete frame buffer
110 			base = info.physical_frame_buffer;
111 			size = info.physical_frame_buffer_size;
112 			remap = true;
113 		}
114 
115 		if (remap) {
116 			area_id area = map_physical_memory("framebuffer buffer", base,
117 				size, B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_WRITE_AREA,
118 				(void**)&frameBuffer);
119 			if (area < 0)
120 				return area;
121 
122 			if (initializing) {
123 				// We need to manually update the kernel's frame buffer address,
124 				// since this frame buffer remapping has not been issued by the
125 				// app_server (which would otherwise take care of this)
126 				frame_buffer_update(frameBuffer, width, height, depth,
127 					bytesPerRow);
128 			}
129 
130 			delete_area(info.shared_info->frame_buffer_area);
131 
132 			info.frame_buffer = frameBuffer;
133 			sharedInfo.frame_buffer_area = area;
134 
135 			// Turn on write combining for the area
136 			vm_set_area_memory_type(area, base, B_MTR_WC);
137 
138 			if (info.physical_frame_buffer_size != 0)
139 				info.complete_frame_buffer_mapped = true;
140 		}
141 	}
142 
143 	if (info.complete_frame_buffer_mapped)
144 		frameBuffer += physicalBase - info.physical_frame_buffer;
145 
146 	// Update shared frame buffer information
147 	sharedInfo.frame_buffer = (uint8*)frameBuffer;
148 	sharedInfo.physical_frame_buffer = (uint8*)physicalBase;
149 	sharedInfo.bytes_per_row = bytesPerRow;
150 
151 	return B_OK;
152 }
153 
154 
155 //	#pragma mark -
156 
157 
158 status_t
159 framebuffer_init(framebuffer_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 	info.complete_frame_buffer_mapped = false;
167 
168 	// Find out which PCI device we belong to, so that we know its frame buffer
169 	// size
170 	find_graphics_card(bufferInfo->physical_frame_buffer,
171 		info.physical_frame_buffer, info.physical_frame_buffer_size);
172 
173 	size_t sharedSize = (sizeof(vesa_shared_info) + 7) & ~7;
174 
175 	info.shared_area = create_area("framebuffer shared info",
176 		(void**)&info.shared_info, B_ANY_KERNEL_ADDRESS,
177 		ROUND_TO_PAGE_SIZE(sharedSize), B_FULL_LOCK,
178 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA);
179 	if (info.shared_area < 0)
180 		return info.shared_area;
181 
182 	vesa_shared_info& sharedInfo = *info.shared_info;
183 
184 	memset(&sharedInfo, 0, sizeof(vesa_shared_info));
185 
186 	sharedInfo.frame_buffer_area = bufferInfo->area;
187 
188 	remap_frame_buffer(info, bufferInfo->physical_frame_buffer,
189 		bufferInfo->width, bufferInfo->height, bufferInfo->depth,
190 		bufferInfo->bytes_per_row, true);
191 		// Does not matter if this fails - the frame buffer was already mapped
192 		// before.
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 
199 	dprintf(DEVICE_NAME ": framebuffer_init() completed successfully!\n");
200 	return B_OK;
201 }
202 
203 
204 void
205 framebuffer_uninit(framebuffer_info& info)
206 {
207 	dprintf(DEVICE_NAME": framebuffer_uninit()\n");
208 
209 	delete_area(info.shared_info->frame_buffer_area);
210 	delete_area(info.shared_area);
211 }
212