xref: /haiku/src/add-ons/accelerants/radeon/SetDisplayMode.c (revision 55b40aa53a835472ec7952b138ae4256203d02e4)
1 /*
2 	Copyright (c) 2002-2004, Thomas Kurschel
3 
4 
5 	Part of Radeon accelerant
6 
7 	Sets display modes, colour palette and handles DPMS
8 */
9 
10 
11 #include "GlobalData.h"
12 #include "generic.h"
13 #include <sys/ioctl.h>
14 #include "radeon_regs.h"
15 #include "mmio.h"
16 #include "crtc_regs.h"
17 #include <GraphicsDefs.h>
18 
19 #include "crtc_regs.h"
20 #include "overlay_regs.h"
21 #include "rbbm_regs.h"
22 #include "dac_regs.h"
23 
24 #include "set_mode.h"
25 
26 #include <string.h>
27 
28 
29 // round virtual width up to next valid size
30 uint32 Radeon_RoundVWidth(
31 	int virtual_width, int bpp )
32 {
33 	// we have to make both the CRTC and the accelerator happy:
34 	// - the CRTC wants virtual width in pixels to be a multiple of 8
35 	// - the accelerator expects width in bytes to be a multiple of 64
36 
37 	// to put that together, width (in bytes) must be a multiple of the least
38 	// common nominator of bytes-per-pixel*8 (CRTC) and 64 (accelerator);
39 
40 	// if bytes-per-pixel is a power of two and less than 8, the LCM is 64;
41 	// almost all colour depth satisfy that apart from 24 bit; in this case,
42 	// the LCM is 64*3=192
43 
44 	// after dividing by bytes-per-pixel we get pixels: in first case,
45 	// width must be multiple of 64/bytes-per-pixel; in second case,
46 	// width must be multiple of 64*3/3=64
47 
48 	if( bpp != 3 )
49 		return (virtual_width + 64/bpp - 1) & ~(64/bpp - 1);
50 	else
51 		return (virtual_width + 63) & ~63;
52 }
53 
54 
55 // list of registers that must be reset before display mode switch
56 // to avoid interferences
57 static struct {
58 	uint16 reg;
59 	uint32 val;
60 } common_regs[] = {
61 	{ RADEON_OVR_CLR, 0 },
62 	{ RADEON_OVR_WID_LEFT_RIGHT, 0 },
63 	{ RADEON_OVR_WID_TOP_BOTTOM, 0 },
64 	{ RADEON_OV0_SCALE_CNTL, 0 },		// disable overlay
65 	{ RADEON_SUBPIC_CNTL, 0 },
66 	{ RADEON_I2C_CNTL_1, 0 },
67 };
68 
69 
70 static void Radeon_InitCommonRegs(
71 	accelerator_info *ai )
72 {
73 	vuint8 *regs = ai->regs;
74 	uint i;
75 
76 	for( i = 0; i < sizeof( common_regs) / sizeof( common_regs[0] ); ++i )
77 		OUTREG( regs, common_regs[i].reg, common_regs[i].val );
78 
79 	// enable extended display modes
80 	OUTREGP( regs, RADEON_CRTC_GEN_CNTL,
81 		RADEON_CRTC_EXT_DISP_EN, ~RADEON_CRTC_EXT_DISP_EN );
82 
83 	// disable flat panel auto-centering
84 	// (if we have a CRT on CRTC1, this must be disabled;
85 	//  if we have a flat panel on CRTC1, we setup CRTC manually, not
86 	//  using the auto-centre, automatic-sync-override magic)
87 	OUTREG( regs, RADEON_CRTC_MORE_CNTL, 0 );
88 }
89 
90 
91 // set display mode of one head;
92 // port restrictions, like fixed-sync TFTs connected to it, are taken care of
93 void Radeon_SetMode(
94 	accelerator_info *ai, crtc_info *crtc, display_mode *mode, impactv_params *tv_params )
95 {
96 	virtual_card *vc = ai->vc;
97 	shared_info *si = ai->si;
98 	vuint8 *regs = ai->regs;
99 	int    format;
100 	int    bpp;
101 	display_device_e disp_devices;
102 	fp_info *fp_info;
103 
104 	crtc_regs		crtc_values;
105 	pll_regs		pll_values;
106 	fp_regs			fp_values;
107 	impactv_regs	impactv_values;
108 	uint32			surface_cntl;
109 
110 	bool internal_tv_encoder;
111 	pll_dividers dividers;
112 
113 	crtc->mode = *mode;
114 
115 	// don't destroy passed values, use our copy instead
116 	mode = &crtc->mode;
117 
118 	disp_devices = crtc->chosen_displays;
119 	fp_info = &si->flatpanels[crtc->flatpanel_port];
120 
121 	// if using an flat panel or LCD, maximum resolution
122 	// is determined by the physical resolution;
123 	// also, all timing is fixed
124 	if( (disp_devices & (dd_lvds | dd_dvi | dd_dvi_ext)) != 0 ) {
125 		if( mode->timing.h_display > fp_info->panel_xres )
126 			mode->timing.h_display = fp_info->panel_xres;
127 		if(	mode->timing.v_display > fp_info->panel_yres )
128 			mode->timing.v_display = fp_info->panel_yres;
129 
130 		mode->timing.h_total = mode->timing.h_display + fp_info->h_blank;
131 		mode->timing.h_sync_start = mode->timing.h_display + fp_info->h_over_plus;
132 		mode->timing.h_sync_end = mode->timing.h_sync_start + fp_info->h_sync_width;
133 		mode->timing.v_total = mode->timing.v_display + fp_info->v_blank;
134 		mode->timing.v_sync_start = mode->timing.v_display + fp_info->v_over_plus;
135 		mode->timing.v_sync_end = mode->timing.v_sync_start + fp_info->v_sync_width;
136 
137 		mode->timing.pixel_clock = fp_info->dot_clock;
138 	}
139 
140 	// TV-out supports at most 1024x768
141 	if( (disp_devices & (dd_ctv | dd_stv)) != 0 ) {
142 		if( mode->timing.h_display > 1024 )
143 			mode->timing.h_display = 1024;
144 
145 		if( mode->timing.v_display > 768 )
146 			mode->timing.v_display = 768;
147 	}
148 
149 	// if using TV-Out, the timing of the source signal must be tweaked to
150 	// get proper timing
151 	internal_tv_encoder = IS_INTERNAL_TV_OUT( si->tv_chip );
152 
153 	// we need higher accuracy then Be thought of
154 	mode->timing.pixel_clock *= 1000;
155 
156 	// TV stuff must be done first as it tweaks the display mode
157 	if( (disp_devices & (dd_ctv | dd_stv)) != 0 ) {
158 		display_mode tweaked_mode;
159 
160 		Radeon_CalcImpacTVParams(
161 			&si->pll, tv_params, vc->tv_standard, internal_tv_encoder,
162 			mode, &tweaked_mode );
163 
164 		*mode = tweaked_mode;
165 	}
166 
167 	Radeon_GetFormat( mode->space, &format, &bpp );
168 
169 	vc->bpp = bpp;
170 	vc->datatype = format;
171 
172 	// time to read original register content
173 	// lock hardware so noone bothers us
174 	Radeon_WaitForIdle( ai, true );
175 
176 	if( (disp_devices & (dd_dvi | dd_lvds | dd_dvi_ext)) != 0 ) {
177 		if( crtc->crtc_idx == 0 )
178 			Radeon_ReadRMXRegisters( ai, &fp_values );
179 
180 		Radeon_ReadFPRegisters( ai, &fp_values );
181 	}
182 
183 	if( (disp_devices & (dd_ctv | dd_stv)) != 0 ) {
184 		// some register's content isn't created from scratch but
185 		// only modified, so we need the original content first
186 		if( internal_tv_encoder )
187 			Radeon_InternalTVOutReadRegisters( ai, &impactv_values );
188 		else
189 			Radeon_TheatreReadTVRegisters( ai, &impactv_values );
190 	}
191 
192 
193 	// calculate all hardware register values
194 	Radeon_CalcCRTCRegisters( ai, crtc, mode, &crtc_values );
195 
196 	surface_cntl = RADEON_SURF_TRANSLATION_DIS;
197 
198 	if( (disp_devices & (dd_ctv | dd_stv)) != 0 ) {
199 		Radeon_CalcImpacTVRegisters( ai, mode, tv_params, &impactv_values,
200 			crtc->crtc_idx, internal_tv_encoder, vc->tv_standard, disp_devices );
201 	}
202 
203 	if( (disp_devices & (dd_stv | dd_ctv)) == 0 )
204 		Radeon_CalcCRTPLLDividers( &si->pll, mode, &dividers );
205 	else
206 		dividers = tv_params->crt_dividers;
207 
208 	if( (disp_devices & dd_lvds) != 0 && si->flatpanels[0].fixed_dividers ) {
209 		SHOW_FLOW0( 0, "Using fixed dividers for laptop panel" );
210 		dividers.feedback = si->flatpanels[0].feedback_div;
211 		dividers.post_code = si->flatpanels[0].post_div;
212 		dividers.ref = si->flatpanels[0].ref_div;
213 	}
214 
215 	Radeon_CalcPLLRegisters( mode, &dividers, &pll_values );
216 
217 	// for first CRTC1, we need to setup RMX properly
218 	if( crtc->crtc_idx == 0 )
219 		Radeon_CalcRMXRegisters( fp_info, mode,
220 			(disp_devices & (dd_lvds | dd_dvi | dd_dvi_ext)) != 0,
221 			&fp_values );
222 
223 	if( (disp_devices & (dd_lvds | dd_dvi | dd_dvi_ext)) != 0 )
224 		Radeon_CalcFPRegisters( ai, crtc, fp_info, &crtc_values, &fp_values );
225 
226 	// we don't use pixel clock anymore, so it can be reset to Be's kHz
227 	mode->timing.pixel_clock /= 1000;
228 
229 	// write values to registers
230 
231 	Radeon_InitCommonRegs( ai );
232 
233 	Radeon_ProgramCRTCRegisters( ai, crtc->crtc_idx, &crtc_values );
234 
235 	OUTREG( regs, RADEON_SURFACE_CNTL, surface_cntl );
236 
237 	if( crtc->crtc_idx == 0 )
238 		Radeon_ProgramRMXRegisters( ai, &fp_values );
239 
240 	if( (disp_devices & (dd_lvds | dd_dvi | dd_dvi_ext)) != 0 )
241 		Radeon_ProgramFPRegisters( ai, crtc, fp_info, &fp_values );
242 
243 	//if( mode->timing.pixel_clock )
244 	Radeon_ProgramPLL( ai, crtc->crtc_idx, &pll_values );
245 
246 	if( (disp_devices & (dd_ctv | dd_stv)) != 0 ) {
247 		if( internal_tv_encoder )
248 			Radeon_InternalTVOutProgramRegisters( ai, &impactv_values );
249 		else
250 			Radeon_TheatreProgramTVRegisters( ai, &impactv_values );
251 	}
252 
253 	crtc->active_displays = disp_devices;
254 
255 	// programming is over, so hardware can be used again
256 	RELEASE_BEN( si->cp.lock );
257 
258 	// overlay must be setup again after modeswitch (whoever was using it)
259 	// TBD: this won't work if another virtual card was using it,
260 	// but currently, virtual cards don't work anyway...
261 	si->active_overlay.crtc_idx = -1;
262 }
263 
264 
265 // enable or disable VBlank interrupts
266 void Radeon_EnableIRQ(
267 	accelerator_info *ai, bool enable )
268 {
269 	shared_info *si = ai->si;
270 	uint32 int_cntl, int_mask;
271 
272 	int_cntl = INREG( ai->regs, RADEON_GEN_INT_CNTL );
273 	int_mask =
274 		RADEON_CRTC_VBLANK_MASK
275 		| (si->num_crtc > 1 ? RADEON_CRTC2_VBLANK_MASK : 0);
276 
277 	if( enable )
278 		int_cntl |= int_mask;
279 	else
280 		int_cntl &= ~int_mask;
281 
282 	OUTREG( ai->regs, RADEON_GEN_INT_CNTL, int_cntl );
283 
284 	if( enable ) {
285 		// on enable, we have to acknowledge all IRQs as the graphics card
286 		// waits for that before it issues further IRQs
287 		OUTREG( ai->regs, RADEON_GEN_INT_STATUS, int_cntl );
288 	}
289 
290 	si->enable_virtual_irq = enable;
291 }
292 
293 
294 // public function: set display mode
295 status_t SET_DISPLAY_MODE(
296 	display_mode *mode_in )
297 {
298 	virtual_card *vc = ai->vc;
299 	shared_info *si = ai->si;
300 	display_mode bounds, mode;
301 
302 	mode = bounds = *mode_in;
303 
304 	ACQUIRE_BEN( si->engine.lock );
305 
306 	SHOW_FLOW( 2, "width=%d, height=%d", mode.timing.h_display, mode.timing.v_display );
307 
308 	// check mode and tweak parameters so we can program hardware
309 	// without any further checks
310 	if( PROPOSE_DISPLAY_MODE( &mode, &bounds, &bounds ) == B_ERROR ) {
311 		SHOW_ERROR0( 2, "invalid mode" );
312 
313 		RELEASE_BEN( si->engine.lock );
314 		return B_ERROR;
315 	}
316 
317 	// already done by propose_display_mode, but it was undone on return;
318 	// do this before equality check to recognize changes of multi-monitor mode
319 	Radeon_DetectMultiMode( vc, &mode );
320 
321 	// mode switches can take quite long and are visible,
322 	// so avoid them if possible
323 	if( memcmp( &mode, &vc->mode, sizeof( display_mode )) == 0 &&
324 		!vc->enforce_mode_change ) {
325 		RELEASE_BEN( si->engine.lock );
326 		return B_OK;
327 	}
328 
329 	// this flag was set when some internal parameter has changed that
330 	// affects effective display mode
331 	vc->enforce_mode_change = false;
332 
333 	// make sure, we don't get disturbed
334 	//Radeon_Finish( ai );
335 	Radeon_EnableIRQ( ai, false );
336 
337 	// free cursor and framebuffer memory
338 	{
339 		radeon_free_mem fm;
340 
341 		fm.magic = RADEON_PRIVATE_DATA_MAGIC;
342 		fm.memory_type = mt_local;
343 		fm.global = true;
344 
345 		if( vc->cursor.mem_handle ) {
346 			fm.handle = vc->cursor.mem_handle;
347 			ioctl( ai->fd, RADEON_FREE_MEM, &fm );
348 		}
349 
350 		if( vc->fb_mem_handle ) {
351 			fm.handle = vc->fb_mem_handle;
352 			ioctl( ai->fd, RADEON_FREE_MEM, &fm );
353 		}
354 	}
355 
356 	memcpy( &vc->mode, &mode, sizeof( display_mode ));
357 
358 	// verify hardware restrictions *after* saving mode
359 	// e.g. if you want a span mode but have one monitor disconnected,
360 	// configuration shouldn't be touched, so you can continue working
361 	// with two monitors later on just like nothing has happened
362 	Radeon_VerifyMultiMode( vc, si, &mode );
363 
364 	// set main flags
365 	vc->independant_heads = vc->assigned_crtc[0] && si->crtc[0].chosen_displays != dd_none;
366 
367 	if( si->num_crtc > 1 )
368 		vc->independant_heads += vc->assigned_crtc[1] && si->crtc[1].chosen_displays != dd_none;
369 
370 	vc->different_heads = Radeon_DifferentPorts( &mode );
371 	SHOW_FLOW( 2, "independant heads: %d, different heads: %d",
372 		vc->independant_heads, vc->different_heads );
373 	vc->scroll = mode.flags & B_SCROLL;
374 	SHOW_FLOW( 2, "scrolling %s", vc->scroll ? "enabled" : "disabled" );
375 
376 	// allocate frame buffer and cursor image memory
377 	{
378 		radeon_alloc_mem am;
379 		int format, bpp;
380 
381 		// alloc cursor memory
382 		am.magic = RADEON_PRIVATE_DATA_MAGIC;
383 		am.size = 1024;
384 		am.memory_type = mt_local;
385 		am.global = true;
386 
387 		if( ioctl( ai->fd, RADEON_ALLOC_MEM, &am ) == B_OK ) {
388 			vc->cursor.mem_handle = am.handle;
389 			vc->cursor.fb_offset = am.offset;
390 		} else {
391 			// too bad that we are out of mem -> set reasonable values as
392 			// it's too late to give up (ouch!)
393 			SHOW_ERROR0( 2, "no memory for cursor image!" );
394 			vc->cursor.mem_handle = 0;
395 			vc->cursor.fb_offset = 0;
396 		}
397 
398 		vc->cursor.data = si->local_mem + vc->cursor.fb_offset;
399 
400 		// alloc frame buffer
401 		Radeon_GetFormat( mode.space, &format, &bpp );
402 		vc->pitch = Radeon_RoundVWidth( mode.virtual_width, bpp ) * bpp;
403 		am.size = vc->pitch * mode.virtual_height;
404 
405 		if( ioctl( ai->fd, RADEON_ALLOC_MEM, &am ) == B_OK ) {
406 			vc->fb_mem_handle = am.handle;
407 			vc->fb_offset = am.offset;
408 		} else 	{
409 			// ouch again - set reasonable values
410 			SHOW_ERROR0( 2, "no memory for frame buffer!" );
411 			vc->fb_mem_handle = 0;
412 			vc->fb_offset = 1024;
413 		}
414 
415 		vc->fbc.frame_buffer = si->local_mem + vc->fb_offset;
416 		vc->fbc.frame_buffer_dma = (void *)((uint8 *)si->framebuffer_pci + vc->fb_offset);
417 		vc->fbc.bytes_per_row = vc->pitch;
418 
419 		SHOW_FLOW( 0, "frame buffer CPU-address=%x, phys-address=%x",
420 			vc->fbc.frame_buffer, vc->fbc.frame_buffer_dma );
421 	}
422 
423 	// multi-screen stuff
424 	Radeon_InitMultiModeVars( ai, &mode );
425 
426 	// GO!
427 
428 	{
429 		routing_regs routing_values;
430 		impactv_params tv_params;
431 
432 		// we first switch off all output, so the monitor(s) won't get invalid signals
433 		if( vc->assigned_crtc[0] ) {
434 			// overwrite list of active displays to switch off displays
435 			// someone else turned on
436 			si->crtc[0].active_displays = vc->controlled_displays;
437 			Radeon_SetDPMS( ai, 0, B_DPMS_SUSPEND );
438 		}
439 		if( vc->assigned_crtc[1] ) {
440 			si->crtc[1].active_displays = vc->controlled_displays;
441 			Radeon_SetDPMS( ai, 1, B_DPMS_SUSPEND );
442 		}
443 
444 		// mark crtc that will be used from now on
445 		vc->used_crtc[0] = vc->assigned_crtc[0] && si->crtc[0].chosen_displays != dd_none;
446 		vc->used_crtc[1] = vc->assigned_crtc[1] && si->crtc[1].chosen_displays != dd_none;
447 
448 		// then change the mode
449 		if( vc->used_crtc[0] )
450 			Radeon_SetMode( ai, &si->crtc[0], &mode, &tv_params );
451 		if( vc->used_crtc[1] )
452 			Radeon_SetMode( ai, &si->crtc[1], &mode, &tv_params );
453 
454 		// setup signal routing
455 		Radeon_ReadMonitorRoutingRegs( ai, &routing_values );
456 		Radeon_CalcMonitorRouting( ai, &tv_params, &routing_values );
457 		Radeon_ProgramMonitorRouting( ai, &routing_values );
458 
459 		// finally, switch display(s) on
460 		if( vc->used_crtc[0] )
461 			Radeon_SetDPMS( ai, 0, B_DPMS_ON );
462 		if( vc->used_crtc[1] )
463 			Radeon_SetDPMS( ai, 1, B_DPMS_ON );
464 
465 		OUTREGP( ai->regs, RADEON_CRTC_EXT_CNTL, 0, ~RADEON_CRTC_DISPLAY_DIS );
466 	}
467 
468    	SHOW_FLOW( 3, "pitch=%ld", vc->pitch );
469 
470    	// we'll modify bits of this reg, so save it for async access
471 	si->dac_cntl2 = INREG( ai->regs, RADEON_DAC_CNTL2 );
472 
473 	// setup 2D registers
474 	Radeon_Init2D( ai );
475 	// setup position of framebuffer for 2D commands
476 	Radeon_FillStateBuffer( ai, vc->datatype );
477 
478 	// remember that 2D accelerator is not prepared for any virtual card
479 	si->active_vc = -1;
480 
481 	// first move to well-defined position (to setup CRTC offset)
482 	Radeon_MoveDisplay( ai, 0, 0 );
483 	// then to (probably faulty) user-defined pos
484 	Radeon_MoveDisplay( ai, mode.h_display_start, mode.v_display_start );
485 
486 	// set standard palette in direct-colour modes
487 	if( vc->used_crtc[0] )
488 		Radeon_InitPalette( ai, 0 );
489 	if( vc->used_crtc[1] )
490 		Radeon_InitPalette( ai, 1 );
491 
492 	// initialize cursor data
493 	if( vc->used_crtc[0] )
494 		Radeon_SetCursorColors( ai, 0 );
495 	if( vc->used_crtc[1] )
496 		Radeon_SetCursorColors( ai, 1 );
497 
498 	// sync should be settled now, so we can reenable IRQs
499 	Radeon_EnableIRQ( ai, true );
500 
501 	RELEASE_BEN( si->engine.lock );
502 
503 	// !! all this must be done after lock has been
504 	//    released to avoid dead-lock !!
505 	// TBD: any invalid intermediate states?
506 
507 	// move_cursor sets all cursor-related variables and registers
508 	vc->cursor.is_visible = false;
509 	MOVE_CURSOR( 0, 0 );
510 
511 	return B_OK;
512 }
513