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	uint8 crtcID = 0;
48
49	const color_space kRadeonHDSpaces[] = {B_RGB32_LITTLE, B_RGB24_LITTLE,
50		B_RGB16_LITTLE, B_RGB15_LITTLE, B_CMAP8};
51
52	gInfo->mode_list_area = create_display_modes("radeon HD modes",
53		&gDisplay[crtcID]->edidData, NULL, 0, kRadeonHDSpaces,
54		B_COUNT_OF(kRadeonHDSpaces), is_mode_supported, &gInfo->mode_list,
55		&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 display 0
114	uint8 crtcID = 0;
115
116	TRACE("%s\n", __func__);
117	if (!gInfo->shared_info->has_edid)
118		return B_ERROR;
119	if (size < sizeof(struct edid1_info))
120		return B_BUFFER_OVERFLOW;
121
122	//memcpy(info, &gInfo->shared_info->edid_info, sizeof(struct edid1_info));
123		// VESA
124	memcpy(info, &gDisplay[crtcID]->edidData, sizeof(struct edid1_info));
125		// Display 0
126
127	*edid_version = EDID_VERSION_1;
128
129	return B_OK;
130}
131
132
133uint32
134radeon_dpms_capabilities(void)
135{
136	// These should be pretty universally supported on Radeon HD cards
137	return B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF;
138}
139
140
141uint32
142radeon_dpms_mode(void)
143{
144	// TODO: this really isn't a good long-term solution
145	// we may need to look at the encoder dpms scratch registers
146	return gInfo->dpms_mode;
147}
148
149
150void
151radeon_dpms_set(uint8 id, int mode)
152{
153	if (mode == B_DPMS_ON) {
154		display_crtc_dpms(id, mode);
155		encoder_dpms_set(id, mode);
156	} else {
157		encoder_dpms_set(id, mode);
158		display_crtc_dpms(id, mode);
159	}
160	gInfo->dpms_mode = mode;
161}
162
163
164void
165radeon_dpms_set_hook(int mode)
166{
167	// TODO: multi-monitor?
168
169	uint8 crtcID = 0;
170
171	if (gDisplay[crtcID]->attached)
172		radeon_dpms_set(crtcID, mode);
173}
174
175
176status_t
177radeon_set_display_mode(display_mode* mode)
178{
179	// TODO: multi-monitor? For now we set the mode on
180	// the first display found.
181
182	TRACE("%s\n", __func__);
183	TRACE("  mode->space: %#" B_PRIx32 "\n", mode->space);
184	TRACE("  mode->virtual_width: %" B_PRIu16 "\n", mode->virtual_width);
185	TRACE("  mode->virtual_height: %" B_PRIu16 "\n", mode->virtual_height);
186	TRACE("  mode->h_display_start: %" B_PRIu16 "\n", mode->h_display_start);
187	TRACE("  mode->v_display_start: %" B_PRIu16 "\n", mode->v_display_start);
188	TRACE("  mode->flags: %#" B_PRIx32 "\n", mode->flags);
189
190	uint8 crtcID = 0;
191
192	if (gDisplay[crtcID]->attached == false)
193		return B_ERROR;
194
195	// Copy this display mode into the "current mode" for the display
196	memcpy(&gDisplay[crtcID]->currentMode, mode, sizeof(display_mode));
197
198	uint32 connectorIndex = gDisplay[crtcID]->connectorIndex;
199
200	// Determine DP lanes if DP
201	if (connector_is_dp(connectorIndex)) {
202		dp_info *dpInfo = &gConnector[connectorIndex]->dpInfo;
203		dpInfo->laneCount = dp_get_lane_count(connectorIndex, mode);
204		dpInfo->linkRate = dp_get_link_rate(connectorIndex, mode);
205	}
206
207	// *** crtc and encoder prep
208	encoder_output_lock(true);
209	display_crtc_lock(crtcID, ATOM_ENABLE);
210	radeon_dpms_set(crtcID, B_DPMS_OFF);
211
212	// *** Set up encoder -> crtc routing
213	encoder_assign_crtc(crtcID);
214
215	// *** CRT controler mode set
216	// Set up PLL for connector
217	pll_pick(connectorIndex);
218	pll_info* pll = &gConnector[connectorIndex]->encoder.pll;
219	TRACE("%s: pll %d selected for connector %" B_PRIu32 "\n", __func__,
220		pll->id, connectorIndex);
221	pll_set(mode, crtcID);
222
223	display_crtc_set_dtd(crtcID, mode);
224
225	display_crtc_fb_set(crtcID, mode);
226	// atombios_overscan_setup
227	display_crtc_scale(crtcID, mode);
228
229	// *** encoder mode set
230	encoder_mode_set(crtcID);
231
232	// *** encoder and CRT controller commit
233	radeon_dpms_set(crtcID, B_DPMS_ON);
234	display_crtc_lock(crtcID, ATOM_DISABLE);
235	encoder_output_lock(false);
236
237	#ifdef TRACE_MODE
238	// for debugging
239	debug_dp_info();
240
241	TRACE("D1CRTC_STATUS        Value: 0x%X\n",
242		Read32(CRT, AVIVO_D1CRTC_STATUS));
243	TRACE("D2CRTC_STATUS        Value: 0x%X\n",
244		Read32(CRT, AVIVO_D2CRTC_STATUS));
245	TRACE("D1CRTC_CONTROL       Value: 0x%X\n",
246		Read32(CRT, AVIVO_D1CRTC_CONTROL));
247	TRACE("D2CRTC_CONTROL       Value: 0x%X\n",
248		Read32(CRT, AVIVO_D2CRTC_CONTROL));
249	TRACE("D1GRPH_ENABLE        Value: 0x%X\n",
250		Read32(CRT, AVIVO_D1GRPH_ENABLE));
251	TRACE("D2GRPH_ENABLE        Value: 0x%X\n",
252		Read32(CRT, AVIVO_D2GRPH_ENABLE));
253	TRACE("D1SCL_ENABLE         Value: 0x%X\n",
254		Read32(CRT, AVIVO_D1SCL_SCALER_ENABLE));
255	TRACE("D2SCL_ENABLE         Value: 0x%X\n",
256		Read32(CRT, AVIVO_D2SCL_SCALER_ENABLE));
257	TRACE("D1CRTC_BLANK_CONTROL Value: 0x%X\n",
258		Read32(CRT, AVIVO_D1CRTC_BLANK_CONTROL));
259	TRACE("D2CRTC_BLANK_CONTROL Value: 0x%X\n",
260		Read32(CRT, AVIVO_D1CRTC_BLANK_CONTROL));
261	#endif
262
263	return B_OK;
264}
265
266
267status_t
268radeon_get_display_mode(display_mode* _currentMode)
269{
270	TRACE("%s\n", __func__);
271
272	*_currentMode = gInfo->shared_info->current_mode;
273	//*_currentMode = gDisplay[X]->currentMode;
274	return B_OK;
275}
276
277
278status_t
279radeon_get_frame_buffer_config(frame_buffer_config* config)
280{
281	TRACE("%s\n", __func__);
282
283	config->frame_buffer = gInfo->shared_info->frame_buffer;
284	config->frame_buffer_dma = (uint8*)gInfo->shared_info->frame_buffer_phys;
285
286	config->bytes_per_row = gInfo->shared_info->bytes_per_row;
287
288	TRACE("  config->frame_buffer: %#" B_PRIxADDR "\n",
289		(phys_addr_t)config->frame_buffer);
290	TRACE("  config->frame_buffer_dma: %#" B_PRIxADDR "\n",
291		(phys_addr_t)config->frame_buffer_dma);
292	TRACE("  config->bytes_per_row: %" B_PRIu32 "\n", config->bytes_per_row);
293
294	return B_OK;
295}
296
297
298status_t
299radeon_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high)
300{
301	TRACE("%s\n", __func__);
302
303	if (_low != NULL) {
304		// lower limit of about 48Hz vertical refresh
305		uint32 totalClocks = (uint32)mode->timing.h_total
306			* (uint32)mode->timing.v_total;
307		uint32 low = (totalClocks * 48L) / 1000L;
308
309		if (low < PLL_MIN_DEFAULT)
310			low = PLL_MIN_DEFAULT;
311		else if (low > PLL_MAX_DEFAULT)
312			return B_ERROR;
313
314		*_low = low;
315	}
316
317	if (_high != NULL)
318		*_high = PLL_MAX_DEFAULT;
319
320	//*_low = 48L;
321	//*_high = 100 * 1000000L;
322	return B_OK;
323}
324
325
326bool
327is_mode_supported(display_mode* mode)
328{
329	bool sane = true;
330
331	// Validate modeline is within a sane range
332	if (is_mode_sane(mode) != B_OK)
333		sane = false;
334
335	// TODO: is_mode_supported on *which* display?
336	uint32 crtid = 0;
337
338	// if we have edid info, check frequency adginst crt reported valid ranges
339	if (gInfo->shared_info->has_edid
340		&& gDisplay[crtid]->foundRanges) {
341
342		// validate horizontal frequency range
343		uint32 hfreq = mode->timing.pixel_clock / mode->timing.h_total;
344		if (hfreq > gDisplay[crtid]->hfreqMax + 1
345			|| hfreq < gDisplay[crtid]->hfreqMin - 1) {
346			//TRACE("!!! mode below falls outside of hfreq range!\n");
347			sane = false;
348		}
349
350		// validate vertical frequency range
351		uint32 vfreq = mode->timing.pixel_clock / ((mode->timing.v_total
352			* mode->timing.h_total) / 1000);
353		if (vfreq > gDisplay[crtid]->vfreqMax + 1
354			|| vfreq < gDisplay[crtid]->vfreqMin - 1) {
355			//TRACE("!!! mode below falls outside of vfreq range!\n");
356			sane = false;
357		}
358	}
359
360	#if 0
361	// Lots of spam, but good for understanding what modelines are in use
362	TRACE("MODE: %d ; %d %d %d %d ; %d %d %d %d is %s\n",
363		mode->timing.pixel_clock, mode->timing.h_display,
364		mode->timing.h_sync_start, mode->timing.h_sync_end,
365		mode->timing.h_total, mode->timing.v_display,
366		mode->timing.v_sync_start, mode->timing.v_sync_end,
367		mode->timing.v_total,
368		sane ? "OK." : "BAD, out of range!");
369	#endif
370
371	return sane;
372}
373
374
375/*
376 * A quick sanity check of the provided display_mode
377 */
378status_t
379is_mode_sane(display_mode* mode)
380{
381	// horizontal timing
382	// validate h_sync_start is less then h_sync_end
383	if (mode->timing.h_sync_start > mode->timing.h_sync_end) {
384		TRACE("%s: ERROR: (%dx%d) "
385			"received h_sync_start greater then h_sync_end!\n",
386			__func__, mode->timing.h_display, mode->timing.v_display);
387		return B_ERROR;
388	}
389	// validate h_total is greater then h_display
390	if (mode->timing.h_total < mode->timing.h_display) {
391		TRACE("%s: ERROR: (%dx%d) "
392			"received h_total greater then h_display!\n",
393			__func__, mode->timing.h_display, mode->timing.v_display);
394		return B_ERROR;
395	}
396
397	// vertical timing
398	// validate v_start is less then v_end
399	if (mode->timing.v_sync_start > mode->timing.v_sync_end) {
400		TRACE("%s: ERROR: (%dx%d) "
401			"received v_sync_start greater then v_sync_end!\n",
402			__func__, mode->timing.h_display, mode->timing.v_display);
403		return B_ERROR;
404	}
405	// validate v_total is greater then v_display
406	if (mode->timing.v_total < mode->timing.v_display) {
407		TRACE("%s: ERROR: (%dx%d) "
408			"received v_total greater then v_display!\n",
409			__func__, mode->timing.h_display, mode->timing.v_display);
410		return B_ERROR;
411	}
412
413	// calculate refresh rate for given timings to whole int (in Hz)
414	int refresh = mode->timing.pixel_clock * 1000
415		/ (mode->timing.h_total * mode->timing.v_total);
416
417	if (refresh < 30 || refresh > 250) {
418		TRACE("%s: ERROR: (%dx%d) "
419			"refresh rate of %dHz is unlikely for any kind of monitor!\n",
420			__func__, mode->timing.h_display, mode->timing.v_display, refresh);
421		return B_ERROR;
422	}
423
424	return B_OK;
425}
426
427
428uint32
429get_mode_bpp(display_mode* mode)
430{
431	// Get bitsPerPixel for given mode
432
433	switch (mode->space) {
434		case B_CMAP8:
435			return 8;
436		case B_RGB15_LITTLE:
437			return 15;
438		case B_RGB16_LITTLE:
439			return 16;
440		case B_RGB24_LITTLE:
441		case B_RGB32_LITTLE:
442			return 32;
443	}
444	ERROR("%s: Unknown colorspace for mode, guessing 32 bits per pixel\n",
445		__func__);
446	return 32;
447}
448
449
450static uint32_t
451radeon_get_backlight_register()
452{
453	// R600 and up is 0x172c else its 0x0018
454	if (gInfo->shared_info->chipsetID >= RADEON_R600)
455		return 0x172c;
456	return 0x0018;
457}
458
459
460status_t
461radeon_set_brightness(float brightness)
462{
463	TRACE("%s (%f)\n", __func__, brightness);
464
465	if (brightness < 0 || brightness > 1)
466		return B_BAD_VALUE;
467
468	uint32_t backlightReg = radeon_get_backlight_register();
469	uint8_t brightnessRaw = (uint8_t)ceilf(brightness * 255);
470	uint32_t level = Read32(OUT, backlightReg);
471	TRACE("brightness level = %lx\n", level);
472	level &= ~ATOM_S2_CURRENT_BL_LEVEL_MASK;
473	level |= (( brightnessRaw << ATOM_S2_CURRENT_BL_LEVEL_SHIFT )
474					& ATOM_S2_CURRENT_BL_LEVEL_MASK);
475	TRACE("new brightness level = %lx\n", level);
476
477	Write32(OUT, backlightReg, level);
478
479	//TODO crtcID = 0: see create_mode
480	// TODO: multi-monitor?  for now we use VESA and not gDisplay edid
481	uint8 crtcID = 0;
482	//TODO : test if it is a LCD ?
483	uint32 connectorIndex = gDisplay[crtcID]->connectorIndex;
484	connector_info* connector = gConnector[connectorIndex];
485	pll_info* pll = &connector->encoder.pll;
486
487	transmitter_dig_setup(connectorIndex, pll->pixelClock,
488		0, 0, ATOM_TRANSMITTER_ACTION_BL_BRIGHTNESS_CONTROL);
489	transmitter_dig_setup(connectorIndex, pll->pixelClock,
490		0, 0, ATOM_TRANSMITTER_ACTION_LCD_BLON);
491
492	return B_OK;
493}
494
495
496status_t
497radeon_get_brightness(float* brightness)
498{
499	TRACE("%s\n", __func__);
500
501	if (brightness == NULL)
502		return B_BAD_VALUE;
503
504	uint32_t backlightReg = Read32(OUT, radeon_get_backlight_register());
505	uint8_t brightnessRaw = ((backlightReg & ATOM_S2_CURRENT_BL_LEVEL_MASK) >>
506			ATOM_S2_CURRENT_BL_LEVEL_SHIFT);
507	*brightness = (float)brightnessRaw / 255;
508
509	return B_OK;
510}
511