Rev 1029 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1029 | serge | 1 | /* |
2 | * Copyright 2007, 2008 Luc Verhaegen |
||
3 | * Copyright 2007, 2008 Matthias Hopf |
||
4 | * Copyright 2007, 2008 Egbert Eich |
||
5 | * Copyright 2007, 2008 Advanced Micro Devices, Inc. |
||
6 | * |
||
7 | * Permission is hereby granted, free of charge, to any person obtaining a |
||
8 | * copy of this software and associated documentation files (the "Software"), |
||
9 | * to deal in the Software without restriction, including without limitation |
||
10 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||
11 | * and/or sell copies of the Software, and to permit persons to whom the |
||
12 | * Software is furnished to do so, subject to the following conditions: |
||
13 | * |
||
14 | * The above copyright notice and this permission notice shall be included in |
||
15 | * all copies or substantial portions of the Software. |
||
16 | * |
||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||
20 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
||
21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
||
22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
||
23 | * OTHER DEALINGS IN THE SOFTWARE. |
||
24 | */ |
||
25 | |||
26 | #ifdef HAVE_CONFIG_H |
||
27 | #include "config.h" |
||
28 | #endif |
||
29 | |||
30 | #include "xf86.h" |
||
31 | |||
32 | /* for usleep */ |
||
33 | #if HAVE_XF86_ANSIC_H |
||
34 | # include "xf86_ansic.h" |
||
35 | #else |
||
36 | # include |
||
37 | #endif |
||
38 | |||
39 | #include "rhd.h" |
||
40 | #include "rhd_crtc.h" |
||
41 | #include "rhd_connector.h" |
||
42 | #include "rhd_output.h" |
||
43 | #include "rhd_regs.h" |
||
44 | #ifdef ATOM_BIOS |
||
45 | #include "rhd_atombios.h" |
||
46 | #endif |
||
47 | |||
48 | struct DDIAPrivate |
||
49 | { |
||
50 | Bool RunDualLink; |
||
51 | CARD32 PcieCfgReg7; |
||
52 | CARD32 CapabilityFlag; |
||
53 | |||
54 | Bool Stored; |
||
55 | |||
56 | CARD32 DdiaPathControl; |
||
57 | CARD32 DdiaCntl; |
||
58 | CARD32 DdiaDcbalancerControl; |
||
59 | CARD32 DdiaPcieLinkControl2; |
||
60 | CARD32 DdiaBitDepthControl; |
||
61 | }; |
||
62 | |||
63 | /* |
||
64 | * |
||
65 | */ |
||
66 | static ModeStatus |
||
67 | DDIAModeValid(struct rhdOutput *Output, DisplayModePtr Mode) |
||
68 | { |
||
69 | RHDFUNC(Output); |
||
70 | |||
71 | if (Mode->Flags & V_INTERLACE) |
||
72 | return MODE_NO_INTERLACE; |
||
73 | |||
74 | if (Mode->Clock < 25000) |
||
75 | return MODE_CLOCK_LOW; |
||
76 | |||
77 | if (Output->Connector->Type == RHD_CONNECTOR_DVI_SINGLE) { |
||
78 | if (Mode->Clock > 165000) |
||
79 | return MODE_CLOCK_HIGH; |
||
80 | } else if (Output->Connector->Type == RHD_CONNECTOR_DVI) { |
||
81 | if (Mode->Clock > 330000) /* could go higher still */ |
||
82 | return MODE_CLOCK_HIGH; |
||
83 | } |
||
84 | |||
85 | return MODE_OK; |
||
86 | } |
||
87 | |||
88 | /* |
||
89 | * |
||
90 | */ |
||
91 | static void |
||
92 | DDIAMode(struct rhdOutput *Output, DisplayModePtr Mode) |
||
93 | { |
||
94 | struct DDIAPrivate *Private = (struct DDIAPrivate *)Output->Private; |
||
95 | CARD32 mux0, mux1, mux2, mux3; |
||
96 | Bool LaneReversal; |
||
97 | RHDPtr rhdPtr = RHDPTRI(Output); |
||
98 | |||
99 | RHDFUNC(Output); |
||
100 | |||
101 | if (Mode->SynthClock >= 165000) |
||
102 | Private->RunDualLink = TRUE; |
||
103 | else |
||
104 | Private->RunDualLink = FALSE; |
||
105 | |||
106 | /* reset on - will be enabled at POWER_ON */ |
||
107 | RHDRegMask(Output, RS69_DDIA_PATH_CONTROL, RS69_DDIA_PIXVLD_RESET, RS69_DDIA_PIXVLD_RESET); |
||
108 | /* RGB 4:4:4 */ |
||
109 | RHDRegMask(Output, RS69_DDIA_CNTL, 0, RS69_DDIA_PIXEL_ENCODING); |
||
110 | /* TMDS_AC */ |
||
111 | RHDRegMask(Output, RS69_DDIA_PATH_CONTROL, |
||
112 | 2 << RS69_DDIA_PATH_SELECT_SHIFT, |
||
113 | 0x3 << RS69_DDIA_PATH_SELECT_SHIFT); |
||
114 | /* dual link */ |
||
115 | RHDRegMask(Output, RS69_DDIA_CNTL, Private->RunDualLink ? |
||
116 | RS69_DDIA_DUAL_LINK_ENABLE : 0, RS69_DDIA_DUAL_LINK_ENABLE); |
||
117 | RHDRegMask(Output, RS69_DDIA_DCBALANCER_CONTROL, |
||
118 | RS69_DDIA_DCBALANCER_EN, |
||
119 | RS69_DDIA_SYNC_DCBAL_EN_MASK | RS69_DDIA_DCBALANCER_EN); |
||
120 | |||
121 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL2, 0x0, 0x80); |
||
122 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL2, 0x0, 0x100); |
||
123 | |||
124 | mux0 = Private->PcieCfgReg7 & 0x3; |
||
125 | mux1 = (Private->PcieCfgReg7 >> 2) & 0x3; |
||
126 | mux2 = (Private->PcieCfgReg7 >> 4) & 0x3; |
||
127 | mux3 = (Private->PcieCfgReg7 >> 6) & 0x3; |
||
128 | |||
129 | RHDRegMask(Output, RS69_DDIA_PCIE_LINK_CONTROL2, |
||
130 | (mux0 << RS69_DDIA_PCIE_OUTPUT_MUX_SEL0) |
||
131 | | (mux1 << RS69_DDIA_PCIE_OUTPUT_MUX_SEL1) |
||
132 | | (mux2 << RS69_DDIA_PCIE_OUTPUT_MUX_SEL2) |
||
133 | | (mux3 << RS69_DDIA_PCIE_OUTPUT_MUX_SEL3), |
||
134 | (3 << RS69_DDIA_PCIE_OUTPUT_MUX_SEL0) |
||
135 | | (3 << RS69_DDIA_PCIE_OUTPUT_MUX_SEL1) |
||
136 | | (3 << RS69_DDIA_PCIE_OUTPUT_MUX_SEL2) |
||
137 | | (3 << RS69_DDIA_PCIE_OUTPUT_MUX_SEL3) |
||
138 | ); |
||
139 | LaneReversal = Private->PcieCfgReg7 & (0x1 << 10); |
||
140 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL2, 0x0, 0x3); |
||
141 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL2, 0x2, 0x2); |
||
142 | |||
143 | RHDRegMask(Output, RS69_DDIA_PCIE_LINK_CONTROL3, |
||
144 | LaneReversal ? RS69_DDIA_PCIE_MIRROR_EN : 0, |
||
145 | RS69_DDIA_PCIE_MIRROR_EN); |
||
146 | |||
147 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL2, 0x70, 0x70); |
||
148 | |||
149 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0, 0x10); |
||
150 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0, 0x60); |
||
151 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0, 0x4000000); |
||
152 | |||
153 | switch (rhdPtr->PciDeviceID) { |
||
154 | case 0x791E: |
||
155 | if (Mode->SynthClock <= 25000) { |
||
156 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x2780, 0x3f80); |
||
157 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x0, 0xc000); |
||
158 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x039f0000, 0x03000000 | 0x039f0000); |
||
159 | } else if (Mode->SynthClock <= 60000) { |
||
160 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x2780, 0x3f80); |
||
161 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x0, 0xc000); |
||
162 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x024f0000, 0x03000000 | 0x024f0000); |
||
163 | } else { |
||
164 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x0980, 0x3f80); |
||
165 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x0, 0xc000); |
||
166 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x01270000, 0x03000000 | 0x01270000); |
||
167 | } |
||
168 | break; |
||
169 | case 0x791F: |
||
170 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x0980, 0x3f80); |
||
171 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x4000, 0xc000); |
||
172 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x00ac0000, 0x03000000 | 0x00ac0000); |
||
173 | if (Private->CapabilityFlag & 0x10) { |
||
174 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x0, 0xc000); |
||
175 | if (Mode->SynthClock <= 6500) |
||
176 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x01ac0000, 0x03ff0000); |
||
177 | else |
||
178 | RHDRegMaskD(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x01110000, 0x03ff0000); |
||
179 | } |
||
180 | break; |
||
181 | } |
||
182 | usleep (1); |
||
183 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x04000000, 0x04000000); |
||
184 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x60, 0x60); |
||
185 | usleep(30); |
||
186 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x01, 0x01); |
||
187 | usleep(1); |
||
188 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x02, 0x02); |
||
189 | usleep(1); |
||
190 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x04, 0x04); |
||
191 | usleep(1); |
||
192 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x08, 0x08); |
||
193 | usleep(1); |
||
194 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x10, 0x10); |
||
195 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL1, 0x0, 0xf); |
||
196 | |||
197 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL2, 0x0180, 0x0180); |
||
198 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL2, 0x600, 0x600); |
||
199 | usleep(5); |
||
200 | RHDRegMask(Output, RS69_DDIA_PCIE_PHY_CONTROL2, 0x0, 0x600); |
||
201 | |||
202 | /* hw reset will be turned off at POWER_ON */ |
||
203 | |||
204 | /* select crtc source, sync_a, no stereosync */ |
||
205 | RHDRegMask(Output, RS69_DDIA_SOURCE_SELECT, Output->Crtc->Id, |
||
206 | RS69_DDIA_SOURCE_SELECT_BIT |
||
207 | | RS69_DDIA_SYNC_SELECT |
||
208 | | RS69_DDIA_STEREOSYNC_SELECT); |
||
209 | } |
||
210 | |||
211 | /* |
||
212 | * |
||
213 | */ |
||
214 | static void |
||
215 | DDIAPower(struct rhdOutput *Output, int Power) |
||
216 | { |
||
217 | RHDDebug(Output->scrnIndex, "%s(%s,%s)\n",__func__,Output->Name, |
||
218 | rhdPowerString[Power]); |
||
219 | |||
220 | switch (Power) { |
||
221 | case RHD_POWER_ON: |
||
222 | RHDRegMask(Output, RS69_DDIA_PATH_CONTROL, RS69_DDIA_PIXVLD_RESET, |
||
223 | RS69_DDIA_PIXVLD_RESET); |
||
224 | RHDRegWrite(Output, RS69_DDIA_BIT_DEPTH_CONTROL, 0); |
||
225 | RHDRegMask(Output, RS69_DDIA_BIT_DEPTH_CONTROL, |
||
226 | RS69_DDIA_TEMPORAL_DITHER_RESET, RS69_DDIA_TEMPORAL_DITHER_RESET); |
||
227 | RHDRegMask(Output, RS69_DDIA_BIT_DEPTH_CONTROL, |
||
228 | 0, RS69_DDIA_TEMPORAL_DITHER_RESET); |
||
229 | RHDRegMask(Output, RS69_DDIA_CNTL, RS69_DDIA_ENABLE, RS69_DDIA_ENABLE); |
||
230 | RHDRegMask(Output, RS69_DDIA_PATH_CONTROL, 0, RS69_DDIA_PIXVLD_RESET); |
||
231 | return; |
||
232 | case RHD_POWER_RESET: |
||
233 | RHDRegMask(Output, RS69_DDIA_CNTL, 0, RS69_DDIA_ENABLE); |
||
234 | return; |
||
235 | case RHD_POWER_SHUTDOWN: |
||
236 | RHDRegMask(Output, RS69_DDIA_BIT_DEPTH_CONTROL, |
||
237 | RS69_DDIA_TEMPORAL_DITHER_RESET, RS69_DDIA_TEMPORAL_DITHER_RESET); |
||
238 | RHDRegMask(Output, RS69_DDIA_BIT_DEPTH_CONTROL, |
||
239 | 0, RS69_DDIA_TEMPORAL_DITHER_RESET); |
||
240 | RHDRegMask(Output, RS69_DDIA_BIT_DEPTH_CONTROL, |
||
241 | 0, |
||
242 | RS69_DDIA_TRUNCATE_EN |
||
243 | | RS69_DDIA_TRUNCATE_DEPTH |
||
244 | | RS69_DDIA_SPATIAL_DITHER_EN |
||
245 | | RS69_DDIA_SPATIAL_DITHER_DEPTH); |
||
246 | RHDRegMask(Output, RS69_DDIA_BIT_DEPTH_CONTROL, |
||
247 | 0, |
||
248 | RS69_DDIA_TEMPORAL_DITHER_EN |
||
249 | | RS69_DDIA_TEMPORAL_DITHER_EN |
||
250 | | RS69_DDIA_TEMPORAL_DITHER_DEPTH |
||
251 | | RS69_DDIA_TEMPORAL_LEVEL); |
||
252 | RHDRegMask(Output, RS69_DDIA_CNTL, 0, RS69_DDIA_ENABLE); |
||
253 | return; |
||
254 | default: |
||
255 | return; |
||
256 | } |
||
257 | } |
||
258 | |||
259 | /* |
||
260 | * |
||
261 | */ |
||
262 | static void |
||
263 | DDIASave(struct rhdOutput *Output) |
||
264 | { |
||
265 | struct DDIAPrivate *Private = (struct DDIAPrivate *)Output->Private; |
||
266 | |||
267 | RHDFUNC(Output); |
||
268 | |||
269 | Private->DdiaPathControl = RHDRegRead(Output, RS69_DDIA_PATH_CONTROL); |
||
270 | Private->DdiaCntl = RHDRegRead(Output, RS69_DDIA_CNTL); |
||
271 | Private->DdiaDcbalancerControl = RHDRegRead(Output, RS69_DDIA_DCBALANCER_CONTROL); |
||
272 | Private->DdiaPcieLinkControl2 = RHDRegRead(Output, RS69_DDIA_PCIE_LINK_CONTROL2); |
||
273 | Private->DdiaBitDepthControl = RHDRegRead(Output, RS69_DDIA_BIT_DEPTH_CONTROL); |
||
274 | |||
275 | Private->Stored = TRUE; |
||
276 | } |
||
277 | |||
278 | /* |
||
279 | * |
||
280 | */ |
||
281 | static void |
||
282 | DDIARestore(struct rhdOutput *Output) |
||
283 | { |
||
284 | struct DDIAPrivate *Private = (struct DDIAPrivate *)Output->Private; |
||
285 | RHDFUNC(Output); |
||
286 | |||
287 | if (!Private->Stored) |
||
288 | return; |
||
289 | |||
290 | /* disalbe */ |
||
291 | RHDRegMask(Output, RS69_DDIA_CNTL, 0, RS69_DDIA_ENABLE); |
||
292 | /* reset on */ |
||
293 | RHDRegMask(Output, RS69_DDIA_PATH_CONTROL, RS69_DDIA_PIXVLD_RESET, RS69_DDIA_PIXVLD_RESET); |
||
294 | RHDRegWrite(Output, RS69_DDIA_PATH_CONTROL, Private->DdiaPathControl | RS69_DDIA_PIXVLD_RESET); |
||
295 | |||
296 | RHDRegWrite(Output, RS69_DDIA_BIT_DEPTH_CONTROL, Private->DdiaBitDepthControl); |
||
297 | /* temporal dither reset on */ |
||
298 | RHDRegWrite(Output, RS69_DDIA_BIT_DEPTH_CONTROL, Private->DdiaBitDepthControl |
||
299 | | RS69_DDIA_TEMPORAL_DITHER_RESET); |
||
300 | /* temporal dither reset off */ |
||
301 | RHDRegWrite(Output, RS69_DDIA_BIT_DEPTH_CONTROL, Private->DdiaBitDepthControl); |
||
302 | |||
303 | RHDRegWrite(Output, RS69_DDIA_DCBALANCER_CONTROL, Private->DdiaDcbalancerControl); |
||
304 | RHDRegWrite(Output, RS69_DDIA_PCIE_LINK_CONTROL2, Private->DdiaPcieLinkControl2); |
||
305 | /* enable if enabled at startup */ |
||
306 | RHDRegWrite(Output, RS69_DDIA_CNTL, Private->DdiaCntl); |
||
307 | /* reset off */ |
||
308 | RHDRegWrite(Output, RS69_DDIA_PATH_CONTROL, Private->DdiaPathControl); |
||
309 | } |
||
310 | |||
311 | /* |
||
312 | * |
||
313 | */ |
||
314 | static void |
||
315 | DDIADestroy(struct rhdOutput *Output) |
||
316 | { |
||
317 | struct DDIAPrivate *Private = (struct DDIAPrivate *)Output->Private; |
||
318 | |||
319 | RHDFUNC(Output); |
||
320 | |||
321 | xfree(Private); |
||
322 | Output->Private = NULL; |
||
323 | } |
||
324 | |||
325 | /* |
||
326 | * |
||
327 | */ |
||
328 | struct rhdOutput * |
||
329 | RHDDDIAInit(RHDPtr rhdPtr) |
||
330 | { |
||
331 | #ifdef ATOM_BIOS |
||
332 | struct rhdOutput *Output; |
||
333 | struct DDIAPrivate *Private; |
||
334 | AtomBiosArgRec data; |
||
335 | |||
336 | RHDFUNC(rhdPtr); |
||
337 | |||
338 | /* |
||
339 | * This needs to be handled separately |
||
340 | * for now we only deal with it here. |
||
341 | */ |
||
342 | if (rhdPtr->ChipSet < RHD_RS600 || rhdPtr->ChipSet >= RHD_RS740) |
||
343 | return FALSE; |
||
344 | |||
345 | Output = xnfcalloc(sizeof(struct rhdOutput), 1); |
||
346 | |||
347 | Output->Name = "DDIA"; |
||
348 | |||
349 | Output->scrnIndex = rhdPtr->scrnIndex; |
||
350 | Output->Id = RHD_OUTPUT_DVO; |
||
351 | |||
352 | Output->Sense = NULL; |
||
353 | Output->ModeValid = DDIAModeValid; |
||
354 | Output->Mode = DDIAMode; |
||
355 | Output->Power = DDIAPower; |
||
356 | Output->Save = DDIASave; |
||
357 | Output->Restore = DDIARestore; |
||
358 | Output->Destroy = DDIADestroy; |
||
359 | |||
360 | Private = xnfcalloc(1, sizeof(struct DDIAPrivate)); |
||
361 | Output->Private = Private; |
||
362 | Private->Stored = FALSE; |
||
363 | |||
364 | if (RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS, |
||
365 | ATOM_GET_PCIENB_CFG_REG7, &data) == ATOM_SUCCESS) { |
||
366 | Private->PcieCfgReg7 = data.val; |
||
367 | } else { |
||
368 | xf86DrvMsg(Output->scrnIndex, X_ERROR, "Retrieval of PCIE MUX values failed. " |
||
369 | "no DDIA block support available\n"); |
||
370 | goto error; |
||
371 | } |
||
372 | if (RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS, |
||
373 | ATOM_GET_CAPABILITY_FLAG, &data) == ATOM_SUCCESS) { |
||
374 | Private->CapabilityFlag = data.val; |
||
375 | } else { |
||
376 | xf86DrvMsg(Output->scrnIndex, X_ERROR, "Retrieval of Capability flag failed. " |
||
377 | "no DDIA block support available\n"); |
||
378 | goto error; |
||
379 | } |
||
380 | |||
381 | return Output; |
||
382 | error: |
||
383 | xfree(Private); |
||
384 | return NULL; |
||
385 | |||
386 | #else |
||
387 | return NULL; |
||
388 | #endif |
||
389 | }>=>=>=>><>><>><>><>><>><>><>><>><>><>><>> |