1/*
2 * Copyright 2005-2015, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <stdlib.h>
8#include <string.h>
9
10#include <compute_display_timing.h>
11#include <create_display_modes.h>
12
13#include "accelerant_protos.h"
14#include "accelerant.h"
15#include "utility.h"
16#include "vesa_info.h"
17
18
19//#define TRACE_MODE
20#ifdef TRACE_MODE
21extern "C" void _sPrintf(const char* format, ...);
22#	define TRACE(x) _sPrintf x
23#else
24#	define TRACE(x) ;
25#endif
26
27
28struct nvidia_resolution {
29	int width;
30	int height;
31};
32
33static const nvidia_resolution kNVidiaAllowedResolutions[] = {
34	{ 1280, 720 },
35	{ 1280, 800 },
36	{ 1360, 768 },
37	{ 1400, 1050 },
38	{ 1440, 900 },
39	{ 1600, 900 },
40	{ 1600, 1200 },
41	{ 1680, 1050 },
42	{ 1920, 1080 },
43	{ 1920, 1200 },
44	{ 2048, 1536 },
45};
46
47
48static uint32
49get_color_space_for_depth(uint32 depth)
50{
51	switch (depth) {
52		case 4:
53			return B_GRAY8;
54				// the app_server is smart enough to translate this to VGA mode
55		case 8:
56			return B_CMAP8;
57		case 15:
58			return B_RGB15;
59		case 16:
60			return B_RGB16;
61		case 24:
62			return B_RGB24;
63		case 32:
64			return B_RGB32;
65	}
66
67	return 0;
68}
69
70
71/*!	Checks if the specified \a mode can be set using VESA. */
72static bool
73is_mode_supported(display_mode* mode)
74{
75	vesa_mode* modes = gInfo->vesa_modes;
76
77	bool colorspaceSupported = false;
78
79	for (uint32 i = gInfo->shared_info->vesa_mode_count; i-- > 0;) {
80		// search mode in VESA mode list
81		// TODO: list is ordered, we could use binary search
82		if (modes[i].width == mode->virtual_width
83			&& modes[i].height == mode->virtual_height
84			&& get_color_space_for_depth(modes[i].bits_per_pixel)
85				== mode->space)
86			return true;
87
88		if (get_color_space_for_depth(modes[i].bits_per_pixel) == mode->space)
89			colorspaceSupported = true;
90	}
91
92	bios_type_enum type = gInfo->shared_info->bios_type;
93	if (type == kIntelBiosType || type == kAtomBiosType1 || type == kAtomBiosType2) {
94		// We know how to patch the BIOS, so we can set any mode we want
95		return colorspaceSupported;
96	}
97
98	if (type == kNVidiaBiosType) {
99		for (size_t i = 0; i < B_COUNT_OF(kNVidiaAllowedResolutions); i++) {
100			if (mode->virtual_width == kNVidiaAllowedResolutions[i].width
101				&& mode->virtual_height == kNVidiaAllowedResolutions[i].height)
102				return colorspaceSupported;
103		}
104	}
105
106	return false;
107}
108
109
110/*!	Creates the initial mode list of the primary accelerant.
111	It's called from vesa_init_accelerant().
112*/
113status_t
114create_mode_list(void)
115{
116	const color_space kVesaSpaces[] = {B_RGB32_LITTLE, B_RGB24_LITTLE,
117		B_RGB16_LITTLE, B_RGB15_LITTLE, B_CMAP8};
118
119	uint32 initialModesCount = 0;
120
121	// Add initial VESA modes.
122	display_mode* initialModes = (display_mode*)malloc(
123		sizeof(display_mode) * gInfo->shared_info->vesa_mode_count);
124	if (initialModes != NULL) {
125		initialModesCount = gInfo->shared_info->vesa_mode_count;
126		vesa_mode* vesaModes = gInfo->vesa_modes;
127
128		for (uint32 i = 0; i < initialModesCount; i++) {
129			compute_display_timing(vesaModes[i].width, vesaModes[i].height,
130				60, false, &initialModes[i].timing);
131			fill_display_mode(vesaModes[i].width, vesaModes[i].height,
132				&initialModes[i]);
133		}
134	}
135
136	gInfo->mode_list_area = create_display_modes("vesa modes",
137		gInfo->shared_info->has_edid ? &gInfo->shared_info->edid_info : NULL,
138		initialModes, initialModesCount,
139		kVesaSpaces, sizeof(kVesaSpaces) / sizeof(kVesaSpaces[0]),
140		is_mode_supported, &gInfo->mode_list, &gInfo->shared_info->mode_count);
141
142	free(initialModes);
143
144	if (gInfo->mode_list_area < 0)
145		return gInfo->mode_list_area;
146
147	gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
148	return B_OK;
149}
150
151
152//	#pragma mark -
153
154
155uint32
156vesa_accelerant_mode_count(void)
157{
158	TRACE(("vesa_accelerant_mode_count() = %d\n", gInfo->shared_info->mode_count));
159	return gInfo->shared_info->mode_count;
160}
161
162
163status_t
164vesa_get_mode_list(display_mode* modeList)
165{
166	TRACE(("vesa_get_mode_info()\n"));
167	memcpy(modeList, gInfo->mode_list,
168		gInfo->shared_info->mode_count * sizeof(display_mode));
169	return B_OK;
170}
171
172
173status_t
174vesa_propose_display_mode(display_mode* target, const display_mode* low,
175	const display_mode* high)
176{
177	TRACE(("vesa_propose_display_mode()\n"));
178
179	// Search for the specified mode in the list. If it's in there, we don't need a custom mode and
180	// we just normalize it to the info provided by the VESA BIOS.
181
182	for (uint32 i = 0; i < gInfo->shared_info->mode_count; i++) {
183		display_mode* current = &gInfo->mode_list[i];
184
185		if (target->virtual_width != current->virtual_width
186			|| target->virtual_height != current->virtual_height
187			|| target->space != current->space)
188			continue;
189
190		*target = *current;
191		return B_OK;
192	}
193
194	bios_type_enum type = gInfo->shared_info->bios_type;
195	if (type == kIntelBiosType || type == kAtomBiosType1 || type == kAtomBiosType2) {
196		// The driver says it knows the BIOS type, and therefore how to patch it to apply custom
197		// modes.
198		return B_OK;
199	}
200
201	if (type == kNVidiaBiosType) {
202		// For NVidia there is only a limited set of extra resolutions we know how to set
203		for (size_t i = 0; i < B_COUNT_OF(kNVidiaAllowedResolutions); i++) {
204			if (target->virtual_width == kNVidiaAllowedResolutions[i].width
205				&& target->virtual_height == kNVidiaAllowedResolutions[i].height)
206				return B_OK;
207		}
208	}
209
210	return B_BAD_VALUE;
211}
212
213
214status_t
215vesa_set_display_mode(display_mode* _mode)
216{
217	TRACE(("vesa_set_display_mode()\n"));
218
219	display_mode mode = *_mode;
220	if (vesa_propose_display_mode(&mode, &mode, &mode) != B_OK)
221		return B_BAD_VALUE;
222
223	vesa_mode* modes = gInfo->vesa_modes;
224	for (int32 i = gInfo->shared_info->vesa_mode_count; i-- > 0;) {
225		// search mode in VESA mode list
226		// TODO: list is ordered, we could use binary search
227		if (modes[i].width == mode.virtual_width
228			&& modes[i].height == mode.virtual_height
229			&& get_color_space_for_depth(modes[i].bits_per_pixel)
230				== mode.space) {
231			if (gInfo->current_mode == i)
232				return B_OK;
233			status_t result = ioctl(gInfo->device, VESA_SET_DISPLAY_MODE, &i, sizeof(i));
234			if (result == B_OK)
235				gInfo->current_mode = i;
236			return result;
237		}
238	}
239
240	// If the mode is not found in the list of standard mode, live patch the BIOS to get it anyway
241	status_t result = ioctl(gInfo->device, VESA_SET_CUSTOM_DISPLAY_MODE,
242		&mode, sizeof(display_mode));
243	if (result == B_OK) {
244		gInfo->current_mode = -1;
245	}
246
247	return result;
248}
249
250
251status_t
252vesa_get_display_mode(display_mode* _currentMode)
253{
254	TRACE(("vesa_get_display_mode()\n"));
255	*_currentMode = gInfo->shared_info->current_mode;
256	return B_OK;
257}
258
259
260status_t
261vesa_get_edid_info(void* info, size_t size, uint32* _version)
262{
263	TRACE(("vesa_get_edid_info()\n"));
264
265	if (!gInfo->shared_info->has_edid)
266		return B_ERROR;
267	if (size < sizeof(struct edid1_info))
268		return B_BUFFER_OVERFLOW;
269
270	memcpy(info, &gInfo->shared_info->edid_info, sizeof(struct edid1_info));
271	*_version = EDID_VERSION_1;
272	return B_OK;
273}
274
275
276status_t
277vesa_get_frame_buffer_config(frame_buffer_config* config)
278{
279	TRACE(("vesa_get_frame_buffer_config()\n"));
280
281	config->frame_buffer = gInfo->shared_info->frame_buffer;
282	config->frame_buffer_dma = gInfo->shared_info->physical_frame_buffer;
283	config->bytes_per_row = gInfo->shared_info->bytes_per_row;
284
285	return B_OK;
286}
287
288
289status_t
290vesa_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high)
291{
292	TRACE(("vesa_get_pixel_clock_limits()\n"));
293
294	// TODO: do some real stuff here (taken from radeon driver)
295	uint32 totalPixel = (uint32)mode->timing.h_total
296		* (uint32)mode->timing.v_total;
297	uint32 clockLimit = 2000000;
298
299	// lower limit of about 48Hz vertical refresh
300	*_low = totalPixel * 48L / 1000L;
301	if (*_low > clockLimit)
302		return B_ERROR;
303
304	*_high = clockLimit;
305	return B_OK;
306}
307
308
309status_t
310vesa_move_display(uint16 h_display_start, uint16 v_display_start)
311{
312	TRACE(("vesa_move_display()\n"));
313	return B_ERROR;
314}
315
316
317status_t
318vesa_get_timing_constraints(display_timing_constraints* constraints)
319{
320	TRACE(("vesa_get_timing_constraints()\n"));
321	return B_ERROR;
322}
323
324
325void
326vesa_set_indexed_colors(uint count, uint8 first, uint8* colors, uint32 flags)
327{
328	TRACE(("vesa_set_indexed_colors()\n"));
329
330	vesa_set_indexed_colors_args args;
331	args.first = first;
332	args.count = count;
333	args.colors = colors;
334	ioctl(gInfo->device, VESA_SET_INDEXED_COLORS, &args, sizeof(args));
335}
336
337