1 /* 2 Copyright (c) 2002, Thomas Kurschel 3 4 5 Part of Radeon accelerant 6 7 Takes of PLL 8 */ 9 10 11 #include "radeon_accelerant.h" 12 13 #include "pll_regs.h" 14 #include "utils.h" 15 16 17 // read value "val" from PLL-register "addr" 18 uint32 Radeon_INPLL( accelerator_info *ai, int addr ) 19 { 20 vuint8 *regs = ai->regs; 21 uint32 res; 22 23 OUTREG8( regs, RADEON_CLOCK_CNTL_INDEX, addr & 0x3f ); 24 res = INREG( regs, RADEON_CLOCK_CNTL_DATA ); 25 26 R300_PLLFix( ai ); 27 return res; 28 } 29 30 // write value "val" to PLL-register "addr" 31 void Radeon_OUTPLL( accelerator_info *ai, uint8 addr, uint32 val ) 32 { 33 vuint8 *regs = ai->regs; 34 35 OUTREG8( regs, RADEON_CLOCK_CNTL_INDEX, ((addr & 0x3f ) | 36 RADEON_PLL_WR_EN)); 37 38 OUTREG( regs, RADEON_CLOCK_CNTL_DATA, val ); 39 40 // TBD: on XFree, there is no call of R300_PLLFix here, 41 // though it should as we've accessed CLOCK_CNTL_INDEX 42 //R300_PLLFix( ai ); 43 } 44 45 // write "val" to PLL-register "addr" keeping bits "mask" 46 void Radeon_OUTPLLP( accelerator_info *ai, uint8 addr, 47 uint32 val, uint32 mask ) 48 { 49 uint32 tmp = Radeon_INPLL( ai, addr ); 50 tmp &= mask; 51 tmp |= val; 52 Radeon_OUTPLL( ai, addr, tmp ); 53 } 54 55 56 static void Radeon_PLLWaitForReadUpdateComplete( accelerator_info *ai, virtual_port *port ) 57 { 58 int i; 59 60 // we should wait forever, but 61 // 1. this is unsafe 62 // 2. some r300 loop forever (reported by XFree86) 63 for( i = 0; i < 10000; ++i ) { 64 if( (Radeon_INPLL( ai, port->is_crtc2 ? RADEON_P2PLL_REF_DIV : RADEON_PPLL_REF_DIV ) 65 & RADEON_PPLL_ATOMIC_UPDATE_R) == 0 ) 66 return; 67 } 68 } 69 70 static void Radeon_PLLWriteUpdate( accelerator_info *ai, virtual_port *port ) 71 { 72 Radeon_PLLWaitForReadUpdateComplete( ai, port ); 73 74 Radeon_OUTPLLP( ai, port->is_crtc2 ? RADEON_P2PLL_REF_DIV : RADEON_PPLL_REF_DIV, 75 RADEON_PPLL_ATOMIC_UPDATE_W, 76 ~RADEON_PPLL_ATOMIC_UPDATE_W ); 77 } 78 79 // r300: to be called after each CLOCK_CNTL_INDEX access 80 // (hardware bug fix suggested by XFree86) 81 void R300_PLLFix( accelerator_info *ai ) 82 { 83 vuint8 *regs = ai->regs; 84 uint32 save, tmp; 85 86 if( ai->si->asic != rt_r300 ) 87 return; 88 89 save = INREG( regs, RADEON_CLOCK_CNTL_INDEX ); 90 tmp = save & ~(0x3f | RADEON_PLL_WR_EN); 91 OUTREG( regs, RADEON_CLOCK_CNTL_INDEX, tmp ); 92 tmp = INREG( regs, RADEON_CLOCK_CNTL_DATA ); 93 OUTREG( regs, RADEON_CLOCK_CNTL_INDEX, save ); 94 } 95 96 97 // table to map divider to register value 98 typedef struct { 99 int divider; 100 int bitvalue; 101 } post_div_entry; 102 103 static post_div_entry post_divs[] = { 104 { 1, 0 }, 105 { 2, 1 }, 106 { 4, 2 }, 107 { 8, 3 }, 108 { 3, 4 }, 109 { 16, 5 }, 110 { 6, 6 }, 111 { 12, 7 }, 112 { 0, 0 } 113 }; 114 115 // calculate PLL dividers (freq is in 10kHz) 116 void Radeon_CalcPLLDividers( pll_info *pll, unsigned long freq, port_regs *values ) 117 { 118 post_div_entry *post_div; 119 120 SHOW_FLOW( 2, "freq=%ld", freq ); 121 122 // formula is for generated frequency is: 123 // (ref_freq * feedback_div) / (ref_div * post_div ) 124 125 // find proper divider by trial-and-error 126 for( post_div = &post_divs[0]; post_div->divider; ++post_div ) { 127 values->pll_output_freq = post_div->divider * freq; 128 129 if( values->pll_output_freq >= pll->min_pll_freq 130 && values->pll_output_freq <= pll->max_pll_freq ) 131 break; 132 } 133 134 if( post_div->divider == 0 ) 135 SHOW_ERROR( 2, "Frequency (%d kHz) is out of PLL range!", freq ); 136 137 values->dot_clock_freq = freq; 138 values->feedback_div = RoundDiv( pll->ref_div * values->pll_output_freq, 139 pll->ref_freq); 140 values->post_div = post_div->divider; 141 142 values->ppll_ref_div = pll->ref_div; 143 values->ppll_div_3 = (values->feedback_div | (post_div->bitvalue << 16)); 144 values->htotal_cntl = 0; 145 146 SHOW_FLOW( 2, "dot_clock_freq=%ld, pll_output_freq=%ld, ref_div=%d, feedback_div=%d, post_div=%d", 147 values->dot_clock_freq, values->pll_output_freq, 148 pll->ref_div, values->feedback_div, values->post_div ); 149 } 150 151 // write values into PLL registers 152 void Radeon_ProgramPLL( accelerator_info *ai, virtual_port *port, port_regs *values ) 153 { 154 vuint8 *regs = ai->regs; 155 156 SHOW_FLOW0( 2, "" ); 157 158 // use some other PLL for pixel clock source to not fiddling with PLL 159 // while somebody is using it 160 Radeon_OUTPLLP( ai, port->is_crtc2 ? RADEON_PIXCLKS_CNTL : RADEON_VCLK_ECP_CNTL, 161 RADEON_VCLK_SRC_CPU_CLK, ~RADEON_VCLK_SRC_SEL_MASK ); 162 163 Radeon_OUTPLLP( ai, 164 port->is_crtc2 ? RADEON_P2PLL_CNTL : RADEON_PPLL_CNTL, 165 RADEON_PPLL_RESET 166 | RADEON_PPLL_ATOMIC_UPDATE_EN 167 | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN, 168 ~(RADEON_PPLL_RESET 169 | RADEON_PPLL_ATOMIC_UPDATE_EN 170 | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN) ); 171 172 // select divider 3 (well, only required for first PLL) 173 OUTREGP( regs, RADEON_CLOCK_CNTL_INDEX, 174 RADEON_PLL_DIV_SEL_DIV3, 175 ~RADEON_PLL_DIV_SEL_MASK ); 176 177 // probably this register doesn't need to be set as is not 178 // touched by anyone (anyway - it doesn't hurt) 179 Radeon_OUTPLLP( ai, 180 port->is_crtc2 ? RADEON_P2PLL_REF_DIV : RADEON_PPLL_REF_DIV, 181 values->ppll_ref_div, 182 ~RADEON_PPLL_REF_DIV_MASK ); 183 184 Radeon_OUTPLLP( ai, 185 port->is_crtc2 ? RADEON_P2PLL_DIV_0 : RADEON_PPLL_DIV_3, 186 values->ppll_div_3, 187 ~RADEON_PPLL_FB3_DIV_MASK ); 188 189 Radeon_OUTPLLP( ai, 190 port->is_crtc2 ? RADEON_P2PLL_DIV_0 : RADEON_PPLL_DIV_3, 191 values->ppll_div_3, 192 ~RADEON_PPLL_POST3_DIV_MASK ); 193 194 Radeon_PLLWriteUpdate( ai, port ); 195 Radeon_PLLWaitForReadUpdateComplete( ai, port ); 196 197 Radeon_OUTPLL( ai, 198 port->is_crtc2 ? RADEON_HTOTAL2_CNTL : RADEON_HTOTAL_CNTL, 199 values->htotal_cntl ); 200 201 Radeon_OUTPLLP( ai, port->is_crtc2 ? RADEON_P2PLL_CNTL : RADEON_PPLL_CNTL, 0, 202 ~(RADEON_PPLL_RESET 203 | RADEON_PPLL_SLEEP 204 | RADEON_PPLL_ATOMIC_UPDATE_EN 205 | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN) ); 206 207 // there is no way to check whether PLL has settled, so wait a bit 208 snooze( 5000 ); 209 210 // use PLL for pixel clock again 211 Radeon_OUTPLLP( ai, port->is_crtc2 ? RADEON_PIXCLKS_CNTL : RADEON_VCLK_ECP_CNTL, 212 RADEON_VCLK_SRC_PPLL_CLK, ~RADEON_VCLK_SRC_SEL_MASK ); 213 } 214