/* r128_drv.h -- Private header for r128 driver -*- linux-c -*- * Created: Mon Dec 13 09:51:11 1999 by faith@precisioninsight.com * * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Authors: * Rickard E. (Rik) Faith * Kevin E. Martin * Gareth Hughes * Michel Dänzer */ #ifndef __R128_DRV_H__ #define __R128_DRV_H__ #define GET_RING_HEAD(dev_priv) R128_READ( R128_PM4_BUFFER_DL_RPTR ) typedef struct drm_r128_freelist { unsigned int age; drm_buf_t *buf; struct drm_r128_freelist *next; struct drm_r128_freelist *prev; } drm_r128_freelist_t; typedef struct drm_r128_ring_buffer { u32 *start; u32 *end; int size; int size_l2qw; u32 tail; u32 tail_mask; int space; int high_mark; } drm_r128_ring_buffer_t; typedef struct drm_r128_private { drm_r128_ring_buffer_t ring; drm_r128_sarea_t *sarea_priv; int cce_mode; int cce_fifo_size; int cce_running; drm_r128_freelist_t *head; drm_r128_freelist_t *tail; int usec_timeout; int is_pci; unsigned long phys_pci_gart; dma_addr_t bus_pci_gart; unsigned long cce_buffers_offset; atomic_t idle_count; int page_flipping; int current_page; u32 crtc_offset; u32 crtc_offset_cntl; u32 color_fmt; unsigned int front_offset; unsigned int front_pitch; unsigned int back_offset; unsigned int back_pitch; u32 depth_fmt; unsigned int depth_offset; unsigned int depth_pitch; unsigned int span_offset; u32 front_pitch_offset_c; u32 back_pitch_offset_c; u32 depth_pitch_offset_c; u32 span_pitch_offset_c; drm_local_map_t *sarea; drm_local_map_t *mmio; drm_local_map_t *cce_ring; drm_local_map_t *ring_rptr; drm_local_map_t *agp_textures; } drm_r128_private_t; typedef struct drm_r128_buf_priv { u32 age; int prim; int discard; int dispatched; drm_r128_freelist_t *list_entry; } drm_r128_buf_priv_t; /* r128_cce.c */ extern int r128_cce_init( DRM_IOCTL_ARGS ); extern int r128_cce_start( DRM_IOCTL_ARGS ); extern int r128_cce_stop( DRM_IOCTL_ARGS ); extern int r128_cce_reset( DRM_IOCTL_ARGS ); extern int r128_cce_idle( DRM_IOCTL_ARGS ); extern int r128_engine_reset( DRM_IOCTL_ARGS ); extern int r128_fullscreen( DRM_IOCTL_ARGS ); extern int r128_cce_buffers( DRM_IOCTL_ARGS ); extern int r128_getparam( DRM_IOCTL_ARGS ); extern void r128_freelist_reset( drm_device_t *dev ); extern drm_buf_t *r128_freelist_get( drm_device_t *dev ); extern int r128_wait_ring( drm_r128_private_t *dev_priv, int n ); extern int r128_do_cce_idle( drm_r128_private_t *dev_priv ); extern int r128_do_cleanup_cce( drm_device_t *dev ); extern int r128_do_cleanup_pageflip( drm_device_t *dev ); /* r128_state.c */ extern int r128_cce_clear( DRM_IOCTL_ARGS ); extern int r128_cce_swap( DRM_IOCTL_ARGS ); extern int r128_cce_flip( DRM_IOCTL_ARGS ); extern int r128_cce_vertex( DRM_IOCTL_ARGS ); extern int r128_cce_indices( DRM_IOCTL_ARGS ); extern int r128_cce_blit( DRM_IOCTL_ARGS ); extern int r128_cce_depth( DRM_IOCTL_ARGS ); extern int r128_cce_stipple( DRM_IOCTL_ARGS ); extern int r128_cce_indirect( DRM_IOCTL_ARGS ); extern int r128_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence); extern irqreturn_t r128_driver_irq_handler( DRM_IRQ_ARGS ); extern void r128_driver_irq_preinstall( drm_device_t *dev ); extern void r128_driver_irq_postinstall( drm_device_t *dev ); extern void r128_driver_irq_uninstall( drm_device_t *dev ); /* Register definitions, register access macros and drmAddMap constants * for Rage 128 kernel driver. */ #define R128_AUX_SC_CNTL 0x1660 # define R128_AUX1_SC_EN (1 << 0) # define R128_AUX1_SC_MODE_OR (0 << 1) # define R128_AUX1_SC_MODE_NAND (1 << 1) # define R128_AUX2_SC_EN (1 << 2) # define R128_AUX2_SC_MODE_OR (0 << 3) # define R128_AUX2_SC_MODE_NAND (1 << 3) # define R128_AUX3_SC_EN (1 << 4) # define R128_AUX3_SC_MODE_OR (0 << 5) # define R128_AUX3_SC_MODE_NAND (1 << 5) #define R128_AUX1_SC_LEFT 0x1664 #define R128_AUX1_SC_RIGHT 0x1668 #define R128_AUX1_SC_TOP 0x166c #define R128_AUX1_SC_BOTTOM 0x1670 #define R128_AUX2_SC_LEFT 0x1674 #define R128_AUX2_SC_RIGHT 0x1678 #define R128_AUX2_SC_TOP 0x167c #define R128_AUX2_SC_BOTTOM 0x1680 #define R128_AUX3_SC_LEFT 0x1684 #define R128_AUX3_SC_RIGHT 0x1688 #define R128_AUX3_SC_TOP 0x168c #define R128_AUX3_SC_BOTTOM 0x1690 #define R128_BRUSH_DATA0 0x1480 #define R128_BUS_CNTL 0x0030 # define R128_BUS_MASTER_DIS (1 << 6) #define R128_CLOCK_CNTL_INDEX 0x0008 #define R128_CLOCK_CNTL_DATA 0x000c # define R128_PLL_WR_EN (1 << 7) #define R128_CONSTANT_COLOR_C 0x1d34 #define R128_CRTC_OFFSET 0x0224 #define R128_CRTC_OFFSET_CNTL 0x0228 # define R128_CRTC_OFFSET_FLIP_CNTL (1 << 16) #define R128_DP_GUI_MASTER_CNTL 0x146c # define R128_GMC_SRC_PITCH_OFFSET_CNTL (1 << 0) # define R128_GMC_DST_PITCH_OFFSET_CNTL (1 << 1) # define R128_GMC_BRUSH_SOLID_COLOR (13 << 4) # define R128_GMC_BRUSH_NONE (15 << 4) # define R128_GMC_DST_16BPP (4 << 8) # define R128_GMC_DST_24BPP (5 << 8) # define R128_GMC_DST_32BPP (6 << 8) # define R128_GMC_DST_DATATYPE_SHIFT 8 # define R128_GMC_SRC_DATATYPE_COLOR (3 << 12) # define R128_DP_SRC_SOURCE_MEMORY (2 << 24) # define R128_DP_SRC_SOURCE_HOST_DATA (3 << 24) # define R128_GMC_CLR_CMP_CNTL_DIS (1 << 28) # define R128_GMC_AUX_CLIP_DIS (1 << 29) # define R128_GMC_WR_MSK_DIS (1 << 30) # define R128_ROP3_S 0x00cc0000 # define R128_ROP3_P 0x00f00000 #define R128_DP_WRITE_MASK 0x16cc #define R128_DST_PITCH_OFFSET_C 0x1c80 # define R128_DST_TILE (1 << 31) #define R128_GEN_INT_CNTL 0x0040 # define R128_CRTC_VBLANK_INT_EN (1 << 0) #define R128_GEN_INT_STATUS 0x0044 # define R128_CRTC_VBLANK_INT (1 << 0) # define R128_CRTC_VBLANK_INT_AK (1 << 0) #define R128_GEN_RESET_CNTL 0x00f0 # define R128_SOFT_RESET_GUI (1 << 0) #define R128_GUI_SCRATCH_REG0 0x15e0 #define R128_GUI_SCRATCH_REG1 0x15e4 #define R128_GUI_SCRATCH_REG2 0x15e8 #define R128_GUI_SCRATCH_REG3 0x15ec #define R128_GUI_SCRATCH_REG4 0x15f0 #define R128_GUI_SCRATCH_REG5 0x15f4 #define R128_GUI_STAT 0x1740 # define R128_GUI_FIFOCNT_MASK 0x0fff # define R128_GUI_ACTIVE (1 << 31) #define R128_MCLK_CNTL 0x000f # define R128_FORCE_GCP (1 << 16) # define R128_FORCE_PIPE3D_CP (1 << 17) # define R128_FORCE_RCP (1 << 18) #define R128_PC_GUI_CTLSTAT 0x1748 #define R128_PC_NGUI_CTLSTAT 0x0184 # define R128_PC_FLUSH_GUI (3 << 0) # define R128_PC_RI_GUI (1 << 2) # define R128_PC_FLUSH_ALL 0x00ff # define R128_PC_BUSY (1 << 31) #define R128_PCI_GART_PAGE 0x017c #define R128_PRIM_TEX_CNTL_C 0x1cb0 #define R128_SCALE_3D_CNTL 0x1a00 #define R128_SEC_TEX_CNTL_C 0x1d00 #define R128_SEC_TEXTURE_BORDER_COLOR_C 0x1d3c #define R128_SETUP_CNTL 0x1bc4 #define R128_STEN_REF_MASK_C 0x1d40 #define R128_TEX_CNTL_C 0x1c9c # define R128_TEX_CACHE_FLUSH (1 << 23) #define R128_WAIT_UNTIL 0x1720 # define R128_EVENT_CRTC_OFFSET (1 << 0) #define R128_WINDOW_XY_OFFSET 0x1bcc /* CCE registers */ #define R128_PM4_BUFFER_OFFSET 0x0700 #define R128_PM4_BUFFER_CNTL 0x0704 # define R128_PM4_MASK (15 << 28) # define R128_PM4_NONPM4 (0 << 28) # define R128_PM4_192PIO (1 << 28) # define R128_PM4_192BM (2 << 28) # define R128_PM4_128PIO_64INDBM (3 << 28) # define R128_PM4_128BM_64INDBM (4 << 28) # define R128_PM4_64PIO_128INDBM (5 << 28) # define R128_PM4_64BM_128INDBM (6 << 28) # define R128_PM4_64PIO_64VCBM_64INDBM (7 << 28) # define R128_PM4_64BM_64VCBM_64INDBM (8 << 28) # define R128_PM4_64PIO_64VCPIO_64INDPIO (15 << 28) # define R128_PM4_BUFFER_CNTL_NOUPDATE (1 << 27) #define R128_PM4_BUFFER_WM_CNTL 0x0708 # define R128_WMA_SHIFT 0 # define R128_WMB_SHIFT 8 # define R128_WMC_SHIFT 16 # define R128_WB_WM_SHIFT 24 #define R128_PM4_BUFFER_DL_RPTR_ADDR 0x070c #define R128_PM4_BUFFER_DL_RPTR 0x0710 #define R128_PM4_BUFFER_DL_WPTR 0x0714 # define R128_PM4_BUFFER_DL_DONE (1 << 31) #define R128_PM4_VC_FPU_SETUP 0x071c #define R128_PM4_IW_INDOFF 0x0738 #define R128_PM4_IW_INDSIZE 0x073c #define R128_PM4_STAT 0x07b8 # define R128_PM4_FIFOCNT_MASK 0x0fff # define R128_PM4_BUSY (1 << 16) # define R128_PM4_GUI_ACTIVE (1 << 31) #define R128_PM4_MICROCODE_ADDR 0x07d4 #define R128_PM4_MICROCODE_RADDR 0x07d8 #define R128_PM4_MICROCODE_DATAH 0x07dc #define R128_PM4_MICROCODE_DATAL 0x07e0 #define R128_PM4_BUFFER_ADDR 0x07f0 #define R128_PM4_MICRO_CNTL 0x07fc # define R128_PM4_MICRO_FREERUN (1 << 30) #define R128_PM4_FIFO_DATA_EVEN 0x1000 #define R128_PM4_FIFO_DATA_ODD 0x1004 /* CCE command packets */ #define R128_CCE_PACKET0 0x00000000 #define R128_CCE_PACKET1 0x40000000 #define R128_CCE_PACKET2 0x80000000 #define R128_CCE_PACKET3 0xC0000000 # define R128_CNTL_HOSTDATA_BLT 0x00009400 # define R128_CNTL_PAINT_MULTI 0x00009A00 # define R128_CNTL_BITBLT_MULTI 0x00009B00 # define R128_3D_RNDR_GEN_INDX_PRIM 0x00002300 #define R128_CCE_PACKET_MASK 0xC0000000 #define R128_CCE_PACKET_COUNT_MASK 0x3fff0000 #define R128_CCE_PACKET0_REG_MASK 0x000007ff #define R128_CCE_PACKET1_REG0_MASK 0x000007ff #define R128_CCE_PACKET1_REG1_MASK 0x003ff800 #define R128_CCE_VC_CNTL_PRIM_TYPE_NONE 0x00000000 #define R128_CCE_VC_CNTL_PRIM_TYPE_POINT 0x00000001 #define R128_CCE_VC_CNTL_PRIM_TYPE_LINE 0x00000002 #define R128_CCE_VC_CNTL_PRIM_TYPE_POLY_LINE 0x00000003 #define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_LIST 0x00000004 #define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_FAN 0x00000005 #define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_STRIP 0x00000006 #define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_TYPE2 0x00000007 #define R128_CCE_VC_CNTL_PRIM_WALK_IND 0x00000010 #define R128_CCE_VC_CNTL_PRIM_WALK_LIST 0x00000020 #define R128_CCE_VC_CNTL_PRIM_WALK_RING 0x00000030 #define R128_CCE_VC_CNTL_NUM_SHIFT 16 #define R128_DATATYPE_VQ 0 #define R128_DATATYPE_CI4 1 #define R128_DATATYPE_CI8 2 #define R128_DATATYPE_ARGB1555 3 #define R128_DATATYPE_RGB565 4 #define R128_DATATYPE_RGB888 5 #define R128_DATATYPE_ARGB8888 6 #define R128_DATATYPE_RGB332 7 #define R128_DATATYPE_Y8 8 #define R128_DATATYPE_RGB8 9 #define R128_DATATYPE_CI16 10 #define R128_DATATYPE_YVYU422 11 #define R128_DATATYPE_VYUY422 12 #define R128_DATATYPE_AYUV444 14 #define R128_DATATYPE_ARGB4444 15 /* Constants */ #define R128_AGP_OFFSET 0x02000000 #define R128_WATERMARK_L 16 #define R128_WATERMARK_M 8 #define R128_WATERMARK_N 8 #define R128_WATERMARK_K 128 #define R128_MAX_USEC_TIMEOUT 100000 /* 100 ms */ #define R128_LAST_FRAME_REG R128_GUI_SCRATCH_REG0 #define R128_LAST_DISPATCH_REG R128_GUI_SCRATCH_REG1 #define R128_MAX_VB_AGE 0x7fffffff #define R128_MAX_VB_VERTS (0xffff) #define R128_RING_HIGH_MARK 128 #define R128_PERFORMANCE_BOXES 0 #define R128_READ(reg) DRM_READ32( dev_priv->mmio, (reg) ) #define R128_WRITE(reg,val) DRM_WRITE32( dev_priv->mmio, (reg), (val) ) #define R128_READ8(reg) DRM_READ8( dev_priv->mmio, (reg) ) #define R128_WRITE8(reg,val) DRM_WRITE8( dev_priv->mmio, (reg), (val) ) #define R128_WRITE_PLL(addr,val) \ do { \ R128_WRITE8(R128_CLOCK_CNTL_INDEX, \ ((addr) & 0x1f) | R128_PLL_WR_EN); \ R128_WRITE(R128_CLOCK_CNTL_DATA, (val)); \ } while (0) extern int R128_READ_PLL(drm_device_t *dev, int addr); #define CCE_PACKET0( reg, n ) (R128_CCE_PACKET0 | \ ((n) << 16) | ((reg) >> 2)) #define CCE_PACKET1( reg0, reg1 ) (R128_CCE_PACKET1 | \ (((reg1) >> 2) << 11) | ((reg0) >> 2)) #define CCE_PACKET2() (R128_CCE_PACKET2) #define CCE_PACKET3( pkt, n ) (R128_CCE_PACKET3 | \ (pkt) | ((n) << 16)) static __inline__ void r128_update_ring_snapshot( drm_r128_private_t *dev_priv ) { drm_r128_ring_buffer_t *ring = &dev_priv->ring; ring->space = (GET_RING_HEAD( dev_priv ) - ring->tail) * sizeof(u32); if ( ring->space <= 0 ) ring->space += ring->size; } /* ================================================================ * Misc helper macros */ #define RING_SPACE_TEST_WITH_RETURN( dev_priv ) \ do { \ drm_r128_ring_buffer_t *ring = &dev_priv->ring; int i; \ if ( ring->space < ring->high_mark ) { \ for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { \ r128_update_ring_snapshot( dev_priv ); \ if ( ring->space >= ring->high_mark ) \ goto __ring_space_done; \ DRM_UDELAY(1); \ } \ DRM_ERROR( "ring space check failed!\n" ); \ return DRM_ERR(EBUSY); \ } \ __ring_space_done: \ ; \ } while (0) #define VB_AGE_TEST_WITH_RETURN( dev_priv ) \ do { \ drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; \ if ( sarea_priv->last_dispatch >= R128_MAX_VB_AGE ) { \ int __ret = r128_do_cce_idle( dev_priv ); \ if ( __ret ) return __ret; \ sarea_priv->last_dispatch = 0; \ r128_freelist_reset( dev ); \ } \ } while (0) #define R128_WAIT_UNTIL_PAGE_FLIPPED() do { \ OUT_RING( CCE_PACKET0( R128_WAIT_UNTIL, 0 ) ); \ OUT_RING( R128_EVENT_CRTC_OFFSET ); \ } while (0) /* ================================================================ * Ring control */ #define R128_VERBOSE 0 #define RING_LOCALS \ int write, _nr; unsigned int tail_mask; volatile u32 *ring; #define BEGIN_RING( n ) do { \ if ( R128_VERBOSE ) { \ DRM_INFO( "BEGIN_RING( %d ) in %s\n", \ (n), __FUNCTION__ ); \ } \ if ( dev_priv->ring.space <= (n) * sizeof(u32) ) { \ COMMIT_RING(); \ r128_wait_ring( dev_priv, (n) * sizeof(u32) ); \ } \ _nr = n; dev_priv->ring.space -= (n) * sizeof(u32); \ ring = dev_priv->ring.start; \ write = dev_priv->ring.tail; \ tail_mask = dev_priv->ring.tail_mask; \ } while (0) /* You can set this to zero if you want. If the card locks up, you'll * need to keep this set. It works around a bug in early revs of the * Rage 128 chipset, where the CCE would read 32 dwords past the end of * the ring buffer before wrapping around. */ #define R128_BROKEN_CCE 1 #define ADVANCE_RING() do { \ if ( R128_VERBOSE ) { \ DRM_INFO( "ADVANCE_RING() wr=0x%06x tail=0x%06x\n", \ write, dev_priv->ring.tail ); \ } \ if ( R128_BROKEN_CCE && write < 32 ) { \ memcpy( dev_priv->ring.end, \ dev_priv->ring.start, \ write * sizeof(u32) ); \ } \ if (((dev_priv->ring.tail + _nr) & tail_mask) != write) { \ DRM_ERROR( \ "ADVANCE_RING(): mismatch: nr: %x write: %x line: %d\n", \ ((dev_priv->ring.tail + _nr) & tail_mask), \ write, __LINE__); \ } else \ dev_priv->ring.tail = write; \ } while (0) #define COMMIT_RING() do { \ if ( R128_VERBOSE ) { \ DRM_INFO( "COMMIT_RING() tail=0x%06x\n", \ dev_priv->ring.tail ); \ } \ DRM_MEMORYBARRIER(); \ R128_WRITE( R128_PM4_BUFFER_DL_WPTR, dev_priv->ring.tail ); \ R128_READ( R128_PM4_BUFFER_DL_WPTR ); \ } while (0) #define OUT_RING( x ) do { \ if ( R128_VERBOSE ) { \ DRM_INFO( " OUT_RING( 0x%08x ) at 0x%x\n", \ (unsigned int)(x), write ); \ } \ ring[write++] = cpu_to_le32( x ); \ write &= tail_mask; \ } while (0) #endif /* __R128_DRV_H__ */ 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
/*
 * \file xf86drmMode.c
 * Header for DRM modesetting interface.
 *
 * \author Jakob Bornecrantz <wallbraker@gmail.com>
 *
 * \par Acknowledgements:
 * Feb 2007, Dave Airlie <airlied@linux.ie>
 */

