BTRFS: Implement BTree::Path and change _Find.
[haiku.git] / src / add-ons / accelerants / radeon / SetDisplayMode.c
blobbd7e40e47565ebbb5e370b45553c37cd6b71a077
1 /*
2 Copyright (c) 2002-2004, Thomas Kurschel
5 Part of Radeon accelerant
7 Sets display modes, colour palette and handles DPMS
8 */
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>
19 #include "crtc_regs.h"
20 #include "overlay_regs.h"
21 #include "rbbm_regs.h"
22 #include "dac_regs.h"
23 #include "fp_regs.h"
24 #include "gpiopad_regs.h"
26 #include "pll_regs.h"
27 #include "tv_out_regs.h"
28 #include "config_regs.h"
29 #include "ddc_regs.h"
30 #include "pll_access.h"
31 #include "theatre_regs.h"
32 #include "set_mode.h"
33 #include "ddc.h"
35 #include "set_mode.h"
37 #include <string.h>
39 status_t Radeon_SetMode(
40 accelerator_info *ai, crtc_info *crtc, display_mode *mode, impactv_params *tv_params );
42 // round virtual width up to next valid size
43 uint32 Radeon_RoundVWidth(
44 int virtual_width, int bpp )
46 // we have to make both the CRTC and the accelerator happy:
47 // - the CRTC wants virtual width in pixels to be a multiple of 8
48 // - the accelerator expects width in bytes to be a multiple of 64
50 // to put that together, width (in bytes) must be a multiple of the least
51 // common nominator of bytes-per-pixel*8 (CRTC) and 64 (accelerator);
53 // if bytes-per-pixel is a power of two and less than 8, the LCM is 64;
54 // almost all colour depth satisfy that apart from 24 bit; in this case,
55 // the LCM is 64*3=192
57 // after dividing by bytes-per-pixel we get pixels: in first case,
58 // width must be multiple of 64/bytes-per-pixel; in second case,
59 // width must be multiple of 64*3/3=64
61 if( bpp != 3 )
62 return (virtual_width + 64/bpp - 1) & ~(64/bpp - 1);
63 else
64 return (virtual_width + 63) & ~63;
68 // list of registers that must be reset before display mode switch
69 // to avoid interferences
70 static struct {
71 uint16 reg;
72 uint32 val;
73 } common_regs[] = {
74 { RADEON_OVR_CLR, 0 },
75 { RADEON_OVR_WID_LEFT_RIGHT, 0 },
76 { RADEON_OVR_WID_TOP_BOTTOM, 0 },
77 { RADEON_OV0_SCALE_CNTL, 0 }, // disable overlay
78 { RADEON_SUBPIC_CNTL, 0 },
79 { RADEON_I2C_CNTL_1, 0 },
83 static void Radeon_InitCommonRegs(
84 accelerator_info *ai )
86 vuint8 *regs = ai->regs;
87 uint i;
89 for( i = 0; i < sizeof( common_regs) / sizeof( common_regs[0] ); ++i )
90 OUTREG( regs, common_regs[i].reg, common_regs[i].val );
92 // enable extended display modes
93 OUTREGP( regs, RADEON_CRTC_GEN_CNTL,
94 RADEON_CRTC_EXT_DISP_EN, ~RADEON_CRTC_EXT_DISP_EN );
96 // disable flat panel auto-centering
97 // (if we have a CRT on CRTC1, this must be disabled;
98 // if we have a flat panel on CRTC1, we setup CRTC manually, not
99 // using the auto-centre, automatic-sync-override magic)
100 OUTREG( regs, RADEON_CRTC_MORE_CNTL, 0 );
104 // set display mode of one head;
105 // port restrictions, like fixed-sync TFTs connected to it, are taken care of
106 status_t Radeon_SetMode(
107 accelerator_info *ai, crtc_info *crtc, display_mode *mode, impactv_params *tv_params )
109 virtual_card *vc = ai->vc;
110 shared_info *si = ai->si;
111 vuint8 *regs = ai->regs;
112 int format;
113 int bpp;
114 display_device_e disp_devices;
115 fp_info *fp_info;
117 crtc_regs crtc_values;
118 pll_regs pll_values;
119 fp_regs fp_values;
120 impactv_regs impactv_values;
121 uint32 surface_cntl;
123 bool internal_tv_encoder;
124 pll_dividers dividers;
126 crtc->mode = *mode;
128 // don't destroy passed values, use our copy instead
129 mode = &crtc->mode;
131 disp_devices = crtc->chosen_displays;
132 fp_info = &si->flatpanels[crtc->flatpanel_port];
134 // if using an flat panel or LCD, maximum resolution
135 // is determined by the physical resolution;
136 // also, all timing is fixed
137 if( (disp_devices & (dd_lvds | dd_dvi | dd_dvi_ext)) != 0 ) {
138 SHOW_FLOW0( 0, "requested resolution higher than native panel" );
139 if( mode->timing.h_display > fp_info->panel_xres )
140 mode->timing.h_display = fp_info->panel_xres;
141 if( mode->timing.v_display > fp_info->panel_yres )
142 mode->timing.v_display = fp_info->panel_yres;
144 if( (disp_devices & dd_dvi_ext) != 0 ) {
145 SHOW_FLOW0( 0, "requested resolution less than second native panel" );
146 if( mode->timing.h_display < fp_info->panel_xres )
147 mode->timing.h_display = fp_info->panel_xres;
148 if( mode->timing.v_display < fp_info->panel_yres )
149 mode->timing.v_display = fp_info->panel_yres;
151 //TODO at this point we know we are going to do centered timing
152 //need to set flags to a. blank the unused memory, b.center screen
153 //for now it's in the top corner, and surrounded by garbage.
154 // although if the DVI panels are the same size and we are cloning
155 // we can switch the FP2 source to RMX, and drive both screens from
156 // the RMX unit.
158 mode->timing.h_total = mode->timing.h_display + fp_info->h_blank;
159 mode->timing.h_sync_start = mode->timing.h_display + fp_info->h_over_plus;
160 mode->timing.h_sync_end = mode->timing.h_sync_start + fp_info->h_sync_width;
161 mode->timing.v_total = mode->timing.v_display + fp_info->v_blank;
162 mode->timing.v_sync_start = mode->timing.v_display + fp_info->v_over_plus;
163 mode->timing.v_sync_end = mode->timing.v_sync_start + fp_info->v_sync_width;
165 mode->timing.pixel_clock = fp_info->dot_clock;
168 // TV-out supports at most 1024x768
169 if( (disp_devices & (dd_ctv | dd_stv)) != 0 ) {
170 if( mode->timing.h_display > 1024 )
171 mode->timing.h_display = 1024;
173 if( mode->timing.v_display > 768 )
174 mode->timing.v_display = 768;
177 // if using TV-Out, the timing of the source signal must be tweaked to
178 // get proper timing
179 internal_tv_encoder = IS_INTERNAL_TV_OUT( si->tv_chip );
181 // we need higher accuracy then Be thought of
182 mode->timing.pixel_clock *= 1000;
184 // TV stuff must be done first as it tweaks the display mode
185 if( (disp_devices & (dd_ctv | dd_stv)) != 0 ) {
186 display_mode tweaked_mode;
188 Radeon_CalcImpacTVParams(
189 &si->pll, tv_params, vc->tv_standard, internal_tv_encoder,
190 mode, &tweaked_mode );
192 *mode = tweaked_mode;
195 Radeon_GetFormat( mode->space, &format, &bpp );
197 vc->bpp = bpp;
198 vc->datatype = format;
200 // time to read original register content
201 // lock hardware so noone bothers us
202 Radeon_WaitForIdle( ai, true );
204 if( (disp_devices & (dd_dvi | dd_lvds | dd_dvi_ext)) != 0 ) {
205 if( crtc->crtc_idx == 0 )
206 Radeon_ReadRMXRegisters( ai, &fp_values );
208 Radeon_ReadFPRegisters( ai, &fp_values );
211 if( (disp_devices & (dd_ctv | dd_stv)) != 0 ) {
212 // some register's content isn't created from scratch but
213 // only modified, so we need the original content first
214 if( internal_tv_encoder )
215 Radeon_InternalTVOutReadRegisters( ai, &impactv_values );
216 else
217 Radeon_TheatreReadTVRegisters( ai, &impactv_values );
221 // calculate all hardware register values
222 Radeon_CalcCRTCRegisters( ai, crtc, mode, &crtc_values );
224 surface_cntl = RADEON_SURF_TRANSLATION_DIS;
226 if( (disp_devices & (dd_ctv | dd_stv)) != 0 ) {
227 Radeon_CalcImpacTVRegisters( ai, mode, tv_params, &impactv_values,
228 crtc->crtc_idx, internal_tv_encoder, vc->tv_standard, disp_devices );
231 if( (disp_devices & (dd_stv | dd_ctv)) == 0 )
232 Radeon_CalcCRTPLLDividers( &si->pll, mode, &dividers );
233 else
234 dividers = tv_params->crt_dividers;
236 if( (disp_devices & dd_lvds) != 0 && si->flatpanels[0].fixed_dividers ) {
237 SHOW_FLOW0( 0, "Using fixed dividers for laptop panel" );
238 dividers.feedback = si->flatpanels[0].feedback_div;
239 dividers.post_code = si->flatpanels[0].post_div;
240 dividers.ref = si->flatpanels[0].ref_div;
243 Radeon_CalcPLLRegisters( mode, &dividers, &pll_values );
245 // for first CRTC1, we need to setup RMX properly
246 if( crtc->crtc_idx == 0 )
247 Radeon_CalcRMXRegisters( fp_info, mode,
248 (disp_devices & (dd_lvds | dd_dvi | dd_dvi_ext)) != 0,
249 &fp_values );
251 if( (disp_devices & (dd_lvds | dd_dvi | dd_dvi_ext)) != 0 )
252 Radeon_CalcFPRegisters( ai, crtc, fp_info, &crtc_values, &fp_values );
254 // we don't use pixel clock anymore, so it can be reset to Be's kHz
255 mode->timing.pixel_clock /= 1000;
257 // write values to registers
259 Radeon_InitCommonRegs( ai );
261 Radeon_ProgramCRTCRegisters( ai, crtc->crtc_idx, &crtc_values );
263 OUTREG( regs, RADEON_SURFACE_CNTL, surface_cntl );
265 if( crtc->crtc_idx == 0 )
266 Radeon_ProgramRMXRegisters( ai, &fp_values );
268 if( (disp_devices & (dd_lvds | dd_dvi | dd_dvi_ext)) != 0 )
269 Radeon_ProgramFPRegisters( ai, crtc, fp_info, &fp_values );
271 //if( mode->timing.pixel_clock )
272 Radeon_ProgramPLL( ai, crtc->crtc_idx, &pll_values );
274 if( (disp_devices & (dd_ctv | dd_stv)) != 0 ) {
275 if( internal_tv_encoder )
276 Radeon_InternalTVOutProgramRegisters( ai, &impactv_values );
277 else
278 Radeon_TheatreProgramTVRegisters( ai, &impactv_values );
281 // spit out some debug stuff in a radeontool stylee
282 SHOW_FLOW0( 0, "" );
283 SHOW_FLOW( 0, "RADEON_DAC_CNTL %08X ", INREG( regs, RADEON_DAC_CNTL ));
284 SHOW_FLOW( 0, "RADEON_DAC_CNTL2 %08X ", INREG( regs, RADEON_DAC_CNTL2 ));
285 SHOW_FLOW( 0, "RADEON_TV_DAC_CNTL %08X ", INREG( regs, RADEON_TV_DAC_CNTL ));
286 SHOW_FLOW( 0, "RADEON_DISP_OUTPUT_CNTL %08X ", INREG( regs, RADEON_DISP_OUTPUT_CNTL ));
287 SHOW_FLOW( 0, "RADEON_AUX_SC_CNTL %08X ", INREG( regs, RADEON_AUX_SC_CNTL ));
288 SHOW_FLOW( 0, "RADEON_CRTC_EXT_CNTL %08X ", INREG( regs, RADEON_CRTC_EXT_CNTL ));
289 SHOW_FLOW( 0, "RADEON_CRTC_GEN_CNTL %08X ", INREG( regs, RADEON_CRTC_GEN_CNTL ));
290 SHOW_FLOW( 0, "RADEON_CRTC2_GEN_CNTL %08X ", INREG( regs, RADEON_CRTC2_GEN_CNTL ));
291 SHOW_FLOW( 0, "RADEON_DISP_MISC_CNTL %08X ", INREG( regs, RADEON_DISP_MISC_CNTL ));
292 SHOW_FLOW( 0, "RADEON_FP_GEN_CNTL %08X ", INREG( regs, RADEON_FP_GEN_CNTL ));
293 SHOW_FLOW( 0, "RADEON_FP2_GEN_CNTL %08X ", INREG( regs, RADEON_FP2_GEN_CNTL ));
294 SHOW_FLOW( 0, "RADEON_LVDS_GEN_CNTL %08X ", INREG( regs, RADEON_LVDS_GEN_CNTL ));
295 SHOW_FLOW( 0, "RADEON_TMDS_PLL_CNTL %08X ", INREG( regs, RADEON_TMDS_PLL_CNTL ));
296 SHOW_FLOW( 0, "RADEON_TMDS_TRANSMITTER_CNTL %08X ", INREG( regs, RADEON_TMDS_TRANSMITTER_CNTL ));
297 SHOW_FLOW( 0, "RADEON_FP_H_SYNC_STRT_WID %08X ", INREG( regs, RADEON_FP_H_SYNC_STRT_WID ));
298 SHOW_FLOW( 0, "RADEON_FP_V_SYNC_STRT_WID %08X ", INREG( regs, RADEON_FP_V_SYNC_STRT_WID ));
299 SHOW_FLOW( 0, "RADEON_FP_H2_SYNC_STRT_WID %08X ", INREG( regs, RADEON_FP_H2_SYNC_STRT_WID ));
300 SHOW_FLOW( 0, "RADEON_FP_V2_SYNC_STRT_WID %08X ", INREG( regs, RADEON_FP_V2_SYNC_STRT_WID ));
301 // spit end
303 crtc->active_displays = disp_devices;
305 // programming is over, so hardware can be used again
306 RELEASE_BEN( si->cp.lock );
308 // overlay must be setup again after modeswitch (whoever was using it)
309 // TBD: this won't work if another virtual card was using it,
310 // but currently, virtual cards don't work anyway...
311 si->active_overlay.crtc_idx = -1;
313 return B_OK;
317 // enable or disable VBlank interrupts
318 void Radeon_EnableIRQ(
319 accelerator_info *ai, bool enable )
321 shared_info *si = ai->si;
322 uint32 int_cntl, int_mask;
324 int_cntl = INREG( ai->regs, RADEON_GEN_INT_CNTL );
325 int_mask =
326 RADEON_CRTC_VBLANK_MASK
327 | (si->num_crtc > 1 ? RADEON_CRTC2_VBLANK_MASK : 0);
329 if( enable )
330 int_cntl |= int_mask;
331 else
332 int_cntl &= ~int_mask;
334 OUTREG( ai->regs, RADEON_GEN_INT_CNTL, int_cntl );
336 if( enable ) {
337 // on enable, we have to acknowledge all IRQs as the graphics card
338 // waits for that before it issues further IRQs
339 OUTREG( ai->regs, RADEON_GEN_INT_STATUS, int_cntl );
342 si->enable_virtual_irq = enable;
346 // public function: set display mode
347 status_t SET_DISPLAY_MODE(
348 display_mode *mode_in )
350 virtual_card *vc = ai->vc;
351 shared_info *si = ai->si;
352 display_mode bounds, mode;
354 mode = bounds = *mode_in;
356 ACQUIRE_BEN( si->engine.lock );
358 SHOW_FLOW( 2, "width=%d, height=%d", mode.timing.h_display, mode.timing.v_display );
360 // check mode and tweak parameters so we can program hardware
361 // without any further checks
362 if( PROPOSE_DISPLAY_MODE( &mode, &bounds, &bounds ) == B_ERROR ) {
363 SHOW_ERROR0( 2, "invalid mode" );
365 RELEASE_BEN( si->engine.lock );
366 return B_ERROR;
369 // already done by propose_display_mode, but it was undone on return;
370 // do this before equality check to recognize changes of multi-monitor mode
371 Radeon_DetectMultiMode( vc, &mode );
373 // mode switches can take quite long and are visible,
374 // so avoid them if possible
375 if( memcmp( &mode, &vc->mode, sizeof( display_mode )) == 0 &&
376 !vc->enforce_mode_change ) {
377 RELEASE_BEN( si->engine.lock );
378 return B_OK;
381 // this flag was set when some internal parameter has changed that
382 // affects effective display mode
383 vc->enforce_mode_change = false;
385 // make sure, we don't get disturbed
386 //Radeon_Finish( ai );
387 Radeon_EnableIRQ( ai, false );
389 // free cursor and framebuffer memory
391 radeon_free_mem fm;
393 fm.magic = RADEON_PRIVATE_DATA_MAGIC;
394 fm.memory_type = mt_local;
395 fm.global = true;
397 if( vc->cursor.mem_handle ) {
398 fm.handle = vc->cursor.mem_handle;
399 ioctl( ai->fd, RADEON_FREE_MEM, &fm );
402 if( vc->fb_mem_handle ) {
403 fm.handle = vc->fb_mem_handle;
404 ioctl( ai->fd, RADEON_FREE_MEM, &fm );
408 memcpy( &vc->mode, &mode, sizeof( display_mode ));
410 // verify hardware restrictions *after* saving mode
411 // e.g. if you want a span mode but have one monitor disconnected,
412 // configuration shouldn't be touched, so you can continue working
413 // with two monitors later on just like nothing has happened
414 Radeon_VerifyMultiMode( vc, si, &mode );
416 // set main flags
417 vc->independant_heads = vc->assigned_crtc[0] && si->crtc[0].chosen_displays != dd_none;
419 if( si->num_crtc > 1 )
420 vc->independant_heads += vc->assigned_crtc[1] && si->crtc[1].chosen_displays != dd_none;
422 vc->different_heads = Radeon_DifferentPorts( &mode );
423 SHOW_FLOW( 2, "independant heads: %d, different heads: %d",
424 vc->independant_heads, vc->different_heads );
425 vc->scroll = mode.flags & B_SCROLL;
426 SHOW_FLOW( 2, "scrolling %s", vc->scroll ? "enabled" : "disabled" );
428 // allocate frame buffer and cursor image memory
430 radeon_alloc_mem am;
431 int format, bpp;
433 // alloc cursor memory
434 am.magic = RADEON_PRIVATE_DATA_MAGIC;
435 am.size = 1024;
436 am.memory_type = mt_local;
437 am.global = true;
439 if( ioctl( ai->fd, RADEON_ALLOC_MEM, &am ) == B_OK ) {
440 vc->cursor.mem_handle = am.handle;
441 vc->cursor.fb_offset = am.offset;
442 } else {
443 // too bad that we are out of mem -> set reasonable values as
444 // it's too late to give up (ouch!)
445 SHOW_ERROR0( 2, "no memory for cursor image!" );
446 vc->cursor.mem_handle = 0;
447 vc->cursor.fb_offset = 0;
450 vc->cursor.data = si->local_mem + vc->cursor.fb_offset;
452 // alloc frame buffer
453 Radeon_GetFormat( mode.space, &format, &bpp );
454 vc->pitch = Radeon_RoundVWidth( mode.virtual_width, bpp ) * bpp;
455 am.size = vc->pitch * mode.virtual_height;
457 if( ioctl( ai->fd, RADEON_ALLOC_MEM, &am ) == B_OK ) {
458 vc->fb_mem_handle = am.handle;
459 vc->fb_offset = am.offset;
460 } else {
461 // ouch again - set reasonable values
462 SHOW_ERROR0( 2, "no memory for frame buffer!" );
463 vc->fb_mem_handle = 0;
464 vc->fb_offset = 1024;
467 vc->fbc.frame_buffer = si->local_mem + vc->fb_offset;
468 vc->fbc.frame_buffer_dma = (void *)((uint8 *)si->framebuffer_pci + vc->fb_offset);
469 vc->fbc.bytes_per_row = vc->pitch;
471 SHOW_FLOW( 0, "frame buffer CPU-address=%x, phys-address=%x",
472 vc->fbc.frame_buffer, vc->fbc.frame_buffer_dma );
475 // multi-screen stuff
476 Radeon_InitMultiModeVars( ai, &mode );
478 // GO!
481 routing_regs routing_values;
482 impactv_params tv_params;
483 status_t err1 , err2;
484 err1 = err2 = B_OK;
486 // we first switch off all output, so the monitor(s) won't get invalid signals
487 if( vc->assigned_crtc[0] ) {
488 // overwrite list of active displays to switch off displays
489 // someone else turned on
490 si->crtc[0].active_displays = vc->controlled_displays;
491 Radeon_SetDPMS( ai, 0, B_DPMS_SUSPEND );
493 if( vc->assigned_crtc[1] ) {
494 si->crtc[1].active_displays = vc->controlled_displays;
495 Radeon_SetDPMS( ai, 1, B_DPMS_SUSPEND );
498 // mark crtc that will be used from now on
499 vc->used_crtc[0] = vc->assigned_crtc[0] && si->crtc[0].chosen_displays != dd_none;
500 vc->used_crtc[1] = vc->assigned_crtc[1] && si->crtc[1].chosen_displays != dd_none;
502 // then change the mode
503 if( vc->used_crtc[0] )
504 err1 = Radeon_SetMode( ai, &si->crtc[0], &mode, &tv_params );
505 if( vc->used_crtc[1] )
506 err2 = Radeon_SetMode( ai, &si->crtc[1], &mode, &tv_params );
509 SHOW_FLOW( 2, "SetModes 1=%s, 2=%s",
510 (err1 == B_OK) ? "OK" : "FAIL", (err2 == B_OK) ? "OK" : "FAIL");
512 // setup signal routing
513 Radeon_ReadMonitorRoutingRegs( ai, &routing_values );
514 Radeon_CalcMonitorRouting( ai, &tv_params, &routing_values );
515 Radeon_ProgramMonitorRouting( ai, &routing_values );
517 // finally, switch display(s) on
518 if( vc->used_crtc[0] )
519 Radeon_SetDPMS( ai, 0, (err1 == B_OK) ? B_DPMS_ON : B_DPMS_SUSPEND );
520 if( vc->used_crtc[1] )
521 Radeon_SetDPMS( ai, 1, (err2 == B_OK) ? B_DPMS_ON : B_DPMS_SUSPEND );
523 OUTREGP( ai->regs, RADEON_CRTC_EXT_CNTL, 0, ~RADEON_CRTC_DISPLAY_DIS );
526 SHOW_FLOW( 3, "pitch=%ld", vc->pitch );
528 // we'll modify bits of this reg, so save it for async access
529 si->dac_cntl2 = INREG( ai->regs, RADEON_DAC_CNTL2 );
531 // setup 2D registers
532 Radeon_Init2D( ai );
533 // setup position of framebuffer for 2D commands
534 Radeon_FillStateBuffer( ai, vc->datatype );
536 // remember that 2D accelerator is not prepared for any virtual card
537 si->active_vc = -1;
539 // first move to well-defined position (to setup CRTC offset)
540 Radeon_MoveDisplay( ai, 0, 0 );
541 // then to (probably faulty) user-defined pos
542 Radeon_MoveDisplay( ai, mode.h_display_start, mode.v_display_start );
544 // set standard palette in direct-colour modes
545 if( vc->used_crtc[0] )
546 Radeon_InitPalette( ai, 0 );
547 if( vc->used_crtc[1] )
548 Radeon_InitPalette( ai, 1 );
550 // initialize cursor data
551 if( vc->used_crtc[0] )
552 Radeon_SetCursorColors( ai, 0 );
553 if( vc->used_crtc[1] )
554 Radeon_SetCursorColors( ai, 1 );
556 // sync should be settled now, so we can reenable IRQs
557 Radeon_EnableIRQ( ai, true );
559 RELEASE_BEN( si->engine.lock );
561 // !! all this must be done after lock has been
562 // released to avoid dead-lock !!
563 // TBD: any invalid intermediate states?
565 // move_cursor sets all cursor-related variables and registers
566 vc->cursor.is_visible = false;
567 MOVE_CURSOR( 0, 0 );
569 return B_OK;