1/*
2 * Copyright 2006-2013, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Support for i915 chipset and up based on the X driver,
6 * Copyright 2006-2007 Intel Corporation.
7 *
8 * Authors:
9 *		Axel D��rfler, axeld@pinc-software.de
10 *		Alexander von Gluck, kallisti5@unixzen.com
11 */
12
13
14#include "mode.h"
15
16#include <create_display_modes.h>
17#include <stdio.h>
18#include <string.h>
19#include <math.h>
20
21#include "accelerant.h"
22#include "accelerant_protos.h"
23#include "bios.h"
24#include "connector.h"
25#include "display.h"
26#include "displayport.h"
27#include "encoder.h"
28#include "pll.h"
29#include "utility.h"
30
31
32#define TRACE_MODE
33#ifdef TRACE_MODE
34extern "C" void _sPrintf(const char* format, ...);
35#	define TRACE(x...) _sPrintf("radeon_hd: " x)
36#else
37#	define TRACE(x...) ;
38#endif
39
40#define ERROR(x...) _sPrintf("radeon_hd: " x)
41
42
43status_t
44create_mode_list(void)
45{
46	// TODO: multi-monitor?  for now we use VESA and not gDisplay edid
47
48	const color_space kRadeonHDSpaces[] = {B_RGB32_LITTLE, B_RGB24_LITTLE,
49		B_RGB16_LITTLE, B_RGB15_LITTLE, B_CMAP8};
50
51	gInfo->mode_list_area = create_display_modes("radeon HD modes",
52		gInfo->shared_info->has_edid ? &gInfo->shared_info->edid_info : NULL,
53		NULL, 0, kRadeonHDSpaces,
54		sizeof(kRadeonHDSpaces) / sizeof(kRadeonHDSpaces[0]),
55		is_mode_supported, &gInfo->mode_list, &gInfo->shared_info->mode_count);
56	if (gInfo->mode_list_area < B_OK)
57		return gInfo->mode_list_area;
58
59	gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
60
61	return B_OK;
62}
63
64
65//	#pragma mark -
66
67
68uint32
69radeon_accelerant_mode_count(void)
70{
71	TRACE("%s\n", __func__);
72	// TODO: multi-monitor?  we need crtcid here
73
74	return gInfo->shared_info->mode_count;
75}
76
77
78status_t
79radeon_get_mode_list(display_mode* modeList)
80{
81	TRACE("%s\n", __func__);
82	// TODO: multi-monitor?  we need crtcid here
83	memcpy(modeList, gInfo->mode_list,
84		gInfo->shared_info->mode_count * sizeof(display_mode));
85	return B_OK;
86}
87
88
89status_t
90radeon_get_preferred_mode(display_mode* preferredMode)
91{
92	TRACE("%s\n", __func__);
93	// TODO: multi-monitor?  we need crtcid here
94
95	uint8_t crtc = 0;
96
97	if (gDisplay[crtc]->preferredMode.virtual_width > 0
98		&& gDisplay[crtc]->preferredMode.virtual_height > 0) {
99		TRACE("%s: preferred mode was found for display %" B_PRIu8 "\n",
100			__func__, crtc);
101		memcpy(preferredMode, &gDisplay[crtc]->preferredMode,
102			sizeof(gDisplay[crtc]->preferredMode));
103		return B_OK;
104	}
105
106	return B_ERROR;
107}
108
109
110status_t
111radeon_get_edid_info(void* info, size_t size, uint32* edid_version)
112{
113	// TODO: multi-monitor?  for now we use VESA edid
114
115	TRACE("%s\n", __func__);
116	if (!gInfo->shared_info->has_edid)
117		return B_ERROR;
118	if (size < sizeof(struct edid1_info))
119		return B_BUFFER_OVERFLOW;
120
121	memcpy(info, &gInfo->shared_info->edid_info, sizeof(struct edid1_info));
122		// VESA
123	//memcpy(info, &gDisplay[0]->edidData, sizeof(struct edid1_info));
124		// Display 0
125
126	*edid_version = EDID_VERSION_1;
127
128	return B_OK;
129}
130
131
132uint32
133radeon_dpms_capabilities(void)
134{
135	// These should be pretty universally supported on Radeon HD cards
136	return B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF;
137}
138
139
140uint32
141radeon_dpms_mode(void)
142{
143	// TODO: this really isn't a good long-term solution
144	// we may need to look at the encoder dpms scratch registers
145	return gInfo->dpms_mode;
146}
147
148
149void
150radeon_dpms_set(uint8 id, int mode)
151{
152	if (mode == B_DPMS_ON) {
153		display_crtc_dpms(id, mode);
154		encoder_dpms_set(id, mode);
155	} else {
156		encoder_dpms_set(id, mode);
157		display_crtc_dpms(id, mode);
158	}
159	gInfo->dpms_mode = mode;
160}
161
162
163void
164radeon_dpms_set_hook(int mode)
165{
166	// TODO: multi-monitor?  for now we use VESA edid
167
168	// As the accelerant hook doesn't pass crtc id
169	for (uint8 id = 0; id < MAX_DISPLAY; id++) {
170		radeon_dpms_set(id, mode);
171	}
172}
173
174
175status_t
176radeon_set_display_mode(display_mode* mode)
177{
178	// TODO: multi-monitor? For now we set the mode on
179	// all displays (this is very incorrect). This also
180	// causes a lot of problems on DisplayPort devices
181
182	// Set mode on each display
183	for (uint8 id = 0; id < MAX_DISPLAY; id++) {
184		if (gDisplay[id]->attached == false)
185			continue;
186
187		// Copy this display mode into the "current mode" for the display
188		memcpy(&gDisplay[id]->currentMode, mode, sizeof(display_mode));
189
190		uint32 connectorIndex = gDisplay[id]->connectorIndex;
191
192		// Determine DP lanes if DP
193		if (connector_is_dp(connectorIndex)) {
194			dp_info *dpInfo = &gConnector[connectorIndex]->dpInfo;
195			dpInfo->laneCount = dp_get_lane_count(connectorIndex, mode);
196			dpInfo->linkRate = dp_get_link_rate(connectorIndex, mode);
197		}
198
199		// *** crtc and encoder prep
200		encoder_output_lock(true);
201		display_crtc_lock(id, ATOM_ENABLE);
202		radeon_dpms_set(id, B_DPMS_OFF);
203
204		// *** Set up encoder -> crtc routing
205		encoder_assign_crtc(id);
206
207		// *** CRT controler mode set
208		// Set up PLL for connector
209		pll_pick(connectorIndex);
210		pll_info* pll = &gConnector[connectorIndex]->encoder.pll;
211		TRACE("%s: pll %d selected for connector %" B_PRIu32 "\n", __func__,
212			pll->id, connectorIndex);
213		pll_set(mode, id);
214
215		display_crtc_set_dtd(id, mode);
216
217		display_crtc_fb_set(id, mode);
218		// atombios_overscan_setup
219		display_crtc_scale(id, mode);
220
221		// *** encoder mode set
222		encoder_mode_set(id);
223
224		// *** encoder and CRT controller commit
225		radeon_dpms_set(id, B_DPMS_ON);
226		display_crtc_lock(id, ATOM_DISABLE);
227		encoder_output_lock(false);
228	}
229
230	#ifdef TRACE_MODE
231	// for debugging
232	debug_dp_info();
233
234	TRACE("D1CRTC_STATUS        Value: 0x%X\n",
235		Read32(CRT, AVIVO_D1CRTC_STATUS));
236	TRACE("D2CRTC_STATUS        Value: 0x%X\n",
237		Read32(CRT, AVIVO_D2CRTC_STATUS));
238	TRACE("D1CRTC_CONTROL       Value: 0x%X\n",
239		Read32(CRT, AVIVO_D1CRTC_CONTROL));
240	TRACE("D2CRTC_CONTROL       Value: 0x%X\n",
241		Read32(CRT, AVIVO_D2CRTC_CONTROL));
242	TRACE("D1GRPH_ENABLE        Value: 0x%X\n",
243		Read32(CRT, AVIVO_D1GRPH_ENABLE));
244	TRACE("D2GRPH_ENABLE        Value: 0x%X\n",
245		Read32(CRT, AVIVO_D2GRPH_ENABLE));
246	TRACE("D1SCL_ENABLE         Value: 0x%X\n",
247		Read32(CRT, AVIVO_D1SCL_SCALER_ENABLE));
248	TRACE("D2SCL_ENABLE         Value: 0x%X\n",
249		Read32(CRT, AVIVO_D2SCL_SCALER_ENABLE));
250	TRACE("D1CRTC_BLANK_CONTROL Value: 0x%X\n",
251		Read32(CRT, AVIVO_D1CRTC_BLANK_CONTROL));
252	TRACE("D2CRTC_BLANK_CONTROL Value: 0x%X\n",
253		Read32(CRT, AVIVO_D1CRTC_BLANK_CONTROL));
254	#endif
255
256	return B_OK;
257}
258
259
260status_t
261radeon_get_display_mode(display_mode* _currentMode)
262{
263	TRACE("%s\n", __func__);
264
265	*_currentMode = gInfo->shared_info->current_mode;
266	//*_currentMode = gDisplay[X]->currentMode;
267	return B_OK;
268}
269
270
271status_t
272radeon_get_frame_buffer_config(frame_buffer_config* config)
273{
274	TRACE("%s\n", __func__);
275
276	config->frame_buffer = gInfo->shared_info->frame_buffer;
277	config->frame_buffer_dma = (uint8*)gInfo->shared_info->frame_buffer_phys;
278
279	config->bytes_per_row = gInfo->shared_info->bytes_per_row;
280
281	return B_OK;
282}
283
284
285status_t
286radeon_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high)
287{
288	TRACE("%s\n", __func__);
289
290	if (_low != NULL) {
291		// lower limit of about 48Hz vertical refresh
292		uint32 totalClocks = (uint32)mode->timing.h_total
293			* (uint32)mode->timing.v_total;
294		uint32 low = (totalClocks * 48L) / 1000L;
295
296		if (low < PLL_MIN_DEFAULT)
297			low = PLL_MIN_DEFAULT;
298		else if (low > PLL_MAX_DEFAULT)
299			return B_ERROR;
300
301		*_low = low;
302	}
303
304	if (_high != NULL)
305		*_high = PLL_MAX_DEFAULT;
306
307	//*_low = 48L;
308	//*_high = 100 * 1000000L;
309	return B_OK;
310}
311
312
313bool
314is_mode_supported(display_mode* mode)
315{
316	bool sane = true;
317
318	// Validate modeline is within a sane range
319	if (is_mode_sane(mode) != B_OK)
320		sane = false;
321
322	// TODO: is_mode_supported on *which* display?
323	uint32 crtid = 0;
324
325	// if we have edid info, check frequency adginst crt reported valid ranges
326	if (gInfo->shared_info->has_edid
327		&& gDisplay[crtid]->foundRanges) {
328
329		// validate horizontal frequency range
330		uint32 hfreq = mode->timing.pixel_clock / mode->timing.h_total;
331		if (hfreq > gDisplay[crtid]->hfreqMax + 1
332			|| hfreq < gDisplay[crtid]->hfreqMin - 1) {
333			//TRACE("!!! mode below falls outside of hfreq range!\n");
334			sane = false;
335		}
336
337		// validate vertical frequency range
338		uint32 vfreq = mode->timing.pixel_clock / ((mode->timing.v_total
339			* mode->timing.h_total) / 1000);
340		if (vfreq > gDisplay[crtid]->vfreqMax + 1
341			|| vfreq < gDisplay[crtid]->vfreqMin - 1) {
342			//TRACE("!!! mode below falls outside of vfreq range!\n");
343			sane = false;
344		}
345	}
346
347	TRACE("MODE: %d ; %d %d %d %d ; %d %d %d %d is %s\n",
348		mode->timing.pixel_clock, mode->timing.h_display,
349		mode->timing.h_sync_start, mode->timing.h_sync_end,
350		mode->timing.h_total, mode->timing.v_display,
351		mode->timing.v_sync_start, mode->timing.v_sync_end,
352		mode->timing.v_total,
353		sane ? "OK." : "BAD, out of range!");
354
355	return sane;
356}
357
358
359/*
360 * A quick sanity check of the provided display_mode
361 */
362status_t
363is_mode_sane(display_mode* mode)
364{
365	// horizontal timing
366	// validate h_sync_start is less then h_sync_end
367	if (mode->timing.h_sync_start > mode->timing.h_sync_end) {
368		TRACE("%s: ERROR: (%dx%d) "
369			"received h_sync_start greater then h_sync_end!\n",
370			__func__, mode->timing.h_display, mode->timing.v_display);
371		return B_ERROR;
372	}
373	// validate h_total is greater then h_display
374	if (mode->timing.h_total < mode->timing.h_display) {
375		TRACE("%s: ERROR: (%dx%d) "
376			"received h_total greater then h_display!\n",
377			__func__, mode->timing.h_display, mode->timing.v_display);
378		return B_ERROR;
379	}
380
381	// vertical timing
382	// validate v_start is less then v_end
383	if (mode->timing.v_sync_start > mode->timing.v_sync_end) {
384		TRACE("%s: ERROR: (%dx%d) "
385			"received v_sync_start greater then v_sync_end!\n",
386			__func__, mode->timing.h_display, mode->timing.v_display);
387		return B_ERROR;
388	}
389	// validate v_total is greater then v_display
390	if (mode->timing.v_total < mode->timing.v_display) {
391		TRACE("%s: ERROR: (%dx%d) "
392			"received v_total greater then v_display!\n",
393			__func__, mode->timing.h_display, mode->timing.v_display);
394		return B_ERROR;
395	}
396
397	// calculate refresh rate for given timings to whole int (in Hz)
398	int refresh = mode->timing.pixel_clock * 1000
399		/ (mode->timing.h_total * mode->timing.v_total);
400
401	if (refresh < 30 || refresh > 250) {
402		TRACE("%s: ERROR: (%dx%d) "
403			"refresh rate of %dHz is unlikely for any kind of monitor!\n",
404			__func__, mode->timing.h_display, mode->timing.v_display, refresh);
405		return B_ERROR;
406	}
407
408	return B_OK;
409}
410
411
412uint32
413get_mode_bpp(display_mode* mode)
414{
415	// Get bitsPerPixel for given mode
416
417	switch (mode->space) {
418		case B_CMAP8:
419			return 8;
420		case B_RGB15_LITTLE:
421			return 15;
422		case B_RGB16_LITTLE:
423			return 16;
424		case B_RGB24_LITTLE:
425		case B_RGB32_LITTLE:
426			return 32;
427	}
428	ERROR("%s: Unknown colorspace for mode, guessing 32 bits per pixel\n",
429		__func__);
430	return 32;
431}
432