BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / accelerants / matrox / SetDisplayMode.c
blob6e73b0b467b1d8d631a17702bffee465c983789f
1 /*
2 Copyright 1999, Be Incorporated. All Rights Reserved.
3 This file may be used under the terms of the Be Sample Code License.
5 Other authors:
6 Mark Watson,
7 Apsed,
8 Rudolf Cornelissen 11/2002-5/2006
9 */
11 #define MODULE_BIT 0x00200000
13 #include "acc_std.h"
16 Enable/Disable interrupts. Just a wrapper around the
17 ioctl() to the kernel driver.
19 static void interrupt_enable(bool flag)
21 status_t result;
22 gx00_set_bool_state sbs;
24 if (si->ps.int_assigned)
26 /* set the magic number so the driver knows we're for real */
27 sbs.magic = GX00_PRIVATE_DATA_MAGIC;
28 sbs.do_it = flag;
29 /* contact driver and get a pointer to the registers and shared data */
30 result = ioctl(fd, GX00_RUN_INTERRUPTS, &sbs, sizeof(sbs));
34 /* First validate the mode, then call lots of bit banging stuff to set the mode(s)! */
35 status_t SET_DISPLAY_MODE(display_mode *mode_to_set)
37 /* BOUNDS WARNING:
38 * It's impossible to deviate whatever small amount in a display_mode if the lower
39 * and upper limits are the same!
40 * Besides:
41 * BeOS (tested R5.0.3PE) is failing BWindowScreen::SetFrameBuffer() if PROPOSEMODE
42 * returns B_BAD_VALUE!
43 * Which means PROPOSEMODE should not return that on anything except on
44 * deviations for:
45 * display_mode.virtual_width;
46 * display_mode.virtual_height;
47 * display_mode.timing.h_display;
48 * display_mode.timing.v_display;
49 * So:
50 * We don't use bounds here by making sure bounds and target are the same struct!
51 * (See the call to PROPOSE_DISPLAY_MODE below) */
52 display_mode /*bounds,*/ target;
54 uint8 colour_depth1 = 32;
55 status_t result;
56 uint32 startadd,startadd_right;
58 /* Adjust mode to valid one and fail if invalid */
59 target /*= bounds*/ = *mode_to_set;
60 /* show the mode bits */
61 LOG(1, ("SETMODE: (ENTER) initial modeflags: $%08x\n", target.flags));
62 LOG(1, ("SETMODE: requested target pixelclock %dkHz\n", target.timing.pixel_clock));
63 LOG(1, ("SETMODE: requested virtual_width %d, virtual_height %d\n",
64 target.virtual_width, target.virtual_height));
66 /* See BOUNDS WARNING above... */
67 if (PROPOSE_DISPLAY_MODE(&target, &target, &target) == B_ERROR) return B_ERROR;
69 /* overlay engine, cursor and MOVE_DISPLAY need to know the status even when
70 * in singlehead mode */
71 si->switched_crtcs = false;
73 /* disable interrupts using the kernel driver */
74 interrupt_enable(false);
76 /* then turn off screen(s) */
77 gx00_crtc_dpms(false, false, false);
78 if (si->ps.secondary_head)
80 g400_crtc2_dpms(false, false, false);
82 else
84 /* on singlehead cards with TVout program the MAVEN as well */
85 if (si->ps.tvout) gx00_maven_dpms(false, false, false);
88 /*where in framebuffer the screen is (should this be dependant on previous MOVEDISPLAY?)*/
89 startadd = (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;
91 /* calculate and set new mode bytes_per_row */
92 gx00_general_validate_pic_size (&target, &si->fbc.bytes_per_row, &si->acc_mode);
94 /*Perform the very long mode switch!*/
95 if (target.flags & DUALHEAD_BITS) /*if some dualhead mode*/
97 uint8 colour_depth2 = colour_depth1;
99 /* init display mode for secondary head */
100 display_mode target2 = target;
102 LOG(1,("SETMODE: setting DUALHEAD mode\n"));
104 /* set the pixel clock PLL(s) */
105 LOG(8,("SETMODE: target clock %dkHz\n",target.timing.pixel_clock));
106 if (gx00_dac_set_pix_pll(target) == B_ERROR)
107 LOG(8,("SETMODE: error setting pixel clock (internal DAC)\n"));
109 /* we do not need to set the pixelclock here for a head that's in TVout mode */
110 if (!(target2.flags & TV_BITS))
112 LOG(8,("SETMODE: target2 clock %dkHz\n",target2.timing.pixel_clock));
113 if (gx00_maven_set_vid_pll(target2) == B_ERROR)
114 LOG(8,("SETMODE: error setting pixel clock (MAVEN)\n"));
117 /*set the colour depth for CRTC1 and the DAC */
118 switch(target.space)
120 case B_RGB16_LITTLE:
121 colour_depth1 = 16;
122 gx00_dac_mode(BPP16, 1.0);
123 gx00_crtc_depth(BPP16);
124 break;
125 case B_RGB32_LITTLE:
126 colour_depth1 = 32;
127 gx00_dac_mode(BPP32, 1.0);
128 gx00_crtc_depth(BPP32);
129 break;
131 /*set the colour depth for CRTC2 and the MAVEN */
132 switch(target2.space)
134 case B_RGB16_LITTLE:
135 colour_depth2 = 16;
136 gx00_maven_mode(BPP16, 1.0);
137 g400_crtc2_depth(BPP16);
138 break;
139 case B_RGB32_LITTLE:
140 colour_depth2 = 32;
141 gx00_maven_mode(BPP32DIR, 1.0);
142 g400_crtc2_depth(BPP32DIR);
143 break;
146 /* check if we are doing interlaced TVout mode */
147 si->interlaced_tv_mode = false;
148 if ((target2.flags & TV_BITS) && (si->ps.card_type >= G450))
149 si->interlaced_tv_mode = true;
151 /*set the display(s) pitches*/
152 gx00_crtc_set_display_pitch ();
153 //fixme: seperate for real dualhead modes:
154 //we need a secondary si->fbc!
155 g400_crtc2_set_display_pitch ();
157 /*work out where the "right" screen starts*/
158 startadd_right=startadd+(target.timing.h_display * (colour_depth1 >> 3));
160 /* calculate needed MAVEN-CRTC delay: formula valid for straight-through CRTC's */
161 si->crtc_delay = 44;
162 if (colour_depth2 == 16) si->crtc_delay += 0;
164 /* set the outputs */
165 switch (si->ps.card_type)
167 case G400:
168 case G400MAX:
169 /* setup vertical timing adjust for crtc connected to the MAVEN:
170 * assuming connected straight through. */
171 /* (extra "blanking" line for MAVEN hardware design fault) */
172 target2.timing.v_display++;
174 switch (target.flags & DUALHEAD_BITS)
176 case DUALHEAD_ON:
177 case DUALHEAD_CLONE:
178 gx00_general_dac_select(DS_CRTC1DAC_CRTC2MAVEN);
179 si->switched_crtcs = false;
180 break;
181 case DUALHEAD_SWITCH:
182 if (i2c_sec_tv_adapter() == B_OK)
184 /* Don't switch CRTC's because MAVEN YUV is impossible then,
185 * and primary head output will be limited to 135Mhz pixelclock. */
186 LOG(4,("SETMODE: secondary TV-adapter detected, switching buffers\n"));
187 gx00_general_dac_select(DS_CRTC1DAC_CRTC2MAVEN);
188 si->switched_crtcs = true;
190 else
192 /* This limits the pixelclocks on both heads to 135Mhz,
193 * but you can use overlay on the other output now. */
194 LOG(4,("SETMODE: no secondary TV-adapter detected, switching CRTCs\n"));
195 gx00_general_dac_select(DS_CRTC1MAVEN_CRTC2DAC);
196 si->switched_crtcs = false;
197 /* re-calculate MAVEN-CRTC delay: formula valid for crossed CRTC's */
198 si->crtc_delay = 17;
199 if (colour_depth1 == 16) si->crtc_delay += 4;
200 /* re-setup vertical timing adjust for crtc connected to the MAVEN:
201 * cross connected. */
202 /* (extra "blanking" line for MAVEN hardware design fault) */
203 target.timing.v_display++;
204 target2.timing.v_display--;
206 break;
208 break;
209 case G450:
210 case G550:
211 if (!si->ps.primary_dvi)
212 /* output connector use is always 'straight-through' */
213 //fixme: re-evaluate when DVI is setup...
215 switch (target.flags & DUALHEAD_BITS)
217 case DUALHEAD_ON:
218 case DUALHEAD_CLONE:
219 gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2);
220 si->switched_crtcs = false;
221 break;
222 case DUALHEAD_SWITCH:
223 if (i2c_sec_tv_adapter() == B_OK)
225 /* Don't switch CRTC's because MAVEN YUV and TVout is impossible then,
226 * and primary head output will be limited to 235Mhz pixelclock. */
227 LOG(4,("SETMODE: secondary TV-adapter detected, switching buffers\n"));
228 gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2);
229 si->switched_crtcs = true;
231 else
233 /* This limits the pixelclocks on both heads to 235Mhz,
234 * but you can use overlay on the other output now. */
235 LOG(4,("SETMODE: no secondary TV-adapter detected, switching CRTCs\n"));
236 gx00_general_dac_select(DS_CRTC1CON2_CRTC2CON1);
237 si->switched_crtcs = false;
239 break;
242 else
243 /* output connector use is cross-linked if no TV cable connected! */
244 //fixme: re-evaluate when DVI is setup...
246 switch (target.flags & DUALHEAD_BITS)
248 case DUALHEAD_ON:
249 case DUALHEAD_CLONE:
250 if (i2c_sec_tv_adapter() == B_OK)
252 gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2);
253 si->switched_crtcs = false;
255 else
257 /* This limits the pixelclocks on both heads to 235Mhz,
258 * but you can use overlay on the other output now. */
259 gx00_general_dac_select(DS_CRTC1CON2_CRTC2CON1);
260 si->switched_crtcs = false;
262 break;
263 case DUALHEAD_SWITCH:
264 if (i2c_sec_tv_adapter() == B_OK)
266 /* Don't switch CRTC's because MAVEN YUV and TVout is impossible then,
267 * and primary head output will be limited to 235Mhz pixelclock. */
268 LOG(4,("SETMODE: secondary TV-adapter detected, switching buffers\n"));
269 gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2);
270 si->switched_crtcs = true;
272 else
274 LOG(4,("SETMODE: no secondary TV-adapter detected, switching CRTCs\n"));
275 gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2);
276 si->switched_crtcs = false;
278 break;
281 break;
282 default:
283 break;
286 if (si->switched_crtcs)
288 uint32 temp = startadd;
289 startadd = startadd_right;
290 startadd_right = temp;
293 /*Tell card what memory to display*/
294 switch (target.flags & DUALHEAD_BITS)
296 case DUALHEAD_ON:
297 case DUALHEAD_SWITCH:
298 gx00_crtc_set_display_start(startadd,colour_depth1);
299 g400_crtc2_set_display_start(startadd_right,colour_depth2);
300 break;
301 case DUALHEAD_CLONE:
302 gx00_crtc_set_display_start(startadd,colour_depth1);
303 g400_crtc2_set_display_start(startadd,colour_depth2);
304 break;
307 /* set the timing */
308 gx00_crtc_set_timing(target);
309 /* we do not need to setup CRTC2 here for a head that's in TVout mode */
310 if (!(target2.flags & TV_BITS)) result = g400_crtc2_set_timing(target2);
312 /* TVout support: setup CRTC2 and it's pixelclock */
313 if (si->ps.tvout && (target2.flags & TV_BITS)) maventv_init(target2);
315 else /* single head mode */
317 status_t status;
318 int colour_mode = BPP32;
320 switch(target.space)
322 case B_CMAP8:
323 colour_depth1 = 8;
324 colour_mode = BPP8;
325 break;
326 case B_RGB15_LITTLE:
327 colour_depth1 = 16;
328 colour_mode = BPP15;
329 break;
330 case B_RGB16_LITTLE:
331 colour_depth1 = 16;
332 colour_mode = BPP16;
333 break;
334 case B_RGB32_LITTLE:
335 colour_depth1 = 32;
336 colour_mode = BPP32;
337 break;
338 default:
339 LOG(8,("SETMODE: Invalid singlehead colour depth 0x%08x\n", target.space));
340 return B_ERROR;
343 /* set the pixel clock PLL */
344 if (si->ps.card_type >= G100)
345 //fixme: how about when TVout is enabled???
346 status = gx00_dac_set_pix_pll(target);
347 else
349 status = mil2_dac_set_pix_pll((target.timing.pixel_clock)/1000.0, colour_depth1);
351 if (status==B_ERROR)
352 LOG(8,("CRTC: error setting pixel clock (internal DAC)\n"));
354 /* set the colour depth for CRTC1 and the DAC */
355 gx00_dac_mode(colour_mode,1.0);
356 gx00_crtc_depth(colour_mode);
358 /* if we do TVout mode, its non-interlaced (as we are on <= G400MAX for sure) */
359 si->interlaced_tv_mode = false;
361 /* set the display pitch */
362 gx00_crtc_set_display_pitch();
364 /* tell the card what memory to display */
365 gx00_crtc_set_display_start(startadd,colour_depth1);
367 /* enable primary analog output */
368 switch (si->ps.card_type)
370 case G100:
371 case G200:
372 case G400:
373 case G400MAX:
374 if (!si->ps.secondary_head && si->ps.tvout && (target.flags & TV_BITS))
375 gx00_general_dac_select(DS_CRTC1MAVEN);
376 else
377 gx00_general_dac_select(DS_CRTC1DAC);
378 break;
379 case G450:
380 case G550:
381 gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2);
382 gx50_general_output_select();
383 break;
384 default:
385 break;
388 /* set the timing */
389 if (!si->ps.secondary_head && si->ps.tvout && (target.flags & TV_BITS))
391 /* calculate MAVEN-CRTC delay: only used during TVout */
392 si->crtc_delay = 17;
393 if (colour_depth1 == 16) si->crtc_delay += 4;
394 /* setup CRTC1 and it's pixelclock for TVout */
395 maventv_init(target);
397 else
399 gx00_maven_shutoff();
400 gx00_crtc_set_timing(target);
404 /* update driver's mode store */
405 si->dm = target;
407 /* set up acceleration for this mode */
408 gx00_acc_init();
410 /* restore screen(s) output state(s) */
411 SET_DPMS_MODE(si->dpms_flags);
413 /* clear line at bottom of screen if dualhead mode:
414 * MAVEN hardware design fault 'fix'.
415 * Note:
416 * Not applicable for singlehead cards with a MAVEN, since it's only used
417 * for TVout there. */
418 if ((target.flags & DUALHEAD_BITS) && (si->ps.card_type <= G400MAX))
419 gx00_maven_clrline();
421 LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0));
423 /* enable interrupts using the kernel driver */
424 interrupt_enable(true);
426 /* optimize memory-access if needed */
427 gx00_crtc_mem_priority(colour_depth1);
429 /* Tune RAM CAS-latency if needed. Must be done *here*! */
430 mga_set_cas_latency();
432 return B_OK;
436 Set which pixel of the virtual frame buffer will show up in the
437 top left corner of the display device. Used for page-flipping
438 games and virtual desktops.
440 status_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start) {
441 uint8 colour_depth;
442 uint32 startadd,startadd_right;
444 LOG(4,("MOVE_DISPLAY: h %d, v %d\n", h_display_start, v_display_start));
446 /* G400 CRTC1 handles multiples of 8 for 8bit, 4 for 16bit, 2 for 32 bit
447 G400 CRTC2 handles multiples of 32 for 16-bit and 16 for 32-bit - must stoop to this in dualhead
450 /* reset lower bits, don't return an error! */
451 if (si->dm.flags & DUALHEAD_BITS)
453 switch(si->dm.space)
455 case B_RGB16_LITTLE:
456 colour_depth=16;
457 h_display_start &= ~0x1f;
458 break;
459 case B_RGB32_LITTLE:
460 colour_depth=32;
461 h_display_start &= ~0x0f;
462 break;
463 default:
464 LOG(8,("SET:Invalid DH colour depth 0x%08x, should never happen\n", si->dm.space));
465 return B_ERROR;
468 else
470 switch(si->dm.space)
472 case B_CMAP8:
473 colour_depth=8;
474 h_display_start &= ~0x07;
475 break;
476 case B_RGB15_LITTLE: case B_RGB16_LITTLE:
477 colour_depth=16;
478 h_display_start &= ~0x03;
479 break;
480 case B_RGB32_LITTLE:
481 colour_depth=32;
482 h_display_start &= ~0x01;
483 break;
484 default:
485 return B_ERROR;
489 /* do not run past end of display */
490 switch (si->dm.flags & DUALHEAD_BITS)
492 case DUALHEAD_ON:
493 case DUALHEAD_SWITCH:
494 if (((si->dm.timing.h_display * 2) + h_display_start) > si->dm.virtual_width)
495 return B_ERROR;
496 break;
497 default:
498 if ((si->dm.timing.h_display + h_display_start) > si->dm.virtual_width)
499 return B_ERROR;
500 break;
502 if ((si->dm.timing.v_display + v_display_start) > si->dm.virtual_height)
503 return B_ERROR;
505 /* everybody remember where we parked... */
506 si->dm.h_display_start = h_display_start;
507 si->dm.v_display_start = v_display_start;
509 /* actually set the registers */
510 //fixme: seperate both heads: we need a secondary si->fbc!
511 startadd = v_display_start * si->fbc.bytes_per_row;
512 startadd += h_display_start * (colour_depth >> 3);
513 startadd += (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;
514 startadd_right = startadd + si->dm.timing.h_display * (colour_depth >> 3);
516 /* account for switched CRTC's */
517 if (si->switched_crtcs)
519 uint32 temp = startadd;
520 startadd = startadd_right;
521 startadd_right = temp;
524 interrupt_enable(false);
526 switch (si->dm.flags&DUALHEAD_BITS)
528 case DUALHEAD_ON:
529 case DUALHEAD_SWITCH:
530 gx00_crtc_set_display_start(startadd,colour_depth);
531 g400_crtc2_set_display_start(startadd_right,colour_depth);
532 break;
533 case DUALHEAD_OFF:
534 gx00_crtc_set_display_start(startadd,colour_depth);
535 break;
536 case DUALHEAD_CLONE:
537 gx00_crtc_set_display_start(startadd,colour_depth);
538 g400_crtc2_set_display_start(startadd,colour_depth);
539 break;
542 interrupt_enable(true);
543 return B_OK;
547 Set the indexed color palette.
549 void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) {
550 int i;
551 uint8 *r,*g,*b;
553 /* Protect gamma correction when not in CMAP8 */
554 if (si->dm.space != B_CMAP8) return;
556 r=si->color_data;
557 g=r+256;
558 b=g+256;
560 i=first;
561 while (count--)
563 r[i]=*color_data++;
564 g[i]=*color_data++;
565 b[i]=*color_data++;
566 i++;
568 gx00_dac_palette(r,g,b);
571 /* Put the display into one of the Display Power Management modes. */
572 status_t SET_DPMS_MODE(uint32 dpms_flags)
574 interrupt_enable(false);
576 LOG(4,("SET_DPMS_MODE: 0x%08x\n", dpms_flags));
578 /* note current DPMS state for our reference */
579 si->dpms_flags = dpms_flags;
581 if (si->dm.flags & DUALHEAD_BITS) /* dualhead */
583 switch(dpms_flags)
585 case B_DPMS_ON: /* H: on, V: on */
586 gx00_crtc_dpms(true, true, true);
587 if (si->ps.secondary_head) g400_crtc2_dpms(true, true, true);
588 break;
589 case B_DPMS_STAND_BY:
590 if (si->settings.greensync)
592 /* blank screen, but keep sync running */
593 gx00_crtc_dpms(false, true, true);
595 else
597 gx00_crtc_dpms(false, false, true);
599 if (si->ps.secondary_head)
601 if ((si->dm.flags & TV_BITS) && (si->ps.card_type > G400MAX))
603 /* keep display enabled in TVout modes for G450 and G550! */
604 g400_crtc2_dpms(true, false, true);
606 else
608 g400_crtc2_dpms(false, false, true);
611 break;
612 case B_DPMS_SUSPEND:
613 if (si->settings.greensync)
615 /* blank screen, but keep sync running */
616 gx00_crtc_dpms(false, true, true);
618 else
620 gx00_crtc_dpms(false, true, false);
622 if (si->ps.secondary_head)
624 if ((si->dm.flags & TV_BITS) && (si->ps.card_type > G400MAX))
626 /* keep display enabled in TVout modes for G450 and G550! */
627 g400_crtc2_dpms(true, true, false);
629 else
631 g400_crtc2_dpms(false, true, false);
634 break;
635 case B_DPMS_OFF: /* H: off, V: off, display off */
636 if (si->settings.greensync)
638 /* blank screen, but keep sync running */
639 gx00_crtc_dpms(false, true, true);
641 else
643 gx00_crtc_dpms(false, false, false);
645 if (si->ps.secondary_head)
647 if ((si->dm.flags & TV_BITS) && (si->ps.card_type > G400MAX))
649 /* keep display enabled in TVout modes for G450 and G550! */
650 g400_crtc2_dpms(true, false, false);
652 else
654 g400_crtc2_dpms(false, false, false);
657 break;
658 default:
659 LOG(8,("SET: Invalid DPMS settings (DH) 0x%08x\n", dpms_flags));
660 interrupt_enable(true);
661 return B_ERROR;
664 else /* singlehead */
666 switch(dpms_flags)
668 case B_DPMS_ON: /* H: on, V: on */
669 gx00_crtc_dpms(true, true, true);
670 /* on singlehead cards with TVout program the MAVEN as well */
671 if (si->dm.flags & TV_BITS) gx00_maven_dpms(true, true, true);
672 break;
673 case B_DPMS_STAND_BY:
674 if (si->settings.greensync)
676 /* blank screen, but keep sync running */
677 gx00_crtc_dpms(false, true, true);
679 else
681 gx00_crtc_dpms(false, false, true);
683 /* on singlehead cards with TVout program the MAVEN as well */
684 if (si->dm.flags & TV_BITS) gx00_maven_dpms(false, false, true);
685 break;
686 case B_DPMS_SUSPEND:
687 if (si->settings.greensync)
689 /* blank screen, but keep sync running */
690 gx00_crtc_dpms(false, true, true);
692 else
694 gx00_crtc_dpms(false, true, false);
696 /* on singlehead cards with TVout program the MAVEN as well */
697 if (si->dm.flags & TV_BITS) gx00_maven_dpms(false, true, false);
698 break;
699 case B_DPMS_OFF: /* H: off, V: off, display off */
700 if (si->settings.greensync)
702 /* blank screen, but keep sync running */
703 gx00_crtc_dpms(false, true, true);
705 else
707 gx00_crtc_dpms(false, false, false);
709 /* on singlehead cards with TVout program the MAVEN as well */
710 if (si->dm.flags & TV_BITS) gx00_maven_dpms(false, false, false);
711 break;
712 default:
713 LOG(8,("SET: Invalid DPMS settings (SH) 0x%08x\n", dpms_flags));
714 interrupt_enable(true);
715 return B_ERROR;
718 interrupt_enable(true);
719 return B_OK;
722 /* Report device DPMS capabilities. */
723 uint32 DPMS_CAPABILITIES(void)
725 if (si->settings.greensync)
726 /* we can blank the screen on CRTC1, G400 CRTC2 does not support intermediate
727 * modes anyway. */
728 //fixme: G450/G550 support full DPMS on CRTC2...
729 return B_DPMS_ON | B_DPMS_OFF;
730 else
731 /* normally CRTC1 supports full DPMS (and on G450/G550 CRTC2 also).. */
732 return B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF;
735 /* Return the current DPMS mode. */
736 uint32 DPMS_MODE(void)
738 return si->dpms_flags;