/*
 * Copyright (c) 2007-2008 Tungsten Graphics, Inc., Cedar Park, Texas.
 * Copyright (c) 2007-2008 Dave Airlie <airlied@linux.ie>
 * Copyright (c) 2007-2008 Jakob Bornecrantz <wallbraker@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 */

/*
 * TODO the types we are after are defined in diffrent headers on diffrent
 * platforms find which headers to include to get uint32_t
 */
#include <stdint.h>
#include <sys/ioctl.h>
#include <stdio.h>

#include "xf86drmMode.h"
#include "xf86drm.h"
#include <drm.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <errno.h>

#define U642VOID(x) ((void *)(unsigned long)(x))
#define VOID2U64(x) ((uint64_t)(unsigned long)(x))

static inline int DRM_IOCTL(int fd, unsigned long cmd, void *arg)
{
	int ret = drmIoctl(fd, cmd, arg);
	return ret < 0 ? -errno : ret;
}

/*
 * Util functions
 */

void* drmAllocCpy(void *array, int count, int entry_size)
{
	char *r;
	int i;

	if (!count || !array || !entry_size)
		return 0;

	if (!(r = drmMalloc(count*entry_size)))
		return 0;

	for (i = 0; i < count; i++)
		memcpy(r+(entry_size*i), array+(entry_size*i), entry_size);

	return r;
}

