1409f1731Sshadow303 /* 2409f1731Sshadow303 Copyright (c) 2002, Thomas Kurschel 3409f1731Sshadow303 4409f1731Sshadow303 5409f1731Sshadow303 Part of Radeon accelerant 6409f1731Sshadow303 7409f1731Sshadow303 CRTC programming 8409f1731Sshadow303 */ 9409f1731Sshadow303 10409f1731Sshadow303 #include "radeon_accelerant.h" 11409f1731Sshadow303 #include "mmio.h" 12409f1731Sshadow303 #include "crtc_regs.h" 13409f1731Sshadow303 #include "dac_regs.h" 14*2a37e4c1Sshadow303 #include "GlobalData.h" 15*2a37e4c1Sshadow303 16*2a37e4c1Sshadow303 17*2a37e4c1Sshadow303 // read old CRTC register content 18*2a37e4c1Sshadow303 void Radeon_ReadCRTCRegisters( accelerator_info *ai, physical_head *head, 19*2a37e4c1Sshadow303 port_regs *values ) 20*2a37e4c1Sshadow303 { 21*2a37e4c1Sshadow303 vuint8 *regs = ai->regs; 22*2a37e4c1Sshadow303 23*2a37e4c1Sshadow303 // only CRTC_EXT_CNTL is programmed by someone else (namely the monitor 24*2a37e4c1Sshadow303 // router); if more registers are affected, you must read them here too! 25*2a37e4c1Sshadow303 if( !head->is_crtc2 ) { 26*2a37e4c1Sshadow303 values->crtc_ext_cntl = INREG( regs, RADEON_CRTC_EXT_CNTL ); 27*2a37e4c1Sshadow303 } 28*2a37e4c1Sshadow303 } 29*2a37e4c1Sshadow303 30409f1731Sshadow303 31409f1731Sshadow303 // hammer CRTC registers 32*2a37e4c1Sshadow303 void Radeon_ProgramCRTCRegisters( accelerator_info *ai, physical_head *head, 33409f1731Sshadow303 port_regs *values ) 34409f1731Sshadow303 { 35409f1731Sshadow303 vuint8 *regs = ai->regs; 36409f1731Sshadow303 37409f1731Sshadow303 SHOW_FLOW0( 2, "" ); 38409f1731Sshadow303 39*2a37e4c1Sshadow303 if( head->is_crtc2 ) { 40409f1731Sshadow303 OUTREGP( regs, RADEON_CRTC2_GEN_CNTL, values->crtc_gen_cntl, 41409f1731Sshadow303 RADEON_CRTC2_VSYNC_DIS | 42409f1731Sshadow303 RADEON_CRTC2_HSYNC_DIS | 43409f1731Sshadow303 RADEON_CRTC2_DISP_DIS ); 44409f1731Sshadow303 45409f1731Sshadow303 OUTREG( regs, RADEON_CRTC2_H_TOTAL_DISP, values->crtc_h_total_disp ); 46409f1731Sshadow303 OUTREG( regs, RADEON_CRTC2_H_SYNC_STRT_WID, values->crtc_h_sync_strt_wid ); 47409f1731Sshadow303 OUTREG( regs, RADEON_CRTC2_V_TOTAL_DISP, values->crtc_v_total_disp ); 48409f1731Sshadow303 OUTREG( regs, RADEON_CRTC2_V_SYNC_STRT_WID, values->crtc_v_sync_strt_wid ); 49409f1731Sshadow303 OUTREG( regs, RADEON_CRTC2_OFFSET_CNTL, values->crtc_offset_cntl ); 50409f1731Sshadow303 OUTREG( regs, RADEON_CRTC2_PITCH, values->crtc_pitch ); 51409f1731Sshadow303 52409f1731Sshadow303 } else { 53409f1731Sshadow303 OUTREG( regs, RADEON_CRTC_GEN_CNTL, values->crtc_gen_cntl ); 54409f1731Sshadow303 55409f1731Sshadow303 OUTREGP( regs, RADEON_CRTC_EXT_CNTL, values->crtc_ext_cntl, 56409f1731Sshadow303 RADEON_CRTC_VSYNC_DIS | 57409f1731Sshadow303 RADEON_CRTC_HSYNC_DIS | 58*2a37e4c1Sshadow303 RADEON_CRTC_DISPLAY_DIS | 59*2a37e4c1Sshadow303 RADEON_CRTC_CRT_ON ); 60409f1731Sshadow303 61409f1731Sshadow303 OUTREGP( regs, RADEON_DAC_CNTL, values->dac_cntl, 62*2a37e4c1Sshadow303 RADEON_DAC_RANGE_CNTL_MASK | RADEON_DAC_BLANKING ); 63409f1731Sshadow303 64409f1731Sshadow303 OUTREG( regs, RADEON_CRTC_H_TOTAL_DISP, values->crtc_h_total_disp ); 65409f1731Sshadow303 OUTREG( regs, RADEON_CRTC_H_SYNC_STRT_WID, values->crtc_h_sync_strt_wid ); 66409f1731Sshadow303 OUTREG( regs, RADEON_CRTC_V_TOTAL_DISP, values->crtc_v_total_disp ); 67409f1731Sshadow303 OUTREG( regs, RADEON_CRTC_V_SYNC_STRT_WID, values->crtc_v_sync_strt_wid ); 68409f1731Sshadow303 OUTREG( regs, RADEON_CRTC_OFFSET_CNTL, values->crtc_offset_cntl ); 69409f1731Sshadow303 OUTREG( regs, RADEON_CRTC_PITCH, values->crtc_pitch ); 70409f1731Sshadow303 } 71409f1731Sshadow303 } 72409f1731Sshadow303 73409f1731Sshadow303 74409f1731Sshadow303 // get required hsync delay depending on bit depth and output device 75*2a37e4c1Sshadow303 uint16 Radeon_GetHSyncFudge( physical_head *head, int datatype ) 76409f1731Sshadow303 { 77409f1731Sshadow303 static int hsync_fudge_default[] = { 0x00, 0x12, 0x09, 0x09, 0x06, 0x05 }; 78409f1731Sshadow303 static int hsync_fudge_fp[] = { 0x02, 0x02, 0x00, 0x00, 0x05, 0x05 }; 79409f1731Sshadow303 80409f1731Sshadow303 // there is an sync delay which depends on colour-depth and output device 81*2a37e4c1Sshadow303 if( (head->chosen_displays & (dd_dvi | dd_dvi_ext | dd_lvds )) != 0 ) 82409f1731Sshadow303 return hsync_fudge_fp[datatype - 1]; 83409f1731Sshadow303 else 84409f1731Sshadow303 return hsync_fudge_default[datatype - 1]; 85409f1731Sshadow303 } 86409f1731Sshadow303 87409f1731Sshadow303 88409f1731Sshadow303 // calculate CRTC register content 89*2a37e4c1Sshadow303 void Radeon_CalcCRTCRegisters( accelerator_info *ai, physical_head *head, 90409f1731Sshadow303 display_mode *mode, port_regs *values ) 91409f1731Sshadow303 { 92409f1731Sshadow303 virtual_card *vc = ai->vc; 93409f1731Sshadow303 int hsync_start; 94409f1731Sshadow303 int hsync_wid; 95409f1731Sshadow303 int hsync_fudge; 96409f1731Sshadow303 int vsync_wid; 97409f1731Sshadow303 98*2a37e4c1Sshadow303 hsync_fudge = Radeon_GetHSyncFudge( head, vc->datatype ); 99409f1731Sshadow303 100*2a37e4c1Sshadow303 if( head->is_crtc2 ) { 101409f1731Sshadow303 values->crtc_gen_cntl = (RADEON_CRTC2_EN 102409f1731Sshadow303 | RADEON_CRTC2_CRT2_ON 103409f1731Sshadow303 | (vc->datatype << 8) 104409f1731Sshadow303 | (0/*doublescan*/ ? RADEON_CRTC2_DBL_SCAN_EN : 0) 105409f1731Sshadow303 | ((mode->timing.flags & B_TIMING_INTERLACED) 106409f1731Sshadow303 ? RADEON_CRTC2_INTERLACE_EN : 0)); 107409f1731Sshadow303 } else { 108409f1731Sshadow303 // here, we should set interlace/double scan mode 109409f1731Sshadow303 // but we don't support them (anyone missing them?) 110409f1731Sshadow303 values->crtc_gen_cntl = (RADEON_CRTC_EXT_DISP_EN 111409f1731Sshadow303 | RADEON_CRTC_EN 112409f1731Sshadow303 | (vc->datatype << 8)); 113409f1731Sshadow303 114409f1731Sshadow303 values->crtc_ext_cntl = 115409f1731Sshadow303 RADEON_VGA_ATI_LINEAR | 116*2a37e4c1Sshadow303 RADEON_XCRT_CNT_EN; 117409f1731Sshadow303 118409f1731Sshadow303 values->dac_cntl = RADEON_DAC_MASK_ALL 119409f1731Sshadow303 | RADEON_DAC_VGA_ADR_EN 120409f1731Sshadow303 | RADEON_DAC_8BIT_EN; 121409f1731Sshadow303 } 122409f1731Sshadow303 123409f1731Sshadow303 values->crtc_h_total_disp = 124409f1731Sshadow303 ((mode->timing.h_total / 8 - 1) & RADEON_CRTC_H_TOTAL) 125409f1731Sshadow303 | (((mode->timing.h_display / 8 - 1) << RADEON_CRTC_H_DISP_SHIFT) & RADEON_CRTC_H_DISP); 126409f1731Sshadow303 127409f1731Sshadow303 hsync_wid = (mode->timing.h_sync_end - mode->timing.h_sync_start) / 8; 128409f1731Sshadow303 129409f1731Sshadow303 hsync_start = mode->timing.h_sync_start - 8 + hsync_fudge; 130409f1731Sshadow303 131409f1731Sshadow303 values->crtc_h_sync_strt_wid = 132409f1731Sshadow303 (hsync_start & (RADEON_CRTC_H_SYNC_STRT_CHAR | RADEON_CRTC_H_SYNC_STRT_PIX)) 133409f1731Sshadow303 | (hsync_wid << RADEON_CRTC_H_SYNC_WID_SHIFT) 134409f1731Sshadow303 | ((mode->flags & B_POSITIVE_HSYNC) == 0 ? RADEON_CRTC_H_SYNC_POL : 0); 135409f1731Sshadow303 136409f1731Sshadow303 values->crtc_v_total_disp = 137409f1731Sshadow303 ((mode->timing.v_total - 1) & RADEON_CRTC_V_TOTAL) 138409f1731Sshadow303 | (((mode->timing.v_display - 1) << RADEON_CRTC_V_DISP_SHIFT) & RADEON_CRTC_V_DISP); 139409f1731Sshadow303 140409f1731Sshadow303 vsync_wid = mode->timing.v_sync_end - mode->timing.v_sync_start; 141409f1731Sshadow303 142409f1731Sshadow303 values->crtc_v_sync_strt_wid = 143409f1731Sshadow303 ((mode->timing.v_sync_start - 1) & RADEON_CRTC_V_SYNC_STRT) 144409f1731Sshadow303 | (vsync_wid << RADEON_CRTC_V_SYNC_WID_SHIFT) 145409f1731Sshadow303 | ((mode->flags & B_POSITIVE_VSYNC) == 0 146409f1731Sshadow303 ? RADEON_CRTC_V_SYNC_POL : 0); 147409f1731Sshadow303 148409f1731Sshadow303 values->crtc_offset_cntl = 0; 149409f1731Sshadow303 150409f1731Sshadow303 values->crtc_pitch = Radeon_RoundVWidth( mode->virtual_width, vc->bpp ) / 8; 151409f1731Sshadow303 152409f1731Sshadow303 SHOW_FLOW( 2, "crtc_pitch=%ld", values->crtc_pitch ); 153409f1731Sshadow303 154409f1731Sshadow303 values->crtc_pitch |= values->crtc_pitch << 16; 155409f1731Sshadow303 } 156*2a37e4c1Sshadow303 157*2a37e4c1Sshadow303 158*2a37e4c1Sshadow303 // update shown are of one port 159*2a37e4c1Sshadow303 static void moveOneDisplay( accelerator_info *ai, virtual_head *virtual_head ) 160*2a37e4c1Sshadow303 { 161*2a37e4c1Sshadow303 virtual_card *vc = ai->vc; 162*2a37e4c1Sshadow303 uint32 offset; 163*2a37e4c1Sshadow303 164*2a37e4c1Sshadow303 offset = (vc->mode.v_display_start + virtual_head->rel_y) * vc->pitch + 165*2a37e4c1Sshadow303 (vc->mode.h_display_start + virtual_head->rel_x) * vc->bpp + 166*2a37e4c1Sshadow303 vc->fb_offset; 167*2a37e4c1Sshadow303 168*2a37e4c1Sshadow303 SHOW_FLOW( 3, "Setting address %x on port %d", 169*2a37e4c1Sshadow303 offset, virtual_head->physical_head ); 170*2a37e4c1Sshadow303 171*2a37e4c1Sshadow303 OUTREG( ai->regs, virtual_head->physical_head ? RADEON_CRTC2_OFFSET : RADEON_CRTC_OFFSET, offset ); 172*2a37e4c1Sshadow303 } 173*2a37e4c1Sshadow303 174*2a37e4c1Sshadow303 // internal function: pan display 175*2a37e4c1Sshadow303 // engine lock should be hold 176*2a37e4c1Sshadow303 status_t Radeon_MoveDisplay( accelerator_info *ai, uint16 h_display_start, uint16 v_display_start ) 177*2a37e4c1Sshadow303 { 178*2a37e4c1Sshadow303 virtual_card *vc = ai->vc; 179*2a37e4c1Sshadow303 180*2a37e4c1Sshadow303 SHOW_FLOW( 4, "h_display_start=%ld, v_display_start=%ld", 181*2a37e4c1Sshadow303 h_display_start, v_display_start ); 182*2a37e4c1Sshadow303 183*2a37e4c1Sshadow303 if( h_display_start + vc->eff_width > vc->mode.virtual_width || 184*2a37e4c1Sshadow303 v_display_start + vc->eff_height > vc->mode.virtual_height ) 185*2a37e4c1Sshadow303 return B_ERROR; 186*2a37e4c1Sshadow303 187*2a37e4c1Sshadow303 // this is needed both for get_mode_info and for scrolling of virtual screens 188*2a37e4c1Sshadow303 vc->mode.h_display_start = h_display_start & ~7; 189*2a37e4c1Sshadow303 vc->mode.v_display_start = v_display_start; 190*2a37e4c1Sshadow303 191*2a37e4c1Sshadow303 // do it 192*2a37e4c1Sshadow303 moveOneDisplay( ai, &vc->heads[0] ); 193*2a37e4c1Sshadow303 194*2a37e4c1Sshadow303 if( vc->independant_heads > 1 ) 195*2a37e4c1Sshadow303 moveOneDisplay( ai, &vc->heads[1] ); 196*2a37e4c1Sshadow303 197*2a37e4c1Sshadow303 // overlay position must be adjusted 198*2a37e4c1Sshadow303 Radeon_UpdateOverlay( ai ); 199*2a37e4c1Sshadow303 200*2a37e4c1Sshadow303 return B_OK; 201*2a37e4c1Sshadow303 } 202*2a37e4c1Sshadow303 203*2a37e4c1Sshadow303 // public function: pan display 204*2a37e4c1Sshadow303 status_t MOVE_DISPLAY( uint16 h_display_start, uint16 v_display_start ) 205*2a37e4c1Sshadow303 { 206*2a37e4c1Sshadow303 shared_info *si = ai->si; 207*2a37e4c1Sshadow303 status_t result; 208*2a37e4c1Sshadow303 209*2a37e4c1Sshadow303 ACQUIRE_BEN( si->engine.lock ); 210*2a37e4c1Sshadow303 211*2a37e4c1Sshadow303 // TBD: we should probably lock card first; in this case, we must 212*2a37e4c1Sshadow303 // split this function into locking and worker part, as this 213*2a37e4c1Sshadow303 // function is used internally as well 214*2a37e4c1Sshadow303 result = Radeon_MoveDisplay( ai, h_display_start, v_display_start ); 215*2a37e4c1Sshadow303 216*2a37e4c1Sshadow303 RELEASE_BEN( si->engine.lock ); 217*2a37e4c1Sshadow303 218*2a37e4c1Sshadow303 return result; 219*2a37e4c1Sshadow303 } 220