tcp: Add APICall trace entry and move TRACEs into locked parts.
[haiku.git] / src / add-ons / accelerants / nvidia / SetDisplayMode.c
blob0f4e4fab86554f1ddf9c62819a59275cb11d62a8
2 /*
3 Copyright 1999, Be Incorporated. All Rights Reserved.
4 This file may be used under the terms of the Be Sample Code License.
6 Other authors:
7 Mark Watson,
8 Apsed,
9 Rudolf Cornelissen 11/2002-3/2009
12 #define MODULE_BIT 0x00200000
14 #include "acc_std.h"
16 /* First validate the mode, then call lots of bit banging stuff to set the mode(s)! */
17 status_t SET_DISPLAY_MODE(display_mode *mode_to_set)
19 /* BOUNDS WARNING:
20 * It's impossible to deviate whatever small amount in a display_mode if the lower
21 * and upper limits are the same!
22 * Besides:
23 * BeOS (tested R5.0.3PE) is failing BWindowScreen::SetFrameBuffer() if PROPOSEMODE
24 * returns B_BAD_VALUE!
25 * Which means PROPOSEMODE should not return that on anything except on
26 * deviations for:
27 * display_mode.virtual_width;
28 * display_mode.virtual_height;
29 * display_mode.timing.h_display;
30 * display_mode.timing.v_display;
31 * So:
32 * We don't use bounds here by making sure bounds and target are the same struct!
33 * (See the call to PROPOSE_DISPLAY_MODE below) */
34 display_mode /*bounds,*/ target;
36 uint8 colour_depth1 = 32;
37 uint32 startadd,startadd_right;
38 // bool crt1, crt2, cross;
40 /* Adjust mode to valid one and fail if invalid */
41 target /*= bounds*/ = *mode_to_set;
42 /* show the mode bits */
43 LOG(1, ("SETMODE: (ENTER) initial modeflags: $%08x\n", target.flags));
44 LOG(1, ("SETMODE: requested target pixelclock %dkHz\n", target.timing.pixel_clock));
45 LOG(1, ("SETMODE: requested virtual_width %d, virtual_height %d\n",
46 target.virtual_width, target.virtual_height));
48 /* See BOUNDS WARNING above... */
49 if (PROPOSE_DISPLAY_MODE(&target, &target, &target) == B_ERROR) return B_ERROR;
51 /* make sure a possible 3D add-on will block rendering and re-initialize itself.
52 * note: update in _this_ order only */
53 /* SET_DISPLAY_MODE will reset this flag when it's done. */
54 si->engine.threeD.mode_changing = true;
55 /* every 3D add-on will reset this bit-flag when it's done. */
56 si->engine.threeD.newmode = 0xffffffff;
57 /* every 3D clone needs to reclaim a slot.
58 * note: this also cleans up reserved channels for killed 3D clones.. */
59 si->engine.threeD.clones = 0x00000000;
61 /* disable interrupts using the kernel driver */
62 head1_interrupt_enable(false);
63 if (si->ps.secondary_head) head2_interrupt_enable(false);
65 /* disable TVout if supported */
66 if (si->ps.tvout) BT_stop_tvout();
68 /* turn off screen(s) _after_ TVout is disabled (if applicable) */
69 head1_dpms(false, false, false, true);
70 if (si->ps.secondary_head) head2_dpms(false, false, false, true);
71 if (si->ps.tvout) BT_dpms(false);
73 /*where in framebuffer the screen is (should this be dependant on previous MOVEDISPLAY?)*/
74 startadd = (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;
76 /* calculate and set new mode bytes_per_row */
77 nv_general_validate_pic_size (&target, &si->fbc.bytes_per_row, &si->acc_mode);
79 /*Perform the very long mode switch!*/
80 if (target.flags & DUALHEAD_BITS) /*if some dualhead mode*/
82 uint8 colour_depth2 = colour_depth1;
84 /* init display mode for secondary head */
85 display_mode target2 = target;
87 LOG(1,("SETMODE: setting DUALHEAD mode\n"));
89 /* validate flags for secondary TVout */
90 //fixme: remove or block on autodetect fail. (is now shutoff)
91 if ((0) && (target2.flags & TV_BITS))
93 target.flags &= ~TV_BITS;//still needed for some routines...
94 target2.flags &= ~TV_BITS;
95 LOG(1,("SETMODE: blocking TVout: no TVout cable connected!\n"));
98 /* detect which connectors have a CRT connected */
99 //fixme: 'hot-plugging' for analog monitors removed: remove code as well;
100 //or make it work with digital panels connected as well.
101 // crt1 = nv_dac_crt_connected();
102 // crt2 = nv_dac2_crt_connected();
103 /* connect outputs 'straight-through' */
104 // if (crt1)
105 // {
106 /* connector1 is used as primary output */
107 // cross = false;
108 // }
109 // else
110 // {
111 // if (crt2)
112 /* connector2 is used as primary output */
113 // cross = true;
114 // else
115 /* no CRT detected: assume connector1 is used as primary output */
116 // cross = false;
117 // }
118 /* set output connectors assignment if possible */
119 if ((target.flags & DUALHEAD_BITS) == DUALHEAD_SWITCH)
120 /* invert output assignment in switch mode */
121 nv_general_head_select(true);
122 else
123 nv_general_head_select(false);
125 /* set the pixel clock PLL(s) */
126 LOG(8,("SETMODE: target clock %dkHz\n",target.timing.pixel_clock));
127 if (head1_set_pix_pll(target) == B_ERROR)
128 LOG(8,("SETMODE: error setting pixel clock (internal DAC)\n"));
130 LOG(8,("SETMODE: target2 clock %dkHz\n",target2.timing.pixel_clock));
131 if (head2_set_pix_pll(target2) == B_ERROR)
132 LOG(8,("SETMODE: error setting pixel clock (DAC2)\n"));
134 /*set the colour depth for CRTC1 and the DAC */
135 switch(target.space)
137 case B_CMAP8:
138 colour_depth1 = 8;
139 head1_mode(BPP8, 1.0);
140 head1_depth(BPP8);
141 break;
142 case B_RGB15_LITTLE:
143 colour_depth1 = 16;
144 head1_mode(BPP15, 1.0);
145 head1_depth(BPP15);
146 break;
147 case B_RGB16_LITTLE:
148 colour_depth1 = 16;
149 head1_mode(BPP16, 1.0);
150 head1_depth(BPP16);
151 break;
152 case B_RGB32_LITTLE:
153 colour_depth1 = 32;
154 head1_mode(BPP32, 1.0);
155 head1_depth(BPP32);
156 break;
158 /*set the colour depth for CRTC2 and DAC2 */
159 switch(target2.space)
161 case B_CMAP8:
162 colour_depth2 = 8;
163 head2_mode(BPP8, 1.0);
164 head2_depth(BPP8);
165 break;
166 case B_RGB15_LITTLE:
167 colour_depth2 = 16;
168 head2_mode(BPP15, 1.0);
169 head2_depth(BPP15);
170 break;
171 case B_RGB16_LITTLE:
172 colour_depth2 = 16;
173 head2_mode(BPP16, 1.0);
174 head2_depth(BPP16);
175 break;
176 case B_RGB32_LITTLE:
177 colour_depth2 = 32;
178 head2_mode(BPP32, 1.0);
179 head2_depth(BPP32);
180 break;
183 /* check if we are doing interlaced TVout mode */
184 //fixme: we don't support interlaced mode?
185 si->interlaced_tv_mode = false;
187 /*set the display(s) pitches*/
188 head1_set_display_pitch ();
189 //fixme: seperate for real dualhead modes:
190 //we need a secondary si->fbc!
191 head2_set_display_pitch ();
193 /*work out where the "right" screen starts*/
194 startadd_right = startadd + (target.timing.h_display * (colour_depth1 >> 3));
196 /* Tell card what memory to display */
197 switch (target.flags & DUALHEAD_BITS)
199 case DUALHEAD_ON:
200 case DUALHEAD_SWITCH:
201 head1_set_display_start(startadd,colour_depth1);
202 head2_set_display_start(startadd_right,colour_depth2);
203 break;
204 case DUALHEAD_CLONE:
205 head1_set_display_start(startadd,colour_depth1);
206 head2_set_display_start(startadd,colour_depth2);
207 break;
210 /* set the timing */
211 head1_set_timing(target);
212 head2_set_timing(target2);
214 /* TVout support: program TVout encoder and modify CRTC timing */
215 if (si->ps.tvout && (target2.flags & TV_BITS)) BT_setmode(target2);
217 else /* single head mode */
219 int colour_mode = BPP32;
221 /* connect output */
222 if (si->ps.secondary_head)
224 /* detect which connectors have a CRT connected */
225 //fixme: 'hot-plugging' for analog monitors removed: remove code as well;
226 //or make it work with digital panels connected as well.
227 // crt1 = nv_dac_crt_connected();
228 // crt2 = nv_dac2_crt_connected();
229 /* connect outputs 'straight-through' */
230 // if (crt1)
231 // {
232 /* connector1 is used as primary output */
233 // cross = false;
234 // }
235 // else
236 // {
237 // if (crt2)
238 /* connector2 is used as primary output */
239 // cross = true;
240 // else
241 /* no CRT detected: assume connector1 is used as primary output */
242 // cross = false;
243 // }
244 /* set output connectors assignment if possible */
245 nv_general_head_select(false);
248 switch(target.space)
250 case B_CMAP8: colour_depth1 = 8; colour_mode = BPP8; break;
251 case B_RGB15_LITTLE: colour_depth1 = 16; colour_mode = BPP15; break;
252 case B_RGB16_LITTLE: colour_depth1 = 16; colour_mode = BPP16; break;
253 case B_RGB32_LITTLE: colour_depth1 = 32; colour_mode = BPP32; break;
254 default:
255 LOG(8,("SETMODE: Invalid singlehead colour depth 0x%08x\n", target.space));
256 return B_ERROR;
259 /* set the pixel clock PLL */
260 if (head1_set_pix_pll(target) == B_ERROR)
261 LOG(8,("CRTC: error setting pixel clock (internal DAC)\n"));
263 /* set the colour depth for CRTC1 and the DAC */
264 /* first set the colordepth */
265 head1_depth(colour_mode);
266 /* then(!) program the PAL (<8bit colordepth does not support 8bit PAL) */
267 head1_mode(colour_mode,1.0);
269 /* set the display pitch */
270 head1_set_display_pitch();
272 /* tell the card what memory to display */
273 head1_set_display_start(startadd,colour_depth1);
275 /* set the timing */
276 head1_set_timing(target);
278 /* TVout support: program TVout encoder and modify CRTC timing */
279 if (si->ps.tvout && (target.flags & TV_BITS)) BT_setmode(target);
281 //fixme: shut-off the videoPLL if it exists...
284 /* update driver's mode store */
285 si->dm = target;
287 /* update FIFO data fetching according to mode */
288 nv_crtc_update_fifo();
289 if (si->ps.secondary_head) nv_crtc2_update_fifo();
291 /* set up acceleration for this mode */
292 /* note:
293 * Maybe later we can forget about non-DMA mode (depends on 3D acceleration
294 * attempts). */
295 if (!si->settings.block_acc) {
296 if (!si->settings.dma_acc)
297 nv_acc_init();
298 else
299 nv_acc_init_dma();
302 /* set up overlay unit for this mode */
303 nv_bes_init();
305 /* note freemem range */
306 /* first free adress follows hardcursor and workspace */
307 si->engine.threeD.mem_low = si->fbc.bytes_per_row * si->dm.virtual_height;
308 if (si->settings.hardcursor) si->engine.threeD.mem_low += 2048;
309 /* last free adress is end-of-ram minus max space needed for overlay bitmaps */
310 //fixme possible:
311 //if overlay buffers are allocated subtract buffersize from mem_high;
312 //only allocate overlay buffers if 3D is not in use. (block overlay during 3D)
313 si->engine.threeD.mem_high = si->ps.memory_size - 1;
314 /* Keep some extra distance as a workaround for certain bugs (see
315 * DriverInterface.h for an explanation). */
316 if (si->ps.card_arch < NV40A)
317 si->engine.threeD.mem_high -= PRE_NV40_OFFSET;
318 else
319 si->engine.threeD.mem_high -= NV40_PLUS_OFFSET;
321 si->engine.threeD.mem_high -= (MAXBUFFERS * 1024 * 1024 * 2); /* see overlay.c file */
323 /* restore screen(s) output state(s) */
324 SET_DPMS_MODE(si->dpms_flags);
326 /* enable interrupts using the kernel driver */
327 //fixme:
328 //add head2 once we use one driver instance 'per head' (instead of 'per card')
329 head1_interrupt_enable(true);
331 /* make sure a possible 3D add-on will re-initialize itself by signalling ready */
332 si->engine.threeD.mode_changing = false;
334 /* optimize memory-access if needed */
335 // head1_mem_priority(colour_depth1);
337 /* Tune RAM CAS-latency if needed. Must be done *here*! */
338 nv_set_cas_latency();
340 LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0));
342 return B_OK;
346 Set which pixel of the virtual frame buffer will show up in the
347 top left corner of the display device. Used for page-flipping
348 games and virtual desktops.
350 status_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start) {
351 uint8 colour_depth;
352 uint32 startadd,startadd_right;
354 LOG(4,("MOVE_DISPLAY: h %d, v %d\n", h_display_start, v_display_start));
356 /* nVidia cards support pixelprecise panning on both heads in all modes:
357 * No stepping granularity needed! */
359 /* determine bits used for the colordepth */
360 switch(si->dm.space)
362 case B_CMAP8:
363 colour_depth=8;
364 break;
365 case B_RGB15_LITTLE:
366 case B_RGB16_LITTLE:
367 colour_depth=16;
368 break;
369 case B_RGB24_LITTLE:
370 colour_depth=24;
371 break;
372 case B_RGB32_LITTLE:
373 colour_depth=32;
374 break;
375 default:
376 return B_ERROR;
379 /* do not run past end of display */
380 switch (si->dm.flags & DUALHEAD_BITS)
382 case DUALHEAD_ON:
383 case DUALHEAD_SWITCH:
384 if (((si->dm.timing.h_display * 2) + h_display_start) > si->dm.virtual_width)
385 return B_ERROR;
386 break;
387 default:
388 if ((si->dm.timing.h_display + h_display_start) > si->dm.virtual_width)
389 return B_ERROR;
390 break;
392 if ((si->dm.timing.v_display + v_display_start) > si->dm.virtual_height)
393 return B_ERROR;
395 /* everybody remember where we parked... */
396 si->dm.h_display_start = h_display_start;
397 si->dm.v_display_start = v_display_start;
399 /* actually set the registers */
400 //fixme: seperate both heads: we need a secondary si->fbc!
401 startadd = v_display_start * si->fbc.bytes_per_row;
402 startadd += h_display_start * (colour_depth >> 3);
403 startadd += (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;
404 startadd_right = startadd + si->dm.timing.h_display * (colour_depth >> 3);
406 /* disable interrupts using the kernel driver */
407 head1_interrupt_enable(false);
408 if (si->ps.secondary_head) head2_interrupt_enable(false);
410 switch (si->dm.flags & DUALHEAD_BITS)
412 case DUALHEAD_ON:
413 case DUALHEAD_SWITCH:
414 head1_set_display_start(startadd,colour_depth);
415 head2_set_display_start(startadd_right,colour_depth);
416 break;
417 case DUALHEAD_OFF:
418 head1_set_display_start(startadd,colour_depth);
419 break;
420 case DUALHEAD_CLONE:
421 head1_set_display_start(startadd,colour_depth);
422 head2_set_display_start(startadd,colour_depth);
423 break;
426 //fixme:
427 //add head2 once we use one driver instance 'per head' (instead of 'per card')
428 head1_interrupt_enable(true);
430 return B_OK;
433 /* Set the indexed color palette */
434 void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) {
435 int i;
436 uint8 *r,*g,*b;
438 /* Protect gamma correction when not in CMAP8 */
439 if (si->dm.space != B_CMAP8) return;
441 r=si->color_data;
442 g=r+256;
443 b=g+256;
445 i=first;
446 while (count--)
448 r[i]=*color_data++;
449 g[i]=*color_data++;
450 b[i]=*color_data++;
451 i++;
453 head1_palette(r,g,b);
454 if (si->dm.flags & DUALHEAD_BITS) head2_palette(r,g,b);
457 /* Put the display into one of the Display Power Management modes. */
458 status_t SET_DPMS_MODE(uint32 dpms_flags)
460 bool display, h1h, h1v, h2h, h2v, do_p1, do_p2;
462 /* disable interrupts using the kernel driver */
463 head1_interrupt_enable(false);
464 if (si->ps.secondary_head) head2_interrupt_enable(false);
466 LOG(4,("SET_DPMS_MODE: $%08x\n", dpms_flags));
468 /* note current DPMS state for our reference */
469 si->dpms_flags = dpms_flags;
471 /* preset: DPMS for panels should be executed */
472 do_p1 = do_p2 = true;
474 /* determine signals to send to head(s) */
475 display = h1h = h1v = h2h = h2v = true;
476 switch(dpms_flags)
478 case B_DPMS_ON: /* H: on, V: on, display on */
479 break;
480 case B_DPMS_STAND_BY:
481 display = h1h = h2h = false;
482 break;
483 case B_DPMS_SUSPEND:
484 display = h1v = h2v = false;
485 break;
486 case B_DPMS_OFF: /* H: off, V: off, display off */
487 display = h1h = h1v = h2h = h2v = false;
488 break;
489 default:
490 LOG(8,("SET: Invalid DPMS settings $%08x\n", dpms_flags));
491 //fixme:
492 //add head2 once we use one driver instance 'per head' (instead of 'per card')
493 head1_interrupt_enable(true);
495 return B_ERROR;
498 /* CRTC used for TVout needs specific DPMS programming */
499 if (si->dm.flags & TV_BITS)
501 /* TV_PRIMARY tells us that the head to be used with TVout is the head that's
502 * actually assigned as being the primary head at powerup:
503 * so non dualhead-mode-dependant, and not 'fixed' CRTC1! */
504 if (si->dm.flags & TV_PRIMARY)
506 LOG(4,("SET_DPMS_MODE: tuning primary head DPMS settings for TVout compatibility\n"));
508 if ((si->dm.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH)
510 if (!(si->settings.vga_on_tv))
512 /* block VGA output on head displaying on TV */
513 /* Note:
514 * this specific sync setting is required: Vsync is used to keep TVout
515 * synchronized to the CRTC 'vertically' (otherwise 'rolling' occurs).
516 * This leaves Hsync only for shutting off the VGA screen. */
517 h1h = false;
518 h1v = true;
519 /* block panel DPMS updates */
520 do_p1 = false;
522 else
524 /* when concurrent VGA is used alongside TVout on a head, DPMS is safest
525 * applied this way: Vsync is needed for stopping TVout successfully when
526 * a (new) modeswitch occurs.
527 * (see routine BT_stop_tvout() in nv_brooktreetv.c) */
528 /* Note:
529 * applying 'normal' DPMS here and forcing Vsync on in the above mentioned
530 * routine seems to not always be enough: sometimes image generation will
531 * not resume in that case. */
532 h1h = display;
533 h1v = true;
536 else
538 if (!(si->settings.vga_on_tv))
540 h2h = false;
541 h2v = true;
542 do_p2 = false;
544 else
546 h2h = display;
547 h2v = true;
551 else
553 LOG(4,("SET_DPMS_MODE: tuning secondary head DPMS settings for TVout compatibility\n"));
555 if ((si->dm.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH)
557 if (!(si->settings.vga_on_tv))
559 h2h = false;
560 h2v = true;
561 do_p2 = false;
563 else
565 h2h = display;
566 h2v = true;
569 else
571 if (!(si->settings.vga_on_tv))
573 h1h = false;
574 h1v = true;
575 do_p1 = false;
577 else
579 h1h = display;
580 h1v = true;
586 /* issue actual DPMS commands as far as applicable */
587 head1_dpms(display, h1h, h1v, do_p1);
588 if ((si->ps.secondary_head) && (si->dm.flags & DUALHEAD_BITS))
589 head2_dpms(display, h2h, h2v, do_p2);
590 if (si->dm.flags & TV_BITS)
591 BT_dpms(display);
593 //fixme:
594 //add head2 once we use one driver instance 'per head' (instead of 'per card')
595 head1_interrupt_enable(true);
597 return B_OK;
600 /* Report device DPMS capabilities */
601 uint32 DPMS_CAPABILITIES(void)
603 return (B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF);
606 /* Return the current DPMS mode */
607 uint32 DPMS_MODE(void)
609 return si->dpms_flags;