/*
 * A couple of free functions.
 */

void drmModeFreeModeInfo(drmModeModeInfoPtr ptr)
{
	if (!ptr)
		return;

	drmFree(ptr);
}

void drmModeFreeResources(drmModeResPtr ptr)
{
	if (!ptr)
		return;

	drmFree(ptr->fbs);
	drmFree(ptr->crtcs);
	drmFree(ptr->connectors);
	drmFree(ptr->encoders);
	drmFree(ptr);

}

void drmModeFreeFB(drmModeFBPtr ptr)
{
	if (!ptr)
		return;

	/* we might add more frees later. */
	drmFree(ptr);
}

void drmModeFreeCrtc(drmModeCrtcPtr ptr)
{
	if (!ptr)
		return;

	drmFree(ptr);

}

void drmModeFreeConnector(drmModeConnectorPtr ptr)
{
	if (!ptr)
		return;

	drmFree(ptr->encoders);
	drmFree(ptr->prop_values);
	drmFree(ptr->props);
	drmFree(ptr->modes);
	drmFree(ptr);

}

void drmModeFreeEncoder(drmModeEncoderPtr ptr)
{
	drmFree(ptr);
}

/*
 * ModeSetting functions.
 */

drmModeResPtr drmModeGetResources(int fd)
{
	struct drm_mode_card_res res, counts;
	drmModeResPtr r = 0;

retry:
	memset(&res, 0, sizeof(struct drm_mode_card_res));
	if (drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res))
		return 0;

	counts = res;

	if (res.count_fbs) {
		res.fb_id_ptr = VOID2U64(drmMalloc(res.count_fbs*sizeof(uint32_t)));
		if (!res.fb_id_ptr)
			goto err_allocs;
	}
	if (res.count_crtcs) {
		res.crtc_id_ptr = VOID2U64(drmMalloc(res.count_crtcs*sizeof(uint32_t)));
		if (!res.crtc_id_ptr)
			goto err_allocs;
	}
	if (res.count_connectors) {
		res.connector_id_ptr = VOID2U64(drmMalloc(res.count_connectors*sizeof(uint32_t)));
		if (!res.connector_id_ptr)
			goto err_allocs;
	}
	if (res.count_encoders) {
		res.encoder_id_ptr = VOID2U64(drmMalloc(res.count_encoders*sizeof(uint32_t)));
		if (!res.encoder_id_ptr)
			goto err_allocs;
	}

	if (drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res))
		goto err_allocs;

	/* The number of available connectors and etc may have changed with a
	 * hotplug event in between the ioctls, in which case the field is
	 * silently ignored by the kernel.
	 */
	if (counts.count_fbs < res.count_fbs ||
	    counts.count_crtcs < res.count_crtcs ||
	    counts.count_connectors < res.count_connectors ||
	    counts.count_encoders < res.count_encoders)
	{
		drmFree(U642VOID(res.fb_id_ptr));
		drmFree(U642VOID(res.crtc_id_ptr));
		drmFree(U642VOID(res.connector_id_ptr));
		drmFree(U642VOID(res.encoder_id_ptr));

		goto retry;
	}

	/*
	 * return
	 */
	if (!(r = drmMalloc(sizeof(*r))))
		goto err_allocs;

	r->min_width     = res.min_width;
	r->max_width     = res.max_width;
	r->min_height    = res.min_height;
	r->max_height    = res.max_height;
	r->count_fbs     = res.count_fbs;
	r->count_crtcs   = res.count_crtcs;
	r->count_connectors = res.count_connectors;
	r->count_encoders = res.count_encoders;

	r->fbs        = drmAllocCpy(U642VOID(res.fb_id_ptr), res.count_fbs, sizeof(uint32_t));
	r->crtcs      = drmAllocCpy(U642VOID(res.crtc_id_ptr), res.count_crtcs, sizeof(uint32_t));
	r->connectors = drmAllocCpy(U642VOID(res.connector_id_ptr), res.count_connectors, sizeof(uint32_t));
	r->encoders   = drmAllocCpy(U642VOID(res.encoder_id_ptr), res.count_encoders, sizeof(uint32_t));
	if ((res.count_fbs && !r->fbs) ||
	    (res.count_crtcs && !r->crtcs) ||
	    (res.count_connectors && !r->connectors) ||
	    (res.count_encoders && !r->encoders))
	{
		drmFree(r->fbs);
		drmFree(r->crtcs);
		drmFree(r->connectors);
		drmFree(r->encoders);
		drmFree(r);
		r = 0;
	}

