xref: /haiku/src/add-ons/accelerants/radeon/SetDisplayMode.c (revision 67bce78b48ed6d01b5a8eef89f5694c372b7e0a1)
1 /*
2 	Copyright (c) 2002, 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 "overlay_regs.h"
20 #include "capture_regs.h"
21 #include "rbbm_regs.h"
22 #include "dac_regs.h"
23 
24 #include <string.h>
25 
26 void Radeon_SetMode( accelerator_info *ai, virtual_port *port, display_mode *mode );
27 void Radeon_EnableIRQ( accelerator_info *ai, bool enable );
28 
29 
30 // Radeon's DACs share same public registers, this function
31 // selects the DAC you'll talk to
32 static void selectDAC( accelerator_info *ai, virtual_port *port )
33 {
34 	Radeon_WriteRegCP( ai, RADEON_DAC_CNTL2,
35 		(port->is_crtc2 ? RADEON_DAC2_PALETTE_ACC_CTL : 0) |
36 		(ai->si->dac_cntl2 & ~RADEON_DAC2_PALETTE_ACC_CTL) );
37 }
38 
39 
40 // set standard colour palette (needed for non-palette modes)
41 static void initDAC( accelerator_info *ai, virtual_port *port )
42 {
43 	int i;
44 
45 	selectDAC( ai, port );
46 
47 	Radeon_WriteRegCP( ai, RADEON_PALETTE_INDEX, 0 );
48 
49 	for( i = 0; i < 256; ++i )
50 		Radeon_WriteRegCP( ai, RADEON_PALETTE_DATA, (i << 16) | (i << 8) | i );
51 }
52 
53 
54 
55 // round virtual width up to next valid size
56 uint32 Radeon_RoundVWidth( int virtual_width, int bpp )
57 {
58 	// we have to make both the CRTC and the accelerator happy:
59 	// - the CRTC wants virtual width in pixels to be a multiple of 8
60 	// - the accelerator expects width in bytes to be a multiple of 64
61 
62 	// to put that together, width (in bytes) must be a multiple of the least
63 	// common nominator of bytes-per-pixel*8 (CRTC) and 64 (accelerator);
64 
65 	// if bytes-per-pixel is a power of two and less than 8, the LCM is 64;
66 	// almost all colour depth satisfy that apart from 24 bit; in this case,
67 	// the LCM is 64*3=192
68 
69 	// after dividing by bytes-per-pixel we get pixels: in first case,
70 	// width must be multiple of 64/bytes-per-pixel; in second case,
71 	// width must be multiple of 64*3/3=64
72 
73 	if( bpp != 3 )
74 		return (virtual_width + 64/bpp - 1) & ~(64/bpp - 1);
75 	else
76 		return (virtual_width + 63) & ~63;
77 }
78 
79 
80 // list of registers that must be reset before display mode switch
81 // to avoid interferences
82 static struct {
83 	uint16 reg;
84 	uint32 val;
85 } common_regs[] = {
86 	{ RADEON_OVR_CLR, 0 },
87 	{ RADEON_OVR_WID_LEFT_RIGHT, 0 },
88 	{ RADEON_OVR_WID_TOP_BOTTOM, 0 },
89 	{ RADEON_OV0_SCALE_CNTL, 0 },
90 	{ RADEON_SUBPIC_CNTL, 0 },
91 	{ RADEON_VIPH_CONTROL, 0 },
92 	{ RADEON_I2C_CNTL_1, 0 },
93 	{ RADEON_GEN_INT_CNTL, 0 },
94 	{ RADEON_CAP0_TRIG_CNTL, 0 },
95 };
96 
97 static void Radeon_InitCommonRegs( accelerator_info *ai )
98 {
99 	vuint8 *regs = ai->regs;
100 	uint i;
101 
102 	for( i = 0; i < sizeof( common_regs) / sizeof( common_regs[0] ); ++i )
103 		OUTREG( regs, common_regs[i].reg, common_regs[i].val );
104 }
105 
106 // set display mode of one port;
107 // port restrictions, like fixed-sync TFTs connected to it, are taken care of
108 void Radeon_SetMode( accelerator_info *ai, virtual_port *port, display_mode *mode )
109 {
110 	virtual_card *vc = ai->vc;
111 	shared_info *si = ai->si;
112 	vuint8 *regs = ai->regs;
113 	int    format;
114 	int    bpp;
115 	display_type_e disp_type;
116 	port_regs values;
117 
118 	port->mode = *mode;
119 
120 	// don't destroy passed values, use our copy instead
121 	mode = &port->mode;
122 
123 	disp_type = si->ports[port->physical_port].disp_type;
124 
125 	// if using an flat panel or LCD, maximum resolution
126 	// is determined by the physical resolution;
127 	// also, all timing is fixed
128 	if( disp_type == dt_dvi_1 || disp_type == dt_lvds ) {
129 		fp_info *fp_info = &si->fp_port;
130 
131 		if( mode->timing.h_display > fp_info->panel_xres )
132 		mode->timing.h_display = fp_info->panel_xres;
133 		if(	mode->timing.v_display > fp_info->panel_yres )
134 		mode->timing.v_display = fp_info->panel_yres;
135 
136 		mode->timing.h_total = mode->timing.h_display + fp_info->h_blank;
137 		mode->timing.h_sync_start = mode->timing.h_display + fp_info->h_over_plus;
138 		mode->timing.h_sync_end = mode->timing.h_sync_start + fp_info->h_sync_width;
139 		mode->timing.v_total = mode->timing.v_display + fp_info->v_blank;
140 		mode->timing.v_sync_start = mode->timing.v_display + fp_info->v_over_plus;
141 		mode->timing.v_sync_end = mode->timing.v_sync_start + fp_info->v_sync_width;
142 
143 		mode->timing.pixel_clock = fp_info->dot_clock;
144 	}
145 
146 	Radeon_GetFormat( mode->space, &format, &bpp );
147 
148 	vc->bpp = bpp;
149 	vc->datatype = format;
150 
151 	// calculate all hardware register values
152 	Radeon_CalcCRTCRegisters( ai, port, mode, &values );
153 
154 	values.surface_cntl = RADEON_SURF_TRANSLATION_DIS;
155 
156 	// for flat panels, we may not have pixel clock if DDC data is missing;
157 	// as we don't change effective resolution we can leave it as set by BIOS
158 	if( mode->timing.pixel_clock )
159 		Radeon_CalcPLLDividers( &si->pll, mode->timing.pixel_clock / 10, &values );
160 
161 	if( disp_type == dt_dvi_1 || disp_type == dt_lvds )
162 		Radeon_CalcFPRegisters( ai, port, &si->fp_port, mode, &values );
163 
164 
165 	// write values to registers
166 	Radeon_SetDPMS( ai, port, B_DPMS_SUSPEND );
167 
168 	Radeon_InitCommonRegs( ai );
169 
170 	Radeon_ProgramCRTCRegisters( ai, port, &values );
171 
172 	OUTREG( regs, RADEON_SURFACE_CNTL, values.surface_cntl );
173 
174 	if( disp_type == dt_dvi_1 || disp_type == dt_lvds )
175 		Radeon_ProgramFPRegisters( ai, &si->fp_port, &values );
176 
177 
178 	if( mode->timing.pixel_clock )
179 		Radeon_ProgramPLL( ai, port, &values );
180 
181 	Radeon_SetDPMS( ai, port, B_DPMS_ON );
182 
183 	// overlay must be setup again after modeswitch (whoever was using it)
184 	// TBD: this won't work if another virtual card was using it,
185 	// but currently, virtual cards don't work anyway...
186 	si->active_overlay.port = -1;
187 }
188 
189 
190 // enable or disable VBlank interrupts
191 void Radeon_EnableIRQ( accelerator_info *ai, bool enable )
192 {
193 	shared_info *si = ai->si;
194 	uint32 int_cntl, int_mask;
195 
196 	int_cntl = INREG( ai->regs, RADEON_GEN_INT_CNTL );
197 	int_mask =
198 		RADEON_CRTC_VBLANK_MASK
199 		| (si->has_crtc2 ? RADEON_CRTC2_VBLANK_MASK : 0);
200 
201 	if( enable )
202 		int_cntl |= int_mask;
203 	else
204 		int_cntl &= ~int_mask;
205 
206 	OUTREG( ai->regs, RADEON_GEN_INT_CNTL, int_cntl );
207 
208 	if( enable ) {
209 		// on enable, we have to acknowledge all IRQs as the graphics card
210 		// waits for that before it issues further IRQs
211 		OUTREG( ai->regs, RADEON_GEN_INT_STATUS, int_cntl );
212 	}
213 
214 	si->enable_virtual_irq = enable;
215 }
216 
217 
218 // public function: set display mode
219 status_t SET_DISPLAY_MODE( display_mode *mode_in )
220 {
221 	virtual_card *vc = ai->vc;
222 	shared_info *si = ai->si;
223 	display_mode bounds, mode;
224 
225 	mode = bounds = *mode_in;
226 
227 	ACQUIRE_BEN( si->engine.lock );
228 
229 	SHOW_FLOW( 2, "width=%d, height=%d", mode.timing.h_display, mode.timing.v_display );
230 
231 	// check mode and tweak parameters so we can program hardware
232 	// without any further checks
233 	if( PROPOSE_DISPLAY_MODE( &mode, &bounds, &bounds ) == B_ERROR ) {
234 		SHOW_ERROR0( 2, "invalid mode" );
235 
236 		RELEASE_BEN( si->engine.lock );
237 		return B_ERROR;
238 	}
239 
240 	// already done by propose_display_mode, but it was undone on return;
241 	// do this before equality check to recognize changed to multi-monitor mode
242 	Radeon_DetectMultiMode( vc, &mode );
243 
244 	// mode switches can take quite long and are visible,
245 	// so avoid them if possible
246 	if( memcmp( &mode, &vc->mode, sizeof( display_mode )) == 0 ) {
247 		RELEASE_BEN( si->engine.lock );
248 		return B_OK;
249 	}
250 
251 	// make sure, we don't get disturbed
252 	Radeon_Finish( ai );
253 	Radeon_EnableIRQ( ai, false );
254 
255 	// free cursor and framebuffer memory
256 	{
257 		radeon_free_local_mem fm;
258 
259 		fm.magic = RADEON_PRIVATE_DATA_MAGIC;
260 
261 		if( vc->cursor.mem_handle ) {
262 			fm.handle = vc->cursor.mem_handle;
263 			ioctl( ai->fd, RADEON_FREE_LOCAL_MEM, &fm );
264 		}
265 
266 		if( vc->fb_mem_handle ) {
267 			fm.handle = vc->fb_mem_handle;
268 			ioctl( ai->fd, RADEON_FREE_LOCAL_MEM, &fm );
269 		}
270 	}
271 
272 	memcpy( &vc->mode, &mode, sizeof( display_mode ));
273 
274 	// verify hardware restrictions *after* saving mode
275 	// e.g. if you want a span mode but have one monitor disconnected,
276 	// configuration shouldn't be touched, so you can continue working
277 	// with two monitors later on just like nothing has happened
278 	Radeon_VerifyMultiMode( vc, si, &mode );
279 
280 	// set main flags
281 	vc->independant_ports = Radeon_NeedsSecondPort( &mode ) ? 2 : 1;
282 	vc->different_ports = Radeon_DifferentPorts( &mode );
283 	SHOW_FLOW( 2, "independant ports: %d", vc->independant_ports );
284 	vc->scroll = mode.flags & B_SCROLL;
285 	SHOW_FLOW( 2, "scrolling %s", vc->scroll ? "enabled" : "disabled" );
286 
287 	// allocate frame buffer and cursor image memory
288 	{
289 		radeon_alloc_local_mem am;
290 		int format, bpp;
291 
292 		// alloc cursor memory
293 		am.magic = RADEON_PRIVATE_DATA_MAGIC;
294 		am.size = 1024;
295 
296 		if( ioctl( ai->fd, RADEON_ALLOC_LOCAL_MEM, &am ) == B_OK ) {
297 			vc->cursor.mem_handle = am.handle;
298 			vc->cursor.fb_offset = am.fb_offset;
299 		} else {
300 			// too bad that we are out of mem -> set reasonable values as
301 			// it's too late to give up (ouch!)
302 			SHOW_ERROR0( 2, "no memory for cursor image!" );
303 			vc->cursor.mem_handle = 0;
304 			vc->cursor.fb_offset = 0;
305 		}
306 
307 		vc->cursor.data = si->framebuffer + vc->cursor.fb_offset;
308 
309 		// alloc frame buffer
310 		Radeon_GetFormat( mode.space, &format, &bpp );
311 		vc->pitch = Radeon_RoundVWidth( mode.virtual_width, bpp ) * bpp;
312 		am.size = vc->pitch * mode.virtual_height;
313 
314 		if( ioctl( ai->fd, RADEON_ALLOC_LOCAL_MEM, &am ) == B_OK ) {
315 			vc->fb_mem_handle = am.handle;
316 			vc->fb_offset = am.fb_offset;
317 		} else 	{
318 			// ouch again - set reasonable values
319 			SHOW_ERROR0( 2, "no memory for frame buffer!" );
320 			vc->fb_mem_handle = 0;
321 			vc->fb_offset = 1024;
322 		}
323 
324 		vc->fbc.frame_buffer = si->framebuffer + vc->fb_offset;
325 		vc->fbc.frame_buffer_dma = (void *)((uint8 *)si->framebuffer_pci + vc->fb_offset);
326 		vc->fbc.bytes_per_row = vc->pitch;
327 	}
328 
329 	// multi-screen stuff
330 	Radeon_InitMultiModeVars( vc, &mode );
331 
332 	// GO!
333 	Radeon_SetMode( ai, &vc->ports[0], &mode );
334 
335 	if( vc->independant_ports > 1 )
336 		Radeon_SetMode( ai, &vc->ports[1], &mode );
337 
338    	SHOW_FLOW( 3, "pitch=%ld", vc->pitch );
339 
340    	// we'll modify bits of this reg, so save it for async access
341 	si->dac_cntl2 = INREG( ai->regs, RADEON_DAC_CNTL2 );
342 
343 	// init accelerator
344 	Radeon_Init2D( ai, vc->datatype );
345 
346 	// remember that 2D accelerator is not prepared for any virtual card
347 	si->active_vc = -1;
348 
349 	Radeon_ActivateVirtualCard( ai );
350 
351 	// first move to well-defined position (to setup CRTC offset)
352 	Radeon_MoveDisplay( ai, 0, 0 );
353 	// then to (probably faulty) user-defined pos
354 	Radeon_MoveDisplay( ai, mode.h_display_start, mode.v_display_start );
355 
356 	// set standard palette in direct-colour modes
357 	initDAC( ai, &vc->ports[0] );
358 	if( vc->independant_ports > 1 )
359 		initDAC( ai, &vc->ports[1] );
360 
361 	// initialize cursor data
362 	Radeon_SetCursorColors( ai, &vc->ports[0] );
363 	if( vc->independant_ports > 1 )
364 		Radeon_SetCursorColors( ai, &vc->ports[1] );
365 
366 	// sync should be settled now, so we can reenable IRQs
367 	// TBD: IRQ handling doesn't work correctly and doesn't make sense with two
368 	// displays connected, so let's leave them disabled for now
369 	//Radeon_EnableIRQ( ai, true );
370 
371 	RELEASE_BEN( si->engine.lock );
372 
373 	// !! all this must be done after lock has been
374 	//    released to avoid dead-lock !!
375 	// TBD: any invalid intermediate states?
376 
377 	// move_cursor sets all cursor-related variables and registers
378 	vc->cursor.is_visible = false;
379 	MOVE_CURSOR( 0, 0 );
380 
381 	return B_OK;
382 }
383 
384 // update shown are of one port
385 static void moveOneDisplay( accelerator_info *ai, virtual_port *port )
386 {
387 	virtual_card *vc = ai->vc;
388 	uint32 offset;
389 
390 	offset = (vc->mode.v_display_start + port->rel_y) * vc->pitch +
391 		(vc->mode.h_display_start + port->rel_x) * vc->bpp +
392 		vc->fb_offset;
393 
394 	SHOW_FLOW( 3, "Setting address %x on port %d",
395 		offset,	port->is_crtc2 );
396 
397 	Radeon_WaitForFifo( ai, 1 );
398 
399 	OUTREG( ai->regs, port->is_crtc2 ? RADEON_CRTC2_OFFSET : RADEON_CRTC_OFFSET, offset );
400 /*	Radeon_WriteRegCP( ai, port->is_crtc2 ? RADEON_CRTC2_OFFSET : RADEON_CRTC_OFFSET,
401 		offset );*/
402 }
403 
404 status_t Radeon_MoveDisplay( accelerator_info *ai, uint16 h_display_start, uint16 v_display_start )
405 {
406 	virtual_card *vc = ai->vc;
407 
408 	SHOW_FLOW( 4, "h_display_start=%ld, v_display_start=%ld",
409 		h_display_start, v_display_start );
410 
411 	if( h_display_start + vc->eff_width > vc->mode.virtual_width ||
412 		v_display_start + vc->eff_height > vc->mode.virtual_height )
413 		return B_ERROR;
414 
415 	// this is needed both for get_mode_info and for scrolling of virtual screens
416 	vc->mode.h_display_start = h_display_start & ~7;
417 	vc->mode.v_display_start = v_display_start;
418 
419 	// do it
420 	moveOneDisplay( ai, &vc->ports[0] );
421 
422 	if( vc->independant_ports > 1 )
423 		moveOneDisplay( ai, &vc->ports[1] );
424 
425 	// overlay position must be adjusted
426 	Radeon_UpdateOverlay( ai );
427 
428 	return B_OK;
429 }
430 
431 // public function: pan display
432 status_t MOVE_DISPLAY( uint16 h_display_start, uint16 v_display_start )
433 {
434 	shared_info *si = ai->si;
435 	status_t result;
436 
437 	ACQUIRE_BEN( si->engine.lock );
438 
439 	// TBD: we should probably lock card first; in this case, we must
440 	//      split this function into locking and worker part, as this
441 	//      function is used internally as well
442 	result = Radeon_MoveDisplay( ai, h_display_start, v_display_start );
443 
444 	RELEASE_BEN( si->engine.lock );
445 
446 	return result;
447 }
448 
449 static void setPalette( accelerator_info *ai, virtual_port *port,
450 	uint count, uint8 first, uint8 *color_data );
451 
452 // public function: set colour palette
453 void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags)
454 {
455 	virtual_card *vc = ai->vc;
456 	shared_info *si = ai->si;
457 //	uint i;
458 
459 	SHOW_FLOW( 3, "first=%d, count=%d", first, flags );
460 
461 	if( vc->mode.space != B_CMAP8 ) {
462 		SHOW_ERROR0( 2, "Tried to set palette in non-palette mode" );
463 		return;
464 	}
465 
466 	// we need to lock card, though this isn't done	in sample driver
467 	ACQUIRE_BEN( si->engine.lock );
468 
469 	setPalette( ai, &vc->ports[0], count, first, color_data );
470 
471 	if( vc->independant_ports > 1 )
472 		setPalette( ai, &vc->ports[1], count, first, color_data );
473 
474 	RELEASE_BEN( si->engine.lock );
475 }
476 
477 
478 // set palette of one DAC
479 static void setPalette( accelerator_info *ai, virtual_port *port,
480 	uint count, uint8 first, uint8 *color_data )
481 {
482 	uint i;
483 
484 	selectDAC( ai, port );
485 
486 	Radeon_WriteRegCP( ai, RADEON_PALETTE_INDEX, first );
487 
488 	for( i = 0; i < count; ++i, color_data += 3 )
489 		Radeon_WriteRegCP( ai, RADEON_PALETTE_DATA,
490 			((uint32)color_data[0] << 16) |
491 			((uint32)color_data[1] << 8) |
492 			 color_data[2] );
493 }
494