vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / accelerants / ati / mach64_mode.cpp
blob57036d3010f230962eab2b53a4665482c972b830
1 /*
2 Haiku ATI video driver adapted from the X.org ATI driver.
4 Copyright 1992,1993,1994,1995,1996,1997 by Kevin E. Martin, Chapel Hill, North Carolina.
5 Copyright 1997 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org
7 Copyright 2009 Haiku, Inc. All rights reserved.
8 Distributed under the terms of the MIT license.
10 Authors:
11 Gerald Zajac 2009
15 #include "accelerant.h"
16 #include "mach64.h"
18 #include <unistd.h>
23 static void SetClockRegisters(const DisplayModeEx& mode)
25 SharedInfo& si = *gInfo.sharedInfo;
26 M64_Params& params = si.m64Params;
28 int p;
29 int postDiv;
30 bool extendedDiv = false;
31 uint32 pixelClock = mode.timing.pixel_clock;
33 if (pixelClock > params.maxPixelClock)
34 pixelClock = params.maxPixelClock;
36 double q = ((pixelClock / 10.0) * params.refDivider) / (2.0 * params.refFreq);
38 if (si.chipType >= MACH64_264VTB) {
39 if (q > 255) {
40 TRACE("SetClockRegisters(): Warning: q > 255\n");
41 q = 255;
42 p = 0;
43 postDiv = 1;
44 } else if (q > 127.5) {
45 p = 0;
46 postDiv = 1;
47 } else if (q > 85) {
48 p = 1;
49 postDiv = 2;
50 } else if (q > 63.75) {
51 p = 0;
52 postDiv = 3;
53 extendedDiv = true;
54 } else if (q > 42.5) {
55 p = 2;
56 postDiv = 4;
57 } else if (q > 31.875) {
58 p = 2;
59 postDiv = 6;
60 extendedDiv = true;
61 } else if (q > 21.25) {
62 p = 3;
63 postDiv = 8;
64 } else if (q >= 10.6666666667) {
65 p = 3;
66 postDiv = 12;
67 extendedDiv = true;
68 } else {
69 TRACE("SetClockRegisters(): Warning: q < 10.66666667\n");
70 p = 3;
71 postDiv = 12;
72 extendedDiv = true;
74 } else {
75 if (q > 255) {
76 TRACE("SetClockRegisters(): Warning: q > 255\n");
77 q = 255;
78 p = 0;
79 } else if (q > 127.5)
80 p = 0;
81 else if (q > 63.75)
82 p = 1;
83 else if (q > 31.875)
84 p = 2;
85 else if (q >= 16)
86 p = 3;
87 else {
88 TRACE("SetClockRegisters(): Warning: q < 16\n");
89 p = 3;
91 postDiv = 1 << p;
94 uint8 fbDiv = uint8(q * postDiv);
96 // With some chips such as those with ID's 4750 & 475A, the display has
97 // ripples when using resolution 1440x900 at 60 Hz refresh rate.
98 // Decrementing fbDiv by 1 seems to fix this problem.
100 if (mode.timing.h_display == 1440 && pixelClock < 108000)
101 fbDiv--;
103 int clkNum = params.clockNumberToProgram;
105 OUTREG8(CLOCK_CNTL, clkNum | CLOCK_STROBE);
107 // Temporarily switch to accelerator mode.
108 uint32 crtc_gen_cntl = INREG(CRTC_GEN_CNTL);
109 if (!(crtc_gen_cntl & CRTC_EXT_DISP_EN))
110 OUTREG(CRTC_GEN_CNTL, crtc_gen_cntl | CRTC_EXT_DISP_EN);
112 // Reset VCLK generator.
113 uint8 vclkCntl = Mach64_GetPLLReg(PLL_VCLK_CNTL);
114 Mach64_SetPLLReg(PLL_VCLK_CNTL, vclkCntl | PLL_VCLK_RESET);
116 // Set post-divider.
117 uint8 tmp = Mach64_GetPLLReg(PLL_VCLK_POST_DIV);
118 Mach64_SetPLLReg(PLL_VCLK_POST_DIV,
119 (tmp & ~(0x03 << (2 * clkNum))) | (p << (2 * clkNum)));
121 // Set feedback divider.
122 Mach64_SetPLLReg(PLL_VCLK0_FB_DIV + clkNum, fbDiv);
124 // Set extended post-divider.
125 if (si.chipType >= MACH64_264VTB) {
126 tmp = Mach64_GetPLLReg(PLL_XCLK_CNTL);
127 if (extendedDiv)
128 Mach64_SetPLLReg(PLL_XCLK_CNTL, tmp | (0x10 << clkNum));
129 else
130 Mach64_SetPLLReg(PLL_XCLK_CNTL, tmp & ~(0x10 << clkNum));
133 // End VCLK generator reset.
134 Mach64_SetPLLReg(PLL_VCLK_CNTL, vclkCntl & ~PLL_VCLK_RESET);
136 snooze(5000);
137 INREG8(DAC_W_INDEX); // Clear DAC counter
139 if (!(crtc_gen_cntl & CRTC_EXT_DISP_EN))
140 OUTREG(CRTC_GEN_CNTL, crtc_gen_cntl); // Restore register
142 // Save parameters that will be used for computing the DSP parameters.
144 params.vClkPostDivider = postDiv;
145 params.vClkFeedbackDivider = fbDiv;
147 return;
151 static void
152 SetDSPRegisters(const DisplayModeEx& mode)
154 // Set up DSP register values for a VTB or later.
156 SharedInfo& si = *gInfo.sharedInfo;
157 M64_Params& params = si.m64Params;
159 #define Maximum_DSP_PRECISION 7
161 uint8 mClkFeedbackDivider = Mach64_GetPLLReg(PLL_MCLK_FB_DIV);
163 /* Compute a memory-to-screen bandwidth ratio */
164 uint32 multiplier = uint32(mClkFeedbackDivider) * params.vClkPostDivider;
165 uint32 divider = uint32(params.vClkFeedbackDivider) * params.xClkRefDivider;
166 divider *= ((mode.bitsPerPixel + 1) / 4);
168 // Start by assuming a display FIFO width of 64 bits.
170 int vshift = (6 - 2) - params.xClkPostDivider;
172 int RASMultiplier = params.xClkMaxRASDelay;
173 int RASDivider = 1;
175 // Determine dsp_precision first.
177 int tmp = Mach64_Divide(multiplier * params.displayFIFODepth, divider, vshift, -1);
178 int dsp_precision;
180 for (dsp_precision = -5; tmp; dsp_precision++)
181 tmp >>= 1;
182 if (dsp_precision < 0)
183 dsp_precision = 0;
184 else if (dsp_precision > Maximum_DSP_PRECISION)
185 dsp_precision = Maximum_DSP_PRECISION;
187 int xshift = 6 - dsp_precision;
188 vshift += xshift;
190 int dsp_off = Mach64_Divide(multiplier * (params.displayFIFODepth - 1),
191 divider, vshift, -1) - Mach64_Divide(1, 1, vshift - xshift, 1);
193 int dsp_on = Mach64_Divide(multiplier, divider, vshift, 1);
194 tmp = Mach64_Divide(RASMultiplier, RASDivider, xshift, 1);
195 if (dsp_on < tmp)
196 dsp_on = tmp;
197 dsp_on += (tmp * 2) +
198 Mach64_Divide(params.xClkPageFaultDelay, 1, xshift, 1);
200 /* Calculate rounding factor and apply it to dsp_on */
201 tmp = ((1 << (Maximum_DSP_PRECISION - dsp_precision)) - 1) >> 1;
202 dsp_on = ((dsp_on + tmp) / (tmp + 1)) * (tmp + 1);
204 if (dsp_on >= ((dsp_off / (tmp + 1)) * (tmp + 1))) {
205 dsp_on = dsp_off - Mach64_Divide(multiplier, divider, vshift, -1);
206 dsp_on = (dsp_on / (tmp + 1)) * (tmp + 1);
209 int dsp_xclks = Mach64_Divide(multiplier, divider, vshift + 5, 1);
211 // Build DSP register contents.
213 uint32 dsp_on_off = SetBits(dsp_on, DSP_ON)
214 | SetBits(dsp_off, DSP_OFF);
215 uint32 dsp_config = SetBits(dsp_precision, DSP_PRECISION)
216 | SetBits(dsp_xclks, DSP_XCLKS_PER_QW)
217 | SetBits(params.displayLoopLatency, DSP_LOOP_LATENCY);
219 OUTREG(DSP_ON_OFF, dsp_on_off);
220 OUTREG(DSP_CONFIG, dsp_config);
224 static void
225 SetCrtcRegisters(const DisplayModeEx& mode)
227 // Calculate the CRTC register values for requested video mode, and then set
228 // set the registers to the calculated values.
230 SharedInfo& si = *gInfo.sharedInfo;
232 uint32 crtc_h_total_disp = ((mode.timing.h_total / 8) - 1)
233 | (((mode.timing.h_display / 8) - 1) << 16);
235 int hSyncWidth = (mode.timing.h_sync_end - mode.timing.h_sync_start) / 8;
236 if (hSyncWidth > 0x3f)
237 hSyncWidth = 0x3f;
239 int hSyncStart = mode.timing.h_sync_start / 8 - 1;
241 uint32 crtc_h_sync_strt_wid = (hSyncWidth << 16)
242 | (hSyncStart & 0xff) | ((hSyncStart & 0x100) << 4)
243 | ((mode.timing.flags & B_POSITIVE_HSYNC) ? 0 : CRTC_H_SYNC_NEG);
245 uint32 crtc_v_total_disp = ((mode.timing.v_total - 1)
246 | ((mode.timing.v_display - 1) << 16));
248 int vSyncWidth = mode.timing.v_sync_end - mode.timing.v_sync_start;
249 if (vSyncWidth > 0x1f)
250 vSyncWidth = 0x1f;
252 uint32 crtc_v_sync_strt_wid = (mode.timing.v_sync_start - 1)
253 | (vSyncWidth << 16)
254 | ((mode.timing.flags & B_POSITIVE_VSYNC) ? 0 : CRTC_V_SYNC_NEG);
256 uint32 crtc_off_pitch = SetBits(mode.timing.h_display >> 3, CRTC_PITCH);
258 uint32 crtc_gen_cntl = INREG(CRTC_GEN_CNTL) &
259 ~(CRTC_DBL_SCAN_EN | CRTC_INTERLACE_EN |
260 CRTC_HSYNC_DIS | CRTC_VSYNC_DIS | CRTC_CSYNC_EN |
261 CRTC_PIX_BY_2_EN | CRTC_VGA_XOVERSCAN |
262 CRTC_PIX_WIDTH | CRTC_BYTE_PIX_ORDER |
263 CRTC_VGA_128KAP_PAGING | CRTC_VFC_SYNC_TRISTATE |
264 CRTC_LOCK_REGS | // already off, but ...
265 CRTC_SYNC_TRISTATE | CRTC_DISP_REQ_EN |
266 CRTC_VGA_TEXT_132 | CRTC_CUR_B_TEST);
268 crtc_gen_cntl |= CRTC_EXT_DISP_EN | CRTC_EN | CRTC_VGA_LINEAR | CRTC_CNT_EN;
270 switch (mode.bitsPerPixel) {
271 case 8:
272 crtc_gen_cntl |= CRTC_PIX_WIDTH_8BPP;
273 break;
274 case 15:
275 crtc_gen_cntl |= CRTC_PIX_WIDTH_15BPP;
276 break;
277 case 16:
278 crtc_gen_cntl |= CRTC_PIX_WIDTH_16BPP;
279 break;
280 case 32:
281 crtc_gen_cntl |= CRTC_PIX_WIDTH_32BPP;
282 break;
283 default:
284 TRACE("Undefined color depth, bitsPerPixel: %d\n", mode.bitsPerPixel);
285 break;
288 // For now, set display FIFO low water mark as high as possible.
289 if (si.chipType < MACH64_264VTB)
290 crtc_gen_cntl |= CRTC_FIFO_LWM;
293 // Write the CRTC registers.
294 //--------------------------
296 // Stop CRTC.
297 OUTREG(CRTC_GEN_CNTL, crtc_gen_cntl & ~(CRTC_EXT_DISP_EN | CRTC_EN));
299 OUTREG(CRTC_H_TOTAL_DISP, crtc_h_total_disp);
300 OUTREG(CRTC_H_SYNC_STRT_WID, crtc_h_sync_strt_wid);
301 OUTREG(CRTC_V_TOTAL_DISP, crtc_v_total_disp);
302 OUTREG(CRTC_V_SYNC_STRT_WID, crtc_v_sync_strt_wid);
304 OUTREG(CRTC_OFF_PITCH, crtc_off_pitch);
306 // Clear overscan registers.
307 OUTREG(OVR_CLR, 0);
308 OUTREG(OVR_WID_LEFT_RIGHT, 0);
309 OUTREG(OVR_WID_TOP_BOTTOM, 0);
311 // Finalise CRTC setup and turn on the screen.
312 OUTREG(CRTC_GEN_CNTL, crtc_gen_cntl);
314 return;
319 status_t
320 Mach64_SetDisplayMode(const DisplayModeEx& mode)
322 // The code to actually configure the display.
323 // All the error checking must be done in ProposeDisplayMode(),
324 // and assume that the mode values we get here are acceptable.
326 SharedInfo& si = *gInfo.sharedInfo;
328 if (si.displayType == MT_VGA) {
329 // Chip is connected to a monitor via a VGA connector.
331 SetCrtcRegisters(mode);
332 SetClockRegisters(mode);
334 if (si.chipType >= MACH64_264VTB)
335 SetDSPRegisters(mode);
337 } else {
338 // Chip is connected to a laptop LCD monitor; or via a DVI interface.
340 uint16 vesaMode = GetVesaModeNumber(display_mode(mode), mode.bitsPerPixel);
341 if (vesaMode == 0)
342 return B_BAD_VALUE;
344 status_t status = ioctl(gInfo.deviceFileDesc, ATI_SET_VESA_DISPLAY_MODE,
345 &vesaMode, sizeof(vesaMode));
346 if (status != B_OK)
347 return status;
350 Mach64_AdjustFrame(mode);
352 // Initialize the palette so that the various color depths will display
353 // the correct colors.
355 OUTREGM(DAC_CNTL, DAC_8BIT_EN, DAC_8BIT_EN);
356 OUTREG8(DAC_MASK, 0xff);
357 OUTREG8(DAC_W_INDEX, 0); // initial color index
359 for (int i = 0; i < 256; i++) {
360 OUTREG8(DAC_DATA, i);
361 OUTREG8(DAC_DATA, i);
362 OUTREG8(DAC_DATA, i);
365 Mach64_EngineInit(mode);
367 return B_OK;
372 void
373 Mach64_AdjustFrame(const DisplayModeEx& mode)
375 // Adjust start address in frame buffer.
377 SharedInfo& si = *gInfo.sharedInfo;
379 int address = (mode.v_display_start * mode.virtual_width
380 + mode.h_display_start) * ((mode.bitsPerPixel + 1) / 8);
382 address &= ~0x07;
383 address += si.frameBufferOffset;
385 OUTREGM(CRTC_OFF_PITCH, address, 0xfffff);
386 return;
390 void
391 Mach64_SetIndexedColors(uint count, uint8 first, uint8* colorData, uint32 flags)
393 // Set the indexed color palette for 8-bit color depth mode.
395 (void)flags; // avoid compiler warning for unused arg
397 if (gInfo.sharedInfo->displayMode.space != B_CMAP8)
398 return ;
400 OUTREG8(DAC_MASK, 0xff);
401 OUTREG8(DAC_W_INDEX, first); // initial color index
403 while (count--) {
404 OUTREG8(DAC_DATA, colorData[0]); // red
405 OUTREG8(DAC_DATA, colorData[1]); // green
406 OUTREG8(DAC_DATA, colorData[2]); // blue
408 colorData += 3;