err_allocs:
	drmFree(U642VOID(res.fb_id_ptr));
	drmFree(U642VOID(res.crtc_id_ptr));
	drmFree(U642VOID(res.connector_id_ptr));
	drmFree(U642VOID(res.encoder_id_ptr));

	return r;
}

int drmModeAddFB(int fd, uint32_t width, uint32_t height, uint8_t depth,
                 uint8_t bpp, uint32_t pitch, uint32_t bo_handle,
		 uint32_t *buf_id)
{
	struct drm_mode_fb_cmd f;
	int ret;

	f.width  = width;
	f.height = height;
	f.pitch  = pitch;
	f.bpp    = bpp;
	f.depth  = depth;
	f.handle = bo_handle;

	if ((ret = DRM_IOCTL(fd, DRM_IOCTL_MODE_ADDFB, &f)))
		return ret;

	*buf_id = f.fb_id;
	return 0;
}

int drmModeAddFB2(int fd, uint32_t width, uint32_t height,
		  uint32_t pixel_format, uint32_t bo_handles[4],
		  uint32_t pitches[4], uint32_t offsets[4],
		  uint32_t *buf_id, uint32_t flags)
{
	struct drm_mode_fb_cmd2 f;
	int ret;

	f.width  = width;
	f.height = height;
	f.pixel_format = pixel_format;
	f.flags = flags;
	memcpy(f.handles, bo_handles, 4 * sizeof(bo_handles[0]));
	memcpy(f.pitches, pitches, 4 * sizeof(pitches[0]));
	memcpy(f.offsets, offsets, 4 * sizeof(offsets[0]));

	if ((ret = DRM_IOCTL(fd, DRM_IOCTL_MODE_ADDFB2, &f)))
		return ret;

	*buf_id = f.fb_id;
	return 0;
}

