xref: /haiku/src/add-ons/accelerants/radeon/pll.c (revision 67bce78b48ed6d01b5a8eef89f5694c372b7e0a1)
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