xref: /haiku/src/add-ons/kernel/drivers/graphics/framebuffer/framebuffer.cpp (revision 13581b3d2a71545960b98fefebc5225b5bf29072)
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 		// 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 
159 status_t
160 framebuffer_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 
212 void
213 framebuffer_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