int drmModeRmFB(int fd, uint32_t bufferId)
{
	return DRM_IOCTL(fd, DRM_IOCTL_MODE_RMFB, &bufferId);


}

drmModeFBPtr drmModeGetFB(int fd, uint32_t buf)
{
	struct drm_mode_fb_cmd info;
	drmModeFBPtr r;

	info.fb_id = buf;

	if (drmIoctl(fd, DRM_IOCTL_MODE_GETFB, &info))
		return NULL;

	if (!(r = drmMalloc(sizeof(*r))))
		return NULL;

	r->fb_id = info.fb_id;
	r->width = info.width;
	r->height = info.height;
	r->pitch = info.pitch;
	r->bpp = info.bpp;
	r->handle = info.handle;
	r->depth = info.depth;

	return r;
}

int drmModeDirtyFB(int fd, uint32_t bufferId,
		   drmModeClipPtr clips, uint32_t num_clips)
{
	struct drm_mode_fb_dirty_cmd dirty = { 0 };

	dirty.fb_id = bufferId;
	dirty.clips_ptr = VOID2U64(clips);
	dirty.num_clips = num_clips;

	return DRM_IOCTL(fd, DRM_IOCTL_MODE_DIRTYFB, &dirty);
}


/*
 * Crtc functions
 */

drmModeCrtcPtr drmModeGetCrtc(int fd, uint32_t crtcId)
{
	struct drm_mode_crtc crtc;
	drmModeCrtcPtr r;

	crtc.crtc_id = crtcId;

	if (drmIoctl(fd, DRM_IOCTL_MODE_GETCRTC, &crtc))
		return 0;

	/*
	 * return
	 */

	if (!(r = drmMalloc(sizeof(*r))))
		return 0;

	r->crtc_id         = crtc.crtc_id;
	r->x               = crtc.x;
	r->y               = crtc.y;
	r->mode_valid      = crtc.mode_valid;
	if (r->mode_valid)
		memcpy(&r->mode, &crtc.mode, sizeof(struct drm_mode_modeinfo));
	r->buffer_id       = crtc.fb_id;
	r->gamma_size      = crtc.gamma_size;
	return r;
}


int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId,
                   uint32_t x, uint32_t y, uint32_t *connectors, int count,
		   drmModeModeInfoPtr mode)
{
	struct drm_mode_crtc crtc;

	crtc.x             = x;
	crtc.y             = y;
	crtc.crtc_id       = crtcId;
	crtc.fb_id         = bufferId;
	crtc.set_connectors_ptr = VOID2U64(connectors);
	crtc.count_connectors = count;
	if (mode) {
	  memcpy(&crtc.mode, mode, sizeof(struct drm_mode_modeinfo));
	  crtc.mode_valid = 1;
	} else
	  crtc.mode_valid = 0;

	return DRM_IOCTL(fd, DRM_IOCTL_MODE_SETCRTC, &crtc);
}

/*
 * Cursor manipulation
 */

int drmModeSetCursor(int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height)
{
	struct drm_mode_cursor arg;

	arg.flags = DRM_MODE_CURSOR_BO;
	arg.crtc_id = crtcId;
	arg.width = width;
	arg.height = height;
	arg.handle = bo_handle;

	return DRM_IOCTL(fd, DRM_IOCTL_MODE_CURSOR, &arg);
}

int drmModeMoveCursor(int fd, uint32_t crtcId, int x, int y)
{
	struct drm_mode_cursor arg;

	arg.flags = DRM_MODE_CURSOR_MOVE;
	arg.crtc_id = crtcId;
	arg.x = x;
	arg.y = y;

	return DRM_IOCTL(fd, DRM_IOCTL_MODE_CURSOR, &arg);
}

/*
 * Encoder get
 */
drmModeEncoderPtr drmModeGetEncoder(int fd, uint32_t encoder_id)
{
	struct drm_mode_get_encoder enc;
	drmModeEncoderPtr r = NULL;

	enc.encoder_id = encoder_id;
	enc.encoder_type = 0;
	enc.possible_crtcs = 0;
	enc.possible_clones = 0;

	if (drmIoctl(fd, DRM_IOCTL_MODE_GETENCODER, &enc))
		return 0;

	if (!(r = drmMalloc(sizeof(*r))))
		return 0;

	r->encoder_id = enc.encoder_id;
	r->crtc_id = enc.crtc_id;
	r->encoder_type = enc.encoder_type;
	r->possible_crtcs = enc.possible_crtcs;
	r->possible_clones = enc.possible_clones;

	return r;
}

/*
 * Connector manipulation
 */

