vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / accelerants / via / SetDisplayMode.c
blobf5b3d79f4fcad6774f0ab16a190cce903cd6f834
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-9/2005
12 #define MODULE_BIT 0x00200000
14 #include "acc_std.h"
17 Enable/Disable interrupts. Just a wrapper around the
18 ioctl() to the kernel driver.
20 static void interrupt_enable(bool flag) {
21 status_t result;
22 eng_set_bool_state sbs;
24 /* set the magic number so the driver knows we're for real */
25 sbs.magic = VIA_PRIVATE_DATA_MAGIC;
26 sbs.do_it = flag;
27 /* contact driver and get a pointer to the registers and shared data */
28 result = ioctl(fd, ENG_RUN_INTERRUPTS, &sbs, sizeof(sbs));
31 /* First validate the mode, then call lots of bit banging stuff to set the mode(s)! */
32 status_t SET_DISPLAY_MODE(display_mode *mode_to_set)
34 /* BOUNDS WARNING:
35 * It's impossible to deviate whatever small amount in a display_mode if the lower
36 * and upper limits are the same!
37 * Besides:
38 * BeOS (tested R5.0.3PE) is failing BWindowScreen::SetFrameBuffer() if PROPOSEMODE
39 * returns B_BAD_VALUE!
40 * Which means PROPOSEMODE should not return that on anything except on
41 * deviations for:
42 * display_mode.virtual_width;
43 * display_mode.virtual_height;
44 * display_mode.timing.h_display;
45 * display_mode.timing.v_display;
46 * So:
47 * We don't use bounds here by making sure bounds and target are the same struct!
48 * (See the call to PROPOSE_DISPLAY_MODE below) */
49 display_mode /*bounds,*/ target;
51 uint8 colour_depth1 = 32;
52 // status_t result;
53 uint32 startadd,startadd_right;
54 bool display, h, v;
55 // bool crt1, crt2, cross;
57 /* Adjust mode to valid one and fail if invalid */
58 target /*= bounds*/ = *mode_to_set;
59 /* show the mode bits */
60 LOG(1, ("SETMODE: (ENTER) initial modeflags: $%08x\n", target.flags));
61 LOG(1, ("SETMODE: requested target pixelclock %dkHz\n", target.timing.pixel_clock));
62 LOG(1, ("SETMODE: requested virtual_width %d, virtual_height %d\n",
63 target.virtual_width, target.virtual_height));
65 /* See BOUNDS WARNING above... */
66 if (PROPOSE_DISPLAY_MODE(&target, &target, &target) == B_ERROR) return B_ERROR;
68 /* if not dualhead capable card clear dualhead flags */
69 if (!(target.flags & DUALHEAD_CAPABLE))
71 target.flags &= ~DUALHEAD_BITS;
73 /* if not TVout capable card clear TVout flags */
74 if (!(target.flags & TV_CAPABLE))
76 target.flags &= ~TV_BITS;
78 LOG(1, ("SETMODE: (CONT.) validated command modeflags: $%08x\n", target.flags));
80 /* disable interrupts using the kernel driver */
81 interrupt_enable(false);
83 /* find current DPMS state, then turn off screen(s) */
84 head1_dpms_fetch(&display, &h, &v);
85 head1_dpms(false, false, false);
86 // if (si->ps.secondary_head) head2_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 eng_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 /* validate flags for secondary TVout */
105 // if ((i2c_sec_tv_adapter() != B_OK) && (target2.flags & TV_BITS))
106 // {
107 // target.flags &= ~TV_BITS;//still needed for some routines...
108 // target2.flags &= ~TV_BITS;
109 // LOG(1,("SETMODE: blocking TVout: no TVout cable connected!\n"));
110 // }
112 /* detect which connectors have a CRT connected */
113 //fixme: 'hot-plugging' for analog monitors removed: remove code as well;
114 //or make it work with digital panels connected as well.
115 // crt1 = eng_dac_crt_connected();
116 // crt2 = eng_dac2_crt_connected();
117 /* connect outputs 'straight-through' */
118 // if (crt1)
119 // {
120 /* connector1 is used as primary output */
121 // cross = false;
122 // }
123 // else
124 // {
125 // if (crt2)
126 /* connector2 is used as primary output */
127 // cross = true;
128 // else
129 /* no CRT detected: assume connector1 is used as primary output */
130 // cross = false;
131 // }
132 /* set output connectors assignment if possible */
133 // if ((target.flags & DUALHEAD_BITS) == DUALHEAD_SWITCH)
134 /* invert output assignment in switch mode */
135 // eng_general_head_select(true);
136 // else
137 // eng_general_head_select(false);
139 /* set the pixel clock PLL(s) */
140 LOG(8,("SETMODE: target clock %dkHz\n",target.timing.pixel_clock));
141 if (head1_set_pix_pll(target) == B_ERROR)
142 LOG(8,("SETMODE: error setting pixel clock (internal DAC)\n"));
144 /* we do not need to set the pixelclock here for a head that's in TVout mode */
145 // if (!(target2.flags & TV_BITS))
146 // {
147 // LOG(8,("SETMODE: target2 clock %dkHz\n",target2.timing.pixel_clock));
148 // if (head2_set_pix_pll(target2) == B_ERROR)
149 // LOG(8,("SETMODE: error setting pixel clock (DAC2)\n"));
150 // }
152 /*set the colour depth for CRTC1 and the DAC */
153 switch(target.space)
155 case B_CMAP8:
156 colour_depth1 = 8;
157 head1_mode(BPP8, 1.0);
158 head1_depth(BPP8);
159 break;
160 case B_RGB15_LITTLE:
161 colour_depth1 = 16;
162 head1_mode(BPP15, 1.0);
163 head1_depth(BPP15);
164 break;
165 case B_RGB16_LITTLE:
166 colour_depth1 = 16;
167 head1_mode(BPP16, 1.0);
168 head1_depth(BPP16);
169 break;
170 case B_RGB32_LITTLE:
171 colour_depth1 = 32;
172 head1_mode(BPP32, 1.0);
173 head1_depth(BPP32);
174 break;
176 /*set the colour depth for CRTC2 and DAC2 */
177 switch(target2.space)
179 case B_CMAP8:
180 colour_depth2 = 8;
181 // head2_mode(BPP8, 1.0);
182 // head2_depth(BPP8);
183 break;
184 case B_RGB15_LITTLE:
185 colour_depth2 = 16;
186 // head2_mode(BPP15, 1.0);
187 // head2_depth(BPP15);
188 break;
189 case B_RGB16_LITTLE:
190 colour_depth2 = 16;
191 // head2_mode(BPP16, 1.0);
192 // head2_depth(BPP16);
193 break;
194 case B_RGB32_LITTLE:
195 colour_depth2 = 32;
196 // head2_mode(BPP32, 1.0);
197 // head2_depth(BPP32);
198 break;
201 /* check if we are doing interlaced TVout mode */
202 si->interlaced_tv_mode = false;
203 /* if ((target2.flags & TV_BITS) && (si->ps.card_type >= G450))
204 si->interlaced_tv_mode = true;
206 /*set the display(s) pitches*/
207 head1_set_display_pitch ();
208 //fixme: seperate for real dualhead modes:
209 //we need a secondary si->fbc!
210 // head2_set_display_pitch ();
212 /*work out where the "right" screen starts*/
213 startadd_right = startadd + (target.timing.h_display * (colour_depth1 >> 3));
215 /* Tell card what memory to display */
216 switch (target.flags & DUALHEAD_BITS)
218 case DUALHEAD_ON:
219 case DUALHEAD_SWITCH:
220 head1_set_display_start(startadd,colour_depth1);
221 // head2_set_display_start(startadd_right,colour_depth2);
222 break;
223 case DUALHEAD_CLONE:
224 head1_set_display_start(startadd,colour_depth1);
225 // head2_set_display_start(startadd,colour_depth2);
226 break;
229 /* set the timing */
230 head1_set_timing(target);
231 /* we do not need to setup CRTC2 here for a head that's in TVout mode */
232 // if (!(target2.flags & TV_BITS)) result = head2_set_timing(target2);
234 /* TVout support: setup CRTC2 and it's pixelclock */
235 // if (si->ps.tvout && (target2.flags & TV_BITS)) maventv_init(target2);
237 else /* single head mode */
239 status_t status;
240 int colour_mode = BPP32;
242 /* connect output */
243 if (si->ps.secondary_head)
245 /* detect which connectors have a CRT connected */
246 //fixme: 'hot-plugging' for analog monitors removed: remove code as well;
247 //or make it work with digital panels connected as well.
248 // crt1 = eng_dac_crt_connected();
249 // crt2 = eng_dac2_crt_connected();
250 /* connect outputs 'straight-through' */
251 // if (crt1)
252 // {
253 /* connector1 is used as primary output */
254 // cross = false;
255 // }
256 // else
257 // {
258 // if (crt2)
259 /* connector2 is used as primary output */
260 // cross = true;
261 // else
262 /* no CRT detected: assume connector1 is used as primary output */
263 // cross = false;
264 // }
265 /* set output connectors assignment if possible */
266 eng_general_head_select(false);
269 switch(target.space)
271 case B_CMAP8: colour_depth1 = 8; colour_mode = BPP8; break;
272 case B_RGB15_LITTLE: colour_depth1 = 16; colour_mode = BPP15; break;
273 case B_RGB16_LITTLE: colour_depth1 = 16; colour_mode = BPP16; break;
274 case B_RGB32_LITTLE: colour_depth1 = 32; colour_mode = BPP32; break;
275 default:
276 LOG(8,("SETMODE: Invalid singlehead colour depth 0x%08x\n", target.space));
277 return B_ERROR;
280 /* set the pixel clock PLL */
281 status = head1_set_pix_pll(target);
283 if (status==B_ERROR)
284 LOG(8,("CRTC: error setting pixel clock (internal DAC)\n"));
286 /* set the colour depth for CRTC1 and the DAC */
287 /* first set the colordepth */
288 head1_depth(colour_mode);
289 /* then(!) program the PAL (<8bit colordepth does not support 8bit PAL) */
290 head1_mode(colour_mode,1.0);
292 /* set the display pitch */
293 head1_set_display_pitch();
295 /* tell the card what memory to display */
296 head1_set_display_start(startadd,colour_depth1);
298 /* set the timing */
299 head1_set_timing(target);
301 //fixme: shut-off the videoPLL if it exists...
304 /* update driver's mode store */
305 si->dm = target;
307 /* turn screen one on */
308 head1_dpms(display, h, v);
309 /* turn screen two on if a dualhead mode is active */
310 // if (target.flags & DUALHEAD_BITS) head2_dpms(display,h,v);
312 /* set up acceleration for this mode */
313 // eng_acc_init();
314 /* set up overlay unit for this mode */
315 eng_bes_init();
317 LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0));
319 /* enable interrupts using the kernel driver */
320 interrupt_enable(true);
322 /* optimize memory-access if needed */
323 // head1_mem_priority(colour_depth1);
325 /* Tune RAM CAS-latency if needed. Must be done *here*! */
326 eng_set_cas_latency();
328 return B_OK;
332 Set which pixel of the virtual frame buffer will show up in the
333 top left corner of the display device. Used for page-flipping
334 games and virtual desktops.
336 status_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start) {
337 uint8 colour_depth;
338 uint32 startadd,startadd_right;
340 LOG(4,("MOVE_DISPLAY: h %d, v %d\n", h_display_start, v_display_start));
342 /* VIA CRTC1 handles multiples of 8 for 8bit, 4 for 16bit, 2 for 32 bit
343 VIA CRTC2 is yet unknown...
346 /* reset lower bits, don't return an error! */
347 if (si->dm.flags & DUALHEAD_BITS)
349 //fixme for VIA...
350 switch(si->dm.space)
352 case B_RGB16_LITTLE:
353 colour_depth=16;
354 h_display_start &= ~0x1f;
355 break;
356 case B_RGB32_LITTLE:
357 colour_depth=32;
358 h_display_start &= ~0x0f;
359 break;
360 default:
361 LOG(8,("SET:Invalid DH colour depth 0x%08x, should never happen\n", si->dm.space));
362 return B_ERROR;
365 else
367 switch(si->dm.space)
369 case B_CMAP8:
370 colour_depth=8;
371 h_display_start &= ~0x07;
372 break;
373 case B_RGB15_LITTLE: case B_RGB16_LITTLE:
374 colour_depth=16;
375 h_display_start &= ~0x03;
376 break;
377 case B_RGB32_LITTLE:
378 colour_depth=32;
379 h_display_start &= ~0x01;
380 break;
381 default:
382 return B_ERROR;
386 /* do not run past end of display */
387 switch (si->dm.flags & DUALHEAD_BITS)
389 case DUALHEAD_ON:
390 case DUALHEAD_SWITCH:
391 if (((si->dm.timing.h_display * 2) + h_display_start) > si->dm.virtual_width)
392 return B_ERROR;
393 break;
394 default:
395 if ((si->dm.timing.h_display + h_display_start) > si->dm.virtual_width)
396 return B_ERROR;
397 break;
399 if ((si->dm.timing.v_display + v_display_start) > si->dm.virtual_height)
400 return B_ERROR;
402 /* everybody remember where we parked... */
403 si->dm.h_display_start = h_display_start;
404 si->dm.v_display_start = v_display_start;
406 /* actually set the registers */
407 //fixme: seperate both heads: we need a secondary si->fbc!
408 startadd = v_display_start * si->fbc.bytes_per_row;
409 startadd += h_display_start * (colour_depth >> 3);
410 startadd += (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;
411 startadd_right = startadd + si->dm.timing.h_display * (colour_depth >> 3);
413 interrupt_enable(false);
415 switch (si->dm.flags & DUALHEAD_BITS)
417 case DUALHEAD_ON:
418 case DUALHEAD_SWITCH:
419 head1_set_display_start(startadd,colour_depth);
420 // head2_set_display_start(startadd_right,colour_depth);
421 break;
422 case DUALHEAD_OFF:
423 head1_set_display_start(startadd,colour_depth);
424 break;
425 case DUALHEAD_CLONE:
426 head1_set_display_start(startadd,colour_depth);
427 // head2_set_display_start(startadd,colour_depth);
428 break;
431 interrupt_enable(true);
432 return B_OK;
435 /* Set the indexed color palette */
436 void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) {
437 int i;
438 uint8 *r,*g,*b;
440 /* Protect gamma correction when not in CMAP8 */
441 if (si->dm.space != B_CMAP8) return;
443 r=si->color_data;
444 g=r+256;
445 b=g+256;
447 i=first;
448 while (count--)
450 r[i]=*color_data++;
451 g[i]=*color_data++;
452 b[i]=*color_data++;
453 i++;
455 head1_palette(r,g,b);
456 // if (si->dm.flags & DUALHEAD_BITS) head2_palette(r,g,b);
459 /* Put the display into one of the Display Power Management modes. */
460 status_t SET_DPMS_MODE(uint32 dpms_flags) {
461 interrupt_enable(false);
463 LOG(4,("SET_DPMS_MODE: 0x%08x\n", dpms_flags));
465 if (si->dm.flags & DUALHEAD_BITS) /*dualhead*/
467 switch(dpms_flags)
469 case B_DPMS_ON: /* H: on, V: on, display on */
470 head1_dpms(true, true, true);
471 // if (si->ps.secondary_head) head2_dpms(true, true, true);
472 break;
473 case B_DPMS_STAND_BY:
474 head1_dpms(false, false, true);
475 // if (si->ps.secondary_head) head2_dpms(false, false, true);
476 break;
477 case B_DPMS_SUSPEND:
478 head1_dpms(false, true, false);
479 // if (si->ps.secondary_head) head2_dpms(false, true, false);
480 break;
481 case B_DPMS_OFF: /* H: off, V: off, display off */
482 head1_dpms(false, false, false);
483 // if (si->ps.secondary_head) head2_dpms(false, false, false);
484 break;
485 default:
486 LOG(8,("SET: Invalid DPMS settings (DH) 0x%08x\n", dpms_flags));
487 interrupt_enable(true);
488 return B_ERROR;
491 else /* singlehead */
493 switch(dpms_flags)
495 case B_DPMS_ON: /* H: on, V: on, display on */
496 head1_dpms(true, true, true);
497 break;
498 case B_DPMS_STAND_BY:
499 head1_dpms(false, false, true);
500 break;
501 case B_DPMS_SUSPEND:
502 head1_dpms(false, true, false);
503 break;
504 case B_DPMS_OFF: /* H: off, V: off, display off */
505 head1_dpms(false, false, false);
506 break;
507 default:
508 LOG(8,("SET: Invalid DPMS settings (DH) 0x%08x\n", dpms_flags));
509 interrupt_enable(true);
510 return B_ERROR;
513 interrupt_enable(true);
514 return B_OK;
517 /* Report device DPMS capabilities */
518 uint32 DPMS_CAPABILITIES(void) {
519 return (B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF);
522 /* Return the current DPMS mode */
523 uint32 DPMS_MODE(void) {
524 bool display, h, v;
526 interrupt_enable(false);
527 head1_dpms_fetch(&display, &h, &v);
529 interrupt_enable(true);
531 if (display && h && v)
532 return B_DPMS_ON;
533 else if(v)
534 return B_DPMS_STAND_BY;
535 else if(h)
536 return B_DPMS_SUSPEND;
537 else
538 return B_DPMS_OFF;