drmModeConnectorPtr drmModeGetConnector(int fd, uint32_t connector_id)
{
	struct drm_mode_get_connector conn, counts;
	drmModeConnectorPtr r = NULL;

retry:
	memset(&conn, 0, sizeof(struct drm_mode_get_connector));
	conn.connector_id = connector_id;

	if (drmIoctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn))
		return 0;

	counts = conn;

	if (conn.count_props) {
		conn.props_ptr = VOID2U64(drmMalloc(conn.count_props*sizeof(uint32_t)));
		if (!conn.props_ptr)
			goto err_allocs;
		conn.prop_values_ptr = VOID2U64(drmMalloc(conn.count_props*sizeof(uint64_t)));
		if (!conn.prop_values_ptr)
			goto err_allocs;
	}

	if (conn.count_modes) {
		conn.modes_ptr = VOID2U64(drmMalloc(conn.count_modes*sizeof(struct drm_mode_modeinfo)));
		if (!conn.modes_ptr)
			goto err_allocs;
	}

	if (conn.count_encoders) {
		conn.encoders_ptr = VOID2U64(drmMalloc(conn.count_encoders*sizeof(uint32_t)));
		if (!conn.encoders_ptr)
			goto err_allocs;
	}

	if (drmIoctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn))
		goto err_allocs;

	/* The number of available connectors and etc may have changed with a
	 * hotplug event in between the ioctls, in which case the field is
	 * silently ignored by the kernel.
	 */
	if (counts.count_props < conn.count_props ||
	    counts.count_modes < conn.count_modes ||
	    counts.count_encoders < conn.count_encoders) {
		drmFree(U642VOID(conn.props_ptr));
		drmFree(U642VOID(conn.prop_values_ptr));
		drmFree(U642VOID(conn.modes_ptr));
		drmFree(U642VOID(conn.encoders_ptr));

		goto retry;
	}

	if(!(r = drmMalloc(sizeof(*r)))) {
		goto err_allocs;
	}

	r->connector_id = conn.connector_id;
	r->encoder_id = conn.encoder_id;
	r->connection   = conn.connection;
	r->mmWidth      = conn.mm_width;
	r->mmHeight     = conn.mm_height;
	/* convert subpixel from kernel to userspace */
	r->subpixel     = conn.subpixel + 1;
	r->count_modes  = conn.count_modes;
	r->count_props  = conn.count_props;
	r->props        = drmAllocCpy(U642VOID(conn.props_ptr), conn.count_props, sizeof(uint32_t));
	r->prop_values  = drmAllocCpy(U642VOID(conn.prop_values_ptr), conn.count_props, sizeof(uint64_t));
	r->modes        = drmAllocCpy(U642VOID(conn.modes_ptr), conn.count_modes, sizeof(struct drm_mode_modeinfo));
	r->count_encoders = conn.count_encoders;
	r->encoders     = drmAllocCpy(U642VOID(conn.encoders_ptr), conn.count_encoders, sizeof(uint32_t));
	r->connector_type  = conn.connector_type;
	r->connector_type_id = conn.connector_type_id;

	if ((r->count_props && !r->props) ||
	    (r->count_props && !r->prop_values) ||
	    (r->count_modes && !r->modes) ||
	    (r->count_encoders && !r->encoders)) {
		drmFree(r->props);
		drmFree(r->prop_values);
		drmFree(r->modes);
		drmFree(r->encoders);
		drmFree(r);
		r = 0;
	}

err_allocs:
	drmFree(U642VOID(conn.prop_values_ptr));
	drmFree(U642VOID(conn.props_ptr));
	drmFree(U642VOID(conn.modes_ptr));
	drmFree(U642VOID(conn.encoders_ptr));

	return r;
}

int drmModeAttachMode(int fd, uint32_t connector_id, drmModeModeInfoPtr mode_info)
{
	struct drm_mode_mode_cmd res;

	memcpy(&res.mode, mode_info, sizeof(struct drm_mode_modeinfo));
	res.connector_id = connector_id;

	return DRM_IOCTL(fd, DRM_IOCTL_MODE_ATTACHMODE, &res);
}

int drmModeDetachMode(int fd, uint32_t connector_id, drmModeModeInfoPtr mode_info)
{
	struct drm_mode_mode_cmd res;

	memcpy(&res.mode, mode_info, sizeof(struct drm_mode_modeinfo));
	res.connector_id = connector_id;

	return DRM_IOCTL(fd, DRM_IOCTL_MODE_DETACHMODE, &res);
}


drmModePropertyPtr drmModeGetProperty(int fd, uint32_t property_id)
{
	struct drm_mode_get_property prop;
	drmModePropertyPtr r;

	prop.prop_id = property_id;
	prop.count_enum_blobs = 0;
	prop.count_values = 0;
	prop.flags = 0;
	prop.enum_blob_ptr = 0;
	prop.values_ptr = 0;

	if (drmIoctl(fd, DRM_IOCTL_MODE_GETPROPERTY, &prop))
		return 0;

	if (prop.count_values)
		prop.values_ptr = VOID2U64(drmMalloc(prop.count_values * sizeof(uint64_t)));

	if (prop.count_enum_blobs && (prop.flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)))
		prop.enum_blob_ptr = VOID2U64(drmMalloc(prop.count_enum_blobs * sizeof(struct drm_mode_property_enum)));

	if (prop.count_enum_blobs && (prop.flags & DRM_MODE_PROP_BLOB)) {
		prop.values_ptr = VOID2U64(drmMalloc(prop.count_enum_blobs * sizeof(uint32_t)));
		prop.enum_blob_ptr = VOID2U64(drmMalloc(prop.count_enum_blobs * sizeof(uint32_t)));
	}

	if (drmIoctl(fd, DRM_IOCTL_MODE_GETPROPERTY, &prop)) {
		r = NULL;
		goto err_allocs;
	}

	if (!(r = drmMalloc(sizeof(*r))))
		return NULL;

	r->prop_id = prop.prop_id;
	r->count_values = prop.count_values;

	r->flags = prop.flags;
	if (prop.count_values)
		r->values = drmAllocCpy(U642VOID(prop.values_ptr), prop.count_values, sizeof(uint64_t));
	if (prop.flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) {
		r->count_enums = prop.count_enum_blobs;
		r->enums = drmAllocCpy(U642VOID(prop.enum_blob_ptr), prop.count_enum_blobs, sizeof(struct drm_mode_property_enum));
	} else if (prop.flags & DRM_MODE_PROP_BLOB) {
		r->values = drmAllocCpy(U642VOID(prop.values_ptr), prop.count_enum_blobs, sizeof(uint32_t));
		r->blob_ids = drmAllocCpy(U642VOID(prop.enum_blob_ptr), prop.count_enum_blobs, sizeof(uint32_t));
		r->count_blobs = prop.count_enum_blobs;
	}
	strncpy(r->name, prop.name, DRM_PROP_NAME_LEN);
	r->name[DRM_PROP_NAME_LEN-1] = 0;

err_allocs:
	drmFree(U642VOID(prop.values_ptr));
	drmFree(U642VOID(prop.enum_blob_ptr));

	return r;
}

void drmModeFreeProperty(drmModePropertyPtr ptr)
{
	if (!ptr)
		return;

	drmFree(ptr->values);
	drmFree(ptr->enums);
	drmFree(ptr);
}

drmModePropertyBlobPtr drmModeGetPropertyBlob(int fd, uint32_t blob_id)
{
	struct drm_mode_get_blob blob;
	drmModePropertyBlobPtr r;

	blob.length = 0;
	blob.data = 0;
	blob.blob_id = blob_id;

	if (drmIoctl(fd, DRM_IOCTL_MODE_GETPROPBLOB, &blob))
		return NULL;

	if (blob.length)
		blob.data = VOID2U64(drmMalloc(blob.length));

	if (drmIoctl(fd, DRM_IOCTL_MODE_GETPROPBLOB, &blob)) {
		r = NULL;
		goto err_allocs;
	}

	if (!(r = drmMalloc(sizeof(*r))))
		goto err_allocs;

	r->id = blob.blob_id;
	r->length = blob.length;
	r->data = drmAllocCpy(U642VOID(blob.data), 1, blob.length);

err_allocs:
	drmFree(U642VOID(blob.data));
	return r;
}

void drmModeFreePropertyBlob(drmModePropertyBlobPtr ptr)
{
	if (!ptr)
		return;

	drmFree(ptr->data);
	drmFree(ptr);
}

int drmModeConnectorSetProperty(int fd, uint32_t connector_id, uint32_t property_id,
			     uint64_t value)
{
	struct drm_mode_connector_set_property osp;

	osp.connector_id = connector_id;
	osp.prop_id = property_id;
	osp.value = value;

	return DRM_IOCTL(fd, DRM_IOCTL_MODE_SETPROPERTY, &osp);
}

/*
 * checks if a modesetting capable driver has attached to the pci id
 * returns 0 if modesetting supported.
 *  -EINVAL or invalid bus id
 *  -ENOSYS if no modesetting support
*/
int drmCheckModesettingSupported(const char *busid)
{
#ifdef __linux__
	char pci_dev_dir[1024];
	int domain, bus, dev, func;
	DIR *sysdir;
	struct dirent *dent;
	int found = 0, ret;

	ret = sscanf(busid, "pci:%04x:%02x:%02x.%d", &domain, &bus, &dev, &func);
	if (ret != 4)
		return -EINVAL;

	sprintf(pci_dev_dir, "/sys/bus/pci/devices/%04x:%02x:%02x.%d/drm",
		domain, bus, dev, func);

	sysdir = opendir(pci_dev_dir);
	if (sysdir) {
		dent = readdir(sysdir);
		while (dent) {
			if (!strncmp(dent->d_name, "controlD", 8)) {
				found = 1;
				break;
			}

			dent = readdir(sysdir);
		}
		closedir(sysdir);
		if (found)
			return 0;
	}

	sprintf(pci_dev_dir, "/sys/bus/pci/devices/%04x:%02x:%02x.%d/",
		domain, bus, dev, func);

	sysdir = opendir(pci_dev_dir);
	if (!sysdir)
		return -EINVAL;

	dent = readdir(sysdir);
	while (dent) {
		if (!strncmp(dent->d_name, "drm:controlD", 12)) {
			found = 1;
			break;
		}

		dent = readdir(sysdir);
	}

	closedir(sysdir);
	if (found)
		return 0;
#endif
	return -ENOSYS;

}

int drmModeCrtcGetGamma(int fd, uint32_t crtc_id, uint32_t size,
			uint16_t *red, uint16_t *green, uint16_t *blue)
{
	struct drm_mode_crtc_lut l;

	l.crtc_id = crtc_id;
	l.gamma_size = size;
	l.red = VOID2U64(red);
	l.green = VOID2U64(green);
	l.blue = VOID2U64(blue);

	return DRM_IOCTL(fd, DRM_IOCTL_MODE_GETGAMMA, &l);
}

int drmModeCrtcSetGamma(int fd, uint32_t crtc_id, uint32_t size,
			uint16_t *red, uint16_t *green, uint16_t *blue)
{
	struct drm_mode_crtc_lut l;

	l.crtc_id = crtc_id;
	l.gamma_size = size;
	l.red = VOID2U64(red);
	l.green = VOID2U64(green);
	l.blue = VOID2U64(blue);

	return DRM_IOCTL(fd, DRM_IOCTL_MODE_SETGAMMA, &l);
}

int drmHandleEvent(int fd, drmEventContextPtr evctx)
{
	char buffer[1024];
	int len, i;
	struct drm_event *e;
	struct drm_event_vblank *vblank;
	
	/* The DRM read semantics guarantees that we always get only
	 * complete events. */

	len = read(fd, buffer, sizeof buffer);
	if (len == 0)
		return 0;
	if (len < sizeof *e)
		return -1;

	i = 0;
	while (i < len) {
		e = (struct drm_event *) &buffer[i];
		switch (e->type) {
		case DRM_EVENT_VBLANK:
			if (evctx->version < 1 ||
			    evctx->vblank_handler == NULL)
				break;
			vblank = (struct drm_event_vblank *) e;
			evctx->vblank_handler(fd,
					      vblank->sequence, 
					      vblank->tv_sec,
					      vblank->tv_usec,
					      U642VOID (vblank->user_data));
			break;
		case DRM_EVENT_FLIP_COMPLETE:
			if (evctx->version < 2 ||
			    evctx->page_flip_handler == NULL)
				break;
			vblank = (struct drm_event_vblank *) e;
			evctx->page_flip_handler(fd,
						 vblank->sequence,
						 vblank->tv_sec,
						 vblank->tv_usec,
						 U642VOID (vblank->user_data));
			break;
		default:
			break;
		}
		i += e->length;
	}

	return 0;
}

int drmModePageFlip(int fd, uint32_t crtc_id, uint32_t fb_id,
		    uint32_t flags, void *user_data)
{
	struct drm_mode_crtc_page_flip flip;

	flip.fb_id = fb_id;
	flip.crtc_id = crtc_id;
	flip.user_data = VOID2U64(user_data);
	flip.flags = flags;
	flip.reserved = 0;

	return DRM_IOCTL(fd, DRM_IOCTL_MODE_PAGE_FLIP, &flip);
}

int drmModeSetPlane(int fd, uint32_t plane_id, uint32_t crtc_id,
		    uint32_t fb_id, uint32_t flags,
		    uint32_t crtc_x, uint32_t crtc_y,
		    uint32_t crtc_w, uint32_t crtc_h,
		    uint32_t src_x, uint32_t src_y,
		    uint32_t src_w, uint32_t src_h)

{
	struct drm_mode_set_plane s;

	s.plane_id = plane_id;
	s.crtc_id = crtc_id;
	s.fb_id = fb_id;
	s.flags = flags;
	s.crtc_x = crtc_x;
	s.crtc_y = crtc_y;
	s.crtc_w = crtc_w;
	s.crtc_h = crtc_h;
	s.src_x = src_x;
	s.src_y = src_y;
	s.src_w = src_w;
	s.src_h = src_h;

	return DRM_IOCTL(fd, DRM_IOCTL_MODE_SETPLANE, &s);
}


drmModePlanePtr drmModeGetPlane(int fd, uint32_t plane_id)
{
	struct drm_mode_get_plane ovr, counts;
	drmModePlanePtr r = 0;

retry:
	memset(&ovr, 0, sizeof(struct drm_mode_get_plane));
	ovr.plane_id = plane_id;
	if (drmIoctl(fd, DRM_IOCTL_MODE_GETPLANE, &ovr))
		return 0;

	counts = ovr;

	if (ovr.count_format_types) {
		ovr.format_type_ptr = VOID2U64(drmMalloc(ovr.count_format_types *
							 sizeof(uint32_t)));
		if (!ovr.format_type_ptr)
			goto err_allocs;
	}

	if (drmIoctl(fd, DRM_IOCTL_MODE_GETPLANE, &ovr))
		goto err_allocs;

	if (counts.count_format_types < ovr.count_format_types) {
		drmFree(U642VOID(ovr.format_type_ptr));
		goto retry;
	}

	if (!(r = drmMalloc(sizeof(*r))))
		goto err_allocs;

	r->count_formats = ovr.count_format_types;
	r->plane_id = ovr.plane_id;
	r->crtc_id = ovr.crtc_id;
	r->fb_id = ovr.fb_id;
	r->possible_crtcs = ovr.possible_crtcs;
	r->gamma_size = ovr.gamma_size;
	r->formats = drmAllocCpy(U642VOID(ovr.format_type_ptr),
				 ovr.count_format_types, sizeof(uint32_t));
	if (ovr.count_format_types && !r->formats) {
		drmFree(r->formats);
		drmFree(r);
		r = 0;
	}

err_allocs:
	drmFree(U642VOID(ovr.format_type_ptr));

	return r;
}

void drmModeFreePlane(drmModePlanePtr ptr)
{
	if (!ptr)
		return;

	drmFree(ptr->formats);
	drmFree(ptr);
}

drmModePlaneResPtr drmModeGetPlaneResources(int fd)
{
	struct drm_mode_get_plane_res res, counts;
	drmModePlaneResPtr r = 0;

retry:
	memset(&res, 0, sizeof(struct drm_mode_get_plane_res));
	if (drmIoctl(fd, DRM_IOCTL_MODE_GETPLANERESOURCES, &res))
		return 0;

	counts = res;

	if (res.count_planes) {
		res.plane_id_ptr = VOID2U64(drmMalloc(res.count_planes *
							sizeof(uint32_t)));
		if (!res.plane_id_ptr)
			goto err_allocs;
	}

	if (drmIoctl(fd, DRM_IOCTL_MODE_GETPLANERESOURCES, &res))
		goto err_allocs;

	if (counts.count_planes < res.count_planes) {
		drmFree(U642VOID(res.plane_id_ptr));
		goto retry;
	}

	if (!(r = drmMalloc(sizeof(*r))))
		goto err_allocs;

	r->count_planes = res.count_planes;
	r->planes = drmAllocCpy(U642VOID(res.plane_id_ptr),
				  res.count_planes, sizeof(uint32_t));
	if (res.count_planes && !r->planes) {
		drmFree(r->planes);
		drmFree(r);
		r = 0;
	}

err_allocs:
	drmFree(U642VOID(res.plane_id_ptr));

	return r;
}

void drmModeFreePlaneResources(drmModePlaneResPtr ptr)
{
	if (!ptr)
		return;

	drmFree(ptr->planes);
	drmFree(ptr);
}

drmModeObjectPropertiesPtr drmModeObjectGetProperties(int fd,
						      uint32_t object_id,
						      uint32_t object_type)
{
	struct drm_mode_obj_get_properties properties;
	drmModeObjectPropertiesPtr ret = NULL;
	uint32_t count;

retry:
	memset(&properties, 0, sizeof(struct drm_mode_obj_get_properties));
	properties.obj_id = object_id;
	properties.obj_type = object_type;

	if (drmIoctl(fd, DRM_IOCTL_MODE_OBJ_GETPROPERTIES, &properties))
		return 0;

	count = properties.count_props;

	if (count) {
		properties.props_ptr = VOID2U64(drmMalloc(count *
							  sizeof(uint32_t)));
		if (!properties.props_ptr)
			goto err_allocs;
		properties.prop_values_ptr = VOID2U64(drmMalloc(count *
						      sizeof(uint64_t)));
		if (!properties.prop_values_ptr)
			goto err_allocs;
	}

	if (drmIoctl(fd, DRM_IOCTL_MODE_OBJ_GETPROPERTIES, &properties))
		goto err_allocs;

	if (count < properties.count_props) {
		drmFree(U642VOID(properties.props_ptr));
		drmFree(U642VOID(properties.prop_values_ptr));
		goto retry;
	}
	count = properties.count_props;

	ret = drmMalloc(sizeof(*ret));
	if (!ret)
		goto err_allocs;

	ret->count_props = count;
	ret->props = drmAllocCpy(U642VOID(properties.props_ptr),
				 count, sizeof(uint32_t));
	ret->prop_values = drmAllocCpy(U642VOID(properties.prop_values_ptr),
				       count, sizeof(uint64_t));
	if (ret->count_props && (!ret->props || !ret->prop_values)) {
		drmFree(ret->props);
		drmFree(ret->prop_values);
		drmFree(ret);
		ret = NULL;
	}

err_allocs:
	drmFree(U642VOID(properties.props_ptr));
	drmFree(U642VOID(properties.prop_values_ptr));
	return ret;
}

void drmModeFreeObjectProperties(drmModeObjectPropertiesPtr ptr)
{
	if (!ptr)
		return;
	drmFree(ptr->props);
	drmFree(ptr->prop_values);
	drmFree(ptr);
}

int drmModeObjectSetProperty(int fd, uint32_t object_id, uint32_t object_type,
			     uint32_t property_id, uint64_t value)
{
	struct drm_mode_obj_set_property prop;

	prop.value = value;
	prop.prop_id = property_id;
	prop.obj_id = object_id;
	prop.obj_type = object_type;

	return DRM_IOCTL(fd, DRM_IOCTL_MODE_OBJ_SETPROPERTY, &prop);
}