diff -Naurp old/src/external/mit/xorg/lib/libdrm/drm/Makefile new/src/external/mit/xorg/lib/libdrm/drm/Makefile --- old/src/external/mit/xorg/lib/libdrm/drm/Makefile 2010-05-23 01:19:24.000000000 +0200 +++ new/src/external/mit/xorg/lib/libdrm/drm/Makefile 2011-02-08 11:14:00.000000000 +0100 @@ -4,29 +4,11 @@ .include -.PATH: ${X11SRCDIR.drm}/include/drm -.PATH: ${X11SRCDIR.drm}/intel -.PATH: ${X11SRCDIR.drm}/radeon +.PATH: ${NETBSDSRCDIR}/sys/dev/pci/drm INCS= drm.h \ - drm_mode.h \ drm_sarea.h \ - i915_drm.h \ - intel_bufmgr.h \ - mach64_drm.h \ - mga_drm.h \ - nouveau_drm.h \ - r128_drm.h \ - radeon_bo.h \ - radeon_bo_gem.h \ - radeon_bo_int.h \ - radeon_cs.h \ - radeon_cs_gem.h \ - radeon_cs_int.h \ - radeon_drm.h \ - savage_drm.h \ - sis_drm.h \ - via_drm.h + i915_drm.h INCSDIR=${X11INCDIR}/libdrm diff -Naurp old/src/external/mit/xorg/lib/libdrm/Makefile new/src/external/mit/xorg/lib/libdrm/Makefile --- old/src/external/mit/xorg/lib/libdrm/Makefile 2010-05-23 01:19:24.000000000 +0200 +++ new/src/external/mit/xorg/lib/libdrm/Makefile 2011-02-08 11:14:00.000000000 +0100 @@ -5,14 +5,14 @@ LIB= drm .PATH: ${X11SRCDIR.${LIB}} -SRCS= xf86drm.c xf86drmHash.c xf86drmRandom.c xf86drmSL.c +SRCS= xf86drm.c xf86drmHash.c xf86drmMode.c xf86drmRandom.c xf86drmSL.c INCS= xf86drm.h xf86drmMode.h INCSDIR=${X11INCDIR} CPPFLAGS+= -I${DESTDIR}${X11INCDIR}/libdrm -SUBDIR= drm kms +SUBDIR= drm PKGCONFIG= libdrm PKGDIST= ${LIB} diff -Naurp old/src/external/mit/xorg/lib/libdrm_intel/Makefile new/src/external/mit/xorg/lib/libdrm_intel/Makefile --- old/src/external/mit/xorg/lib/libdrm_intel/Makefile 2010-06-03 11:43:16.000000000 +0200 +++ new/src/external/mit/xorg/lib/libdrm_intel/Makefile 2011-02-08 11:14:00.000000000 +0100 @@ -7,6 +7,9 @@ LIB= drm_intel SRCS= intel_bufmgr.c intel_bufmgr_fake.c intel_bufmgr_gem.c mm.c +INCS= intel_bufmgr.h +INCSDIR=${X11INCDIR}/libdrm + CPPFLAGS+= -I${DESTDIR}${X11INCDIR}/libdrm CPPFLAGS+= -I${DESTDIR}${X11INCDIR}/X11 -I${X11SRCDIR.drm} diff -Naurp old/src/external/mit/xorg/server/drivers/xf86-video-intel/Makefile new/src/external/mit/xorg/server/drivers/xf86-video-intel/Makefile --- old/src/external/mit/xorg/server/drivers/xf86-video-intel/Makefile 2011-01-19 00:33:52.000000000 +0100 +++ new/src/external/mit/xorg/server/drivers/xf86-video-intel/Makefile 2011-02-08 11:14:00.000000000 +0100 @@ -6,25 +6,39 @@ DRIVER_NAME= intel_drv SRCS= drmmode_display.c i810_accel.c i810_cursor.c i810_dga.c SRCS+= i810_driver.c i810_io.c i810_memory.c i810_video.c SRCS+= i810_wmark.c i830_3d.c i830_accel.c i830_bios.c -SRCS+= i830_batchbuffer.c i830_crt.c i830_cursor.c i830_debug.c +SRCS+= i830_batchbuffer.c i830_crt.c i830_cursor.c SRCS+= i830_display.c i830_quirks.c i830_driver.c i830_dvo.c SRCS+= i830_hdmi.c i830_i2c.c i830_io.c i830_lvds.c i830_memory.c SRCS+= i830_modes.c i830_video.c i830_sdvo.c i830_tv.c -SRCS+= i915_3d.c i915_video.c i965_video.c -SRCS+= i830_xaa.c i830_render.c i915_render.c i965_render.c -SRCS+= i830_dri.c i830_exa.c -SRCS+= i830_hwmc.c i915_hwmc.c i965_hwmc.c +SRCS+= i915_3d.c i915_video.c i965_video.c +SRCS+= i830_uxa.c i830_render.c i915_render.c i965_render.c +SRCS+= i810_dri.c i830_dri.c i810_hwmc.c +#SRCS+= i830_hwmc.c +SRCS+= uxa.c uxa-accel.c uxa-glyphs.c uxa-render.c uxa-unaccel.c MAN= intel.4 -CPPFLAGS+= -I${DESTDIR}${X11INCDIR}/X11 -CPPFLAGS+= -I${DESTDIR}${X11INCDIR}/X11/dri -CPPFLAGS+= -I${DESTDIR}${X11INCDIR}/libdrm -CPPFLAGS+= -DI830_XV -DI830_USE_XAA -DI830_USE_EXA -DINTEL_XVMC +CPPFLAGS+= -I${DESTDIR}${X11INCDIR}/X11 +CPPFLAGS+= -I${DESTDIR}${X11INCDIR}/X11/dri +CPPFLAGS+= -I${DESTDIR}${X11INCDIR}/libdrm +CPPFLAGS+= -I${X11SRCDIR.${DRIVER}}/uxa +CPPFLAGS+= -I${X11SRCDIR.${DRIVER}}/src/render_program +#CPPFLAGS+= -DINTEL_XVMC LDADD+= -ldrm_intel .include "../Makefile.xf86-driver" +.PATH: ${X11SRCDIR.${DRIVER}}/uxa + +# configure disables XvMC support by default +CPPFLAGS+= -UENABLE_XVMC -UINTEL_XVMC + +# no KMS support +CPPFLAGS+= -UKMS_ONLY + +# debug messages are most welcome (for now) +CPPFLAGS+= -DI830DEBUG + SUBDIR= ch7017 ch7xxx ivch sil164 tfp410 .include diff -Naurp old/src/sys/arch/amd64/conf/GENERIC new/src/sys/arch/amd64/conf/GENERIC --- old/src/sys/arch/amd64/conf/GENERIC 2011-01-26 01:25:55.000000000 +0100 +++ new/src/sys/arch/amd64/conf/GENERIC 2011-02-08 11:14:00.000000000 +0100 @@ -375,14 +375,18 @@ pcppi0 at isa? sysbeep0 at pcppi? # DRI driver -i915drm* at vga? # Intel i915, i945 DRM driver -mach64drm* at vga? # mach64 (3D Rage Pro, Rage) DRM driver -mgadrm* at vga? # Matrox G[24]00, G[45]50 DRM driver -r128drm* at vga? # ATI Rage 128 DRM driver -radeondrm* at vga? # ATI Radeon DRM driver -savagedrm* at vga? # S3 Savage DRM driver -sisdrm* at vga? # SiS DRM driver -tdfxdrm* at vga? # 3dfx (voodoo) DRM driver +#i915drm* at vga? # Intel i915, i945 DRM driver +#mach64drm* at vga? # mach64 (3D Rage Pro, Rage) DRM driver +#mgadrm* at vga? # Matrox G[24]00, G[45]50 DRM driver +#r128drm* at vga? # ATI Rage 128 DRM driver +#radeondrm* at vga? # ATI Radeon DRM driver +#savagedrm* at vga? # S3 Savage DRM driver +#sisdrm* at vga? # SiS DRM driver +#tdfxdrm* at vga? # 3dfx (voodoo) DRM driver + +# New DRM/GEM driver ported from OpenBSD (Intel only) +inteldrm* at vga? +drmdev* at inteldrm? # Cryptographic Devices diff -Naurp old/src/sys/arch/i386/conf/GENERIC new/src/sys/arch/i386/conf/GENERIC --- old/src/sys/arch/i386/conf/GENERIC 2011-01-26 19:48:12.000000000 +0100 +++ new/src/sys/arch/i386/conf/GENERIC 2011-02-08 11:14:00.000000000 +0100 @@ -565,15 +565,19 @@ pcppi0 at isa? sysbeep0 at pcppi? # DRI driver -i915drm* at vga? # Intel i915, i945 DRM driver -mach64drm* at vga? # mach64 (3D Rage Pro, Rage) DRM driver -mgadrm* at vga? # Matrox G[24]00, G[45]50 DRM driver -r128drm* at vga? # ATI Rage 128 DRM driver -radeondrm* at vga? # ATI Radeon DRM driver -savagedrm* at vga? # S3 Savage DRM driver -sisdrm* at vga? # SiS DRM driver -tdfxdrm* at vga? # 3dfx (voodoo) DRM driver -viadrm* at vga? # VIA DRM driver +#i915drm* at vga? # Intel i915, i945 DRM driver +#mach64drm* at vga? # mach64 (3D Rage Pro, Rage) DRM driver +#mgadrm* at vga? # Matrox G[24]00, G[45]50 DRM driver +#r128drm* at vga? # ATI Rage 128 DRM driver +#radeondrm* at vga? # ATI Radeon DRM driver +#savagedrm* at vga? # S3 Savage DRM driver +#sisdrm* at vga? # SiS DRM driver +#tdfxdrm* at vga? # 3dfx (voodoo) DRM driver +#viadrm* at vga? # VIA DRM driver + +# New DRM/GEM driver ported from OpenBSD (Intel only) +inteldrm* at vga? +drmdev* at inteldrm? # Serial Devices diff -Naurp old/src/sys/arch/i386/eisa/eisa_machdep.c new/src/sys/arch/i386/eisa/eisa_machdep.c --- old/src/sys/arch/i386/eisa/eisa_machdep.c 2009-11-18 00:51:59.000000000 +0100 +++ new/src/sys/arch/i386/eisa/eisa_machdep.c 2011-02-08 11:14:00.000000000 +0100 @@ -93,6 +93,7 @@ __KERNEL_RCSID(0, "$NetBSD: eisa_machdep * of these funcions. */ struct x86_bus_dma_tag eisa_bus_dma_tag = { + NULL, /* cookie */ 0, /* _tag_needs_free */ 0, /* _bounce_thresh */ 0, /* _bounce_alloc_lo */ diff -Naurp old/src/sys/arch/i386/mca/mca_machdep.c new/src/sys/arch/i386/mca/mca_machdep.c --- old/src/sys/arch/i386/mca/mca_machdep.c 2010-03-23 22:18:23.000000000 +0100 +++ new/src/sys/arch/i386/mca/mca_machdep.c 2011-02-08 11:14:00.000000000 +0100 @@ -101,6 +101,7 @@ static void _mca_bus_dmamap_sync(bus_dma #define MCA_DMA_BOUNCE_THRESHOLD (16 * 1024 * 1024) struct x86_bus_dma_tag mca_bus_dma_tag = { + NULL, /* cookie */ 0, MCA_DMA_BOUNCE_THRESHOLD, /* _bounce_thresh */ 0, /* _bounce_alloc_lo */ diff -Naurp old/src/sys/arch/x86/conf/files.x86 new/src/sys/arch/x86/conf/files.x86 --- old/src/sys/arch/x86/conf/files.x86 2010-07-18 11:29:12.000000000 +0200 +++ new/src/sys/arch/x86/conf/files.x86 2011-02-08 11:14:00.000000000 +0100 @@ -66,6 +66,7 @@ file arch/x86/x86/patch.c file arch/x86/x86/platform.c file arch/x86/x86/pmap.c file arch/x86/x86/procfs_machdep.c procfs +file arch/x86/x86/sg_dma.c file arch/x86/x86/sys_machdep.c file arch/x86/x86/syscall.c file arch/x86/x86/vm_machdep.c diff -Naurp old/src/sys/arch/x86/include/bus.h new/src/sys/arch/x86/include/bus.h --- old/src/sys/arch/x86/include/bus.h 2010-04-28 21:17:04.000000000 +0200 +++ new/src/sys/arch/x86/include/bus.h 2011-02-08 11:14:00.000000000 +0100 @@ -111,6 +111,13 @@ typedef struct x86_bus_dmamap *bus_dmam typedef struct x86_bus_dma_segment { bus_addr_t ds_addr; /* DMA address */ bus_size_t ds_len; /* length of transfer */ + /* + * Ugh. need this so can pass alignment down from bus_dmamem_alloc + * to scatter gather maps. only the first one is used so the rest is + * wasted space. bus_dma could do with fixing the api for this. + */ + bus_size_t _ds_boundary; /* don't cross */ + bus_size_t _ds_align; /* align to me */ } bus_dma_segment_t; /* @@ -142,4 +149,11 @@ struct x86_bus_dmamap { #include +/* + * Used to tune _bus_dmamem_alloc_range() for sg_dmamem_alloc(). + * See also comment in struct x86_bus_dma_segment. + * XXX Fix this! + */ +#define BUS_DMA_SG 0x8000 /* Internal. memory is for SG map */ + #endif /* _X86_BUS_H_ */ diff -Naurp old/src/sys/arch/x86/include/bus_private.h new/src/sys/arch/x86/include/bus_private.h --- old/src/sys/arch/x86/include/bus_private.h 2010-11-12 14:40:11.000000000 +0100 +++ new/src/sys/arch/x86/include/bus_private.h 2011-02-08 11:14:00.000000000 +0100 @@ -192,6 +192,8 @@ _bus_virt_to_bus(struct pmap *pm, vaddr_ #endif struct x86_bus_dma_tag { + void *_cookie; /* cookie used in the guts */ + /* * The `bounce threshold' is checked while we are loading * the DMA map. If the physical address of the segment diff -Naurp old/src/sys/arch/x86/include/sg_dma.h new/src/sys/arch/x86/include/sg_dma.h --- old/src/sys/arch/x86/include/sg_dma.h 1970-01-01 01:00:00.000000000 +0100 +++ new/src/sys/arch/x86/include/sg_dma.h 2011-02-08 11:14:00.000000000 +0100 @@ -0,0 +1,135 @@ +/* $OpenBSD: bus.h,v 1.24 2010/09/06 19:05:48 kettenis Exp $ */ +/* $NetBSD: bus.h,v 1.6 1996/11/10 03:19:25 thorpej Exp $ */ + +/*- + * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 1996 Charles M. Hannum. All rights reserved. + * Copyright (c) 1996 Jason R. Thorpe. All rights reserved. + * Copyright (c) 1996 Christopher G. Demetriou. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christopher G. Demetriou + * for the NetBSD Project. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _X86_SG_DMA_H_ +#define _X86_SG_DMA_H_ + +#include +#include +#include +#include +#include + +#include + +/* Scatter gather bus_dma functions. */ +struct sg_cookie { + kmutex_t sg_mtx; + struct extent *sg_ex; + void *sg_hdl; + + void (*bind_page)(void *, bus_addr_t, paddr_t, int); + void (*unbind_page)(void *, bus_addr_t); + void (*flush_tlb)(void *); +}; + +/* + * per-map DVMA page table + */ +struct sg_page_entry { + SPLAY_ENTRY(sg_page_entry) spe_node; + paddr_t spe_pa; + bus_addr_t spe_va; +}; + +/* for sg_dma this will be in the map's dm_cookie. */ +struct sg_page_map { + SPLAY_HEAD(sg_page_tree, sg_page_entry) spm_tree; + + void *spm_origbuf; /* pointer to original data */ + int spm_buftype; /* type of data */ + struct proc *spm_proc; /* proc that owns the mapping */ + + int spm_maxpage; /* Size of allocated page map */ + int spm_pagecnt; /* Number of entries in use */ + bus_addr_t spm_start; /* dva when bound */ + bus_size_t spm_size; /* size of bound map */ + struct sg_page_entry spm_map[1]; +}; + +struct sg_cookie *sg_dmatag_init(char *, void *, bus_addr_t, bus_size_t, + void (*)(void *, bus_addr_t, paddr_t, int), + void (*)(void *, bus_addr_t), void (*)(void *)); +void sg_dmatag_destroy(struct sg_cookie *); +int sg_dmamap_create(bus_dma_tag_t, bus_size_t, int, bus_size_t, + bus_size_t, int, bus_dmamap_t *); +void sg_dmamap_destroy(bus_dma_tag_t, bus_dmamap_t); +void sg_dmamap_set_alignment(bus_dma_tag_t, bus_dmamap_t, u_long); +int sg_dmamap_load(bus_dma_tag_t, bus_dmamap_t, void *, bus_size_t, + struct proc *, int); +int sg_dmamap_load_mbuf(bus_dma_tag_t, bus_dmamap_t, + struct mbuf *, int); +int sg_dmamap_load_uio(bus_dma_tag_t, bus_dmamap_t, struct uio *, int); +int sg_dmamap_load_raw(bus_dma_tag_t, bus_dmamap_t, bus_dma_segment_t *, + int, bus_size_t, int); +void sg_dmamap_unload(bus_dma_tag_t, bus_dmamap_t); +int sg_dmamap_load_buffer(bus_dma_tag_t, bus_dmamap_t, void *, bus_size_t, + struct proc *, int, int *, int); +int sg_dmamap_load_physarray(bus_dma_tag_t, bus_dmamap_t, paddr_t *, + int, int, int *, int); +int sg_dmamem_alloc(bus_dma_tag_t, bus_size_t, bus_size_t, bus_size_t, + bus_dma_segment_t *, int, int *, int); + +#endif /* _X86_SG_DMA_H_ */ diff -Naurp old/src/sys/arch/x86/isa/isa_machdep.c new/src/sys/arch/x86/isa/isa_machdep.c --- old/src/sys/arch/x86/isa/isa_machdep.c 2009-08-19 17:04:27.000000000 +0200 +++ new/src/sys/arch/x86/isa/isa_machdep.c 2011-02-08 11:14:00.000000000 +0100 @@ -96,6 +96,7 @@ __KERNEL_RCSID(0, "$NetBSD: isa_machdep. static int _isa_dma_may_bounce(bus_dma_tag_t, bus_dmamap_t, int, int *); struct x86_bus_dma_tag isa_bus_dma_tag = { + NULL, /* cookie */ 0, /* _tag_needs_free */ ISA_DMA_BOUNCE_THRESHOLD, /* _bounce_thresh */ 0, /* _bounce_alloc_lo */ diff -Naurp old/src/sys/arch/x86/pci/agp_machdep.c new/src/sys/arch/x86/pci/agp_machdep.c --- old/src/sys/arch/x86/pci/agp_machdep.c 2006-12-18 13:11:33.000000000 +0100 +++ new/src/sys/arch/x86/pci/agp_machdep.c 2011-02-08 11:14:00.000000000 +0100 @@ -1,23 +1,464 @@ -/* $NetBSD: agp_machdep.c,v 1.1 2006/12/18 12:11:33 christos Exp $ */ +/* $OpenBSD: agp_machdep.c,v 1.6 2010/05/10 22:06:04 oga Exp $ */ -#include -__KERNEL_RCSID(0, "$NetBSD: agp_machdep.c,v 1.1 2006/12/18 12:11:33 christos Exp $"); +/* + * Copyright (c) 2008 - 2009 Owain G. Ainsworth + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/* + * Copyright (c) 2002 Michael Shalayeff + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ #include #include - -#include -#include +#include +#include +#include #include #include #include #include +#include #include +#include +#include + +#include + +#include "agp_i810.h" + +/* bus_dma functions */ + +#if NAGP_I810 > 0 +void intagp_dma_sync(bus_dma_tag_t, bus_dmamap_t, bus_addr_t, + bus_size_t, int); +#endif /* NAGP_I810 > 0 */ + +static void agp_sg_bind_page(void *, bus_addr_t, paddr_t, int); +static void agp_sg_unbind_page(void *, bus_addr_t); +static void agp_sg_flush_tlb(void *); void agp_flush_cache(void) { - wbinvd(); + wbinvd(); +} + +void +agp_flush_cache_range(vaddr_t va, vsize_t sz) +{ +#if defined(__HAVE_PMAP_FLUSH_CACHE) + pmap_flush_cache(va, sz); +#else /* defined(__HAVE_PMAP_FLUSH_CACHE) */ + wbinvd(); +#endif /* defined(__HAVE_PMAP_FLUSH_CACHE) */ +} + +/* + * functions for bus_dma used by drm for GEM + * + * We use the sg_dma backend (also used by iommu) to provide the actual + * implementation, so all we need provide is the magic to create the tag, and + * the appropriate callbacks. + * + * We give the backend drivers a chance to honour the bus_dma flags, some of + * these may be used, for example to provide snooped mappings (intagp). + * For intagp at least, we honour the BUS_DMA_COHERENT flag, though it is not + * used often, and is * technically to be used for dmamem_map, we use it for + * dmamap_load since adding coherency involes flags to the gtt pagetables. + * We only use it for very special circumstances since when a GTT mapping is + * set to coherent, the cpu can't read or write through the gtt aperture. + * + * Currently, since the userland agp driver still needs to access the gart, we + * only do bus_dma for a section that we've been told is ours, hence the need + * for the init function at present. + */ + +static void +agp_sg_bind_page(void *dev, bus_addr_t address, paddr_t physical, int flags) +{ + struct agp_softc *sc = dev; + int error; + + error = AGP_BIND_PAGE(sc, address - sc->as_apaddr, + _BUS_PHYS_TO_BUS(physical), flags); + if (error) + aprint_error_dev(sc->as_dev, + "%s: failed: ba %#"PRIxPADDR", pa %#"PRIxPADDR"\n", + __func__, address, physical); +} + +static void +agp_sg_unbind_page(void *dev, bus_addr_t address) +{ + struct agp_softc *sc = dev; + int error; + + error = AGP_UNBIND_PAGE(sc, address - sc->as_apaddr); + if (error) + aprint_error_dev(sc->as_dev, "%s: failed: ba %#"PRIxPADDR"\n", + __func__, address); +} + +static void +agp_sg_flush_tlb(void *dev) +{ + struct agp_softc *sc = dev; + + AGP_FLUSH_TLB(sc); +} + +int +agp_bus_dma_init(struct agp_softc *sc, bus_addr_t start, bus_addr_t end, + bus_dma_tag_t *dmat) +{ + struct x86_bus_dma_tag *tag; + struct sg_cookie *cookie; + + /* + * XXX add agp map into the main queue that takes up our chunk of + * GTT space to prevent the userland api stealing any of it. + */ + if ((tag = malloc(sizeof(*tag), M_DMAMAP, + M_ZERO | M_WAITOK | M_CANFAIL)) == NULL) + return (ENOMEM); + + if ((cookie = sg_dmatag_init(__UNCONST("agpgtt"), sc, + start, end - start, + agp_sg_bind_page, agp_sg_unbind_page, agp_sg_flush_tlb)) == NULL) { + free(tag, M_DMAMAP); + return (ENOMEM); + } + + tag->_cookie = cookie; + tag->_dmamap_create = sg_dmamap_create; + tag->_dmamap_destroy = sg_dmamap_destroy; + tag->_dmamap_load = sg_dmamap_load; + tag->_dmamap_load_mbuf = sg_dmamap_load_mbuf; + tag->_dmamap_load_uio = sg_dmamap_load_uio; + tag->_dmamap_load_raw = sg_dmamap_load_raw; + tag->_dmamap_unload = sg_dmamap_unload; + tag->_dmamem_alloc = sg_dmamem_alloc; + tag->_dmamem_free = _bus_dmamem_free; + tag->_dmamem_map = _bus_dmamem_map; + tag->_dmamem_unmap = _bus_dmamem_unmap; + tag->_dmamem_mmap = _bus_dmamem_mmap; + tag->_dmatag_subregion = _bus_dmatag_subregion; + tag->_dmatag_destroy = _bus_dmatag_destroy; + + /* Driver may need special sync handling */ + if (sc->as_methods->dma_sync != NULL) { + tag->_dmamap_sync = sc->as_methods->dma_sync; + } else { +#ifdef __i386__ + tag->_dmamap_sync = NULL; +#else + tag->_dmamap_sync = _bus_dmamap_sync; +#endif + } + + *dmat = tag; + return (0); +} + +void +agp_bus_dma_destroy(struct agp_softc *sc, bus_dma_tag_t dmat) +{ + struct sg_cookie *cookie = dmat->_cookie; + bus_addr_t offset; + + + /* + * XXX clear up blocker queue + */ + + /* + * some backends use a dummy page to avoid errors on prefetching, etc. + * make sure that all of them are clean. + */ + for (offset = cookie->sg_ex->ex_start; + offset < cookie->sg_ex->ex_end; offset += PAGE_SIZE) + agp_sg_unbind_page(sc, offset); + + sg_dmatag_destroy(cookie); + free(dmat, M_DMAMAP); +} + +void +agp_bus_dma_set_alignment(bus_dma_tag_t tag, bus_dmamap_t dmam, + u_long alignment) +{ + sg_dmamap_set_alignment(tag, dmam, alignment); +} + +struct agp_map { + bus_space_tag_t bst; + bus_size_t size; +#ifdef __i386__ + bus_addr_t addr; + int flags; +#else + bus_space_handle_t bsh; +#endif +}; + +#ifdef __i386__ +extern struct extent *ioport_ex; +extern struct extent *iomem_ex; +#endif + +int +agp_init_map(bus_space_tag_t tag, bus_addr_t address, bus_size_t size, + int flags, struct agp_map **mapp) +{ +#ifdef __i386__ + struct extent *ex; +#endif + struct agp_map *map; + int error; + +#ifdef __i386__ + if (tag->bst_type == X86_BUS_SPACE_IO) { + ex = ioport_ex; + if (flags & BUS_SPACE_MAP_LINEAR) + return (EINVAL); + } else if (tag->bst_type == X86_BUS_SPACE_MEM) { + ex = iomem_ex; + } else { + panic("agp_init_map: bad bus space tag"); + } + /* + * We grab the extent out of the bus region ourselves + * so we don't need to do these allocations every time. + */ + error = extent_alloc_region(ex, address, size, + EX_NOWAIT | EX_MALLOCOK); + if (error) + return (error); +#endif + + map = malloc(sizeof(*map), M_AGP, M_WAITOK | M_CANFAIL); + if (map == NULL) + return (ENOMEM); + + map->bst = tag; + map->size = size; +#ifdef __i386__ + map->addr = address; + map->flags = flags; +#else + if ((error = bus_space_map(tag, address, size, flags, &map->bsh)) != 0) { + free(map, M_AGP); + return (error); + } +#endif + + *mapp = map; + return (0); +} + +void +agp_destroy_map(struct agp_map *map) +{ +#ifdef __i386__ + struct extent *ex; + + if (map->bst->bst_type == X86_BUS_SPACE_IO) + ex = ioport_ex; + else if (map->bst->bst_type == X86_BUS_SPACE_MEM) + ex = iomem_ex; + else + panic("agp_destroy_map: bad bus space tag"); + + if (extent_free(ex, map->addr, map->size, + EX_NOWAIT | EX_MALLOCOK )) + printf("agp_destroy_map: can't free region\n"); +#else + bus_space_unmap(map->bst, map->bsh, map->size); +#endif + free(map, M_AGP); +} + + +int +agp_map_subregion(struct agp_map *map, bus_size_t offset, bus_size_t size, + bus_space_handle_t *bshp) +{ +#ifdef __i386__ + return (_x86_memio_map(map->bst, map->addr + offset, size, + map->flags, bshp)); +#else + if (offset > map->size || size > map->size || offset + size > map->size) + return (EINVAL); + return (bus_space_subregion(map->bst, map->bsh, offset, size, bshp)); +#endif +} + +void +agp_unmap_subregion(struct agp_map *map, bus_space_handle_t bsh, + bus_size_t size) +{ +#ifdef __i386__ + return (_x86_memio_unmap(map->bst, bsh, size, NULL)); +#else + /* subregion doesn't need unmapping, do nothing */ +#endif +} + +/* + * ick ick ick. However, the rest of this driver is supposedly MI (though + * they only exist on x86), so this can't be in dev/pci. + */ + +#if NAGP_I810 > 0 + +/* + * bus_dmamap_sync routine for intagp. + * + * This is tailored to the usage that drm with the GEM memory manager + * will be using, since intagp is for intel IGD, and thus shouldn't be + * used for anything other than gpu-based work. Essentially for the intel GEM + * driver we use bus_dma as an abstraction to convert our memory into a gtt + * address and deal with any cache incoherencies that we create. + * + * We use the cflush instruction to deal with clearing the caches, since our + * cache is physically indexed, we can even map then clear the page and it'll + * work. on i386 we need to check for the presence of cflush() in cpuid, + * however, all cpus that have a new enough intel GMCH should be suitable. + */ +void +intagp_dma_sync(bus_dma_tag_t tag, bus_dmamap_t dmam, + bus_addr_t offset, bus_size_t size, int ops) +{ +#if defined(__HAVE_PMAP_FLUSH_CACHE) && defined(__HAVE_PMAP_FLUSH_PAGE) + bus_dma_segment_t *segp; + struct sg_page_map *spm; + vaddr_t addr; + paddr_t pa; + bus_addr_t poff, endoff, soff; +#endif /* defined(__HAVE_PMAP_FLUSH_CACHE) && defined(__HAVE_PMAP_FLUSH_PAGE) */ + +#ifdef DIAGNOSTIC + if ((ops & (BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE)) != 0 && + (ops & (BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE)) != 0) + panic("agp_dmamap_sync: mix PRE and POST"); + if (offset >= dmam->dm_mapsize) + panic("_intagp_dma_sync: bad offset %lu (size = %zu)", + offset, dmam->dm_mapsize); + if (size == 0 || (offset + size) > dmam->dm_mapsize) + panic("intagp_dma_sync: bad length"); +#endif /* DIAGNOSTIC */ + + /* Coherent mappings need no sync. */ + if (dmam->_dm_flags & BUS_DMA_COHERENT) + return; + + /* + * We need to clflush the object cache in all cases but postwrite. + * + * - Due to gpu incoherency, postread we need to flush speculative + * reads (which are not written back on intel cpus). + * + * - preread we need to flush data which will very soon be stale from + * the caches + * + * - prewrite we need to make sure our data hits the memory before the + * gpu hoovers it up. + * + * The chipset also may need flushing, but that fits badly into + * bus_dma and it done in the driver. + */ + if (ops & BUS_DMASYNC_POSTREAD || ops & BUS_DMASYNC_PREREAD || + ops & BUS_DMASYNC_PREWRITE) { +#if defined(__HAVE_PMAP_FLUSH_CACHE) && defined(__HAVE_PMAP_FLUSH_PAGE) + if (curcpu()->ci_cflush_lsize == 0) { + /* save some wbinvd()s. we're MD anyway so it's ok */ + wbinvd(); + return; + } + + soff = trunc_page(offset); + endoff = round_page(offset + size); + x86_mfence(); + spm = dmam->_dm_cookie; + switch (spm->spm_buftype) { + case X86_DMA_BUFTYPE_LINEAR: + addr = (vaddr_t)spm->spm_origbuf + soff; + while (soff < endoff) { + pmap_flush_cache(addr, PAGE_SIZE); + soff += PAGE_SIZE; + addr += PAGE_SIZE; + } break; + case X86_DMA_BUFTYPE_RAW: + segp = (bus_dma_segment_t *)spm->spm_origbuf; + poff = 0; + + while (poff < soff) { + if (poff + segp->ds_len > soff) + break; + poff += segp->ds_len; + segp++; + } + /* first time round may not start at seg beginning */ + pa = segp->ds_addr + (soff - poff); + while (poff < endoff) { + for (; pa < segp->ds_addr + segp->ds_len && + poff < endoff; pa += PAGE_SIZE) { + pmap_flush_page(pa); + poff += PAGE_SIZE; + } + segp++; + if (poff < endoff) + pa = segp->ds_addr; + } + break; + /* You do not want to load mbufs or uios onto a graphics card */ + case X86_DMA_BUFTYPE_MBUF: + /* FALLTHROUGH */ + case X86_DMA_BUFTYPE_UIO: + /* FALLTHROUGH */ + default: + panic("intagp_dmamap_sync: bad buftype %d", + spm->spm_buftype); + } + x86_mfence(); +#else /* defined(__HAVE_PMAP_FLUSH_CACHE) && defined(__HAVE_PMAP_FLUSH_PAGE) */ + wbinvd(); +#endif /* defined(__HAVE_PMAP_FLUSH_CACHE) && defined(__HAVE_PMAP_FLUSH_PAGE) */ + } } +#endif /* NAGP_I810 > 0 */ diff -Naurp old/src/sys/arch/x86/pci/pci_machdep.c new/src/sys/arch/x86/pci/pci_machdep.c --- old/src/sys/arch/x86/pci/pci_machdep.c 2010-04-30 23:05:27.000000000 +0200 +++ new/src/sys/arch/x86/pci/pci_machdep.c 2011-02-08 11:14:00.000000000 +0100 @@ -191,6 +191,7 @@ struct { * of these functions. */ struct x86_bus_dma_tag pci_bus_dma_tag = { + NULL, /* cookie */ 0, /* tag_needs_free */ #if defined(_LP64) || defined(PAE) PCI32_DMA_BOUNCE_THRESHOLD, /* bounce_thresh */ @@ -221,6 +222,7 @@ struct x86_bus_dma_tag pci_bus_dma_tag = #ifdef _LP64 struct x86_bus_dma_tag pci_bus_dma64_tag = { + NULL, /* cookie */ 0, /* tag_needs_free */ 0, 0, diff -Naurp old/src/sys/arch/x86/x86/bus_dma.c new/src/sys/arch/x86/x86/bus_dma.c --- old/src/sys/arch/x86/x86/bus_dma.c 2010-11-06 12:46:03.000000000 +0100 +++ new/src/sys/arch/x86/x86/bus_dma.c 2011-02-08 11:14:00.000000000 +0100 @@ -162,6 +162,13 @@ _bus_dmamem_alloc_range(bus_dma_tag_t t, KASSERT(boundary >= PAGE_SIZE || boundary == 0); + segs[0]._ds_boundary = boundary; + segs[0]._ds_align = alignment; + if (flags & BUS_DMA_SG) { + boundary = 0; + alignment = 0; + } + /* * Allocate pages from the VM system. * We accept boundaries < size, splitting in multiple segments diff -Naurp old/src/sys/arch/x86/x86/sg_dma.c new/src/sys/arch/x86/x86/sg_dma.c --- old/src/sys/arch/x86/x86/sg_dma.c 1970-01-01 01:00:00.000000000 +0100 +++ new/src/sys/arch/x86/x86/sg_dma.c 2011-02-08 11:14:00.000000000 +0100 @@ -0,0 +1,960 @@ +/* $OpenBSD: sg_dma.c,v 1.9 2010/04/20 23:12:01 phessler Exp $ */ +/* + * Copyright (c) 2009 Owain G. Ainsworth + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/* + * Copyright (c) 2003 Henric Jungheim + * Copyright (c) 2001, 2002 Eduardo Horvath + * Copyright (c) 1999, 2000 Matthew R. Green + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Support for scatter/gather style dma through agp or an iommu. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#ifndef MAX_DMA_SEGS +#define MAX_DMA_SEGS 20 +#endif + +/* Disable 24-bit DMA support if it's not supported by arch. */ +#ifndef BUS_DMA_24BIT +#define BUS_DMA_24BIT 0 +#endif + +int sg_dmamap_load_seg(bus_dma_tag_t, struct sg_cookie *, + bus_dmamap_t, bus_dma_segment_t *, int, int, bus_size_t, + bus_size_t); +struct sg_page_map *sg_iomap_create(int); +int sg_dmamap_append_range(bus_dma_tag_t, bus_dmamap_t, paddr_t, + bus_size_t, int, bus_size_t); +int sg_iomap_insert_page(struct sg_page_map *, paddr_t); +bus_addr_t sg_iomap_translate(struct sg_page_map *, paddr_t); +void sg_iomap_load_map(struct sg_cookie *, struct sg_page_map *, + bus_addr_t, int); +void sg_iomap_unload_map(struct sg_cookie *, struct sg_page_map *); +void sg_iomap_destroy(struct sg_page_map *); +void sg_iomap_clear_pages(struct sg_page_map *); + +struct sg_cookie * +sg_dmatag_init(char *name, void *hdl, bus_addr_t start, bus_size_t size, + void bind(void *, bus_addr_t, paddr_t, int), + void unbind(void *, bus_addr_t), void flush_tlb(void *)) +{ + struct sg_cookie *cookie; + + cookie = malloc(sizeof(*cookie), M_DMAMAP, M_NOWAIT|M_ZERO); + if (cookie == NULL) + return (NULL); + + cookie->sg_ex = extent_create(name, start, start + size - 1, + M_DMAMAP, NULL, 0, EX_NOWAIT | EX_NOCOALESCE); + if (cookie->sg_ex == NULL) { + free(cookie, M_DMAMAP); + return (NULL); + } + + cookie->sg_hdl = hdl; + mutex_init(&cookie->sg_mtx, MUTEX_DEFAULT, IPL_HIGH); + cookie->bind_page = bind; + cookie->unbind_page = unbind; + cookie->flush_tlb = flush_tlb; + + return (cookie); +} + +void +sg_dmatag_destroy(struct sg_cookie *cookie) +{ + mutex_destroy(&cookie->sg_mtx); + extent_destroy(cookie->sg_ex); + free(cookie, M_DMAMAP); +} + +int +sg_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments, + bus_size_t maxsegsz, bus_size_t boundary, int flags, bus_dmamap_t *dmamap) +{ + struct sg_page_map *spm; + bus_dmamap_t map; + int ret; + + if ((ret = _bus_dmamap_create(t, size, nsegments, maxsegsz, boundary, + flags, &map)) != 0) + return (ret); + + if ((spm = sg_iomap_create(atop(round_page(size)))) == NULL) { + _bus_dmamap_destroy(t, map); + return (ENOMEM); + } + + map->_dm_cookie = spm; + *dmamap = map; + + return (0); +} + +void +sg_dmamap_set_alignment(bus_dma_tag_t tag, bus_dmamap_t dmam, + u_long alignment) +{ + if (alignment < PAGE_SIZE) + return; + + dmam->dm_segs[0]._ds_align = alignment; +} + +void +sg_dmamap_destroy(bus_dma_tag_t t, bus_dmamap_t map) +{ + /* + * The specification (man page) requires a loaded + * map to be unloaded before it is destroyed. + */ + if (map->dm_nsegs) + bus_dmamap_unload(t, map); + + if (map->_dm_cookie) + sg_iomap_destroy(map->_dm_cookie); + map->_dm_cookie = NULL; + _bus_dmamap_destroy(t, map); +} + +/* + * Load a contiguous kva buffer into a dmamap. The physical pages are + * not assumed to be contiguous. Two passes are made through the buffer + * and both call pmap_extract() for the same va->pa translations. It + * is possible to run out of pa->dvma mappings; the code should be smart + * enough to resize the iomap (when the "flags" permit allocation). It + * is trivial to compute the number of entries required (round the length + * up to the page size and then divide by the page size)... + */ +int +sg_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf, + bus_size_t buflen, struct proc *p, int flags) +{ + int err = 0; + bus_size_t sgsize; + u_long dvmaddr, sgstart, sgend; + bus_size_t align, boundary; + struct sg_cookie *is = t->_cookie; + struct sg_page_map *spm = map->_dm_cookie; + pmap_t pmap; + + if (map->dm_nsegs) { + /* + * Is it still in use? _bus_dmamap_load should have taken care + * of this. + */ +#ifdef DIAGNOSTIC + panic("sg_dmamap_load: map still in use"); +#endif + bus_dmamap_unload(t, map); + } + + /* + * Make sure that on error condition we return "no valid mappings". + */ + map->dm_nsegs = 0; + + if (buflen < 1 || buflen > map->_dm_size) + return (EINVAL); + + /* + * A boundary presented to bus_dmamem_alloc() takes precedence + * over boundary in the map. + */ + if ((boundary = (map->dm_segs[0]._ds_boundary)) == 0) + boundary = map->_dm_boundary; + align = MAX(map->dm_segs[0]._ds_align, PAGE_SIZE); + + pmap = p ? p->p_vmspace->vm_map.pmap : pmap_kernel(); + + /* Count up the total number of pages we need */ + sg_iomap_clear_pages(spm); + { /* Scope */ + bus_addr_t a, aend; + bus_addr_t addr = (bus_addr_t)buf; + int seg_len = buflen; + + aend = round_page(addr + seg_len); + for (a = trunc_page(addr); a < aend; a += PAGE_SIZE) { + paddr_t pa; + + if (pmap_extract(pmap, a, &pa) == FALSE) { + printf("iomap pmap error addr %#"PRIxPADDR"\n", a); + sg_iomap_clear_pages(spm); + return (EFBIG); + } + + err = sg_iomap_insert_page(spm, pa); + if (err) { + printf("iomap insert error: %d for " + "va %#"PRIxPADDR" pa %#"PRIxPADDR" " + "(buf %p len %zd/%zx)\n", + err, a, pa, buf, buflen, buflen); + sg_iomap_clear_pages(spm); + return (EFBIG); + } + } + } + sgsize = spm->spm_pagecnt * PAGE_SIZE; + + mutex_enter(&is->sg_mtx); + if (flags & BUS_DMA_24BIT) { + sgstart = MAX(is->sg_ex->ex_start, 0xff000000); + sgend = MIN(is->sg_ex->ex_end, 0xffffffff); + } else { + sgstart = is->sg_ex->ex_start; + sgend = is->sg_ex->ex_end; + } + + /* + * If our segment size is larger than the boundary we need to + * split the transfer up into little pieces ourselves. + */ + err = extent_alloc_subregion1(is->sg_ex, sgstart, sgend, + sgsize, align, 0, (sgsize > boundary) ? 0 : boundary, + EX_NOWAIT | EX_BOUNDZERO, (u_long *)&dvmaddr); + mutex_exit(&is->sg_mtx); + if (err != 0) { + sg_iomap_clear_pages(spm); + return (err); + } + + /* Set the active DVMA map */ + spm->spm_start = dvmaddr; + spm->spm_size = sgsize; + + map->dm_mapsize = buflen; + + sg_iomap_load_map(is, spm, dvmaddr, flags); + + { /* Scope */ + bus_addr_t a, aend; + bus_addr_t addr = (bus_addr_t)buf; + int seg_len = buflen; + + aend = round_page(addr + seg_len); + for (a = trunc_page(addr); a < aend; a += PAGE_SIZE) { + bus_addr_t pgstart; + bus_addr_t pgend; + paddr_t pa; + int pglen; + + /* Yuck... Redoing the same pmap_extract... */ + if (pmap_extract(pmap, a, &pa) == FALSE) { + printf("iomap pmap error addr %#"PRIxPADDR"\n", a); + err = EFBIG; + break; + } + + pgstart = pa | (MAX(a, addr) & PAGE_MASK); + pgend = pa | (MIN(a + PAGE_SIZE - 1, + addr + seg_len - 1) & PAGE_MASK); + pglen = pgend - pgstart + 1; + + if (pglen < 1) + continue; + + err = sg_dmamap_append_range(t, map, pgstart, + pglen, flags, boundary); + if (err == EFBIG) + break; + else if (err) { + printf("iomap load seg page: %d for " + "va %#"PRIxPADDR" pa %#"PRIxPADDR" (%#"PRIxPADDR" - %#"PRIxPADDR") " + "for %d/0x%x\n", + err, a, pa, pgstart, pgend, pglen, pglen); + break; + } + } + } + if (err) { + sg_dmamap_unload(t, map); + } else { + spm->spm_origbuf = buf; + spm->spm_buftype = X86_DMA_BUFTYPE_LINEAR; + spm->spm_proc = p; + } + + return (err); +} + +/* + * Load an mbuf into our map. we convert it to some bus_dma_segment_ts then + * pass it to load_raw. + */ +int +sg_dmamap_load_mbuf(bus_dma_tag_t t, bus_dmamap_t map, struct mbuf *mb, + int flags) +{ + /* + * This code is adapted from sparc64, for very fragmented data + * we may need to adapt the algorithm + */ + bus_dma_segment_t segs[MAX_DMA_SEGS]; + struct sg_page_map *spm = map->_dm_cookie; + size_t len; + int i, err; + + /* + * Make sure that on error condition we return "no valid mappings". + */ + map->dm_mapsize = 0; + map->dm_nsegs = 0; + + if (mb->m_pkthdr.len > map->_dm_size) + return (EINVAL); + + i = 0; + len = 0; + while (mb) { + vaddr_t vaddr = mtod(mb, vaddr_t); + long buflen = (long)mb->m_len; + + len += buflen; + while (buflen > 0 && i < MAX_DMA_SEGS) { + paddr_t pa; + long incr; + + incr = min(buflen, NBPG); + + if (pmap_extract(pmap_kernel(), vaddr, &pa) == FALSE) + return EINVAL; + + buflen -= incr; + vaddr += incr; + + if (i > 0 && pa == (segs[i - 1].ds_addr + + segs[i - 1].ds_len) && ((segs[i - 1].ds_len + incr) + < map->_dm_maxmaxsegsz)) { + /* contigious, great! */ + segs[i - 1].ds_len += incr; + continue; + } + segs[i].ds_addr = pa; + segs[i].ds_len = incr; + segs[i]._ds_boundary = 0; + segs[i]._ds_align = 0; + i++; + } + mb = mb->m_next; + if (mb && i >= MAX_DMA_SEGS) { + /* our map, it is too big! */ + return (EFBIG); + } + } + + err = sg_dmamap_load_raw(t, map, segs, i, (bus_size_t)len, flags); + + if (err == 0) { + spm->spm_origbuf = mb; + spm->spm_buftype = X86_DMA_BUFTYPE_MBUF; + } + return (err); +} + +/* + * Load a uio into the map. Turn it into segments and call load_raw() + */ +int +sg_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map, struct uio *uio, + int flags) +{ + /* + * loading uios is kinda broken since we can't lock the pages. + * and unlock them at unload. Perhaps page loaning is the answer. + * 'till then we only accept kernel data + */ + bus_dma_segment_t segs[MAX_DMA_SEGS]; + struct sg_page_map *spm = map->_dm_cookie; + size_t len; + int i, j, err; + + /* + * Make sure that on errror we return "no valid mappings". + */ + map->dm_mapsize = 0; + map->dm_nsegs = 0; + + if (uio->uio_resid > map->_dm_size) + return (EINVAL); + + if (!VMSPACE_IS_KERNEL_P(uio->uio_vmspace)) + return (EOPNOTSUPP); + + i = j = 0; + len = 0; + while (j < uio->uio_iovcnt) { + vaddr_t vaddr = (vaddr_t)uio->uio_iov[j].iov_base; + long buflen = (long)uio->uio_iov[j].iov_len; + + len += buflen; + while (buflen > 0 && i < MAX_DMA_SEGS) { + paddr_t pa; + long incr; + + incr = min(buflen, NBPG); + (void)pmap_extract(pmap_kernel(), vaddr, &pa); + buflen -= incr; + vaddr += incr; + + if (i > 0 && pa == (segs[i - 1].ds_addr + + segs[i -1].ds_len) && ((segs[i - 1].ds_len + incr) + < map->_dm_maxmaxsegsz)) { + /* contigious, yay! */ + segs[i - 1].ds_len += incr; + continue; + } + segs[i].ds_addr = pa; + segs[i].ds_len = incr; + segs[i]._ds_boundary = 0; + segs[i]._ds_align = 0; + i++; + } + j++; + if ((uio->uio_iovcnt - j) && i >= MAX_DMA_SEGS) { + /* Our map, is it too big! */ + return (EFBIG); + } + + } + + err = sg_dmamap_load_raw(t, map, segs, i, (bus_size_t)len, flags); + + if (err == 0) { + spm->spm_origbuf = uio; + spm->spm_buftype = X86_DMA_BUFTYPE_UIO; + } + return (err); +} + +/* + * Load a dvmamap from an array of segs. It calls sg_dmamap_append_range() + * or for part of the 2nd pass through the mapping. + */ +int +sg_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map, + bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags) +{ + int i; + int left; + int err = 0; + bus_size_t sgsize; + bus_size_t boundary, align; + u_long dvmaddr, sgstart, sgend; + struct sg_cookie *is = t->_cookie; + struct sg_page_map *spm = map->_dm_cookie; + + if (map->dm_nsegs) { + /* Already in use?? */ +#ifdef DIAGNOSTIC + panic("sg_dmamap_load_raw: map still in use"); +#endif + bus_dmamap_unload(t, map); + } + + /* + * A boundary presented to bus_dmamem_alloc() takes precedence + * over boundary in the map. + */ + if ((boundary = segs[0]._ds_boundary) == 0) + boundary = map->_dm_boundary; + + align = MAX(MAX(segs[0]._ds_align, map->dm_segs[0]._ds_align), + PAGE_SIZE); + + /* + * Make sure that on error condition we return "no valid mappings". + */ + map->dm_nsegs = 0; + + sg_iomap_clear_pages(spm); + /* Count up the total number of pages we need */ + for (i = 0, left = size; left > 0 && i < nsegs; i++) { + bus_addr_t a, aend; + bus_size_t len = segs[i].ds_len; + bus_addr_t addr = segs[i].ds_addr; + int seg_len = MIN(left, len); + + if (len < 1) + continue; + + aend = round_page(addr + seg_len); + for (a = trunc_page(addr); a < aend; a += PAGE_SIZE) { + + err = sg_iomap_insert_page(spm, a); + if (err) { + printf("iomap insert error: %d for " + "pa %#"PRIxPADDR"\n", err, a); + sg_iomap_clear_pages(spm); + return (EFBIG); + } + } + + left -= seg_len; + } + sgsize = spm->spm_pagecnt * PAGE_SIZE; + + mutex_enter(&is->sg_mtx); + if (flags & BUS_DMA_24BIT) { + sgstart = MAX(is->sg_ex->ex_start, 0xff000000); + sgend = MIN(is->sg_ex->ex_end, 0xffffffff); + } else { + sgstart = is->sg_ex->ex_start; + sgend = is->sg_ex->ex_end; + } + + /* + * If our segment size is larger than the boundary we need to + * split the transfer up into little pieces ourselves. + */ + err = extent_alloc_subregion1(is->sg_ex, sgstart, sgend, + sgsize, align, 0, (sgsize > boundary) ? 0 : boundary, + EX_NOWAIT | EX_BOUNDZERO, (u_long *)&dvmaddr); + mutex_exit(&is->sg_mtx); + + if (err != 0) { + sg_iomap_clear_pages(spm); + return (err); + } + + /* Set the active DVMA map */ + spm->spm_start = dvmaddr; + spm->spm_size = sgsize; + + map->dm_mapsize = size; + + sg_iomap_load_map(is, spm, dvmaddr, flags); + + err = sg_dmamap_load_seg(t, is, map, segs, nsegs, flags, + size, boundary); + + if (err) { + sg_dmamap_unload(t, map); + } else { + /* This will be overwritten if mbuf or uio called us */ + spm->spm_origbuf = segs; + spm->spm_buftype = X86_DMA_BUFTYPE_RAW; + } + + return (err); +} + +/* + * Insert a range of addresses into a loaded map respecting the specified + * boundary and alignment restrictions. The range is specified by its + * physical address and length. The range cannot cross a page boundary. + * This code (along with most of the rest of the function in this file) + * assumes that the IOMMU page size is equal to PAGE_SIZE. + */ +int +sg_dmamap_append_range(bus_dma_tag_t t, bus_dmamap_t map, paddr_t pa, + bus_size_t length, int flags, bus_size_t boundary) +{ + struct sg_page_map *spm = map->_dm_cookie; + bus_addr_t sgstart, sgend, bd_mask; + bus_dma_segment_t *seg = NULL; + int i = map->dm_nsegs; + + sgstart = sg_iomap_translate(spm, pa); + sgend = sgstart + length - 1; + +#ifdef DIAGNOSTIC + if (sgstart == 0 || sgstart > sgend) { + printf("append range invalid mapping for %#"PRIxPADDR" " + "(%#"PRIxPADDR" - %#"PRIxPADDR")\n", pa, sgstart, sgend); + map->dm_nsegs = 0; + return (EINVAL); + } +#endif + +#ifdef DEBUG + if (trunc_page(sgstart) != trunc_page(sgend)) { + printf("append range crossing page boundary! " + "pa %#"PRIxPADDR" length %zd/0x%zx sgstart %#"PRIxPADDR" sgend %#"PRIxPADDR"\n", + pa, length, length, sgstart, sgend); + } +#endif + + /* + * We will attempt to merge this range with the previous entry + * (if there is one). + */ + if (i > 0) { + seg = &map->dm_segs[i - 1]; + if (sgstart == seg->ds_addr + seg->ds_len) { + length += seg->ds_len; + sgstart = seg->ds_addr; + sgend = sgstart + length - 1; + } else + seg = NULL; + } + + if (seg == NULL) { + seg = &map->dm_segs[i]; + if (++i > map->_dm_segcnt) { + map->dm_nsegs = 0; + return (EFBIG); + } + } + + /* + * At this point, "i" is the index of the *next* bus_dma_segment_t + * (the segment count, aka map->dm_nsegs) and "seg" points to the + * *current* entry. "length", "sgstart", and "sgend" reflect what + * we intend to put in "*seg". No assumptions should be made about + * the contents of "*seg". Only "boundary" issue can change this + * and "boundary" is often zero, so explicitly test for that case + * (the test is strictly an optimization). + */ + if (boundary != 0) { + bd_mask = ~(boundary - 1); + + while ((sgstart & bd_mask) != (sgend & bd_mask)) { + /* + * We are crossing a boundary so fill in the current + * segment with as much as possible, then grab a new + * one. + */ + + seg->ds_addr = sgstart; + seg->ds_len = boundary - (sgstart & bd_mask); + + sgstart += seg->ds_len; /* sgend stays the same */ + length -= seg->ds_len; + + seg = &map->dm_segs[i]; + if (++i > map->_dm_segcnt) { + map->dm_nsegs = 0; + return (EFBIG); + } + } + } + + seg->ds_addr = sgstart; + seg->ds_len = length; + map->dm_nsegs = i; + + return (0); +} + +/* + * Populate the iomap from a bus_dma_segment_t array. See note for + * sg_dmamap_load() regarding page entry exhaustion of the iomap. + * This is less of a problem for load_seg, as the number of pages + * is usually similar to the number of segments (nsegs). + */ +int +sg_dmamap_load_seg(bus_dma_tag_t t, struct sg_cookie *is, + bus_dmamap_t map, bus_dma_segment_t *segs, int nsegs, int flags, + bus_size_t size, bus_size_t boundary) +{ + int i; + int left; + int seg; + + /* + * Keep in mind that each segment could span + * multiple pages and that these are not always + * adjacent. The code is no longer adding dvma + * aliases to the IOMMU. The STC will not cross + * page boundaries anyway and a IOMMU table walk + * vs. what may be a streamed PCI DMA to a ring + * descriptor is probably a wash. It eases TLB + * pressure and in the worst possible case, it is + * only as bad a non-IOMMUed architecture. More + * importantly, the code is not quite as hairy. + * (It's bad enough as it is.) + */ + left = size; + seg = 0; + for (i = 0; left > 0 && i < nsegs; i++) { + bus_addr_t a, aend; + bus_size_t len = segs[i].ds_len; + bus_addr_t addr = segs[i].ds_addr; + int seg_len = MIN(left, len); + + if (len < 1) + continue; + + aend = round_page(addr + seg_len); + for (a = trunc_page(addr); a < aend; a += PAGE_SIZE) { + bus_addr_t pgstart; + bus_addr_t pgend; + int pglen; + int err; + + pgstart = MAX(a, addr); + pgend = MIN(a + PAGE_SIZE - 1, addr + seg_len - 1); + pglen = pgend - pgstart + 1; + + if (pglen < 1) + continue; + + err = sg_dmamap_append_range(t, map, pgstart, + pglen, flags, boundary); + if (err == EFBIG) + return (err); + if (err) { + printf("iomap load seg page: %d for " + "pa %#"PRIxPADDR" (%#"PRIxPADDR" - %#"PRIxPADDR" for %d/%x\n", + err, a, pgstart, pgend, pglen, pglen); + return (err); + } + + } + + left -= seg_len; + } + return (0); +} + +/* + * Unload a dvmamap. + */ +void +sg_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map) +{ + struct sg_cookie *is = t->_cookie; + struct sg_page_map *spm = map->_dm_cookie; + bus_addr_t dvmaddr = spm->spm_start; + bus_size_t sgsize = spm->spm_size; + int error; + + /* Remove the IOMMU entries */ + sg_iomap_unload_map(is, spm); + + /* Clear the iomap */ + sg_iomap_clear_pages(spm); + + mutex_enter(&is->sg_mtx); + error = extent_free(is->sg_ex, dvmaddr, + sgsize, EX_NOWAIT); + spm->spm_start = 0; + spm->spm_size = 0; + mutex_exit(&is->sg_mtx); + if (error != 0) + printf("warning: %zd of DVMA space lost\n", sgsize); + + spm->spm_buftype = X86_DMA_BUFTYPE_INVALID; + spm->spm_origbuf = NULL; + spm->spm_proc = NULL; + _bus_dmamap_unload(t, map); +} + +/* + * Alloc dma safe memory, telling the backend that we're scatter gather + * to ease pressure on the vm. + * + * This assumes that we can map all physical memory. + */ +int +sg_dmamem_alloc(bus_dma_tag_t t, bus_size_t size, + bus_size_t alignment, bus_size_t boundary, bus_dma_segment_t *segs, + int nsegs, int *rsegs, int flags) +{ + return (_bus_dmamem_alloc_range(t, size, alignment, boundary, + segs, nsegs, rsegs, flags | BUS_DMA_SG, 0, -1)); +} + +/* + * Create a new iomap. + */ +struct sg_page_map * +sg_iomap_create(int n) +{ + struct sg_page_map *spm; + + /* Safety for heavily fragmented data, such as mbufs */ + n += 4; + if (n < 16) + n = 16; + + spm = malloc(sizeof(*spm) + (n - 1) * sizeof(spm->spm_map[0]), + M_DMAMAP, M_NOWAIT | M_ZERO); + if (spm == NULL) + return (NULL); + + /* Initialize the map. */ + spm->spm_maxpage = n; + SPLAY_INIT(&spm->spm_tree); + + return (spm); +} + +/* + * Destroy an iomap. + */ +void +sg_iomap_destroy(struct sg_page_map *spm) +{ +#ifdef DIAGNOSTIC + if (spm->spm_pagecnt > 0) + printf("sg_iomap_destroy: %d page entries in use\n", + spm->spm_pagecnt); +#endif + + free(spm, M_DMAMAP); +} + +/* + * Utility function used by splay tree to order page entries by pa. + */ +static inline int +iomap_compare(struct sg_page_entry *a, struct sg_page_entry *b) +{ + return ((a->spe_pa > b->spe_pa) ? 1 : + (a->spe_pa < b->spe_pa) ? -1 : 0); +} + +SPLAY_PROTOTYPE(sg_page_tree, sg_page_entry, spe_node, iomap_compare); + +SPLAY_GENERATE(sg_page_tree, sg_page_entry, spe_node, iomap_compare); + +/* + * Insert a pa entry in the iomap. + */ +int +sg_iomap_insert_page(struct sg_page_map *spm, paddr_t pa) +{ + struct sg_page_entry *e; + + if (spm->spm_pagecnt >= spm->spm_maxpage) { + struct sg_page_entry spe; + + spe.spe_pa = pa; + if (SPLAY_FIND(sg_page_tree, &spm->spm_tree, &spe)) + return (0); + + return (ENOMEM); + } + + e = &spm->spm_map[spm->spm_pagecnt]; + + e->spe_pa = pa; + e->spe_va = 0; + + e = SPLAY_INSERT(sg_page_tree, &spm->spm_tree, e); + + /* Duplicates are okay, but only count them once. */ + if (e) + return (0); + + ++spm->spm_pagecnt; + + return (0); +} + +/* + * Locate the iomap by filling in the pa->va mapping and inserting it + * into the IOMMU tables. + */ +void +sg_iomap_load_map(struct sg_cookie *sc, struct sg_page_map *spm, + bus_addr_t vmaddr, int flags) +{ + struct sg_page_entry *e; + int i; + + for (i = 0, e = spm->spm_map; i < spm->spm_pagecnt; ++i, ++e) { + e->spe_va = vmaddr; + sc->bind_page(sc->sg_hdl, e->spe_va, e->spe_pa, flags); + vmaddr += PAGE_SIZE; + } + sc->flush_tlb(sc->sg_hdl); +} + +/* + * Remove the iomap from the IOMMU. + */ +void +sg_iomap_unload_map(struct sg_cookie *sc, struct sg_page_map *spm) +{ + struct sg_page_entry *e; + int i; + + for (i = 0, e = spm->spm_map; i < spm->spm_pagecnt; ++i, ++e) + sc->unbind_page(sc->sg_hdl, e->spe_va); + sc->flush_tlb(sc->sg_hdl); +} + +/* + * Translate a physical address (pa) into a DVMA address. + */ +bus_addr_t +sg_iomap_translate(struct sg_page_map *spm, paddr_t pa) +{ + struct sg_page_entry *e, pe; + paddr_t offset = pa & PAGE_MASK; + + pe.spe_pa = trunc_page(pa); + + e = SPLAY_FIND(sg_page_tree, &spm->spm_tree, &pe); + + if (e == NULL) + return (0); + + return (e->spe_va | offset); +} + +/* + * Clear the iomap table and tree. + */ +void +sg_iomap_clear_pages(struct sg_page_map *spm) +{ + spm->spm_pagecnt = 0; + SPLAY_INIT(&spm->spm_tree); +} diff -Naurp old/src/sys/arch/xen/xen/isa_machdep.c new/src/sys/arch/xen/xen/isa_machdep.c --- old/src/sys/arch/xen/xen/isa_machdep.c 2010-02-06 18:48:54.000000000 +0100 +++ new/src/sys/arch/xen/xen/isa_machdep.c 2011-02-08 11:14:00.000000000 +0100 @@ -97,6 +97,7 @@ __KERNEL_RCSID(0, "$NetBSD: isa_machdep. static int _isa_dma_may_bounce(bus_dma_tag_t, bus_dmamap_t, int, int *); struct x86_bus_dma_tag isa_bus_dma_tag = { + NULL, /* cookie */ 0, /* _tag_needs_free */ ISA_DMA_BOUNCE_THRESHOLD, /* _bounce_thresh */ 0, /* _bounce_alloc_lo */ diff -Naurp old/src/sys/arch/xen/xen/xpci_xenbus.c new/src/sys/arch/xen/xen/xpci_xenbus.c --- old/src/sys/arch/xen/xen/xpci_xenbus.c 2010-04-28 21:17:04.000000000 +0200 +++ new/src/sys/arch/xen/xen/xpci_xenbus.c 2011-02-08 11:14:00.000000000 +0100 @@ -91,6 +91,7 @@ CFATTACH_DECL_NEW(xpci_xenbus, sizeof(st xpci_xenbus_match, xpci_xenbus_attach, xpci_xenbus_detach, NULL); struct x86_bus_dma_tag pci_bus_dma_tag = { + NULL, /* cookie */ 0, /* tag_needs_free */ #if defined(_LP64) || defined(PAE) PCI32_DMA_BOUNCE_THRESHOLD, /* bounce_thresh */ @@ -121,6 +122,7 @@ struct x86_bus_dma_tag pci_bus_dma_tag = #ifdef _LP64 struct x86_bus_dma_tag pci_bus_dma64_tag = { + NULL, /* cookie */ 0, /* tag_needs_free */ 0, 0, diff -Naurp old/src/sys/conf/files new/src/sys/conf/files --- old/src/sys/conf/files 2010-11-23 12:14:08.000000000 +0100 +++ new/src/sys/conf/files 2011-02-08 11:14:00.000000000 +0100 @@ -1065,7 +1065,8 @@ file dev/ic/lan9118.c smsh # DRM - Direct Rendering Infrastructure: dev/drm define drm {} -include "external/bsd/drm/conf/files.drm" +#include "external/bsd/drm/conf/files.drm" +include "dev/pci/drm/files.drm" # Definitions for wscons # device attributes: display, display with emulator, keyboard, and mouse diff -Naurp old/src/sys/dev/pci/agp_ali.c new/src/sys/dev/pci/agp_ali.c --- old/src/sys/dev/pci/agp_ali.c 2010-11-13 14:52:04.000000000 +0100 +++ new/src/sys/dev/pci/agp_ali.c 2011-02-08 11:14:00.000000000 +0100 @@ -54,7 +54,7 @@ struct agp_ali_softc { static u_int32_t agp_ali_get_aperture(struct agp_softc *); static int agp_ali_set_aperture(struct agp_softc *sc, u_int32_t); -static int agp_ali_bind_page(struct agp_softc *, off_t, bus_addr_t); +static int agp_ali_bind_page(struct agp_softc *, off_t, bus_addr_t, int); static int agp_ali_unbind_page(struct agp_softc *, off_t); static void agp_ali_flush_tlb(struct agp_softc *); @@ -65,6 +65,7 @@ static struct agp_methods agp_ali_method agp_ali_bind_page, agp_ali_unbind_page, agp_ali_flush_tlb, + NULL, agp_generic_enable, agp_generic_alloc_memory, agp_generic_free_memory, @@ -214,7 +215,8 @@ agp_ali_set_aperture(struct agp_softc *s } static int -agp_ali_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical) +agp_ali_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical, + int flags) { struct agp_ali_softc *asc = sc->as_chipc; diff -Naurp old/src/sys/dev/pci/agp_amd64.c new/src/sys/dev/pci/agp_amd64.c --- old/src/sys/dev/pci/agp_amd64.c 2010-11-13 14:52:04.000000000 +0100 +++ new/src/sys/dev/pci/agp_amd64.c 2011-02-08 11:14:00.000000000 +0100 @@ -56,7 +56,7 @@ __KERNEL_RCSID(0, "$NetBSD: agp_amd64.c, static uint32_t agp_amd64_get_aperture(struct agp_softc *); static int agp_amd64_set_aperture(struct agp_softc *, uint32_t); -static int agp_amd64_bind_page(struct agp_softc *, off_t, bus_addr_t); +static int agp_amd64_bind_page(struct agp_softc *, off_t, bus_addr_t, int); static int agp_amd64_unbind_page(struct agp_softc *, off_t); static void agp_amd64_flush_tlb(struct agp_softc *); @@ -90,6 +90,7 @@ static struct agp_methods agp_amd64_meth agp_amd64_bind_page, agp_amd64_unbind_page, agp_amd64_flush_tlb, + NULL, agp_generic_enable, agp_generic_alloc_memory, agp_generic_free_memory, @@ -373,7 +374,8 @@ agp_amd64_set_aperture(struct agp_softc } static int -agp_amd64_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical) +agp_amd64_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical, + int flags) { struct agp_amd64_softc *asc = sc->as_chipc; diff -Naurp old/src/sys/dev/pci/agp_amd.c new/src/sys/dev/pci/agp_amd.c --- old/src/sys/dev/pci/agp_amd.c 2010-11-13 14:52:04.000000000 +0100 +++ new/src/sys/dev/pci/agp_amd.c 2011-02-08 11:14:00.000000000 +0100 @@ -73,7 +73,7 @@ struct agp_amd_softc { static u_int32_t agp_amd_get_aperture(struct agp_softc *); static int agp_amd_set_aperture(struct agp_softc *, u_int32_t); -static int agp_amd_bind_page(struct agp_softc *, off_t, bus_addr_t); +static int agp_amd_bind_page(struct agp_softc *, off_t, bus_addr_t, int); static int agp_amd_unbind_page(struct agp_softc *, off_t); static void agp_amd_flush_tlb(struct agp_softc *); @@ -84,6 +84,7 @@ static struct agp_methods agp_amd_method agp_amd_bind_page, agp_amd_unbind_page, agp_amd_flush_tlb, + NULL, agp_generic_enable, agp_generic_alloc_memory, agp_generic_free_memory, @@ -303,7 +304,8 @@ agp_amd_set_aperture(struct agp_softc *s } static int -agp_amd_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical) +agp_amd_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical, + int flags) { struct agp_amd_softc *asc = sc->as_chipc; diff -Naurp old/src/sys/dev/pci/agp_apple.c new/src/sys/dev/pci/agp_apple.c --- old/src/sys/dev/pci/agp_apple.c 2010-11-13 14:52:04.000000000 +0100 +++ new/src/sys/dev/pci/agp_apple.c 2011-02-08 11:14:00.000000000 +0100 @@ -46,7 +46,7 @@ __KERNEL_RCSID(0, "$NetBSD: agp_apple.c, static u_int32_t agp_apple_get_aperture(struct agp_softc *); static int agp_apple_set_aperture(struct agp_softc *, u_int32_t); -static int agp_apple_bind_page(struct agp_softc *, off_t, bus_addr_t); +static int agp_apple_bind_page(struct agp_softc *, off_t, bus_addr_t, int); static int agp_apple_unbind_page(struct agp_softc *, off_t); static void agp_apple_flush_tlb(struct agp_softc *); @@ -56,6 +56,7 @@ static struct agp_methods agp_apple_meth agp_apple_bind_page, agp_apple_unbind_page, agp_apple_flush_tlb, + NULL, agp_generic_enable, agp_generic_alloc_memory, agp_generic_free_memory, @@ -154,7 +155,8 @@ agp_apple_set_aperture(struct agp_softc } static int -agp_apple_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical) +agp_apple_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical, + int flags) { struct agp_apple_softc *asc = sc->as_chipc; diff -Naurp old/src/sys/dev/pci/agp.c new/src/sys/dev/pci/agp.c --- old/src/sys/dev/pci/agp.c 2010-11-13 14:52:04.000000000 +0100 +++ new/src/sys/dev/pci/agp.c 2011-02-08 11:14:00.000000000 +0100 @@ -665,7 +665,7 @@ agp_generic_bind_memory(struct agp_softc AGP_DPF(("binding offset %#lx to pa %#lx\n", (unsigned long)(offset + done + j), (unsigned long)pa)); - error = AGP_BIND_PAGE(sc, offset + done + j, pa); + error = AGP_BIND_PAGE(sc, offset + done + j, pa, 0); if (error) { /* * Bail out. Reverse all the mappings diff -Naurp old/src/sys/dev/pci/agp_i810.c new/src/sys/dev/pci/agp_i810.c --- old/src/sys/dev/pci/agp_i810.c 2011-01-31 00:43:08.000000000 +0100 +++ new/src/sys/dev/pci/agp_i810.c 2011-02-08 11:14:00.000000000 +0100 @@ -56,6 +56,9 @@ __KERNEL_RCSID(0, "$NetBSD: agp_i810.c,v #define READ4(off) bus_space_read_4(isc->bst, isc->bsh, off) #define WRITE4(off,v) bus_space_write_4(isc->bst, isc->bsh, off, v) +/* Memory is snooped, must not be accessed through gtt from the cpu. */ +#define INTEL_COHERENT 0x6 + #define CHIP_I810 0 /* i810/i815 */ #define CHIP_I830 1 /* 830M/845G */ #define CHIP_I855 2 /* 852GM/855GM/865G */ @@ -66,6 +69,9 @@ __KERNEL_RCSID(0, "$NetBSD: agp_i810.c,v struct agp_i810_softc { u_int32_t initial_aperture; /* aperture size at startup */ + bus_dmamap_t scrib_dmamap; + bus_dma_segment_t scrib_seg; + void *scrib_vaddr; struct agp_gatt *gatt; int chiptype; /* i810-like or i830 */ u_int32_t dcache_size; /* i810 only */ @@ -81,12 +87,14 @@ struct agp_i810_softc { }; /* XXX hack, see below */ +static bool agp_i810_vga_mapped = false; static bus_addr_t agp_i810_vga_regbase; +static bus_space_tag_t agp_i810_vga_bst; static bus_space_handle_t agp_i810_vga_bsh; static u_int32_t agp_i810_get_aperture(struct agp_softc *); static int agp_i810_set_aperture(struct agp_softc *, u_int32_t); -static int agp_i810_bind_page(struct agp_softc *, off_t, bus_addr_t); +static int agp_i810_bind_page(struct agp_softc *, off_t, bus_addr_t, int); static int agp_i810_unbind_page(struct agp_softc *, off_t); static void agp_i810_flush_tlb(struct agp_softc *); static int agp_i810_enable(struct agp_softc *, u_int32_t mode); @@ -103,12 +111,16 @@ static int agp_i810_init(struct agp_soft static int agp_i810_write_gtt_entry(struct agp_i810_softc *, off_t, bus_addr_t); +extern void intagp_dma_sync(bus_dma_tag_t, bus_dmamap_t, bus_addr_t, + bus_size_t, int); + static struct agp_methods agp_i810_methods = { agp_i810_get_aperture, agp_i810_set_aperture, agp_i810_bind_page, agp_i810_unbind_page, agp_i810_flush_tlb, + intagp_dma_sync, agp_i810_enable, agp_i810_alloc_memory, agp_i810_free_memory, @@ -420,10 +432,17 @@ agp_i810_attach(device_t parent, device_ * XXX horrible hack to allow drm code to use our mapping * of VGA chip registers */ + agp_i810_vga_mapped = true; agp_i810_vga_regbase = mmadr; + agp_i810_vga_bst = isc->bst; agp_i810_vga_bsh = isc->bsh; - return agp_i810_init(sc); + error = agp_i810_init(sc); + if (error != 0) { + free(gatt, M_AGP); + agp_generic_detach(sc); + } + return error; } /* @@ -431,11 +450,13 @@ agp_i810_attach(device_t parent, device_ * of VGA chip registers */ int -agp_i810_borrow(bus_addr_t base, bus_space_handle_t *hdlp) +agp_i810_borrow(bus_addr_t base, bus_space_tag_t *tagp, bus_space_handle_t *hdlp) { - if (!agp_i810_vga_regbase || base != agp_i810_vga_regbase) + if (!agp_i810_vga_mapped || !agp_i810_vga_regbase || + base != agp_i810_vga_regbase) return 0; + *tagp = agp_i810_vga_bst; *hdlp = agp_i810_vga_bsh; return 1; } @@ -444,6 +465,8 @@ static int agp_i810_init(struct agp_soft { struct agp_i810_softc *isc; struct agp_gatt *gatt; + bus_addr_t tmp; + int dummyrseg; isc = sc->as_chipc; gatt = isc->gatt; @@ -462,8 +485,6 @@ static int agp_i810_init(struct agp_soft if (agp_alloc_dmamem(sc->as_dmat, 64 * 1024, 0, &gatt->ag_dmamap, &virtual, &gatt->ag_physical, &gatt->ag_dmaseg, 1, &dummyseg) != 0) { - free(gatt, M_AGP); - agp_generic_detach(sc); return ENOMEM; } gatt->ag_virtual = (uint32_t *)virtual; @@ -495,7 +516,6 @@ static int agp_i810_init(struct agp_soft isc->stolen = 0; aprint_error( ": unknown memory configuration, disabling\n"); - agp_generic_detach(sc); return EINVAL; } @@ -550,7 +570,6 @@ static int agp_i810_init(struct agp_soft break; default: aprint_error("Bad PGTBL size\n"); - agp_generic_detach(sc); return EINVAL; } break; @@ -564,7 +583,6 @@ static int agp_i810_init(struct agp_soft break; default: aprint_error(": Bad PGTBL size\n"); - agp_generic_detach(sc); return EINVAL; } break; @@ -573,7 +591,6 @@ static int agp_i810_init(struct agp_soft break; default: aprint_error(": bad chiptype\n"); - agp_generic_detach(sc); return EINVAL; } @@ -620,7 +637,6 @@ static int agp_i810_init(struct agp_soft default: aprint_error( ": unknown memory configuration, disabling\n"); - agp_generic_detach(sc); return EINVAL; } @@ -667,6 +683,25 @@ static int agp_i810_init(struct agp_soft gatt->ag_physical = pgtblctl & ~1; } + /* Intel recommends that you have a fake page bound to the gtt always */ + if (agp_alloc_dmamem(sc->as_dmat, AGP_PAGE_SIZE, 0, &isc->scrib_dmamap, + &isc->scrib_vaddr, &tmp, &isc->scrib_seg, 1, &dummyrseg) != 0) { + aprint_error(": can't get scribble page\n"); + return ENOMEM; + } + + tmp = 0; + if (isc->chiptype == CHIP_I810) { + tmp += isc->dcache_size; + } else { + tmp += isc->stolen << AGP_PAGE_SHIFT; + } + + /* initialise all gtt entries to point to scribble page */ + for (; tmp < sc->as_apsize; tmp += AGP_PAGE_SIZE) + agp_i810_unbind_page(sc, tmp); + /* XXX we'll need to restore the GTT contents when we go kms */ + /* * Make sure the chipset can see everything. */ @@ -695,6 +730,8 @@ agp_i810_detach(struct agp_softc *sc) pgtblctl &= ~1; WRITE4(AGP_I810_PGTBL_CTL, pgtblctl); } + agp_free_dmamem(sc->as_dmat, AGP_PAGE_SIZE, isc->scrib_dmamap, + isc->scrib_vaddr, &isc->scrib_seg, 1); /* Put the aperture back the way it started. */ AGP_SET_APERTURE(sc, isc->initial_aperture); @@ -831,7 +868,8 @@ agp_i810_set_aperture(struct agp_softc * } static int -agp_i810_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical) +agp_i810_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical, + int flags) { struct agp_i810_softc *isc = sc->as_chipc; @@ -854,6 +892,13 @@ agp_i810_bind_page(struct agp_softc *sc, } } + /* + * COHERENT mappings mean set the snoop bit. this should never be + * accessed by the gpu through the gtt. + */ + if (flags & BUS_DMA_COHERENT) + physical |= INTEL_COHERENT; + return agp_i810_write_gtt_entry(isc, offset, physical | 1); } @@ -875,7 +920,8 @@ agp_i810_unbind_page(struct agp_softc *s } } - return agp_i810_write_gtt_entry(isc, offset, 0); + return agp_i810_write_gtt_entry(isc, offset, + isc->scrib_dmamap->dm_segs[0].ds_addr | 1); } /* @@ -1011,7 +1057,7 @@ agp_i810_bind_memory(struct agp_softc *s if (mem->am_type == 2) { for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) agp_i810_bind_page(sc, offset + i, - mem->am_physical + i); + mem->am_physical + i, 0); mem->am_offset = offset; mem->am_is_bound = 1; return 0; diff -Naurp old/src/sys/dev/pci/agp_intel.c new/src/sys/dev/pci/agp_intel.c --- old/src/sys/dev/pci/agp_intel.c 2010-11-13 14:52:05.000000000 +0100 +++ new/src/sys/dev/pci/agp_intel.c 2011-02-08 11:14:00.000000000 +0100 @@ -65,7 +65,7 @@ struct agp_intel_softc { static u_int32_t agp_intel_get_aperture(struct agp_softc *); static int agp_intel_set_aperture(struct agp_softc *, u_int32_t); -static int agp_intel_bind_page(struct agp_softc *, off_t, bus_addr_t); +static int agp_intel_bind_page(struct agp_softc *, off_t, bus_addr_t, int); static int agp_intel_unbind_page(struct agp_softc *, off_t); static void agp_intel_flush_tlb(struct agp_softc *); static int agp_intel_init(struct agp_softc *); @@ -77,6 +77,7 @@ static struct agp_methods agp_intel_meth agp_intel_bind_page, agp_intel_unbind_page, agp_intel_flush_tlb, + NULL, agp_generic_enable, agp_generic_alloc_memory, agp_generic_free_memory, @@ -342,7 +343,8 @@ agp_intel_set_aperture(struct agp_softc } static int -agp_intel_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical) +agp_intel_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical, + int flags) { struct agp_intel_softc *isc = sc->as_chipc; diff -Naurp old/src/sys/dev/pci/agp_sis.c new/src/sys/dev/pci/agp_sis.c --- old/src/sys/dev/pci/agp_sis.c 2010-11-13 14:52:05.000000000 +0100 +++ new/src/sys/dev/pci/agp_sis.c 2011-02-08 11:14:00.000000000 +0100 @@ -54,7 +54,7 @@ struct agp_sis_softc { static u_int32_t agp_sis_get_aperture(struct agp_softc *); static int agp_sis_set_aperture(struct agp_softc *, u_int32_t); -static int agp_sis_bind_page(struct agp_softc *, off_t, bus_addr_t); +static int agp_sis_bind_page(struct agp_softc *, off_t, bus_addr_t, int); static int agp_sis_unbind_page(struct agp_softc *, off_t); static void agp_sis_flush_tlb(struct agp_softc *); @@ -64,6 +64,7 @@ static struct agp_methods agp_sis_method agp_sis_bind_page, agp_sis_unbind_page, agp_sis_flush_tlb, + NULL, agp_generic_enable, agp_generic_alloc_memory, agp_generic_free_memory, @@ -190,7 +191,8 @@ agp_sis_set_aperture(struct agp_softc *s } static int -agp_sis_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical) +agp_sis_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical, + int flags) { struct agp_sis_softc *ssc = sc->as_chipc; diff -Naurp old/src/sys/dev/pci/agpvar.h new/src/sys/dev/pci/agpvar.h --- old/src/sys/dev/pci/agpvar.h 2009-05-06 12:34:32.000000000 +0200 +++ new/src/sys/dev/pci/agpvar.h 2011-02-08 11:14:00.000000000 +0100 @@ -103,9 +103,11 @@ struct agp_softc; struct agp_methods { u_int32_t (*get_aperture)(struct agp_softc *); int (*set_aperture)(struct agp_softc *, u_int32_t); - int (*bind_page)(struct agp_softc *, off_t, bus_addr_t); + int (*bind_page)(struct agp_softc *, off_t, bus_addr_t, int); int (*unbind_page)(struct agp_softc *, off_t); void (*flush_tlb)(struct agp_softc *); + void (*dma_sync)(bus_dma_tag_t, bus_dmamap_t, bus_addr_t, + bus_size_t, int); int (*enable)(struct agp_softc *, u_int32_t mode); struct agp_memory *(*alloc_memory)(struct agp_softc *, int, vsize_t); int (*free_memory)(struct agp_softc *, struct agp_memory *); @@ -115,7 +117,7 @@ struct agp_methods { #define AGP_GET_APERTURE(sc) ((sc)->as_methods->get_aperture(sc)) #define AGP_SET_APERTURE(sc,a) ((sc)->as_methods->set_aperture((sc),(a))) -#define AGP_BIND_PAGE(sc,o,p) ((sc)->as_methods->bind_page((sc),(o),(p))) +#define AGP_BIND_PAGE(sc,o,p,f) ((sc)->as_methods->bind_page((sc),(o),(p),(f))) #define AGP_UNBIND_PAGE(sc,o) ((sc)->as_methods->unbind_page((sc), (o))) #define AGP_FLUSH_TLB(sc) ((sc)->as_methods->flush_tlb(sc)) #define AGP_ENABLE(sc,m) ((sc)->as_methods->enable((sc),(m))) @@ -167,6 +169,7 @@ int agpbusprint(void *, const char *); * Functions private to the AGP code. */ void agp_flush_cache(void); +void agp_flush_cache_range(vaddr_t, vsize_t); int agp_find_caps(pci_chipset_tag_t, pcitag_t); int agp_map_aperture(struct pci_attach_args *, struct agp_softc *, int); struct agp_gatt *agp_alloc_gatt(struct agp_softc *); @@ -196,6 +199,22 @@ int agp_alloc_dmamem(bus_dma_tag_t, size void agp_free_dmamem(bus_dma_tag_t, size_t, bus_dmamap_t, void *, bus_dma_segment_t *, int) ; +int agp_bus_dma_init(struct agp_softc *, bus_addr_t, bus_addr_t, + bus_dma_tag_t *); +void agp_bus_dma_destroy(struct agp_softc *, bus_dma_tag_t); +void agp_bus_dma_set_alignment(bus_dma_tag_t, bus_dmamap_t, + u_long); + +struct agp_map; + +int agp_init_map(bus_space_tag_t, bus_addr_t, bus_size_t, int, struct + agp_map **); +void agp_destroy_map(struct agp_map *); +int agp_map_subregion(struct agp_map *, bus_size_t, bus_size_t, + bus_space_handle_t *); +void agp_unmap_subregion(struct agp_map *, bus_space_handle_t, + bus_size_t); + MALLOC_DECLARE(M_AGP); /* @@ -268,6 +287,6 @@ void agp_memory_info(void *, void *, str * XXX horrible hack to allow drm code to use our mapping * of VGA chip registers */ -int agp_i810_borrow(bus_addr_t, bus_space_handle_t *); +int agp_i810_borrow(bus_addr_t, bus_space_tag_t *, bus_space_handle_t *); #endif /* !_PCI_AGPPRIV_H_ */ diff -Naurp old/src/sys/dev/pci/agp_via.c new/src/sys/dev/pci/agp_via.c --- old/src/sys/dev/pci/agp_via.c 2010-11-13 14:52:05.000000000 +0100 +++ new/src/sys/dev/pci/agp_via.c 2011-02-08 11:14:00.000000000 +0100 @@ -50,7 +50,7 @@ __KERNEL_RCSID(0, "$NetBSD: agp_via.c,v static u_int32_t agp_via_get_aperture(struct agp_softc *); static int agp_via_set_aperture(struct agp_softc *, u_int32_t); -static int agp_via_bind_page(struct agp_softc *, off_t, bus_addr_t); +static int agp_via_bind_page(struct agp_softc *, off_t, bus_addr_t, int); static int agp_via_unbind_page(struct agp_softc *, off_t); static void agp_via_flush_tlb(struct agp_softc *); @@ -60,6 +60,7 @@ static struct agp_methods agp_via_method agp_via_bind_page, agp_via_unbind_page, agp_via_flush_tlb, + NULL, agp_generic_enable, agp_generic_alloc_memory, agp_generic_free_memory, @@ -228,7 +229,8 @@ agp_via_set_aperture(struct agp_softc *s } static int -agp_via_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical) +agp_via_bind_page(struct agp_softc *sc, off_t offset, bus_addr_t physical, + int flags) { struct agp_via_softc *asc = sc->as_chipc; diff -Naurp old/src/sys/dev/pci/drm/drm_agpsupport.c new/src/sys/dev/pci/drm/drm_agpsupport.c --- old/src/sys/dev/pci/drm/drm_agpsupport.c 1970-01-01 01:00:00.000000000 +0100 +++ new/src/sys/dev/pci/drm/drm_agpsupport.c 2011-02-08 11:14:00.000000000 +0100 @@ -0,0 +1,375 @@ +/*- + * 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 + * VA LINUX SYSTEMS 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. + * + * Author: + * Rickard E. (Rik) Faith + * Gareth Hughes + * + */ + +/* + * Support code for tying the kernel AGP support to DRM drivers and + * the DRM's AGP ioctls. + */ + +#include "drmP.h" + +struct drm_agp_mem *drm_agp_lookup_entry(struct drm_device *, void *); +void drm_agp_remove_entry(struct drm_device *, + struct drm_agp_mem *); + +int +drm_agp_info(struct drm_device * dev, struct drm_agp_info *info) +{ + struct agp_info *kern; + + if (dev->agp == NULL || !dev->agp->acquired) + return (EINVAL); + + kern = &dev->agp->info; +#ifndef DRM_NO_AGP + agp_get_info(dev->agp->agpdev, kern); +#endif + info->agp_version_major = 1; + info->agp_version_minor = 0; + info->mode = kern->ai_mode; + info->aperture_base = kern->ai_aperture_base; + info->aperture_size = kern->ai_aperture_size; + info->memory_allowed = kern->ai_memory_allowed; + info->memory_used = kern->ai_memory_used; + info->id_vendor = kern->ai_devid & 0xffff; + info->id_device = kern->ai_devid >> 16; + + return (0); +} + +int +drm_agp_info_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_agp_info *info = data; + + return (drm_agp_info(dev, info)); +} + +int +drm_agp_acquire_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return (drm_agp_acquire(dev)); +} + +int +drm_agp_acquire(struct drm_device *dev) +{ +#ifndef DRM_NO_AGP + int retcode; + + if (dev->agp == NULL || dev->agp->acquired) + return (EINVAL); + + retcode = agp_acquire(dev->agp->agpdev); + if (retcode) + return (retcode); + + dev->agp->acquired = 1; +#endif + return (0); +} + +int +drm_agp_release_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return (drm_agp_release(dev)); +} + +int +drm_agp_release(struct drm_device * dev) +{ +#ifndef DRM_NO_AGP + if (dev->agp == NULL || !dev->agp->acquired) + return (EINVAL); + agp_release(dev->agp->agpdev); + dev->agp->acquired = 0; +#endif + return (0); +} + +int +drm_agp_enable(struct drm_device *dev, drm_agp_mode_t mode) +{ + int retcode = 0; +#ifndef DRM_NO_AGP + if (dev->agp == NULL || !dev->agp->acquired) + return (EINVAL); + + dev->agp->mode = mode.mode; + if ((retcode = agp_enable(dev->agp->agpdev, mode.mode)) == 0) + dev->agp->enabled = 1; +#endif + return (retcode); +} + +int +drm_agp_enable_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_agp_mode *mode = data; + + return (drm_agp_enable(dev, *mode)); +} + +int +drm_agp_alloc(struct drm_device *dev, struct drm_agp_buffer *request) +{ +#ifndef DRM_NO_AGP + struct drm_agp_mem *entry; + void *handle; + struct agp_memory_info info; + unsigned long pages; + u_int32_t type; + + if (dev->agp == NULL || !dev->agp->acquired) + return (EINVAL); + + entry = drm_alloc(sizeof(*entry)); + if (entry == NULL) + return (ENOMEM); + + pages = (request->size + PAGE_SIZE - 1) / PAGE_SIZE; + type = (u_int32_t)request->type; + + handle = agp_alloc_memory(dev->agp->agpdev, type, + pages << AGP_PAGE_SHIFT); + if (handle == NULL) { + drm_free(entry); + return (ENOMEM); + } + + entry->handle = handle; + entry->bound = 0; + entry->pages = pages; + + agp_memory_info(dev->agp->agpdev, entry->handle, &info); + + request->handle = (unsigned long)entry->handle; + request->physical = info.ami_physical; + DRM_LOCK(); + TAILQ_INSERT_HEAD(&dev->agp->memory, entry, link); + DRM_UNLOCK(); +#endif + + return (0); +} + +int +drm_agp_alloc_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_agp_buffer *request = data; + + return (drm_agp_alloc(dev, request)); +} + +/* + * find entry on agp list. Must be called with dev_lock locked. + */ +struct drm_agp_mem * +drm_agp_lookup_entry(struct drm_device *dev, void *handle) +{ + struct drm_agp_mem *entry; + + TAILQ_FOREACH(entry, &dev->agp->memory, link) { + if (entry->handle == handle) + break; + } + return (entry); +} + +int +drm_agp_unbind(struct drm_device *dev, struct drm_agp_binding *request) +{ + struct drm_agp_mem *entry; + int retcode; + + if (dev->agp == NULL || !dev->agp->acquired) + return (EINVAL); + + DRM_LOCK(); + entry = drm_agp_lookup_entry(dev, (void *)request->handle); + if (entry == NULL || !entry->bound) { + DRM_UNLOCK(); + return (EINVAL); + } + + retcode = agp_unbind_memory(dev->agp->agpdev, entry->handle); + + if (retcode == 0) + entry->bound = 0; + DRM_UNLOCK(); + + return (retcode); +} + +int +drm_agp_unbind_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_agp_binding *request = data; + + return (drm_agp_unbind(dev, request)); +} + +int +drm_agp_bind(struct drm_device *dev, struct drm_agp_binding *request) +{ + struct drm_agp_mem *entry; + int retcode, page; + + if (dev->agp == NULL || !dev->agp->acquired) + return (EINVAL); + + DRM_DEBUG("agp_bind, page_size=%x\n", PAGE_SIZE); + + DRM_LOCK(); + entry = drm_agp_lookup_entry(dev, (void *)request->handle); + if (entry == NULL || entry->bound) { + DRM_UNLOCK(); + return (EINVAL); + } + + page = (request->offset + PAGE_SIZE - 1) / PAGE_SIZE; + + retcode = agp_bind_memory(dev->agp->agpdev, entry->handle, + page * PAGE_SIZE); + if (retcode == 0) + entry->bound = dev->agp->base + (page << PAGE_SHIFT); + DRM_UNLOCK(); + + return (retcode); +} + +int +drm_agp_bind_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_agp_binding *request = data; + + return (drm_agp_bind(dev, request)); +} + +/* + * Remove entry from list and free. Call locked. + */ +void +drm_agp_remove_entry(struct drm_device *dev, struct drm_agp_mem *entry) +{ + TAILQ_REMOVE(&dev->agp->memory, entry, link); + + if (entry->bound) + agp_unbind_memory(dev->agp->agpdev, entry->handle); + agp_free_memory(dev->agp->agpdev, entry->handle); + drm_free(entry); +} + +void +drm_agp_takedown(struct drm_device *dev) +{ + struct drm_agp_mem *entry; + + if (dev->agp == NULL) + return; + + /* + * Remove AGP resources, but leave dev->agp intact until + * we detach the device + */ + DRM_LOCK(); + while ((entry = TAILQ_FIRST(&dev->agp->memory)) != NULL) + drm_agp_remove_entry(dev, entry); + DRM_UNLOCK(); + + drm_agp_release(dev); + dev->agp->enabled = 0; +} + +int +drm_agp_free(struct drm_device *dev, struct drm_agp_buffer *request) +{ + struct drm_agp_mem *entry; + + if (dev->agp == NULL || !dev->agp->acquired) + return (EINVAL); + + DRM_LOCK(); + entry = drm_agp_lookup_entry(dev, (void*)request->handle); + if (entry == NULL) { + DRM_UNLOCK(); + return (EINVAL); + } + + drm_agp_remove_entry(dev, entry); + DRM_UNLOCK(); + + return (0); +} + +int +drm_agp_free_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_agp_buffer *request = data; + + return (drm_agp_free(dev, request)); +} + +struct drm_agp_head * +drm_agp_init(void) +{ +#ifndef DRM_NO_AGP + struct agp_softc *agpdev; + struct drm_agp_head *head = NULL; + int agp_available = 1; + + agpdev = agp_find_device(0); + if (agpdev == NULL) + agp_available = 0; + + DRM_DEBUG("agp_available = %d\n", agp_available); + + if (agp_available) { + head = drm_calloc(1, sizeof(*head)); + if (head == NULL) + return (NULL); + head->agpdev = agpdev; + agp_get_info(agpdev, &head->info); + head->base = head->info.ai_aperture_base; + TAILQ_INIT(&head->memory); + } + return (head); +#else + return (NULL); +#endif +} diff -Naurp old/src/sys/dev/pci/drm/drm_atomic.h new/src/sys/dev/pci/drm/drm_atomic.h --- old/src/sys/dev/pci/drm/drm_atomic.h 1970-01-01 01:00:00.000000000 +0100 +++ new/src/sys/dev/pci/drm/drm_atomic.h 2011-02-08 11:14:00.000000000 +0100 @@ -0,0 +1,193 @@ +/** + * \file drm_atomic.h + * Atomic operations used in the DRM which may or may not be provided by the OS. + * + * \author Eric Anholt + */ + +/*- + * Copyright 2004 Eric Anholt + * 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 + * VA LINUX SYSTEMS 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. + */ + +#ifdef __OpenBSD__ +#include +#endif + +#ifdef __NetBSD__ +#include +#endif + +/* Many of these implementations are rather fake, but good enough. */ + +typedef u_int32_t atomic_t; + +#if !defined(__NetBSD__) + +#ifdef __FreeBSD__ +#define atomic_set(p, v) (*(p) = (v)) +#define atomic_read(p) (*(p)) +#define atomic_inc(p) atomic_add_int(p, 1) +#define atomic_dec(p) atomic_subtract_int(p, 1) +#define atomic_add(n, p) atomic_add_int(p, n) +#define atomic_sub(n, p) atomic_subtract_int(p, n) +#else /* __FreeBSD__ */ +/* FIXME */ +#define atomic_set(p, v) (*(p) = (v)) +#define atomic_read(p) (*(p)) +#define atomic_inc(p) (*(p) += 1) +#define atomic_dec(p) (*(p) -= 1) +#define atomic_add(n, p) (*(p) += (n)) +#define atomic_sub(n, p) (*(p) -= (n)) +/* FIXME */ +#define atomic_add_int(p, v) *(p) += v +#define atomic_subtract_int(p, v) *(p) -= v +#ifdef __OpenBSD__ +#define atomic_set_int(p, bits) atomic_setbits_int(p,bits) +#define atomic_clear_int(p, bits) atomic_clearbits_int(p,bits) +#else +#define atomic_set_int(p, bits) *(p) |= (bits) +#define atomic_clear_int(p, bits) *(p) &= ~(bits) +#endif +#endif /* !__FreeBSD__ */ + +#if !defined(__FreeBSD_version) || (__FreeBSD_version < 500000) +#if defined(__i386__) || defined(__amd64__) +/* The extra atomic functions from 5.0 haven't been merged to 4.x */ +static __inline int +atomic_cmpset_int(volatile u_int *dst, u_int exp, u_int src) +{ + int res = exp; + + __asm __volatile ( + " lock ; " + " cmpxchgl %1,%2 ; " + " setz %%al ; " + " movzbl %%al,%0 ; " + "1: " + "# atomic_cmpset_int" + : "+a" (res) /* 0 (result) */ + : "r" (src), /* 1 */ + "m" (*(dst)) /* 2 */ + : "memory"); + + return (res); +} +#else /* __i386__ */ +static __inline int +atomic_cmpset_int(__volatile__ u_int *dst, u_int old, u_int new) +{ + int s = splhigh(); + if (*dst==old) { + *dst = new; + splx(s); + return 1; + } + splx(s); + return 0; +} +#endif /* !__i386__ */ +#endif /* !__FreeBSD_version || __FreeBSD_version < 500000 */ + +static __inline atomic_t +test_and_set_bit(u_int b, volatile void *p) +{ + int s = splhigh(); + unsigned int m = 1<> 5), 1 << (b & 0x1f)); +} + +static __inline void +set_bit(u_int b, volatile void *p) +{ + atomic_set_int(((volatile u_int *)p) + (b >> 5), 1 << (b & 0x1f)); +} + +static __inline int +test_bit(u_int b, volatile void *p) +{ + return !!(((volatile u_int *)p)[b >> 5] & (1 << (b & 0x1f))); +} + +static __inline int +find_first_zero_bit(volatile void *p, int max_) +{ + int b; + volatile u_int *ptr = (volatile u_int *)p; + + for (b = 0; b < max_; b += 32) { + if (ptr[b >> 5] != ~0) { + for (;;) { + if ((ptr[b >> 5] & (1 << (b & 0x1f))) == 0) + return b; + b++; + } + } + } + return max_; +} diff -Naurp old/src/sys/dev/pci/drm/drm_bufs.c new/src/sys/dev/pci/drm/drm_bufs.c --- old/src/sys/dev/pci/drm/drm_bufs.c 1970-01-01 01:00:00.000000000 +0100 +++ new/src/sys/dev/pci/drm/drm_bufs.c 2011-02-08 11:14:00.000000000 +0100 @@ -0,0 +1,1018 @@ +/*- + * Copyright 1999, 2000 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 + * VA LINUX SYSTEMS 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 + * Gareth Hughes + * + */ + +/** @file drm_bufs.c + * Implementation of the ioctls for setup of DRM mappings and DMA buffers. + */ + +#include "sys/types.h" +#include "dev/pci/pcireg.h" + +#include "drmP.h" + +int drm_addbufs_pci(struct drm_device *, struct drm_buf_desc *); +int drm_addbufs_sg(struct drm_device *, struct drm_buf_desc *); +int drm_addbufs_agp(struct drm_device *, struct drm_buf_desc *); + +/* + * Compute order. Can be made faster. + */ +int +drm_order(unsigned long size) +{ + int order; + unsigned long tmp; + + for (order = 0, tmp = size; tmp >>= 1; ++order) + ; + + if (size & ~(1 << order)) + ++order; + + return order; +} + +struct drm_local_map * +drm_core_findmap(struct drm_device *dev, unsigned long offset) +{ + struct drm_local_map *map; + + DRM_LOCK(); + TAILQ_FOREACH(map, &dev->maplist, link) { + if (offset == map->ext) + break; + } + DRM_UNLOCK(); + return (map); +} + +int +drm_addmap(struct drm_device * dev, unsigned long offset, unsigned long size, + enum drm_map_type type, enum drm_map_flags flags, + struct drm_local_map **map_ptr) +{ + struct drm_local_map *map; + int align, ret = 0; +#if 0 /* disabled for now */ + struct drm_agp_mem *entry; + int valid; +#endif + + /* Only allow shared memory to be removable since we only keep enough + * book keeping information about shared memory to allow for removal + * when processes fork. + */ + if ((flags & _DRM_REMOVABLE) && type != _DRM_SHM) { + DRM_ERROR("Requested removable map for non-DRM_SHM\n"); + return EINVAL; + } + if ((offset & PAGE_MASK) || (size & PAGE_MASK)) { + DRM_ERROR("offset/size not page aligned: 0x%lx/0x%lx\n", + offset, size); + return EINVAL; + } + if (offset + size < offset) { + DRM_ERROR("offset and size wrap around: 0x%lx/0x%lx\n", + offset, size); + return EINVAL; + } + + DRM_DEBUG("offset = 0x%08lx, size = 0x%08lx, type = %d\n", offset, + size, type); + + /* + * Check if this is just another version of a kernel-allocated map, and + * just hand that back if so. + */ + DRM_LOCK(); + if (type == _DRM_REGISTERS || type == _DRM_FRAME_BUFFER || + type == _DRM_SHM) { + TAILQ_FOREACH(map, &dev->maplist, link) { + if (map->type == type && (map->offset == offset || + (map->type == _DRM_SHM && + map->flags == _DRM_CONTAINS_LOCK))) { + DRM_DEBUG("Found kernel map %d\n", type); + goto done; + } + } + } + DRM_UNLOCK(); + + /* Allocate a new map structure, fill it in, and do any type-specific + * initialization necessary. + */ + map = drm_calloc(1, sizeof(*map)); + if (map == NULL) { + DRM_LOCK(); + return ENOMEM; + } + + map->offset = offset; + map->size = size; + map->type = type; + map->flags = flags; + + + DRM_LOCK(); +#if !defined(__NetBSD__) + ret = extent_alloc(dev->handle_ext, map->size, PAGE_SIZE, 0, +#else /* !defined(__NetBSD__) */ + ret = extent_alloc(dev->handle_ext, map->size, PAGE_SIZE, +#endif /* !defined(__NetBSD__) */ + 0, EX_NOWAIT, &map->ext); + if (ret) { + DRM_ERROR("can't find free offset\n"); + DRM_UNLOCK(); + drm_free(map); + return (ret); + } + DRM_UNLOCK(); + + switch (map->type) { + case _DRM_REGISTERS: + if (!(map->flags & _DRM_WRITE_COMBINING)) + break; + /* FALLTHROUGH */ + case _DRM_FRAME_BUFFER: + if (drm_mtrr_add(map->offset, map->size, DRM_MTRR_WC) == 0) + map->mtrr = 1; + break; + case _DRM_AGP: + /*valid = 0;*/ + /* In some cases (i810 driver), user space may have already + * added the AGP base itself, because dev->agp->base previously + * only got set during AGP enable. So, only add the base + * address if the map's offset isn't already within the + * aperture. + */ + if (map->offset < dev->agp->base || + map->offset > dev->agp->base + + dev->agp->info.ai_aperture_size - 1) { + map->offset += dev->agp->base; + } + map->mtrr = dev->agp->mtrr; /* for getmap */ +#if 0 /* disabled for now */ + /* + * If agp is in control of userspace (some intel drivers for + * example. In which case ignore this loop. + */ + DRM_LOCK(); + TAILQ_FOREACH(entry, &dev->agp->memory, link) { + DRM_DEBUG("bound = %p, pages = %p, %p\n", + entry->bound, entry->pages, + entry->pages * PAGE_SIZE); + if ((map->offset >= entry->bound) && + (map->offset + map->size <= + entry->bound + entry->pages * PAGE_SIZE)) { + valid = 1; + break; + } + } + if (!TAILQ_EMPTY(&dev->agp->memory) && !valid) { + DRM_UNLOCK(); + drm_free(map); + DRM_ERROR("invalid agp map requested\n"); + return (EACCES); + } + DRM_UNLOCK(); +#endif + break; + case _DRM_SCATTER_GATHER: + if (dev->sg == NULL) { + drm_free(map); + return (EINVAL); + } + map->offset += dev->sg->handle; + break; + case _DRM_SHM: + case _DRM_CONSISTENT: + /* + * Unfortunately, we don't get any alignment specification from + * the caller, so we have to guess. So try to align the bus + * address of the map to its size if possible, otherwise just + * assume PAGE_SIZE alignment. + */ + align = map->size; + if ((align & (align - 1)) != 0) + align = PAGE_SIZE; + map->dmamem = drm_dmamem_alloc(dev->dmat, map->size, align, + 1, map->size, 0, 0); + if (map->dmamem == NULL) { + drm_free(map); + return (ENOMEM); + } + map->handle = map->dmamem->kva; + map->offset = map->dmamem->map->dm_segs[0].ds_addr; + if (map->type == _DRM_SHM && map->flags & _DRM_CONTAINS_LOCK) { + DRM_LOCK(); + /* Prevent a 2nd X Server from creating a 2nd lock */ + if (dev->lock.hw_lock != NULL) { + DRM_UNLOCK(); + drm_dmamem_free(dev->dmat, map->dmamem); + drm_free(map); + return (EBUSY); + } + dev->lock.hw_lock = map->handle; + DRM_UNLOCK(); + } + break; + default: + DRM_ERROR("Bad map type %d\n", map->type); + drm_free(map); + return EINVAL; + } + + DRM_LOCK(); + TAILQ_INSERT_TAIL(&dev->maplist, map, link); +done: + DRM_UNLOCK(); + + DRM_DEBUG("Added map %d 0x%lx/0x%lx\n", map->type, map->offset, + map->size); + + *map_ptr = map; + + return 0; +} + +int +drm_addmap_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_map *request = data; + struct drm_local_map *map; + int err; + + if (!(file_priv->flags & (FREAD|FWRITE))) + return EACCES; /* Require read/write */ + + err = drm_addmap(dev, request->offset, request->size, request->type, + request->flags, &map); + if (err != 0) + return err; + + request->offset = map->offset; + request->size = map->size; + request->type = map->type; + request->flags = map->flags; + request->mtrr = map->mtrr; + request->handle = map->handle; + + request->handle = (void *)map->ext; + + return 0; +} + +void +drm_rmmap(struct drm_device *dev, struct drm_local_map *map) +{ + DRM_LOCK(); + drm_rmmap_locked(dev, map); + DRM_UNLOCK(); +} + + +void +drm_rmmap_locked(struct drm_device *dev, struct drm_local_map *map) +{ + TAILQ_REMOVE(&dev->maplist, map, link); + + switch (map->type) { + case _DRM_REGISTERS: + /* FALLTHROUGH */ + case _DRM_FRAME_BUFFER: + if (map->mtrr) { + int retcode; + + retcode = drm_mtrr_del(0, map->offset, map->size, + DRM_MTRR_WC); + DRM_DEBUG("mtrr_del = %d\n", retcode); + } + break; + case _DRM_AGP: + /* FALLTHROUGH */ + case _DRM_SCATTER_GATHER: + break; + case _DRM_SHM: + /* FALLTHROUGH */ + case _DRM_CONSISTENT: + drm_dmamem_free(dev->dmat, map->dmamem); + break; + default: + DRM_ERROR("Bad map type %d\n", map->type); + break; + } + + /* NOCOALESCE set, can't fail */ + extent_free(dev->handle_ext, map->ext, map->size, EX_NOWAIT); + + drm_free(map); +} + +/* Remove a map private from list and deallocate resources if the mapping + * isn't in use. + */ + +int +drm_rmmap_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_local_map *map; + struct drm_map *request = data; + + DRM_LOCK(); + TAILQ_FOREACH(map, &dev->maplist, link) { + if (map->handle == request->handle && + map->flags & _DRM_REMOVABLE) + break; + } + + /* No match found. */ + if (map == NULL) { + DRM_UNLOCK(); + return (EINVAL); + } + + drm_rmmap_locked(dev, map); + + DRM_UNLOCK(); + + return 0; +} + +/* + * DMA buffers api. + * + * The implementation used to be significantly more complicated, but the + * complexity has been moved into the drivers as different buffer management + * schemes evolved. + * + * This api is going to die eventually. + */ + +int +drm_dma_setup(struct drm_device *dev) +{ + + dev->dma = drm_calloc(1, sizeof(*dev->dma)); + if (dev->dma == NULL) + return (ENOMEM); + +#if !defined(__NetBSD__) + rw_init(&dev->dma->dma_lock, "drmdma"); +#else /* !defined(__NetBSD__) */ + rw_init(&dev->dma->dma_lock); +#endif /* !defined(__NetBSD__) */ + + return (0); +} + +void +drm_cleanup_buf(struct drm_device *dev, struct drm_buf_entry *entry) +{ + int i; + + if (entry->seg_count) { + for (i = 0; i < entry->seg_count; i++) + drm_dmamem_free(dev->dmat, entry->seglist[i]); + drm_free(entry->seglist); + + entry->seg_count = 0; + } + + if (entry->buf_count) { + for (i = 0; i < entry->buf_count; i++) { + drm_free(entry->buflist[i].dev_private); + } + drm_free(entry->buflist); + + entry->buf_count = 0; + } +} + +void +drm_dma_takedown(struct drm_device *dev) +{ + struct drm_device_dma *dma = dev->dma; + int i; + + if (dma == NULL) + return; + + /* Clear dma buffers */ + for (i = 0; i <= DRM_MAX_ORDER; i++) + drm_cleanup_buf(dev, &dma->bufs[i]); + + drm_free(dma->buflist); + drm_free(dma->pagelist); + drm_free(dev->dma); + dev->dma = NULL; +} + + +void +drm_free_buffer(struct drm_device *dev, struct drm_buf *buf) +{ + if (buf == NULL) + return; + + buf->pending = 0; + buf->file_priv= NULL; + buf->used = 0; +} + +void +drm_reclaim_buffers(struct drm_device *dev, struct drm_file *file_priv) +{ + struct drm_device_dma *dma = dev->dma; + int i; + + if (dma == NULL) + return; + for (i = 0; i < dma->buf_count; i++) { + if (dma->buflist[i]->file_priv == file_priv) + drm_free_buffer(dev, dma->buflist[i]); + } +} + +/* Call into the driver-specific DMA handler */ +int +drm_dma(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_device_dma *dma = dev->dma; + struct drm_dma *d = data; + int ret = 0; + + if (dev->driver->dma_ioctl == NULL) { + DRM_DEBUG("DMA ioctl on driver with no dma handler\n"); + return (EINVAL); + } + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + /* Please don't send us buffers. + */ + if (d->send_count != 0) { + DRM_ERROR("process trying to send %d buffers via drmDMA\n", + d->send_count); + return (EINVAL); + } + + /* We'll send you buffers. + */ + if (d->request_count < 0 || d->request_count > dma->buf_count) { + DRM_ERROR("Process trying to get %d buffers (of %d max)\n", + d->request_count, dma->buf_count); + return (EINVAL); + } + d->granted_count = 0; + + if (d->request_count) + ret = dev->driver->dma_ioctl(dev, d, file_priv); + return (ret); +} + +int +drm_addbufs_agp(struct drm_device *dev, struct drm_buf_desc *request) +{ + struct drm_device_dma *dma = dev->dma; + struct drm_buf_entry *entry; + struct drm_buf *buf, **temp_buflist; + unsigned long agp_offset, offset; + int alignment, count, order, page_order, size; + int total, byte_count, i; +#if 0 /* disabled for now */ + struct drm_agp_mem *agp_entry; + int valid; +#endif + + count = request->count; + order = drm_order(request->size); + size = 1 << order; + + alignment = (request->flags & _DRM_PAGE_ALIGN) + ? round_page(size) : size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + + byte_count = 0; + agp_offset = dev->agp->base + request->agp_start; + + DRM_DEBUG("count: %d\n", count); + DRM_DEBUG("order: %d\n", order); + DRM_DEBUG("size: %d\n", size); + DRM_DEBUG("agp_offset: 0x%lx\n", agp_offset); + DRM_DEBUG("alignment: %d\n", alignment); + DRM_DEBUG("page_order: %d\n", page_order); + DRM_DEBUG("total: %d\n", total); + + /* Make sure buffers are located in AGP memory that we own */ + + /* Breaks MGA due to drm_alloc_agp not setting up entries for the + * memory. Safe to ignore for now because these ioctls are still + * root-only. + */ +#if 0 /* disabled for now */ + valid = 0; + DRM_LOCK(); + TAILQ_FOREACH(agp_entry, &dev->agp->memory, link) { + if ((agp_offset >= agp_entry->bound) && + (agp_offset + total * count <= + agp_entry->bound + agp_entry->pages * PAGE_SIZE)) { + valid = 1; + break; + } + } + if (!TAILQ_EMPTY(&dev->agp->memory) && !valid) { + DRM_DEBUG("zone invalid\n"); + DRM_UNLOCK(); + return (EINVAL); + } + DRM_UNLOCK(); +#endif + + entry = &dma->bufs[order]; + + entry->buflist = drm_calloc(count, sizeof(*entry->buflist)); + if (entry->buflist == NULL) + return ENOMEM; + + entry->buf_size = size; + entry->page_order = page_order; + + offset = 0; + + while (entry->buf_count < count) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->used = 0; + + buf->offset = (dma->byte_count + offset); + buf->bus_address = agp_offset + offset; + buf->pending = 0; + buf->file_priv = NULL; + + buf->dev_private = drm_calloc(1, dev->driver->buf_priv_size); + if (buf->dev_private == NULL) { + /* Set count correctly so we free the proper amount. */ + entry->buf_count = count; + drm_cleanup_buf(dev, entry); + return ENOMEM; + } + + offset += alignment; + entry->buf_count++; + byte_count += PAGE_SIZE << page_order; + } + + DRM_DEBUG("byte_count: %d\n", byte_count); + + /* OpenBSD lacks realloc in kernel */ + temp_buflist = drm_realloc(dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist)); + if (temp_buflist == NULL) { + /* Free the entry because it isn't valid */ + drm_cleanup_buf(dev, entry); + return ENOMEM; + } + dma->buflist = temp_buflist; + + for (i = 0; i < entry->buf_count; i++) + dma->buflist[i + dma->buf_count] = &entry->buflist[i]; + + dma->buf_count += entry->buf_count; + dma->byte_count += byte_count; + + DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count); + DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count); + + request->count = entry->buf_count; + request->size = size; + + dma->flags = _DRM_DMA_USE_AGP; + + return 0; +} + +int +drm_addbufs_pci(struct drm_device *dev, struct drm_buf_desc *request) +{ + struct drm_device_dma *dma = dev->dma; + struct drm_buf *buf, **temp_buflist; + struct drm_buf_entry *entry; + int alignment, byte_count, count, i, order; + int page_count, page_order, size, total; + unsigned long offset, *temp_pagelist; + + count = request->count; + order = drm_order(request->size); + size = 1 << order; + + DRM_DEBUG("count=%d, size=%d (%d), order=%d\n", + request->count, request->size, size, order); + + alignment = (request->flags & _DRM_PAGE_ALIGN) + ? round_page(size) : size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + + entry = &dma->bufs[order]; + + entry->buflist = drm_calloc(count, sizeof(*entry->buflist)); + entry->seglist = drm_calloc(count, sizeof(*entry->seglist)); + + /* Keep the original pagelist until we know all the allocations + * have succeeded + */ + temp_pagelist = drm_calloc((dma->page_count + (count << page_order)), + sizeof(*dma->pagelist)); + + if (entry->buflist == NULL || entry->seglist == NULL || + temp_pagelist == NULL) { + drm_free(temp_pagelist); + drm_free(entry->seglist); + drm_free(entry->buflist); + return ENOMEM; + } + + memcpy(temp_pagelist, dma->pagelist, dma->page_count * + sizeof(*dma->pagelist)); + + DRM_DEBUG("pagelist: %d entries\n", + dma->page_count + (count << page_order)); + + entry->buf_size = size; + entry->page_order = page_order; + byte_count = 0; + page_count = 0; + + while (entry->buf_count < count) { + struct drm_dmamem *mem = drm_dmamem_alloc(dev->dmat, size, + alignment, 1, size, 0, 0); + if (mem == NULL) { + /* Set count correctly so we free the proper amount. */ + entry->buf_count = count; + entry->seg_count = count; + drm_cleanup_buf(dev, entry); + drm_free(temp_pagelist); + return ENOMEM; + } + + entry->seglist[entry->seg_count++] = mem; + for (i = 0; i < (1 << page_order); i++) { + DRM_DEBUG("page %d @ %p\n", dma->page_count + + page_count, (void *)((vaddr_t)mem->kva + PAGE_SIZE * i)); + temp_pagelist[dma->page_count + page_count++] = + (vaddr_t)mem->kva + PAGE_SIZE * i; + } + for (offset = 0; + offset + size <= total && entry->buf_count < count; + offset += alignment, ++entry->buf_count) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->used = 0; + buf->offset = (dma->byte_count + byte_count + offset); + buf->address = (void *)((vaddr_t)mem->kva + offset); + buf->bus_address = mem->map->dm_segs[0].ds_addr + + offset; + buf->pending = 0; + buf->file_priv = NULL; + + buf->dev_private = drm_calloc(1, + dev->driver->buf_priv_size); + if (buf->dev_private == NULL) { + /* Set count so we free the proper amount. */ + entry->buf_count = count; + entry->seg_count = count; + drm_cleanup_buf(dev, entry); + drm_free(temp_pagelist); + return ENOMEM; + } + + DRM_DEBUG("buffer %d\n", + entry->buf_count); + } + byte_count += PAGE_SIZE << page_order; + } + + temp_buflist = drm_realloc(dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist)); + if (temp_buflist == NULL) { + /* Free the entry because it isn't valid */ + drm_cleanup_buf(dev, entry); + drm_free(temp_pagelist); + return ENOMEM; + } + dma->buflist = temp_buflist; + + for (i = 0; i < entry->buf_count; i++) + dma->buflist[i + dma->buf_count] = &entry->buflist[i]; + + /* No allocations failed, so now we can replace the orginal pagelist + * with the new one. + */ + drm_free(dma->pagelist); + dma->pagelist = temp_pagelist; + + dma->buf_count += entry->buf_count; + dma->seg_count += entry->seg_count; + dma->page_count += entry->seg_count << page_order; + dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order); + + request->count = entry->buf_count; + request->size = size; + + return 0; + +} + +int +drm_addbufs_sg(struct drm_device *dev, struct drm_buf_desc *request) +{ + struct drm_device_dma *dma = dev->dma; + struct drm_buf_entry *entry; + struct drm_buf *buf, **temp_buflist; + unsigned long agp_offset, offset; + int alignment, byte_count, count, i, order; + int page_order, size, total; + + count = request->count; + order = drm_order(request->size); + size = 1 << order; + + alignment = (request->flags & _DRM_PAGE_ALIGN) + ? round_page(size) : size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + + byte_count = 0; + agp_offset = request->agp_start; + + DRM_DEBUG("count: %d\n", count); + DRM_DEBUG("order: %d\n", order); + DRM_DEBUG("size: %d\n", size); + DRM_DEBUG("agp_offset: %ld\n", agp_offset); + DRM_DEBUG("alignment: %d\n", alignment); + DRM_DEBUG("page_order: %d\n", page_order); + DRM_DEBUG("total: %d\n", total); + + entry = &dma->bufs[order]; + + entry->buflist = drm_calloc(count, sizeof(*entry->buflist)); + if (entry->buflist == NULL) + return ENOMEM; + + entry->buf_size = size; + entry->page_order = page_order; + + offset = 0; + + while (entry->buf_count < count) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->used = 0; + + buf->offset = (dma->byte_count + offset); + buf->bus_address = agp_offset + offset; + buf->pending = 0; + buf->file_priv = NULL; + + buf->dev_private = drm_calloc(1, dev->driver->buf_priv_size); + if (buf->dev_private == NULL) { + /* Set count correctly so we free the proper amount. */ + entry->buf_count = count; + drm_cleanup_buf(dev, entry); + return ENOMEM; + } + + DRM_DEBUG("buffer %d\n", entry->buf_count); + + offset += alignment; + entry->buf_count++; + byte_count += PAGE_SIZE << page_order; + } + + DRM_DEBUG("byte_count: %d\n", byte_count); + + temp_buflist = drm_realloc(dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist)); + if (temp_buflist == NULL) { + /* Free the entry because it isn't valid */ + drm_cleanup_buf(dev, entry); + return ENOMEM; + } + dma->buflist = temp_buflist; + + for (i = 0; i < entry->buf_count; i++) + dma->buflist[i + dma->buf_count] = &entry->buflist[i]; + + dma->buf_count += entry->buf_count; + dma->byte_count += byte_count; + + DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count); + DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count); + + request->count = entry->buf_count; + request->size = size; + + dma->flags = _DRM_DMA_USE_SG; + + return 0; +} + +int +drm_addbufs(struct drm_device *dev, struct drm_buf_desc *request) +{ + struct drm_device_dma *dma = dev->dma; + int order, ret; + + if (request->count < 0 || request->count > 4096) + return (EINVAL); + + order = drm_order(request->size); + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) + return (EINVAL); + + rw_enter_write(&dma->dma_lock); + + /* No more allocations after first buffer-using ioctl. */ + if (dma->buf_use != 0) { + rw_exit_write(&dma->dma_lock); + return (EBUSY); + } + /* No more than one allocation per order */ + if (dma->bufs[order].buf_count != 0) { + rw_exit_write(&dma->dma_lock); + return (ENOMEM); + } + + if (request->flags & _DRM_AGP_BUFFER) + ret = drm_addbufs_agp(dev, request); + else if (request->flags & _DRM_SG_BUFFER) + ret = drm_addbufs_sg(dev, request); + else + ret = drm_addbufs_pci(dev, request); + + rw_exit_write(&dma->dma_lock); + + return (ret); +} + +int +drm_freebufs(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_device_dma *dma = dev->dma; + struct drm_buf_free *request = data; + struct drm_buf *buf; + int i, idx, retcode = 0; + + DRM_DEBUG("%d\n", request->count); + + rw_enter_write(&dma->dma_lock); + for (i = 0; i < request->count; i++) { + if (DRM_COPY_FROM_USER(&idx, &request->list[i], sizeof(idx))) { + retcode = EFAULT; + break; + } + if (idx < 0 || idx >= dma->buf_count) { + DRM_ERROR("Index %d (of %d max)\n", idx, + dma->buf_count - 1); + retcode = EINVAL; + break; + } + buf = dma->buflist[idx]; + if (buf->file_priv != file_priv) { + DRM_ERROR("Process %d freeing buffer not owned\n", + DRM_CURRENTPID); + retcode = EINVAL; + break; + } + drm_free_buffer(dev, buf); + } + rw_exit_write(&dma->dma_lock); + + return retcode; +} + +int +drm_mapbufs(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_device_dma *dma = dev->dma; + struct drm_buf_map *request = data; + struct vnode *vn; + vaddr_t address, vaddr; + voff_t foff; + vsize_t size; + const int zero = 0; + int i, retcode = 0; + + if (!vfinddev(file_priv->kdev, VCHR, &vn)) + return EINVAL; + + rw_enter_write(&dma->dma_lock); + dev->dma->buf_use++; /* Can't allocate more after this call */ + rw_exit_write(&dma->dma_lock); + + if (request->count < dma->buf_count) + goto done; + + if ((dev->driver->flags & DRIVER_AGP && + (dma->flags & _DRM_DMA_USE_AGP)) || + (dev->driver->flags & DRIVER_SG && + (dma->flags & _DRM_DMA_USE_SG))) { + struct drm_local_map *map = dev->agp_buffer_map; + + if (map == NULL) { + DRM_DEBUG("couldn't find agp buffer map\n"); + retcode = EINVAL; + goto done; + } + size = round_page(map->size); + foff = map->ext; + } else { + size = round_page(dma->byte_count), + foff = 0; + } + +#if !defined(__NetBSD__) + vaddr = uvm_map_hint(curproc, VM_PROT_READ | VM_PROT_WRITE); + retcode = uvm_mmap(&curproc->p_vmspace->vm_map, &vaddr, size, + UVM_PROT_READ | UVM_PROT_WRITE, UVM_PROT_ALL, MAP_SHARED, + (caddr_t)vn, foff, curproc->p_rlimit[RLIMIT_MEMLOCK].rlim_cur, + curproc); +#else /* !defined(__NetBSD__) */ + vaddr = curproc->p_emul->e_vm_default_addr(curproc, + (vaddr_t)curproc->p_vmspace->vm_daddr, size); + retcode = uvm_mmap(&curproc->p_vmspace->vm_map, &vaddr, size, + UVM_PROT_READ | UVM_PROT_WRITE, UVM_PROT_ALL, MAP_SHARED, + (caddr_t)vn, foff, curproc->p_rlimit[RLIMIT_MEMLOCK].rlim_cur); +#endif /* !defined(__NetBSD__) */ + if (retcode) { + DRM_DEBUG("uvm_mmap failed\n"); + goto done; + } + + request->virtual = (void *)vaddr; + + for (i = 0; i < dma->buf_count; i++) { + if (DRM_COPY_TO_USER(&request->list[i].idx, + &dma->buflist[i]->idx, sizeof(request->list[0].idx))) { + retcode = EFAULT; + goto done; + } + if (DRM_COPY_TO_USER(&request->list[i].total, + &dma->buflist[i]->total, sizeof(request->list[0].total))) { + retcode = EFAULT; + goto done; + } + if (DRM_COPY_TO_USER(&request->list[i].used, &zero, + sizeof(zero))) { + retcode = EFAULT; + goto done; + } + address = vaddr + dma->buflist[i]->offset; /* *** */ + if (DRM_COPY_TO_USER(&request->list[i].address, &address, + sizeof(address))) { + retcode = EFAULT; + goto done; + } + } + + done: + request->count = dma->buf_count; + + DRM_DEBUG("%d buffers, retcode = %d\n", request->count, retcode); + + return retcode; +} diff -Naurp old/src/sys/dev/pci/drm/drm_context.c new/src/sys/dev/pci/drm/drm_context.c --- old/src/sys/dev/pci/drm/drm_context.c 1970-01-01 01:00:00.000000000 +0100 +++ new/src/sys/dev/pci/drm/drm_context.c 2011-02-08 11:14:00.000000000 +0100 @@ -0,0 +1,173 @@ +/*- + * Copyright 1999, 2000 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 + * VA LINUX SYSTEMS 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 + * Gareth Hughes + * + */ + +/** @file drm_context.c + * Implementation of the context management ioctls. + */ + +#include "drmP.h" + +/* ================================================================ + * Context bitmap support + */ + +void +drm_ctxbitmap_free(struct drm_device *dev, int ctx_handle) +{ + if (ctx_handle < 0 || ctx_handle >= DRM_MAX_CTXBITMAP || + dev->ctx_bitmap == NULL) { + DRM_ERROR("Attempt to free invalid context handle: %d\n", + ctx_handle); + return; + } + + DRM_LOCK(); + clear_bit(ctx_handle, dev->ctx_bitmap); + DRM_UNLOCK(); + return; +} + +int +drm_ctxbitmap_next(struct drm_device *dev) +{ + int bit; + + if (dev->ctx_bitmap == NULL) + return (-1); + + DRM_LOCK(); + bit = find_first_zero_bit(dev->ctx_bitmap, DRM_MAX_CTXBITMAP); + if (bit >= DRM_MAX_CTXBITMAP) { + DRM_UNLOCK(); + return (-1); + } + + set_bit(bit, dev->ctx_bitmap); + DRM_DEBUG("drm_ctxbitmap_next bit : %d\n", bit); + DRM_UNLOCK(); + return (bit); +} + +int +drm_ctxbitmap_init(struct drm_device *dev) +{ + atomic_t *bitmap; + int i, temp; + + + if ((bitmap = drm_calloc(1, PAGE_SIZE)) == NULL) + return (ENOMEM); + DRM_LOCK(); + dev->ctx_bitmap = bitmap; + DRM_UNLOCK(); + + for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + temp = drm_ctxbitmap_next(dev); + DRM_DEBUG("drm_ctxbitmap_init : %d\n", temp); + } + + return (0); +} + +void +drm_ctxbitmap_cleanup(struct drm_device *dev) +{ + atomic_t *bitmap; + + DRM_LOCK(); + bitmap = dev->ctx_bitmap; + dev->ctx_bitmap = NULL; + DRM_UNLOCK(); + drm_free(bitmap); +} + +int +drm_resctx(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_ctx_res *res = data; + struct drm_ctx ctx; + int i; + + if (res->count >= DRM_RESERVED_CONTEXTS) { + bzero(&ctx, sizeof(ctx)); + for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + ctx.handle = i; + if (DRM_COPY_TO_USER(&res->contexts[i], + &ctx, sizeof(ctx))) + return (EFAULT); + } + } + res->count = DRM_RESERVED_CONTEXTS; + + return (0); +} + +int +drm_addctx(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_ctx *ctx = data; + + ctx->handle = drm_ctxbitmap_next(dev); + if (ctx->handle == DRM_KERNEL_CONTEXT) { + /* Skip kernel's context and get a new one. */ + ctx->handle = drm_ctxbitmap_next(dev); + } + DRM_DEBUG("%d\n", ctx->handle); + if (ctx->handle == -1) { + DRM_DEBUG("Not enough free contexts.\n"); + /* Should this return -EBUSY instead? */ + return (ENOMEM); + } + + return (0); +} + +int +drm_getctx(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_ctx *ctx = data; + + /* This is 0, because we don't handle any context flags */ + ctx->flags = 0; + + return (0); +} + +int +drm_rmctx(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_ctx *ctx = data; + + DRM_DEBUG("%d\n", ctx->handle); + if (ctx->handle != DRM_KERNEL_CONTEXT) + drm_ctxbitmap_free(dev, ctx->handle); + + return (0); +} diff -Naurp old/src/sys/dev/pci/drm/drm_drv.c new/src/sys/dev/pci/drm/drm_drv.c --- old/src/sys/dev/pci/drm/drm_drv.c 1970-01-01 01:00:00.000000000 +0100 +++ new/src/sys/dev/pci/drm/drm_drv.c 2011-02-08 11:14:00.000000000 +0100 @@ -0,0 +1,1852 @@ +/*- + * Copyright 2007-2009 Owain G. Ainsworth + * Copyright © 2008 Intel Corporation + * Copyright 2003 Eric Anholt + * Copyright 1999, 2000 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 + * VA LINUX SYSTEMS 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 + * Daryll Strauss + * Gareth Hughes + * Eric Anholt + * Owain Ainsworth + * + */ + +/** @file drm_drv.c + * The catch-all file for DRM device support, including module setup/teardown, + * open/close, and ioctl dispatch. + */ + +#include +#if !defined(__NetBSD__) +#include +#else +#include +#endif +#include +#include + +#include /* for TIOCSGRP */ + +#include "drmP.h" +#include "drm.h" +#include "drm_sarea.h" + +#ifdef DRM_DEBUG_DEFAULT_ON +int drm_debug_flag = 1; +#else +int drm_debug_flag = 0; +#endif + +int drm_firstopen(struct drm_device *); +int drm_lastclose(struct drm_device *); +void drm_attach(struct device *, struct device *, void *); +#if !defined(__NetBSD__) +int drm_probe(struct device *, void *, void *); +#else /* !defined(__NetBSD__) */ +int drm_probe(struct device *, struct cfdata *, void *); +#endif /* !defined(__NetBSD__) */ +int drm_detach(struct device *, int); +#if !defined(__NetBSD__) +int drm_activate(struct device *, int); +#else /* !defined(__NetBSD__) */ +int drm_activate(struct device *, devact_t); +#endif /* !defined(__NetBSD__) */ +int drmprint(void *, const char *); +int drm_dequeue_event(struct drm_device *, struct drm_file *, size_t, + struct drm_pending_event **); + +int drm_getunique(struct drm_device *, void *, struct drm_file *); +int drm_version(struct drm_device *, void *, struct drm_file *); +int drm_setversion(struct drm_device *, void *, struct drm_file *); +int drm_getmagic(struct drm_device *, void *, struct drm_file *); +int drm_authmagic(struct drm_device *, void *, struct drm_file *); +int drm_file_cmp(struct drm_file *, struct drm_file *); +SPLAY_PROTOTYPE(drm_file_tree, drm_file, link, drm_file_cmp); + +/* functions used by the per-open handle code to grab references to object */ +void drm_handle_ref(struct drm_obj *); +void drm_handle_unref(struct drm_obj *); + +int drm_handle_cmp(struct drm_handle *, struct drm_handle *); +int drm_name_cmp(struct drm_obj *, struct drm_obj *); +int drm_fault(struct uvm_faultinfo *, vaddr_t, vm_page_t *, int, int, +#if !defined(__NetBSD__) + vm_fault_t, vm_prot_t, int); +#else /* !defined(__NetBSD__) */ + vm_prot_t, int); +#endif /* !defined(__NetBSD__) */ +boolean_t drm_flush(struct uvm_object *, voff_t, voff_t, int); + +SPLAY_PROTOTYPE(drm_obj_tree, drm_handle, entry, drm_handle_cmp); +SPLAY_PROTOTYPE(drm_name_tree, drm_obj, entry, drm_name_cmp); + +#if defined(__NetBSD__) +const struct cdevsw drm_cdevsw = { + drmopen, + drmclose, + drmread, + nowrite, + drmioctl, + nostop, + notty, + drmpoll, + drmmmap, + nokqfilter, + D_TTY | D_NEGOFFSAFE +}; +#endif /* defined(__NetBSD__) */ + +/* + * attach drm to a pci-based driver. + * + * This function does all the pci-specific calculations for the + * drm_attach_args. + */ +struct device * +drm_attach_pci(const struct drm_driver_info *driver, struct pci_attach_args *pa, + int is_agp, struct device *dev) +{ + struct drm_attach_args arg; + + arg.driver = driver; + arg.dmat = pa->pa_dmat; + arg.bst = pa->pa_memt; + arg.irq = pa->pa_intrline; + arg.is_agp = is_agp; + + arg.busid_len = 20; + arg.busid = malloc(arg.busid_len + 1, M_DRM, M_NOWAIT); + if (arg.busid == NULL) { + printf("%s: no memory for drm\n", dev->dv_xname); + return (NULL); + } + snprintf(arg.busid, arg.busid_len, "pci:%04x:%02x:%02x.%1x", +#if !defined(__NetBSD__) + pa->pa_domain, pa->pa_bus, pa->pa_device, pa->pa_function); +#else /* !defined(__NetBSD__) */ + device_unit(device_parent(device_parent(dev))), + pa->pa_bus, pa->pa_device, pa->pa_function); +#endif /* !defined(__NetBSD__) */ + + return (config_found(dev, &arg, drmprint)); +} + +int +drmprint(void *aux, const char *pnp) +{ + if (pnp != NULL) + printf("drm at %s", pnp); + return (UNCONF); +} + +int +drm_pciprobe(struct pci_attach_args *pa, const struct drm_pcidev *idlist) +{ + const struct drm_pcidev *id_entry; + + id_entry = drm_find_description(PCI_VENDOR(pa->pa_id), + PCI_PRODUCT(pa->pa_id), idlist); + if (id_entry != NULL) + return 1; + + return 0; +} + +int +#if !defined(__NetBSD__) +drm_probe(struct device *parent, void *match, void *aux) +#else /* !defined(__NetBSD__) */ +drm_probe(struct device *parent, struct cfdata *match, void *aux) +#endif /* !defined(__NetBSD__) */ +{ + struct drm_attach_args *da = aux; + + return (da->driver != NULL ? 1 : 0); +} + +void +drm_attach(struct device *parent, struct device *self, void *aux) +{ + struct drm_device *dev = (struct drm_device *)self; + struct drm_attach_args *da = aux; + + dev->dev_private = parent; + dev->driver = da->driver; + + dev->dmat = da->dmat; + dev->bst = da->bst; + dev->irq = da->irq; + dev->unique = da->busid; + dev->unique_len = da->busid_len; + +#if !defined(__NetBSD__) + rw_init(&dev->dev_lock, "drmdevlk"); +#else /* !defined(__NetBSD__) */ + rw_init(&dev->dev_lock); +#endif /* !defined(__NetBSD__) */ + mtx_init(&dev->lock.spinlock, IPL_NONE); + mtx_init(&dev->event_lock, IPL_TTY); +#if defined(__NetBSD__) + cv_init(&dev->lock.condvar, "drmlkq"); +#endif /* defined(__NetBSD__) */ + + TAILQ_INIT(&dev->maplist); + SPLAY_INIT(&dev->files); + + if (dev->driver->vblank_pipes != 0 && drm_vblank_init(dev, + dev->driver->vblank_pipes)) { + printf(": failed to allocate vblank data\n"); + goto error; + } + + /* + * the dma buffers api is just weird. offset 1Gb to ensure we don't + * conflict with it. + */ + dev->handle_ext = extent_create("drmext", 1024*1024*1024, LONG_MAX, + M_DRM, NULL, 0, EX_NOWAIT | EX_NOCOALESCE); + if (dev->handle_ext == NULL) { + DRM_ERROR("Failed to initialise handle extent\n"); + goto error; + } + + if (dev->driver->flags & DRIVER_AGP) { + if (da->is_agp) + dev->agp = drm_agp_init(); + if (dev->driver->flags & DRIVER_AGP_REQUIRE && + dev->agp == NULL) { + printf(": couldn't find agp\n"); + goto error; + } + if (dev->agp != NULL) { + if (drm_mtrr_add(dev->agp->info.ai_aperture_base, + dev->agp->info.ai_aperture_size, DRM_MTRR_WC) == 0) + dev->agp->mtrr = 1; + } + } + + if (drm_ctxbitmap_init(dev) != 0) { + printf(": couldn't allocate memory for context bitmap.\n"); + goto error; + } + + if (dev->driver->flags & DRIVER_GEM) { + mtx_init(&dev->obj_name_lock, IPL_NONE); + SPLAY_INIT(&dev->name_tree); + KASSERT(dev->driver->gem_size >= sizeof(struct drm_obj)); + /* XXX unique name */ + pool_init(&dev->objpl, dev->driver->gem_size, 0, 0, 0, +#if !defined(__NetBSD__) + "drmobjpl", &pool_allocator_nointr); +#else /* !defined(__NetBSD__) */ + "drmobjpl", &pool_allocator_nointr, IPL_NONE); +#endif /* !defined(__NetBSD__) */ + } + +#if defined(__NetBSD__) + (void)pmf_device_register(self, NULL, NULL); +#endif /* defined(__NetBSD__) */ + + printf("\n"); + return; + +error: + drm_lastclose(dev); +} + +int +drm_detach(struct device *self, int flags) +{ + struct drm_device *dev = (struct drm_device *)self; + +#if defined(__NetBSD__) + pmf_device_deregister(self); +#endif /* defined(__NetBSD__) */ + + drm_lastclose(dev); + + drm_ctxbitmap_cleanup(dev); + + extent_destroy(dev->handle_ext); + + drm_vblank_cleanup(dev); + + if (dev->agp && dev->agp->mtrr) { + int retcode; + + retcode = drm_mtrr_del(0, dev->agp->info.ai_aperture_base, + dev->agp->info.ai_aperture_size, DRM_MTRR_WC); + DRM_DEBUG("mtrr_del = %d", retcode); + } + + + if (dev->agp != NULL) { + drm_free(dev->agp); + dev->agp = NULL; + } + +#if defined(__NetBSD__) + if (dev->driver->flags & DRIVER_GEM) { + pool_destroy(&dev->objpl); + mutex_destroy(&dev->obj_name_lock); + } + cv_destroy(&dev->lock.condvar); + mutex_destroy(&dev->event_lock); + mutex_destroy(&dev->lock.spinlock); + rw_destroy(&dev->dev_lock); +#endif /* defined(__NetBSD__) */ + + return 0; +} + +int +#if !defined(__NetBSD__) +drm_activate(struct device *self, int act) +#else /* !defined(__NetBSD__) */ +drm_activate(struct device *self, devact_t act) +#endif /* !defined(__NetBSD__) */ +{ + switch (act) { +#if !defined(__NetBSD__) + case DVACT_ACTIVATE: + break; +#endif /* !defined(__NetBSD__) */ + + case DVACT_DEACTIVATE: + /* FIXME */ + break; + } + return (0); +} + +#if !defined(__NetBSD__) +struct cfattach drm_ca = { + sizeof(struct drm_device), drm_probe, drm_attach, + drm_detach, drm_activate +}; + +struct cfdriver drm_cd = { + 0, "drm", DV_DULL +}; +#else /* !defined(__NetBSD__) */ +/* + * We do not use CFATTACH_DECL_NEW, in order to be compatible with + * the rest of the code, which does for instance + * struct drm_device *dev = (struct drm_device *)self; + * instead of + * struct drm_device *dev = device_private(self); + */ +CFATTACH_DECL(drmdev, sizeof(struct drm_device), + drm_probe, drm_attach, drm_detach, drm_activate); +#endif /* !defined(__NetBSD__) */ + +const struct drm_pcidev * +drm_find_description(int vendor, int device, const struct drm_pcidev *idlist) +{ + int i = 0; + + for (i = 0; idlist[i].vendor != 0; i++) { + if ((idlist[i].vendor == vendor) && + (idlist[i].device == device)) + return &idlist[i]; + } + return NULL; +} + +int +drm_file_cmp(struct drm_file *f1, struct drm_file *f2) +{ + return (f1->minor < f2->minor ? -1 : f1->minor > f2->minor); +} + +SPLAY_GENERATE(drm_file_tree, drm_file, link, drm_file_cmp); + +struct drm_file * +drm_find_file_by_minor(struct drm_device *dev, int minor) +{ + struct drm_file key; + + key.minor = minor; + return (SPLAY_FIND(drm_file_tree, &dev->files, &key)); +} + +int +drm_firstopen(struct drm_device *dev) +{ + struct drm_local_map *map; + int i; + + /* prebuild the SAREA */ + i = drm_addmap(dev, 0, SAREA_MAX, _DRM_SHM, + _DRM_CONTAINS_LOCK, &map); + if (i != 0) + return i; + + if (dev->driver->firstopen) + dev->driver->firstopen(dev); + + if (dev->driver->flags & DRIVER_DMA) { + if ((i = drm_dma_setup(dev)) != 0) + return (i); + } + + dev->magicid = 1; + + dev->irq_enabled = 0; + dev->if_version = 0; + + dev->buf_pgid = 0; + + DRM_DEBUG("\n"); + + return 0; +} + +int +drm_lastclose(struct drm_device *dev) +{ + struct drm_local_map *map, *mapsave; + + DRM_DEBUG("\n"); + + if (dev->driver->lastclose != NULL) + dev->driver->lastclose(dev); + + if (dev->irq_enabled) + drm_irq_uninstall(dev); + + drm_agp_takedown(dev); + drm_dma_takedown(dev); + + DRM_LOCK(); + if (dev->sg != NULL) { + struct drm_sg_mem *sg = dev->sg; + dev->sg = NULL; + + DRM_UNLOCK(); + drm_sg_cleanup(dev, sg); + DRM_LOCK(); + } + + for (map = TAILQ_FIRST(&dev->maplist); map != TAILQ_END(&dev->maplist); + map = mapsave) { + mapsave = TAILQ_NEXT(map, link); + if ((map->flags & _DRM_DRIVER) == 0) + drm_rmmap_locked(dev, map); + } + + if (dev->lock.hw_lock != NULL) { + dev->lock.hw_lock = NULL; /* SHM removed */ + dev->lock.file_priv = NULL; + mtx_enter(&dev->lock.spinlock); +#if !defined(__NetBSD__) + wakeup(&dev->lock); /* there should be nothing sleeping on it */ +#else /* !defined(__NetBSD__) */ + cv_broadcast(&dev->lock.condvar); +#endif /* !defined(__NetBSD__) */ + mtx_leave(&dev->lock.spinlock); + } + DRM_UNLOCK(); + + return 0; +} + +int +#if !defined(__NetBSD__) +drmopen(dev_t kdev, int flags, int fmt, struct proc *p) +#else /* !defined(__NetBSD__) */ +drmopen(dev_t kdev, int flags, int fmt, struct lwp *p) +#endif /* !defined(__NetBSD__) */ +{ + struct drm_device *dev = NULL; + struct drm_file *file_priv; + int ret = 0; + + dev = drm_get_device_from_kdev(kdev); + if (dev == NULL) + return (ENXIO); + + DRM_DEBUG("open_count = %d\n", dev->open_count); + + if (flags & O_EXCL) + return (EBUSY); /* No exclusive opens */ + + DRM_LOCK(); + if (dev->open_count++ == 0) { + DRM_UNLOCK(); + if ((ret = drm_firstopen(dev)) != 0) + goto err; + } else { + DRM_UNLOCK(); + } + + /* always allocate at least enough space for our data */ + file_priv = drm_calloc(1, max(dev->driver->file_priv_size, + sizeof(*file_priv))); + if (file_priv == NULL) { + ret = ENOMEM; + goto err; + } + + file_priv->kdev = kdev; + file_priv->flags = flags; + file_priv->minor = minor(kdev); + TAILQ_INIT(&file_priv->evlist); +#if defined(__NetBSD__) + cv_init(&file_priv->evlist_condvar, "drmread"); +#endif /* defined(__NetBSD__) */ + file_priv->event_space = 4096; /* 4k for event buffer */ + DRM_DEBUG("minor = %d\n", file_priv->minor); + + /* for compatibility root is always authenticated */ + file_priv->authenticated = DRM_SUSER(p); + + if (dev->driver->flags & DRIVER_GEM) { + SPLAY_INIT(&file_priv->obj_tree); + mtx_init(&file_priv->table_lock, IPL_NONE); + } + + if (dev->driver->open) { + ret = dev->driver->open(dev, file_priv); + if (ret != 0) { + goto free_priv; + } + } + + DRM_LOCK(); + /* first opener automatically becomes master if root */ + if (SPLAY_EMPTY(&dev->files) && !DRM_SUSER(p)) { + DRM_UNLOCK(); + ret = EPERM; + goto free_priv; + } + + file_priv->master = SPLAY_EMPTY(&dev->files); + + SPLAY_INSERT(drm_file_tree, &dev->files, file_priv); + DRM_UNLOCK(); + + return (0); + +free_priv: +#if defined(__NetBSD__) + mutex_destroy(&file_priv->table_lock); + cv_destroy(&file_priv->evlist_condvar); +#endif /* defined(__NetBSD__) */ + drm_free(file_priv); +err: + DRM_LOCK(); + --dev->open_count; + DRM_UNLOCK(); + return (ret); +} + +int +#if !defined(__NetBSD__) +drmclose(dev_t kdev, int flags, int fmt, struct proc *p) +#else /* !defined(__NetBSD__) */ +drmclose(dev_t kdev, int flags, int fmt, struct lwp *p) +#endif /* !defined(__NetBSD__) */ +{ + struct drm_device *dev = drm_get_device_from_kdev(kdev); + struct drm_file *file_priv; + struct drm_pending_event *ev, *evtmp; + int i, retcode = 0; + + if (dev == NULL) + return (ENXIO); + + DRM_DEBUG("open_count = %d\n", dev->open_count); + + DRM_LOCK(); + file_priv = drm_find_file_by_minor(dev, minor(kdev)); + if (file_priv == NULL) { + DRM_ERROR("can't find authenticator\n"); + retcode = EINVAL; + goto done; + } + DRM_UNLOCK(); + + if (dev->driver->close != NULL) + dev->driver->close(dev, file_priv); + + DRM_DEBUG("pid = %d, device = %p, open_count = %d\n", + DRM_CURRENTPID, &dev->device, dev->open_count); + + if (dev->lock.hw_lock && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) + && dev->lock.file_priv == file_priv) { + DRM_DEBUG("Process %d dead, freeing lock for context %d\n", + DRM_CURRENTPID, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + + drm_lock_free(&dev->lock, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + } + if (dev->driver->flags & DRIVER_DMA) + drm_reclaim_buffers(dev, file_priv); + + mtx_enter(&dev->event_lock); + for (i = 0; i < dev->vblank->vb_num; i++) { + struct drmevlist *list = &dev->vblank->vb_crtcs[i].vbl_events; + for (ev = TAILQ_FIRST(list); ev != TAILQ_END(list); + ev = evtmp) { + evtmp = TAILQ_NEXT(ev, link); + if (ev->file_priv == file_priv) { + TAILQ_REMOVE(list, ev, link); + drm_vblank_put(dev, i); + ev->destroy(ev); + } + } + } + while ((ev = TAILQ_FIRST(&file_priv->evlist)) != NULL) { + TAILQ_REMOVE(&file_priv->evlist, ev, link); + ev->destroy(ev); + } + mtx_leave(&dev->event_lock); + + DRM_LOCK(); + if (dev->driver->flags & DRIVER_GEM) { + struct drm_handle *han; + mtx_enter(&file_priv->table_lock); + while ((han = SPLAY_ROOT(&file_priv->obj_tree)) != NULL) { + SPLAY_REMOVE(drm_obj_tree, &file_priv->obj_tree, han); + drm_handle_unref(han->obj); + drm_free(han); + } + mtx_leave(&file_priv->table_lock); + } + + dev->buf_pgid = 0; + + SPLAY_REMOVE(drm_file_tree, &dev->files, file_priv); +#if defined(__NetBSD__) + mutex_destroy(&file_priv->table_lock); + cv_destroy(&file_priv->evlist_condvar); +#endif /* defined(__NetBSD__) */ + drm_free(file_priv); + +done: + if (--dev->open_count == 0) { + DRM_UNLOCK(); + retcode = drm_lastclose(dev); + } else + DRM_UNLOCK(); + + return (retcode); +} + +/* drmioctl is called whenever a process performs an ioctl on /dev/drm. + */ +int +drmioctl(dev_t kdev, u_long cmd, caddr_t data, int flags, +#if !defined(__NetBSD__) + struct proc *p) +#else /* !defined(__NetBSD__) */ + struct lwp *p) +#endif /* !defined(__NetBSD__) */ +{ + struct drm_device *dev = drm_get_device_from_kdev(kdev); + struct drm_file *file_priv; + + if (dev == NULL) + return ENODEV; + + DRM_LOCK(); + file_priv = drm_find_file_by_minor(dev, minor(kdev)); + DRM_UNLOCK(); + if (file_priv == NULL) { + DRM_ERROR("can't find authenticator\n"); + return EINVAL; + } + + ++file_priv->ioctl_count; + + DRM_DEBUG("pid=%d, cmd=0x%02lx, nr=0x%02lx, dev %p, auth=%d\n", + DRM_CURRENTPID, cmd, DRM_IOCTL_NR(cmd), &dev->device, + file_priv->authenticated); + + switch (cmd) { + case FIONBIO: + case FIOASYNC: + return 0; + + case TIOCSPGRP: + dev->buf_pgid = *(int *)data; + return 0; + + case TIOCGPGRP: + *(int *)data = dev->buf_pgid; + return 0; + case DRM_IOCTL_VERSION: + return (drm_version(dev, data, file_priv)); + case DRM_IOCTL_GET_UNIQUE: + return (drm_getunique(dev, data, file_priv)); + case DRM_IOCTL_GET_MAGIC: + return (drm_getmagic(dev, data, file_priv)); + case DRM_IOCTL_WAIT_VBLANK: + return (drm_wait_vblank(dev, data, file_priv)); + case DRM_IOCTL_MODESET_CTL: + return (drm_modeset_ctl(dev, data, file_priv)); + case DRM_IOCTL_GEM_CLOSE: + return (drm_gem_close_ioctl(dev, data, file_priv)); + + /* removed */ + case DRM_IOCTL_GET_MAP: + /* FALLTHROUGH */ + case DRM_IOCTL_GET_CLIENT: + /* FALLTHROUGH */ + case DRM_IOCTL_GET_STATS: + return (EINVAL); + /* + * no-oped ioctls, we don't check permissions on them because + * they do nothing. they'll be removed as soon as userland is + * definitely purged + */ + case DRM_IOCTL_SET_SAREA_CTX: + case DRM_IOCTL_BLOCK: + case DRM_IOCTL_UNBLOCK: + case DRM_IOCTL_MOD_CTX: + case DRM_IOCTL_MARK_BUFS: + case DRM_IOCTL_FINISH: + case DRM_IOCTL_INFO_BUFS: + case DRM_IOCTL_SWITCH_CTX: + case DRM_IOCTL_NEW_CTX: + case DRM_IOCTL_GET_SAREA_CTX: + return (0); + } + + if (file_priv->authenticated == 1) { + switch (cmd) { + case DRM_IOCTL_RM_MAP: + return (drm_rmmap_ioctl(dev, data, file_priv)); + case DRM_IOCTL_GET_CTX: + return (drm_getctx(dev, data, file_priv)); + case DRM_IOCTL_RES_CTX: + return (drm_resctx(dev, data, file_priv)); + case DRM_IOCTL_LOCK: + return (drm_lock(dev, data, file_priv)); + case DRM_IOCTL_UNLOCK: + return (drm_unlock(dev, data, file_priv)); + case DRM_IOCTL_MAP_BUFS: + return (drm_mapbufs(dev, data, file_priv)); + case DRM_IOCTL_FREE_BUFS: + return (drm_freebufs(dev, data, file_priv)); + case DRM_IOCTL_DMA: + return (drm_dma(dev, data, file_priv)); + case DRM_IOCTL_AGP_INFO: + return (drm_agp_info_ioctl(dev, data, file_priv)); + case DRM_IOCTL_GEM_FLINK: + return (drm_gem_flink_ioctl(dev, data, file_priv)); + case DRM_IOCTL_GEM_OPEN: + return (drm_gem_open_ioctl(dev, data, file_priv)); + + } + } + + /* master is always root */ + if (file_priv->master == 1) { + switch(cmd) { + case DRM_IOCTL_SET_VERSION: + return (drm_setversion(dev, data, file_priv)); + case DRM_IOCTL_IRQ_BUSID: + return (drm_irq_by_busid(dev, data, file_priv)); + case DRM_IOCTL_AUTH_MAGIC: + return (drm_authmagic(dev, data, file_priv)); + case DRM_IOCTL_ADD_MAP: + return (drm_addmap_ioctl(dev, data, file_priv)); + case DRM_IOCTL_ADD_CTX: + return (drm_addctx(dev, data, file_priv)); + case DRM_IOCTL_RM_CTX: + return (drm_rmctx(dev, data, file_priv)); + case DRM_IOCTL_ADD_BUFS: + return (drm_addbufs(dev, (struct drm_buf_desc *)data)); + case DRM_IOCTL_CONTROL: + return (drm_control(dev, data, file_priv)); + case DRM_IOCTL_AGP_ACQUIRE: + return (drm_agp_acquire_ioctl(dev, data, file_priv)); + case DRM_IOCTL_AGP_RELEASE: + return (drm_agp_release_ioctl(dev, data, file_priv)); + case DRM_IOCTL_AGP_ENABLE: + return (drm_agp_enable_ioctl(dev, data, file_priv)); + case DRM_IOCTL_AGP_ALLOC: + return (drm_agp_alloc_ioctl(dev, data, file_priv)); + case DRM_IOCTL_AGP_FREE: + return (drm_agp_free_ioctl(dev, data, file_priv)); + case DRM_IOCTL_AGP_BIND: + return (drm_agp_bind_ioctl(dev, data, file_priv)); + case DRM_IOCTL_AGP_UNBIND: + return (drm_agp_unbind_ioctl(dev, data, file_priv)); + case DRM_IOCTL_SG_ALLOC: + return (drm_sg_alloc_ioctl(dev, data, file_priv)); + case DRM_IOCTL_SG_FREE: + return (drm_sg_free(dev, data, file_priv)); + case DRM_IOCTL_ADD_DRAW: + case DRM_IOCTL_RM_DRAW: + case DRM_IOCTL_UPDATE_DRAW: + /* + * Support removed from kernel since it's not used. + * just return zero until userland stops calling this + * ioctl. + */ + return (0); + case DRM_IOCTL_SET_UNIQUE: + /* + * Deprecated in DRM version 1.1, and will return EBUSY + * when setversion has + * requested version 1.1 or greater. + */ + return (EBUSY); + } + } + if (dev->driver->ioctl != NULL) + return (dev->driver->ioctl(dev, cmd, data, file_priv)); + else + return (EINVAL); +} + +int +drmread(dev_t kdev, struct uio *uio, int ioflag) +{ + struct drm_device *dev = drm_get_device_from_kdev(kdev); + struct drm_file *file_priv; + struct drm_pending_event *ev; + int error = 0; + + if (dev == NULL) + return (ENXIO); + + DRM_LOCK(); + file_priv = drm_find_file_by_minor(dev, minor(kdev)); + DRM_UNLOCK(); + if (file_priv == NULL) + return (ENXIO); + + /* + * The semantics are a little weird here. We will wait until we + * have events to process, but as soon as we have events we will + * only deliver as many as we have. + * Note that events are atomic, if the read buffer will not fit in + * a whole event, we won't read any of it out. + */ + mtx_enter(&dev->event_lock); + while (error == 0 && TAILQ_EMPTY(&file_priv->evlist)) { + if (ioflag & IO_NDELAY) { + mtx_leave(&dev->event_lock); + return (EAGAIN); + } +#if !defined(__NetBSD__) + error = msleep(&file_priv->evlist, &dev->event_lock, + PWAIT | PCATCH, "drmread", 0); +#else /* !defined(__NetBSD__) */ + error = cv_wait_sig(&file_priv->evlist_condvar, &dev->event_lock); +#endif /* !defined(__NetBSD__) */ + } + if (error) { + mtx_leave(&dev->event_lock); + return (error); + } + while (drm_dequeue_event(dev, file_priv, uio->uio_resid, &ev)) { + MUTEX_ASSERT_UNLOCKED(&dev->event_lock); + /* XXX we always destroy the event on error. */ + error = uiomove(ev->event, ev->event->length, uio); + ev->destroy(ev); + if (error) + break; + mtx_enter(&dev->event_lock); + } + MUTEX_ASSERT_UNLOCKED(&dev->event_lock); + + return (error); +} + +/* + * Deqeue an event from the file priv in question. returning 1 if an + * event was found. We take the resid from the read as a parameter because + * we will only dequeue and event if the read buffer has space to fit the + * entire thing. + * + * We are called locked, but we will *unlock* the queue on return so that + * we may sleep to copyout the event. + */ +int +drm_dequeue_event(struct drm_device *dev, struct drm_file *file_priv, + size_t resid, struct drm_pending_event **out) +{ + struct drm_pending_event *ev; + int gotone = 0; + + MUTEX_ASSERT_LOCKED(&dev->event_lock); + if ((ev = TAILQ_FIRST(&file_priv->evlist)) == NULL || + ev->event->length > resid) + goto out; + + TAILQ_REMOVE(&file_priv->evlist, ev, link); + file_priv->event_space += ev->event->length; + *out = ev; + gotone = 1; + +out: + mtx_leave(&dev->event_lock); + + return (gotone); +} + +/* XXX kqfilter ... */ +int +#if !defined(__NetBSD__) +drmpoll(dev_t kdev, int events, struct proc *p) +#else /* !defined(__NetBSD__) */ +drmpoll(dev_t kdev, int events, struct lwp *p) +#endif /* !defined(__NetBSD__) */ +{ + struct drm_device *dev = drm_get_device_from_kdev(kdev); + struct drm_file *file_priv; + int revents = 0; + + if (dev == NULL) + return (POLLERR); + + DRM_LOCK(); + file_priv = drm_find_file_by_minor(dev, minor(kdev)); + DRM_UNLOCK(); + if (file_priv == NULL) + return (POLLERR); + + mtx_enter(&dev->event_lock); + if (events & (POLLIN | POLLRDNORM)) { + if (!TAILQ_EMPTY(&file_priv->evlist)) + revents |= events & (POLLIN | POLLRDNORM); + else + selrecord(p, &file_priv->rsel); + } + mtx_leave(&dev->event_lock); + + return (revents); +} + +struct drm_local_map * +drm_getsarea(struct drm_device *dev) +{ + struct drm_local_map *map; + + DRM_LOCK(); + TAILQ_FOREACH(map, &dev->maplist, link) { + if (map->type == _DRM_SHM && (map->flags & _DRM_CONTAINS_LOCK)) + break; + } + DRM_UNLOCK(); + return (map); +} + +paddr_t +drmmmap(dev_t kdev, off_t offset, int prot) +{ + struct drm_device *dev = drm_get_device_from_kdev(kdev); + struct drm_local_map *map; + struct drm_file *file_priv; + enum drm_map_type type; + + if (dev == NULL) + return (-1); + + DRM_LOCK(); + file_priv = drm_find_file_by_minor(dev, minor(kdev)); + DRM_UNLOCK(); + if (file_priv == NULL) { + DRM_ERROR("can't find authenticator\n"); + return (-1); + } + + if (!file_priv->authenticated) + return (-1); + + if (dev->dma && offset >= 0 && offset < ptoa(dev->dma->page_count)) { + struct drm_device_dma *dma = dev->dma; + paddr_t phys = -1; + + rw_enter_write(&dma->dma_lock); + if (dma->pagelist != NULL) + phys = dma->pagelist[offset >> PAGE_SHIFT]; + rw_exit_write(&dma->dma_lock); + + return (phys); + } + + /* + * A sequential search of a linked list is + * fine here because: 1) there will only be + * about 5-10 entries in the list and, 2) a + * DRI client only has to do this mapping + * once, so it doesn't have to be optimized + * for performance, even if the list was a + * bit longer. + */ + DRM_LOCK(); + TAILQ_FOREACH(map, &dev->maplist, link) { + if (offset >= map->ext && + offset < map->ext + map->size) { + offset -= map->ext; + break; + } + } + + if (map == NULL) { + DRM_UNLOCK(); + DRM_DEBUG("can't find map\n"); + return (-1); + } + if (((map->flags & _DRM_RESTRICTED) && file_priv->master == 0)) { + DRM_UNLOCK(); + DRM_DEBUG("restricted map\n"); + return (-1); + } + type = map->type; + DRM_UNLOCK(); + + switch (type) { + case _DRM_FRAME_BUFFER: + case _DRM_REGISTERS: + case _DRM_AGP: + return (offset + map->offset); + break; + /* XXX unify all the bus_dmamem_mmap bits */ + case _DRM_SCATTER_GATHER: + return (bus_dmamem_mmap(dev->dmat, dev->sg->mem->segs, + dev->sg->mem->nsegs, map->offset - dev->sg->handle + + offset, prot, BUS_DMA_NOWAIT)); + case _DRM_SHM: + case _DRM_CONSISTENT: + return (bus_dmamem_mmap(dev->dmat, map->dmamem->segs, + map->dmamem->nsegs, offset, prot, BUS_DMA_NOWAIT)); + default: + DRM_ERROR("bad map type %d\n", type); + return (-1); /* This should never happen. */ + } + /* NOTREACHED */ +} + +/* + * Beginning in revision 1.1 of the DRM interface, getunique will return + * a unique in the form pci:oooo:bb:dd.f (o=domain, b=bus, d=device, f=function) + * before setunique has been called. The format for the bus-specific part of + * the unique is not defined for any other bus. + */ +int +drm_getunique(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_unique *u = data; + + if (u->unique_len >= dev->unique_len) { + if (DRM_COPY_TO_USER(u->unique, dev->unique, dev->unique_len)) + return EFAULT; + } + u->unique_len = dev->unique_len; + + return 0; +} + +#define DRM_IF_MAJOR 1 +#define DRM_IF_MINOR 2 + +int +drm_version(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_version *version_ = data; + int len; + +#define DRM_COPY(name, value) \ + len = strlen( value ); \ + if ( len > name##_len ) len = name##_len; \ + name##_len = strlen( value ); \ + if ( len && name ) { \ + if ( DRM_COPY_TO_USER( name, value, len ) ) \ + return EFAULT; \ + } + + version_->version_major = dev->driver->major; + version_->version_minor = dev->driver->minor; + version_->version_patchlevel = dev->driver->patchlevel; + + DRM_COPY(version_->name, dev->driver->name); + DRM_COPY(version_->date, dev->driver->date); + DRM_COPY(version_->desc, dev->driver->desc); + + return 0; +} + +int +drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_set_version ver, *sv = data; + int if_version; + + /* Save the incoming data, and set the response before continuing + * any further. + */ + ver = *sv; + sv->drm_di_major = DRM_IF_MAJOR; + sv->drm_di_minor = DRM_IF_MINOR; + sv->drm_dd_major = dev->driver->major; + sv->drm_dd_minor = dev->driver->minor; + + /* + * We no longer support interface versions less than 1.1, so error + * out if the xserver is too old. 1.1 always ties the drm to a + * certain busid, this was done on attach + */ + if (ver.drm_di_major != -1) { + if (ver.drm_di_major != DRM_IF_MAJOR || ver.drm_di_minor < 1 || + ver.drm_di_minor > DRM_IF_MINOR) { + return EINVAL; + } + if_version = DRM_IF_VERSION(ver.drm_di_major, ver.drm_dd_minor); + dev->if_version = imax(if_version, dev->if_version); + } + + if (ver.drm_dd_major != -1) { + if (ver.drm_dd_major != dev->driver->major || + ver.drm_dd_minor < 0 || + ver.drm_dd_minor > dev->driver->minor) + return EINVAL; + } + + return 0; +} + +struct drm_dmamem * +drm_dmamem_alloc(bus_dma_tag_t dmat, bus_size_t size, bus_size_t alignment, + int nsegments, bus_size_t maxsegsz, int mapflags, int loadflags) +{ + struct drm_dmamem *mem; + size_t strsize; + /* + * segs is the last member of the struct since we modify the size + * to allow extra segments if more than one are allowed. + */ + strsize = sizeof(*mem) + (sizeof(bus_dma_segment_t) * (nsegments - 1)); + mem = malloc(strsize, M_DRM, M_NOWAIT | M_ZERO); + if (mem == NULL) + return (NULL); + + mem->size = size; + + if (bus_dmamap_create(dmat, size, nsegments, maxsegsz, 0, + BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &mem->map) != 0) + goto strfree; + + if (bus_dmamem_alloc(dmat, size, alignment, 0, mem->segs, nsegments, +#if !defined(__NetBSD__) + &mem->nsegs, BUS_DMA_NOWAIT | BUS_DMA_ZERO) != 0) +#else /* !defined(__NetBSD__) */ + &mem->nsegs, BUS_DMA_NOWAIT) != 0) +#endif /* !defined(__NetBSD__) */ + goto destroy; + + if (bus_dmamem_map(dmat, mem->segs, mem->nsegs, size, + &mem->kva, BUS_DMA_NOWAIT | mapflags) != 0) + goto free; + +#if defined(__NetBSD__) + memset(mem->kva, 0, size); +#endif /* defined(__NetBSD__) */ + + if (bus_dmamap_load(dmat, mem->map, mem->kva, size, + NULL, BUS_DMA_NOWAIT | loadflags) != 0) + goto unmap; + + return (mem); + +unmap: + bus_dmamem_unmap(dmat, mem->kva, size); +free: + bus_dmamem_free(dmat, mem->segs, mem->nsegs); +destroy: + bus_dmamap_destroy(dmat, mem->map); +strfree: + free(mem, M_DRM); + + return (NULL); +} + +void +drm_dmamem_free(bus_dma_tag_t dmat, struct drm_dmamem *mem) +{ + if (mem == NULL) + return; + + bus_dmamap_unload(dmat, mem->map); + bus_dmamem_unmap(dmat, mem->kva, mem->size); + bus_dmamem_free(dmat, mem->segs, mem->nsegs); + bus_dmamap_destroy(dmat, mem->map); + free(mem, M_DRM); +} + +/** + * Called by the client, this returns a unique magic number to be authorized + * by the master. + * + * The master may use its own knowledge of the client (such as the X + * connection that the magic is passed over) to determine if the magic number + * should be authenticated. + */ +int +drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_auth *auth = data; + + if (dev->magicid == 0) + dev->magicid = 1; + + /* Find unique magic */ + if (file_priv->magic) { + auth->magic = file_priv->magic; + } else { + DRM_LOCK(); + file_priv->magic = auth->magic = dev->magicid++; + DRM_UNLOCK(); + DRM_DEBUG("%d\n", auth->magic); + } + + DRM_DEBUG("%u\n", auth->magic); + return (0); +} + +/** + * Marks the client associated with the given magic number as authenticated. + */ +int +drm_authmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_file *p; + struct drm_auth *auth = data; + int ret = EINVAL; + + DRM_DEBUG("%u\n", auth->magic); + + if (auth->magic == 0) + return (ret); + + DRM_LOCK(); + SPLAY_FOREACH(p, drm_file_tree, &dev->files) { + if (p->magic == auth->magic) { + p->authenticated = 1; + p->magic = 0; + ret = 0; + break; + } + } + DRM_UNLOCK(); + + return (ret); +} + +struct uvm_pagerops drm_pgops = { + NULL, + drm_ref, + drm_unref, + drm_fault, +#if defined(__NetBSD__) + NULL, +#endif /* defined(__NetBSD__) */ + drm_flush, +}; + + +void +drm_hold_object_locked(struct drm_obj *obj) +{ + while (obj->do_flags & DRM_BUSY) { + atomic_setbits_int(&obj->do_flags, DRM_WANTED); +#if !defined(__NetBSD__) + simple_unlock(&uobj->vmobjlock); +#else /* !defined(__NetBSD__) */ + mutex_exit(&obj->uobj.vmobjlock); +#endif /* !defined(__NetBSD__) */ +#ifdef DRMLOCKDEBUG + { + int ret = 0; + ret = tsleep(obj, PVM, "drm_hold", 3 * hz); /* XXX msleep */ + if (ret) + printf("still waiting for obj %p, owned by %p\n", + obj, obj->holding_proc); + } +#else + tsleep(obj, PVM, "drm_hold", 0); /* XXX msleep */ +#endif +#if !defined(__NetBSD__) + simple_lock(&uobj->vmobjlock); +#else /* !defined(__NetBSD__) */ + mutex_enter(&obj->uobj.vmobjlock); +#endif /* !defined(__NetBSD__) */ + } +#ifdef DRMLOCKDEBUG + obj->holding_proc = curproc; +#endif + atomic_setbits_int(&obj->do_flags, DRM_BUSY); +} + +void +drm_hold_object(struct drm_obj *obj) +{ +#if !defined(__NetBSD__) + simple_lock(&obj->uobj->vmobjlock); +#else /* !defined(__NetBSD__) */ + mutex_enter(&obj->uobj.vmobjlock); +#endif /* !defined(__NetBSD__) */ + drm_hold_object_locked(obj); +#if !defined(__NetBSD__) + simple_unlock(&obj->uobj->vmobjlock); +#else /* !defined(__NetBSD__) */ + mutex_exit(&obj->uobj.vmobjlock); +#endif /* !defined(__NetBSD__) */ +} + +int +drm_try_hold_object(struct drm_obj *obj) +{ +#if !defined(__NetBSD__) + simple_lock(&obj->uobj->vmobjlock); +#else /* !defined(__NetBSD__) */ + mutex_enter(&obj->uobj.vmobjlock); +#endif /* !defined(__NetBSD__) */ + /* if the object is free, grab it */ + if (obj->do_flags & (DRM_BUSY | DRM_WANTED)) { +#if !defined(__NetBSD__) + simple_unlock(&obj->uobj->vmobjlock); +#else /* !defined(__NetBSD__) */ + mutex_exit(&obj->uobj.vmobjlock); +#endif /* !defined(__NetBSD__) */ + return (0); + } + atomic_setbits_int(&obj->do_flags, DRM_BUSY); +#ifdef DRMLOCKDEBUG + obj->holding_proc = curproc; +#endif +#if !defined(__NetBSD__) + simple_unlock(&obj->uobj->vmobjlock); +#else /* !defined(__NetBSD__) */ + mutex_exit(&obj->uobj.vmobjlock); +#endif /* !defined(__NetBSD__) */ + return (1); +} + + +void +drm_unhold_object_locked(struct drm_obj *obj) +{ + if (obj->do_flags & DRM_WANTED) + wakeup(obj); +#ifdef DRMLOCKDEBUG + obj->holding_proc = NULL; +#endif + atomic_clearbits_int(&obj->do_flags, DRM_WANTED | DRM_BUSY); +} + +void +drm_unhold_object(struct drm_obj *obj) +{ +#if !defined(__NetBSD__) + simple_lock(&obj->uobj->vmobjlock); +#else /* !defined(__NetBSD__) */ + mutex_enter(&obj->uobj.vmobjlock); +#endif /* !defined(__NetBSD__) */ + drm_unhold_object_locked(obj); +#if !defined(__NetBSD__) + simple_unlock(&obj->uobj->vmobjlock); +#else /* !defined(__NetBSD__) */ + mutex_exit(&obj->uobj.vmobjlock); +#endif /* !defined(__NetBSD__) */ +} + +void +drm_ref_locked(struct uvm_object *uobj) +{ + uobj->uo_refs++; +} + +void +drm_ref(struct uvm_object *uobj) +{ +#if !defined(__NetBSD__) + simple_lock(&uobj->vmobjlock); +#else /* !defined(__NetBSD__) */ + mutex_enter(&uobj->vmobjlock); +#endif /* !defined(__NetBSD__) */ + drm_ref_locked(uobj); +#if !defined(__NetBSD__) + simple_unlock(&uobj->vmobjlock); +#else /* !defined(__NetBSD__) */ + mutex_exit(&uobj->vmobjlock); +#endif /* !defined(__NetBSD__) */ +} + +void +drm_unref(struct uvm_object *uobj) +{ +#if !defined(__NetBSD__) + simple_lock(&uobj->vmobjlock); +#else /* !defined(__NetBSD__) */ + mutex_enter(&uobj->vmobjlock); +#endif /* !defined(__NetBSD__) */ + drm_unref_locked(uobj); +} + +void +drm_unref_locked(struct uvm_object *uobj) +{ + struct drm_obj *obj = (struct drm_obj *)uobj; + struct drm_device *dev = obj->dev; + +again: + if (uobj->uo_refs > 1) { + uobj->uo_refs--; +#if !defined(__NetBSD__) + simple_unlock(&uobj->vmobjlock); +#else /* !defined(__NetBSD__) */ + mutex_exit(&uobj->vmobjlock); +#endif /* !defined(__NetBSD__) */ + return; + } + + /* inlined version of drm_hold because we want to trylock then sleep */ + if (obj->do_flags & DRM_BUSY) { + atomic_setbits_int(&obj->do_flags, DRM_WANTED); +#if !defined(__NetBSD__) + simple_unlock(&uobj->vmobjlock); +#else /* !defined(__NetBSD__) */ + mutex_exit(&uobj->vmobjlock); +#endif /* !defined(__NetBSD__) */ + tsleep(obj, PVM, "drm_unref", 0); /* XXX msleep */ +#if !defined(__NetBSD__) + simple_lock(&uobj->vmobjlock); +#else /* !defined(__NetBSD__) */ + mutex_enter(&uobj->vmobjlock); +#endif /* !defined(__NetBSD__) */ + goto again; + } +#ifdef DRMLOCKDEBUG + obj->holding_proc = curproc; +#endif + atomic_setbits_int(&obj->do_flags, DRM_BUSY); +#if !defined(__NetBSD__) + simple_unlock(&obj->vmobjlock); +#else /* !defined(__NetBSD__) */ + mutex_exit(&uobj->vmobjlock); +#endif /* !defined(__NetBSD__) */ + /* We own this thing now. it is on no queues, though it may still + * be bound to the aperture (and on the inactive list, in which case + * idling the buffer is what triggered the free. Since we know no one + * else can grab it now, we can nuke with impunity. + */ + if (dev->driver->gem_free_object != NULL) + dev->driver->gem_free_object(obj); + + uao_detach(obj->uao); + + atomic_dec(&dev->obj_count); + atomic_sub(obj->size, &dev->obj_memory); + if (obj->do_flags & DRM_WANTED) /* should never happen, not on lists */ + wakeup(obj); +#if defined(__NetBSD__) + UVM_OBJ_DESTROY(&obj->uobj); +#endif /* defined(__NetBSD__) */ + pool_put(&dev->objpl, obj); +} + +/* + * convenience function to unreference and unhold an object. + */ +void +drm_unhold_and_unref(struct drm_obj *obj) +{ + drm_lock_obj(obj); + drm_unhold_object_locked(obj); + drm_unref_locked(&obj->uobj); +} + + +boolean_t +drm_flush(struct uvm_object *uobj, voff_t start, voff_t stop, int flags) +{ + return (TRUE); +} + + +int +drm_fault(struct uvm_faultinfo *ufi, vaddr_t vaddr, vm_page_t *pps, +#if !defined(__NetBSD__) + int npages, int centeridx, vm_fault_t fault_type, +#else /* !defined(__NetBSD__) */ + int npages, int centeridx, +#endif /* !defined(__NetBSD__) */ + vm_prot_t access_type, int flags) +{ + struct vm_map_entry *entry = ufi->entry; + struct uvm_object *uobj = entry->object.uvm_obj; + struct drm_obj *obj = (struct drm_obj *)uobj; + struct drm_device *dev = obj->dev; + int ret; + UVMHIST_FUNC("udv_fault"); UVMHIST_CALLED(maphist); + UVMHIST_LOG(maphist," flags=%ld", flags,0,0,0); + + /* + * we do not allow device mappings to be mapped copy-on-write + * so we kill any attempt to do so here. + */ + + if (UVM_ET_ISCOPYONWRITE(entry)) { + UVMHIST_LOG(maphist, "<- failed -- COW entry (etype=0x%lx)", + entry->etype, 0,0,0); + uvmfault_unlockall(ufi, ufi->entry->aref.ar_amap, uobj, NULL); + return(VM_PAGER_ERROR); + } + + /* Call down into driver to do the magic */ + ret = dev->driver->gem_fault(obj, ufi, entry->offset + (vaddr - + entry->start), vaddr, pps, npages, centeridx, + access_type, flags); + return (ret); +} + +/* + * Code to support memory managers based on the GEM (Graphics + * Execution Manager) api. + */ +struct drm_obj * +drm_gem_object_alloc(struct drm_device *dev, size_t size) +{ + struct drm_obj *obj; + + KASSERT((size & (PAGE_SIZE -1)) == 0); + +#if !defined(__NetBSD__) + if ((obj = pool_get(&dev->objpl, PR_WAITOK | PR_ZERO)) == NULL) + return (NULL); +#else /* !defined(__NetBSD__) */ + if ((obj = pool_get(&dev->objpl, PR_WAITOK)) == NULL) + return (NULL); + memset(obj, 0, dev->objpl.pr_size); +#endif /* !defined(__NetBSD__) */ + + obj->dev = dev; + + /* uao create can't fail in the 0 case, it just sleeps */ + obj->uao = uao_create(size, 0); + obj->size = size; +#if !defined(__NetBSD__) + uvm_objinit(&obj->uobj, &drm_pgops, 1); +#else /* !defined(__NetBSD__) */ + UVM_OBJ_INIT(&obj->uobj, &drm_pgops, 1); +#endif /* !defined(__NetBSD__) */ + + if (dev->driver->gem_init_object != NULL && + dev->driver->gem_init_object(obj) != 0) { + uao_detach(obj->uao); +#if defined(__NetBSD__) + UVM_OBJ_DESTROY(&obj->uobj); +#endif /* defined(__NetBSD__) */ + pool_put(&dev->objpl, obj); + return (NULL); + } + atomic_inc(&dev->obj_count); + atomic_add(obj->size, &dev->obj_memory); + return (obj); +} + +int +drm_handle_create(struct drm_file *file_priv, struct drm_obj *obj, + int *handlep) +{ + struct drm_handle *han; + + if ((han = drm_calloc(1, sizeof(*han))) == NULL) + return (ENOMEM); + + han->obj = obj; + mtx_enter(&file_priv->table_lock); +again: + *handlep = han->handle = ++file_priv->obj_id; + /* + * Make sure we have no duplicates. this'll hurt once we wrap, 0 is + * reserved. + */ + if (han->handle == 0 || SPLAY_INSERT(drm_obj_tree, + &file_priv->obj_tree, han)) + goto again; + mtx_leave(&file_priv->table_lock); + + drm_handle_ref(obj); + return (0); +} + +struct drm_obj * +drm_gem_object_lookup(struct drm_device *dev, struct drm_file *file_priv, + int handle) +{ + struct drm_obj *obj; + struct drm_handle *han, search; + + search.handle = handle; + + mtx_enter(&file_priv->table_lock); + han = SPLAY_FIND(drm_obj_tree, &file_priv->obj_tree, &search); + if (han == NULL) { + mtx_leave(&file_priv->table_lock); + return (NULL); + } + + obj = han->obj; + drm_ref(&obj->uobj); + mtx_leave(&file_priv->table_lock); + + return (obj); +} + +int +drm_gem_close_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_gem_close *args = data; + struct drm_handle *han, find; + struct drm_obj *obj; + + if ((dev->driver->flags & DRIVER_GEM) == 0) + return (ENODEV); + + find.handle = args->handle; + mtx_enter(&file_priv->table_lock); + han = SPLAY_FIND(drm_obj_tree, &file_priv->obj_tree, &find); + if (han == NULL) { + mtx_leave(&file_priv->table_lock); + return (EINVAL); + } + + obj = han->obj; + SPLAY_REMOVE(drm_obj_tree, &file_priv->obj_tree, han); + mtx_leave(&file_priv->table_lock); + + drm_free(han); + + DRM_LOCK(); + drm_handle_unref(obj); + DRM_UNLOCK(); + + return (0); +} + +int +drm_gem_flink_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_gem_flink *args = data; + struct drm_obj *obj; + + if (!(dev->driver->flags & DRIVER_GEM)) + return (ENODEV); + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (obj == NULL) + return (EBADF); + + mtx_enter(&dev->obj_name_lock); + if (!obj->name) { +again: + obj->name = ++dev->obj_name; + /* 0 is reserved, make sure we don't clash. */ + if (obj->name == 0 || SPLAY_INSERT(drm_name_tree, + &dev->name_tree, obj)) + goto again; + /* name holds a reference to the object */ + drm_ref(&obj->uobj); + } + mtx_leave(&dev->obj_name_lock); + + args->name = (uint64_t)obj->name; + + drm_unref(&obj->uobj); + + return (0); +} + +int +drm_gem_open_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_gem_open *args = data; + struct drm_obj *obj, search; + int ret, handle; + + if (!(dev->driver->flags & DRIVER_GEM)) + return (ENODEV); + + search.name = args->name; + mtx_enter(&dev->obj_name_lock); + obj = SPLAY_FIND(drm_name_tree, &dev->name_tree, &search); + if (obj != NULL) + drm_ref(&obj->uobj); + mtx_leave(&dev->obj_name_lock); + if (obj == NULL) + return (ENOENT); + + /* this gives our reference to the handle */ + ret = drm_handle_create(file_priv, obj, &handle); + if (ret) { + drm_unref(&obj->uobj); + return (ret); + } + + args->handle = handle; + args->size = obj->size; + + return (0); +} + +/* + * grab a reference for a per-open handle. + * The object contains a handlecount too because if all handles disappear we + * need to also remove the global name (names initially are per open unless the + * flink ioctl is called. + */ +void +drm_handle_ref(struct drm_obj *obj) +{ + /* we are given the reference from the caller, so just + * crank handlecount. + */ + obj->handlecount++; +} + +/* + * Remove the reference owned by a per-open handle. If we're the last one, + * remove the reference from flink, too. + */ +void +drm_handle_unref(struct drm_obj *obj) +{ + /* do this first in case this is the last reference */ + if (--obj->handlecount == 0) { + struct drm_device *dev = obj->dev; + + mtx_enter(&dev->obj_name_lock); + if (obj->name) { + SPLAY_REMOVE(drm_name_tree, &dev->name_tree, obj); + obj->name = 0; + mtx_leave(&dev->obj_name_lock); + /* name held a reference to object */ + drm_unref(&obj->uobj); + } else { + mtx_leave(&dev->obj_name_lock); + } + } + drm_unref(&obj->uobj); +} + +/* + * Helper function to load a uvm anonymous object into a dmamap, to be used + * for binding to a translation-table style sg mechanism (e.g. agp, or intel + * gtt). + * + * For now we ignore maxsegsz. + */ +int +drm_gem_load_uao(bus_dma_tag_t dmat, bus_dmamap_t map, struct uvm_object *uao, + bus_size_t size, int flags, bus_dma_segment_t **segp) +{ + bus_dma_segment_t *segs; + struct vm_page *pg; + struct pglist plist; + u_long npages = size >> PAGE_SHIFT, i = 0; + int ret; + + TAILQ_INIT(&plist); + + /* + * This is really quite ugly, but nothing else would need + * bus_dmamap_load_uao() yet. + */ + segs = malloc(npages * sizeof(*segs), M_DRM, M_WAITOK | M_ZERO); + if (segs == NULL) + return (ENOMEM); + + /* This may sleep, no choice in the matter */ + if (uvm_objwire(uao, 0, size, &plist) != 0) { + ret = ENOMEM; + goto free; + } + +#if !defined(__NetBSD__) + TAILQ_FOREACH(pg, &plist, pageq) { +#else /* !defined(__NetBSD__) */ + TAILQ_FOREACH(pg, &plist, pageq.queue) { +#endif /* !defined(__NetBSD__) */ + paddr_t pa = VM_PAGE_TO_PHYS(pg); + + if (i > 0 && pa == (segs[i - 1].ds_addr + + segs[i - 1].ds_len)) { + /* contiguous, yay */ + segs[i - 1].ds_len += PAGE_SIZE; + continue; + } + segs[i].ds_addr = pa; + segs[i].ds_len = PAGE_SIZE; + if (i++ > npages) + break; + } + /* this should be impossible */ + if (pg != TAILQ_END(&pageq)) { + ret = EINVAL; + goto unwire; + } + + if ((ret = bus_dmamap_load_raw(dmat, map, segs, i, size, flags)) != 0) + goto unwire; + + *segp = segs; + + return (0); + +unwire: + uvm_objunwire(uao, 0, size); +free: + free(segs, M_DRM); + return (ret); +} + +int +drm_handle_cmp(struct drm_handle *a, struct drm_handle *b) +{ + return (a->handle < b->handle ? -1 : a->handle > b->handle); +} + +int +drm_name_cmp(struct drm_obj *a, struct drm_obj *b) +{ + return (a->name < b->name ? -1 : a->name > b->name); +} + +SPLAY_GENERATE(drm_obj_tree, drm_handle, entry, drm_handle_cmp); + +SPLAY_GENERATE(drm_name_tree, drm_obj, entry, drm_name_cmp); diff -Naurp old/src/sys/dev/pci/drm/drm.h new/src/sys/dev/pci/drm/drm.h --- old/src/sys/dev/pci/drm/drm.h 1970-01-01 01:00:00.000000000 +0100 +++ new/src/sys/dev/pci/drm/drm.h 2011-02-08 11:14:00.000000000 +0100 @@ -0,0 +1,1011 @@ +/** + * \file drm.h + * Header for the Direct Rendering Manager + * + * \author Rickard E. (Rik) Faith + * + * \par Acknowledgments: + * Dec 1999, Richard Henderson , move to generic \c cmpxchg. + */ + +/* + * 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 + * VA LINUX SYSTEMS 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. + */ + +#ifndef _DRM_H_ +#define _DRM_H_ + +#ifndef __user +#define __user +#endif + +#ifdef __GNUC__ +# define DEPRECATED __attribute__ ((deprecated)) +#else +# define DEPRECATED +#endif + +#include +#define DRM_IOCTL_NR(n) ((n) & 0xff) +#define DRM_IOC_VOID IOC_VOID +#define DRM_IOC_READ IOC_OUT +#define DRM_IOC_WRITE IOC_IN +#define DRM_IOC_READWRITE IOC_INOUT +#define DRM_IOC(dir, group, nr, size) _IOC(dir, group, nr, size) + +#define DRM_MAJOR 88 +#define DRM_MAX_MINOR 15 + +#define DRM_NAME "drm" /**< Name in kernel, /dev, and /proc */ +#define DRM_MIN_ORDER 5 /**< At least 2^5 bytes = 32 bytes */ +#define DRM_MAX_ORDER 22 /**< Up to 2^22 bytes = 4MB */ +#define DRM_RAM_PERCENT 10 /**< How much system ram can we lock? */ + +#define _DRM_LOCK_HELD 0x80000000U /**< Hardware lock is held */ +#define _DRM_LOCK_CONT 0x40000000U /**< Hardware lock is contended */ +#define _DRM_LOCK_IS_HELD(lock) ((lock) & _DRM_LOCK_HELD) +#define _DRM_LOCK_IS_CONT(lock) ((lock) & _DRM_LOCK_CONT) +#define _DRM_LOCKING_CONTEXT(lock) ((lock) & ~(_DRM_LOCK_HELD|_DRM_LOCK_CONT)) + +#include +typedef unsigned long drm_handle_t; /**< To mapped regions */ +typedef unsigned int drm_context_t; /**< GLXContext handle */ +typedef unsigned int drm_drawable_t; +typedef unsigned int drm_magic_t; /**< Magic for authentication */ + +/** + * Cliprect. + * + * \warning: If you change this structure, make sure you change + * XF86DRIClipRectRec in the server as well + * + * \note KW: Actually it's illegal to change either for + * backwards-compatibility reasons. + */ +struct drm_clip_rect { + unsigned short x1; + unsigned short y1; + unsigned short x2; + unsigned short y2; +}; + +/** + * Texture region, + */ +struct drm_tex_region { + unsigned char next; + unsigned char prev; + unsigned char in_use; + unsigned char padding; + unsigned int age; +}; + +/** + * Hardware lock. + * + * The lock structure is a simple cache-line aligned integer. To avoid + * processor bus contention on a multiprocessor system, there should not be any + * other data stored in the same cache line. + */ +struct drm_hw_lock { + __volatile__ unsigned int lock; /**< lock variable */ + char padding[60]; /**< Pad to cache line */ +}; + +/** + * DRM_IOCTL_VERSION ioctl argument type. + * + * \sa drmGetVersion(). + */ +struct drm_version { + int version_major; /**< Major version */ + int version_minor; /**< Minor version */ + int version_patchlevel; /**< Patch level */ + size_t name_len; /**< Length of name buffer */ + char __user *name; /**< Name of driver */ + size_t date_len; /**< Length of date buffer */ + char __user *date; /**< User-space buffer to hold date */ + size_t desc_len; /**< Length of desc buffer */ + char __user *desc; /**< User-space buffer to hold desc */ +}; + +/** + * DRM_IOCTL_GET_UNIQUE ioctl argument type. + * + * \sa drmGetBusid() and drmSetBusId(). + */ +struct drm_unique { + size_t unique_len; /**< Length of unique */ + char __user *unique; /**< Unique name for driver instantiation */ +}; + +struct drm_list { + int count; /**< Length of user-space structures */ + struct drm_version __user *version; +}; + +struct drm_block { + int unused; +}; + +/** + * DRM_IOCTL_CONTROL ioctl argument type. + * + * \sa drmCtlInstHandler() and drmCtlUninstHandler(). + */ +struct drm_control { + enum { + DRM_ADD_COMMAND, + DRM_RM_COMMAND, + DRM_INST_HANDLER, + DRM_UNINST_HANDLER + } func; + int irq; +}; + +/** + * Type of memory to map. + */ +enum drm_map_type { + _DRM_FRAME_BUFFER = 0, /**< WC (no caching), no core dump */ + _DRM_REGISTERS = 1, /**< no caching, no core dump */ + _DRM_SHM = 2, /**< shared, cached */ + _DRM_AGP = 3, /**< AGP/GART */ + _DRM_SCATTER_GATHER = 4, /**< Scatter/gather memory for PCI DMA */ + _DRM_CONSISTENT = 5, /**< Consistent memory for PCI DMA */ +}; + +/** + * Memory mapping flags. + */ +enum drm_map_flags { + _DRM_RESTRICTED = 0x01, /**< Cannot be mapped to user-virtual */ + _DRM_READ_ONLY = 0x02, + _DRM_LOCKED = 0x04, /**< shared, cached, locked */ + _DRM_KERNEL = 0x08, /**< kernel requires access */ + _DRM_WRITE_COMBINING = 0x10, /**< use write-combining if available */ + _DRM_CONTAINS_LOCK = 0x20, /**< SHM page that contains lock */ + _DRM_REMOVABLE = 0x40, /**< Removable mapping */ + _DRM_DRIVER = 0x80 /**< Managed by driver */ +}; + +struct drm_ctx_priv_map { + unsigned int ctx_id; /**< Context requesting private mapping */ + void *handle; /**< Handle of map */ +}; + +/** + * DRM_IOCTL_GET_MAP, DRM_IOCTL_ADD_MAP and DRM_IOCTL_RM_MAP ioctls + * argument type. + * + * \sa drmAddMap(). + */ +struct drm_map { + unsigned long offset; /**< Requested physical address (0 for SAREA)*/ + unsigned long size; /**< Requested physical size (bytes) */ + enum drm_map_type type; /**< Type of memory to map */ + enum drm_map_flags flags; /**< Flags */ + void *handle; /**< User-space: "Handle" to pass to mmap() */ + /**< Kernel-space: kernel-virtual address */ + int mtrr; /**< MTRR slot used */ + /* Private data */ +}; + +/** + * DRM_IOCTL_GET_CLIENT ioctl argument type. + */ +struct drm_client { + int idx; /**< Which client desired? */ + int auth; /**< Is client authenticated? */ + unsigned long pid; /**< Process ID */ + unsigned long uid; /**< User ID */ + unsigned long magic; /**< Magic */ + unsigned long iocs; /**< Ioctl count */ +}; + +enum drm_stat_type { + _DRM_STAT_LOCK, + _DRM_STAT_OPENS, + _DRM_STAT_CLOSES, + _DRM_STAT_IOCTLS, + _DRM_STAT_LOCKS, + _DRM_STAT_UNLOCKS, + _DRM_STAT_VALUE, /**< Generic value */ + _DRM_STAT_BYTE, /**< Generic byte counter (1024bytes/K) */ + _DRM_STAT_COUNT, /**< Generic non-byte counter (1000/k) */ + + _DRM_STAT_IRQ, /**< IRQ */ + _DRM_STAT_PRIMARY, /**< Primary DMA bytes */ + _DRM_STAT_SECONDARY, /**< Secondary DMA bytes */ + _DRM_STAT_DMA, /**< DMA */ + _DRM_STAT_SPECIAL, /**< Special DMA (e.g., priority or polled) */ + _DRM_STAT_MISSED /**< Missed DMA opportunity */ + /* Add to the *END* of the list */ +}; + +/** + * DRM_IOCTL_GET_STATS ioctl argument type. + */ +struct drm_stats { + unsigned long count; + struct { + unsigned long value; + enum drm_stat_type type; + } data[15]; +}; + +/** + * Hardware locking flags. + */ +enum drm_lock_flags { + _DRM_LOCK_READY = 0x01, /**< Wait until hardware is ready for DMA */ + _DRM_LOCK_QUIESCENT = 0x02, /**< Wait until hardware quiescent */ + _DRM_LOCK_FLUSH = 0x04, /**< Flush this context's DMA queue first */ + _DRM_LOCK_FLUSH_ALL = 0x08, /**< Flush all DMA queues first */ + /* These *HALT* flags aren't supported yet + -- they will be used to support the + full-screen DGA-like mode. */ + _DRM_HALT_ALL_QUEUES = 0x10, /**< Halt all current and future queues */ + _DRM_HALT_CUR_QUEUES = 0x20 /**< Halt all current queues */ +}; + +/** + * DRM_IOCTL_LOCK, DRM_IOCTL_UNLOCK and DRM_IOCTL_FINISH ioctl argument type. + * + * \sa drmGetLock() and drmUnlock(). + */ +struct drm_lock { + int context; + enum drm_lock_flags flags; +}; + +/** + * DMA flags + * + * \warning + * These values \e must match xf86drm.h. + * + * \sa drm_dma. + */ +enum drm_dma_flags { + /* Flags for DMA buffer dispatch */ + _DRM_DMA_BLOCK = 0x01, /**< + * Block until buffer dispatched. + * + * \note The buffer may not yet have + * been processed by the hardware -- + * getting a hardware lock with the + * hardware quiescent will ensure + * that the buffer has been + * processed. + */ + _DRM_DMA_WHILE_LOCKED = 0x02, /**< Dispatch while lock held */ + _DRM_DMA_PRIORITY = 0x04, /**< High priority dispatch */ + + /* Flags for DMA buffer request */ + _DRM_DMA_WAIT = 0x10, /**< Wait for free buffers */ + _DRM_DMA_SMALLER_OK = 0x20, /**< Smaller-than-requested buffers OK */ + _DRM_DMA_LARGER_OK = 0x40 /**< Larger-than-requested buffers OK */ +}; + +/** + * DRM_IOCTL_ADD_BUFS and DRM_IOCTL_MARK_BUFS ioctl argument type. + * + * \sa drmAddBufs(). + */ +struct drm_buf_desc { + int count; /**< Number of buffers of this size */ + int size; /**< Size in bytes */ + int low_mark; /**< Low water mark */ + int high_mark; /**< High water mark */ + enum { + _DRM_PAGE_ALIGN = 0x01, /**< Align on page boundaries for DMA */ + _DRM_AGP_BUFFER = 0x02, /**< Buffer is in AGP space */ + _DRM_SG_BUFFER = 0x04, /**< Scatter/gather memory buffer */ + _DRM_FB_BUFFER = 0x08, /**< Buffer is in frame buffer */ + _DRM_PCI_BUFFER_RO = 0x10 /**< Map PCI DMA buffer read-only */ + } flags; + unsigned long agp_start; /**< + * Start address of where the AGP buffers are + * in the AGP aperture + */ +}; + +/** + * DRM_IOCTL_INFO_BUFS ioctl argument type. + */ +struct drm_buf_info { + int count; /**< Number of buffers described in list */ + struct drm_buf_desc __user *list; /**< List of buffer descriptions */ +}; + +/** + * DRM_IOCTL_FREE_BUFS ioctl argument type. + */ +struct drm_buf_free { + int count; + int __user *list; +}; + +/** + * Buffer information + * + * \sa drm_buf_map. + */ +struct drm_buf_pub { + int idx; /**< Index into the master buffer list */ + int total; /**< Buffer size */ + int used; /**< Amount of buffer in use (for DMA) */ + void __user *address; /**< Address of buffer */ +}; + +/** + * DRM_IOCTL_MAP_BUFS ioctl argument type. + */ +struct drm_buf_map { + int count; /**< Length of the buffer list */ + void __user *virtual; /**< Mmap'd area in user-virtual */ + struct drm_buf_pub __user *list; /**< Buffer information */ +}; + +/** + * DRM_IOCTL_DMA ioctl argument type. + * + * Indices here refer to the offset into the buffer list in drm_buf_get. + * + * \sa drmDMA(). + */ +struct drm_dma { + int context; /**< Context handle */ + int send_count; /**< Number of buffers to send */ + int __user *send_indices; /**< List of handles to buffers */ + int __user *send_sizes; /**< Lengths of data to send */ + enum drm_dma_flags flags; /**< Flags */ + int request_count; /**< Number of buffers requested */ + int request_size; /**< Desired size for buffers */ + int __user *request_indices; /**< Buffer information */ + int __user *request_sizes; + int granted_count; /**< Number of buffers granted */ +}; + +enum drm_ctx_flags { + _DRM_CONTEXT_PRESERVED = 0x01, + _DRM_CONTEXT_2DONLY = 0x02 +}; + +/** + * DRM_IOCTL_ADD_CTX ioctl argument type. + * + * \sa drmCreateContext() and drmDestroyContext(). + */ +struct drm_ctx { + drm_context_t handle; + enum drm_ctx_flags flags; +}; + +/** + * DRM_IOCTL_RES_CTX ioctl argument type. + */ +struct drm_ctx_res { + int count; + struct drm_ctx __user *contexts; +}; + +/** + * DRM_IOCTL_ADD_DRAW and DRM_IOCTL_RM_DRAW ioctl argument type. + */ +struct drm_draw { + drm_drawable_t handle; +}; + +/** + * DRM_IOCTL_UPDATE_DRAW ioctl argument type. + */ +typedef enum { + DRM_DRAWABLE_CLIPRECTS, +} drm_drawable_info_type_t; + +struct drm_update_draw { + drm_drawable_t handle; + unsigned int type; + unsigned int num; + unsigned long long data; +}; + +/** + * DRM_IOCTL_GET_MAGIC and DRM_IOCTL_AUTH_MAGIC ioctl argument type. + */ +struct drm_auth { + drm_magic_t magic; +}; + +/** + * DRM_IOCTL_IRQ_BUSID ioctl argument type. + * + * \sa drmGetInterruptFromBusID(). + */ +struct drm_irq_busid { + int irq; /**< IRQ number */ + int busnum; /**< bus number */ + int devnum; /**< device number */ + int funcnum; /**< function number */ +}; + +enum drm_vblank_seq_type { + _DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */ + _DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */ + _DRM_VBLANK_EVENT = 0x4000000, /**< Send event instead of blocking */ + _DRM_VBLANK_FLIP = 0x8000000, /**< Scheduled buffer swap should flip */ + _DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */ + _DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */ + _DRM_VBLANK_SIGNAL = 0x40000000 /**< Send signal instead of blocking */ +}; + +#define _DRM_VBLANK_TYPES_MASK (_DRM_VBLANK_ABSOLUTE | _DRM_VBLANK_RELATIVE) +#define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_EVENT | _DRM_VBLANK_SIGNAL | \ + _DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS) + +struct drm_wait_vblank_request { + enum drm_vblank_seq_type type; + unsigned int sequence; + unsigned long signal; +}; + +struct drm_wait_vblank_reply { + enum drm_vblank_seq_type type; + unsigned int sequence; + long tval_sec; + long tval_usec; +}; + +/** + * DRM_IOCTL_WAIT_VBLANK ioctl argument type. + * + * \sa drmWaitVBlank(). + */ +union drm_wait_vblank { + struct drm_wait_vblank_request request; + struct drm_wait_vblank_reply reply; +}; + +#define _DRM_PRE_MODESET 1 +#define _DRM_POST_MODESET 2 + +/** + * DRM_IOCTL_MODESET_CTL ioctl argument type + * + * \sa drmModesetCtl(). + */ +struct drm_modeset_ctl { + uint32_t crtc; + uint32_t cmd; +}; + +/** + * DRM_IOCTL_AGP_ENABLE ioctl argument type. + * + * \sa drmAgpEnable(). + */ +struct drm_agp_mode { + unsigned long mode; /**< AGP mode */ +}; + +/** + * DRM_IOCTL_AGP_ALLOC and DRM_IOCTL_AGP_FREE ioctls argument type. + * + * \sa drmAgpAlloc() and drmAgpFree(). + */ +struct drm_agp_buffer { + unsigned long size; /**< In bytes -- will round to page boundary */ + unsigned long handle; /**< Used for binding / unbinding */ + unsigned long type; /**< Type of memory to allocate */ + unsigned long physical; /**< Physical used by i810 */ +}; + +/** + * DRM_IOCTL_AGP_BIND and DRM_IOCTL_AGP_UNBIND ioctls argument type. + * + * \sa drmAgpBind() and drmAgpUnbind(). + */ +struct drm_agp_binding { + unsigned long handle; /**< From drm_agp_buffer */ + unsigned long offset; /**< In bytes -- will round to page boundary */ +}; + +/** + * DRM_IOCTL_AGP_INFO ioctl argument type. + * + * \sa drmAgpVersionMajor(), drmAgpVersionMinor(), drmAgpGetMode(), + * drmAgpBase(), drmAgpSize(), drmAgpMemoryUsed(), drmAgpMemoryAvail(), + * drmAgpVendorId() and drmAgpDeviceId(). + */ +struct drm_agp_info { + int agp_version_major; + int agp_version_minor; + unsigned long mode; + unsigned long aperture_base; /* physical address */ + unsigned long aperture_size; /* bytes */ + unsigned long memory_allowed; /* bytes */ + unsigned long memory_used; + + /* PCI information */ + unsigned short id_vendor; + unsigned short id_device; +}; + +/** + * DRM_IOCTL_SG_ALLOC ioctl argument type. + */ +struct drm_scatter_gather { + unsigned long size; /**< In bytes -- will round to page boundary */ + unsigned long handle; /**< Used for mapping / unmapping */ +}; + +/** + * DRM_IOCTL_SET_VERSION ioctl argument type. + */ +struct drm_set_version { + int drm_di_major; + int drm_di_minor; + int drm_dd_major; + int drm_dd_minor; +}; + +/* DRM_IOCTL_GEM_CLOSE ioctl argument type */ +struct drm_gem_close { + /* Handle of the object to be closed. */ + uint32_t handle; + uint32_t pad; +}; + +/* DRM_IOCTL_GEM_FLINK ioctl argument type */ +struct drm_gem_flink { + /* Handle for the object being named */ + uint32_t handle; + + /* Returned global name */ + uint32_t name; +}; + +/* DRM_IOCTL_GEM_OPEN ioctl argument type */ +struct drm_gem_open { + /* Name of object being opened */ + uint32_t name; + + /* Returned handle for the object */ + uint32_t handle; + + /* Returned size of the object */ + uint64_t size; +}; + +/* + * Modesetting interface defines and types. + * Be warned that OpenBSD does not support these ioctls at the moment, + * and that a different approach may be chosen in the future. + */ +#define DRM_DISPLAY_INFO_LEN 32 +#define DRM_CONNECTOR_NAME_LEN 32 +#define DRM_DISPLAY_MODE_LEN 32 +#define DRM_PROP_NAME_LEN 32 + +#define DRM_MODE_TYPE_BUILTIN (1<<0) +#define DRM_MODE_TYPE_CLOCK_C ((1<<1) | DRM_MODE_TYPE_BUILTIN) +#define DRM_MODE_TYPE_CRTC_C ((1<<2) | DRM_MODE_TYPE_BUILTIN) +#define DRM_MODE_TYPE_PREFERRED (1<<3) +#define DRM_MODE_TYPE_DEFAULT (1<<4) +#define DRM_MODE_TYPE_USERDEF (1<<5) +#define DRM_MODE_TYPE_DRIVER (1<<6) + +/* Video mode flags */ +/* bit compatible with the xorg definitions. */ +#define DRM_MODE_FLAG_PHSYNC (1<<0) +#define DRM_MODE_FLAG_NHSYNC (1<<1) +#define DRM_MODE_FLAG_PVSYNC (1<<2) +#define DRM_MODE_FLAG_NVSYNC (1<<3) +#define DRM_MODE_FLAG_INTERLACE (1<<4) +#define DRM_MODE_FLAG_DBLSCAN (1<<5) +#define DRM_MODE_FLAG_CSYNC (1<<6) +#define DRM_MODE_FLAG_PCSYNC (1<<7) +#define DRM_MODE_FLAG_NCSYNC (1<<8) +#define DRM_MODE_FLAG_HSKEW (1<<9) /* hskew provided */ +#define DRM_MODE_FLAG_BCAST (1<<10) +#define DRM_MODE_FLAG_PIXMUX (1<<11) +#define DRM_MODE_FLAG_DBLCLK (1<<12) +#define DRM_MODE_FLAG_CLKDIV2 (1<<13) + +/* DPMS flags */ +/* bit compatible with the xorg definitions. */ +#define DRM_MODE_DPMS_ON 0 +#define DRM_MODE_DPMS_STANDBY 1 +#define DRM_MODE_DPMS_SUSPEND 2 +#define DRM_MODE_DPMS_OFF 3 + +/* Scaling mode options */ +#define DRM_MODE_SCALE_NON_GPU 0 +#define DRM_MODE_SCALE_FULLSCREEN 1 +#define DRM_MODE_SCALE_NO_SCALE 2 +#define DRM_MODE_SCALE_ASPECT 3 + +/* Dithering mode options */ +#define DRM_MODE_DITHERING_OFF 0 +#define DRM_MODE_DITHERING_ON 1 + +struct drm_mode_modeinfo { + uint32_t clock; + uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew; + uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan; + + uint32_t vrefresh; /* vertical refresh * 1000 */ + + uint32_t flags; + uint32_t type; + char name[DRM_DISPLAY_MODE_LEN]; +}; + +struct drm_mode_card_res { + uint64_t fb_id_ptr; + uint64_t crtc_id_ptr; + uint64_t connector_id_ptr; + uint64_t encoder_id_ptr; + uint32_t count_fbs; + uint32_t count_crtcs; + uint32_t count_connectors; + uint32_t count_encoders; + uint32_t min_width, max_width; + uint32_t min_height, max_height; +}; + +struct drm_mode_crtc { + uint64_t set_connectors_ptr; + uint32_t count_connectors; + + uint32_t crtc_id; /**< Id */ + uint32_t fb_id; /**< Id of framebuffer */ + + uint32_t x, y; /**< Position on the frameuffer */ + + uint32_t gamma_size; + uint32_t mode_valid; + struct drm_mode_modeinfo mode; +}; + +#define DRM_MODE_ENCODER_NONE 0 +#define DRM_MODE_ENCODER_DAC 1 +#define DRM_MODE_ENCODER_TMDS 2 +#define DRM_MODE_ENCODER_LVDS 3 +#define DRM_MODE_ENCODER_TVDAC 4 + +struct drm_mode_get_encoder { + uint32_t encoder_id; + uint32_t encoder_type; + + uint32_t crtc_id; /**< Id of crtc */ + + uint32_t possible_crtcs; + uint32_t possible_clones; +}; + +/* This is for connectors with multiple signal types. */ +/* Try to match DRM_MODE_CONNECTOR_X as closely as possible. */ +#define DRM_MODE_SUBCONNECTOR_Automatic 0 +#define DRM_MODE_SUBCONNECTOR_Unknown 0 +#define DRM_MODE_SUBCONNECTOR_DVID 3 +#define DRM_MODE_SUBCONNECTOR_DVIA 4 +#define DRM_MODE_SUBCONNECTOR_Composite 5 +#define DRM_MODE_SUBCONNECTOR_SVIDEO 6 +#define DRM_MODE_SUBCONNECTOR_Component 8 + +#define DRM_MODE_CONNECTOR_Unknown 0 +#define DRM_MODE_CONNECTOR_VGA 1 +#define DRM_MODE_CONNECTOR_DVII 2 +#define DRM_MODE_CONNECTOR_DVID 3 +#define DRM_MODE_CONNECTOR_DVIA 4 +#define DRM_MODE_CONNECTOR_Composite 5 +#define DRM_MODE_CONNECTOR_SVIDEO 6 +#define DRM_MODE_CONNECTOR_LVDS 7 +#define DRM_MODE_CONNECTOR_Component 8 +#define DRM_MODE_CONNECTOR_9PinDIN 9 +#define DRM_MODE_CONNECTOR_DisplayPort 10 +#define DRM_MODE_CONNECTOR_HDMIA 11 +#define DRM_MODE_CONNECTOR_HDMIB 12 + +struct drm_mode_get_connector { + + uint64_t encoders_ptr; + uint64_t modes_ptr; + uint64_t props_ptr; + uint64_t prop_values_ptr; + + uint32_t count_modes; + uint32_t count_props; + uint32_t count_encoders; + + uint32_t encoder_id; /**< Current Encoder */ + uint32_t connector_id; /**< Id */ + uint32_t connector_type; + uint32_t connector_type_id; + + uint32_t connection; + uint32_t mm_width, mm_height; /**< HxW in millimeters */ + uint32_t subpixel; +}; + +#define DRM_MODE_PROP_PENDING (1<<0) +#define DRM_MODE_PROP_RANGE (1<<1) +#define DRM_MODE_PROP_IMMUTABLE (1<<2) +#define DRM_MODE_PROP_ENUM (1<<3) /* enumerated type with text strings */ +#define DRM_MODE_PROP_BLOB (1<<4) + +struct drm_mode_property_enum { + uint64_t value; + char name[DRM_PROP_NAME_LEN]; +}; + +struct drm_mode_get_property { + uint64_t values_ptr; /* values and blob lengths */ + uint64_t enum_blob_ptr; /* enum and blob id ptrs */ + + uint32_t prop_id; + uint32_t flags; + char name[DRM_PROP_NAME_LEN]; + + uint32_t count_values; + uint32_t count_enum_blobs; +}; + +struct drm_mode_connector_set_property { + uint64_t value; + uint32_t prop_id; + uint32_t connector_id; +}; + +struct drm_mode_get_blob { + uint32_t blob_id; + uint32_t length; + uint64_t data; +}; + +struct drm_mode_fb_cmd { + uint32_t fb_id; + uint32_t width, height; + uint32_t pitch; + uint32_t bpp; + uint32_t depth; + /* driver specific handle */ + uint32_t handle; +}; + +struct drm_mode_mode_cmd { + uint32_t connector_id; + struct drm_mode_modeinfo mode; +}; + +#define DRM_MODE_CURSOR_BO (1<<0) +#define DRM_MODE_CURSOR_MOVE (1<<1) + +/* + * depending on the value in flags different members are used. + * + * CURSOR_BO uses + * crtc + * width + * height + * handle - if 0 turns the cursor of + * + * CURSOR_MOVE uses + * crtc + * x + * y + */ +struct drm_mode_cursor { + uint32_t flags; + uint32_t crtc_id; + int32_t x; + int32_t y; + uint32_t width; + uint32_t height; + /* driver specific handle */ + uint32_t handle; +}; + +struct drm_mode_crtc_lut { + uint32_t crtc_id; + uint32_t gamma_size; + + /* pointers to arrays */ + uint64_t red; + uint64_t green; + uint64_t blue; +}; + +#define DRM_IOCTL_BASE 'd' +#define DRM_IO(nr) _IO(DRM_IOCTL_BASE,nr) +#define DRM_IOR(nr,type) _IOR(DRM_IOCTL_BASE,nr,type) +#define DRM_IOW(nr,type) _IOW(DRM_IOCTL_BASE,nr,type) +#define DRM_IOWR(nr,type) _IOWR(DRM_IOCTL_BASE,nr,type) + +#define DRM_IOCTL_VERSION DRM_IOWR(0x00, struct drm_version) +#define DRM_IOCTL_GET_UNIQUE DRM_IOWR(0x01, struct drm_unique) +#define DRM_IOCTL_GET_MAGIC DRM_IOR( 0x02, struct drm_auth) +#define DRM_IOCTL_IRQ_BUSID DRM_IOWR(0x03, struct drm_irq_busid) +#define DRM_IOCTL_GET_MAP DRM_IOWR(0x04, struct drm_map) +#define DRM_IOCTL_GET_CLIENT DRM_IOWR(0x05, struct drm_client) +#define DRM_IOCTL_GET_STATS DRM_IOR( 0x06, struct drm_stats) +#define DRM_IOCTL_SET_VERSION DRM_IOWR(0x07, struct drm_set_version) +#define DRM_IOCTL_MODESET_CTL DRM_IOW(0x08, struct drm_modeset_ctl) +#define DRM_IOCTL_GEM_CLOSE DRM_IOW (0x09, struct drm_gem_close) +#define DRM_IOCTL_GEM_FLINK DRM_IOWR(0x0a, struct drm_gem_flink) +#define DRM_IOCTL_GEM_OPEN DRM_IOWR(0x0b, struct drm_gem_open) + +#define DRM_IOCTL_SET_UNIQUE DRM_IOW( 0x10, struct drm_unique) +#define DRM_IOCTL_AUTH_MAGIC DRM_IOW( 0x11, struct drm_auth) +#define DRM_IOCTL_BLOCK DRM_IOWR(0x12, struct drm_block) +#define DRM_IOCTL_UNBLOCK DRM_IOWR(0x13, struct drm_block) +#define DRM_IOCTL_CONTROL DRM_IOW( 0x14, struct drm_control) +#define DRM_IOCTL_ADD_MAP DRM_IOWR(0x15, struct drm_map) +#define DRM_IOCTL_ADD_BUFS DRM_IOWR(0x16, struct drm_buf_desc) +#define DRM_IOCTL_MARK_BUFS DRM_IOW( 0x17, struct drm_buf_desc) +#define DRM_IOCTL_INFO_BUFS DRM_IOWR(0x18, struct drm_buf_info) +#define DRM_IOCTL_MAP_BUFS DRM_IOWR(0x19, struct drm_buf_map) +#define DRM_IOCTL_FREE_BUFS DRM_IOW( 0x1a, struct drm_buf_free) + +#define DRM_IOCTL_RM_MAP DRM_IOW( 0x1b, struct drm_map) + +#define DRM_IOCTL_SET_SAREA_CTX DRM_IOW( 0x1c, struct drm_ctx_priv_map) +#define DRM_IOCTL_GET_SAREA_CTX DRM_IOWR(0x1d, struct drm_ctx_priv_map) + +#define DRM_IOCTL_ADD_CTX DRM_IOWR(0x20, struct drm_ctx) +#define DRM_IOCTL_RM_CTX DRM_IOWR(0x21, struct drm_ctx) +#define DRM_IOCTL_MOD_CTX DRM_IOW( 0x22, struct drm_ctx) +#define DRM_IOCTL_GET_CTX DRM_IOWR(0x23, struct drm_ctx) +#define DRM_IOCTL_SWITCH_CTX DRM_IOW( 0x24, struct drm_ctx) +#define DRM_IOCTL_NEW_CTX DRM_IOW( 0x25, struct drm_ctx) +#define DRM_IOCTL_RES_CTX DRM_IOWR(0x26, struct drm_ctx_res) +#define DRM_IOCTL_ADD_DRAW DRM_IOWR(0x27, struct drm_draw) +#define DRM_IOCTL_RM_DRAW DRM_IOWR(0x28, struct drm_draw) +#define DRM_IOCTL_DMA DRM_IOWR(0x29, struct drm_dma) +#define DRM_IOCTL_LOCK DRM_IOW( 0x2a, struct drm_lock) +#define DRM_IOCTL_UNLOCK DRM_IOW( 0x2b, struct drm_lock) +#define DRM_IOCTL_FINISH DRM_IOW( 0x2c, struct drm_lock) + +#define DRM_IOCTL_AGP_ACQUIRE DRM_IO( 0x30) +#define DRM_IOCTL_AGP_RELEASE DRM_IO( 0x31) +#define DRM_IOCTL_AGP_ENABLE DRM_IOW( 0x32, struct drm_agp_mode) +#define DRM_IOCTL_AGP_INFO DRM_IOR( 0x33, struct drm_agp_info) +#define DRM_IOCTL_AGP_ALLOC DRM_IOWR(0x34, struct drm_agp_buffer) +#define DRM_IOCTL_AGP_FREE DRM_IOW( 0x35, struct drm_agp_buffer) +#define DRM_IOCTL_AGP_BIND DRM_IOW( 0x36, struct drm_agp_binding) +#define DRM_IOCTL_AGP_UNBIND DRM_IOW( 0x37, struct drm_agp_binding) + +#define DRM_IOCTL_SG_ALLOC DRM_IOWR(0x38, struct drm_scatter_gather) +#define DRM_IOCTL_SG_FREE DRM_IOW( 0x39, struct drm_scatter_gather) + +#define DRM_IOCTL_WAIT_VBLANK DRM_IOWR(0x3a, union drm_wait_vblank) + +#define DRM_IOCTL_UPDATE_DRAW DRM_IOW(0x3f, struct drm_update_draw) + +#define DRM_IOCTL_MODE_GETRESOURCES DRM_IOWR(0xA0, struct drm_mode_card_res) +#define DRM_IOCTL_MODE_GETCRTC DRM_IOWR(0xA1, struct drm_mode_crtc) +#define DRM_IOCTL_MODE_SETCRTC DRM_IOWR(0xA2, struct drm_mode_crtc) +#define DRM_IOCTL_MODE_CURSOR DRM_IOWR(0xA3, struct drm_mode_cursor) +#define DRM_IOCTL_MODE_GETGAMMA DRM_IOWR(0xA4, struct drm_mode_crtc_lut) +#define DRM_IOCTL_MODE_SETGAMMA DRM_IOWR(0xA5, struct drm_mode_crtc_lut) +#define DRM_IOCTL_MODE_GETENCODER DRM_IOWR(0xA6, struct drm_mode_get_encoder) +#define DRM_IOCTL_MODE_GETCONNECTOR DRM_IOWR(0xA7, struct drm_mode_get_connector) +#define DRM_IOCTL_MODE_ATTACHMODE DRM_IOWR(0xA8, struct drm_mode_mode_cmd) +#define DRM_IOCTL_MODE_DETACHMODE DRM_IOWR(0xA9, struct drm_mode_mode_cmd) + +#define DRM_IOCTL_MODE_GETPROPERTY DRM_IOWR(0xAA, struct drm_mode_get_property) +#define DRM_IOCTL_MODE_SETPROPERTY DRM_IOWR(0xAB, struct drm_mode_connector_set_property) +#define DRM_IOCTL_MODE_GETPROPBLOB DRM_IOWR(0xAC, struct drm_mode_get_blob) +#define DRM_IOCTL_MODE_GETFB DRM_IOWR(0xAD, struct drm_mode_fb_cmd) +#define DRM_IOCTL_MODE_ADDFB DRM_IOWR(0xAE, struct drm_mode_fb_cmd) +#define DRM_IOCTL_MODE_RMFB DRM_IOWR(0xAF, unsigned int) + +/** + * Device specific ioctls should only be in their respective headers + * The device specific ioctl range is from 0x40 to 0x99. + * Generic IOCTLS restart at 0xA0. + * + * \sa drmCommandNone(), drmCommandRead(), drmCommandWrite(), and + * drmCommandReadWrite(). + */ +#define DRM_COMMAND_BASE 0x40 +#define DRM_COMMAND_END 0xA0 + +/** + * Header for events written back to userspace on the drm fd. The + * type defines the type of event, the length specifies the total + * length of the event (including the header), and user_data is + * typically a 64 bit value passed with the ioctl that triggered the + * event. A read on the drm fd will always only return complete + * events, that is, if for example the read buffer is 100 bytes, and + * there are two 64 byte events pending, only one will be returned. + * + * Event types 0 - 0x7fffffff are generic drm events, 0x80000000 and + * up are chipset specific. + */ +struct drm_event { + u_int32_t type; + u_int32_t length; +}; + +#define DRM_EVENT_VBLANK 0x01 +#define DRM_EVENT_FLIP_COMPLETE 0x02 + +struct drm_event_vblank { + struct drm_event base; + u_int64_t user_data; + u_int32_t tv_sec; + u_int32_t tv_usec; + u_int32_t sequence; + u_int32_t reserved; +}; + +/* typedef area */ +#ifndef __KERNEL__ +typedef struct drm_clip_rect drm_clip_rect_t; +typedef struct drm_tex_region drm_tex_region_t; +typedef struct drm_hw_lock drm_hw_lock_t; +typedef struct drm_version drm_version_t; +typedef struct drm_unique drm_unique_t; +typedef struct drm_list drm_list_t; +typedef struct drm_block drm_block_t; +typedef struct drm_control drm_control_t; +typedef enum drm_map_type drm_map_type_t; +typedef enum drm_map_flags drm_map_flags_t; +typedef struct drm_ctx_priv_map drm_ctx_priv_map_t; +typedef struct drm_map drm_map_t; +typedef struct drm_client drm_client_t; +typedef enum drm_stat_type drm_stat_type_t; +typedef struct drm_stats drm_stats_t; +typedef enum drm_lock_flags drm_lock_flags_t; +typedef struct drm_lock drm_lock_t; +typedef enum drm_dma_flags drm_dma_flags_t; +typedef struct drm_buf_desc drm_buf_desc_t; +typedef struct drm_buf_info drm_buf_info_t; +typedef struct drm_buf_free drm_buf_free_t; +typedef struct drm_buf_pub drm_buf_pub_t; +typedef struct drm_buf_map drm_buf_map_t; +typedef struct drm_dma drm_dma_t; +typedef union drm_wait_vblank drm_wait_vblank_t; +typedef struct drm_agp_mode drm_agp_mode_t; +typedef enum drm_ctx_flags drm_ctx_flags_t; +typedef struct drm_ctx drm_ctx_t; +typedef struct drm_ctx_res drm_ctx_res_t; +typedef struct drm_draw drm_draw_t; +typedef struct drm_update_draw drm_update_draw_t; +typedef struct drm_auth drm_auth_t; +typedef struct drm_irq_busid drm_irq_busid_t; +typedef enum drm_vblank_seq_type drm_vblank_seq_type_t; + +typedef struct drm_agp_buffer drm_agp_buffer_t; +typedef struct drm_agp_binding drm_agp_binding_t; +typedef struct drm_agp_info drm_agp_info_t; +typedef struct drm_scatter_gather drm_scatter_gather_t; +typedef struct drm_set_version drm_set_version_t; +#endif + +#endif diff -Naurp old/src/sys/dev/pci/drm/drm_irq.c new/src/sys/dev/pci/drm/drm_irq.c --- old/src/sys/dev/pci/drm/drm_irq.c 1970-01-01 01:00:00.000000000 +0100 +++ new/src/sys/dev/pci/drm/drm_irq.c 2011-02-08 11:14:00.000000000 +0100 @@ -0,0 +1,517 @@ +/*- + * Copyright 2003 Eric Anholt + * 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 + * ERIC ANHOLT 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: + * Eric Anholt + * + */ + +/** @file drm_irq.c + * Support code for handling setup/teardown of interrupt handlers and + * handing interrupt handlers off to the drivers. + */ + +#include "drmP.h" +#include "drm.h" + +void drm_update_vblank_count(struct drm_device *, int); +void vblank_disable(void *); +int drm_queue_vblank_event(struct drm_device *, int, + union drm_wait_vblank *, struct drm_file *); +void drm_handle_vblank_events(struct drm_device *, int); + +#ifdef DRM_VBLANK_DEBUG +#define DPRINTF(x...) do { printf(x); } while(/* CONSTCOND */ 0) +#else +#define DPRINTF(x...) do { } while(/* CONSTCOND */ 0) +#endif + +int +drm_irq_by_busid(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_irq_busid *irq = data; + + /* + * This is only ever called by root as part of a stupid interface. + * just hand over the irq without checking the busid. If all clients + * can be forced to use interface 1.2 then this can die. + */ + irq->irq = dev->irq; + + DRM_DEBUG("%d:%d:%d => IRQ %d\n", irq->busnum, irq->devnum, + irq->funcnum, irq->irq); + + return 0; +} + +int +drm_irq_install(struct drm_device *dev) +{ + int ret; + + if (dev->irq == 0 || dev->dev_private == NULL) + return (EINVAL); + + DRM_DEBUG("irq=%d\n", dev->irq); + + DRM_LOCK(); + if (dev->irq_enabled) { + DRM_UNLOCK(); + return (EBUSY); + } + dev->irq_enabled = 1; + DRM_UNLOCK(); + + if ((ret = dev->driver->irq_install(dev)) != 0) + goto err; + + return (0); +err: + DRM_LOCK(); + dev->irq_enabled = 0; + DRM_UNLOCK(); + return (ret); +} + +int +drm_irq_uninstall(struct drm_device *dev) +{ + int i; + + DRM_LOCK(); + if (!dev->irq_enabled) { + DRM_UNLOCK(); + return (EINVAL); + } + + dev->irq_enabled = 0; + DRM_UNLOCK(); + + /* + * Ick. we're about to turn of vblanks, so make sure anyone waiting + * on them gets woken up. Also make sure we update state correctly + * so that we can continue refcounting correctly. + */ + if (dev->vblank != NULL) { + mtx_enter(&dev->vblank->vb_lock); + for (i = 0; i < dev->vblank->vb_num; i++) { +#if !defined(__NetBSD__) + wakeup(&dev->vblank->vb_crtcs[i]); +#else /* !defined(__NetBSD__) */ + cv_broadcast(&dev->vblank->vb_crtcs[i].condvar); +#endif /* !defined(__NetBSD__) */ + dev->vblank->vb_crtcs[i].vbl_enabled = 0; + dev->vblank->vb_crtcs[i].vbl_last = + dev->driver->get_vblank_counter(dev, i); + } + mtx_leave(&dev->vblank->vb_lock); + } + + DRM_DEBUG("irq=%d\n", dev->irq); + + dev->driver->irq_uninstall(dev); + + return (0); +} + +int +drm_control(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_control *ctl = data; + + /* Handle drivers who used to require IRQ setup no longer does. */ + if (!(dev->driver->flags & DRIVER_IRQ)) + return (0); + + switch (ctl->func) { + case DRM_INST_HANDLER: + if (dev->if_version < DRM_IF_VERSION(1, 2) && + ctl->irq != dev->irq) + return (EINVAL); + return (drm_irq_install(dev)); + case DRM_UNINST_HANDLER: + return (drm_irq_uninstall(dev)); + default: + return (EINVAL); + } +} + +void +vblank_disable(void *arg) +{ + struct drm_device *dev = (struct drm_device*)arg; + struct drm_vblank_info *vbl = dev->vblank; + struct drm_vblank *crtc; + int i; + + mtx_enter(&vbl->vb_lock); + for (i = 0; i < vbl->vb_num; i++) { + crtc = &vbl->vb_crtcs[i]; + + if (crtc->vbl_refs == 0 && crtc->vbl_enabled) { + DPRINTF("%s: disabling crtc %d\n", __func__, i); + crtc->vbl_last = + dev->driver->get_vblank_counter(dev, i); + dev->driver->disable_vblank(dev, i); + crtc->vbl_enabled = 0; + } + } + mtx_leave(&vbl->vb_lock); +} + +void +drm_vblank_cleanup(struct drm_device *dev) +{ +#if defined(__NetBSD__) + int i; +#endif /* defined(__NetBSD__) */ + + if (dev->vblank == NULL) + return; /* not initialised */ + + timeout_del(&dev->vblank->vb_disable_timer); +#if defined(__NetBSD__) + callout_destroy(&dev->vblank->vb_disable_timer); +#endif /* defined(__NetBSD__) */ + + vblank_disable(dev); + +#if defined(__NetBSD__) + for (i = 0; i < dev->vblank->vb_num; i++) + cv_destroy(&dev->vblank->vb_crtcs[i].condvar); + mutex_destroy(&dev->vblank->vb_lock); +#endif /* defined(__NetBSD__) */ + + drm_free(dev->vblank); + dev->vblank = NULL; +} + +int +drm_vblank_init(struct drm_device *dev, int num_crtcs) +{ + int i; + + dev->vblank = malloc(sizeof(*dev->vblank) + (num_crtcs * + sizeof(struct drm_vblank)), M_DRM, M_WAITOK | M_CANFAIL | M_ZERO); + if (dev->vblank == NULL) + return (ENOMEM); + + dev->vblank->vb_num = num_crtcs; + mtx_init(&dev->vblank->vb_lock, IPL_TTY); + timeout_set(&dev->vblank->vb_disable_timer, vblank_disable, dev); + for (i = 0; i < num_crtcs; i++) + TAILQ_INIT(&dev->vblank->vb_crtcs[i].vbl_events); +#if defined(__NetBSD__) + for (i = 0; i < num_crtcs; i++) + cv_init(&dev->vblank->vb_crtcs[i].condvar, "drmvblq"); +#endif /* defined(__NetBSD__) */ + + return (0); +} + +u_int32_t +drm_vblank_count(struct drm_device *dev, int crtc) +{ + return (dev->vblank->vb_crtcs[crtc].vbl_count); +} + +void +drm_update_vblank_count(struct drm_device *dev, int crtc) +{ + u_int32_t cur_vblank, diff; + + /* + * Interrupt was disabled prior to this call, so deal with counter wrap + * note that we may have lost a full vb_max events if + * the register is small or the interrupts were off for a long time. + */ + cur_vblank = dev->driver->get_vblank_counter(dev, crtc); + diff = cur_vblank - dev->vblank->vb_crtcs[crtc].vbl_last; + if (cur_vblank < dev->vblank->vb_crtcs[crtc].vbl_last) + diff += dev->vblank->vb_max; + + dev->vblank->vb_crtcs[crtc].vbl_count += diff; +} + +int +drm_vblank_get(struct drm_device *dev, int crtc) +{ + struct drm_vblank_info *vbl = dev->vblank; + int ret = 0; + + if (dev->irq_enabled == 0) + return (EINVAL); + + mtx_enter(&vbl->vb_lock); + DPRINTF("%s: %d refs = %d\n", __func__, crtc, + vbl->vb_crtcs[crtc].vbl_refs); + vbl->vb_crtcs[crtc].vbl_refs++; + if (vbl->vb_crtcs[crtc].vbl_refs == 1 && + vbl->vb_crtcs[crtc].vbl_enabled == 0) { + if ((ret = dev->driver->enable_vblank(dev, crtc)) == 0) { + vbl->vb_crtcs[crtc].vbl_enabled = 1; + drm_update_vblank_count(dev, crtc); + } else { + vbl->vb_crtcs[crtc].vbl_refs--; + } + + } + mtx_leave(&vbl->vb_lock); + + return (ret); +} + +void +drm_vblank_put(struct drm_device *dev, int crtc) +{ + mtx_enter(&dev->vblank->vb_lock); + /* Last user schedules disable */ + DPRINTF("%s: %d refs = %d\n", __func__, crtc, + dev->vblank->vb_crtcs[crtc].vbl_refs); + KASSERT(dev->vblank->vb_crtcs[crtc].vbl_refs > 0); + if (--dev->vblank->vb_crtcs[crtc].vbl_refs == 0) + timeout_add_sec(&dev->vblank->vb_disable_timer, 5); + mtx_leave(&dev->vblank->vb_lock); +} + +int +drm_modeset_ctl(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_modeset_ctl *modeset = data; + struct drm_vblank *vbl; + int crtc, ret = 0; + + /* not initialised yet, just noop */ + if (dev->vblank == NULL) + return (0); + + crtc = modeset->crtc; + if (crtc >= dev->vblank->vb_num) + return (EINVAL); + + vbl = &dev->vblank->vb_crtcs[crtc]; + + /* + * If interrupts are enabled/disabled between calls to this ioctl then + * it can get nasty. So just grab a reference so that the interrupts + * keep going through the modeset + */ + switch (modeset->cmd) { + case _DRM_PRE_MODESET: + DPRINTF("%s: pre modeset on %d\n", __func__, crtc); + if (vbl->vbl_inmodeset == 0) { + mtx_enter(&dev->vblank->vb_lock); + vbl->vbl_inmodeset = 0x1; + mtx_leave(&dev->vblank->vb_lock); + if (drm_vblank_get(dev, crtc) == 0) + vbl->vbl_inmodeset |= 0x2; + } + break; + case _DRM_POST_MODESET: + DPRINTF("%s: post modeset on %d\n", __func__, crtc); + if (vbl->vbl_inmodeset) { + if (vbl->vbl_inmodeset & 0x2) + drm_vblank_put(dev, crtc); + vbl->vbl_inmodeset = 0; + } + break; + default: + ret = EINVAL; + break; + } + + return (ret); +} + +int +drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct timeval now; + union drm_wait_vblank *vblwait = data; + int ret, flags, crtc, seq; + + if (!dev->irq_enabled || dev->vblank == NULL || + vblwait->request.type & _DRM_VBLANK_SIGNAL) + return (EINVAL); + + flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK; + crtc = flags & _DRM_VBLANK_SECONDARY ? 1 : 0; + + if (crtc >= dev->vblank->vb_num) + return (EINVAL); + + if ((ret = drm_vblank_get(dev, crtc)) != 0) + return (ret); + seq = drm_vblank_count(dev, crtc); + + if (vblwait->request.type & _DRM_VBLANK_RELATIVE) { + vblwait->request.sequence += seq; + vblwait->request.type &= ~_DRM_VBLANK_RELATIVE; + } + + flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK; + if ((flags & _DRM_VBLANK_NEXTONMISS) && + (seq - vblwait->request.sequence) <= (1<<23)) { + vblwait->request.sequence = seq + 1; + } + + if (flags & _DRM_VBLANK_EVENT) + return (drm_queue_vblank_event(dev, crtc, vblwait, file_priv)); + + DPRINTF("%s: %d waiting on %d, current %d\n", __func__, crtc, + vblwait->request.sequence, drm_vblank_count(dev, crtc)); + DRM_WAIT_ON(ret, &dev->vblank->vb_crtcs[crtc], &dev->vblank->vb_lock, + 3 * hz, "drmvblq", ((drm_vblank_count(dev, crtc) - + vblwait->request.sequence) <= (1 << 23)) || dev->irq_enabled == 0); + + microtime(&now); + vblwait->reply.tval_sec = now.tv_sec; + vblwait->reply.tval_usec = now.tv_usec; + vblwait->reply.sequence = drm_vblank_count(dev, crtc); + DPRINTF("%s: %d done waiting, seq = %d\n", __func__, crtc, + vblwait->reply.sequence); + + drm_vblank_put(dev, crtc); + return (ret); +} + +int +drm_queue_vblank_event(struct drm_device *dev, int crtc, + union drm_wait_vblank *vblwait, struct drm_file *file_priv) +{ + struct drm_pending_vblank_event *vev; + struct timeval now; + u_int seq; + + + vev = drm_calloc(1, sizeof(*vev)); + if (vev == NULL) + return (ENOMEM); + + vev->event.base.type = DRM_EVENT_VBLANK; + vev->event.base.length = sizeof(vev->event); + vev->event.user_data = vblwait->request.signal; + vev->base.event = &vev->event.base; + vev->base.file_priv = file_priv; + vev->base.destroy = (void (*) (struct drm_pending_event *))drm_free; + + microtime(&now); + + mtx_enter(&dev->event_lock); + if (file_priv->event_space < sizeof(vev->event)) { + mtx_leave(&dev->event_lock); + drm_free(vev); + return (ENOMEM); + } + + + seq = drm_vblank_count(dev, crtc); + file_priv->event_space -= sizeof(vev->event); + + DPRINTF("%s: queueing event %d on crtc %d\n", __func__, seq, crtc); + + if ((vblwait->request.type & _DRM_VBLANK_NEXTONMISS) && + (seq - vblwait->request.sequence) <= (1 << 23)) { + vblwait->request.sequence = seq + 1; + vblwait->reply.sequence = vblwait->request.sequence; + } + + vev->event.sequence = vblwait->request.sequence; + if ((seq - vblwait->request.sequence) <= (1 << 23)) { + vev->event.tv_sec = now.tv_sec; + vev->event.tv_usec = now.tv_usec; + DPRINTF("%s: already passed, dequeuing: crtc %d, value %d\n", + __func__, crtc, seq); + drm_vblank_put(dev, crtc); + TAILQ_INSERT_TAIL(&file_priv->evlist, &vev->base, link); +#if !defined(__NetBSD__) + wakeup(&file_priv->evlist); +#else /* !defined(__NetBSD__) */ + cv_broadcast(&file_priv->evlist_condvar); +#endif /* !defined(__NetBSD__) */ + selwakeup(&file_priv->rsel); + } else { + TAILQ_INSERT_TAIL(&dev->vblank->vb_crtcs[crtc].vbl_events, + &vev->base, link); + } + mtx_leave(&dev->event_lock); + + return (0); +} + +void +drm_handle_vblank_events(struct drm_device *dev, int crtc) +{ + struct drmevlist *list; + struct drm_pending_event *ev, *tmp; + struct drm_pending_vblank_event *vev; + struct timeval now; + u_int seq; + + list = &dev->vblank->vb_crtcs[crtc].vbl_events; + microtime(&now); + seq = drm_vblank_count(dev, crtc); + + mtx_enter(&dev->event_lock); + for (ev = TAILQ_FIRST(list); ev != TAILQ_END(list); ev = tmp) { + tmp = TAILQ_NEXT(ev, link); + + vev = (struct drm_pending_vblank_event *)ev; + + if ((seq - vev->event.sequence) > (1 << 23)) + continue; + DPRINTF("%s: got vblank event on crtc %d, value %d\n", + __func__, crtc, seq); + + vev->event.sequence = seq; + vev->event.tv_sec = now.tv_sec; + vev->event.tv_usec = now.tv_usec; + drm_vblank_put(dev, crtc); + TAILQ_REMOVE(list, ev, link); + TAILQ_INSERT_TAIL(&ev->file_priv->evlist, ev, link); +#if !defined(__NetBSD__) + wakeup(&ev->file_priv->evlist); +#else /* !defined(__NetBSD__) */ + cv_broadcast(&ev->file_priv->evlist_condvar); +#endif /* !defined(__NetBSD__) */ + selwakeup(&ev->file_priv->rsel); + } + mtx_leave(&dev->event_lock); +} + +void +drm_handle_vblank(struct drm_device *dev, int crtc) +{ + /* + * XXX if we had proper atomic operations this mutex wouldn't + * XXX need to be held. + */ + mtx_enter(&dev->vblank->vb_lock); + dev->vblank->vb_crtcs[crtc].vbl_count++; +#if !defined(__NetBSD__) + wakeup(&dev->vblank->vb_crtcs[crtc]); +#else /* !defined(__NetBSD__) */ + cv_broadcast(&dev->vblank->vb_crtcs[crtc].condvar); +#endif /* !defined(__NetBSD__) */ + mtx_leave(&dev->vblank->vb_lock); + drm_handle_vblank_events(dev, crtc); +} diff -Naurp old/src/sys/dev/pci/drm/drm_lock.c new/src/sys/dev/pci/drm/drm_lock.c --- old/src/sys/dev/pci/drm/drm_lock.c 1970-01-01 01:00:00.000000000 +0100 +++ new/src/sys/dev/pci/drm/drm_lock.c 2011-02-08 11:14:00.000000000 +0100 @@ -0,0 +1,170 @@ +/*- + * 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 + * VA LINUX SYSTEMS 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 + * Gareth Hughes + * + */ + +/** @file drm_lock.c + * Implementation of the ioctls and other support code for dealing with the + * hardware lock. + * + * The DRM hardware lock is a shared structure between the kernel and userland. + * + * On uncontended access where the new context was the last context, the + * client may take the lock without dropping down into the kernel, using atomic + * compare-and-set. + * + * If the client finds during compare-and-set that it was not the last owner + * of the lock, it calls the DRM lock ioctl, which may sleep waiting for the + * lock, and may have side-effects of kernel-managed context switching. + * + * When the client releases the lock, if the lock is marked as being contended + * by another client, then the DRM unlock ioctl is called so that the + * contending client may be woken up. + */ + +#include "drmP.h" + +int +drm_lock_take(struct drm_lock_data *lock_data, unsigned int context) +{ + volatile unsigned int *lock = &lock_data->hw_lock->lock; + unsigned int old, new; + + do { + old = *lock; + if (old & _DRM_LOCK_HELD) + new = old | _DRM_LOCK_CONT; + else + new = context | _DRM_LOCK_HELD; + } while (!atomic_cmpset_int(lock, old, new)); + + if (_DRM_LOCKING_CONTEXT(old) == context && _DRM_LOCK_IS_HELD(old)) { + if (context != DRM_KERNEL_CONTEXT) + DRM_ERROR("%d holds heavyweight lock\n", context); + return (0); + } + /* If the lock wasn't held before, it's ours */ + return (!_DRM_LOCK_IS_HELD(old)); +} + +int +drm_lock_free(struct drm_lock_data *lock_data, unsigned int context) +{ + volatile unsigned int *lock = &lock_data->hw_lock->lock; + unsigned int old, new; + + mtx_enter(&lock_data->spinlock); + lock_data->file_priv = NULL; + do { + old = *lock; + new = 0; + } while (!atomic_cmpset_int(lock, old, new)); + mtx_leave(&lock_data->spinlock); + + if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) { + DRM_ERROR("%d freed heavyweight lock held by %d\n", + context, _DRM_LOCKING_CONTEXT(old)); + return 1; + } + mtx_enter(&lock_data->spinlock); +#if !defined(__NetBSD__) + wakeup(lock_data); +#else /* !defined(__NetBSD__) */ + cv_broadcast(&lock_data->condvar); +#endif /* !defined(__NetBSD__) */ + mtx_leave(&lock_data->spinlock); + return 0; +} + +int +drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_lock *lock = data; + int ret = 0; + + if (lock->context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + DRM_CURRENTPID, lock->context); + return EINVAL; + } + + DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n", + lock->context, DRM_CURRENTPID, dev->lock.hw_lock->lock, + lock->flags); + + mtx_enter(&dev->lock.spinlock); + for (;;) { + if (drm_lock_take(&dev->lock, lock->context)) { + dev->lock.file_priv = file_priv; + break; /* Got lock */ + } + + /* Contention */ +#if !defined(__NetBSD__) + ret = msleep(&dev->lock, &dev->lock.spinlock, + PZERO | PCATCH, "drmlkq", 0); +#else /* !defined(__NetBSD__) */ + ret = cv_wait_sig(&dev->lock.condvar, &dev->lock.spinlock); +#endif /* !defined(__NetBSD__) */ + if (ret != 0) + break; + } + mtx_leave(&dev->lock.spinlock); + DRM_DEBUG("%d %s\n", lock->context, ret ? "interrupted" : "has lock"); + + if (ret != 0) + return ret; + + /* XXX: Add signal blocking here */ + + return 0; +} + +int +drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_lock *lock = data; + + if (lock->context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + DRM_CURRENTPID, lock->context); + return EINVAL; + } + /* Check that the context unlock being requested actually matches + * who currently holds the lock. + */ + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) || + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock) != lock->context) + return EINVAL; + + if (drm_lock_free(&dev->lock, lock->context)) { + DRM_ERROR("\n"); + } + + return 0; +} diff -Naurp old/src/sys/dev/pci/drm/drm_memory.c new/src/sys/dev/pci/drm/drm_memory.c --- old/src/sys/dev/pci/drm/drm_memory.c 1970-01-01 01:00:00.000000000 +0100 +++ new/src/sys/dev/pci/drm/drm_memory.c 2011-02-08 11:14:00.000000000 +0100 @@ -0,0 +1,191 @@ +/*- + *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 + * VA LINUX SYSTEMS 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 + * Gareth Hughes + * + */ + +/** @file drm_memory.c + * Wrappers for kernel memory allocation routines, and MTRR management support. + * + * This file previously implemented a memory consumption tracking system using + * the "area" argument for various different types of allocations, but that + * has been stripped out for now. + */ + +#include "drmP.h" + +#if defined(__NetBSD__) +MALLOC_DEFINE(M_DRM, "drm", "Direct Rendering Management"); +#endif /* defined(__NetBSD__) */ + +void* +drm_alloc(size_t size) +{ + return (malloc(size, M_DRM, M_NOWAIT)); +} + +void * +drm_calloc(size_t nmemb, size_t size) +{ + if (nmemb == 0 || SIZE_MAX / nmemb < size) + return (NULL); + else + return malloc(size * nmemb, M_DRM, M_NOWAIT | M_ZERO); +} + +void * +drm_realloc(void *oldpt, size_t oldsize, size_t size) +{ + void *pt; + + pt = malloc(size, M_DRM, M_NOWAIT); + if (pt == NULL) + return NULL; + if (oldpt && oldsize) { + memcpy(pt, oldpt, min(oldsize, size)); + free(oldpt, M_DRM); + } + return pt; +} + +void +drm_free(void *pt) +{ + if (pt != NULL) + free(pt, M_DRM); +} + +/* Inline replacements for DRM_IOREMAP macros */ +void +drm_core_ioremap(struct drm_local_map *map, struct drm_device *dev) +{ + DRM_DEBUG("offset: 0x%lx size: 0x%lx type: %d\n", map->offset, map->size, + map->type); + + /* default to failure. */ + map->handle = 0; + + if (map->type == _DRM_AGP || map->type == _DRM_FRAME_BUFFER) { + /* + * there can be multiple agp maps in the same BAR, agp also + * quite possibly isn't the same as the vga device, just try + * to map it. + */ + DRM_DEBUG("AGP map\n"); + map->bst = dev->bst; + if (bus_space_map(map->bst, map->offset, + map->size, BUS_SPACE_MAP_LINEAR | + BUS_SPACE_MAP_PREFETCHABLE, &map->bsh)) { + DRM_ERROR("ioremap fail\n"); + return; + } + /* handles are still supposed to be kernel virtual addresses */ + map->handle = bus_space_vaddr(map->bst, map->bsh); + } +} + +void +drm_core_ioremapfree(struct drm_local_map *map) +{ + if (map->handle && map->size && (map->type == _DRM_AGP || + map->type == _DRM_FRAME_BUFFER)) { + bus_space_unmap(map->bst, map->bsh, map->size); + map->handle = 0; + } +} + +#if !defined(__NetBSD__) +int +drm_mtrr_add(unsigned long offset, size_t size, int flags) +{ +#ifndef DRM_NO_MTRR + int act; + struct mem_range_desc mrdesc; + + mrdesc.mr_base = offset; + mrdesc.mr_len = size; + mrdesc.mr_flags = flags; + act = MEMRANGE_SET_UPDATE; + strlcpy(mrdesc.mr_owner, "drm", sizeof(mrdesc.mr_owner)); + return mem_range_attr_set(&mrdesc, &act); +#else + return 0; +#endif +} + +int +drm_mtrr_del(int handle, unsigned long offset, size_t size, int flags) +{ +#ifndef DRM_NO_MTRR + int act; + struct mem_range_desc mrdesc; + + mrdesc.mr_base = offset; + mrdesc.mr_len = size; + mrdesc.mr_flags = flags; + act = MEMRANGE_SET_REMOVE; + strlcpy(mrdesc.mr_owner, "drm", sizeof(mrdesc.mr_owner)); + return mem_range_attr_set(&mrdesc, &act); +#else + return 0; +#endif +} +#else /* !defined(__NetBSD__) */ +int +drm_mtrr_add(unsigned long offset, size_t size, int flags) +{ +#ifdef MTRR_GETSET_KERNEL + struct mtrr mtrrmap; + int one = 1; + + mtrrmap.base = offset; + mtrrmap.len = size; + mtrrmap.type = flags; + mtrrmap.flags = MTRR_VALID; + return mtrr_set(&mtrrmap, &one, NULL, MTRR_GETSET_KERNEL); +#else + return 0; +#endif +} + +int +drm_mtrr_del(int handle, unsigned long offset, size_t size, int flags) +{ +#ifdef MTRR_GETSET_KERNEL + struct mtrr mtrrmap; + int one = 1; + + mtrrmap.base = offset; + mtrrmap.len = size; + mtrrmap.type = flags; + mtrrmap.flags = 0; + return mtrr_set(&mtrrmap, &one, NULL, MTRR_GETSET_KERNEL); +#else + return 0; +#endif +} +#endif /* !defined(__NetBSD__) */ diff -Naurp old/src/sys/dev/pci/drm/drmP.h new/src/sys/dev/pci/drm/drmP.h --- old/src/sys/dev/pci/drm/drmP.h 1970-01-01 01:00:00.000000000 +0100 +++ new/src/sys/dev/pci/drm/drmP.h 2011-02-08 11:14:00.000000000 +0100 @@ -0,0 +1,912 @@ +/* drmP.h -- Private header for Direct Rendering Manager -*- linux-c -*- + * Created: Mon Jan 4 10:05:05 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 + * VA LINUX SYSTEMS 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 + * Gareth Hughes + * + */ + +#ifndef _DRM_P_H_ +#define _DRM_P_H_ + +#if defined(_KERNEL) || defined(__KERNEL__) + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if !defined(__NetBSD__) +#include +#else /* !defined(__NetBSD__) */ +#if defined(__i386__) || defined(__x86_64__) +#include +#endif +#endif /* !defined(__NetBSD__) */ +#include +#include + +#if defined(__NetBSD__) +#include +#include +#include +#endif /* defined(__NetBSD__) */ + +#include +#include +#include +#include +#include +#include +#include + +#if defined(__NetBSD__) +MALLOC_DECLARE(M_DRM); + +/* OpenBSD queue(3) compatibility definitions. */ +#define TAILQ_END(head) NULL + +/* OpenBSD UVM pager compatibility definitions. */ +#define VM_PAGER_OK 0 +#define VM_PAGER_ERROR EIO +#define VM_PAGER_REFAULT ERESTART + +/* OpenBSD UVM object compatibility definitions. */ +#define uvm_objwire(uobj, start, end, plist) \ + uobj_wirepages(uobj, start, end, plist) +#define uvm_objunwire(uobj, start, end) \ + uobj_unwirepages(uobj, start, end) + +/* OpenBSD mutex(9) compatibility definitions. */ +#define mtx_init(mtx, lvl) mutex_init(mtx, MUTEX_DEFAULT, lvl) +#define mtx_enter(mtx) mutex_enter(mtx) +#define mtx_leave(mtx) mutex_exit(mtx) + +#define MUTEX_ASSERT_LOCKED(mtx) (KASSERT(mutex_owned(mtx))) +#define MUTEX_ASSERT_UNLOCKED(mtx) (KASSERT(!(mutex_owned(mtx)))) + +/* OpenBSD rwlock(9) compatibility definitions. */ +#define rw_enter_write(rwl) rw_enter(rwl, RW_WRITER) +#define rw_exit_write(rwl) rw_exit(rwl) +#define rw_enter_read(rwl) rw_enter(rwl, RW_READER) +#define rw_exit_read(rwl) rw_exit(rwl) + +/* OpenBSD timeout(9) compatibility definitions. */ +static __inline void +timeout_set(callout_t *cs, void (*func)(void *), void *arg) +{ + callout_init(cs, 0); + callout_setfunc(cs, func, arg); +} +static __inline void +timeout_del(callout_t *cs) +{ + (void)callout_stop(cs); +} +static __inline void +timeout_add_sec(callout_t *cs, int sec) +{ + long long ticks; + + ticks = (long long)hz * sec; + if (ticks > INT_MAX) + ticks = INT_MAX; + + callout_schedule(cs, (int)ticks); +} +static __inline void +timeout_add_msec(callout_t *cs, int msec) +{ + long long ticks; + + ticks = (long long)msec * 1000 / tick; + if (ticks > INT_MAX) + ticks = INT_MAX; + + callout_schedule(cs, (int)ticks); +} + +/* OpenBSD selrecord/selwakeup compatibility definitions. */ +#define selwakeup(sip) selnotify(sip, 0, 0) + +/* OpenBSD types compatibility definitions. */ +typedef void * caddr_t; +typedef struct vm_page * vm_page_t; +#endif /* defined(__NetBSD__) */ + +#include "drm.h" +#include "drm_atomic.h" + +#define DRM_KERNEL_CONTEXT 0 /* Change drm_resctx if changed */ +#define DRM_RESERVED_CONTEXTS 1 /* Change drm_resctx if changed */ + +#define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8) + + /* Internal types and structures */ +#define DRM_IF_VERSION(maj, min) (maj << 16 | min) + +#define __OS_HAS_AGP 1 + +#define DRM_CURRENTPID curproc->p_pid +#define DRM_LOCK() rw_enter_write(&dev->dev_lock) +#define DRM_UNLOCK() rw_exit_write(&dev->dev_lock) +#define DRM_READLOCK() rw_enter_read(&dev->dev_lock) +#define DRM_READUNLOCK() rw_exit_read(&dev->dev_lock) +#define DRM_MAXUNITS 8 + +#if !defined(__NetBSD__) + +/* D_CLONE only supports one device, this will be fixed eventually */ +#define drm_get_device_from_kdev(_kdev) \ + (drm_cd.cd_ndevs > 0 ? drm_cd.cd_devs[0] : NULL) +#if 0 +#define drm_get_device_from_kdev(_kdev) \ + (minor(_kdev) < drm_cd.cd_ndevs) ? drm_cd.cd_devs[minor(_kdev)] : NULL +#endif + +/* DRM_SUSER returns true if the user is superuser */ +#define DRM_SUSER(p) (suser(p, p->p_acflag) == 0) +#define DRM_MTRR_WC MDF_WRITECOMBINE + +#define PAGE_ALIGN(addr) (((addr) + PAGE_MASK) & ~PAGE_MASK) + +extern struct cfdriver drm_cd; + +#else /* !defined(__NetBSD__) */ + +/* D_CLONE only supports one device, this will be fixed eventually */ +#define drm_get_device_from_kdev(_kdev) \ + device_private(drmdev_cd.cd_ndevs > 0 ? drmdev_cd.cd_devs[0] : NULL) + +#define DRM_SUSER(l) (kauth_cred_getsvuid((l)->l_cred) == 0) + +#ifdef MTRR_TYPE_WC +#define DRM_MTRR_WC MTRR_TYPE_WC +#else +#define DRM_MTRR_WC 0 +#endif + +#define PAGE_ALIGN(addr) round_page(addr) + +extern struct cfdriver drmdev_cd; + +#endif /* !defined(__NetBSD__) */ + +typedef u_int64_t u64; +typedef u_int32_t u32; +typedef u_int16_t u16; +typedef u_int8_t u8; + +/* DRM_READMEMORYBARRIER() prevents reordering of reads. + * DRM_WRITEMEMORYBARRIER() prevents reordering of writes. + * DRM_MEMORYBARRIER() prevents reordering of reads and writes. + */ +#if defined(__i386__) +#define DRM_READMEMORYBARRIER() __asm __volatile( \ + "lock; addl $0,0(%%esp)" : : : "memory"); +#define DRM_WRITEMEMORYBARRIER() __asm __volatile("" : : : "memory"); +#define DRM_MEMORYBARRIER() __asm __volatile( \ + "lock; addl $0,0(%%esp)" : : : "memory"); +#elif defined(__alpha__) +#define DRM_READMEMORYBARRIER() alpha_mb(); +#define DRM_WRITEMEMORYBARRIER() alpha_wmb(); +#define DRM_MEMORYBARRIER() alpha_mb(); +#elif defined(__amd64__) +#define DRM_READMEMORYBARRIER() __asm __volatile( \ + "lock; addl $0,0(%%rsp)" : : : "memory"); +#define DRM_WRITEMEMORYBARRIER() __asm __volatile("" : : : "memory"); +#define DRM_MEMORYBARRIER() __asm __volatile( \ + "lock; addl $0,0(%%rsp)" : : : "memory"); +#endif + +#define DRM_COPY_TO_USER(user, kern, size) copyout(kern, user, size) +#define DRM_COPY_FROM_USER(kern, user, size) copyin(user, kern, size) +#define le32_to_cpu(x) letoh32(x) +#define cpu_to_le32(x) htole32(x) + +#define DRM_UDELAY(udelay) DELAY(udelay) + +#define LOCK_TEST_WITH_RETURN(dev, file_priv) \ +do { \ + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) || \ + dev->lock.file_priv != file_priv) { \ + DRM_ERROR("%s called without lock held\n", \ + __FUNCTION__); \ + return EINVAL; \ + } \ +} while (0) + +#if !defined(__NetBSD__) +#define DRM_WAIT_ON(ret, queue, lock, timeout, msg, condition ) do { \ + mtx_enter(lock); \ + while ((ret) == 0) { \ + if (condition) \ + break; \ + ret = msleep((queue), (lock), PZERO | PCATCH, \ + (msg), (timeout)); \ + } \ + mtx_leave(lock); \ +} while (/* CONSTCOND */ 0) +#else /* !defined(__NetBSD__) */ +#define DRM_WAIT_ON(ret, queue, lock, timeout, msg, condition ) do { \ + mtx_enter(lock); \ + while ((ret) == 0) { \ + if (condition) \ + break; \ + ret = cv_timedwait_sig((queue.condvar), (lock), \ + (timeout)); \ + } \ + mtx_leave(lock); \ +} while (/* CONSTCOND */ 0) +#endif /* !defined(__NetBSD__) */ + +#define DRM_ERROR(fmt, arg...) \ + printf("error: [" DRM_NAME ":pid%d:%s] *ERROR* " fmt, \ + curproc->p_pid, __func__ , ## arg) + + +#define DRM_INFO(fmt, arg...) printf("%s: " fmt, dev_priv->dev.dv_xname, ## arg) + +#ifdef DRMDEBUG +#undef DRM_DEBUG +#define DRM_DEBUG(fmt, arg...) do { \ + if (drm_debug_flag) \ + printf("[" DRM_NAME ":pid%d:%s] " fmt, curproc->p_pid, \ + __func__ , ## arg); \ +} while (0) +#else +#define DRM_DEBUG(fmt, arg...) do { } while(/* CONSTCOND */ 0) +#endif + +struct drm_pcidev { + int vendor; + int device; + long driver_private; +}; + +struct drm_file; +struct drm_device; + +struct drm_buf { + int idx; /* Index into master buflist */ + int total; /* Buffer size */ + int used; /* Amount of buffer in use (for DMA) */ + unsigned long offset; /* Byte offset (used internally) */ + void *address; /* KVA of buffer */ + unsigned long bus_address; /* Bus address of buffer */ + __volatile__ int pending; /* On hardware DMA queue */ + struct drm_file *file_priv; /* Unique identifier of holding process */ + void *dev_private; /* Per-buffer private storage */ +}; + +struct drm_dmamem { + bus_dmamap_t map; + caddr_t kva; + bus_size_t size; + int nsegs; + bus_dma_segment_t segs[1]; +}; + +struct drm_buf_entry { + struct drm_dmamem **seglist; + struct drm_buf *buflist; + int buf_count; + int buf_size; + int page_order; + int seg_count; +}; + +struct drm_pending_event { + TAILQ_ENTRY(drm_pending_event) link; + struct drm_event *event; + struct drm_file *file_priv; + void (*destroy)(struct drm_pending_event *); +}; + +struct drm_pending_vblank_event { + struct drm_pending_event base; + struct drm_event_vblank event; +}; + +TAILQ_HEAD(drmevlist, drm_pending_event); + +struct drm_file { + SPLAY_HEAD(drm_obj_tree, drm_handle) obj_tree; + struct drmevlist evlist; +#if !defined(__NetBSD__) + struct mutex table_lock; +#else /* !defined(__NetBSD__) */ + kmutex_t table_lock; + kcondvar_t evlist_condvar; +#endif /* !defined(__NetBSD__) */ + struct selinfo rsel; + SPLAY_ENTRY(drm_file) link; + int authenticated; + unsigned long ioctl_count; + dev_t kdev; + drm_magic_t magic; + int event_space; + int flags; + int master; + int minor; + u_int obj_id; /*next gem id*/ +}; + +struct drm_lock_data { +#if !defined(__NetBSD__) + struct mutex spinlock; +#else /* !defined(__NetBSD__) */ + kmutex_t spinlock; + kcondvar_t condvar; +#endif /* !defined(__NetBSD__) */ + struct drm_hw_lock *hw_lock; /* Hardware lock */ + /* Unique identifier of holding process (NULL is kernel) */ + struct drm_file *file_priv; +}; + +/* This structure, in the struct drm_device, is always initialized while + * the device is open. dev->dma_lock protects the incrementing of + * dev->buf_use, which when set marks that no further bufs may be allocated + * until device teardown occurs (when the last open of the device has closed). + * The high/low watermarks of bufs are only touched by the X Server, and thus + * not concurrently accessed, so no locking is needed. + */ +struct drm_device_dma { +#if !defined(__NetBSD__) + struct rwlock dma_lock; +#else /* !defined(__NetBSD__) */ + krwlock_t dma_lock; +#endif /* !defined(__NetBSD__) */ + struct drm_buf_entry bufs[DRM_MAX_ORDER+1]; + struct drm_buf **buflist; /* Vector of pointers info bufs*/ + unsigned long *pagelist; + unsigned long byte_count; + int buf_use; /* Buffers used no more alloc */ + int buf_count; + int page_count; + int seg_count; + enum { + _DRM_DMA_USE_AGP = 0x01, + _DRM_DMA_USE_SG = 0x02 + } flags; +}; + +struct drm_agp_mem { + void *handle; + unsigned long bound; /* address */ + int pages; + TAILQ_ENTRY(drm_agp_mem) link; +}; + +struct drm_agp_head { + struct agp_softc *agpdev; + const char *chipset; + TAILQ_HEAD(agp_memlist, drm_agp_mem) memory; + struct agp_info info; + unsigned long base; + unsigned long mode; + unsigned long page_mask; + int acquired; + int cant_use_aperture; + int enabled; + int mtrr; +}; + +struct drm_sg_mem { + struct drm_dmamem *mem; + unsigned long handle; +}; + +struct drm_local_map { + TAILQ_ENTRY(drm_local_map) link; /* Link for map list */ + struct drm_dmamem *dmamem;/* Handle to DMA mem */ + void *handle;/* KVA, if mapped */ + bus_space_tag_t bst; /* Tag for mapped pci mem */ + bus_space_handle_t bsh; /* Handle to mapped pci mem */ + u_long ext; /* extent for mmap */ + u_long offset;/* Physical address */ + u_long size; /* Physical size (bytes) */ + int mtrr; /* Boolean: MTRR used */ + enum drm_map_flags flags; /* Flags */ + enum drm_map_type type; /* Type of memory mapped */ +}; + +struct drm_vblank_info { +#if !defined(__NetBSD__) + struct mutex vb_lock; /* VBLANK data lock */ + struct timeout vb_disable_timer; /* timer for disable */ +#else /* !defined(__NetBSD__) */ + kmutex_t vb_lock; /* VBLANK data lock */ + callout_t vb_disable_timer; /* timer for disable */ +#endif /* !defined(__NetBSD__) */ + int vb_num; /* number of crtcs */ + u_int32_t vb_max; /* counter reg size */ + struct drm_vblank { + struct drmevlist vbl_events; /* vblank events */ + u_int32_t vbl_last; /* Last recieved */ + u_int32_t vbl_count; /* interrupt no. */ + int vbl_refs; /* Number of users */ + int vbl_enabled; /* Enabled? */ + int vbl_inmodeset; /* in a modeset? */ +#if defined(__NetBSD__) + kcondvar_t condvar; +#endif /* defined(__NetBSD__) */ + } vb_crtcs[1]; +}; + +/* Heap implementation for radeon and i915 legacy */ +TAILQ_HEAD(drm_heap, drm_mem); + +struct drm_mem { + TAILQ_ENTRY(drm_mem) link; + struct drm_file *file_priv; /* NULL: free, other: real files */ + int start; + int size; +}; + +/* location of GART table */ +#define DRM_ATI_GART_MAIN 1 +#define DRM_ATI_GART_FB 2 + +#define DRM_ATI_GART_PCI 1 +#define DRM_ATI_GART_PCIE 2 +#define DRM_ATI_GART_IGP 3 +#define DRM_ATI_GART_R600 4 + +#define DMA_BIT_MASK(n) (((n) == 64) ? ~0ULL : (1ULL<<(n)) -1) +#define upper_32_bits(_val) ((u_int32_t)(((_val) >> 16) >> 16)) + +struct drm_ati_pcigart_info { + union pcigart_table { + struct fb_gart { + bus_space_tag_t bst; + bus_space_handle_t bsh; + } fb; + struct mem_gart { + struct drm_dmamem *mem; + u_int32_t *addr; + } dma; + } tbl; + bus_addr_t bus_addr; + bus_addr_t table_mask; + bus_size_t table_size; + int gart_table_location; + int gart_reg_if; +}; + +/* + * Locking protocol: + * All drm object are uvm objects, as such they have a reference count and + * a lock. On the other hand, operations carries out by the drm may involve + * sleeping (waiting for rendering to finish, say), while you wish to have + * mutual exclusion on an object. For this reason, all drm-related operations + * on drm objects must acquire the DRM_BUSY flag on the object as the first + * thing that they do. If the BUSY flag is already on the object, set DRM_WANTED + * and sleep until the other locker is done with it. When the BUSY flag is + * acquired then only that flag and a reference is required to do most + * operations on the drm_object. The uvm object is still bound by uvm locking + * protocol. + * + * Subdrivers (radeon, intel, etc) may have other locking requirement, these + * requirements will be detailed in those drivers. + */ +struct drm_obj { + struct uvm_object uobj; + SPLAY_ENTRY(drm_obj) entry; + struct drm_device *dev; + struct uvm_object *uao; + + size_t size; + int name; + int handlecount; +/* any flags over 0x00000010 are device specific */ +#define DRM_BUSY 0x00000001 +#define DRM_WANTED 0x00000002 + u_int do_flags; +#ifdef DRMLOCKDEBUG /* to tell owner */ + struct proc *holding_proc; +#endif + uint32_t read_domains; + uint32_t write_domain; + + uint32_t pending_read_domains; + uint32_t pending_write_domain; +}; + +struct drm_handle { + SPLAY_ENTRY(drm_handle) entry; + struct drm_obj *obj; + uint32_t handle; +}; + +struct drm_driver_info { + int (*firstopen)(struct drm_device *); + int (*open)(struct drm_device *, struct drm_file *); + int (*ioctl)(struct drm_device*, u_long, caddr_t, + struct drm_file *); + void (*close)(struct drm_device *, struct drm_file *); + void (*lastclose)(struct drm_device *); + int (*dma_ioctl)(struct drm_device *, struct drm_dma *, + struct drm_file *); + int (*irq_install)(struct drm_device *); + void (*irq_uninstall)(struct drm_device *); + int vblank_pipes; + u_int32_t (*get_vblank_counter)(struct drm_device *, int); + int (*enable_vblank)(struct drm_device *, int); + void (*disable_vblank)(struct drm_device *, int); + /* + * driver-specific constructor for gem objects to set up private data. + * returns 0 on success. + */ + int (*gem_init_object)(struct drm_obj *); + void (*gem_free_object)(struct drm_obj *); + int (*gem_fault)(struct drm_obj *, struct uvm_faultinfo *, off_t, + vaddr_t, vm_page_t *, int, int, vm_prot_t, int); + + size_t gem_size; + size_t buf_priv_size; + size_t file_priv_size; + + int major; + int minor; + int patchlevel; + const char *name; /* Simple driver name */ + const char *desc; /* Longer driver name */ + const char *date; /* Date of last major changes. */ + +#define DRIVER_AGP 0x1 +#define DRIVER_AGP_REQUIRE 0x2 +#define DRIVER_MTRR 0x4 +#define DRIVER_DMA 0x8 +#define DRIVER_PCI_DMA 0x10 +#define DRIVER_SG 0x20 +#define DRIVER_IRQ 0x40 +#define DRIVER_GEM 0x80 + + u_int flags; +}; + +/** + * DRM device functions structure + */ +struct drm_device { + struct device device; /* softc is an extension of struct device */ + + const struct drm_driver_info *driver; + + bus_dma_tag_t dmat; + bus_space_tag_t bst; + + char *unique; /* Unique identifier: e.g., busid */ + int unique_len; /* Length of unique field */ + + int if_version; /* Highest interface version set */ + /* Locks */ +#if !defined(__NetBSD__) + struct rwlock dev_lock; /* protects everything else */ +#else /* !defined(__NetBSD__) */ + krwlock_t dev_lock; /* protects everything else */ +#endif /* !defined(__NetBSD__) */ + + /* Usage Counters */ + int open_count; /* Outstanding files open */ + + /* Authentication */ + SPLAY_HEAD(drm_file_tree, drm_file) files; + drm_magic_t magicid; + + /* Linked list of mappable regions. Protected by dev_lock */ + struct extent *handle_ext; + TAILQ_HEAD(drm_map_list, drm_local_map) maplist; + + + struct drm_lock_data lock; /* Information on hardware lock */ + + /* DMA queues (contexts) */ + struct drm_device_dma *dma; /* Optional pointer for DMA support */ + + /* Context support */ + int irq; /* Interrupt used by board */ + int irq_enabled; /* True if the irq handler is enabled */ + + /* VBLANK support */ + struct drm_vblank_info *vblank; /* One per ctrc */ +#if !defined(__NetBSD__) + struct mutex event_lock; +#else /* !defined(__NetBSD__) */ + kmutex_t event_lock; +#endif /* !defined(__NetBSD__) */ + + pid_t buf_pgid; + + struct drm_agp_head *agp; + struct drm_sg_mem *sg; /* Scatter gather memory */ + atomic_t *ctx_bitmap; + void *dev_private; + struct drm_local_map *agp_buffer_map; + + /* GEM info */ +#if !defined(__NetBSD__) + struct mutex obj_name_lock; +#else /* !defined(__NetBSD__) */ + kmutex_t obj_name_lock; +#endif /* !defined(__NetBSD__) */ + atomic_t obj_count; + u_int obj_name; + atomic_t obj_memory; + atomic_t pin_count; + atomic_t pin_memory; + atomic_t gtt_count; + atomic_t gtt_memory; + uint32_t gtt_total; + uint32_t invalidate_domains; + uint32_t flush_domains; + SPLAY_HEAD(drm_name_tree, drm_obj) name_tree; + struct pool objpl; +}; + +struct drm_attach_args { + const struct drm_driver_info *driver; + char *busid; + bus_dma_tag_t dmat; + bus_space_tag_t bst; + size_t busid_len; + int is_agp; + u_int8_t irq; +}; + +extern int drm_debug_flag; + +/* Device setup support (drm_drv.c) */ +int drm_pciprobe(struct pci_attach_args *, const struct drm_pcidev * ); +struct device *drm_attach_pci(const struct drm_driver_info *, + struct pci_attach_args *, int, struct device *); +dev_type_ioctl(drmioctl); +dev_type_read(drmread); +dev_type_poll(drmpoll); +dev_type_open(drmopen); +dev_type_close(drmclose); +dev_type_mmap(drmmmap); +struct drm_local_map *drm_getsarea(struct drm_device *); +struct drm_dmamem *drm_dmamem_alloc(bus_dma_tag_t, bus_size_t, bus_size_t, + int, bus_size_t, int, int); +void drm_dmamem_free(bus_dma_tag_t, struct drm_dmamem *); + +const struct drm_pcidev *drm_find_description(int , int , + const struct drm_pcidev *); + +/* File operations helpers (drm_fops.c) */ +struct drm_file *drm_find_file_by_minor(struct drm_device *, int); + +/* Memory management support (drm_memory.c) */ +void *drm_alloc(size_t); +void *drm_calloc(size_t, size_t); +void *drm_realloc(void *, size_t, size_t); +void drm_free(void *); + +/* XXX until we get PAT support */ +#define drm_core_ioremap_wc drm_core_ioremap +void drm_core_ioremap(struct drm_local_map *, struct drm_device *); +void drm_core_ioremapfree(struct drm_local_map *); + +int drm_mtrr_add(unsigned long, size_t, int); +int drm_mtrr_del(int, unsigned long, size_t, int); + +/* Heap interface (DEPRECATED) */ +int drm_init_heap(struct drm_heap *, int, int); +struct drm_mem *drm_alloc_block(struct drm_heap *, int, int, + struct drm_file *); +int drm_mem_free(struct drm_heap *, int, struct drm_file *); +void drm_mem_release(struct drm_heap *, struct drm_file *); +void drm_mem_takedown(struct drm_heap *); + +/* Context management (DRI1, deprecated) */ +int drm_ctxbitmap_init(struct drm_device *); +void drm_ctxbitmap_cleanup(struct drm_device *); +void drm_ctxbitmap_free(struct drm_device *, int); +int drm_ctxbitmap_next(struct drm_device *); + +/* Locking IOCTL support (drm_lock.c) */ +int drm_lock_take(struct drm_lock_data *, unsigned int); +int drm_lock_free(struct drm_lock_data *, unsigned int); + +/* Buffer management and DMA support (drm_bufs.c) */ +int drm_order(unsigned long); +struct drm_local_map *drm_core_findmap(struct drm_device *, unsigned long); +int drm_rmmap_ioctl(struct drm_device *, void *, struct drm_file *); +void drm_rmmap(struct drm_device *, struct drm_local_map *); +void drm_rmmap_locked(struct drm_device *, struct drm_local_map *); +int drm_addmap_ioctl(struct drm_device *, void *, struct drm_file *); +int drm_addmap(struct drm_device *, unsigned long, unsigned long, + enum drm_map_type, enum drm_map_flags, struct drm_local_map **); +int drm_addbufs(struct drm_device *, struct drm_buf_desc *); +int drm_freebufs(struct drm_device *, void *, struct drm_file *); +int drm_mapbufs(struct drm_device *, void *, struct drm_file *); +int drm_dma(struct drm_device *, void *, struct drm_file *); +int drm_dma_setup(struct drm_device *); +void drm_dma_takedown(struct drm_device *); +void drm_cleanup_buf(struct drm_device *, struct drm_buf_entry *); +void drm_free_buffer(struct drm_device *, struct drm_buf *); +void drm_reclaim_buffers(struct drm_device *, struct drm_file *); + +/* IRQ support (drm_irq.c) */ +int drm_irq_install(struct drm_device *); +int drm_irq_uninstall(struct drm_device *); +void drm_vblank_cleanup(struct drm_device *); +int drm_vblank_init(struct drm_device *, int); +u_int32_t drm_vblank_count(struct drm_device *, int); +int drm_vblank_get(struct drm_device *, int); +void drm_vblank_put(struct drm_device *, int); +int drm_modeset_ctl(struct drm_device *, void *, struct drm_file *); +void drm_handle_vblank(struct drm_device *, int); + +/* AGP/PCI Express/GART support (drm_agpsupport.c) */ +struct drm_agp_head *drm_agp_init(void); +void drm_agp_takedown(struct drm_device *); +int drm_agp_acquire(struct drm_device *); +int drm_agp_release(struct drm_device *); +int drm_agp_info(struct drm_device *, struct drm_agp_info *); +int drm_agp_enable(struct drm_device *, struct drm_agp_mode); +void *drm_agp_allocate_memory(size_t, u32); +int drm_agp_free_memory(void *); +int drm_agp_bind_memory(void *, off_t); +int drm_agp_unbind_memory(void *); +int drm_agp_alloc(struct drm_device *, struct drm_agp_buffer *); +int drm_agp_free(struct drm_device *, struct drm_agp_buffer *); +int drm_agp_bind(struct drm_device *, struct drm_agp_binding *); +int drm_agp_unbind(struct drm_device *, struct drm_agp_binding *); + +/* Scatter Gather Support (drm_scatter.c) */ +void drm_sg_cleanup(struct drm_device *, struct drm_sg_mem *); +int drm_sg_alloc(struct drm_device *, struct drm_scatter_gather *); + +/* ATI PCIGART support (ati_pcigart.c) */ +int drm_ati_pcigart_init(struct drm_device *, + struct drm_ati_pcigart_info *); +int drm_ati_pcigart_cleanup(struct drm_device *, + struct drm_ati_pcigart_info *); + +/* Locking IOCTL support (drm_drv.c) */ +int drm_lock(struct drm_device *, void *, struct drm_file *); +int drm_unlock(struct drm_device *, void *, struct drm_file *); + +/* Context IOCTL support (drm_context.c) */ +int drm_resctx(struct drm_device *, void *, struct drm_file *); +int drm_addctx(struct drm_device *, void *, struct drm_file *); +int drm_getctx(struct drm_device *, void *, struct drm_file *); +int drm_rmctx(struct drm_device *, void *, struct drm_file *); + +/* IRQ support (drm_irq.c) */ +int drm_control(struct drm_device *, void *, struct drm_file *); +int drm_wait_vblank(struct drm_device *, void *, struct drm_file *); +int drm_irq_by_busid(struct drm_device *, void *, struct drm_file *); + +/* AGP/GART support (drm_agpsupport.c) */ +int drm_agp_acquire_ioctl(struct drm_device *, void *, struct drm_file *); +int drm_agp_release_ioctl(struct drm_device *, void *, struct drm_file *); +int drm_agp_enable_ioctl(struct drm_device *, void *, struct drm_file *); +int drm_agp_info_ioctl(struct drm_device *, void *, struct drm_file *); +int drm_agp_alloc_ioctl(struct drm_device *, void *, struct drm_file *); +int drm_agp_free_ioctl(struct drm_device *, void *, struct drm_file *); +int drm_agp_unbind_ioctl(struct drm_device *, void *, struct drm_file *); +int drm_agp_bind_ioctl(struct drm_device *, void *, struct drm_file *); + +/* Scatter Gather Support (drm_scatter.c) */ +int drm_sg_alloc_ioctl(struct drm_device *, void *, struct drm_file *); +int drm_sg_free(struct drm_device *, void *, struct drm_file *); + +struct drm_obj *drm_gem_object_alloc(struct drm_device *, size_t); +void drm_unref(struct uvm_object *); +void drm_ref(struct uvm_object *); +void drm_unref_locked(struct uvm_object *); +void drm_ref_locked(struct uvm_object *); +void drm_hold_object_locked(struct drm_obj *); +void drm_hold_object(struct drm_obj *); +void drm_unhold_object_locked(struct drm_obj *); +void drm_unhold_object(struct drm_obj *); +int drm_try_hold_object(struct drm_obj *); +void drm_unhold_and_unref(struct drm_obj *); +int drm_handle_create(struct drm_file *, struct drm_obj *, int *); +struct drm_obj *drm_gem_object_lookup(struct drm_device *, + struct drm_file *, int ); +int drm_gem_close_ioctl(struct drm_device *, void *, struct drm_file *); +int drm_gem_flink_ioctl(struct drm_device *, void *, struct drm_file *); +int drm_gem_open_ioctl(struct drm_device *, void *, struct drm_file *); +int drm_gem_load_uao(bus_dma_tag_t, bus_dmamap_t, struct uvm_object *, + bus_size_t, int, bus_dma_segment_t **); + +static __inline void +drm_gem_object_reference(struct drm_obj *obj) +{ + drm_ref(&obj->uobj); +} + +static __inline void +drm_gem_object_unreference(struct drm_obj *obj) +{ + drm_unref(&obj->uobj); +} + +static __inline void +drm_lock_obj(struct drm_obj *obj) +{ +#if !defined(__NetBSD__) + simple_lock(&obj->uobj); +#else /* !defined(__NetBSD__) */ + mutex_enter(&obj->uobj.vmobjlock); +#endif /* !defined(__NetBSD__) */ +} + +static __inline void +drm_unlock_obj(struct drm_obj *obj) +{ +#if !defined(__NetBSD__) + simple_unlock(&obj->uobj); +#else /* !defined(__NetBSD__) */ + mutex_exit(&obj->uobj.vmobjlock); +#endif /* !defined(__NetBSD__) */ +} +#ifdef DRMLOCKDEBUG + +#define DRM_ASSERT_HELD(obj) \ + KASSERT(obj->do_flags & DRM_BUSY && obj->holding_proc == curproc) +#if !defined(__NetBSD__) +#define DRM_OBJ_ASSERT_LOCKED(obj) /* XXX mutexes */ +#else /* !defined(__NetBSD__) */ +#define DRM_OBJ_ASSERT_LOCKED(obj) KASSERT(mutex_owned(&obj->uobj.vmobjlock)) +#endif /* !defined(__NetBSD__) */ +#define DRM_ASSERT_LOCKED(lock) MUTEX_ASSERT_LOCKED(lock) +#else + +#define DRM_ASSERT_HELD(obj) +#define DRM_OBJ_ASSERT_LOCKED(obj) +#define DRM_ASSERT_LOCKED(lock) + +#endif + + +#endif /* __KERNEL__ */ +#endif /* _DRM_P_H_ */ diff -Naurp old/src/sys/dev/pci/drm/drm_sarea.h new/src/sys/dev/pci/drm/drm_sarea.h --- old/src/sys/dev/pci/drm/drm_sarea.h 1970-01-01 01:00:00.000000000 +0100 +++ new/src/sys/dev/pci/drm/drm_sarea.h 2011-02-08 11:14:00.000000000 +0100 @@ -0,0 +1,84 @@ +/** + * \file drm_sarea.h + * \brief SAREA definitions + * + * \author Michel D�zer + */ + +/* + * Copyright 2002 Tungsten Graphics, Inc., Cedar Park, Texas. + * 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 + * TUNGSTEN GRAPHICS 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. + */ + +#ifndef _DRM_SAREA_H_ +#define _DRM_SAREA_H_ + +#include "drm.h" + +/* SAREA area needs to be at least a page */ +#if defined(__alpha__) +#define SAREA_MAX 0x2000 +#elif defined(__ia64__) +#define SAREA_MAX 0x10000 /* 64kB */ +#else +/* Intel 830M driver needs at least 8k SAREA */ +#define SAREA_MAX 0x2000UL +#endif + +/** Maximum number of drawables in the SAREA */ +#define SAREA_MAX_DRAWABLES 256 + +#define SAREA_DRAWABLE_CLAIMED_ENTRY 0x80000000 + +/** SAREA drawable */ +struct drm_sarea_drawable { + unsigned int stamp; + unsigned int flags; +}; + +/** SAREA frame */ +struct drm_sarea_frame { + unsigned int x; + unsigned int y; + unsigned int width; + unsigned int height; + unsigned int fullscreen; +}; + +/** SAREA */ +struct drm_sarea { + /** first thing is always the DRM locking structure */ + struct drm_hw_lock lock; + /** \todo Use readers/writer lock for drm_sarea::drawable_lock */ + struct drm_hw_lock drawable_lock; + struct drm_sarea_drawable drawableTable[SAREA_MAX_DRAWABLES]; /**< drawables */ + struct drm_sarea_frame frame; /**< frame */ + drm_context_t dummy_context; +}; + +#ifndef __KERNEL__ +typedef struct drm_sarea_drawable drm_sarea_drawable_t; +typedef struct drm_sarea_frame drm_sarea_frame_t; +typedef struct drm_sarea drm_sarea_t; +#endif + +#endif /* _DRM_SAREA_H_ */ diff -Naurp old/src/sys/dev/pci/drm/drm_scatter.c new/src/sys/dev/pci/drm/drm_scatter.c --- old/src/sys/dev/pci/drm/drm_scatter.c 1970-01-01 01:00:00.000000000 +0100 +++ new/src/sys/dev/pci/drm/drm_scatter.c 2011-02-08 11:14:00.000000000 +0100 @@ -0,0 +1,118 @@ +/*- + * 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: + * Gareth Hughes + * Eric Anholt + * + */ + +/* + * Allocation of memory for scatter-gather mappings by the graphics chip. + * + * The memory allocated here is then made into an aperture in the card + * by drm_ati_pcigart_init(). + */ +#include "drmP.h" + +void +drm_sg_cleanup(struct drm_device *dev, struct drm_sg_mem *entry) +{ + if (entry == NULL) + return; + + drm_dmamem_free(dev->dmat, entry->mem); + drm_free(entry); +} + +int +drm_sg_alloc(struct drm_device * dev, struct drm_scatter_gather *request) +{ + struct drm_sg_mem *entry; + bus_size_t size; + unsigned long pages; + + if (dev->sg != NULL) + return (EINVAL); + + entry = drm_calloc(1, sizeof(*entry)); + if (entry == NULL) + return (ENOMEM); + + pages = round_page(request->size) / PAGE_SIZE; + size = pages << PAGE_SHIFT; + + DRM_DEBUG("sg size=%ld pages=%ld\n", request->size, pages); + + if ((entry->mem = drm_dmamem_alloc(dev->dmat, size, PAGE_SIZE, pages, + PAGE_SIZE, 0, 0)) == NULL) + return (ENOMEM); + + request->handle = entry->handle = (unsigned long)entry->mem->kva; + DRM_DEBUG("sg alloc handle = %08lx\n", entry->handle); + + DRM_LOCK(); + if (dev->sg) { + DRM_UNLOCK(); + drm_sg_cleanup(dev, entry); + return EINVAL; + } + dev->sg = entry; + DRM_UNLOCK(); + + return (0); +} + +int +drm_sg_alloc_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_scatter_gather *request = data; + int ret; + + DRM_DEBUG("\n"); + + ret = drm_sg_alloc(dev, request); + return ret; +} + +int +drm_sg_free(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_scatter_gather *request = data; + struct drm_sg_mem *entry; + + DRM_LOCK(); + entry = dev->sg; + dev->sg = NULL; + DRM_UNLOCK(); + + if (entry == NULL || entry->handle != request->handle) + return EINVAL; + + DRM_DEBUG("sg free virtual = 0x%lx\n", entry->handle); + + drm_sg_cleanup(dev, entry); + + return 0; +} diff -Naurp old/src/sys/dev/pci/drm/files.drm new/src/sys/dev/pci/drm/files.drm --- old/src/sys/dev/pci/drm/files.drm 1970-01-01 01:00:00.000000000 +0100 +++ new/src/sys/dev/pci/drm/files.drm 2011-02-08 11:14:00.000000000 +0100 @@ -0,0 +1,20 @@ +# $NetBSD: files.drm,v 1.2 2007/03/28 11:29:37 jmcneill Exp $ +# $OpenBSD: files.drm,v 1.20 2010/05/25 17:15:49 oga Exp $ + +# direct rendering modules +define drmbase {} +device drmdev: drmbase +attach drmdev at drmbase +file dev/pci/drm/drm_agpsupport.c drmdev +file dev/pci/drm/drm_bufs.c drmdev +file dev/pci/drm/drm_context.c drmdev +file dev/pci/drm/drm_drv.c drmdev needs-flag +file dev/pci/drm/drm_irq.c drmdev +file dev/pci/drm/drm_lock.c drmdev +file dev/pci/drm/drm_memory.c drmdev +file dev/pci/drm/drm_scatter.c drmdev + +device inteldrm: drmbase +attach inteldrm at drm +file dev/pci/drm/i915_drv.c inteldrm +file dev/pci/drm/i915_irq.c inteldrm diff -Naurp old/src/sys/dev/pci/drm/i915_drm.h new/src/sys/dev/pci/drm/i915_drm.h --- old/src/sys/dev/pci/drm/i915_drm.h 1970-01-01 01:00:00.000000000 +0100 +++ new/src/sys/dev/pci/drm/i915_drm.h 2011-02-08 11:14:00.000000000 +0100 @@ -0,0 +1,657 @@ +/* + * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. + * 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, sub license, 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 NON-INFRINGEMENT. + * IN NO EVENT SHALL TUNGSTEN GRAPHICS 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. + * + */ + +#ifndef _I915_DRM_H_ +#define _I915_DRM_H_ + +/* Please note that modifications to all structs defined here are + * subject to backwards-compatibility constraints. + */ + +#include "drm.h" + +/* Each region is a minimum of 16k, and there are at most 255 of them. + */ +#define I915_NR_TEX_REGIONS 255 /* table size 2k - maximum due to use + * of chars for next/prev indices */ +#define I915_LOG_MIN_TEX_REGION_SIZE 14 + +typedef struct _drm_i915_init { + enum { + I915_INIT_DMA = 0x01, + I915_CLEANUP_DMA = 0x02, + I915_RESUME_DMA = 0x03 + } func; + unsigned int mmio_offset; + int sarea_priv_offset; + unsigned int ring_start; + unsigned int ring_end; + unsigned int ring_size; + unsigned int front_offset; + unsigned int back_offset; + unsigned int depth_offset; + unsigned int w; + unsigned int h; + unsigned int pitch; + unsigned int pitch_bits; + unsigned int back_pitch; + unsigned int depth_pitch; + unsigned int cpp; + unsigned int chipset; +} drm_i915_init_t; + +typedef struct drm_i915_sarea { + struct drm_tex_region texList[I915_NR_TEX_REGIONS + 1]; + int last_upload; /* last time texture was uploaded */ + int last_enqueue; /* last time a buffer was enqueued */ + int last_dispatch; /* age of the most recently dispatched buffer */ + int ctxOwner; /* last context to upload state */ + int texAge; + int pf_enabled; /* is pageflipping allowed? */ + int pf_active; + int pf_current_page; /* which buffer is being displayed? */ + int perf_boxes; /* performance boxes to be displayed */ + int width, height; /* screen size in pixels */ + + drm_handle_t front_handle; + int front_offset; + int front_size; + + drm_handle_t back_handle; + int back_offset; + int back_size; + + drm_handle_t depth_handle; + int depth_offset; + int depth_size; + + drm_handle_t tex_handle; + int tex_offset; + int tex_size; + int log_tex_granularity; + int pitch; + int rotation; /* 0, 90, 180 or 270 */ + int rotated_offset; + int rotated_size; + int rotated_pitch; + int virtualX, virtualY; + + unsigned int front_tiled; + unsigned int back_tiled; + unsigned int depth_tiled; + unsigned int rotated_tiled; + unsigned int rotated2_tiled; + + int planeA_x; + int planeA_y; + int planeA_w; + int planeA_h; + int planeB_x; + int planeB_y; + int planeB_w; + int planeB_h; + + /* Triple buffering */ + drm_handle_t third_handle; + int third_offset; + int third_size; + unsigned int third_tiled; + + /* buffer object handles for the static buffers. May change + * over the lifetime of the client, though it doesn't in our current + * implementation. + */ + unsigned int front_bo_handle; + unsigned int back_bo_handle; + unsigned int third_bo_handle; + unsigned int depth_bo_handle; +} drm_i915_sarea_t; + +/* Flags for perf_boxes + */ +#define I915_BOX_RING_EMPTY 0x1 +#define I915_BOX_FLIP 0x2 +#define I915_BOX_WAIT 0x4 +#define I915_BOX_TEXTURE_LOAD 0x8 +#define I915_BOX_LOST_CONTEXT 0x10 + +/* I915 specific ioctls + * The device specific ioctl range is 0x40 to 0x79. + */ +#define DRM_I915_INIT 0x00 +#define DRM_I915_FLUSH 0x01 +#define DRM_I915_FLIP 0x02 +#define DRM_I915_BATCHBUFFER 0x03 +#define DRM_I915_IRQ_EMIT 0x04 +#define DRM_I915_IRQ_WAIT 0x05 +#define DRM_I915_GETPARAM 0x06 +#define DRM_I915_SETPARAM 0x07 +#define DRM_I915_ALLOC 0x08 +#define DRM_I915_FREE 0x09 +#define DRM_I915_INIT_HEAP 0x0a +#define DRM_I915_CMDBUFFER 0x0b +#define DRM_I915_DESTROY_HEAP 0x0c +#define DRM_I915_SET_VBLANK_PIPE 0x0d +#define DRM_I915_GET_VBLANK_PIPE 0x0e +#define DRM_I915_VBLANK_SWAP 0x0f +#define DRM_I915_HWS_ADDR 0x11 +#define DRM_I915_GEM_INIT 0x13 +#define DRM_I915_GEM_EXECBUFFER 0x14 +#define DRM_I915_GEM_PIN 0x15 +#define DRM_I915_GEM_UNPIN 0x16 +#define DRM_I915_GEM_BUSY 0x17 +#define DRM_I915_GEM_THROTTLE 0x18 +#define DRM_I915_GEM_ENTERVT 0x19 +#define DRM_I915_GEM_LEAVEVT 0x1a +#define DRM_I915_GEM_CREATE 0x1b +#define DRM_I915_GEM_PREAD 0x1c +#define DRM_I915_GEM_PWRITE 0x1d +#define DRM_I915_GEM_MMAP 0x1e +#define DRM_I915_GEM_SET_DOMAIN 0x1f +#define DRM_I915_GEM_SW_FINISH 0x20 +#define DRM_I915_GEM_SET_TILING 0x21 +#define DRM_I915_GEM_GET_TILING 0x22 +#define DRM_I915_GEM_GET_APERTURE 0x23 +#define DRM_I915_GEM_EXECBUFFER2 0x24 +#define DRM_I915_GEM_MADVISE 0x25 + +#define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) +#define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) +#define DRM_IOCTL_I915_FLIP DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLIP) +#define DRM_IOCTL_I915_BATCHBUFFER DRM_IOW( DRM_COMMAND_BASE + DRM_I915_BATCHBUFFER, drm_i915_batchbuffer_t) +#define DRM_IOCTL_I915_IRQ_EMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_IRQ_EMIT, drm_i915_irq_emit_t) +#define DRM_IOCTL_I915_IRQ_WAIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_IRQ_WAIT, drm_i915_irq_wait_t) +#define DRM_IOCTL_I915_GETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GETPARAM, drm_i915_getparam_t) +#define DRM_IOCTL_I915_SETPARAM DRM_IOW( DRM_COMMAND_BASE + DRM_I915_SETPARAM, drm_i915_setparam_t) +#define DRM_IOCTL_I915_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_ALLOC, drm_i915_mem_alloc_t) +#define DRM_IOCTL_I915_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_I915_FREE, drm_i915_mem_free_t) +#define DRM_IOCTL_I915_INIT_HEAP DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT_HEAP, drm_i915_mem_init_heap_t) +#define DRM_IOCTL_I915_CMDBUFFER DRM_IOW( DRM_COMMAND_BASE + DRM_I915_CMDBUFFER, drm_i915_cmdbuffer_t) +#define DRM_IOCTL_I915_DESTROY_HEAP DRM_IOW( DRM_COMMAND_BASE + DRM_I915_DESTROY_HEAP, drm_i915_mem_destroy_heap_t) +#define DRM_IOCTL_I915_SET_VBLANK_PIPE DRM_IOW( DRM_COMMAND_BASE + DRM_I915_SET_VBLANK_PIPE, drm_i915_vblank_pipe_t) +#define DRM_IOCTL_I915_GET_VBLANK_PIPE DRM_IOR( DRM_COMMAND_BASE + DRM_I915_GET_VBLANK_PIPE, drm_i915_vblank_pipe_t) +#define DRM_IOCTL_I915_VBLANK_SWAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_VBLANK_SWAP, drm_i915_vblank_swap_t) +#define DRM_IOCTL_I915_HWS_ADDR DRM_IOW(DRM_COMMAND_BASE + DRM_I915_HWS_ADDR, drm_i915_hws_addr_t) +#define DRM_IOCTL_I915_GEM_INIT DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_INIT, struct drm_i915_gem_init) +#define DRM_IOCTL_I915_GEM_EXECBUFFER DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER, struct drm_i915_gem_execbuffer) +#define DRM_IOCTL_I915_GEM_EXECBUFFER2 DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER2, struct drm_i915_gem_execbuffer2) +#define DRM_IOCTL_I915_GEM_PIN DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_PIN, struct drm_i915_gem_pin) +#define DRM_IOCTL_I915_GEM_UNPIN DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_UNPIN, struct drm_i915_gem_unpin) +#define DRM_IOCTL_I915_GEM_BUSY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_BUSY, struct drm_i915_gem_busy) +#define DRM_IOCTL_I915_GEM_THROTTLE DRM_IO ( DRM_COMMAND_BASE + DRM_I915_GEM_THROTTLE) +#define DRM_IOCTL_I915_GEM_ENTERVT DRM_IO(DRM_COMMAND_BASE + DRM_I915_GEM_ENTERVT) +#define DRM_IOCTL_I915_GEM_LEAVEVT DRM_IO(DRM_COMMAND_BASE + DRM_I915_GEM_LEAVEVT) +#define DRM_IOCTL_I915_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_CREATE, struct drm_i915_gem_create) +#define DRM_IOCTL_I915_GEM_PREAD DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PREAD, struct drm_i915_gem_pread) +#define DRM_IOCTL_I915_GEM_PWRITE DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PWRITE, struct drm_i915_gem_pwrite) +#define DRM_IOCTL_I915_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP, struct drm_i915_gem_mmap) +#define DRM_IOCTL_I915_GEM_SET_DOMAIN DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SET_DOMAIN, struct drm_i915_gem_set_domain) +#define DRM_IOCTL_I915_GEM_SW_FINISH DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SW_FINISH, struct drm_i915_gem_sw_finish) +#define DRM_IOCTL_I915_GEM_SET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_SET_TILING, struct drm_i915_gem_set_tiling) +#define DRM_IOCTL_I915_GEM_GET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_TILING, struct drm_i915_gem_get_tiling) +#define DRM_IOCTL_I915_GEM_GET_APERTURE DRM_IOR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_APERTURE, struct drm_i915_gem_get_aperture) +#define DRM_IOCTL_I915_GEM_MADVISE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MADVISE, struct drm_i915_gem_madvise) + +/* Allow drivers to submit batchbuffers directly to hardware, relying + * on the security mechanisms provided by hardware. + */ +typedef struct drm_i915_batchbuffer { + int start; /* agp offset */ + int used; /* nr bytes in use */ + int DR1; /* hw flags for GFX_OP_DRAWRECT_INFO */ + int DR4; /* window origin for GFX_OP_DRAWRECT_INFO */ + int num_cliprects; /* mulitpass with multiple cliprects? */ + struct drm_clip_rect __user *cliprects; /* pointer to userspace cliprects */ +} drm_i915_batchbuffer_t; + +/* As above, but pass a pointer to userspace buffer which can be + * validated by the kernel prior to sending to hardware. + */ +typedef struct _drm_i915_cmdbuffer { + char __user *buf; /* pointer to userspace command buffer */ + int sz; /* nr bytes in buf */ + int DR1; /* hw flags for GFX_OP_DRAWRECT_INFO */ + int DR4; /* window origin for GFX_OP_DRAWRECT_INFO */ + int num_cliprects; /* mulitpass with multiple cliprects? */ + struct drm_clip_rect __user *cliprects; /* pointer to userspace cliprects */ +} drm_i915_cmdbuffer_t; + +/* Userspace can request & wait on irq's: + */ +typedef struct drm_i915_irq_emit { + int __user *irq_seq; +} drm_i915_irq_emit_t; + +typedef struct drm_i915_irq_wait { + int irq_seq; +} drm_i915_irq_wait_t; + +/* Ioctl to query kernel params: + */ +#define I915_PARAM_IRQ_ACTIVE 1 +#define I915_PARAM_ALLOW_BATCHBUFFER 2 +#define I915_PARAM_LAST_DISPATCH 3 +#define I915_PARAM_CHIPSET_ID 4 +#define I915_PARAM_HAS_GEM 5 +#define I915_PARAM_NUM_FENCES_AVAIL 6 +#define I915_PARAM_HAS_EXECBUF2 9 + +typedef struct drm_i915_getparam { + int param; + int __user *value; +} drm_i915_getparam_t; + +/* Ioctl to set kernel params: + */ +#define I915_SETPARAM_USE_MI_BATCHBUFFER_START 1 +#define I915_SETPARAM_TEX_LRU_LOG_GRANULARITY 2 +#define I915_SETPARAM_ALLOW_BATCHBUFFER 3 +#define I915_SETPARAM_NUM_USED_FENCES 4 + +typedef struct drm_i915_setparam { + int param; + int value; +} drm_i915_setparam_t; + +/* A memory manager for regions of shared memory: + */ +#define I915_MEM_REGION_AGP 1 + +typedef struct drm_i915_mem_alloc { + int region; + int alignment; + int size; + int __user *region_offset; /* offset from start of fb or agp */ +} drm_i915_mem_alloc_t; + +typedef struct drm_i915_mem_free { + int region; + int region_offset; +} drm_i915_mem_free_t; + +typedef struct drm_i915_mem_init_heap { + int region; + int size; + int start; +} drm_i915_mem_init_heap_t; + +/* Allow memory manager to be torn down and re-initialized (eg on + * rotate): + */ +typedef struct drm_i915_mem_destroy_heap { + int region; +} drm_i915_mem_destroy_heap_t; + +/* Allow X server to configure which pipes to monitor for vblank signals + */ +#define DRM_I915_VBLANK_PIPE_A 1 +#define DRM_I915_VBLANK_PIPE_B 2 + +typedef struct drm_i915_vblank_pipe { + int pipe; +} drm_i915_vblank_pipe_t; + +/* Schedule buffer swap at given vertical blank: + */ +typedef struct drm_i915_vblank_swap { + drm_drawable_t drawable; + enum drm_vblank_seq_type seqtype; + unsigned int sequence; +} drm_i915_vblank_swap_t; + +typedef struct drm_i915_hws_addr { + uint64_t addr; +} drm_i915_hws_addr_t; + +struct drm_i915_gem_init { + /** + * Beginning offset in the GTT to be managed by the DRM memory + * manager. + */ + uint64_t gtt_start; + /** + * Ending offset in the GTT to be managed by the DRM memory + * manager. + */ + uint64_t gtt_end; +}; + +struct drm_i915_gem_create { + /** + * Requested size for the object. + * + * The (page-aligned) allocated size for the object will be returned. + */ + uint64_t size; + /** + * Returned handle for the object. + * + * Object handles are nonzero. + */ + uint32_t handle; + uint32_t pad; +}; + +struct drm_i915_gem_pread { + /** Handle for the object being read. */ + uint32_t handle; + uint32_t pad; + /** Offset into the object to read from */ + uint64_t offset; + /** Length of data to read */ + uint64_t size; + /** + * Pointer to write the data into. + * + * This is a fixed-size type for 32/64 compatibility. + */ + uint64_t data_ptr; +}; + +struct drm_i915_gem_pwrite { + /** Handle for the object being written to. */ + uint32_t handle; + uint32_t pad; + /** Offset into the object to write to */ + uint64_t offset; + /** Length of data to write */ + uint64_t size; + /** + * Pointer to read the data from. + * + * This is a fixed-size type for 32/64 compatibility. + */ + uint64_t data_ptr; +}; + +struct drm_i915_gem_mmap { + /** Handle for the object being mapped. */ + uint32_t handle; + uint32_t pad; + /** Offset in the object to map. */ + uint64_t offset; + /** + * Length of data to map. + * + * The value will be page-aligned. + */ + uint64_t size; + /** + * Returned pointer the data was mapped at. + * + * This is a fixed-size type for 32/64 compatibility. + */ + uint64_t addr_ptr; +}; + +struct drm_i915_gem_set_domain { + /** Handle for the object */ + uint32_t handle; + + /** New read domains */ + uint32_t read_domains; + + /** New write domain */ + uint32_t write_domain; +}; + +struct drm_i915_gem_sw_finish { + /** Handle for the object */ + uint32_t handle; +}; + +struct drm_i915_gem_relocation_entry { + /** + * Handle of the buffer being pointed to by this relocation entry. + * + * It's appealing to make this be an index into the mm_validate_entry + * list to refer to the buffer, but this allows the driver to create + * a relocation list for state buffers and not re-write it per + * exec using the buffer. + */ + uint32_t target_handle; + + /** + * Value to be added to the offset of the target buffer to make up + * the relocation entry. + */ + uint32_t delta; + + /** Offset in the buffer the relocation entry will be written into */ + uint64_t offset; + + /** + * Offset value of the target buffer that the relocation entry was last + * written as. + * + * If the buffer has the same offset as last time, we can skip syncing + * and writing the relocation. This value is written back out by + * the execbuffer ioctl when the relocation is written. + */ + uint64_t presumed_offset; + + /** + * Target memory domains read by this operation. + */ + uint32_t read_domains; + + /** + * Target memory domains written by this operation. + * + * Note that only one domain may be written by the whole + * execbuffer operation, so that where there are conflicts, + * the application will get -EINVAL back. + */ + uint32_t write_domain; +}; + +/** @{ + * Intel memory domains + * + * Most of these just align with the various caches in + * the system and are used to flush and invalidate as + * objects end up cached in different domains. + */ +/** CPU cache */ +#define I915_GEM_DOMAIN_CPU 0x00000001 +/** Render cache, used by 2D and 3D drawing */ +#define I915_GEM_DOMAIN_RENDER 0x00000002 +/** Sampler cache, used by texture engine */ +#define I915_GEM_DOMAIN_SAMPLER 0x00000004 +/** Command queue, used to load batch buffers */ +#define I915_GEM_DOMAIN_COMMAND 0x00000008 +/** Instruction cache, used by shader programs */ +#define I915_GEM_DOMAIN_INSTRUCTION 0x00000010 +/** Vertex address cache */ +#define I915_GEM_DOMAIN_VERTEX 0x00000020 +/** GTT domain - aperture and scanout */ +#define I915_GEM_DOMAIN_GTT 0x00000040 +/** @} */ + +struct drm_i915_gem_exec_object2 { + /** + * User's handle for a buffer to be bound into the GTT for this + * operation. + */ + u_int32_t handle; + + /** Number of relocations to be performed on this buffer */ + u_int32_t relocation_count; + /** + * Pointer to array of struct drm_i915_gem_relocation_entry containing + * the relocations to be performed in this buffer. + */ + u_int64_t relocs_ptr; + + /** Required alignment in graphics aperture */ + u_int64_t alignment; + + /** + * Returned value of the updated offset of the object, for future + * presumed_offset writes. + */ + u_int64_t offset; + +#define EXEC_OBJECT_NEEDS_FENCE (1<<0) + u_int64_t flags; + u_int64_t rsvd1; + u_int64_t rsvd2; +}; + +struct drm_i915_gem_execbuffer2 { + /** + * List of gem_exec_object2 structs + */ + u_int64_t buffers_ptr; + u_int32_t buffer_count; + + /** Offset in the batchbuffer to start execution from. */ + u_int32_t batch_start_offset; + /** Bytes used in batchbuffer from batch_start_offset */ + u_int32_t batch_len; + u_int64_t flags; /* currently unused */ + u_int64_t rsvd1; + u_int64_t rsvd2; +}; + +struct drm_i915_gem_pin { + /** Handle of the buffer to be pinned. */ + uint32_t handle; + uint32_t pad; + + /** alignment required within the aperture */ + uint64_t alignment; + + /** Returned GTT offset of the buffer. */ + uint64_t offset; +}; + +struct drm_i915_gem_unpin { + /** Handle of the buffer to be unpinned. */ + uint32_t handle; + uint32_t pad; +}; + +struct drm_i915_gem_busy { + /** Handle of the buffer to check for busy */ + uint32_t handle; + + /** Return busy status (1 if busy, 0 if idle) */ + uint32_t busy; +}; + +#define I915_TILING_NONE 0 +#define I915_TILING_X 1 +#define I915_TILING_Y 2 + +#define I915_BIT_6_SWIZZLE_NONE 0 +#define I915_BIT_6_SWIZZLE_9 1 +#define I915_BIT_6_SWIZZLE_9_10 2 +#define I915_BIT_6_SWIZZLE_9_11 3 +#define I915_BIT_6_SWIZZLE_9_10_11 4 +/* Not seen by userland */ +#define I915_BIT_6_SWIZZLE_UNKNOWN 5 +/* Seen by userland. */ +#define I915_BIT_6_SWIZZLE_9_17 6 +#define I915_BIT_6_SWIZZLE_9_10_17 7 + +struct drm_i915_gem_set_tiling { + /** Handle of the buffer to have its tiling state updated */ + uint32_t handle; + + /** + * Tiling mode for the object (I915_TILING_NONE, I915_TILING_X, + * I915_TILING_Y). + * + * This value is to be set on request, and will be updated by the + * kernel on successful return with the actual chosen tiling layout. + * + * The tiling mode may be demoted to I915_TILING_NONE when the system + * has bit 6 swizzling that can't be managed correctly by GEM. + * + * Buffer contents become undefined when changing tiling_mode. + */ + uint32_t tiling_mode; + + /** + * Stride in bytes for the object when in I915_TILING_X or + * I915_TILING_Y. + */ + uint32_t stride; + + /** + * Returned address bit 6 swizzling required for CPU access through + * mmap mapping. + */ + uint32_t swizzle_mode; +}; + +struct drm_i915_gem_get_tiling { + /** Handle of the buffer to get tiling state for. */ + uint32_t handle; + + /** + * Current tiling mode for the object (I915_TILING_NONE, I915_TILING_X, + * I915_TILING_Y). + */ + uint32_t tiling_mode; + + /** + * Returned address bit 6 swizzling required for CPU access through + * mmap mapping. + */ + uint32_t swizzle_mode; +}; + +struct drm_i915_gem_get_aperture { + /** Total size of the aperture used by i915_gem_execbuffer, in bytes */ + uint64_t aper_size; + + /** + * Available space in the aperture used by i915_gem_execbuffer, in + * bytes + */ + uint64_t aper_available_size; +}; + +#define I915_MADV_WILLNEED 0 +#define I915_MADV_DONTNEED 1 +#define __I915_MADV_PURGED 2 /* internal state */ + +struct drm_i915_gem_madvise { + /** Handle of the buffer to change the backing store advice */ + u_int32_t handle; + + /* Advice: either the buffer will be needed again in the near future, + * or wont be and could be discarded under memory pressure. + */ + u_int32_t madv; + + /** Whether the backing store still exists. */ + u_int32_t retained; +}; + +#endif /* _I915_DRM_H_ */ diff -Naurp old/src/sys/dev/pci/drm/i915_drv.c new/src/sys/dev/pci/drm/i915_drv.c --- old/src/sys/dev/pci/drm/i915_drv.c 1970-01-01 01:00:00.000000000 +0100 +++ new/src/sys/dev/pci/drm/i915_drv.c 2011-02-08 11:14:00.000000000 +0100 @@ -0,0 +1,6500 @@ +/* + * Copyright (c) 2008-2009 Owain G. Ainsworth + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/*- + * Copyright © 2008 Intel Corporation + * Copyright 2003 Tungsten Graphics, 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 + * VA LINUX SYSTEMS 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: + * Gareth Hughes + * Eric Anholt + * + */ + +#include "drmP.h" +#include "drm.h" +#include "i915_drm.h" +#include "i915_drv.h" + +#if !defined(__NetBSD__) +#include +#else /* !defined(__NetBSD__) */ +#include +#endif /* !defined(__NetBSD__) */ + +#include +#if !defined(__NetBSD__) +#include +#else +#include +#include +#endif +#if 0 +# define INTELDRM_WATCH_COHERENCY +# define WATCH_INACTIVE +#endif + +#define I915_GEM_GPU_DOMAINS (~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT)) + +#if !defined(__NetBSD__) +int inteldrm_probe(struct device *, void *, void *); +#else /* !defined(__NetBSD__) */ +int inteldrm_probe(struct device *, struct cfdata *, void *); +#endif /* !defined(__NetBSD__) */ +void inteldrm_attach(struct device *, struct device *, void *); +int inteldrm_detach(struct device *, int); +#if !defined(__NetBSD__) +int inteldrm_activate(struct device *, int); +#else /* !defined(__NetBSD__) */ +static bool inteldrm_suspend(struct device *, const pmf_qual_t *); +static bool inteldrm_resume(struct device *, const pmf_qual_t *); +#endif /* !defined(__NetBSD__) */ +int inteldrm_ioctl(struct drm_device *, u_long, caddr_t, struct drm_file *); +int inteldrm_doioctl(struct drm_device *, u_long, caddr_t, struct drm_file *); +int inteldrm_intr(void *); +void inteldrm_error(struct inteldrm_softc *); +int inteldrm_ironlake_intr(void *); +void inteldrm_lastclose(struct drm_device *); + +void inteldrm_wrap_ring(struct inteldrm_softc *); +int inteldrm_gmch_match(struct pci_attach_args *); +void inteldrm_chipset_flush(struct inteldrm_softc *); +void inteldrm_timeout(void *); +void inteldrm_hangcheck(void *); +void inteldrm_hung(void *, void *); +void inteldrm_965_reset(struct inteldrm_softc *, u_int8_t); +int inteldrm_fault(struct drm_obj *, struct uvm_faultinfo *, off_t, + vaddr_t, vm_page_t *, int, int, vm_prot_t, int ); +void inteldrm_wipe_mappings(struct drm_obj *); +void inteldrm_purge_obj(struct drm_obj *); +void inteldrm_set_max_obj_size(struct inteldrm_softc *); +void inteldrm_quiesce(struct inteldrm_softc *); + +/* For reset and suspend */ +int inteldrm_save_state(struct inteldrm_softc *); +int inteldrm_restore_state(struct inteldrm_softc *); +int inteldrm_save_display(struct inteldrm_softc *); +int inteldrm_restore_display(struct inteldrm_softc *); +void i915_save_vga(struct inteldrm_softc *); +void i915_restore_vga(struct inteldrm_softc *); +void i915_save_modeset_reg(struct inteldrm_softc *); +void i915_restore_modeset_reg(struct inteldrm_softc *); +u_int8_t i915_read_indexed(struct inteldrm_softc *, u_int16_t, + u_int16_t, u_int8_t); +void i915_write_indexed(struct inteldrm_softc *, u_int16_t, + u_int16_t, u_int8_t, u_int8_t); +void i915_write_ar(struct inteldrm_softc *, u_int16_t, u_int8_t, + u_int8_t, u_int16_t); +u_int8_t i915_read_ar(struct inteldrm_softc *, u_int16_t, + u_int8_t, u_int16_t); +void i915_save_palette(struct inteldrm_softc *, enum pipe); +void i915_restore_palette(struct inteldrm_softc *, enum pipe); + +void i915_alloc_ifp(struct inteldrm_softc *, struct pci_attach_args *); +void i965_alloc_ifp(struct inteldrm_softc *, struct pci_attach_args *); + +void inteldrm_detect_bit_6_swizzle(struct inteldrm_softc *, + struct pci_attach_args *); + +#if !defined(__NetBSD__) +int inteldrm_setup_mchbar(struct inteldrm_softc *, + struct pci_attach_args *); +void inteldrm_teardown_mchbar(struct inteldrm_softc *, + struct pci_attach_args *, int); +#else /* !defined(__NetBSD__) */ +int inteldrm_setup_mchbar(struct inteldrm_softc *, + struct pci_attach_args *, bus_space_handle_t *); +void inteldrm_teardown_mchbar(struct inteldrm_softc *, + struct pci_attach_args *, int, bus_space_handle_t); +#endif /* !defined(__NetBSD__) */ + +/* Ioctls */ +int inteldrm_getparam(struct inteldrm_softc *dev_priv, void *data); +int inteldrm_setparam(struct inteldrm_softc *dev_priv, void *data); +int i915_gem_init_ioctl(struct drm_device *, void *, struct drm_file *); +int i915_gem_create_ioctl(struct drm_device *, void *, struct drm_file *); +int i915_gem_pread_ioctl(struct drm_device *, void *, struct drm_file *); +int i915_gem_pwrite_ioctl(struct drm_device *, void *, struct drm_file *); +int i915_gem_set_domain_ioctl(struct drm_device *, void *, + struct drm_file *); +int i915_gem_execbuffer2(struct drm_device *, void *, struct drm_file *); +int i915_gem_pin_ioctl(struct drm_device *, void *, struct drm_file *); +int i915_gem_unpin_ioctl(struct drm_device *, void *, struct drm_file *); +int i915_gem_busy_ioctl(struct drm_device *, void *, struct drm_file *); +int i915_gem_entervt_ioctl(struct drm_device *, void *, struct drm_file *); +int i915_gem_leavevt_ioctl(struct drm_device *, void *, struct drm_file *); +int i915_gem_get_aperture_ioctl(struct drm_device *, void *, + struct drm_file *); +int i915_gem_set_tiling(struct drm_device *, void *, struct drm_file *); +int i915_gem_get_tiling(struct drm_device *, void *, struct drm_file *); +int i915_gem_gtt_map_ioctl(struct drm_device *, void *, struct drm_file *); +int i915_gem_madvise_ioctl(struct drm_device *, void *, struct drm_file *); + +/* GEM memory manager functions */ +int i915_gem_init_object(struct drm_obj *); +void i915_gem_free_object(struct drm_obj *); +int i915_gem_object_pin(struct drm_obj *, uint32_t, int); +void i915_gem_object_unpin(struct drm_obj *); +void i915_gem_retire_requests(struct inteldrm_softc *); +void i915_gem_retire_request(struct inteldrm_softc *, + struct inteldrm_request *); +void i915_gem_retire_work_handler(void *, void*); +int i915_gem_idle(struct inteldrm_softc *); +void i915_gem_object_move_to_active(struct drm_obj *); +void i915_gem_object_move_off_active(struct drm_obj *); +void i915_gem_object_move_to_inactive(struct drm_obj *); +void i915_gem_object_move_to_inactive_locked(struct drm_obj *); +uint32_t i915_add_request(struct inteldrm_softc *); +void inteldrm_process_flushing(struct inteldrm_softc *, u_int32_t); +void i915_move_to_tail(struct inteldrm_obj *, struct i915_gem_list *); +void i915_list_remove(struct inteldrm_obj *); +int i915_gem_init_hws(struct inteldrm_softc *); +void i915_gem_cleanup_hws(struct inteldrm_softc *); +int i915_gem_init_ringbuffer(struct inteldrm_softc *); +int inteldrm_start_ring(struct inteldrm_softc *); +void i915_gem_cleanup_ringbuffer(struct inteldrm_softc *); +int i915_gem_ring_throttle(struct drm_device *, struct drm_file *); +int i915_gem_evict_inactive(struct inteldrm_softc *, int); +int i915_gem_get_relocs_from_user(struct drm_i915_gem_exec_object2 *, + u_int32_t, struct drm_i915_gem_relocation_entry **); +int i915_gem_put_relocs_to_user(struct drm_i915_gem_exec_object2 *, + u_int32_t, struct drm_i915_gem_relocation_entry *); +void i915_dispatch_gem_execbuffer(struct drm_device *, + struct drm_i915_gem_execbuffer2 *, uint64_t); +void i915_gem_object_set_to_gpu_domain(struct drm_obj *); +int i915_gem_object_pin_and_relocate(struct drm_obj *, + struct drm_file *, struct drm_i915_gem_exec_object2 *, + struct drm_i915_gem_relocation_entry *); +int i915_gem_object_bind_to_gtt(struct drm_obj *, bus_size_t, int); +int i915_wait_request(struct inteldrm_softc *, uint32_t, int); +u_int32_t i915_gem_flush(struct inteldrm_softc *, uint32_t, uint32_t); +int i915_gem_object_unbind(struct drm_obj *, int); + +struct drm_obj *i915_gem_find_inactive_object(struct inteldrm_softc *, + size_t); + +int i915_gem_evict_everything(struct inteldrm_softc *, int); +int i915_gem_evict_something(struct inteldrm_softc *, size_t, int); +int i915_gem_object_set_to_gtt_domain(struct drm_obj *, int, int); +int i915_gem_object_set_to_cpu_domain(struct drm_obj *, int, int); +int i915_gem_object_flush_gpu_write_domain(struct drm_obj *, int, int, int); +int i915_gem_get_fence_reg(struct drm_obj *, int); +int i915_gem_object_put_fence_reg(struct drm_obj *, int); +bus_size_t i915_gem_get_gtt_alignment(struct drm_obj *); + +bus_size_t i915_get_fence_size(struct inteldrm_softc *, bus_size_t); +int i915_tiling_ok(struct drm_device *, int, int, int); +int i915_gem_object_fence_offset_ok(struct drm_obj *, int); +void i965_write_fence_reg(struct inteldrm_fence *); +void i915_write_fence_reg(struct inteldrm_fence *); +void i830_write_fence_reg(struct inteldrm_fence *); +void i915_gem_bit_17_swizzle(struct drm_obj *); +void i915_gem_save_bit_17_swizzle(struct drm_obj *); +int inteldrm_swizzle_page(struct vm_page *page); + +/* Debug functions, mostly called from ddb */ +void i915_gem_seqno_info(int); +void i915_interrupt_info(int); +void i915_gem_fence_regs_info(int); +void i915_hws_info(int); +void i915_batchbuffer_info(int); +void i915_ringbuffer_data(int); +void i915_ringbuffer_info(int); +#ifdef WATCH_INACTIVE +void inteldrm_verify_inactive(struct inteldrm_softc *, char *, int); +#else +#define inteldrm_verify_inactive(dev,file,line) +#endif + +static const struct drm_pcidev inteldrm_pciidlist[] = { + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82830M_IGD, + CHIP_I830|CHIP_M|CHIP_GEN2}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82845G_IGD, + CHIP_I845G|CHIP_GEN2}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82855GM_IGD, + CHIP_I85X|CHIP_M|CHIP_GEN2}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82865G_IGD, + CHIP_I865G|CHIP_GEN2}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82915G_IGD_1, + CHIP_I915G|CHIP_I9XX|CHIP_GEN3}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_E7221_IGD, + CHIP_I915G|CHIP_I9XX|CHIP_GEN3}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82915GM_IGD_1, + CHIP_I915GM|CHIP_I9XX|CHIP_M|CHIP_GEN3}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82945G_IGD_1, + CHIP_I945G|CHIP_I9XX|CHIP_GEN3}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82945GM_IGD_1, + CHIP_I945GM|CHIP_I9XX|CHIP_M|CHIP_GEN3}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82945GME_IGD_1, + CHIP_I945GM|CHIP_I9XX|CHIP_M|CHIP_GEN3}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82946GZ_IGD_1, + CHIP_I965|CHIP_I9XX|CHIP_GEN4}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82G35_IGD_1, + CHIP_I965|CHIP_I9XX|CHIP_GEN4}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82Q965_IGD_1, + CHIP_I965|CHIP_I9XX|CHIP_GEN4}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82G965_IGD_1, + CHIP_I965|CHIP_I9XX|CHIP_GEN4}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82GM965_IGD_1, + CHIP_I965GM|CHIP_I965|CHIP_I9XX|CHIP_M|CHIP_GEN4}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82GME965_IGD_1, + CHIP_I965|CHIP_I9XX|CHIP_GEN4}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82G33_IGD_1, + CHIP_G33|CHIP_I9XX|CHIP_HWS|CHIP_GEN3}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82Q35_IGD_1, + CHIP_G33|CHIP_I9XX|CHIP_HWS|CHIP_GEN3}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82Q33_IGD_1, + CHIP_G33|CHIP_I9XX|CHIP_HWS|CHIP_GEN3}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82GM45_IGD_1, + CHIP_G4X|CHIP_GM45|CHIP_I965|CHIP_I9XX|CHIP_M|CHIP_HWS|CHIP_GEN4}, + {PCI_VENDOR_INTEL, 0x2E02, + CHIP_G4X|CHIP_I965|CHIP_I9XX|CHIP_HWS|CHIP_GEN4}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82Q45_IGD_1, + CHIP_G4X|CHIP_I965|CHIP_I9XX|CHIP_HWS|CHIP_GEN4}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82G45_IGD_1, + CHIP_G4X|CHIP_I965|CHIP_I9XX|CHIP_HWS|CHIP_GEN4}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82G41_IGD_1, + CHIP_G4X|CHIP_I965|CHIP_I9XX|CHIP_HWS|CHIP_GEN4}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_PINEVIEW_IGC_1, + CHIP_G33|CHIP_PINEVIEW|CHIP_M|CHIP_I9XX|CHIP_HWS|CHIP_GEN3}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_PINEVIEW_M_IGC_1, + CHIP_G33|CHIP_PINEVIEW|CHIP_M|CHIP_I9XX|CHIP_HWS|CHIP_GEN3}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_CLARKDALE_IGD, + CHIP_IRONLAKE|CHIP_IRONLAKE_D|CHIP_I965|CHIP_I9XX|CHIP_HWS}, + {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_ARRANDALE_IGD, + CHIP_IRONLAKE|CHIP_M|CHIP_IRONLAKE_M|CHIP_I965|CHIP_I9XX|CHIP_HWS}, + {0, 0, 0} +}; + +static const struct drm_driver_info inteldrm_driver = { + .buf_priv_size = 1, /* No dev_priv */ + .file_priv_size = sizeof(struct inteldrm_file), + .ioctl = inteldrm_ioctl, + .lastclose = inteldrm_lastclose, + .vblank_pipes = 2, + .get_vblank_counter = i915_get_vblank_counter, + .enable_vblank = i915_enable_vblank, + .disable_vblank = i915_disable_vblank, + .irq_install = i915_driver_irq_install, + .irq_uninstall = i915_driver_irq_uninstall, + + .gem_init_object = i915_gem_init_object, + .gem_free_object = i915_gem_free_object, + .gem_fault = inteldrm_fault, + .gem_size = sizeof(struct inteldrm_obj), + + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, + + .flags = DRIVER_AGP | DRIVER_AGP_REQUIRE | + DRIVER_MTRR | DRIVER_IRQ | DRIVER_GEM, +}; + +int +#if !defined(__NetBSD__) +inteldrm_probe(struct device *parent, void *match, void *aux) +#else /* !defined(__NetBSD__) */ +inteldrm_probe(struct device *parent, struct cfdata *match, void *aux) +#endif /* !defined(__NetBSD__) */ +{ + return (drm_pciprobe((struct pci_attach_args *)aux, + inteldrm_pciidlist)); +} + +/* + * We're intel IGD, bus 0 function 0 dev 0 should be the GMCH, so it should + * be Intel + */ +int +inteldrm_gmch_match(struct pci_attach_args *pa) +{ + if (pa->pa_bus == 0 && pa->pa_device == 0 && pa->pa_function == 0 && + PCI_VENDOR(pa->pa_id) == PCI_VENDOR_INTEL && + PCI_CLASS(pa->pa_class) == PCI_CLASS_BRIDGE && + PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_BRIDGE_HOST) + return (1); + return (0); +} + +#if defined(__NetBSD__) + +/* + * Task work queue. Implements OpenBSD's workq_add_task(9) API. + */ + +struct workq { + struct pool wq_pool; + struct workqueue *wq_queue; +}; + +typedef void (*workq_fn)(void *, void *); + +struct workq_task { + struct work wqt_work; + workq_fn wqt_func; + void *wqt_arg1; + void *wqt_arg2; +}; + +#define WQ_WAITOK (1<<0) + +static void +workq_worker(struct work *wk, void *arg) +{ + struct workq_task *wqt = (struct workq_task *)wk; + struct pool *wpl = arg; + + KASSERT(wqt != NULL && wpl != NULL); + KASSERT(wk == &wqt->wqt_work); + + wqt->wqt_func(wqt->wqt_arg1, wqt->wqt_arg2); + + pool_put(wpl, wqt); +} + +static struct workq * +workq_create(const char *name, int maxqs, int ipl) +{ + struct workq *wq; + + wq = malloc(sizeof(*wq), M_DRM, M_NOWAIT); + if (wq == NULL) + return NULL; + + if (workqueue_create(&wq->wq_queue, name, workq_worker, + &wq->wq_pool, PRI_BIO, ipl, 0)) { + free(wq, M_DRM); + return NULL; + } + + pool_init(&wq->wq_pool, sizeof(struct workq_task), 0, 0, 0, + name, NULL, IPL_HIGH); + + return wq; +} + +static int +workq_add_task(struct workq *wq, int flags, workq_fn func, void *a1, void *a2) +{ + struct workq_task *wqt; + + KASSERT(wq != NULL); + + wqt = pool_get(&wq->wq_pool, (flags & WQ_WAITOK) ? + PR_WAITOK : PR_NOWAIT); + if (wqt == NULL) + return ENOMEM; + + wqt->wqt_func = func; + wqt->wqt_arg1 = a1; + wqt->wqt_arg2 = a2; + + workqueue_enqueue(wq->wq_queue, &wqt->wqt_work, NULL); + + return 0; +} + +#endif /* defined(__NetBSD__) */ + +void +inteldrm_attach(struct device *parent, struct device *self, void *aux) +{ + struct inteldrm_softc *dev_priv = (struct inteldrm_softc *)self; + struct pci_attach_args *pa = aux, bpa; +#if !defined(__NetBSD__) + struct vga_pci_bar *bar; +#else /* !defined(__NetBSD__) */ + bus_addr_t vb_base; +#endif /* !defined(__NetBSD__) */ + struct drm_device *dev; + const struct drm_pcidev *id_entry; + int i; + + id_entry = drm_find_description(PCI_VENDOR(pa->pa_id), + PCI_PRODUCT(pa->pa_id), inteldrm_pciidlist); + dev_priv->flags = id_entry->driver_private; + dev_priv->pci_device = PCI_PRODUCT(pa->pa_id); + + dev_priv->pc = pa->pa_pc; + dev_priv->tag = pa->pa_tag; + dev_priv->dmat = pa->pa_dmat; + dev_priv->bst = pa->pa_memt; + +#if !defined(__NetBSD__) + /* we need to use this api for now due to sharing with intagp */ + bar = vga_pci_bar_info((struct vga_pci_softc *)parent, + (IS_I9XX(dev_priv) ? 0 : 1)); + if (bar == NULL) { + printf(": can't get BAR info\n"); + return; + } + + dev_priv->regs = vga_pci_bar_map((struct vga_pci_softc *)parent, + bar->addr, 0, 0); + if (dev_priv->regs == NULL) { +#else /* !defined(__NetBSD__) */ + vb_base = vga_bar_base(parent, (IS_I9XX(dev_priv) ? 0 : 1)); + /* XXX horrible kludge: agp might have mapped it */ + if (!agp_i810_borrow(vb_base, &dev_priv->regs->bst, + &dev_priv->regs->bsh)) { +#endif /* !defined(__NetBSD__) */ + printf(": can't map mmio space\n"); + return; + } + + if (pci_intr_map(pa, &dev_priv->ih) != 0) { + printf(": couldn't map interrupt\n"); + return; + } + + /* + * set up interrupt handler, note that we don't switch the interrupt + * on until the X server talks to us, kms will change this. + */ + dev_priv->irqh = pci_intr_establish(dev_priv->pc, dev_priv->ih, IPL_TTY, + (HAS_PCH_SPLIT(dev_priv) ? inteldrm_ironlake_intr : inteldrm_intr), +#if !defined(__NetBSD__) + dev_priv, dev_priv->dev.dv_xname); +#else /* !defined(__NetBSD__) */ + dev_priv); +#endif /* !defined(__NetBSD__) */ + if (dev_priv->irqh == NULL) { + printf(": couldn't establish interrupt\n"); + return; + } + + /* Unmask the interrupts that we always want on. */ + if (HAS_PCH_SPLIT(dev_priv)) { + dev_priv->irq_mask_reg = ~PCH_SPLIT_DISPLAY_INTR_FIX; + /* masked for now, turned on on demand */ + dev_priv->gt_irq_mask_reg = ~PCH_SPLIT_RENDER_INTR_FIX; + dev_priv->pch_irq_mask_reg = ~PCH_SPLIT_HOTPLUG_INTR_FIX; + } else { + dev_priv->irq_mask_reg = ~I915_INTERRUPT_ENABLE_FIX; + } + + dev_priv->workq = workq_create("intelrel", 1, IPL_TTY); + if (dev_priv->workq == NULL) { + printf("couldn't create workq\n"); + return; + } + + /* GEM init */ + TAILQ_INIT(&dev_priv->mm.active_list); + TAILQ_INIT(&dev_priv->mm.flushing_list); + TAILQ_INIT(&dev_priv->mm.inactive_list); + TAILQ_INIT(&dev_priv->mm.gpu_write_list); + TAILQ_INIT(&dev_priv->mm.request_list); + TAILQ_INIT(&dev_priv->mm.fence_list); + timeout_set(&dev_priv->mm.retire_timer, inteldrm_timeout, dev_priv); + timeout_set(&dev_priv->mm.hang_timer, inteldrm_hangcheck, dev_priv); + dev_priv->mm.next_gem_seqno = 1; + dev_priv->mm.suspended = 1; + + /* On GEN3 we really need to make sure the ARB C3 LP bit is set */ + if (IS_GEN3(dev_priv)) { + u_int32_t tmp = I915_READ(MI_ARB_STATE); + if (!(tmp & MI_ARB_C3_LP_WRITE_ENABLE)) { + /* + * arb state is a masked write, so set bit + bit + * in mask + */ + tmp = MI_ARB_C3_LP_WRITE_ENABLE | + (MI_ARB_C3_LP_WRITE_ENABLE << MI_ARB_MASK_SHIFT); + I915_WRITE(MI_ARB_STATE, tmp); + } + } + + /* For the X server, in kms mode this will not be needed */ + dev_priv->fence_reg_start = 3; + + if (IS_I965G(dev_priv) || IS_I945G(dev_priv) || IS_I945GM(dev_priv) || + IS_G33(dev_priv)) + dev_priv->num_fence_regs = 16; + else + dev_priv->num_fence_regs = 8; + /* Initialise fences to zero, else on some macs we'll get corruption */ + if (IS_I965G(dev_priv)) { + for (i = 0; i < 16; i++) + I915_WRITE64(FENCE_REG_965_0 + (i * 8), 0); + } else { + for (i = 0; i < 8; i++) + I915_WRITE(FENCE_REG_830_0 + (i * 4), 0); + if (IS_I945G(dev_priv) || IS_I945GM(dev_priv) || + IS_G33(dev_priv)) + for (i = 0; i < 8; i++) + I915_WRITE(FENCE_REG_945_8 + (i * 4), 0); + } + + if (pci_find_device(&bpa, inteldrm_gmch_match) == 0) { + printf(": can't find GMCH\n"); + return; + } + + /* Set up the IFP for chipset flushing */ + if (dev_priv->flags & (CHIP_I915G|CHIP_I915GM|CHIP_I945G|CHIP_I945GM)) { + i915_alloc_ifp(dev_priv, &bpa); + } else if (IS_I965G(dev_priv) || IS_G33(dev_priv)) { + i965_alloc_ifp(dev_priv, &bpa); + } else { + int nsegs; + /* + * I8XX has no flush page mechanism, we fake it by writing until + * the cache is empty. allocate a page to scribble on + */ + dev_priv->ifp.i8xx.kva = NULL; + if (bus_dmamem_alloc(pa->pa_dmat, PAGE_SIZE, 0, 0, + &dev_priv->ifp.i8xx.seg, 1, &nsegs, BUS_DMA_WAITOK) == 0) { + if (bus_dmamem_map(pa->pa_dmat, &dev_priv->ifp.i8xx.seg, + 1, PAGE_SIZE, &dev_priv->ifp.i8xx.kva, 0) != 0) { + bus_dmamem_free(pa->pa_dmat, + &dev_priv->ifp.i8xx.seg, nsegs); + dev_priv->ifp.i8xx.kva = NULL; + } + } + } + + inteldrm_detect_bit_6_swizzle(dev_priv, &bpa); + /* Init HWS */ + if (!I915_NEED_GFX_HWS(dev_priv)) { + if (i915_init_phys_hws(dev_priv, pa->pa_dmat) != 0) { + printf(": couldn't alloc HWS page\n"); + return; + } + } + + printf(": %s\n", pci_intr_string(pa->pa_pc, dev_priv->ih)); + + mtx_init(&dev_priv->user_irq_lock, IPL_TTY); + mtx_init(&dev_priv->list_lock, IPL_NONE); + mtx_init(&dev_priv->request_lock, IPL_NONE); + mtx_init(&dev_priv->fence_lock, IPL_NONE); +#if defined(__NetBSD__) + cv_init(&dev_priv->condvar, "gemwt"); +#endif /* defined(__NetBSD__) */ + + /* All intel chipsets need to be treated as agp, so just pass one */ + dev_priv->drmdev = drm_attach_pci(&inteldrm_driver, pa, 1, self); + + dev = (struct drm_device *)dev_priv->drmdev; + + /* + * XXX [GS] I'm not sure about what the following does. It seems + * related to the use of write combining. But x86_mem_add_mapping + * already does it in NetBSD since BUS_SPACE_MAP_PREFETCHABLE will + * be used in agp_map_subregion. + * + * XXX [GS] See OpenBSD's uvm_page_physload_flags which differs + * from NetBSD's uvm_page_physload. + */ +#if !defined(__NetBSD__) + /* XXX would be a lot nicer to get agp info before now */ + uvm_page_physload_flags(atop(dev->agp->base), atop(dev->agp->base + + dev->agp->info.ai_aperture_size), atop(dev->agp->base), + atop(dev->agp->base + dev->agp->info.ai_aperture_size), 0, + PHYSLOAD_DEVICE); + /* array of vm pages that physload introduced. */ + dev_priv->pgs = PHYS_TO_VM_PAGE(dev->agp->base); + KASSERT(dev_priv->pgs != NULL); + /* + * XXX mark all pages write combining so user mmaps get the right + * bits. We really need a proper MI api for doing this, but for now + * this allows us to use PAT where available. + */ + for (i = 0; i < atop(dev->agp->info.ai_aperture_size); i++) + atomic_setbits_int(&(dev_priv->pgs[i].pg_flags), PG_PMAP_WC); +#endif /* !defined(__NetBSD__) */ + if (agp_init_map(dev_priv->bst, dev->agp->base, + dev->agp->info.ai_aperture_size, BUS_SPACE_MAP_LINEAR | + BUS_SPACE_MAP_PREFETCHABLE, &dev_priv->agph)) + panic("can't map aperture"); + +#if defined(__NetBSD__) + (void)pmf_device_register(self, inteldrm_suspend, inteldrm_resume); +#endif /* defined(__NetBSD__) */ +} + +int +inteldrm_detach(struct device *self, int flags) +{ + struct inteldrm_softc *dev_priv = (struct inteldrm_softc *)self; + +#if defined(__NetBSD__) + pmf_device_deregister(self); +#endif /* defined(__NetBSD__) */ + + timeout_del(&dev_priv->mm.retire_timer); + timeout_del(&dev_priv->mm.hang_timer); +#if defined(__NetBSD__) + callout_destroy(&dev_priv->mm.retire_timer); + callout_destroy(&dev_priv->mm.hang_timer); +#endif /* defined(__NetBSD__) */ + + agp_destroy_map(dev_priv->agph); + + /* this will quiesce any dma that's going on and kill the timeouts. */ + if (dev_priv->drmdev != NULL) { + config_detach(dev_priv->drmdev, flags); + dev_priv->drmdev = NULL; + } + + if (!I915_NEED_GFX_HWS(dev_priv) && dev_priv->hws_dmamem) { + drm_dmamem_free(dev_priv->dmat, dev_priv->hws_dmamem); + dev_priv->hws_dmamem = NULL; + /* Need to rewrite hardware status page */ + I915_WRITE(HWS_PGA, 0x1ffff000); + dev_priv->hw_status_page = NULL; + } + + if (IS_I9XX(dev_priv) && dev_priv->ifp.i9xx.valid) { +#if !defined(__NetBSD__) + bus_space_unmap(dev_priv->ifp.i9xx.bst, dev_priv->ifp.i9xx.bsh, +#else /* !defined(__NetBSD__) */ + bus_space_free(dev_priv->ifp.i9xx.bst, dev_priv->ifp.i9xx.bsh, +#endif /* !defined(__NetBSD__) */ + PAGE_SIZE); + } else if (dev_priv->flags & (CHIP_I830 | CHIP_I845G | CHIP_I85X | + CHIP_I865G) && dev_priv->ifp.i8xx.kva != NULL) { + bus_dmamem_unmap(dev_priv->dmat, dev_priv->ifp.i8xx.kva, + PAGE_SIZE); + bus_dmamem_free(dev_priv->dmat, &dev_priv->ifp.i8xx.seg, 1); + } + + pci_intr_disestablish(dev_priv->pc, dev_priv->irqh); + +#if defined(__NetBSD__) + cv_destroy(&dev_priv->condvar); + mutex_destroy(&dev_priv->fence_lock); + mutex_destroy(&dev_priv->request_lock); + mutex_destroy(&dev_priv->list_lock); + mutex_destroy(&dev_priv->user_irq_lock); +#endif /* defined(__NetBSD__) */ + + /* XXX Destroy the task queue dev_priv->workq. */ + +#if !defined(__NetBSD__) + if (dev_priv->regs != NULL) + vga_pci_bar_unmap(dev_priv->regs); +#endif /* !defined(__NetBSD__) */ + + return (0); +} + +#if !defined(__NetBSD__) +int +inteldrm_activate(struct device *arg, int act) +{ + struct inteldrm_softc *dev_priv = (struct inteldrm_softc *)arg; + + switch (act) { + case DVACT_QUIESCE: + inteldrm_quiesce(dev_priv); + break; + case DVACT_SUSPEND: + inteldrm_save_state(dev_priv); + break; + case DVACT_RESUME: + inteldrm_restore_state(dev_priv); + /* entrypoints can stop sleeping now */ + atomic_clearbits_int(&dev_priv->sc_flags, INTELDRM_QUIET); + wakeup(&dev_priv->flags); + break; + } + + return (0); +} +#else /* !defined(__NetBSD__) */ +static bool +inteldrm_suspend(struct device *arg, const pmf_qual_t *qual) +{ + struct inteldrm_softc *dev_priv = (struct inteldrm_softc *)arg; + + inteldrm_quiesce(dev_priv); + inteldrm_save_state(dev_priv); + + return true; +} + +static bool +inteldrm_resume(struct device *arg, const pmf_qual_t *qual) +{ + struct inteldrm_softc *dev_priv = (struct inteldrm_softc *)arg; + + inteldrm_restore_state(dev_priv); + /* entrypoints can stop sleeping now */ + atomic_clearbits_int(&dev_priv->sc_flags, INTELDRM_QUIET); + wakeup(&dev_priv->flags); + + return true; +} +#endif /* !defined(__NetBSD__) */ + +#if !defined(__NetBSD__) +struct cfattach inteldrm_ca = { + sizeof(struct inteldrm_softc), inteldrm_probe, inteldrm_attach, + inteldrm_detach, inteldrm_activate +}; + +struct cfdriver inteldrm_cd = { + 0, "inteldrm", DV_DULL +}; +#else /* !defined(__NetBSD__) */ +/* + * We do not use CFATTACH_DECL_NEW, in order to be compatible with + * the rest of the code (see similar comment in drm_drv.c). + */ +CFATTACH_DECL(inteldrm, sizeof(struct inteldrm_softc), + inteldrm_probe, inteldrm_attach, inteldrm_detach, NULL); +#endif /* !defined(__NetBSD__) */ + +int +inteldrm_ioctl(struct drm_device *dev, u_long cmd, caddr_t data, + struct drm_file *file_priv) +{ + struct inteldrm_softc *dev_priv = dev->dev_private; + int error = 0; + + while ((dev_priv->sc_flags & INTELDRM_QUIET) && error == 0) + error = tsleep(&dev_priv->flags, PCATCH, "intelioc", 0); + if (error) + return (error); + dev_priv->entries++; + + error = inteldrm_doioctl(dev, cmd, data, file_priv); + + dev_priv->entries--; + if (dev_priv->sc_flags & INTELDRM_QUIET) + wakeup(&dev_priv->entries); + return (error); +} + +int +inteldrm_doioctl(struct drm_device *dev, u_long cmd, caddr_t data, + struct drm_file *file_priv) +{ + struct inteldrm_softc *dev_priv = dev->dev_private; + + if (file_priv->authenticated == 1) { + switch (cmd) { + case DRM_IOCTL_I915_GETPARAM: + return (inteldrm_getparam(dev_priv, data)); + case DRM_IOCTL_I915_GEM_EXECBUFFER2: + return (i915_gem_execbuffer2(dev, data, file_priv)); + case DRM_IOCTL_I915_GEM_BUSY: + return (i915_gem_busy_ioctl(dev, data, file_priv)); + case DRM_IOCTL_I915_GEM_THROTTLE: + return (i915_gem_ring_throttle(dev, file_priv)); + case DRM_IOCTL_I915_GEM_MMAP: + return (i915_gem_gtt_map_ioctl(dev, data, file_priv)); + case DRM_IOCTL_I915_GEM_CREATE: + return (i915_gem_create_ioctl(dev, data, file_priv)); + case DRM_IOCTL_I915_GEM_PREAD: + return (i915_gem_pread_ioctl(dev, data, file_priv)); + case DRM_IOCTL_I915_GEM_PWRITE: + return (i915_gem_pwrite_ioctl(dev, data, file_priv)); + case DRM_IOCTL_I915_GEM_SET_DOMAIN: + return (i915_gem_set_domain_ioctl(dev, data, + file_priv)); + case DRM_IOCTL_I915_GEM_SET_TILING: + return (i915_gem_set_tiling(dev, data, file_priv)); + case DRM_IOCTL_I915_GEM_GET_TILING: + return (i915_gem_get_tiling(dev, data, file_priv)); + case DRM_IOCTL_I915_GEM_GET_APERTURE: + return (i915_gem_get_aperture_ioctl(dev, data, + file_priv)); + case DRM_IOCTL_I915_GEM_MADVISE: + return (i915_gem_madvise_ioctl(dev, data, file_priv)); + default: + break; + } + } + + if (file_priv->master == 1) { + switch (cmd) { + case DRM_IOCTL_I915_SETPARAM: + return (inteldrm_setparam(dev_priv, data)); + case DRM_IOCTL_I915_GEM_INIT: + return (i915_gem_init_ioctl(dev, data, file_priv)); + case DRM_IOCTL_I915_GEM_ENTERVT: + return (i915_gem_entervt_ioctl(dev, data, file_priv)); + case DRM_IOCTL_I915_GEM_LEAVEVT: + return (i915_gem_leavevt_ioctl(dev, data, file_priv)); + case DRM_IOCTL_I915_GEM_PIN: + return (i915_gem_pin_ioctl(dev, data, file_priv)); + case DRM_IOCTL_I915_GEM_UNPIN: + return (i915_gem_unpin_ioctl(dev, data, file_priv)); + } + } + return (EINVAL); +} + +int +inteldrm_ironlake_intr(void *arg) +{ + struct inteldrm_softc *dev_priv = arg; + struct drm_device *dev = (struct drm_device *)dev_priv->drmdev; + u_int32_t de_iir, gt_iir, de_ier, pch_iir; + int ret = 0; + + de_ier = I915_READ(DEIER); + I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL); + (void)I915_READ(DEIER); + + de_iir = I915_READ(DEIIR); + gt_iir = I915_READ(GTIIR); + pch_iir = I915_READ(SDEIIR); + + if (de_iir == 0 && gt_iir == 0 && pch_iir == 0) + goto done; + ret = 1; + + if (gt_iir & GT_USER_INTERRUPT) { + mtx_enter(&dev_priv->user_irq_lock); +#if !defined(__NetBSD__) + wakeup(dev_priv); +#else /* !defined(__NetBSD__) */ + cv_broadcast(&dev_priv->condvar); +#endif /* !defined(__NetBSD__) */ + mtx_leave(&dev_priv->user_irq_lock); + dev_priv->mm.hang_cnt = 0; + timeout_add_msec(&dev_priv->mm.hang_timer, 750); + } + if (gt_iir & GT_MASTER_ERROR) + inteldrm_error(dev_priv); + + if (de_iir & DE_PIPEA_VBLANK) + drm_handle_vblank(dev, 0); + if (de_iir & DE_PIPEB_VBLANK) + drm_handle_vblank(dev, 1); + + /* should clear PCH hotplug event before clearing CPU irq */ + I915_WRITE(SDEIIR, pch_iir); + I915_WRITE(GTIIR, gt_iir); + I915_WRITE(DEIIR, de_iir); + +done: + I915_WRITE(DEIER, de_ier); + (void)I915_READ(DEIER); + + return (ret); +} + +int +inteldrm_intr(void *arg) +{ + struct inteldrm_softc *dev_priv = arg; + struct drm_device *dev = (struct drm_device *)dev_priv->drmdev; + u_int32_t iir, pipea_stats = 0, pipeb_stats = 0; + + /* we're not set up, don't poke the hw */ + if (dev_priv->hw_status_page == NULL) + return (0); + /* + * lock is to protect from writes to PIPESTAT and IMR from other cores. + */ + mtx_enter(&dev_priv->user_irq_lock); + iir = I915_READ(IIR); + if (iir == 0) { + mtx_leave(&dev_priv->user_irq_lock); + return (0); + } + + /* + * Clear the PIPE(A|B)STAT regs before the IIR + */ + if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) { + pipea_stats = I915_READ(PIPEASTAT); + I915_WRITE(PIPEASTAT, pipea_stats); + } + if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) { + pipeb_stats = I915_READ(PIPEBSTAT); + I915_WRITE(PIPEBSTAT, pipeb_stats); + } + if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) + inteldrm_error(dev_priv); + + I915_WRITE(IIR, iir); + (void)I915_READ(IIR); /* Flush posted writes */ + + if (iir & I915_USER_INTERRUPT) { +#if !defined(__NetBSD__) + wakeup(dev_priv); +#else /* !defined(__NetBSD__) */ + cv_broadcast(&dev_priv->condvar); +#endif /* !defined(__NetBSD__) */ + dev_priv->mm.hang_cnt = 0; + timeout_add_msec(&dev_priv->mm.hang_timer, 750); + } + + mtx_leave(&dev_priv->user_irq_lock); + + if (pipea_stats & I915_VBLANK_INTERRUPT_STATUS) + drm_handle_vblank(dev, 0); + + if (pipeb_stats & I915_VBLANK_INTERRUPT_STATUS) + drm_handle_vblank(dev, 1); + + return (1); +} + +u_int32_t +inteldrm_read_hws(struct inteldrm_softc *dev_priv, int reg) +{ + struct drm_device *dev = (struct drm_device *)dev_priv->drmdev; + struct inteldrm_obj *obj_priv; + bus_dma_tag_t tag; + bus_dmamap_t map; + u_int32_t val; + + if (I915_NEED_GFX_HWS(dev_priv)) { + obj_priv = (struct inteldrm_obj *)dev_priv->hws_obj; + map = obj_priv->dmamap; + tag = dev_priv->agpdmat; + } else { + map = dev_priv->hws_dmamem->map; + tag = dev->dmat; + } + + bus_dmamap_sync(tag, map, 0, PAGE_SIZE, BUS_DMASYNC_POSTREAD); + + val = ((volatile u_int32_t *)(dev_priv->hw_status_page))[reg]; + bus_dmamap_sync(tag, map, 0, PAGE_SIZE, BUS_DMASYNC_PREREAD); + + return (val); +} + +/* + * These five ring manipulation functions are protected by dev->dev_lock. + */ +int +inteldrm_wait_ring(struct inteldrm_softc *dev_priv, int n) +{ + struct inteldrm_ring *ring = &dev_priv->ring; + u_int32_t acthd_reg, acthd, last_acthd, last_head; + int i; + + acthd_reg = IS_I965G(dev_priv) ? ACTHD_I965 : ACTHD; + last_head = I915_READ(PRB0_HEAD) & HEAD_ADDR; + last_acthd = I915_READ(acthd_reg); + + /* ugh. Could really do with a proper, resettable timer here. */ + for (i = 0; i < 100000; i++) { + ring->head = I915_READ(PRB0_HEAD) & HEAD_ADDR; + acthd = I915_READ(acthd_reg); + ring->space = ring->head - (ring->tail + 8); + + INTELDRM_VPRINTF("%s: head: %x tail: %x space: %x\n", __func__, + ring->head, ring->tail, ring->space); + if (ring->space < 0) + ring->space += ring->size; + if (ring->space >= n) + return (0); + + /* Only timeout if the ring isn't chewing away on something */ + if (ring->head != last_head || acthd != last_acthd) + i = 0; + + last_head = ring->head; + last_acthd = acthd; + delay(10); + } + + return (EBUSY); +} + +void +inteldrm_wrap_ring(struct inteldrm_softc *dev_priv) +{ + u_int32_t rem;; + + rem = dev_priv->ring.size - dev_priv->ring.tail; + if (dev_priv->ring.space < rem && + inteldrm_wait_ring(dev_priv, rem) != 0) + return; /* XXX */ + + dev_priv->ring.space -= rem; + + bus_space_set_region_4(dev_priv->bst, dev_priv->ring.bsh, + dev_priv->ring.woffset, MI_NOOP, rem / 4); + + dev_priv->ring.tail = 0; +} + +void +inteldrm_begin_ring(struct inteldrm_softc *dev_priv, int ncmd) +{ + int bytes = 4 * ncmd; + + INTELDRM_VPRINTF("%s: %d\n", __func__, ncmd); + if (dev_priv->ring.tail + bytes > dev_priv->ring.size) + inteldrm_wrap_ring(dev_priv); + if (dev_priv->ring.space < bytes) + inteldrm_wait_ring(dev_priv, bytes); + dev_priv->ring.woffset = dev_priv->ring.tail; + dev_priv->ring.tail += bytes; + dev_priv->ring.tail &= dev_priv->ring.size - 1; + dev_priv->ring.space -= bytes; +} + +void +inteldrm_out_ring(struct inteldrm_softc *dev_priv, u_int32_t cmd) +{ + INTELDRM_VPRINTF("%s: %x\n", __func__, cmd); + bus_space_write_4(dev_priv->bst, dev_priv->ring.bsh, + dev_priv->ring.woffset, cmd); + /* + * don't need to deal with wrap here because we padded + * the ring out if we would wrap + */ + dev_priv->ring.woffset += 4; +} + +void +inteldrm_advance_ring(struct inteldrm_softc *dev_priv) +{ + INTELDRM_VPRINTF("%s: %x, %x\n", __func__, dev_priv->ring.wspace, + dev_priv->ring.woffset); + DRM_MEMORYBARRIER(); + I915_WRITE(PRB0_TAIL, dev_priv->ring.tail); +} + +void +inteldrm_update_ring(struct inteldrm_softc *dev_priv) +{ + struct inteldrm_ring *ring = &dev_priv->ring; + + ring->head = (I915_READ(PRB0_HEAD) & HEAD_ADDR); + ring->tail = (I915_READ(PRB0_TAIL) & TAIL_ADDR); + ring->space = ring->head - (ring->tail + 8); + if (ring->space < 0) + ring->space += ring->size; + INTELDRM_VPRINTF("%s: head: %x tail: %x space: %x\n", __func__, + ring->head, ring->tail, ring->space); +} + +/* + * Sets up the hardware status page for devices that need a physical address + * in the register. + */ +int +i915_init_phys_hws(struct inteldrm_softc *dev_priv, bus_dma_tag_t dmat) +{ + /* Program Hardware Status Page */ + if ((dev_priv->hws_dmamem = drm_dmamem_alloc(dmat, PAGE_SIZE, + PAGE_SIZE, 1, PAGE_SIZE, 0, BUS_DMA_READ)) == NULL) { + return (ENOMEM); + } + + dev_priv->hw_status_page = dev_priv->hws_dmamem->kva; + + memset(dev_priv->hw_status_page, 0, PAGE_SIZE); + + bus_dmamap_sync(dmat, dev_priv->hws_dmamem->map, 0, PAGE_SIZE, + BUS_DMASYNC_PREREAD); + I915_WRITE(HWS_PGA, dev_priv->hws_dmamem->map->dm_segs[0].ds_addr); + DRM_DEBUG("Enabled hardware status page\n"); + return (0); +} + +void +i915_alloc_ifp(struct inteldrm_softc *dev_priv, struct pci_attach_args *bpa) +{ + bus_addr_t addr; + u_int32_t reg; + + dev_priv->ifp.i9xx.valid = true; + dev_priv->ifp.i9xx.bst = bpa->pa_memt; + + reg = pci_conf_read(bpa->pa_pc, bpa->pa_tag, I915_IFPADDR); + if (reg & 0x1) { + addr = (bus_addr_t)reg; + addr &= ~0x1; + /* XXX extents ... need data on whether bioses alloc or not. */ + if (bus_space_map(bpa->pa_memt, addr, PAGE_SIZE, 0, + &dev_priv->ifp.i9xx.bsh) != 0) + goto nope; + return; +#if !defined(__NetBSD__) + } else if (bpa->pa_memex == NULL || extent_alloc(bpa->pa_memex, + PAGE_SIZE, PAGE_SIZE, 0, 0, 0, &addr) || bus_space_map(bpa->pa_memt, + addr, PAGE_SIZE, 0, &dev_priv->ifp.i9xx.bsh)) +#else /* !defined(__NetBSD__) */ + } else if (bus_space_alloc(bpa->pa_memt, 0, 0xffffffff, + PAGE_SIZE, PAGE_SIZE, 0, 0, &addr, &dev_priv->ifp.i9xx.bsh)) +#endif /* !defined(__NetBSD__) */ + goto nope; + + pci_conf_write(bpa->pa_pc, bpa->pa_tag, I915_IFPADDR, addr | 0x1); + + return; + +nope: + dev_priv->ifp.i9xx.valid = false; + printf(": no ifp "); +} + +void +i965_alloc_ifp(struct inteldrm_softc *dev_priv, struct pci_attach_args *bpa) +{ + bus_addr_t addr; + u_int32_t lo, hi; + + dev_priv->ifp.i9xx.valid = true; + dev_priv->ifp.i9xx.bst = bpa->pa_memt; + + hi = pci_conf_read(bpa->pa_pc, bpa->pa_tag, I965_IFPADDR + 4); + lo = pci_conf_read(bpa->pa_pc, bpa->pa_tag, I965_IFPADDR); + if (lo & 0x1) { + addr = (((u_int64_t)hi << 32) | lo); + addr &= ~0x1; + /* XXX extents ... need data on whether bioses alloc or not. */ + if (bus_space_map(bpa->pa_memt, addr, PAGE_SIZE, 0, + &dev_priv->ifp.i9xx.bsh) != 0) + goto nope; + return; +#if !defined(__NetBSD__) + } else if (bpa->pa_memex == NULL || extent_alloc(bpa->pa_memex, + PAGE_SIZE, PAGE_SIZE, 0, 0, 0, &addr) || bus_space_map(bpa->pa_memt, + addr, PAGE_SIZE, 0, &dev_priv->ifp.i9xx.bsh)) +#else /* !defined(__NetBSD__) */ + } else if (bus_space_alloc(bpa->pa_memt, 0, 0xffffffff, + PAGE_SIZE, PAGE_SIZE, 0, 0, &addr, &dev_priv->ifp.i9xx.bsh)) +#endif /* !defined(__NetBSD__) */ + goto nope; + + pci_conf_write(bpa->pa_pc, bpa->pa_tag, I965_IFPADDR + 4, + upper_32_bits(addr)); + pci_conf_write(bpa->pa_pc, bpa->pa_tag, I965_IFPADDR, + (addr & 0xffffffff) | 0x1); + + return; + +nope: + dev_priv->ifp.i9xx.valid = false; + printf(": no ifp "); +} + +void +inteldrm_chipset_flush(struct inteldrm_softc *dev_priv) +{ + /* + * Write to this flush page flushes the chipset write cache. + * The write will return when it is done. + */ + if (IS_I9XX(dev_priv)) { + if (dev_priv->ifp.i9xx.valid) + bus_space_write_4(dev_priv->ifp.i9xx.bst, + dev_priv->ifp.i9xx.bsh, 0, 1); + } else { + /* + * I8XX don't have a flush page mechanism, but do have the + * cache. Do it the bruteforce way. we write 1024 byes into + * the cache, then clflush them out so they'll kick the stuff + * we care about out of the chipset cache. + */ + if (dev_priv->ifp.i8xx.kva != NULL) { + memset(dev_priv->ifp.i8xx.kva, 0, 1024); + agp_flush_cache_range((vaddr_t)dev_priv->ifp.i8xx.kva, + 1024); + } + } +} + +void +inteldrm_lastclose(struct drm_device *dev) +{ + struct inteldrm_softc *dev_priv = dev->dev_private; +#if !defined(__NetBSD__) + struct vm_page *p; +#endif /* !defined(__NetBSD__) */ + int ret; + + ret = i915_gem_idle(dev_priv); + if (ret) + DRM_ERROR("failed to idle hardware: %d\n", ret); + + if (dev_priv->agpdmat != NULL) { + /* + * make sure we nuke everything, we may have mappings that we've + * unrefed, but uvm has a reference to them for maps. Make sure + * they get unbound and any accesses will segfault. + * XXX only do ones in GEM. + */ +#if !defined(__NetBSD__) + for (p = dev_priv->pgs; p < dev_priv->pgs + + (dev->agp->info.ai_aperture_size / PAGE_SIZE); p++) + pmap_page_protect(p, VM_PROT_NONE); +#endif /* !defined(__NetBSD__) */ + agp_bus_dma_destroy(dev->agp->agpdev, + dev_priv->agpdmat); + } + dev_priv->agpdmat = NULL; +} + +int +inteldrm_getparam(struct inteldrm_softc *dev_priv, void *data) +{ + drm_i915_getparam_t *param = data; + int value; + + switch (param->param) { + case I915_PARAM_CHIPSET_ID: + value = dev_priv->pci_device; + break; + case I915_PARAM_HAS_GEM: + value = 1; + break; + case I915_PARAM_NUM_FENCES_AVAIL: + value = dev_priv->num_fence_regs - dev_priv->fence_reg_start; + break; + case I915_PARAM_HAS_EXECBUF2: + value = 1; + break; + default: + DRM_DEBUG("Unknown parameter %d\n", param->param); + return (EINVAL); + } + return (copyout(&value, param->value, sizeof(int))); +} + +int +inteldrm_setparam(struct inteldrm_softc *dev_priv, void *data) +{ + drm_i915_setparam_t *param = data; + + switch (param->param) { + case I915_SETPARAM_NUM_USED_FENCES: + if (param->value > dev_priv->num_fence_regs || + param->value < 0) + return EINVAL; + /* Userspace can use first N regs */ + dev_priv->fence_reg_start = param->value; + break; + default: + DRM_DEBUG("unknown parameter %d\n", param->param); + return (EINVAL); + } + + return 0; +} + + +int +i915_gem_init_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct inteldrm_softc *dev_priv = dev->dev_private; + struct drm_i915_gem_init *args = data; + + DRM_LOCK(); + + if (args->gtt_start >= args->gtt_end || + args->gtt_end > dev->agp->info.ai_aperture_size || + (args->gtt_start & PAGE_MASK) != 0 || + (args->gtt_end & PAGE_MASK) != 0) { + DRM_UNLOCK(); + return (EINVAL); + } + /* + * putting stuff in the last page of the aperture can cause nasty + * problems with prefetch going into unassigned memory. Since we put + * a scratch page on all unused aperture pages, just leave the last + * page as a spill to prevent gpu hangs. + */ + if (args->gtt_end == dev->agp->info.ai_aperture_size) + args->gtt_end -= 4096; + + if (agp_bus_dma_init(dev->agp->agpdev, + dev->agp->base + args->gtt_start, dev->agp->base + args->gtt_end, + &dev_priv->agpdmat) != 0) { + DRM_UNLOCK(); + return (ENOMEM); + } + + dev->gtt_total = (uint32_t)(args->gtt_end - args->gtt_start); + inteldrm_set_max_obj_size(dev_priv); + + DRM_UNLOCK(); + + return 0; +} + +void +inteldrm_set_max_obj_size(struct inteldrm_softc *dev_priv) +{ + struct drm_device *dev = (struct drm_device *)dev_priv->drmdev; + + /* + * Allow max obj size up to the size where ony 2 would fit the + * aperture, but some slop exists due to alignment etc + */ + dev_priv->max_gem_obj_size = (dev->gtt_total - + atomic_read(&dev->pin_memory)) * 3 / 4 / 2; + +} + +int +i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_i915_gem_get_aperture *args = data; + + /* we need a write lock here to make sure we get the right value */ + DRM_LOCK(); + args->aper_size = dev->gtt_total; + args->aper_available_size = (args->aper_size - + atomic_read(&dev->pin_memory)); + DRM_UNLOCK(); + + return (0); +} + +/** + * Creates a new mm object and returns a handle to it. + */ +int +i915_gem_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct inteldrm_softc *dev_priv = dev->dev_private; + struct drm_i915_gem_create *args = data; + struct drm_obj *obj; + int handle, ret; + + args->size = round_page(args->size); + /* + * XXX to avoid copying between 2 objs more than half the aperture size + * we don't allow allocations that are that big. This will be fixed + * eventually by intelligently falling back to cpu reads/writes in + * such cases. (linux allows this but does cpu maps in the ddx instead). + */ + if (args->size > dev_priv->max_gem_obj_size) + return (EFBIG); + + /* Allocate the new object */ + obj = drm_gem_object_alloc(dev, args->size); + if (obj == NULL) + return (ENOMEM); + + /* we give our reference to the handle */ + ret = drm_handle_create(file_priv, obj, &handle); + + if (ret == 0) + args->handle = handle; + else + drm_unref(&obj->uobj); + + return (ret); +} + +/** + * Reads data from the object referenced by handle. + * + * On error, the contents of *data are undefined. + */ +int +i915_gem_pread_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct inteldrm_softc *dev_priv = dev->dev_private; + struct drm_i915_gem_pread *args = data; + struct drm_obj *obj; + struct inteldrm_obj *obj_priv; + char *vaddr; + bus_space_handle_t bsh; + bus_size_t bsize; + voff_t offset; + int ret; + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (obj == NULL) + return (EBADF); + DRM_READLOCK(); + drm_hold_object(obj); + + /* + * Bounds check source. + */ + if (args->offset > obj->size || args->size > obj->size || + args->offset + args->size > obj->size) { + ret = EINVAL; + goto out; + } + + ret = i915_gem_object_pin(obj, 0, 1); + if (ret) { + goto out; + } + ret = i915_gem_object_set_to_gtt_domain(obj, 0, 1); + if (ret) + goto unpin; + + obj_priv = (struct inteldrm_obj *)obj; + offset = obj_priv->gtt_offset + args->offset; + + bsize = round_page(offset + args->size) - trunc_page(offset); + + if ((ret = agp_map_subregion(dev_priv->agph, + trunc_page(offset), bsize, &bsh)) != 0) + goto unpin; + vaddr = bus_space_vaddr(dev->bst, bsh); + if (vaddr == NULL) { + ret = EFAULT; + goto unmap; + } + + ret = copyout(vaddr + (offset & PAGE_MASK), + (char *)(uintptr_t)args->data_ptr, args->size); + +unmap: + agp_unmap_subregion(dev_priv->agph, bsh, bsize); +unpin: + i915_gem_object_unpin(obj); +out: + drm_unhold_and_unref(obj); + DRM_READUNLOCK(); + + return (ret); +} + + +/** + * Writes data to the object referenced by handle. + * + * On error, the contents of the buffer that were to be modified are undefined. + */ +int +i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct inteldrm_softc *dev_priv = dev->dev_private; + struct drm_i915_gem_pwrite *args = data; + struct drm_obj *obj; + struct inteldrm_obj *obj_priv; + char *vaddr; + bus_space_handle_t bsh; + bus_size_t bsize; + off_t offset; + int ret = 0; + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (obj == NULL) + return (EBADF); + DRM_READLOCK(); + drm_hold_object(obj); + + /* Bounds check destination. */ + if (args->offset > obj->size || args->size > obj->size || + args->offset + args->size > obj->size) { + ret = EINVAL; + goto out; + } + + ret = i915_gem_object_pin(obj, 0, 1); + if (ret) { + goto out; + } + ret = i915_gem_object_set_to_gtt_domain(obj, 1, 1); + if (ret) + goto unpin; + + obj_priv = (struct inteldrm_obj *)obj; + offset = obj_priv->gtt_offset + args->offset; + bsize = round_page(offset + args->size) - trunc_page(offset); + + if ((ret = agp_map_subregion(dev_priv->agph, + trunc_page(offset), bsize, &bsh)) != 0) + goto unpin; + vaddr = bus_space_vaddr(dev_priv->bst, bsh); + if (vaddr == NULL) { + ret = EFAULT; + goto unmap; + } + + ret = copyin((char *)(uintptr_t)args->data_ptr, + vaddr + (offset & PAGE_MASK), args->size); + + +unmap: + agp_unmap_subregion(dev_priv->agph, bsh, bsize); +unpin: + i915_gem_object_unpin(obj); +out: + drm_unhold_and_unref(obj); + DRM_READUNLOCK(); + + return (ret); +} + +/** + * Called when user space prepares to use an object with the CPU, either through + * the mmap ioctl's mapping or a GTT mapping. + */ +int +i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_i915_gem_set_domain *args = data; + struct drm_obj *obj; + u_int32_t read_domains = args->read_domains; + u_int32_t write_domain = args->write_domain; + int ret; + + /* + * Only handle setting domains to types we allow the cpu to see. + * while linux allows the CPU domain here, we only allow GTT since that + * is all that we let userland near. + * Also sanity check that having something in the write domain implies + * it's in the read domain, and only that read domain. + */ + if ((write_domain | read_domains) & ~I915_GEM_DOMAIN_GTT || + (write_domain != 0 && read_domains != write_domain)) + return (EINVAL); + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (obj == NULL) + return (EBADF); + drm_hold_object(obj); + + ret = i915_gem_object_set_to_gtt_domain(obj, write_domain != 0, 1); + + drm_unhold_and_unref(obj); + /* + * Silently promote `you're not bound, there was nothing to do' + * to success, since the client was just asking us to make sure + * everything was done. + */ + return ((ret == EINVAL) ? 0 : ret); +} + +int +i915_gem_gtt_map_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_i915_gem_mmap *args = data; + struct drm_obj *obj; + struct inteldrm_obj *obj_priv; + vaddr_t addr; + voff_t offset; + vsize_t end, nsize; + int ret; + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (obj == NULL) + return (EBADF); + + /* Since we are doing purely uvm-related operations here we do + * not need to hold the object, a reference alone is sufficient + */ + obj_priv = (struct inteldrm_obj *)obj; + + /* Check size. Also ensure that the object is not purgeable */ + if (args->size == 0 || args->offset > obj->size || args->size > + obj->size || (args->offset + args->size) > obj->size || + i915_obj_purgeable(obj_priv)) { + ret = EINVAL; + goto done; + } + + end = round_page(args->offset + args->size); + offset = trunc_page(args->offset); + nsize = end - offset; + + /* + * We give our reference from object_lookup to the mmap, so only + * must free it in the case that the map fails. + */ +#if !defined(__NetBSD__) + addr = uvm_map_hint(curproc, VM_PROT_READ | VM_PROT_WRITE); + ret = uvm_map_p(&curproc->p_vmspace->vm_map, &addr, nsize, &obj->uobj, + offset, 0, UVM_MAPFLAG(UVM_PROT_RW, UVM_PROT_RW, + UVM_INH_SHARE, UVM_ADV_RANDOM, 0), curproc); +#else /* !defined(__NetBSD__) */ + addr = curproc->p_emul->e_vm_default_addr(curproc, + (vaddr_t)curproc->p_vmspace->vm_daddr, nsize); + ret = uvm_map(&curproc->p_vmspace->vm_map, &addr, nsize, &obj->uobj, + offset, 0, UVM_MAPFLAG(UVM_PROT_RW, UVM_PROT_RW, + UVM_INH_SHARE, UVM_ADV_RANDOM, 0)); +#endif /* !defined(__NetBSD__) */ + +done: + if (ret == 0) + args->addr_ptr = (uint64_t) addr + (args->offset & PAGE_MASK); + else + drm_unref(&obj->uobj); + + return (ret); +} + +/* called locked */ +void +i915_gem_object_move_to_active(struct drm_obj *obj) +{ + struct drm_device *dev = obj->dev; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + struct inteldrm_fence *reg; + u_int32_t seqno = dev_priv->mm.next_gem_seqno; + + MUTEX_ASSERT_LOCKED(&dev_priv->request_lock); + MUTEX_ASSERT_LOCKED(&dev_priv->list_lock); + + /* Add a reference if we're newly entering the active list. */ + if (!inteldrm_is_active(obj_priv)) { + drm_ref(&obj->uobj); + atomic_setbits_int(&obj->do_flags, I915_ACTIVE); + } + + if (inteldrm_needs_fence(obj_priv)) { + reg = &dev_priv->fence_regs[obj_priv->fence_reg]; + reg->last_rendering_seqno = seqno; + } + if (obj->write_domain) + obj_priv->last_write_seqno = seqno; + + /* Move from whatever list we were on to the tail of execution. */ + i915_move_to_tail(obj_priv, &dev_priv->mm.active_list); + obj_priv->last_rendering_seqno = seqno; +} + +void +i915_gem_object_move_off_active(struct drm_obj *obj) +{ + struct drm_device *dev = obj->dev; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + struct inteldrm_fence *reg; + + MUTEX_ASSERT_LOCKED(&dev_priv->list_lock); + DRM_OBJ_ASSERT_LOCKED(obj); + + obj_priv->last_rendering_seqno = 0; + /* if we have a fence register, then reset the seqno */ + if (obj_priv->fence_reg != I915_FENCE_REG_NONE) { + reg = &dev_priv->fence_regs[obj_priv->fence_reg]; + reg->last_rendering_seqno = 0; + } + if (obj->write_domain == 0) + obj_priv->last_write_seqno = 0; +} + +/* If you call this on an object that you have held, you must have your own + * reference, not just the reference from the active list. + */ +void +i915_gem_object_move_to_inactive(struct drm_obj *obj) +{ + struct drm_device *dev = obj->dev; + struct inteldrm_softc *dev_priv = dev->dev_private; + + mtx_enter(&dev_priv->list_lock); + drm_lock_obj(obj); + /* unlocks list lock and object lock */ + i915_gem_object_move_to_inactive_locked(obj); +} + +/* called locked */ +void +i915_gem_object_move_to_inactive_locked(struct drm_obj *obj) +{ + struct drm_device *dev = obj->dev; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + + MUTEX_ASSERT_LOCKED(&dev_priv->list_lock); + DRM_OBJ_ASSERT_LOCKED(obj); + + inteldrm_verify_inactive(dev_priv, __FILE__, __LINE__); + if (obj_priv->pin_count != 0) + i915_list_remove(obj_priv); + else + i915_move_to_tail(obj_priv, &dev_priv->mm.inactive_list); + + i915_gem_object_move_off_active(obj); + atomic_clearbits_int(&obj->do_flags, I915_FENCED_EXEC); + + KASSERT((obj->do_flags & I915_GPU_WRITE) == 0); + /* unlock because this unref could recurse */ + mtx_leave(&dev_priv->list_lock); + if (inteldrm_is_active(obj_priv)) { + atomic_clearbits_int(&obj->do_flags, + I915_ACTIVE); + drm_unref_locked(&obj->uobj); + } else { + drm_unlock_obj(obj); + } + inteldrm_verify_inactive(dev_priv, __FILE__, __LINE__); +} + +void +inteldrm_purge_obj(struct drm_obj *obj) +{ + DRM_ASSERT_HELD(obj); + /* + * may sleep. We free here instead of deactivate (which + * the madvise() syscall would do) because in this case + * (userland bo cache and GL_APPLE_object_purgeable objects in + * OpenGL) the pages are defined to be freed if they were cleared + * so kill them and free up the memory + */ +#if !defined(__NetBSD__) + simple_lock(&obj->uao->vmobjlock); + obj->uao->pgops->pgo_flush(obj->uao, 0, obj->size, + PGO_ALLPAGES | PGO_FREE); + simple_unlock(&obj->uao->vmobjlock); +#else /* !defined(__NetBSD__) */ + mutex_enter(&obj->uao->vmobjlock); + obj->uao->pgops->pgo_put(obj->uao, 0, obj->size, + PGO_ALLPAGES | PGO_FREE); + /* the mutex is already released (cf. uao_put in uvm_aobj.c) */ + KASSERT(!mutex_owned(&obj->uao->vmobjlock)); +#endif /* !defined(__NetBSD__) */ + + /* + * If flush failed, it may have halfway through, so just + * always mark as purged + */ + atomic_setbits_int(&obj->do_flags, I915_PURGED); +} + +void +inteldrm_process_flushing(struct inteldrm_softc *dev_priv, + u_int32_t flush_domains) +{ + struct inteldrm_obj *obj_priv, *next; + + MUTEX_ASSERT_LOCKED(&dev_priv->request_lock); + mtx_enter(&dev_priv->list_lock); + for (obj_priv = TAILQ_FIRST(&dev_priv->mm.gpu_write_list); + obj_priv != TAILQ_END(&dev_priv->mm.gpu_write_list); + obj_priv = next) { + struct drm_obj *obj = &(obj_priv->obj); + + next = TAILQ_NEXT(obj_priv, write_list); + + if ((obj->write_domain & flush_domains)) { + TAILQ_REMOVE(&dev_priv->mm.gpu_write_list, + obj_priv, write_list); + atomic_clearbits_int(&obj->do_flags, + I915_GPU_WRITE); + i915_gem_object_move_to_active(obj); + obj->write_domain = 0; + /* if we still need the fence, update LRU */ + if (inteldrm_needs_fence(obj_priv)) { + KASSERT(obj_priv->fence_reg != + I915_FENCE_REG_NONE); + /* we have a fence, won't sleep, can't fail + * since we have the fence we no not need + * to have the object held + */ + i915_gem_get_fence_reg(obj, 1); + } + + } + } + mtx_leave(&dev_priv->list_lock); +} + +/** + * Creates a new sequence number, emitting a write of it to the status page + * plus an interrupt, which will trigger and interrupt if they are currently + * enabled. + * + * Must be called with struct_lock held. + * + * Returned sequence numbers are nonzero on success. + */ +uint32_t +i915_add_request(struct inteldrm_softc *dev_priv) +{ + struct inteldrm_request *request; + uint32_t seqno; + int was_empty; + + MUTEX_ASSERT_LOCKED(&dev_priv->request_lock); + + request = drm_calloc(1, sizeof(*request)); + if (request == NULL) { + printf("%s: failed to allocate request\n", __func__); + return 0; + } + + /* Grab the seqno we're going to make this request be, and bump the + * next (skipping 0 so it can be the reserved no-seqno value). + */ + seqno = dev_priv->mm.next_gem_seqno; + dev_priv->mm.next_gem_seqno++; + if (dev_priv->mm.next_gem_seqno == 0) + dev_priv->mm.next_gem_seqno++; + + BEGIN_LP_RING(4); + OUT_RING(MI_STORE_DWORD_INDEX); + OUT_RING(I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT); + OUT_RING(seqno); + + OUT_RING(MI_USER_INTERRUPT); + ADVANCE_LP_RING(); + + DRM_DEBUG("%d\n", seqno); + + /* XXX request timing for throttle */ + request->seqno = seqno; + was_empty = TAILQ_EMPTY(&dev_priv->mm.request_list); + TAILQ_INSERT_TAIL(&dev_priv->mm.request_list, request, list); + + if (dev_priv->mm.suspended == 0) { + if (was_empty) + timeout_add_sec(&dev_priv->mm.retire_timer, 1); + /* XXX was_empty? */ + timeout_add_msec(&dev_priv->mm.hang_timer, 750); + } + return seqno; +} + +/** + * Moves buffers associated only with the given active seqno from the active + * to inactive list, potentially freeing them. + * + * called with and sleeps with the drm_lock. + */ +void +i915_gem_retire_request(struct inteldrm_softc *dev_priv, + struct inteldrm_request *request) +{ + struct inteldrm_obj *obj_priv; + + MUTEX_ASSERT_LOCKED(&dev_priv->request_lock); + mtx_enter(&dev_priv->list_lock); + /* Move any buffers on the active list that are no longer referenced + * by the ringbuffer to the flushing/inactive lists as appropriate. */ + while ((obj_priv = TAILQ_FIRST(&dev_priv->mm.active_list)) != NULL) { + struct drm_obj *obj = &obj_priv->obj; + + /* If the seqno being retired doesn't match the oldest in the + * list, then the oldest in the list must still be newer than + * this seqno. + */ + if (obj_priv->last_rendering_seqno != request->seqno) + break; + + drm_lock_obj(obj); + /* + * If we're now clean and can be read from, move inactive, + * else put on the flushing list to signify that we're not + * available quite yet. + */ + if (obj->write_domain != 0) { + KASSERT(inteldrm_is_active(obj_priv)); + i915_move_to_tail(obj_priv, + &dev_priv->mm.flushing_list); + i915_gem_object_move_off_active(obj); + drm_unlock_obj(obj); + } else { + /* unlocks object for us and drops ref */ + i915_gem_object_move_to_inactive_locked(obj); + mtx_enter(&dev_priv->list_lock); + } + } + mtx_leave(&dev_priv->list_lock); +} + +/** + * This function clears the request list as sequence numbers are passed. + */ +void +i915_gem_retire_requests(struct inteldrm_softc *dev_priv) +{ + struct inteldrm_request *request; + uint32_t seqno; + + if (dev_priv->hw_status_page == NULL) + return; + + seqno = i915_get_gem_seqno(dev_priv); + + mtx_enter(&dev_priv->request_lock); + while ((request = TAILQ_FIRST(&dev_priv->mm.request_list)) != NULL) { + if (i915_seqno_passed(seqno, request->seqno) || + dev_priv->mm.wedged) { + TAILQ_REMOVE(&dev_priv->mm.request_list, request, list); + i915_gem_retire_request(dev_priv, request); + mtx_leave(&dev_priv->request_lock); + + drm_free(request); + mtx_enter(&dev_priv->request_lock); + } else + break; + } + mtx_leave(&dev_priv->request_lock); +} + +void +i915_gem_retire_work_handler(void *arg1, void *unused) +{ + struct inteldrm_softc *dev_priv = arg1; + + i915_gem_retire_requests(dev_priv); + if (!TAILQ_EMPTY(&dev_priv->mm.request_list)) + timeout_add_sec(&dev_priv->mm.retire_timer, 1); +} + +/** + * Waits for a sequence number to be signaled, and cleans up the + * request and object lists appropriately for that event. + * + * Called locked, sleeps with it. + */ +int +i915_wait_request(struct inteldrm_softc *dev_priv, uint32_t seqno, + int interruptible) +{ + int ret = 0; + + /* Check first because poking a wedged chip is bad. */ + if (dev_priv->mm.wedged) + return (EIO); + + if (seqno == dev_priv->mm.next_gem_seqno) { + mtx_enter(&dev_priv->request_lock); + seqno = i915_add_request(dev_priv); + mtx_leave(&dev_priv->request_lock); + if (seqno == 0) + return (ENOMEM); + } + + if (!i915_seqno_passed(i915_get_gem_seqno(dev_priv), seqno)) { + mtx_enter(&dev_priv->user_irq_lock); + i915_user_irq_get(dev_priv); + while (ret == 0) { + if (i915_seqno_passed(i915_get_gem_seqno(dev_priv), + seqno) || dev_priv->mm.wedged) + break; +#if !defined(__NetBSD__) + ret = msleep(dev_priv, &dev_priv->user_irq_lock, + PZERO | (interruptible ? PCATCH : 0), "gemwt", 0); +#else /* !defined(__NetBSD__) */ + if (interruptible) + ret = cv_wait_sig(&dev_priv->condvar, + &dev_priv->user_irq_lock); + else + cv_wait(&dev_priv->condvar, + &dev_priv->user_irq_lock); +#endif /* !defined(__NetBSD__) */ + } + i915_user_irq_put(dev_priv); + mtx_leave(&dev_priv->user_irq_lock); + } + if (dev_priv->mm.wedged) + ret = EIO; + + /* Directly dispatch request retiring. While we have the work queue + * to handle this, the waiter on a request often wants an associated + * buffer to have made it to the inactive list, and we would need + * a separate wait queue to handle that. + */ + if (ret == 0) + i915_gem_retire_requests(dev_priv); + + return (ret); +} + +/* + * flush and invalidate the provided domains + * if we have successfully queued a gpu flush, then we return a seqno from + * the request. else (failed or just cpu flushed) we return 0. + */ +u_int32_t +i915_gem_flush(struct inteldrm_softc *dev_priv, uint32_t invalidate_domains, + uint32_t flush_domains) +{ + uint32_t cmd; + int ret = 0; + + if (flush_domains & I915_GEM_DOMAIN_CPU) + inteldrm_chipset_flush(dev_priv); + if (((invalidate_domains | flush_domains) & I915_GEM_GPU_DOMAINS) == 0) + return (0); + /* + * read/write caches: + * + * I915_GEM_DOMAIN_RENDER is always invalidated, but is + * only flushed if MI_NO_WRITE_FLUSH is unset. On 965, it is + * also flushed at 2d versus 3d pipeline switches. + * + * read-only caches: + * + * I915_GEM_DOMAIN_SAMPLER is flushed on pre-965 if + * MI_READ_FLUSH is set, and is always flushed on 965. + * + * I915_GEM_DOMAIN_COMMAND may not exist? + * + * I915_GEM_DOMAIN_INSTRUCTION, which exists on 965, is + * invalidated when MI_EXE_FLUSH is set. + * + * I915_GEM_DOMAIN_VERTEX, which exists on 965, is + * invalidated with every MI_FLUSH. + * + * TLBs: + * + * On 965, TLBs associated with I915_GEM_DOMAIN_COMMAND + * and I915_GEM_DOMAIN_CPU in are invalidated at PTE write and + * I915_GEM_DOMAIN_RENDER and I915_GEM_DOMAIN_SAMPLER + * are flushed at any MI_FLUSH. + */ + + cmd = MI_FLUSH | MI_NO_WRITE_FLUSH; + if ((invalidate_domains | flush_domains) & + I915_GEM_DOMAIN_RENDER) + cmd &= ~MI_NO_WRITE_FLUSH; + /* + * On the 965, the sampler cache always gets flushed + * and this bit is reserved. + */ + if (!IS_I965G(dev_priv) && + invalidate_domains & I915_GEM_DOMAIN_SAMPLER) + cmd |= MI_READ_FLUSH; + if (invalidate_domains & I915_GEM_DOMAIN_INSTRUCTION) + cmd |= MI_EXE_FLUSH; + + mtx_enter(&dev_priv->request_lock); + BEGIN_LP_RING(2); + OUT_RING(cmd); + OUT_RING(MI_NOOP); + ADVANCE_LP_RING(); + + /* if this is a gpu flush, process the results */ + if (flush_domains & I915_GEM_GPU_DOMAINS) { + inteldrm_process_flushing(dev_priv, flush_domains); + ret = i915_add_request(dev_priv); + } + mtx_leave(&dev_priv->request_lock); + + return (ret); +} + +/** + * Unbinds an object from the GTT aperture. + * + * XXX track dirty and pass down to uvm (note, DONTNEED buffers are clean). + */ +int +i915_gem_object_unbind(struct drm_obj *obj, int interruptible) +{ + struct drm_device *dev = obj->dev; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + int ret = 0; + + DRM_ASSERT_HELD(obj); + /* + * if it's already unbound, or we've already done lastclose, just + * let it happen. XXX does this fail to unwire? + */ + if (obj_priv->dmamap == NULL || dev_priv->agpdmat == NULL) + return 0; + + if (obj_priv->pin_count != 0) { + DRM_ERROR("Attempting to unbind pinned buffer\n"); + return (EINVAL); + } + + KASSERT(!i915_obj_purged(obj_priv)); + + /* Move the object to the CPU domain to ensure that + * any possible CPU writes while it's not in the GTT + * are flushed when we go to remap it. This will + * also ensure that all pending GPU writes are finished + * before we unbind. + */ + ret = i915_gem_object_set_to_cpu_domain(obj, 1, interruptible); + if (ret) + return ret; + + KASSERT(!inteldrm_is_active(obj_priv)); + + /* if it's purgeable don't bother dirtying the pages */ + if (i915_obj_purgeable(obj_priv)) + atomic_clearbits_int(&obj->do_flags, I915_DIRTY); + /* + * unload the map, then unwire the backing object. + */ + i915_gem_save_bit_17_swizzle(obj); + bus_dmamap_unload(dev_priv->agpdmat, obj_priv->dmamap); + uvm_objunwire(obj->uao, 0, obj->size); + /* XXX persistent dmamap worth the memory? */ + bus_dmamap_destroy(dev_priv->agpdmat, obj_priv->dmamap); + obj_priv->dmamap = NULL; + free(obj_priv->dma_segs, M_DRM); + obj_priv->dma_segs = NULL; + /* XXX this should change whether we tell uvm the page is dirty */ + atomic_clearbits_int(&obj->do_flags, I915_DIRTY); + + obj_priv->gtt_offset = 0; + atomic_dec(&dev->gtt_count); + atomic_sub(obj->size, &dev->gtt_memory); + + /* Remove ourselves from any LRU list if present. */ + i915_list_remove((struct inteldrm_obj *)obj); + + if (i915_obj_purgeable(obj_priv)) + inteldrm_purge_obj(obj); + + return (0); +} + +int +i915_gem_evict_something(struct inteldrm_softc *dev_priv, size_t min_size, + int interruptible) +{ + struct drm_obj *obj; + struct inteldrm_request *request; + struct inteldrm_obj *obj_priv; + u_int32_t seqno; + int ret = 0, write_domain = 0; + + for (;;) { + i915_gem_retire_requests(dev_priv); + + /* If there's an inactive buffer available now, grab it + * and be done. + */ + obj = i915_gem_find_inactive_object(dev_priv, min_size); + if (obj != NULL) { + obj_priv = (struct inteldrm_obj *)obj; + /* find inactive object returns the object with a + * reference for us, and held + */ + KASSERT(obj_priv->pin_count == 0); + KASSERT(!inteldrm_is_active(obj_priv)); + DRM_ASSERT_HELD(obj); + + /* Wait on the rendering and unbind the buffer. */ + ret = i915_gem_object_unbind(obj, interruptible); + drm_unhold_and_unref(obj); + return (ret); + } + + /* If we didn't get anything, but the ring is still processing + * things, wait for one of those things to finish and hopefully + * leave us a buffer to evict. + */ + mtx_enter(&dev_priv->request_lock); + if ((request = TAILQ_FIRST(&dev_priv->mm.request_list)) + != NULL) { + seqno = request->seqno; + mtx_leave(&dev_priv->request_lock); + + ret = i915_wait_request(dev_priv, seqno, interruptible); + if (ret) + return (ret); + + continue; + } + mtx_leave(&dev_priv->request_lock); + + /* If we didn't have anything on the request list but there + * are buffers awaiting a flush, emit one and try again. + * When we wait on it, those buffers waiting for that flush + * will get moved to inactive. + */ + mtx_enter(&dev_priv->list_lock); + TAILQ_FOREACH(obj_priv, &dev_priv->mm.flushing_list, list) { + obj = &obj_priv->obj; + if (obj->size >= min_size) { + write_domain = obj->write_domain; + break; + } + obj = NULL; + } + mtx_leave(&dev_priv->list_lock); + + if (write_domain) { + if (i915_gem_flush(dev_priv, write_domain, + write_domain) == 0) + return (ENOMEM); + continue; + } + + /* + * If we didn't do any of the above, there's no single buffer + * large enough to swap out for the new one, so just evict + * everything and start again. (This should be rare.) + */ + if (!TAILQ_EMPTY(&dev_priv->mm.inactive_list)) + return (i915_gem_evict_inactive(dev_priv, + interruptible)); + else + return (i915_gem_evict_everything(dev_priv, + interruptible)); + } + /* NOTREACHED */ +} + +struct drm_obj * +i915_gem_find_inactive_object(struct inteldrm_softc *dev_priv, + size_t min_size) +{ + struct drm_obj *obj, *best = NULL, *first = NULL; + struct inteldrm_obj *obj_priv; + + /* + * We don't need references to the object as long as we hold the list + * lock, they won't disappear until we release the lock. + */ + mtx_enter(&dev_priv->list_lock); + TAILQ_FOREACH(obj_priv, &dev_priv->mm.inactive_list, list) { + obj = &obj_priv->obj; + if (obj->size >= min_size) { + if ((!inteldrm_is_dirty(obj_priv) || + i915_obj_purgeable(obj_priv)) && + (best == NULL || obj->size < best->size)) { + best = obj; + if (best->size == min_size) + break; + } + } + if (first == NULL) + first = obj; + } + if (best == NULL) + best = first; + if (best) { + drm_ref(&best->uobj); + /* + * if we couldn't grab it, we may as well fail and go + * onto the next step for the sake of simplicity. + */ + if (drm_try_hold_object(best) == 0) { + drm_unref(&best->uobj); + best = NULL; + } + } + mtx_leave(&dev_priv->list_lock); + return (best); +} + +int +i915_gem_evict_everything(struct inteldrm_softc *dev_priv, int interruptible) +{ + u_int32_t seqno; + int ret; + + if (TAILQ_EMPTY(&dev_priv->mm.inactive_list) && + TAILQ_EMPTY(&dev_priv->mm.flushing_list) && + TAILQ_EMPTY(&dev_priv->mm.active_list)) + return (ENOSPC); + + seqno = i915_gem_flush(dev_priv, I915_GEM_GPU_DOMAINS, + I915_GEM_GPU_DOMAINS); + if (seqno == 0) + return (ENOMEM); + + if ((ret = i915_wait_request(dev_priv, seqno, interruptible)) != 0 || + (ret = i915_gem_evict_inactive(dev_priv, interruptible)) != 0) + return (ret); + + /* + * All lists should be empty because we flushed the whole queue, then + * we evicted the whole shebang, only pinned objects are still bound. + */ + KASSERT(TAILQ_EMPTY(&dev_priv->mm.inactive_list)); + KASSERT(TAILQ_EMPTY(&dev_priv->mm.flushing_list)); + KASSERT(TAILQ_EMPTY(&dev_priv->mm.active_list)); + + return (0); +} +/* + * return required GTT alignment for an object, taking into account potential + * fence register needs + */ +bus_size_t +i915_gem_get_gtt_alignment(struct drm_obj *obj) +{ + struct drm_device *dev = obj->dev; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + bus_size_t start, i; + + /* + * Minimum alignment is 4k (GTT page size), but fence registers may + * modify this + */ + if (IS_I965G(dev_priv) || obj_priv->tiling_mode == I915_TILING_NONE) + return (4096); + + /* + * Older chips need to be aligned to the size of the smallest fence + * register that can contain the object. + */ + if (IS_I9XX(dev_priv)) + start = 1024 * 1024; + else + start = 512 * 1024; + + for (i = start; i < obj->size; i <<= 1) + ; + + return (i); +} + +void +i965_write_fence_reg(struct inteldrm_fence *reg) +{ + struct drm_obj *obj = reg->obj; + struct drm_device *dev = obj->dev; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + int regnum = obj_priv->fence_reg; + u_int64_t val; + + val = (uint64_t)((obj_priv->gtt_offset + obj->size - 4096) & + 0xfffff000) << 32; + val |= obj_priv->gtt_offset & 0xfffff000; + val |= ((obj_priv->stride / 128) - 1) << I965_FENCE_PITCH_SHIFT; + if (obj_priv->tiling_mode == I915_TILING_Y) + val |= 1 << I965_FENCE_TILING_Y_SHIFT; + val |= I965_FENCE_REG_VALID; + + I915_WRITE64(FENCE_REG_965_0 + (regnum * 8), val); +} + +void +i915_write_fence_reg(struct inteldrm_fence *reg) +{ + struct drm_obj *obj = reg->obj; + struct drm_device *dev = obj->dev; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + bus_size_t fence_reg; + u_int32_t val; + u_int32_t pitch_val; + int regnum = obj_priv->fence_reg; + int tile_width; + + if ((obj_priv->gtt_offset & ~I915_FENCE_START_MASK) || + (obj_priv->gtt_offset & (obj->size - 1))) { + DRM_ERROR("object 0x%jx not 1M or size (0x%zx) aligned\n", + (uintmax_t)obj_priv->gtt_offset, obj->size); + return; + } + + if (obj_priv->tiling_mode == I915_TILING_Y && + HAS_128_BYTE_Y_TILING(dev_priv)) + tile_width = 128; + else + tile_width = 512; + + /* Note: pitch better be a power of two tile widths */ + pitch_val = obj_priv->stride / tile_width; + pitch_val = ffs(pitch_val) - 1; + + /* XXX print more */ + if ((obj_priv->tiling_mode == I915_TILING_Y && + HAS_128_BYTE_Y_TILING(dev_priv) && + pitch_val > I830_FENCE_MAX_PITCH_VAL) || + pitch_val > I915_FENCE_MAX_PITCH_VAL) + printf("%s: invalid pitch provided", __func__); + + val = obj_priv->gtt_offset; + if (obj_priv->tiling_mode == I915_TILING_Y) + val |= 1 << I830_FENCE_TILING_Y_SHIFT; + val |= I915_FENCE_SIZE_BITS(obj->size); + val |= pitch_val << I830_FENCE_PITCH_SHIFT; + val |= I830_FENCE_REG_VALID; + + if (regnum < 8) + fence_reg = FENCE_REG_830_0 + (regnum * 4); + else + fence_reg = FENCE_REG_945_8 + ((regnum - 8) * 4); + I915_WRITE(fence_reg, val); +} + +void +i830_write_fence_reg(struct inteldrm_fence *reg) +{ + struct drm_obj *obj = reg->obj; + struct drm_device *dev = obj->dev; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + int regnum = obj_priv->fence_reg; + u_int32_t pitch_val, val; + + if ((obj_priv->gtt_offset & ~I830_FENCE_START_MASK) || + (obj_priv->gtt_offset & (obj->size - 1))) { + DRM_ERROR("object 0x%08jx not 512K or size aligned 0x%zx\n", + (uintmax_t)obj_priv->gtt_offset, obj->size); + return; + } + + pitch_val = ffs(obj_priv->stride / 128) - 1; + + val = obj_priv->gtt_offset; + if (obj_priv->tiling_mode == I915_TILING_Y) + val |= 1 << I830_FENCE_TILING_Y_SHIFT; + val |= I830_FENCE_SIZE_BITS(obj->size); + val |= pitch_val << I830_FENCE_PITCH_SHIFT; + val |= I830_FENCE_REG_VALID; + + I915_WRITE(FENCE_REG_830_0 + (regnum * 4), val); + +} + +/* + * i915_gem_get_fence_reg - set up a fence reg for an object + * + * When mapping objects through the GTT, userspace wants to be able to write + * to them without having to worry about swizzling if the object is tiled. + * + * This function walks the fence regs looking for a free one, stealing one + * if it can't find any. + * + * It then sets up the reg based on the object's properties: address, pitch + * and tiling format. + */ +int +i915_gem_get_fence_reg(struct drm_obj *obj, int interruptible) +{ + struct drm_device *dev = obj->dev; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + struct inteldrm_obj *old_obj_priv = NULL; + struct drm_obj *old_obj = NULL; + struct inteldrm_fence *reg = NULL; + int i, ret, avail; + + /* If our fence is getting used, just update our place in the LRU */ + if (obj_priv->fence_reg != I915_FENCE_REG_NONE) { + mtx_enter(&dev_priv->fence_lock); + reg = &dev_priv->fence_regs[obj_priv->fence_reg]; + + TAILQ_REMOVE(&dev_priv->mm.fence_list, reg, list); + TAILQ_INSERT_TAIL(&dev_priv->mm.fence_list, reg, list); + mtx_leave(&dev_priv->fence_lock); + return (0); + } + + DRM_ASSERT_HELD(obj); + switch (obj_priv->tiling_mode) { + case I915_TILING_NONE: + DRM_ERROR("allocating a fence for non-tiled object?\n"); + break; + case I915_TILING_X: + if (obj_priv->stride == 0) + return (EINVAL); + if (obj_priv->stride & (512 - 1)) + DRM_ERROR("object 0x%08jx is X tiled but has non-512B" + " pitch\n", (uintmax_t)obj_priv->gtt_offset); + break; + case I915_TILING_Y: + if (obj_priv->stride == 0) + return (EINVAL); + if (obj_priv->stride & (128 - 1)) + DRM_ERROR("object 0x%08jx is Y tiled but has non-128B" + " pitch\n", (uintmax_t)obj_priv->gtt_offset); + break; + } + +again: + /* First try to find a free reg */ + avail = 0; + mtx_enter(&dev_priv->fence_lock); + for (i = dev_priv->fence_reg_start; i < dev_priv->num_fence_regs; i++) { + reg = &dev_priv->fence_regs[i]; + if (reg->obj == NULL) + break; + + old_obj_priv = (struct inteldrm_obj *)reg->obj; + if (old_obj_priv->pin_count == 0) + avail++; + } + + /* None available, try to steal one or wait for a user to finish */ + if (i == dev_priv->num_fence_regs) { + if (avail == 0) { + mtx_leave(&dev_priv->fence_lock); + return (ENOMEM); + } + + TAILQ_FOREACH(reg, &dev_priv->mm.fence_list, + list) { + old_obj = reg->obj; + old_obj_priv = (struct inteldrm_obj *)old_obj; + + if (old_obj_priv->pin_count) + continue; + + /* Ref it so that wait_rendering doesn't free it under + * us. if we can't hold it, it may change state soon + * so grab the next one. + */ + drm_ref(&old_obj->uobj); + if (drm_try_hold_object(old_obj) == 0) { + drm_unref(&old_obj->uobj); + continue; + } + + break; + } + mtx_leave(&dev_priv->fence_lock); + + /* if we tried all of them, give it another whirl. we failed to + * get a hold this go round. + */ + if (reg == NULL) + goto again; + + ret = i915_gem_object_put_fence_reg(old_obj, interruptible); + drm_unhold_and_unref(old_obj); + if (ret != 0) + return (ret); + /* we should have freed one up now, so relock and re-search */ + goto again; + } + + /* + * Here we will either have found a register in the first + * loop, or we will have waited for one and in the second case + * and thus have grabbed the object in question, freed the register + * then redone the second loop (having relocked the fence list). + * Therefore at this point it is impossible to have a null value + * in reg. + */ + KASSERT(reg != NULL); + + obj_priv->fence_reg = i; + reg->obj = obj; + TAILQ_INSERT_TAIL(&dev_priv->mm.fence_list, reg, list); + + if (IS_I965G(dev_priv)) + i965_write_fence_reg(reg); + else if (IS_I9XX(dev_priv)) + i915_write_fence_reg(reg); + else + i830_write_fence_reg(reg); + mtx_leave(&dev_priv->fence_lock); + + return 0; +} + +int +i915_gem_object_put_fence_reg(struct drm_obj *obj, int interruptible) +{ + struct drm_device *dev = obj->dev; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + struct inteldrm_fence *reg; + int ret; + + DRM_ASSERT_HELD(obj); + if (obj_priv->fence_reg == I915_FENCE_REG_NONE) + return (0); + + /* + * If the last execbuffer we did on the object needed a fence then + * we must emit a flush. + */ + if (inteldrm_needs_fence(obj_priv)) { + ret = i915_gem_object_flush_gpu_write_domain(obj, 1, + interruptible, 0); + if (ret != 0) + return (ret); + } + + /* if rendering is queued up that depends on the fence, wait for it */ + reg = &dev_priv->fence_regs[obj_priv->fence_reg]; + if (reg->last_rendering_seqno != 0) { + ret = i915_wait_request(dev_priv, reg->last_rendering_seqno, + interruptible); + if (ret != 0) + return (ret); + } + + /* tiling changed, must wipe userspace mappings */ + if ((obj->write_domain | obj->read_domains) & I915_GEM_DOMAIN_GTT) { + inteldrm_wipe_mappings(obj); + if (obj->write_domain == I915_GEM_DOMAIN_GTT) + obj->write_domain = 0; + } + + mtx_enter(&dev_priv->fence_lock); + if (IS_I965G(dev_priv)) { + I915_WRITE64(FENCE_REG_965_0 + (obj_priv->fence_reg * 8), 0); + } else { + u_int32_t fence_reg; + + if (obj_priv->fence_reg < 8) + fence_reg = FENCE_REG_830_0 + obj_priv->fence_reg * 4; + else + fence_reg = FENCE_REG_945_8 + + (obj_priv->fence_reg - 8) * 4; + I915_WRITE(fence_reg , 0); + } + + reg->obj = NULL; + TAILQ_REMOVE(&dev_priv->mm.fence_list, reg, list); + obj_priv->fence_reg = I915_FENCE_REG_NONE; + mtx_leave(&dev_priv->fence_lock); + atomic_clearbits_int(&obj->do_flags, I915_FENCE_INVALID); + + return (0); +} + +int +inteldrm_fault(struct drm_obj *obj, struct uvm_faultinfo *ufi, off_t offset, + vaddr_t vaddr, vm_page_t *pps, int npages, int centeridx, + vm_prot_t access_type, int flags) +{ + struct drm_device *dev = obj->dev; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + paddr_t paddr; + int lcv, ret; + int write = !!(access_type & VM_PROT_WRITE); + vm_prot_t mapprot; + boolean_t locked = TRUE; + +#if defined(__NetBSD__) + UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist); +#endif /* defined(__NetBSD__) */ + + /* Are we about to suspend?, if so wait until we're done */ + if (dev_priv->sc_flags & INTELDRM_QUIET) { + /* we're about to sleep, unlock the map etc */ + uvmfault_unlockall(ufi, NULL, &obj->uobj, NULL); + while (dev_priv->sc_flags & INTELDRM_QUIET) + tsleep(&dev_priv->flags, 0, "intelflt", 0); + dev_priv->entries++; + /* + * relock so we're in the same state we would be in if we + * were not quiesced before + */ + locked = uvmfault_relock(ufi); + if (locked) { + drm_lock_obj(obj); + } else { + dev_priv->entries--; + if (dev_priv->sc_flags & INTELDRM_QUIET) + wakeup(&dev_priv->entries); + return (VM_PAGER_REFAULT); + } + } else { + dev_priv->entries++; + } + +#if !defined(__NetBSD__) + if (rw_enter(&dev->dev_lock, RW_NOSLEEP | RW_READ) != 0) { +#else /* !defined(__NetBSD__) */ + if (rw_tryenter(&dev->dev_lock, RW_READER) == 0) { +#endif /* !defined(__NetBSD__) */ + uvmfault_unlockall(ufi, NULL, &obj->uobj, NULL); + DRM_READLOCK(); + locked = uvmfault_relock(ufi); + if (locked) + drm_lock_obj(obj); + } + if (locked) + drm_hold_object_locked(obj); + else { /* obj already unlocked */ + dev_priv->entries--; + if (dev_priv->sc_flags & INTELDRM_QUIET) + wakeup(&dev_priv->entries); + return (VM_PAGER_REFAULT); + } + + /* we have a hold set on the object now, we can unlock so that we can + * sleep in binding and flushing. + */ + drm_unlock_obj(obj); + + if (obj_priv->dmamap != NULL && + (obj_priv->gtt_offset & (i915_gem_get_gtt_alignment(obj) - 1) || + (!i915_gem_object_fence_offset_ok(obj, obj_priv->tiling_mode)))) { + /* + * pinned objects are defined to have a sane alignment which can + * not change. + */ + KASSERT(obj_priv->pin_count == 0); + if ((ret = i915_gem_object_unbind(obj, 0))) + goto error; + } + + if (obj_priv->dmamap == NULL) { + ret = i915_gem_object_bind_to_gtt(obj, 0, 0); + if (ret) { + printf("%s: failed to bind\n", __func__); + goto error; + } + i915_gem_object_move_to_inactive(obj); + } + + /* + * We could only do this on bind so allow for map_buffer_range + * unsynchronised objects (where buffer suballocation + * is done by the GL application), however it gives coherency problems + * normally. + */ + ret = i915_gem_object_set_to_gtt_domain(obj, write, 0); + if (ret) { + printf("%s: failed to set to gtt (%d)\n", + __func__, ret); + goto error; + } + + mapprot = ufi->entry->protection; + /* + * if it's only a read fault, we only put ourselves into the gtt + * read domain, so make sure we fault again and set ourselves to write. + * this prevents us needing userland to do domain management and get + * it wrong, and makes us fully coherent with the gpu re mmap. + */ + if (write == 0) + mapprot &= ~VM_PROT_WRITE; + /* XXX try and be more efficient when we do this */ + for (lcv = 0 ; lcv < npages ; lcv++, offset += PAGE_SIZE, + vaddr += PAGE_SIZE) { + if ((flags & PGO_ALLPAGES) == 0 && lcv != centeridx) + continue; + + if (pps[lcv] == PGO_DONTCARE) + continue; + + paddr = dev->agp->base + obj_priv->gtt_offset + offset; + + UVMHIST_LOG(maphist, + " MAPPING: device: pm=%p, va=0x%lx, pa=0x%lx, at=%ld", + ufi->orig_map->pmap, vaddr, (u_long)paddr, mapprot); + if (pmap_enter(ufi->orig_map->pmap, vaddr, paddr, + mapprot, PMAP_CANFAIL | mapprot) != 0) { + drm_unhold_object(obj); + uvmfault_unlockall(ufi, ufi->entry->aref.ar_amap, + NULL, NULL); + DRM_READUNLOCK(); + dev_priv->entries--; + if (dev_priv->sc_flags & INTELDRM_QUIET) + wakeup(&dev_priv->entries); + uvm_wait("intelflt"); + return (VM_PAGER_REFAULT); + } + } +error: + drm_unhold_object(obj); + uvmfault_unlockall(ufi, ufi->entry->aref.ar_amap, NULL, NULL); + DRM_READUNLOCK(); + dev_priv->entries--; + if (dev_priv->sc_flags & INTELDRM_QUIET) + wakeup(&dev_priv->entries); + pmap_update(ufi->orig_map->pmap); + if (ret == EIO) { + /* + * EIO means we're wedged, so upon resetting the gpu we'll + * be alright and can refault. XXX only on resettable chips. + */ + ret = VM_PAGER_REFAULT; + } else if (ret) { + ret = VM_PAGER_ERROR; + } else { + ret = VM_PAGER_OK; + } + return (ret); +} + +void +inteldrm_wipe_mappings(struct drm_obj *obj) +{ +#if !defined(__NetBSD__) + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + struct drm_device *dev = obj->dev; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct vm_page *pg; +#endif /* !defined(__NetBSD__) */ + + DRM_ASSERT_HELD(obj); + /* make sure any writes hit the bus before we do whatever change + * that prompted us to kill the mappings. + */ + DRM_MEMORYBARRIER(); + /* nuke all our mappings. XXX optimise. */ +#if !defined(__NetBSD__) + for (pg = &dev_priv->pgs[atop(obj_priv->gtt_offset)]; pg != + &dev_priv->pgs[atop(obj_priv->gtt_offset + obj->size)]; pg++) + pmap_page_protect(pg, VM_PROT_NONE); +#endif /* !defined(__NetBSD__) */ +} + +/** + * Finds free space in the GTT aperture and binds the object there. + */ +int +i915_gem_object_bind_to_gtt(struct drm_obj *obj, bus_size_t alignment, + int interruptible) +{ + struct drm_device *dev = obj->dev; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + int ret; + + DRM_ASSERT_HELD(obj); + if (dev_priv->agpdmat == NULL) + return (EINVAL); + if (alignment == 0) { + alignment = i915_gem_get_gtt_alignment(obj); + } else if (alignment & (i915_gem_get_gtt_alignment(obj) - 1)) { + DRM_ERROR("Invalid object alignment requested %ju\n", + (uintmax_t)alignment); + return (EINVAL); + } + + /* Can't bind a purgeable buffer */ + if (i915_obj_purgeable(obj_priv)) { + printf("tried to bind purgeable buffer"); + return (EINVAL); + } + + if ((ret = bus_dmamap_create(dev_priv->agpdmat, obj->size, 1, + obj->size, 0, BUS_DMA_WAITOK, &obj_priv->dmamap)) != 0) { + DRM_ERROR("Failed to create dmamap\n"); + return (ret); + } + agp_bus_dma_set_alignment(dev_priv->agpdmat, obj_priv->dmamap, + alignment); + + search_free: + /* + * the helper function wires the uao then binds it to the aperture for + * us, so all we have to do is set up the dmamap then load it. + */ + ret = drm_gem_load_uao(dev_priv->agpdmat, obj_priv->dmamap, obj->uao, + obj->size, BUS_DMA_WAITOK | obj_priv->dma_flags, + &obj_priv->dma_segs); + /* XXX NOWAIT? */ + if (ret != 0) { + /* If the gtt is empty and we're still having trouble + * fitting our object in, we're out of memory. + */ + if (TAILQ_EMPTY(&dev_priv->mm.inactive_list) && + TAILQ_EMPTY(&dev_priv->mm.flushing_list) && + TAILQ_EMPTY(&dev_priv->mm.active_list)) { + DRM_ERROR("GTT full, but LRU list empty\n"); + goto error; + } + + ret = i915_gem_evict_something(dev_priv, obj->size, + interruptible); + if (ret != 0) + goto error; + goto search_free; + } + i915_gem_bit_17_swizzle(obj); + + obj_priv->gtt_offset = obj_priv->dmamap->dm_segs[0].ds_addr - + dev->agp->base; + + atomic_inc(&dev->gtt_count); + atomic_add(obj->size, &dev->gtt_memory); + + /* Assert that the object is not currently in any GPU domain. As it + * wasn't in the GTT, there shouldn't be any way it could have been in + * a GPU cache + */ + KASSERT((obj->read_domains & I915_GEM_GPU_DOMAINS) == 0); + KASSERT((obj->write_domain & I915_GEM_GPU_DOMAINS) == 0); + + return (0); + +error: + bus_dmamap_destroy(dev_priv->agpdmat, obj_priv->dmamap); + obj_priv->dmamap = NULL; + obj_priv->gtt_offset = 0; + return (ret); +} + +/* + * Flush the GPU write domain for the object if dirty, then wait for the + * rendering to complete. When this returns it is safe to unbind from the + * GTT or access from the CPU. + */ +int +i915_gem_object_flush_gpu_write_domain(struct drm_obj *obj, int pipelined, + int interruptible, int write) +{ + struct drm_device *dev = obj->dev; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + u_int32_t seqno; + int ret = 0; + + DRM_ASSERT_HELD(obj); + if ((obj->write_domain & I915_GEM_GPU_DOMAINS) != 0) { + /* + * Queue the GPU write cache flushing we need. + * This call will move stuff form the flushing list to the + * active list so all we need to is wait for it. + */ + (void)i915_gem_flush(dev_priv, 0, obj->write_domain); + KASSERT(obj->write_domain == 0); + } + + /* wait for queued rendering so we know it's flushed and bo is idle */ + if (pipelined == 0 && inteldrm_is_active(obj_priv)) { + if (write) { + seqno = obj_priv->last_rendering_seqno; + } else { + seqno = obj_priv->last_write_seqno; + } + ret = i915_wait_request(dev_priv, seqno, interruptible); + } + return (ret); +} + +/* + * Moves a single object to the GTT and possibly write domain. + * + * This function returns when the move is complete, including waiting on + * flushes to occur. + */ +int +i915_gem_object_set_to_gtt_domain(struct drm_obj *obj, int write, + int interruptible) +{ + struct drm_device *dev = obj->dev; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + int ret; + + DRM_ASSERT_HELD(obj); + /* Not valid to be called on unbound objects. */ + if (obj_priv->dmamap == NULL) + return (EINVAL); + /* Wait on any GPU rendering and flushing to occur. */ + if ((ret = i915_gem_object_flush_gpu_write_domain(obj, 0, + interruptible, write)) != 0) + return (ret); + + if (obj->write_domain == I915_GEM_DOMAIN_CPU) { + /* clflush the pages, and flush chipset cache */ + bus_dmamap_sync(dev_priv->agpdmat, obj_priv->dmamap, 0, + obj->size, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + inteldrm_chipset_flush(dev_priv); + obj->write_domain = 0; + } + + /* We're accessing through the gpu, so grab a new fence register or + * update the LRU. + */ + if (obj->do_flags & I915_FENCE_INVALID) { + ret = i915_gem_object_put_fence_reg(obj, interruptible); + if (ret) + return (ret); + } + if (obj_priv->tiling_mode != I915_TILING_NONE) + ret = i915_gem_get_fence_reg(obj, interruptible); + + /* + * If we're writing through the GTT domain then the CPU and GPU caches + * will need to be invalidated at next use. + * It should now be out of any other write domains and we can update + * to the correct ones + */ + KASSERT((obj->write_domain & ~I915_GEM_DOMAIN_GTT) == 0); + if (write) { + obj->read_domains = obj->write_domain = I915_GEM_DOMAIN_GTT; + atomic_setbits_int(&obj->do_flags, I915_DIRTY); + } else { + obj->read_domains |= I915_GEM_DOMAIN_GTT; + } + + return (ret); +} + +/* + * Moves a single object to the CPU read and possibly write domain. + * + * This function returns when the move is complete, including waiting on + * flushes to return. + */ +int +i915_gem_object_set_to_cpu_domain(struct drm_obj *obj, int write, + int interruptible) +{ + struct drm_device *dev = obj->dev; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + int ret; + + DRM_ASSERT_HELD(obj); + /* Wait on any GPU rendering and flushing to occur. */ + if ((ret = i915_gem_object_flush_gpu_write_domain(obj, 0, + interruptible, write)) != 0) + return (ret); + + if (obj->write_domain == I915_GEM_DOMAIN_GTT || + (write && obj->read_domains & I915_GEM_DOMAIN_GTT)) { + /* + * No actual flushing is required for the GTT write domain. + * Writes to it immeditately go to main memory as far as we + * know, so there's no chipset flush. It also doesn't land + * in render cache. + */ + inteldrm_wipe_mappings(obj); + if (obj->write_domain == I915_GEM_DOMAIN_GTT) + obj->write_domain = 0; + } + + /* remove the fence register since we're not using it anymore */ + if ((ret = i915_gem_object_put_fence_reg(obj, interruptible)) != 0) + return (ret); + + /* Flush the CPU cache if it's still invalid. */ + if ((obj->read_domains & I915_GEM_DOMAIN_CPU) == 0) { + bus_dmamap_sync(dev_priv->agpdmat, obj_priv->dmamap, 0, + obj->size, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + obj->read_domains |= I915_GEM_DOMAIN_CPU; + } + + /* + * It should now be out of any other write domain, and we can update + * the domain value for our changes. + */ + KASSERT((obj->write_domain & ~I915_GEM_DOMAIN_CPU) == 0); + + /* + * If we're writing through the CPU, then the GPU read domains will + * need to be invalidated at next use. + */ + if (write) + obj->read_domains = obj->write_domain = I915_GEM_DOMAIN_CPU; + + return (0); +} + +/* + * Set the next domain for the specified object. This + * may not actually perform the necessary flushing/invaliding though, + * as that may want to be batched with other set_domain operations + * + * This is (we hope) the only really tricky part of gem. The goal + * is fairly simple -- track which caches hold bits of the object + * and make sure they remain coherent. A few concrete examples may + * help to explain how it works. For shorthand, we use the notation + * (read_domains, write_domain), e.g. (CPU, CPU) to indicate the + * a pair of read and write domain masks. + * + * Case 1: the batch buffer + * + * 1. Allocated + * 2. Written by CPU + * 3. Mapped to GTT + * 4. Read by GPU + * 5. Unmapped from GTT + * 6. Freed + * + * Let's take these a step at a time + * + * 1. Allocated + * Pages allocated from the kernel may still have + * cache contents, so we set them to (CPU, CPU) always. + * 2. Written by CPU (using pwrite) + * The pwrite function calls set_domain (CPU, CPU) and + * this function does nothing (as nothing changes) + * 3. Mapped by GTT + * This function asserts that the object is not + * currently in any GPU-based read or write domains + * 4. Read by GPU + * i915_gem_execbuffer calls set_domain (COMMAND, 0). + * As write_domain is zero, this function adds in the + * current read domains (CPU+COMMAND, 0). + * flush_domains is set to CPU. + * invalidate_domains is set to COMMAND + * clflush is run to get data out of the CPU caches + * then i915_dev_set_domain calls i915_gem_flush to + * emit an MI_FLUSH and drm_agp_chipset_flush + * 5. Unmapped from GTT + * i915_gem_object_unbind calls set_domain (CPU, CPU) + * flush_domains and invalidate_domains end up both zero + * so no flushing/invalidating happens + * 6. Freed + * yay, done + * + * Case 2: The shared render buffer + * + * 1. Allocated + * 2. Mapped to GTT + * 3. Read/written by GPU + * 4. set_domain to (CPU,CPU) + * 5. Read/written by CPU + * 6. Read/written by GPU + * + * 1. Allocated + * Same as last example, (CPU, CPU) + * 2. Mapped to GTT + * Nothing changes (assertions find that it is not in the GPU) + * 3. Read/written by GPU + * execbuffer calls set_domain (RENDER, RENDER) + * flush_domains gets CPU + * invalidate_domains gets GPU + * clflush (obj) + * MI_FLUSH and drm_agp_chipset_flush + * 4. set_domain (CPU, CPU) + * flush_domains gets GPU + * invalidate_domains gets CPU + * flush_gpu_write (obj) to make sure all drawing is complete. + * This will include an MI_FLUSH to get the data from GPU + * to memory + * clflush (obj) to invalidate the CPU cache + * Another MI_FLUSH in i915_gem_flush (eliminate this somehow?) + * 5. Read/written by CPU + * cache lines are loaded and dirtied + * 6. Read written by GPU + * Same as last GPU access + * + * Case 3: The constant buffer + * + * 1. Allocated + * 2. Written by CPU + * 3. Read by GPU + * 4. Updated (written) by CPU again + * 5. Read by GPU + * + * 1. Allocated + * (CPU, CPU) + * 2. Written by CPU + * (CPU, CPU) + * 3. Read by GPU + * (CPU+RENDER, 0) + * flush_domains = CPU + * invalidate_domains = RENDER + * clflush (obj) + * MI_FLUSH + * drm_agp_chipset_flush + * 4. Updated (written) by CPU again + * (CPU, CPU) + * flush_domains = 0 (no previous write domain) + * invalidate_domains = 0 (no new read domains) + * 5. Read by GPU + * (CPU+RENDER, 0) + * flush_domains = CPU + * invalidate_domains = RENDER + * clflush (obj) + * MI_FLUSH + * drm_agp_chipset_flush + */ +void +i915_gem_object_set_to_gpu_domain(struct drm_obj *obj) +{ + struct drm_device *dev = obj->dev; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + u_int32_t invalidate_domains = 0; + u_int32_t flush_domains = 0; + + DRM_ASSERT_HELD(obj); + KASSERT((obj->pending_read_domains & I915_GEM_DOMAIN_CPU) == 0); + KASSERT(obj->pending_write_domain != I915_GEM_DOMAIN_CPU); + /* + * If the object isn't moving to a new write domain, + * let the object stay in multiple read domains + */ + if (obj->pending_write_domain == 0) + obj->pending_read_domains |= obj->read_domains; + else + atomic_setbits_int(&obj->do_flags, I915_DIRTY); + + /* + * Flush the current write domain if + * the new read domains don't match. Invalidate + * any read domains which differ from the old + * write domain + */ + if (obj->write_domain && + obj->write_domain != obj->pending_read_domains) { + flush_domains |= obj->write_domain; + invalidate_domains |= obj->pending_read_domains & + ~obj->write_domain; + } + /* + * Invalidate any read caches which may have + * stale data. That is, any new read domains. + */ + invalidate_domains |= obj->pending_read_domains & ~obj->read_domains; + /* clflush the cpu now, gpu caches get queued. */ + if ((flush_domains | invalidate_domains) & I915_GEM_DOMAIN_CPU) { + bus_dmamap_sync(dev_priv->agpdmat, obj_priv->dmamap, 0, + obj->size, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + } + if ((flush_domains | invalidate_domains) & I915_GEM_DOMAIN_GTT) { + inteldrm_wipe_mappings(obj); + } + + /* The actual obj->write_domain will be updated with + * pending_write_domain after we emit the accumulated flush for all of + * the domain changes in execuffer (which clears object's write + * domains). So if we have a current write domain that we aren't + * changing, set pending_write_domain to it. + */ + if (flush_domains == 0 && obj->pending_write_domain == 0 && + (obj->pending_read_domains == obj->write_domain || + obj->write_domain == 0)) + obj->pending_write_domain = obj->write_domain; + obj->read_domains = obj->pending_read_domains; + obj->pending_read_domains = 0; + + dev->invalidate_domains |= invalidate_domains; + dev->flush_domains |= flush_domains; +} + +/** + * Pin an object to the GTT and evaluate the relocations landing in it. + */ +int +i915_gem_object_pin_and_relocate(struct drm_obj *obj, + struct drm_file *file_priv, struct drm_i915_gem_exec_object2 *entry, + struct drm_i915_gem_relocation_entry *relocs) +{ + struct drm_device *dev = obj->dev; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct drm_obj *target_obj; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + bus_space_handle_t bsh; + int i, ret, needs_fence; + + DRM_ASSERT_HELD(obj); + needs_fence = ((entry->flags & EXEC_OBJECT_NEEDS_FENCE) && + obj_priv->tiling_mode != I915_TILING_NONE); + if (needs_fence) + atomic_setbits_int(&obj->do_flags, I915_EXEC_NEEDS_FENCE); + + /* Choose the GTT offset for our buffer and put it there. */ + ret = i915_gem_object_pin(obj, (u_int32_t)entry->alignment, + needs_fence); + if (ret) + return ret; + + entry->offset = obj_priv->gtt_offset; + + /* Apply the relocations, using the GTT aperture to avoid cache + * flushing requirements. + */ + for (i = 0; i < entry->relocation_count; i++) { + struct drm_i915_gem_relocation_entry *reloc = &relocs[i]; + struct inteldrm_obj *target_obj_priv; + uint32_t reloc_val, reloc_offset; + + target_obj = drm_gem_object_lookup(obj->dev, file_priv, + reloc->target_handle); + /* object must have come before us in the list */ + if (target_obj == NULL) { + i915_gem_object_unpin(obj); + return (EBADF); + } + if ((target_obj->do_flags & I915_IN_EXEC) == 0) { + printf("%s: object not already in execbuffer\n", + __func__); + ret = EBADF; + goto err; + } + + target_obj_priv = (struct inteldrm_obj *)target_obj; + + /* The target buffer should have appeared before us in the + * exec_object list, so it should have a GTT space bound by now. + */ + if (target_obj_priv->dmamap == 0) { + DRM_ERROR("No GTT space found for object %d\n", + reloc->target_handle); + ret = EINVAL; + goto err; + } + + /* must be in one write domain and one only */ + if (reloc->write_domain & (reloc->write_domain - 1)) { + ret = EINVAL; + goto err; + } + if (reloc->read_domains & I915_GEM_DOMAIN_CPU || + reloc->write_domain & I915_GEM_DOMAIN_CPU) { + DRM_ERROR("relocation with read/write CPU domains: " + "obj %p target %d offset %d " + "read %08x write %08x", obj, + reloc->target_handle, (int)reloc->offset, + reloc->read_domains, reloc->write_domain); + ret = EINVAL; + goto err; + } + + if (reloc->write_domain && target_obj->pending_write_domain && + reloc->write_domain != target_obj->pending_write_domain) { + DRM_ERROR("Write domain conflict: " + "obj %p target %d offset %d " + "new %08x old %08x\n", + obj, reloc->target_handle, + (int) reloc->offset, + reloc->write_domain, + target_obj->pending_write_domain); + ret = EINVAL; + goto err; + } + + target_obj->pending_read_domains |= reloc->read_domains; + target_obj->pending_write_domain |= reloc->write_domain; + + + if (reloc->offset > obj->size - 4) { + DRM_ERROR("Relocation beyond object bounds: " + "obj %p target %d offset %d size %d.\n", + obj, reloc->target_handle, + (int) reloc->offset, (int) obj->size); + ret = EINVAL; + goto err; + } + if (reloc->offset & 3) { + DRM_ERROR("Relocation not 4-byte aligned: " + "obj %p target %d offset %d.\n", + obj, reloc->target_handle, + (int) reloc->offset); + ret = EINVAL; + goto err; + } + + if (reloc->delta > target_obj->size) { + DRM_ERROR("reloc larger than target\n"); + ret = EINVAL; + goto err; + } + + /* Map the page containing the relocation we're going to + * perform. + */ + reloc_offset = obj_priv->gtt_offset + reloc->offset; + reloc_val = target_obj_priv->gtt_offset + reloc->delta; + + if (target_obj_priv->gtt_offset == reloc->presumed_offset) { + drm_gem_object_unreference(target_obj); + continue; + } + + ret = i915_gem_object_set_to_gtt_domain(obj, 1, 1); + if (ret != 0) + goto err; + + if ((ret = agp_map_subregion(dev_priv->agph, + trunc_page(reloc_offset), PAGE_SIZE, &bsh)) != 0) { + DRM_ERROR("map failed...\n"); + goto err; + } + + bus_space_write_4(dev_priv->bst, bsh, reloc_offset & PAGE_MASK, + reloc_val); + + reloc->presumed_offset = target_obj_priv->gtt_offset; + + agp_unmap_subregion(dev_priv->agph, bsh, PAGE_SIZE); + drm_gem_object_unreference(target_obj); + } + + return 0; + +err: + /* we always jump to here mid-loop */ + drm_gem_object_unreference(target_obj); + i915_gem_object_unpin(obj); + return (ret); +} + +/** Dispatch a batchbuffer to the ring + */ +void +i915_dispatch_gem_execbuffer(struct drm_device *dev, + struct drm_i915_gem_execbuffer2 *exec, uint64_t exec_offset) +{ + struct inteldrm_softc *dev_priv = dev->dev_private; + uint32_t exec_start, exec_len; + + MUTEX_ASSERT_LOCKED(&dev_priv->request_lock); + exec_start = (uint32_t)exec_offset + exec->batch_start_offset; + exec_len = (uint32_t)exec->batch_len; + + if (IS_I830(dev_priv) || IS_845G(dev_priv)) { + BEGIN_LP_RING(6); + OUT_RING(MI_BATCH_BUFFER); + OUT_RING(exec_start | MI_BATCH_NON_SECURE); + OUT_RING(exec_start + exec_len - 4); + OUT_RING(MI_NOOP); + } else { + BEGIN_LP_RING(4); + if (IS_I965G(dev_priv)) { + OUT_RING(MI_BATCH_BUFFER_START | (2 << 6) | + MI_BATCH_NON_SECURE_I965); + OUT_RING(exec_start); + } else { + OUT_RING(MI_BATCH_BUFFER_START | (2 << 6)); + OUT_RING(exec_start | MI_BATCH_NON_SECURE); + } + } + + /* + * Ensure that the commands in the batch buffer are + * finished before the interrupt fires (from a subsequent request + * added). We get back a seqno representing the execution of the + * current buffer, which we can wait on. We would like to mitigate + * these interrupts, likely by only creating seqnos occasionally + * (so that we have *some* interrupts representing completion of + * buffers that we can wait on when trying to clear up gtt space). + */ + OUT_RING(MI_FLUSH | MI_NO_WRITE_FLUSH); + OUT_RING(MI_NOOP); + ADVANCE_LP_RING(); + /* + * move to active associated all previous buffers with the seqno + * that this call will emit. so we don't need the return. If it fails + * then the next seqno will take care of it. + */ + (void)i915_add_request(dev_priv); + + inteldrm_verify_inactive(dev_priv, __FILE__, __LINE__); +} + +/* Throttle our rendering by waiting until the ring has completed our requests + * emitted over 20 msec ago. + * + * This should get us reasonable parallelism between CPU and GPU but also + * relatively low latency when blocking on a particular request to finish. + */ +int +i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file_priv) +{ +#if 0 + struct inteldrm_file *intel_file = (struct inteldrm_file *)file_priv; + u_int32_t seqno; +#endif + int ret = 0; + + return ret; +} + +int +i915_gem_get_relocs_from_user(struct drm_i915_gem_exec_object2 *exec_list, + u_int32_t buffer_count, struct drm_i915_gem_relocation_entry **relocs) +{ + u_int32_t reloc_count = 0, reloc_index = 0, i; + int ret; + + *relocs = NULL; + for (i = 0; i < buffer_count; i++) { + if (reloc_count + exec_list[i].relocation_count < reloc_count) + return (EINVAL); + reloc_count += exec_list[i].relocation_count; + } + + if (reloc_count == 0) + return (0); + + if (SIZE_MAX / reloc_count < sizeof(**relocs)) + return (EINVAL); + *relocs = drm_alloc(reloc_count * sizeof(**relocs)); + for (i = 0; i < buffer_count; i++) { + if ((ret = copyin((void *)(uintptr_t)exec_list[i].relocs_ptr, + &(*relocs)[reloc_index], exec_list[i].relocation_count * + sizeof(**relocs))) != 0) { + drm_free(*relocs); + *relocs = NULL; + return (ret); + } + reloc_index += exec_list[i].relocation_count; + } + + return (0); +} + +int +i915_gem_put_relocs_to_user(struct drm_i915_gem_exec_object2 *exec_list, + u_int32_t buffer_count, struct drm_i915_gem_relocation_entry *relocs) +{ + u_int32_t reloc_count = 0, i; + int ret = 0; + + if (relocs == NULL) + return (0); + + for (i = 0; i < buffer_count; i++) { + if ((ret = copyout(&relocs[reloc_count], + (void *)(uintptr_t)exec_list[i].relocs_ptr, + exec_list[i].relocation_count * sizeof(*relocs))) != 0) + break; + reloc_count += exec_list[i].relocation_count; + } + + drm_free(relocs); + + return (ret); +} + +int +i915_gem_execbuffer2(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct inteldrm_softc *dev_priv = dev->dev_private; + struct drm_i915_gem_execbuffer2 *args = data; + struct drm_i915_gem_exec_object2 *exec_list = NULL; + struct drm_i915_gem_relocation_entry *relocs = NULL; + struct inteldrm_obj *obj_priv, *batch_obj_priv; + struct drm_obj **object_list = NULL; + struct drm_obj *batch_obj, *obj; + size_t oflow; + int ret, ret2, i; + int pinned = 0, pin_tries; + uint32_t reloc_index; + + /* + * Check for valid execbuffer offset. We can do this early because + * bound object are always page aligned, so only the start offset + * matters. Also check for integer overflow in the batch offset and size + */ + if ((args->batch_start_offset | args->batch_len) & 0x7 || + args->batch_start_offset + args->batch_len < args->batch_len || + args->batch_start_offset + args->batch_len < + args->batch_start_offset) + return (EINVAL); + + if (args->buffer_count < 1) { + DRM_ERROR("execbuf with %d buffers\n", args->buffer_count); + return (EINVAL); + } + /* Copy in the exec list from userland, check for overflow */ + oflow = SIZE_MAX / args->buffer_count; + if (oflow < sizeof(*exec_list) || oflow < sizeof(*object_list)) + return (EINVAL); + exec_list = drm_alloc(sizeof(*exec_list) * args->buffer_count); + object_list = drm_alloc(sizeof(*object_list) * args->buffer_count); + if (exec_list == NULL || object_list == NULL) { + ret = ENOMEM; + goto pre_mutex_err; + } + ret = copyin((void *)(uintptr_t)args->buffers_ptr, exec_list, + sizeof(*exec_list) * args->buffer_count); + if (ret != 0) + goto pre_mutex_err; + + ret = i915_gem_get_relocs_from_user(exec_list, args->buffer_count, + &relocs); + if (ret != 0) + goto pre_mutex_err; + + DRM_LOCK(); + inteldrm_verify_inactive(dev_priv, __FILE__, __LINE__); + + /* XXX check these before we copyin... but we do need the lock */ + if (dev_priv->mm.wedged) { + ret = EIO; + goto unlock; + } + + if (dev_priv->mm.suspended) { + ret = EBUSY; + goto unlock; + } + + /* Look up object handles */ + for (i = 0; i < args->buffer_count; i++) { + object_list[i] = drm_gem_object_lookup(dev, file_priv, + exec_list[i].handle); + obj = object_list[i]; + if (obj == NULL) { + DRM_ERROR("Invalid object handle %d at index %d\n", + exec_list[i].handle, i); + ret = EBADF; + goto err; + } + if (obj->do_flags & I915_IN_EXEC) { + DRM_ERROR("Object %p appears more than once in object_list\n", + object_list[i]); + ret = EBADF; + goto err; + } + atomic_setbits_int(&obj->do_flags, I915_IN_EXEC); + } + + /* Pin and relocate */ + for (pin_tries = 0; ; pin_tries++) { + ret = pinned = 0; + reloc_index = 0; + + for (i = 0; i < args->buffer_count; i++) { + object_list[i]->pending_read_domains = 0; + object_list[i]->pending_write_domain = 0; + drm_hold_object(object_list[i]); + ret = i915_gem_object_pin_and_relocate(object_list[i], + file_priv, &exec_list[i], &relocs[reloc_index]); + if (ret) { + drm_unhold_object(object_list[i]); + break; + } + pinned++; + reloc_index += exec_list[i].relocation_count; + } + /* success */ + if (ret == 0) + break; + + /* error other than GTT full, or we've already tried again */ + if (ret != ENOSPC || pin_tries >= 1) + goto err; + + /* + * unpin all of our buffers and unhold them so they can be + * unbound so we can try and refit everything in the aperture. + */ + for (i = 0; i < pinned; i++) { + i915_gem_object_unpin(object_list[i]); + drm_unhold_object(object_list[i]); + } + pinned = 0; + /* evict everyone we can from the aperture */ + ret = i915_gem_evict_everything(dev_priv, 1); + if (ret) + goto err; + } + + /* If we get here all involved objects are referenced, pinned, relocated + * and held. Now we can finish off the exec processing. + * + * First, set the pending read domains for the batch buffer to + * command. + */ + batch_obj = object_list[args->buffer_count - 1]; + batch_obj_priv = (struct inteldrm_obj *)batch_obj; + if (args->batch_start_offset + args->batch_len > batch_obj->size || + batch_obj->pending_write_domain) { + ret = EINVAL; + goto err; + } + batch_obj->pending_read_domains |= I915_GEM_DOMAIN_COMMAND; + + inteldrm_verify_inactive(dev_priv, __FILE__, __LINE__); + + /* + * Zero the global flush/invalidate flags. These will be modified as + * new domains are computed for each object + */ + dev->invalidate_domains = 0; + dev->flush_domains = 0; + + /* Compute new gpu domains and update invalidate/flush */ + for (i = 0; i < args->buffer_count; i++) + i915_gem_object_set_to_gpu_domain(object_list[i]); + + inteldrm_verify_inactive(dev_priv, __FILE__, __LINE__); + + /* flush and invalidate any domains that need them. */ + (void)i915_gem_flush(dev_priv, dev->invalidate_domains, + dev->flush_domains); + + /* + * update the write domains, and fence/gpu write accounting information. + * Also do the move to active list here. The lazy seqno accounting will + * make sure that they have the correct seqno. If the add_request + * fails, then we will wait for a later batch (or one added on the + * wait), which will waste some time, but if we're that low on memory + * then we could fail in much worse ways. + */ + mtx_enter(&dev_priv->request_lock); /* to prevent races on next_seqno */ + mtx_enter(&dev_priv->list_lock); + for (i = 0; i < args->buffer_count; i++) { + obj = object_list[i]; + obj_priv = (struct inteldrm_obj *)obj; + drm_lock_obj(obj); + + obj->write_domain = obj->pending_write_domain; + /* + * if we have a write domain, add us to the gpu write list + * else we can remove the bit because it has been flushed. + */ + if (obj->do_flags & I915_GPU_WRITE) + TAILQ_REMOVE(&dev_priv->mm.gpu_write_list, obj_priv, + write_list); + if (obj->write_domain) { + TAILQ_INSERT_TAIL(&dev_priv->mm.gpu_write_list, + obj_priv, write_list); + atomic_setbits_int(&obj->do_flags, I915_GPU_WRITE); + } else { + atomic_clearbits_int(&obj->do_flags, + I915_GPU_WRITE); + } + /* if this batchbuffer needs a fence, then the object is + * counted as fenced exec. else any outstanding fence waits + * will just wait on the fence last_seqno. + */ + if (inteldrm_exec_needs_fence(obj_priv)) { + atomic_setbits_int(&obj->do_flags, + I915_FENCED_EXEC); + } else { + atomic_clearbits_int(&obj->do_flags, + I915_FENCED_EXEC); + } + + drm_unlock_obj(obj); + i915_gem_object_move_to_active(object_list[i]); + } + mtx_leave(&dev_priv->list_lock); + + inteldrm_verify_inactive(dev_priv, __FILE__, __LINE__); + + /* Exec the batchbuffer */ + /* + * XXX make sure that this may never fail by preallocating the request. + */ + i915_dispatch_gem_execbuffer(dev, args, batch_obj_priv->gtt_offset); + mtx_leave(&dev_priv->request_lock); + + inteldrm_verify_inactive(dev_priv, __FILE__, __LINE__); + + ret = copyout(exec_list, (void *)(uintptr_t)args->buffers_ptr, + sizeof(*exec_list) * args->buffer_count); + +err: + for (i = 0; i < args->buffer_count; i++) { + if (object_list[i] == NULL) + break; + + atomic_clearbits_int(&object_list[i]->do_flags, I915_IN_EXEC | + I915_EXEC_NEEDS_FENCE); + if (i < pinned) { + i915_gem_object_unpin(object_list[i]); + drm_unhold_and_unref(object_list[i]); + } else { + drm_unref(&object_list[i]->uobj); + } + } + +unlock: + DRM_UNLOCK(); + +pre_mutex_err: + /* update userlands reloc state. */ + ret2 = i915_gem_put_relocs_to_user(exec_list, + args->buffer_count, relocs); + if (ret2 != 0 && ret == 0) + ret = ret2; + + drm_free(object_list); + drm_free(exec_list); + + return ret; +} + +int +i915_gem_object_pin(struct drm_obj *obj, uint32_t alignment, int needs_fence) +{ + struct drm_device *dev = obj->dev; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + int ret; + + DRM_ASSERT_HELD(obj); + inteldrm_verify_inactive(dev_priv, __FILE__, __LINE__); + /* + * if already bound, but alignment is unsuitable, unbind so we can + * fix it. Similarly if we have constraints due to fence registers, + * adjust if needed. Note that if we are already pinned we may as well + * fail because whatever depends on this alignment will render poorly + * otherwise, so just fail the pin (with a printf so we can fix a + * wrong userland). + */ + if (obj_priv->dmamap != NULL && + ((alignment && obj_priv->gtt_offset & (alignment - 1)) || + obj_priv->gtt_offset & (i915_gem_get_gtt_alignment(obj) - 1) || + !i915_gem_object_fence_offset_ok(obj, obj_priv->tiling_mode))) { + /* if it is already pinned we sanitised the alignment then */ + KASSERT(obj_priv->pin_count == 0); + if ((ret = i915_gem_object_unbind(obj, 1))) + return (ret); + } + + if (obj_priv->dmamap == NULL) { + ret = i915_gem_object_bind_to_gtt(obj, alignment, 1); + if (ret != 0) + return (ret); + } + + /* + * due to lazy fence destruction we may have an invalid fence now. + * So if so, nuke it before we do anything with the gpu. + * XXX 965+ can put this off.. and be faster + */ + if (obj->do_flags & I915_FENCE_INVALID) { + ret= i915_gem_object_put_fence_reg(obj, 1); + if (ret) + return (ret); + } + /* + * Pre-965 chips may need a fence register set up in order to + * handle tiling properly. GTT mapping may have blown it away so + * restore. + * With execbuf2 support we don't always need it, but if we do grab + * it. + */ + if (needs_fence && obj_priv->tiling_mode != I915_TILING_NONE && + (ret = i915_gem_get_fence_reg(obj, 1)) != 0) + return (ret); + + /* If the object is not active and not pending a flush, + * remove it from the inactive list + */ + if (++obj_priv->pin_count == 1) { + atomic_inc(&dev->pin_count); + atomic_add(obj->size, &dev->pin_memory); + if (!inteldrm_is_active(obj_priv)) + i915_list_remove(obj_priv); + } + inteldrm_verify_inactive(dev_priv, __FILE__, __LINE__); + + return (0); +} + +void +i915_gem_object_unpin(struct drm_obj *obj) +{ + struct drm_device *dev = obj->dev; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + + inteldrm_verify_inactive(dev_priv, __FILE__, __LINE__); + KASSERT(obj_priv->pin_count >= 1); + KASSERT(obj_priv->dmamap != NULL); + DRM_ASSERT_HELD(obj); + + /* If the object is no longer pinned, and is + * neither active nor being flushed, then stick it on + * the inactive list + */ + if (--obj_priv->pin_count == 0) { + if (!inteldrm_is_active(obj_priv)) + i915_gem_object_move_to_inactive(obj); + atomic_dec(&dev->pin_count); + atomic_sub(obj->size, &dev->pin_memory); + } + inteldrm_verify_inactive(dev_priv, __FILE__, __LINE__); +} + +int +i915_gem_pin_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct inteldrm_softc *dev_priv = dev->dev_private; + struct drm_i915_gem_pin *args = data; + struct drm_obj *obj; + struct inteldrm_obj *obj_priv; + int ret = 0; + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (obj == NULL) + return (EBADF); + DRM_LOCK(); + drm_hold_object(obj); + + obj_priv = (struct inteldrm_obj *)obj; + if (i915_obj_purgeable(obj_priv)) { + printf("%s: pinning purgeable object\n", __func__); + ret = EINVAL; + goto out; + } + + if (++obj_priv->user_pin_count == 1) { + ret = i915_gem_object_pin(obj, args->alignment, 1); + if (ret != 0) + goto out; + inteldrm_set_max_obj_size(dev_priv); + } + + /* XXX - flush the CPU caches for pinned objects + * as the X server doesn't manage domains yet + */ + i915_gem_object_set_to_gtt_domain(obj, 1, 1); + args->offset = obj_priv->gtt_offset; + +out: + drm_unhold_and_unref(obj); + DRM_UNLOCK(); + + return (ret); +} + +int +i915_gem_unpin_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct inteldrm_softc *dev_priv = dev->dev_private; + struct drm_i915_gem_pin *args = data; + struct inteldrm_obj *obj_priv; + struct drm_obj *obj; + int ret = 0; + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (obj == NULL) + return (EBADF); + + DRM_LOCK(); + drm_hold_object(obj); + + obj_priv = (struct inteldrm_obj *)obj; + if (obj_priv->user_pin_count == 0) { + ret = EINVAL; + goto out; + } + + if (--obj_priv->user_pin_count == 0) { + i915_gem_object_unpin(obj); + inteldrm_set_max_obj_size(dev_priv); + } + +out: + drm_unhold_and_unref(obj); + DRM_UNLOCK(); + return (ret); +} + +int +i915_gem_busy_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct inteldrm_softc *dev_priv = dev->dev_private; + struct drm_i915_gem_busy *args = data; + struct drm_obj *obj; + struct inteldrm_obj *obj_priv; + int ret = 0; + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (obj == NULL) { + DRM_ERROR("Bad handle in i915_gem_busy_ioctl(): %d\n", + args->handle); + return (EBADF); + } + + obj_priv = (struct inteldrm_obj *)obj; + args->busy = inteldrm_is_active(obj_priv); + if (args->busy) { + /* + * Unconditionally flush objects write domain if they are + * busy. The fact userland is calling this ioctl means that + * it wants to use this buffer sooner rather than later, so + * flushing now shoul reduce latency. + */ + if (obj->write_domain) + (void)i915_gem_flush(dev_priv, obj->write_domain, + obj->write_domain); + /* + * Update the active list after the flush otherwise this is + * only updated on a delayed timer. Updating now reduces + * working set size. + */ + i915_gem_retire_requests(dev_priv); + args->busy = inteldrm_is_active(obj_priv); + } + + drm_unref(&obj->uobj); + return (ret); +} + +int +i915_gem_madvise_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_i915_gem_madvise *args = data; + struct drm_obj *obj; + struct inteldrm_obj *obj_priv; + int need, ret = 0; + + switch (args->madv) { + case I915_MADV_DONTNEED: + need = 0; + break; + case I915_MADV_WILLNEED: + need = 1; + break; + default: + return (EINVAL); + } + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (obj == NULL) + return (EBADF); + + drm_hold_object(obj); + obj_priv = (struct inteldrm_obj *)obj; + + /* invalid to madvise on a pinned BO */ + if (obj_priv->pin_count) { + ret = EINVAL; + goto out; + } + + if (!i915_obj_purged(obj_priv)) { + if (need) { + atomic_clearbits_int(&obj->do_flags, + I915_DONTNEED); + } else { + atomic_setbits_int(&obj->do_flags, I915_DONTNEED); + } + } + + + /* if the object is no longer bound, discard its backing storage */ + if (i915_obj_purgeable(obj_priv) && obj_priv->dmamap == NULL) + inteldrm_purge_obj(obj); + + args->retained = !i915_obj_purged(obj_priv); + +out: + drm_unhold_and_unref(obj); + + return (ret); +} + +int +i915_gem_init_object(struct drm_obj *obj) +{ + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + + /* + * We've just allocated pages from the kernel, + * so they've just been written by the CPU with + * zeros. They'll need to be flushed before we + * use them with the GPU. + */ + obj->write_domain = I915_GEM_DOMAIN_CPU; + obj->read_domains = I915_GEM_DOMAIN_CPU; + + /* normal objects don't need special treatment */ + obj_priv->dma_flags = 0; + obj_priv->fence_reg = I915_FENCE_REG_NONE; + + return 0; +} + +/* + * NOTE all object unreferences in this driver need to hold the DRM_LOCK(), + * because if they free they poke around in driver structures. + */ +void +i915_gem_free_object(struct drm_obj *obj) +{ + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + + DRM_ASSERT_HELD(obj); + while (obj_priv->pin_count > 0) + i915_gem_object_unpin(obj); + + i915_gem_object_unbind(obj, 0); + drm_free(obj_priv->bit_17); + obj_priv->bit_17 = NULL; + /* XXX dmatag went away? */ +} + +/* Clear out the inactive list and unbind everything in it. */ +int +i915_gem_evict_inactive(struct inteldrm_softc *dev_priv, int interruptible) +{ + struct inteldrm_obj *obj_priv; + int ret = 0; + + mtx_enter(&dev_priv->list_lock); + while ((obj_priv = TAILQ_FIRST(&dev_priv->mm.inactive_list)) != NULL) { + if (obj_priv->pin_count != 0) { + ret = EINVAL; + DRM_ERROR("Pinned object in unbind list\n"); + break; + } + /* reference it so that we can frob it outside the lock */ + drm_ref(&obj_priv->obj.uobj); + mtx_leave(&dev_priv->list_lock); + + drm_hold_object(&obj_priv->obj); + ret = i915_gem_object_unbind(&obj_priv->obj, interruptible); + drm_unhold_and_unref(&obj_priv->obj); + + mtx_enter(&dev_priv->list_lock); + if (ret) + break; + } + mtx_leave(&dev_priv->list_lock); + + return (ret); +} + +void +inteldrm_quiesce(struct inteldrm_softc *dev_priv) +{ + /* + * Right now we depend on X vt switching, so we should be + * already suspended, but fallbacks may fault, etc. + * Since we can't readback the gtt to reset what we have, make + * sure that everything is unbound. + */ + KASSERT(dev_priv->mm.suspended); + KASSERT(dev_priv->ring.ring_obj == NULL); + atomic_setbits_int(&dev_priv->sc_flags, INTELDRM_QUIET); + while (dev_priv->entries) + tsleep(&dev_priv->entries, 0, "intelquiet", 0); + /* + * nothing should be dirty WRT the chip, only stuff that's bound + * for gtt mapping. Nothing should be pinned over vt switch, if it + * is then rendering corruption will occur due to api misuse, shame. + */ + KASSERT(TAILQ_EMPTY(&dev_priv->mm.flushing_list)); + KASSERT(TAILQ_EMPTY(&dev_priv->mm.active_list)); + /* Disabled because root could panic the kernel if this was enabled */ + /* KASSERT(dev->pin_count == 0); */ + + /* can't fail since uninterruptible */ + (void)i915_gem_evict_inactive(dev_priv, 0); +} + +int +i915_gem_idle(struct inteldrm_softc *dev_priv) +{ + struct drm_device *dev = (struct drm_device *)dev_priv->drmdev; + int ret; + + DRM_LOCK(); + if (dev_priv->mm.suspended || dev_priv->ring.ring_obj == NULL) { + DRM_UNLOCK(); + return (0); + } + + /* + * To idle the gpu, flush anything pending then unbind the whole + * shebang. If we're wedged, assume that the reset workq will clear + * everything out and continue as normal. + */ + if ((ret = i915_gem_evict_everything(dev_priv, 1)) != 0 && + ret != ENOSPC && ret != EIO) { + DRM_UNLOCK(); + return (ret); + } + + /* Hack! Don't let anybody do execbuf while we don't control the chip. + * We need to replace this with a semaphore, or something. + */ + dev_priv->mm.suspended = 1; + /* if we hung then the timer alredy fired. */ + timeout_del(&dev_priv->mm.hang_timer); + + inteldrm_update_ring(dev_priv); + i915_gem_cleanup_ringbuffer(dev_priv); + DRM_UNLOCK(); + + /* this should be idle now */ + timeout_del(&dev_priv->mm.retire_timer); + + return 0; +} + +int +i915_gem_init_hws(struct inteldrm_softc *dev_priv) +{ + struct drm_device *dev = (struct drm_device *)dev_priv->drmdev; + struct drm_obj *obj; + struct inteldrm_obj *obj_priv; + int ret; + + /* If we need a physical address for the status page, it's already + * initialized at driver load time. + */ + if (!I915_NEED_GFX_HWS(dev_priv)) + return 0; + + obj = drm_gem_object_alloc(dev, 4096); + if (obj == NULL) { + DRM_ERROR("Failed to allocate status page\n"); + return (ENOMEM); + } + obj_priv = (struct inteldrm_obj *)obj; + drm_hold_object(obj); + /* + * snooped gtt mapping please . + * Normally this flag is only to dmamem_map, but it's been overloaded + * for the agp mapping + */ + obj_priv->dma_flags = BUS_DMA_COHERENT | BUS_DMA_READ; + + ret = i915_gem_object_pin(obj, 4096, 0); + if (ret != 0) { + drm_unhold_and_unref(obj); + return ret; + } + + dev_priv->hw_status_page = (void *)vm_map_min(kernel_map); + obj->uao->pgops->pgo_reference(obj->uao); + ret = uvm_map(kernel_map, (vaddr_t *)&dev_priv->hw_status_page, + PAGE_SIZE, obj->uao, 0, 0, UVM_MAPFLAG(UVM_PROT_RW, UVM_PROT_RW, + UVM_INH_SHARE, UVM_ADV_RANDOM, 0)); + if (ret != 0) { + DRM_ERROR("Failed to map status page.\n"); + obj->uao->pgops->pgo_detach(obj->uao); + i915_gem_object_unpin(obj); + drm_unhold_and_unref(obj); + return (EINVAL); + } + drm_unhold_object(obj); + dev_priv->hws_obj = obj; + memset(dev_priv->hw_status_page, 0, PAGE_SIZE); + I915_WRITE(HWS_PGA, obj_priv->gtt_offset); + I915_READ(HWS_PGA); /* posting read */ + DRM_DEBUG("hws offset: 0x%08jx\n", (uintmax_t)obj_priv->gtt_offset); + + return 0; +} + +void +i915_gem_cleanup_hws(struct inteldrm_softc *dev_priv) +{ + struct drm_obj *obj; + + if (!I915_NEED_GFX_HWS(dev_priv) || dev_priv->hws_obj == NULL) + return; + + obj = dev_priv->hws_obj; + + uvm_unmap(kernel_map, (vaddr_t)dev_priv->hw_status_page, + (vaddr_t)dev_priv->hw_status_page + PAGE_SIZE); + dev_priv->hw_status_page = NULL; + drm_hold_object(obj); + i915_gem_object_unpin(obj); + drm_unhold_and_unref(obj); + dev_priv->hws_obj = NULL; + + /* Write high address into HWS_PGA when disabling. */ + I915_WRITE(HWS_PGA, 0x1ffff000); +} + +int +i915_gem_init_ringbuffer(struct inteldrm_softc *dev_priv) +{ + struct drm_device *dev = (struct drm_device *)dev_priv->drmdev; + struct drm_obj *obj; + struct inteldrm_obj *obj_priv; + int ret; + + ret = i915_gem_init_hws(dev_priv); + if (ret != 0) + return ret; + + obj = drm_gem_object_alloc(dev, 128 * 1024); + if (obj == NULL) { + DRM_ERROR("Failed to allocate ringbuffer\n"); + ret = ENOMEM; + goto delhws; + } + drm_hold_object(obj); + obj_priv = (struct inteldrm_obj *)obj; + + ret = i915_gem_object_pin(obj, 4096, 0); + if (ret != 0) + goto unref; + + /* Set up the kernel mapping for the ring. */ + dev_priv->ring.size = obj->size; + + if ((ret = agp_map_subregion(dev_priv->agph, obj_priv->gtt_offset, + obj->size, &dev_priv->ring.bsh)) != 0) { + DRM_INFO("can't map ringbuffer\n"); + goto unpin; + } + dev_priv->ring.ring_obj = obj; + + if ((ret = inteldrm_start_ring(dev_priv)) != 0) + goto unmap; + + drm_unhold_object(obj); + return (0); + +unmap: + agp_unmap_subregion(dev_priv->agph, dev_priv->ring.bsh, obj->size); +unpin: + memset(&dev_priv->ring, 0, sizeof(dev_priv->ring)); + i915_gem_object_unpin(obj); +unref: + drm_unhold_and_unref(obj); +delhws: + i915_gem_cleanup_hws(dev_priv); + return (ret); +} + +int +inteldrm_start_ring(struct inteldrm_softc *dev_priv) +{ + struct drm_obj *obj = dev_priv->ring.ring_obj; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + u_int32_t head; + + /* Stop the ring if it's running. */ + I915_WRITE(PRB0_CTL, 0); + I915_WRITE(PRB0_TAIL, 0); + I915_WRITE(PRB0_HEAD, 0); + + /* Initialize the ring. */ + I915_WRITE(PRB0_START, obj_priv->gtt_offset); + head = I915_READ(PRB0_HEAD) & HEAD_ADDR; + + /* G45 ring initialisation fails to reset head to zero */ + if (head != 0) { + I915_WRITE(PRB0_HEAD, 0); + DRM_DEBUG("Forced ring head to zero ctl %08x head %08x" + "tail %08x start %08x\n", I915_READ(PRB0_CTL), + I915_READ(PRB0_HEAD), I915_READ(PRB0_TAIL), + I915_READ(PRB0_START)); + } + + I915_WRITE(PRB0_CTL, ((obj->size - 4096) & RING_NR_PAGES) | + RING_NO_REPORT | RING_VALID); + + head = I915_READ(PRB0_HEAD) & HEAD_ADDR; + /* If ring head still != 0, the ring is dead */ + if (head != 0) { + DRM_ERROR("Ring initialisation failed: ctl %08x head %08x" + "tail %08x start %08x\n", I915_READ(PRB0_CTL), + I915_READ(PRB0_HEAD), I915_READ(PRB0_TAIL), + I915_READ(PRB0_START)); + return (EIO); + } + + /* Update our cache of the ring state */ + inteldrm_update_ring(dev_priv); + + if (IS_I9XX(dev_priv) && !IS_GEN3(dev_priv)) + I915_WRITE(MI_MODE, (VS_TIMER_DISPATCH) << 15 | + VS_TIMER_DISPATCH); + + return (0); +} + +void +i915_gem_cleanup_ringbuffer(struct inteldrm_softc *dev_priv) +{ + if (dev_priv->ring.ring_obj == NULL) + return; + agp_unmap_subregion(dev_priv->agph, dev_priv->ring.bsh, + dev_priv->ring.ring_obj->size); + drm_hold_object(dev_priv->ring.ring_obj); + i915_gem_object_unpin(dev_priv->ring.ring_obj); + drm_unhold_and_unref(dev_priv->ring.ring_obj); + dev_priv->ring.ring_obj = NULL; + memset(&dev_priv->ring, 0, sizeof(dev_priv->ring)); + + i915_gem_cleanup_hws(dev_priv); +} + +int +i915_gem_entervt_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct inteldrm_softc *dev_priv = dev->dev_private; + int ret; + + if (dev_priv->mm.wedged) { + DRM_ERROR("Reenabling wedged hardware, good luck\n"); + dev_priv->mm.wedged = 0; + } + + + DRM_LOCK(); + dev_priv->mm.suspended = 0; + + ret = i915_gem_init_ringbuffer(dev_priv); + if (ret != 0) { + DRM_UNLOCK(); + return (ret); + } + + /* gtt mapping means that the inactive list may not be empty */ + KASSERT(TAILQ_EMPTY(&dev_priv->mm.active_list)); + KASSERT(TAILQ_EMPTY(&dev_priv->mm.flushing_list)); + KASSERT(TAILQ_EMPTY(&dev_priv->mm.request_list)); + DRM_UNLOCK(); + + drm_irq_install(dev); + + return (0); +} + +int +i915_gem_leavevt_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct inteldrm_softc *dev_priv = dev->dev_private; + int ret; + + /* don't unistall if we fail, repeat calls on failure will screw us */ + if ((ret = i915_gem_idle(dev_priv)) == 0) + drm_irq_uninstall(dev); + return (ret); +} + +void +inteldrm_timeout(void *arg) +{ + struct inteldrm_softc *dev_priv = arg; + + if (workq_add_task(dev_priv->workq, 0, i915_gem_retire_work_handler, + dev_priv, NULL) == ENOMEM) + DRM_ERROR("failed to run retire handler\n"); +} + +/* + * handle hung hardware, or error interrupts. for now print debug info. + */ +void +inteldrm_error(struct inteldrm_softc *dev_priv) +{ + u_int32_t eir, ipeir; + u_int8_t reset = GDRST_RENDER; + const char *errbitstr; +#if defined(__NetBSD__) + char errbuf[128]; +#endif /* defined(__NetBSD__) */ + + eir = I915_READ(EIR); + if (eir == 0) + return; + + if (IS_IRONLAKE(dev_priv)) { + errbitstr = "\20\x05PTEE\x04MPVE\x03CPVE"; + } else if (IS_G4X(dev_priv)) { + errbitstr = "\20\x10 BCSINSTERR\x06PTEERR\x05MPVERR\x04CPVERR" + "\x03 BCSPTEERR\x02REFRESHERR\x01INSTERR"; + } else { + errbitstr = "\20\x5PTEERR\x2REFRESHERR\x1INSTERR"; + } + +#if !defined(__NetBSD__) + printf("render error detected, EIR: %b\n", eir, errbitstr); +#else /* !defined(__NetBSD__) */ + snprintb(errbuf, sizeof(errbuf), errbitstr, eir); + printf("render error detected, EIR: %s\n", errbuf); +#endif /* !defined(__NetBSD__) */ + if (IS_IRONLAKE(dev_priv)) { + if (eir & GT_ERROR_PTE) { + dev_priv->mm.wedged = 1; + reset = GDRST_FULL; + } + } else { + if (IS_G4X(dev_priv)) { + if (eir & (GM45_ERROR_MEM_PRIV | GM45_ERROR_CP_PRIV)) { + printf(" IPEIR: 0x%08x\n", + I915_READ(IPEIR_I965)); + printf(" IPEHR: 0x%08x\n", + I915_READ(IPEHR_I965)); + printf(" INSTDONE: 0x%08x\n", + I915_READ(INSTDONE_I965)); + printf(" INSTPS: 0x%08x\n", + I915_READ(INSTPS)); + printf(" INSTDONE1: 0x%08x\n", + I915_READ(INSTDONE1)); + printf(" ACTHD: 0x%08x\n", + I915_READ(ACTHD_I965)); + } + if (eir & GM45_ERROR_PAGE_TABLE) { + printf(" PGTBL_ER: 0x%08x\n", + I915_READ(PGTBL_ER)); + dev_priv->mm.wedged = 1; + reset = GDRST_FULL; + + } + } else if (IS_I9XX(dev_priv) && eir & I915_ERROR_PAGE_TABLE) { + printf(" PGTBL_ER: 0x%08x\n", I915_READ(PGTBL_ER)); + dev_priv->mm.wedged = 1; + reset = GDRST_FULL; + } + if (eir & I915_ERROR_MEMORY_REFRESH) { + printf("PIPEASTAT: 0x%08x\n", + I915_READ(PIPEASTAT)); + printf("PIPEBSTAT: 0x%08x\n", + I915_READ(PIPEBSTAT)); + } + if (eir & I915_ERROR_INSTRUCTION) { + printf(" INSTPM: 0x%08x\n", + I915_READ(INSTPM)); + if (!IS_I965G(dev_priv)) { + ipeir = I915_READ(IPEIR); + + printf(" IPEIR: 0x%08x\n", + I915_READ(IPEIR)); + printf(" IPEHR: 0x%08x\n", + I915_READ(IPEHR)); + printf(" INSTDONE: 0x%08x\n", + I915_READ(INSTDONE)); + printf(" ACTHD: 0x%08x\n", + I915_READ(ACTHD)); + I915_WRITE(IPEIR, ipeir); + (void)I915_READ(IPEIR); + } else { + ipeir = I915_READ(IPEIR_I965); + + printf(" IPEIR: 0x%08x\n", + I915_READ(IPEIR_I965)); + printf(" IPEHR: 0x%08x\n", + I915_READ(IPEHR_I965)); + printf(" INSTDONE: 0x%08x\n", + I915_READ(INSTDONE_I965)); + printf(" INSTPS: 0x%08x\n", + I915_READ(INSTPS)); + printf(" INSTDONE1: 0x%08x\n", + I915_READ(INSTDONE1)); + printf(" ACTHD: 0x%08x\n", + I915_READ(ACTHD_I965)); + I915_WRITE(IPEIR_I965, ipeir); + (void)I915_READ(IPEIR_I965); + } + } + } + + I915_WRITE(EIR, eir); + eir = I915_READ(EIR); + /* + * nasty errors don't clear and need a reset, mask them until we reset + * else we'll get infinite interrupt storms. + */ + if (eir) { + if (dev_priv->mm.wedged == 0) + DRM_ERROR("EIR stuck: 0x%08x, masking\n", eir); + I915_WRITE(EMR, I915_READ(EMR) | eir); + if (IS_IRONLAKE(dev_priv)) { + I915_WRITE(GTIIR, GT_MASTER_ERROR); + } else { + I915_WRITE(IIR, + I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); + } + } + /* + * if it was a pagetable error, or we were called from hangcheck, then + * reset the gpu. + */ + if (dev_priv->mm.wedged && workq_add_task(dev_priv->workq, 0, + inteldrm_hung, dev_priv, (void *)(uintptr_t)reset) == ENOMEM) + DRM_INFO("failed to schedule reset task\n"); + +} + +void +inteldrm_hung(void *arg, void *reset_type) +{ + struct inteldrm_softc *dev_priv = arg; + struct drm_device *dev = (struct drm_device *)dev_priv->drmdev; + struct inteldrm_obj *obj_priv; + u_int8_t reset = (u_int8_t)(uintptr_t)reset_type; + + DRM_LOCK(); + if (HAS_RESET(dev_priv)) { + DRM_INFO("resetting gpu: "); + inteldrm_965_reset(dev_priv, reset); + printf("done!\n"); + } else + printf("no reset function for chipset.\n"); + + /* + * Clear out all of the requests and make everything inactive. + */ + i915_gem_retire_requests(dev_priv); + + /* + * Clear the active and flushing lists to inactive. Since + * we've reset the hardware then they're not going to get + * flushed or completed otherwise. nuke the domains since + * they're now irrelavent. + */ + mtx_enter(&dev_priv->list_lock); + while ((obj_priv = TAILQ_FIRST(&dev_priv->mm.flushing_list)) != NULL) { + drm_lock_obj(&obj_priv->obj); + if (obj_priv->obj.write_domain & I915_GEM_GPU_DOMAINS) { + TAILQ_REMOVE(&dev_priv->mm.gpu_write_list, + obj_priv, write_list); + atomic_clearbits_int(&obj_priv->obj.do_flags, + I915_GPU_WRITE); + obj_priv->obj.write_domain &= ~I915_GEM_GPU_DOMAINS; + } + /* unlocks object and list */ + i915_gem_object_move_to_inactive_locked(&obj_priv->obj); + mtx_enter(&dev_priv->list_lock); + } + mtx_leave(&dev_priv->list_lock); + + /* unbind everything */ + (void)i915_gem_evict_inactive(dev_priv, 0); + + if (HAS_RESET(dev_priv)) + dev_priv->mm.wedged = 0; + DRM_UNLOCK(); +} + +void +inteldrm_hangcheck(void *arg) +{ + struct inteldrm_softc *dev_priv = arg; + u_int32_t acthd, instdone, instdone1; + + /* are we idle? no requests, or ring is empty */ + if (TAILQ_EMPTY(&dev_priv->mm.request_list) || + (I915_READ(PRB0_HEAD) & HEAD_ADDR) == + (I915_READ(PRB0_TAIL) & TAIL_ADDR)) { + dev_priv->mm.hang_cnt = 0; + return; + } + + if (IS_I965G(dev_priv)) { + acthd = I915_READ(ACTHD_I965); + instdone = I915_READ(INSTDONE_I965); + instdone1 = I915_READ(INSTDONE1); + } else { + acthd = I915_READ(ACTHD); + instdone = I915_READ(INSTDONE); + instdone1 = 0; + } + + /* if we've hit ourselves before and the hardware hasn't moved, hung. */ + if (dev_priv->mm.last_acthd == acthd && + dev_priv->mm.last_instdone == instdone && + dev_priv->mm.last_instdone1 == instdone1) { + /* if that's twice we didn't hit it, then we're hung */ + if (++dev_priv->mm.hang_cnt >= 2) { + if (!IS_GEN2(dev_priv)) { + u_int32_t tmp = I915_READ(PRB0_CTL); + if (tmp & RING_WAIT) { + I915_WRITE(PRB0_CTL, tmp); + (void)I915_READ(PRB0_CTL); + goto out; + } + } + dev_priv->mm.hang_cnt = 0; + /* XXX atomic */ + dev_priv->mm.wedged = 1; + DRM_INFO("gpu hung!\n"); + /* XXX locking */ + mtx_enter(&dev_priv->user_irq_lock); +#if !defined(__NetBSD__) + wakeup(dev_priv); +#else /* !defined(__NetBSD__) */ + cv_broadcast(&dev_priv->condvar); +#endif /* !defined(__NetBSD__) */ + mtx_leave(&dev_priv->user_irq_lock); + inteldrm_error(dev_priv); + return; + } + } else { + dev_priv->mm.hang_cnt = 0; + + dev_priv->mm.last_acthd = acthd; + dev_priv->mm.last_instdone = instdone; + dev_priv->mm.last_instdone1 = instdone1; + } +out: + /* Set ourselves up again, in case we haven't added another batch */ + timeout_add_msec(&dev_priv->mm.hang_timer, 750); +} + +void +i915_move_to_tail(struct inteldrm_obj *obj_priv, struct i915_gem_list *head) +{ + i915_list_remove(obj_priv); + TAILQ_INSERT_TAIL(head, obj_priv, list); + obj_priv->current_list = head; +} + +void +i915_list_remove(struct inteldrm_obj *obj_priv) +{ + if (obj_priv->current_list != NULL) + TAILQ_REMOVE(obj_priv->current_list, obj_priv, list); + obj_priv->current_list = NULL; +} + +/* + * + * Support for managing tiling state of buffer objects. + * + * The idea behind tiling is to increase cache hit rates by rearranging + * pixel data so that a group of pixel accesses are in the same cacheline. + * Performance improvement from doing this on the back/depth buffer are on + * the order of 30%. + * + * Intel architectures make this somewhat more complicated, though, by + * adjustments made to addressing of data when the memory is in interleaved + * mode (matched pairs of DIMMS) to improve memory bandwidth. + * For interleaved memory, the CPU sends every sequential 64 bytes + * to an alternate memory channel so it can get the bandwidth from both. + * + * The GPU also rearranges its accesses for increased bandwidth to interleaved + * memory, and it matches what the CPU does for non-tiled. However, when tiled + * it does it a little differently, since one walks addresses not just in the + * X direction but also Y. So, along with alternating channels when bit + * 6 of the address flips, it also alternates when other bits flip -- Bits 9 + * (every 512 bytes, an X tile scanline) and 10 (every two X tile scanlines) + * are common to both the 915 and 965-class hardware. + * + * The CPU also sometimes XORs in higher bits as well, to improve + * bandwidth doing strided access like we do so frequently in graphics. This + * is called "Channel XOR Randomization" in the MCH documentation. The result + * is that the CPU is XORing in either bit 11 or bit 17 to bit 6 of its address + * decode. + * + * All of this bit 6 XORing has an effect on our memory management, + * as we need to make sure that the 3d driver can correctly address object + * contents. + * + * If we don't have interleaved memory, all tiling is safe and no swizzling is + * required. + * + * When bit 17 is XORed in, we simply refuse to tile at all. Bit + * 17 is not just a page offset, so as we page an object out and back in, + * individual pages in it will have different bit 17 addresses, resulting in + * each 64 bytes being swapped with its neighbor! + * + * Otherwise, if interleaved, we have to tell the 3d driver what the address + * swizzling it needs to do is, since it's writing with the CPU to the pages + * (bit 6 and potentially bit 11 XORed in), and the GPU is reading from the + * pages (bit 6, 9, and 10 XORed in), resulting in a cumulative bit swizzling + * required by the CPU of XORing in bit 6, 9, 10, and potentially 11, in order + * to match what the GPU expects. + */ + +#define MCHBAR_I915 0x44 +#define MCHBAR_I965 0x48 +#define MCHBAR_SIZE (4*4096) + +#define DEVEN_REG 0x54 +#define DEVEN_MCHBAR_EN (1 << 28) + + +/* + * Check the MCHBAR on the host bridge is enabled, and if not allocate it. + * we do not need to actually map it because we access the bar through it's + * mirror on the IGD, however, if it is disabled or not allocated then + * the mirror does not work. *sigh*. + * + * we return a trinary state: + * 0 = already enabled, or can not enable + * 1 = enabled, needs disable + * 2 = enabled, needs disable and free. + */ +int +inteldrm_setup_mchbar(struct inteldrm_softc *dev_priv, +#if !defined(__NetBSD__) + struct pci_attach_args *bpa) +#else /* !defined(__NetBSD__) */ + struct pci_attach_args *bpa, bus_space_handle_t *mchbsh) +#endif /* !defined(__NetBSD__) */ +{ + u_int64_t mchbar_addr; + pcireg_t tmp, low, high = 0; + u_long addr; + int reg = IS_I965G(dev_priv) ? MCHBAR_I965 : MCHBAR_I915; + int ret = 1, enabled = 0; + + if (IS_I915G(dev_priv) || IS_I915GM(dev_priv)) { + tmp = pci_conf_read(bpa->pa_pc, bpa->pa_tag, DEVEN_REG); + enabled = !!(tmp & DEVEN_MCHBAR_EN); + } else { + tmp = pci_conf_read(bpa->pa_pc, bpa->pa_tag, reg); + enabled = tmp & 1; + } + + if (enabled) { + return (0); + } + + if (IS_I965G(dev_priv)) + high = pci_conf_read(bpa->pa_pc, bpa->pa_tag, reg + 4); + low = pci_conf_read(bpa->pa_pc, bpa->pa_tag, reg); + mchbar_addr = ((u_int64_t)high << 32) | low; + + /* + * XXX need to check to see if it's allocated in the pci resources, + * right now we just check to see if there's any address there + * + * if there's no address, then we allocate one. + * note that we can't just use pci_mapreg_map here since some intel + * BARs are special in that they set bit 0 to show they're enabled, + * this is not handled by generic pci code. + */ + if (mchbar_addr == 0) { + addr = (u_long)mchbar_addr; +#if !defined(__NetBSD__) + if (bpa->pa_memex == NULL || extent_alloc(bpa->pa_memex, + MCHBAR_SIZE, MCHBAR_SIZE, 0, 0, 0, &addr)) { +#else /* !defined(__NetBSD__) */ + if (bus_space_alloc(bpa->pa_memt, 0, 0xffffffff, + MCHBAR_SIZE, MCHBAR_SIZE, 0, 0, &addr, mchbsh)) { +#endif /* !defined(__NetBSD__) */ + return (0); /* just say we don't need to disable */ + } else { + mchbar_addr = addr; + ret = 2; + /* We've allocated it, now fill in the BAR again */ + if (IS_I965G(dev_priv)) + pci_conf_write(bpa->pa_pc, bpa->pa_tag, + reg + 4, upper_32_bits(mchbar_addr)); + pci_conf_write(bpa->pa_pc, bpa->pa_tag, + reg, mchbar_addr & 0xffffffff); + } + } + /* set the enable bit */ + if (IS_I915G(dev_priv) || IS_I915GM(dev_priv)) { + pci_conf_write(bpa->pa_pc, bpa->pa_tag, DEVEN_REG, + tmp | DEVEN_MCHBAR_EN); + } else { + tmp = pci_conf_read(bpa->pa_pc, bpa->pa_tag, reg); + pci_conf_write(bpa->pa_pc, bpa->pa_tag, reg, tmp | 1); + } + + return (ret); +} + +/* + * we take the trinary returned from inteldrm_setup_mchbar and clean up after + * it. + */ +void +inteldrm_teardown_mchbar(struct inteldrm_softc *dev_priv, +#if !defined(__NetBSD__) + struct pci_attach_args *bpa, int disable) +#else /* !defined(__NetBSD__) */ + struct pci_attach_args *bpa, int disable, bus_space_handle_t mchbsh) +#endif /* !defined(__NetBSD__) */ +{ + u_int64_t mchbar_addr; + pcireg_t tmp, low, high = 0; + int reg = IS_I965G(dev_priv) ? MCHBAR_I965 : MCHBAR_I915; + + switch(disable) { + case 2: + if (IS_I965G(dev_priv)) + high = pci_conf_read(bpa->pa_pc, bpa->pa_tag, reg + 4); + low = pci_conf_read(bpa->pa_pc, bpa->pa_tag, reg); + mchbar_addr = ((u_int64_t)high << 32) | low; +#if !defined(__NetBSD__) + if (bpa->pa_memex) + extent_free(bpa->pa_memex, mchbar_addr, MCHBAR_SIZE, 0); +#else /* !defined(__NetBSD__) */ + bus_space_free(bpa->pa_memt, mchbsh, MCHBAR_SIZE); +#endif /* !defined(__NetBSD__) */ + /* FALLTHROUGH */ + case 1: + if (IS_I915G(dev_priv) || IS_I915GM(dev_priv)) { + tmp = pci_conf_read(bpa->pa_pc, bpa->pa_tag, DEVEN_REG); + tmp &= ~DEVEN_MCHBAR_EN; + pci_conf_write(bpa->pa_pc, bpa->pa_tag, DEVEN_REG, tmp); + } else { + tmp = pci_conf_read(bpa->pa_pc, bpa->pa_tag, reg); + tmp &= ~1; + pci_conf_write(bpa->pa_pc, bpa->pa_tag, reg, tmp); + } + break; + case 0: + default: + break; + }; +} + +/** + * Detects bit 6 swizzling of address lookup between IGD access and CPU + * access through main memory. + */ +void +inteldrm_detect_bit_6_swizzle(struct inteldrm_softc *dev_priv, + struct pci_attach_args *bpa) +{ + uint32_t swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN; + uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN; + int need_disable; + + if (!IS_I9XX(dev_priv)) { + /* As far as we know, the 865 doesn't have these bit 6 + * swizzling issues. + */ + swizzle_x = I915_BIT_6_SWIZZLE_NONE; + swizzle_y = I915_BIT_6_SWIZZLE_NONE; + } else if (HAS_PCH_SPLIT(dev_priv)) { + /* + * On ironlake and sandybridge the swizzling is the same + * no matter what the DRAM config + */ + swizzle_x = I915_BIT_6_SWIZZLE_9_10; + swizzle_y = I915_BIT_6_SWIZZLE_9; + } else if (IS_MOBILE(dev_priv)) { + uint32_t dcc; +#if defined(__NetBSD__) + bus_space_handle_t mchbsh; +#endif /* defined(__NetBSD__) */ + + /* try to enable MCHBAR, a lot of biosen disable it */ +#if !defined(__NetBSD__) + need_disable = inteldrm_setup_mchbar(dev_priv, bpa); +#else /* !defined(__NetBSD__) */ + need_disable = inteldrm_setup_mchbar(dev_priv, bpa, &mchbsh); +#endif /* !defined(__NetBSD__) */ + + /* On 915-945 and GM965, channel interleave by the CPU is + * determined by DCC. The CPU will alternate based on bit 6 + * in interleaved mode, and the GPU will then also alternate + * on bit 6, 9, and 10 for X, but the CPU may also optionally + * alternate based on bit 17 (XOR not disabled and XOR + * bit == 17). + */ + dcc = I915_READ(DCC); + switch (dcc & DCC_ADDRESSING_MODE_MASK) { + case DCC_ADDRESSING_MODE_SINGLE_CHANNEL: + case DCC_ADDRESSING_MODE_DUAL_CHANNEL_ASYMMETRIC: + swizzle_x = I915_BIT_6_SWIZZLE_NONE; + swizzle_y = I915_BIT_6_SWIZZLE_NONE; + break; + case DCC_ADDRESSING_MODE_DUAL_CHANNEL_INTERLEAVED: + if (dcc & DCC_CHANNEL_XOR_DISABLE) { + /* This is the base swizzling by the GPU for + * tiled buffers. + */ + swizzle_x = I915_BIT_6_SWIZZLE_9_10; + swizzle_y = I915_BIT_6_SWIZZLE_9; + } else if ((dcc & DCC_CHANNEL_XOR_BIT_17) == 0) { + /* Bit 11 swizzling by the CPU in addition. */ + swizzle_x = I915_BIT_6_SWIZZLE_9_10_11; + swizzle_y = I915_BIT_6_SWIZZLE_9_11; + } else { + /* Bit 17 swizzling by the CPU in addition. */ + swizzle_x = I915_BIT_6_SWIZZLE_9_10_17; + swizzle_y = I915_BIT_6_SWIZZLE_9_17; + } + break; + } + if (dcc == 0xffffffff) { + DRM_ERROR("Couldn't read from MCHBAR. " + "Disabling tiling.\n"); + swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN; + swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN; + } + +#if !defined(__NetBSD__) + inteldrm_teardown_mchbar(dev_priv, bpa, need_disable); +#else /* !defined(__NetBSD__) */ + inteldrm_teardown_mchbar(dev_priv, bpa, need_disable, mchbsh); +#endif /* !defined(__NetBSD__) */ + } else { + /* The 965, G33, and newer, have a very flexible memory + * configuration. It will enable dual-channel mode + * (interleaving) on as much memory as it can, and the GPU + * will additionally sometimes enable different bit 6 + * swizzling for tiled objects from the CPU. + * + * Here's what I found on G965: + * + * slot fill memory size swizzling + * 0A 0B 1A 1B 1-ch 2-ch + * 512 0 0 0 512 0 O + * 512 0 512 0 16 1008 X + * 512 0 0 512 16 1008 X + * 0 512 0 512 16 1008 X + * 1024 1024 1024 0 2048 1024 O + * + * We could probably detect this based on either the DRB + * matching, which was the case for the swizzling required in + * the table above, or from the 1-ch value being less than + * the minimum size of a rank. + */ + if (I915_READ16(C0DRB3) != I915_READ16(C1DRB3)) { + swizzle_x = I915_BIT_6_SWIZZLE_NONE; + swizzle_y = I915_BIT_6_SWIZZLE_NONE; + } else { + swizzle_x = I915_BIT_6_SWIZZLE_9_10; + swizzle_y = I915_BIT_6_SWIZZLE_9; + } + } + + dev_priv->mm.bit_6_swizzle_x = swizzle_x; + dev_priv->mm.bit_6_swizzle_y = swizzle_y; +} + +int +inteldrm_swizzle_page(struct vm_page *pg) +{ + vaddr_t va; + int i; + u_int8_t temp[64], *vaddr; + +#if defined (__HAVE_PMAP_DIRECT) + va = pmap_map_direct(pg); +#else +#if !defined(__NetBSD__) + va = uvm_km_valloc(kernel_map, PAGE_SIZE); +#else /* !defined(__NetBSD__) */ + va = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, UVM_KMF_VAONLY); +#endif /* !defined(__NetBSD__) */ + if (va == 0) + return (ENOMEM); +#if !defined(__NetBSD__) + pmap_kenter_pa(va, VM_PAGE_TO_PHYS(pg), UVM_PROT_RW); +#else /* !defined(__NetBSD__) */ + pmap_kenter_pa(va, VM_PAGE_TO_PHYS(pg), + VM_PROT_READ | VM_PROT_WRITE, PMAP_WRITE_COMBINE); +#endif /* !defined(__NetBSD__) */ + pmap_update(pmap_kernel()); +#endif + vaddr = (u_int8_t *)va; + + for (i = 0; i < PAGE_SIZE; i += 128) { + memcpy(temp, &vaddr[i], 64); + memcpy(&vaddr[i], &vaddr[i + 64], 64); + memcpy(&vaddr[i + 64], temp, 64); + } + +#if defined (__HAVE_PMAP_DIRECT) + pmap_unmap_direct(va); +#else + pmap_kremove(va, PAGE_SIZE); + pmap_update(pmap_kernel()); +#if !defined(__NetBSD__) + uvm_km_free(kernel_map, va, PAGE_SIZE); +#else /* !defined(__NetBSD__) */ + uvm_km_free(kernel_map, va, PAGE_SIZE, UVM_KMF_VAONLY); +#endif /* !defined(__NetBSD__) */ +#endif + return (0); +} + +void +i915_gem_bit_17_swizzle(struct drm_obj *obj) +{ + struct drm_device *dev = obj->dev; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + struct vm_page *pg; + bus_dma_segment_t *segp; + int page_count = obj->size >> PAGE_SHIFT; + int i, n, ret; + + if (dev_priv->mm.bit_6_swizzle_x != I915_BIT_6_SWIZZLE_9_10_17 || + obj_priv->bit_17 == NULL) + return; + + segp = &obj_priv->dma_segs[0]; + n = 0; + for (i = 0; i < page_count; i++) { + /* compare bit 17 with previous one (in case we swapped). + * if they don't match we'll have to swizzle the page + */ + if ((((segp->ds_addr + n) >> 17) & 0x1) != + test_bit(i, obj_priv->bit_17)) { + /* XXX move this to somewhere where we already have pg */ + pg = PHYS_TO_VM_PAGE(segp->ds_addr + n); + KASSERT(pg != NULL); + ret = inteldrm_swizzle_page(pg); + if (ret) + return; +#if !defined(__NetBSD__) + atomic_clearbits_int(&pg->pg_flags, PG_CLEAN); +#else /* !defined(__NetBSD__) */ + /* XXX This assignment should be atomic. */ + pg->flags &= ~(PG_CLEAN); +#endif /* !defined(__NetBSD__) */ + } + + n += PAGE_SIZE; + if (n >= segp->ds_len) { + n = 0; + segp++; + } + } + +} + +void +i915_gem_save_bit_17_swizzle(struct drm_obj *obj) +{ + struct drm_device *dev = obj->dev; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + bus_dma_segment_t *segp; + int page_count = obj->size >> PAGE_SHIFT, i, n; + + if (dev_priv->mm.bit_6_swizzle_x != I915_BIT_6_SWIZZLE_9_10_17) + return; + + if (obj_priv->bit_17 == NULL) { + /* round up number of pages to a multiple of 32 so we know what + * size to make the bitmask. XXX this is wasteful with malloc + * and a better way should be done + */ + size_t nb17 = ((page_count + 31) & ~31)/32; + obj_priv->bit_17 = drm_alloc(nb17 * sizeof(u_int32_t)); + if (obj_priv->bit_17 == NULL) { + return; + } + + } + + segp = &obj_priv->dma_segs[0]; + n = 0; + for (i = 0; i < page_count; i++) { + if ((segp->ds_addr + n) & (1 << 17)) + set_bit(i, obj_priv->bit_17); + else + clear_bit(i, obj_priv->bit_17); + + n += PAGE_SIZE; + if (n >= segp->ds_len) { + n = 0; + segp++; + } + } +} + +bus_size_t +i915_get_fence_size(struct inteldrm_softc *dev_priv, bus_size_t size) +{ + bus_size_t i, start; + + if (IS_I965G(dev_priv)) { + /* 965 can have fences anywhere, so align to gpu-page size */ + return ((size + (4096 - 1)) & ~(4096 - 1)); + } else { + /* + * Align the size to a power of two greater than the smallest + * fence size. + */ + if (IS_I9XX(dev_priv)) + start = 1024 * 1024; + else + start = 512 * 1024; + + for (i = start; i < size; i <<= 1) + ; + + return (i); + } +} + +int +i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode) +{ + struct inteldrm_softc *dev_priv = dev->dev_private; + int tile_width; + + /* Linear is always ok */ + if (tiling_mode == I915_TILING_NONE) + return (1); + + if (!IS_I9XX(dev_priv) || (tiling_mode == I915_TILING_Y && + HAS_128_BYTE_Y_TILING(dev_priv))) + tile_width = 128; + else + tile_width = 512; + + /* Check stride and size constraints */ + if (IS_I965G(dev_priv)) { + /* fence reg has end address, so size is ok */ + if (stride / 128 > I965_FENCE_MAX_PITCH_VAL) + return (0); + } else if (IS_GEN3(dev_priv) || IS_GEN2(dev_priv)) { + if (stride > 8192) + return (0); + if (IS_GEN3(dev_priv)) { + if (size > I830_FENCE_MAX_SIZE_VAL << 20) + return (0); + } else if (size > I830_FENCE_MAX_SIZE_VAL << 19) + return (0); + } + + /* 965+ just needs multiples of the tile width */ + if (IS_I965G(dev_priv)) + return ((stride & (tile_width - 1)) == 0); + + /* Pre-965 needs power-of-two */ + if (stride < tile_width || stride & (stride - 1) || + i915_get_fence_size(dev_priv, size) != size) + return (0); + return (1); +} + +int +i915_gem_object_fence_offset_ok(struct drm_obj *obj, int tiling_mode) +{ + struct drm_device *dev = obj->dev; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct inteldrm_obj *obj_priv = (struct inteldrm_obj *)obj; + + if (obj_priv->dmamap == NULL || tiling_mode == I915_TILING_NONE) + return (1); + + if (!IS_I965G(dev_priv)) { + if (obj_priv->gtt_offset & (obj->size -1)) + return (0); + if (IS_I9XX(dev_priv)) { + if (obj_priv->gtt_offset & ~I915_FENCE_START_MASK) + return (0); + } else { + if (obj_priv->gtt_offset & ~I830_FENCE_START_MASK) + return (0); + } + } + return (1); +} +/** + * Sets the tiling mode of an object, returning the required swizzling of + * bit 6 of addresses in the object. + */ +int +i915_gem_set_tiling(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_i915_gem_set_tiling *args = data; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct drm_obj *obj; + struct inteldrm_obj *obj_priv; + int ret = 0; + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (obj == NULL) + return (EBADF); + obj_priv = (struct inteldrm_obj *)obj; + drm_hold_object(obj); + + if (obj_priv->pin_count != 0) { + ret = EBUSY; + goto out; + } + if (i915_tiling_ok(dev, args->stride, obj->size, + args->tiling_mode) == 0) { + ret = EINVAL; + goto out; + } + + if (args->tiling_mode == I915_TILING_NONE) { + args->swizzle_mode = I915_BIT_6_SWIZZLE_NONE; + args->stride = 0; + } else { + if (args->tiling_mode == I915_TILING_X) + args->swizzle_mode = dev_priv->mm.bit_6_swizzle_x; + else + args->swizzle_mode = dev_priv->mm.bit_6_swizzle_y; + /* If we can't handle the swizzling, make it untiled. */ + if (args->swizzle_mode == I915_BIT_6_SWIZZLE_UNKNOWN) { + args->tiling_mode = I915_TILING_NONE; + args->swizzle_mode = I915_BIT_6_SWIZZLE_NONE; + args->stride = 0; + } + } + + if (args->tiling_mode != obj_priv->tiling_mode || + args->stride != obj_priv->stride) { + /* + * We need to rebind the object if its current allocation no + * longer meets the alignment restrictions for its new tiling + * mode. Otherwise we can leave it alone, but must clear any + * fence register. + */ + /* fence may no longer be correct, wipe it */ + inteldrm_wipe_mappings(obj); + if (obj_priv->fence_reg != I915_FENCE_REG_NONE) + atomic_setbits_int(&obj->do_flags, + I915_FENCE_INVALID); + obj_priv->tiling_mode = args->tiling_mode; + obj_priv->stride = args->stride; + } + +out: + drm_unhold_and_unref(obj); + + return (ret); +} + +/** + * Returns the current tiling mode and required bit 6 swizzling for the object. + */ +int +i915_gem_get_tiling(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_i915_gem_get_tiling *args = data; + struct inteldrm_softc *dev_priv = dev->dev_private; + struct drm_obj *obj; + struct inteldrm_obj *obj_priv; + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (obj == NULL) + return (EBADF); + drm_hold_object(obj); + obj_priv = (struct inteldrm_obj *)obj; + + args->tiling_mode = obj_priv->tiling_mode; + switch (obj_priv->tiling_mode) { + case I915_TILING_X: + args->swizzle_mode = dev_priv->mm.bit_6_swizzle_x; + break; + case I915_TILING_Y: + args->swizzle_mode = dev_priv->mm.bit_6_swizzle_y; + break; + case I915_TILING_NONE: + args->swizzle_mode = I915_BIT_6_SWIZZLE_NONE; + break; + default: + DRM_ERROR("unknown tiling mode\n"); + } + + drm_unhold_and_unref(obj); + + return 0; +} + + +/** + * inteldrm_pipe_enabled - check if a pipe is enabled + * @dev: DRM device + * @pipe: pipe to check + * + * Reading certain registers when the pipe is disabled can hang the chip. + * Use this routine to make sure the PLL is running and the pipe is active + * before reading such registers if unsure. + */ +int +inteldrm_pipe_enabled(struct inteldrm_softc *dev_priv, int pipe) +{ + bus_size_t pipeconf; + +#if 0 + if (IS_IRONLAKE(dev_priv)) { + pipeconf = (pipe ? PCH_DPLL_A : PCH_DPLL_B); + } else { +#endif + pipeconf = (pipe ? PIPEBCONF : PIPEACONF); +#if 0 + } +#endif + return ((I915_READ(pipeconf) & PIPEACONF_ENABLE) == PIPEACONF_ENABLE); +} + +/* + * Register save/restore for various instances + */ +void +i915_save_palette(struct inteldrm_softc *dev_priv, enum pipe pipe) +{ + u_int32_t *array; + bus_size_t reg = (pipe == PIPE_A ? PALETTE_A : PALETTE_B); + int i; + + if (!inteldrm_pipe_enabled(dev_priv, pipe)) + return; + + if (IS_IRONLAKE(dev_priv)) + reg = (pipe == PIPE_A) ? LGC_PALETTE_A : LGC_PALETTE_B; + + if (pipe == PIPE_A) + array = dev_priv->save_palette_a; + else + array = dev_priv->save_palette_b; + + for (i = 0; i < 256; i++) + array[i] = I915_READ(reg + (i << 2)); +} + +void +i915_restore_palette(struct inteldrm_softc *dev_priv, enum pipe pipe) +{ + u_int32_t *array; + bus_size_t reg = (pipe == PIPE_A ? PALETTE_A : PALETTE_B); + int i; + + if (!inteldrm_pipe_enabled(dev_priv, pipe)) + return; + + if (IS_IRONLAKE(dev_priv)) + reg = (pipe == PIPE_A) ? LGC_PALETTE_A : LGC_PALETTE_B; + + if (pipe == PIPE_A) + array = dev_priv->save_palette_a; + else + array = dev_priv->save_palette_b; + + for(i = 0; i < 256; i++) + I915_WRITE(reg + (i << 2), array[i]); +} + +u_int8_t +i915_read_ar(struct inteldrm_softc *dev_priv, u_int16_t st01, + u_int8_t reg, u_int16_t palette_enable) +{ + I915_READ8(st01); + I915_WRITE8(VGA_AR_INDEX, palette_enable | reg); + return I915_READ8(VGA_AR_DATA_READ); +} + +void +i915_write_ar(struct inteldrm_softc *dev_priv, u_int16_t st01, u_int8_t reg, + u_int8_t val, u_int16_t palette_enable) +{ + I915_READ8(st01); + I915_WRITE8(VGA_AR_INDEX, palette_enable | reg); + I915_WRITE8(VGA_AR_DATA_WRITE, val); +} + +u_int8_t +i915_read_indexed(struct inteldrm_softc *dev_priv, u_int16_t index_port, + u_int16_t data_port, u_int8_t reg) +{ + I915_WRITE8(index_port, reg); + return I915_READ8(data_port); +} + +void +i915_write_indexed(struct inteldrm_softc *dev_priv, u_int16_t index_port, + u_int16_t data_port, u_int8_t reg, u_int8_t val) +{ + I915_WRITE8(index_port, reg); + I915_WRITE8(data_port, val); +} + +void +i915_save_vga(struct inteldrm_softc *dev_priv) +{ + int i; + u16 cr_index, cr_data, st01; + + /* VGA color palette registers */ + dev_priv->saveDACMASK = I915_READ8(VGA_DACMASK); + + /* MSR bits */ + dev_priv->saveMSR = I915_READ8(VGA_MSR_READ); + if (dev_priv->saveMSR & VGA_MSR_CGA_MODE) { + cr_index = VGA_CR_INDEX_CGA; + cr_data = VGA_CR_DATA_CGA; + st01 = VGA_ST01_CGA; + } else { + cr_index = VGA_CR_INDEX_MDA; + cr_data = VGA_CR_DATA_MDA; + st01 = VGA_ST01_MDA; + } + + /* CRT controller regs */ + i915_write_indexed(dev_priv, cr_index, cr_data, 0x11, + i915_read_indexed(dev_priv, cr_index, cr_data, 0x11) & (~0x80)); + for (i = 0; i <= 0x24; i++) + dev_priv->saveCR[i] = i915_read_indexed(dev_priv, + cr_index, cr_data, i); + /* Make sure we don't turn off CR group 0 writes */ + dev_priv->saveCR[0x11] &= ~0x80; + + /* Attribute controller registers */ + I915_READ8(st01); + dev_priv->saveAR_INDEX = I915_READ8(VGA_AR_INDEX); + for (i = 0; i <= 0x14; i++) + dev_priv->saveAR[i] = i915_read_ar(dev_priv, st01, i, 0); + I915_READ8(st01); + I915_WRITE8(VGA_AR_INDEX, dev_priv->saveAR_INDEX); + I915_READ8(st01); + + /* Graphics controller registers */ + for (i = 0; i < 9; i++) + dev_priv->saveGR[i] = i915_read_indexed(dev_priv, + VGA_GR_INDEX, VGA_GR_DATA, i); + + dev_priv->saveGR[0x10] = i915_read_indexed(dev_priv, + VGA_GR_INDEX, VGA_GR_DATA, 0x10); + dev_priv->saveGR[0x11] = i915_read_indexed(dev_priv, + VGA_GR_INDEX, VGA_GR_DATA, 0x11); + dev_priv->saveGR[0x18] = i915_read_indexed(dev_priv, + VGA_GR_INDEX, VGA_GR_DATA, 0x18); + + /* Sequencer registers */ + for (i = 0; i < 8; i++) + dev_priv->saveSR[i] = i915_read_indexed(dev_priv, + VGA_SR_INDEX, VGA_SR_DATA, i); +} + +void +i915_restore_vga(struct inteldrm_softc *dev_priv) +{ + u_int16_t cr_index, cr_data, st01; + int i; + + /* MSR bits */ + I915_WRITE8(VGA_MSR_WRITE, dev_priv->saveMSR); + if (dev_priv->saveMSR & VGA_MSR_CGA_MODE) { + cr_index = VGA_CR_INDEX_CGA; + cr_data = VGA_CR_DATA_CGA; + st01 = VGA_ST01_CGA; + } else { + cr_index = VGA_CR_INDEX_MDA; + cr_data = VGA_CR_DATA_MDA; + st01 = VGA_ST01_MDA; + } + + /* Sequencer registers, don't write SR07 */ + for (i = 0; i < 7; i++) + i915_write_indexed(dev_priv, VGA_SR_INDEX, VGA_SR_DATA, i, + dev_priv->saveSR[i]); + + /* CRT controller regs */ + /* Enable CR group 0 writes */ + i915_write_indexed(dev_priv, cr_index, cr_data, 0x11, + dev_priv->saveCR[0x11]); + for (i = 0; i <= 0x24; i++) + i915_write_indexed(dev_priv, cr_index, cr_data, + i, dev_priv->saveCR[i]); + + /* Graphics controller regs */ + for (i = 0; i < 9; i++) + i915_write_indexed(dev_priv, VGA_GR_INDEX, VGA_GR_DATA, i, + dev_priv->saveGR[i]); + + i915_write_indexed(dev_priv, VGA_GR_INDEX, VGA_GR_DATA, 0x10, + dev_priv->saveGR[0x10]); + i915_write_indexed(dev_priv, VGA_GR_INDEX, VGA_GR_DATA, 0x11, + dev_priv->saveGR[0x11]); + i915_write_indexed(dev_priv, VGA_GR_INDEX, VGA_GR_DATA, 0x18, + dev_priv->saveGR[0x18]); + + /* Attribute controller registers */ + I915_READ8(st01); /* switch back to index mode */ + for (i = 0; i <= 0x14; i++) + i915_write_ar(dev_priv, st01, i, dev_priv->saveAR[i], 0); + I915_READ8(st01); /* switch back to index mode */ + I915_WRITE8(VGA_AR_INDEX, dev_priv->saveAR_INDEX | 0x20); + I915_READ8(st01); + + /* VGA color palette registers */ + I915_WRITE8(VGA_DACMASK, dev_priv->saveDACMASK); +} + +void +i915_save_modeset_reg(struct inteldrm_softc *dev_priv) +{ + + if (IS_IRONLAKE(dev_priv)) { + dev_priv->savePCH_DREF_CONTROL = I915_READ(PCH_DREF_CONTROL); + dev_priv->saveDISP_ARB_CTL = I915_READ(DISP_ARB_CTL); + } + + /* Pipe & plane A info */ + dev_priv->savePIPEACONF = I915_READ(PIPEACONF); + dev_priv->savePIPEASRC = I915_READ(PIPEASRC); + if (IS_IRONLAKE(dev_priv)) { + dev_priv->saveFPA0 = I915_READ(PCH_FPA0); + dev_priv->saveFPA1 = I915_READ(PCH_FPA1); + dev_priv->saveDPLL_A = I915_READ(PCH_DPLL_A); + } else { + dev_priv->saveFPA0 = I915_READ(FPA0); + dev_priv->saveFPA1 = I915_READ(FPA1); + dev_priv->saveDPLL_A = I915_READ(DPLL_A); + } + if (IS_I965G(dev_priv) && !IS_IRONLAKE(dev_priv)) + dev_priv->saveDPLL_A_MD = I915_READ(DPLL_A_MD); + dev_priv->saveHTOTAL_A = I915_READ(HTOTAL_A); + dev_priv->saveHBLANK_A = I915_READ(HBLANK_A); + dev_priv->saveHSYNC_A = I915_READ(HSYNC_A); + dev_priv->saveVTOTAL_A = I915_READ(VTOTAL_A); + dev_priv->saveVBLANK_A = I915_READ(VBLANK_A); + dev_priv->saveVSYNC_A = I915_READ(VSYNC_A); + if (IS_IRONLAKE(dev_priv)) { + dev_priv->savePIPEA_DATA_M1 = I915_READ(PIPEA_DATA_M1); + dev_priv->savePIPEA_DATA_N1 = I915_READ(PIPEA_DATA_N1); + dev_priv->savePIPEA_LINK_M1 = I915_READ(PIPEA_LINK_M1); + dev_priv->savePIPEA_LINK_N1 = I915_READ(PIPEA_LINK_N1); + + dev_priv->saveFDI_TXA_CTL = I915_READ(FDI_TXA_CTL); + dev_priv->saveFDI_RXA_CTL = I915_READ(FDI_RXA_CTL); + + dev_priv->savePFA_CTL_1 = I915_READ(PFA_CTL_1); + dev_priv->savePFA_WIN_SZ = I915_READ(PFA_WIN_SZ); + dev_priv->savePFA_WIN_POS = I915_READ(PFA_WIN_POS); + + dev_priv->saveTRANSACONF = I915_READ(TRANSACONF); + dev_priv->saveTRANS_HTOTAL_A = I915_READ(TRANS_HTOTAL_A); + dev_priv->saveTRANS_HBLANK_A = I915_READ(TRANS_HBLANK_A); + dev_priv->saveTRANS_HSYNC_A = I915_READ(TRANS_HSYNC_A); + dev_priv->saveTRANS_VTOTAL_A = I915_READ(TRANS_VTOTAL_A); + dev_priv->saveTRANS_VBLANK_A = I915_READ(TRANS_VBLANK_A); + dev_priv->saveTRANS_VSYNC_A = I915_READ(TRANS_VSYNC_A); + } else { + dev_priv->saveBCLRPAT_A = I915_READ(BCLRPAT_A); + } + + dev_priv->saveDSPACNTR = I915_READ(DSPACNTR); + dev_priv->saveDSPASTRIDE = I915_READ(DSPASTRIDE); + dev_priv->saveDSPASIZE = I915_READ(DSPASIZE); + dev_priv->saveDSPAPOS = I915_READ(DSPAPOS); + dev_priv->saveDSPAADDR = I915_READ(DSPAADDR); + if (IS_I965G(dev_priv)) { + dev_priv->saveDSPASURF = I915_READ(DSPASURF); + dev_priv->saveDSPATILEOFF = I915_READ(DSPATILEOFF); + } + i915_save_palette(dev_priv, PIPE_A); + dev_priv->savePIPEASTAT = I915_READ(PIPEASTAT); + + /* Pipe & plane B info */ + dev_priv->savePIPEBCONF = I915_READ(PIPEBCONF); + dev_priv->savePIPEBSRC = I915_READ(PIPEBSRC); + if (IS_IRONLAKE(dev_priv)) { + dev_priv->saveFPA0 = I915_READ(PCH_FPB0); + dev_priv->saveFPA1 = I915_READ(PCH_FPB1); + dev_priv->saveDPLL_A = I915_READ(PCH_DPLL_B); + } else { + dev_priv->saveFPB0 = I915_READ(FPB0); + dev_priv->saveFPB1 = I915_READ(FPB1); + dev_priv->saveDPLL_B = I915_READ(DPLL_B); + } + if (IS_I965G(dev_priv) && !IS_IRONLAKE(dev_priv)) + dev_priv->saveDPLL_B_MD = I915_READ(DPLL_B_MD); + dev_priv->saveHTOTAL_B = I915_READ(HTOTAL_B); + dev_priv->saveHBLANK_B = I915_READ(HBLANK_B); + dev_priv->saveHSYNC_B = I915_READ(HSYNC_B); + dev_priv->saveVTOTAL_B = I915_READ(VTOTAL_B); + dev_priv->saveVBLANK_B = I915_READ(VBLANK_B); + dev_priv->saveVSYNC_B = I915_READ(VSYNC_B); + if (IS_IRONLAKE(dev_priv)) { + dev_priv->savePIPEB_DATA_M1 = I915_READ(PIPEB_DATA_M1); + dev_priv->savePIPEB_DATA_N1 = I915_READ(PIPEB_DATA_N1); + dev_priv->savePIPEB_LINK_M1 = I915_READ(PIPEB_LINK_M1); + dev_priv->savePIPEB_LINK_N1 = I915_READ(PIPEB_LINK_N1); + + dev_priv->saveFDI_TXB_CTL = I915_READ(FDI_TXB_CTL); + dev_priv->saveFDI_RXB_CTL = I915_READ(FDI_RXB_CTL); + + dev_priv->savePFB_CTL_1 = I915_READ(PFB_CTL_1); + dev_priv->savePFB_WIN_SZ = I915_READ(PFB_WIN_SZ); + dev_priv->savePFB_WIN_POS = I915_READ(PFB_WIN_POS); + + dev_priv->saveTRANSBCONF = I915_READ(TRANSBCONF); + dev_priv->saveTRANS_HTOTAL_B = I915_READ(TRANS_HTOTAL_B); + dev_priv->saveTRANS_HBLANK_B = I915_READ(TRANS_HBLANK_B); + dev_priv->saveTRANS_HSYNC_B = I915_READ(TRANS_HSYNC_B); + dev_priv->saveTRANS_VTOTAL_B = I915_READ(TRANS_VTOTAL_B); + dev_priv->saveTRANS_VBLANK_B = I915_READ(TRANS_VBLANK_B); + dev_priv->saveTRANS_VSYNC_B = I915_READ(TRANS_VSYNC_B); + } else { + dev_priv->saveBCLRPAT_A = I915_READ(BCLRPAT_A); + } + + dev_priv->saveDSPBCNTR = I915_READ(DSPBCNTR); + dev_priv->saveDSPBSTRIDE = I915_READ(DSPBSTRIDE); + dev_priv->saveDSPBSIZE = I915_READ(DSPBSIZE); + dev_priv->saveDSPBPOS = I915_READ(DSPBPOS); + dev_priv->saveDSPBADDR = I915_READ(DSPBADDR); + if (IS_I965GM(dev_priv) || IS_GM45(dev_priv)) { + dev_priv->saveDSPBSURF = I915_READ(DSPBSURF); + dev_priv->saveDSPBTILEOFF = I915_READ(DSPBTILEOFF); + } + i915_save_palette(dev_priv, PIPE_B); + dev_priv->savePIPEBSTAT = I915_READ(PIPEBSTAT); +} + +void +i915_restore_modeset_reg(struct inteldrm_softc *dev_priv) +{ + bus_size_t dpll_a_reg, fpa0_reg, fpa1_reg; + bus_size_t dpll_b_reg, fpb0_reg, fpb1_reg; + + if (IS_IRONLAKE(dev_priv)) { + dpll_a_reg = PCH_DPLL_A; + dpll_b_reg = PCH_DPLL_B; + fpa0_reg = PCH_FPA0; + fpb0_reg = PCH_FPB0; + fpa1_reg = PCH_FPA1; + fpb1_reg = PCH_FPB1; + } else { + dpll_a_reg = DPLL_A; + dpll_b_reg = DPLL_B; + fpa0_reg = FPA0; + fpb0_reg = FPB0; + fpa1_reg = FPA1; + fpb1_reg = FPB1; + } + + if (IS_IRONLAKE(dev_priv)) { + I915_WRITE(PCH_DREF_CONTROL, dev_priv->savePCH_DREF_CONTROL); + I915_WRITE(DISP_ARB_CTL, dev_priv->saveDISP_ARB_CTL); + } + + /* Pipe & plane A info */ + /* Prime the clock */ + if (dev_priv->saveDPLL_A & DPLL_VCO_ENABLE) { + I915_WRITE(DPLL_A, dev_priv->saveDPLL_A & + ~DPLL_VCO_ENABLE); + DRM_UDELAY(150); + } + I915_WRITE(fpa0_reg, dev_priv->saveFPA0); + I915_WRITE(fpa1_reg, dev_priv->saveFPA1); + /* Actually enable it */ + I915_WRITE(dpll_a_reg, dev_priv->saveDPLL_A); + DRM_UDELAY(150); + if (IS_I965G(dev_priv) && !IS_IRONLAKE(dev_priv)) + I915_WRITE(DPLL_A_MD, dev_priv->saveDPLL_A_MD); + DRM_UDELAY(150); + + /* Restore mode */ + I915_WRITE(HTOTAL_A, dev_priv->saveHTOTAL_A); + I915_WRITE(HBLANK_A, dev_priv->saveHBLANK_A); + I915_WRITE(HSYNC_A, dev_priv->saveHSYNC_A); + I915_WRITE(VTOTAL_A, dev_priv->saveVTOTAL_A); + I915_WRITE(VBLANK_A, dev_priv->saveVBLANK_A); + I915_WRITE(VSYNC_A, dev_priv->saveVSYNC_A); + if (IS_IRONLAKE(dev_priv)) { + I915_WRITE(PIPEA_DATA_M1, dev_priv->savePIPEA_DATA_M1); + I915_WRITE(PIPEA_DATA_N1, dev_priv->savePIPEA_DATA_N1); + I915_WRITE(PIPEA_LINK_M1, dev_priv->savePIPEA_LINK_M1); + I915_WRITE(PIPEA_LINK_N1, dev_priv->savePIPEA_LINK_N1); + + I915_WRITE(FDI_RXA_CTL, dev_priv->saveFDI_RXA_CTL); + I915_WRITE(FDI_TXA_CTL, dev_priv->saveFDI_TXA_CTL); + + I915_WRITE(PFA_CTL_1, dev_priv->savePFA_CTL_1); + I915_WRITE(PFA_WIN_SZ, dev_priv->savePFA_WIN_SZ); + I915_WRITE(PFA_WIN_POS, dev_priv->savePFA_WIN_POS); + + I915_WRITE(TRANSACONF, dev_priv->saveTRANSACONF); + I915_WRITE(TRANS_HTOTAL_A, dev_priv->saveTRANS_HTOTAL_A); + I915_WRITE(TRANS_HBLANK_A, dev_priv->saveTRANS_HBLANK_A); + I915_WRITE(TRANS_HSYNC_A, dev_priv->saveTRANS_HSYNC_A); + I915_WRITE(TRANS_VTOTAL_A, dev_priv->saveTRANS_VTOTAL_A); + I915_WRITE(TRANS_VBLANK_A, dev_priv->saveTRANS_VBLANK_A); + I915_WRITE(TRANS_VSYNC_A, dev_priv->saveTRANS_VSYNC_A); + } else { + I915_WRITE(BCLRPAT_A, dev_priv->saveBCLRPAT_A); + } + + /* Restore plane info */ + I915_WRITE(DSPASIZE, dev_priv->saveDSPASIZE); + I915_WRITE(DSPAPOS, dev_priv->saveDSPAPOS); + I915_WRITE(PIPEASRC, dev_priv->savePIPEASRC); + I915_WRITE(DSPAADDR, dev_priv->saveDSPAADDR); + I915_WRITE(DSPASTRIDE, dev_priv->saveDSPASTRIDE); + if (IS_I965G(dev_priv)) { + I915_WRITE(DSPASURF, dev_priv->saveDSPASURF); + I915_WRITE(DSPATILEOFF, dev_priv->saveDSPATILEOFF); + } + + I915_WRITE(PIPEACONF, dev_priv->savePIPEACONF); + + i915_restore_palette(dev_priv, PIPE_A); + /* Enable the plane */ + I915_WRITE(DSPACNTR, dev_priv->saveDSPACNTR); + I915_WRITE(DSPAADDR, I915_READ(DSPAADDR)); + + /* Pipe & plane B info */ + if (dev_priv->saveDPLL_B & DPLL_VCO_ENABLE) { + I915_WRITE(DPLL_B, dev_priv->saveDPLL_B & + ~DPLL_VCO_ENABLE); + DRM_UDELAY(150); + } + I915_WRITE(fpb0_reg, dev_priv->saveFPB0); + I915_WRITE(fpb1_reg, dev_priv->saveFPB1); + /* Actually enable it */ + I915_WRITE(dpll_b_reg, dev_priv->saveDPLL_B); + DRM_UDELAY(150); + if (IS_I965G(dev_priv) && !IS_IRONLAKE(dev_priv)) + I915_WRITE(DPLL_B_MD, dev_priv->saveDPLL_B_MD); + DRM_UDELAY(150); + + /* Restore mode */ + I915_WRITE(HTOTAL_B, dev_priv->saveHTOTAL_B); + I915_WRITE(HBLANK_B, dev_priv->saveHBLANK_B); + I915_WRITE(HSYNC_B, dev_priv->saveHSYNC_B); + I915_WRITE(VTOTAL_B, dev_priv->saveVTOTAL_B); + I915_WRITE(VBLANK_B, dev_priv->saveVBLANK_B); + I915_WRITE(VSYNC_B, dev_priv->saveVSYNC_B); + if (IS_IRONLAKE(dev_priv)) { + I915_WRITE(PIPEB_DATA_M1, dev_priv->savePIPEB_DATA_M1); + I915_WRITE(PIPEB_DATA_N1, dev_priv->savePIPEB_DATA_N1); + I915_WRITE(PIPEB_LINK_M1, dev_priv->savePIPEB_LINK_M1); + I915_WRITE(PIPEB_LINK_N1, dev_priv->savePIPEB_LINK_N1); + + I915_WRITE(FDI_RXB_CTL, dev_priv->saveFDI_RXB_CTL); + I915_WRITE(FDI_TXB_CTL, dev_priv->saveFDI_TXB_CTL); + + I915_WRITE(PFB_CTL_1, dev_priv->savePFB_CTL_1); + I915_WRITE(PFB_WIN_SZ, dev_priv->savePFB_WIN_SZ); + I915_WRITE(PFB_WIN_POS, dev_priv->savePFB_WIN_POS); + + I915_WRITE(TRANSBCONF, dev_priv->saveTRANSBCONF); + I915_WRITE(TRANS_HTOTAL_B, dev_priv->saveTRANS_HTOTAL_B); + I915_WRITE(TRANS_HBLANK_B, dev_priv->saveTRANS_HBLANK_B); + I915_WRITE(TRANS_HSYNC_B, dev_priv->saveTRANS_HSYNC_B); + I915_WRITE(TRANS_VTOTAL_B, dev_priv->saveTRANS_VTOTAL_B); + I915_WRITE(TRANS_VBLANK_B, dev_priv->saveTRANS_VBLANK_B); + I915_WRITE(TRANS_VSYNC_B, dev_priv->saveTRANS_VSYNC_B); + } else { + I915_WRITE(BCLRPAT_B, dev_priv->saveBCLRPAT_B); + } + + /* Restore plane info */ + I915_WRITE(DSPBSIZE, dev_priv->saveDSPBSIZE); + I915_WRITE(DSPBPOS, dev_priv->saveDSPBPOS); + I915_WRITE(PIPEBSRC, dev_priv->savePIPEBSRC); + I915_WRITE(DSPBADDR, dev_priv->saveDSPBADDR); + I915_WRITE(DSPBSTRIDE, dev_priv->saveDSPBSTRIDE); + if (IS_I965G(dev_priv)) { + I915_WRITE(DSPBSURF, dev_priv->saveDSPBSURF); + I915_WRITE(DSPBTILEOFF, dev_priv->saveDSPBTILEOFF); + } + + I915_WRITE(PIPEBCONF, dev_priv->savePIPEBCONF); + + i915_restore_palette(dev_priv, PIPE_B); + /* Enable the plane */ + I915_WRITE(DSPBCNTR, dev_priv->saveDSPBCNTR); + I915_WRITE(DSPBADDR, I915_READ(DSPBADDR)); +} + +int +inteldrm_save_display(struct inteldrm_softc *dev_priv) +{ + /* Display arbitration control */ + dev_priv->saveDSPARB = I915_READ(DSPARB); + + /* This is only meaningful in non-KMS mode */ + /* Don't save them in KMS mode */ + i915_save_modeset_reg(dev_priv); + /* Cursor state */ + dev_priv->saveCURACNTR = I915_READ(CURACNTR); + dev_priv->saveCURAPOS = I915_READ(CURAPOS); + dev_priv->saveCURABASE = I915_READ(CURABASE); + dev_priv->saveCURBCNTR = I915_READ(CURBCNTR); + dev_priv->saveCURBPOS = I915_READ(CURBPOS); + dev_priv->saveCURBBASE = I915_READ(CURBBASE); + if (!IS_I9XX(dev_priv)) + dev_priv->saveCURSIZE = I915_READ(CURSIZE); + + /* CRT state */ + if (IS_IRONLAKE(dev_priv)) { + dev_priv->saveADPA = I915_READ(PCH_ADPA); + } else { + dev_priv->saveADPA = I915_READ(ADPA); + } + + /* LVDS state */ + if (IS_IRONLAKE(dev_priv)) { + dev_priv->savePP_CONTROL = I915_READ(PCH_PP_CONTROL); + dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_PCH_CTL1); + dev_priv->saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_PCH_CTL2); + dev_priv->saveBLC_CPU_PWM_CTL = I915_READ(BLC_PWM_CPU_CTL); + dev_priv->saveBLC_CPU_PWM_CTL2 = I915_READ(BLC_PWM_CPU_CTL2); + dev_priv->saveLVDS = I915_READ(PCH_LVDS); + } else { + dev_priv->savePP_CONTROL = I915_READ(PP_CONTROL); + dev_priv->savePFIT_PGM_RATIOS = I915_READ(PFIT_PGM_RATIOS); + dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL); + dev_priv->saveBLC_HIST_CTL = I915_READ(BLC_HIST_CTL); + if (IS_I965G(dev_priv)) + dev_priv->saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_CTL2); + if (IS_MOBILE(dev_priv) && !IS_I830(dev_priv)) + dev_priv->saveLVDS = I915_READ(LVDS); + } + if (!IS_I830(dev_priv) && !IS_845G(dev_priv)) + dev_priv->savePFIT_CONTROL = I915_READ(PFIT_CONTROL); + if (IS_IRONLAKE(dev_priv)) { + dev_priv->savePP_ON_DELAYS = I915_READ(PCH_PP_ON_DELAYS); + dev_priv->savePP_OFF_DELAYS = I915_READ(PCH_PP_OFF_DELAYS); + dev_priv->savePP_DIVISOR = I915_READ(PCH_PP_DIVISOR); + } else { + dev_priv->savePP_ON_DELAYS = I915_READ(PP_ON_DELAYS); + dev_priv->savePP_OFF_DELAYS = I915_READ(PP_OFF_DELAYS); + dev_priv->savePP_DIVISOR = I915_READ(PP_DIVISOR); + } + + /* FIXME: save TV & SDVO state */ + + /* FBC state XXX only if supported */ + if (IS_GM45(dev_priv)) { + dev_priv->saveDPFC_CB_BASE = I915_READ(DPFC_CB_BASE); + } else { + dev_priv->saveFBC_CFB_BASE = I915_READ(FBC_CFB_BASE); + dev_priv->saveFBC_LL_BASE = I915_READ(FBC_LL_BASE); + dev_priv->saveFBC_CONTROL2 = I915_READ(FBC_CONTROL2); + dev_priv->saveFBC_CONTROL = I915_READ(FBC_CONTROL); + } + + /* VGA state */ + dev_priv->saveVGA0 = I915_READ(VGA0); + dev_priv->saveVGA1 = I915_READ(VGA1); + dev_priv->saveVGA_PD = I915_READ(VGA_PD); + if (IS_IRONLAKE(dev_priv)) + dev_priv->saveVGACNTRL = I915_READ(CPU_VGACNTRL); + else + dev_priv->saveVGACNTRL = I915_READ(VGACNTRL); + + i915_save_vga(dev_priv); + + return 0; +} + +int +inteldrm_restore_display(struct inteldrm_softc *dev_priv) +{ + /* Display arbitration */ + I915_WRITE(DSPARB, dev_priv->saveDSPARB); + + /* This is only meaningful in non-KMS mode */ + /* Don't restore them in KMS mode */ + i915_restore_modeset_reg(dev_priv); + /* Cursor state */ + I915_WRITE(CURAPOS, dev_priv->saveCURAPOS); + I915_WRITE(CURACNTR, dev_priv->saveCURACNTR); + I915_WRITE(CURABASE, dev_priv->saveCURABASE); + I915_WRITE(CURBPOS, dev_priv->saveCURBPOS); + I915_WRITE(CURBCNTR, dev_priv->saveCURBCNTR); + I915_WRITE(CURBBASE, dev_priv->saveCURBBASE); + if (!IS_I9XX(dev_priv)) + I915_WRITE(CURSIZE, dev_priv->saveCURSIZE); + + /* CRT state */ + if (IS_IRONLAKE(dev_priv)) + I915_WRITE(PCH_ADPA, dev_priv->saveADPA); + else + I915_WRITE(ADPA, dev_priv->saveADPA); + + /* LVDS state */ + if (IS_I965G(dev_priv) && !IS_IRONLAKE(dev_priv)) + I915_WRITE(BLC_PWM_CTL2, dev_priv->saveBLC_PWM_CTL2); + + if (IS_IRONLAKE(dev_priv)) { + I915_WRITE(PCH_LVDS, dev_priv->saveLVDS); + } else if (IS_MOBILE(dev_priv) && !IS_I830(dev_priv)) + I915_WRITE(LVDS, dev_priv->saveLVDS); + + if (!IS_I830(dev_priv) && !IS_845G(dev_priv) && !IS_IRONLAKE(dev_priv)) + I915_WRITE(PFIT_CONTROL, dev_priv->savePFIT_CONTROL); + + if (IS_IRONLAKE(dev_priv)) { + I915_WRITE(BLC_PWM_PCH_CTL1, dev_priv->saveBLC_PWM_CTL); + I915_WRITE(BLC_PWM_PCH_CTL2, dev_priv->saveBLC_PWM_CTL2); + I915_WRITE(BLC_PWM_CPU_CTL, dev_priv->saveBLC_CPU_PWM_CTL); + I915_WRITE(BLC_PWM_CPU_CTL2, dev_priv->saveBLC_CPU_PWM_CTL2); + I915_WRITE(PCH_PP_ON_DELAYS, dev_priv->savePP_ON_DELAYS); + I915_WRITE(PCH_PP_OFF_DELAYS, dev_priv->savePP_OFF_DELAYS); + I915_WRITE(PCH_PP_DIVISOR, dev_priv->savePP_DIVISOR); + I915_WRITE(PCH_PP_CONTROL, dev_priv->savePP_CONTROL); + I915_WRITE(MCHBAR_RENDER_STANDBY, + dev_priv->saveMCHBAR_RENDER_STANDBY); + } else { + I915_WRITE(PFIT_PGM_RATIOS, dev_priv->savePFIT_PGM_RATIOS); + I915_WRITE(BLC_PWM_CTL, dev_priv->saveBLC_PWM_CTL); + I915_WRITE(BLC_HIST_CTL, dev_priv->saveBLC_HIST_CTL); + I915_WRITE(PP_ON_DELAYS, dev_priv->savePP_ON_DELAYS); + I915_WRITE(PP_OFF_DELAYS, dev_priv->savePP_OFF_DELAYS); + I915_WRITE(PP_DIVISOR, dev_priv->savePP_DIVISOR); + I915_WRITE(PP_CONTROL, dev_priv->savePP_CONTROL); + } + + /* FIXME: restore TV & SDVO state */ + + /* FBC info */ + if (IS_GM45(dev_priv)) { + I915_WRITE(DPFC_CB_BASE, dev_priv->saveDPFC_CB_BASE); + } else { + I915_WRITE(FBC_CFB_BASE, dev_priv->saveFBC_CFB_BASE); + I915_WRITE(FBC_LL_BASE, dev_priv->saveFBC_LL_BASE); + I915_WRITE(FBC_CONTROL2, dev_priv->saveFBC_CONTROL2); + I915_WRITE(FBC_CONTROL, dev_priv->saveFBC_CONTROL); + } + + /* VGA state */ + if (IS_IRONLAKE(dev_priv)) + I915_WRITE(CPU_VGACNTRL, dev_priv->saveVGACNTRL); + else + I915_WRITE(VGACNTRL, dev_priv->saveVGACNTRL); + I915_WRITE(VGA0, dev_priv->saveVGA0); + I915_WRITE(VGA1, dev_priv->saveVGA1); + I915_WRITE(VGA_PD, dev_priv->saveVGA_PD); + DRM_UDELAY(150); + + i915_restore_vga(dev_priv); + + return 0; +} + +int +inteldrm_save_state(struct inteldrm_softc *dev_priv) +{ + int i; + + dev_priv->saveLBB = pci_conf_read(dev_priv->pc, dev_priv->tag, LBB); + + /* Hardware status page */ + dev_priv->saveHWS = I915_READ(HWS_PGA); + + inteldrm_save_display(dev_priv); + + /* Interrupt state */ + if (IS_IRONLAKE(dev_priv)) { + dev_priv->saveDEIER = I915_READ(DEIER); + dev_priv->saveDEIMR = I915_READ(DEIMR); + dev_priv->saveGTIER = I915_READ(GTIER); + dev_priv->saveGTIMR = I915_READ(GTIMR); + dev_priv->saveFDI_RXA_IMR = I915_READ(FDI_RXA_IMR); + dev_priv->saveFDI_RXB_IMR = I915_READ(FDI_RXB_IMR); + dev_priv->saveMCHBAR_RENDER_STANDBY = + I915_READ(MCHBAR_RENDER_STANDBY); + } else { + dev_priv->saveIER = I915_READ(IER); + dev_priv->saveIMR = I915_READ(IMR); + } + + /* Clock gating state */ + if (IS_IRONLAKE(dev_priv)) { + dev_priv->saveDSPCLK_GATE_D = I915_READ(PCH_DSPCLK_GATE_D); + dev_priv->saveDSPCLK_GATE = I915_READ(ILK_DSPCLK_GATE); + } else if (IS_G4X(dev_priv)) { + dev_priv->saveRENCLK_GATE_D1 = I915_READ(RENCLK_GATE_D1); + dev_priv->saveRENCLK_GATE_D2 = I915_READ(RENCLK_GATE_D2); + dev_priv->saveRAMCLK_GATE_D = I915_READ(RAMCLK_GATE_D); + dev_priv->saveDSPCLK_GATE_D = I915_READ(DSPCLK_GATE_D); + } else if (IS_I965GM(dev_priv)) { + dev_priv->saveRENCLK_GATE_D1 = I915_READ(RENCLK_GATE_D1); + dev_priv->saveRENCLK_GATE_D2 = I915_READ(RENCLK_GATE_D2); + dev_priv->saveDSPCLK_GATE_D = I915_READ(DSPCLK_GATE_D); + dev_priv->saveRAMCLK_GATE_D = I915_READ(RAMCLK_GATE_D); + dev_priv->saveDEUC = I915_READ16(DEUC); + } else if (IS_I965G(dev_priv)) { + dev_priv->saveRENCLK_GATE_D1 = I915_READ(RENCLK_GATE_D1); + dev_priv->saveRENCLK_GATE_D2 = I915_READ(RENCLK_GATE_D2); + } else if (IS_I9XX(dev_priv)) { + dev_priv->saveD_STATE = I915_READ(D_STATE); + } else if (IS_I85X(dev_priv) || IS_I865G(dev_priv)) { + dev_priv->saveRENCLK_GATE_D1 = I915_READ(RENCLK_GATE_D1); + } else if (IS_I830(dev_priv)) { + dev_priv->saveDSPCLK_GATE_D = I915_READ(DSPCLK_GATE_D); + } + + /* Cache mode state */ + dev_priv->saveCACHE_MODE_0 = I915_READ(CACHE_MODE_0); + + /* Memory Arbitration state */ + dev_priv->saveMI_ARB_STATE = I915_READ(MI_ARB_STATE); + + /* Scratch space */ + for (i = 0; i < 16; i++) { + dev_priv->saveSWF0[i] = I915_READ(SWF00 + (i << 2)); + dev_priv->saveSWF1[i] = I915_READ(SWF10 + (i << 2)); + } + for (i = 0; i < 3; i++) + dev_priv->saveSWF2[i] = I915_READ(SWF30 + (i << 2)); + + /* Fences */ + if (IS_I965G(dev_priv)) { + for (i = 0; i < 16; i++) + dev_priv->saveFENCE[i] = I915_READ64(FENCE_REG_965_0 + + (i * 8)); + } else { + for (i = 0; i < 8; i++) + dev_priv->saveFENCE[i] = I915_READ(FENCE_REG_830_0 + (i * 4)); + + if (IS_I945G(dev_priv) || IS_I945GM(dev_priv) || + IS_G33(dev_priv)) + for (i = 0; i < 8; i++) + dev_priv->saveFENCE[i+8] = I915_READ(FENCE_REG_945_8 + (i * 4)); + } + + return 0; +} + +int +inteldrm_restore_state(struct inteldrm_softc *dev_priv) +{ + int i; + + pci_conf_write(dev_priv->pc, dev_priv->tag, LBB, dev_priv->saveLBB); + + /* Hardware status page */ + I915_WRITE(HWS_PGA, dev_priv->saveHWS); + + /* Fences */ + if (IS_I965G(dev_priv)) { + for (i = 0; i < 16; i++) + I915_WRITE64(FENCE_REG_965_0 + (i * 8), dev_priv->saveFENCE[i]); + } else { + for (i = 0; i < 8; i++) + I915_WRITE(FENCE_REG_830_0 + (i * 4), dev_priv->saveFENCE[i]); + if (IS_I945G(dev_priv) || IS_I945GM(dev_priv) || + IS_G33(dev_priv)) + for (i = 0; i < 8; i++) + I915_WRITE(FENCE_REG_945_8 + (i * 4), dev_priv->saveFENCE[i+8]); + } + + inteldrm_restore_display(dev_priv); + + /* Interrupt state */ + if (IS_IRONLAKE(dev_priv)) { + I915_WRITE(DEIER, dev_priv->saveDEIER); + I915_WRITE(DEIMR, dev_priv->saveDEIMR); + I915_WRITE(GTIER, dev_priv->saveGTIER); + I915_WRITE(GTIMR, dev_priv->saveGTIMR); + I915_WRITE(FDI_RXA_IMR, dev_priv->saveFDI_RXA_IMR); + I915_WRITE(FDI_RXB_IMR, dev_priv->saveFDI_RXB_IMR); + } else { + I915_WRITE (IER, dev_priv->saveIER); + I915_WRITE (IMR, dev_priv->saveIMR); + } + + /* Clock gating state */ + if (IS_IRONLAKE(dev_priv)) { + I915_WRITE(PCH_DSPCLK_GATE_D, dev_priv->saveDSPCLK_GATE_D); + I915_WRITE(ILK_DSPCLK_GATE, dev_priv->saveDSPCLK_GATE); + } if (IS_G4X(dev_priv)) { + I915_WRITE(RENCLK_GATE_D1, dev_priv->saveRENCLK_GATE_D1); + I915_WRITE(RENCLK_GATE_D2, dev_priv->saveRENCLK_GATE_D2); + I915_WRITE(RAMCLK_GATE_D, dev_priv->saveRAMCLK_GATE_D); + I915_WRITE(DSPCLK_GATE_D, dev_priv->saveDSPCLK_GATE_D); + } else if (IS_I965GM(dev_priv)) { + I915_WRITE(RENCLK_GATE_D1, dev_priv->saveRENCLK_GATE_D1); + I915_WRITE(RENCLK_GATE_D2, dev_priv->saveRENCLK_GATE_D2); + I915_WRITE(DSPCLK_GATE_D, dev_priv->saveDSPCLK_GATE_D); + I915_WRITE(RAMCLK_GATE_D, dev_priv->saveRAMCLK_GATE_D); + I915_WRITE16(DEUC, dev_priv->saveDEUC); + } else if (IS_I965G(dev_priv)) { + I915_WRITE(RENCLK_GATE_D1, dev_priv->saveRENCLK_GATE_D1); + I915_WRITE(RENCLK_GATE_D2, dev_priv->saveRENCLK_GATE_D2); + } else if (IS_I9XX(dev_priv)) { + I915_WRITE(D_STATE, dev_priv->saveD_STATE); + } else if (IS_I85X(dev_priv) || IS_I865G(dev_priv)) { + I915_WRITE(RENCLK_GATE_D1, dev_priv->saveRENCLK_GATE_D1); + } else if (IS_I830(dev_priv)) { + I915_WRITE(DSPCLK_GATE_D, dev_priv->saveDSPCLK_GATE_D); + } + + /* Cache mode state */ + I915_WRITE (CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000); + + /* Memory arbitration state */ + I915_WRITE (MI_ARB_STATE, dev_priv->saveMI_ARB_STATE | 0xffff0000); + + for (i = 0; i < 16; i++) { + I915_WRITE(SWF00 + (i << 2), dev_priv->saveSWF0[i]); + I915_WRITE(SWF10 + (i << 2), dev_priv->saveSWF1[i]); + } + for (i = 0; i < 3; i++) + I915_WRITE(SWF30 + (i << 2), dev_priv->saveSWF2[i]); + + return 0; +} + +/* + * Reset the chip after a hang (965 only) + * + * The procedure that should be followed is relatively simple: + * - reset the chip using the reset reg + * - re-init context state + * - re-init Hardware status page + * - re-init ringbuffer + * - re-init interrupt state + * - re-init display + */ +void +inteldrm_965_reset(struct inteldrm_softc *dev_priv, u_int8_t flags) +{ + pcireg_t reg; + int i = 0; + + if (flags == GDRST_FULL) + inteldrm_save_display(dev_priv); + + reg = pci_conf_read(dev_priv->pc, dev_priv->tag, GDRST); + /* + * Set the domains we want to reset, then bit 0 (reset itself). + * then we wait for the hardware to clear it. + */ + pci_conf_write(dev_priv->pc, dev_priv->tag, GDRST, + reg | (u_int32_t)flags | ((flags == GDRST_FULL) ? 0x1 : 0x0)); + delay(50); + /* don't clobber the rest of the register */ + pci_conf_write(dev_priv->pc, dev_priv->tag, GDRST, reg & 0xfe); + + /* if this fails we're pretty much fucked, but don't loop forever */ + do { + delay(100); + reg = pci_conf_read(dev_priv->pc, dev_priv->tag, GDRST); + } while ((reg & 0x1) && ++i < 10); + + if (reg & 0x1) + printf("bit 0 not cleared .. "); + + /* put everything back together again */ + + /* + * GTT is already up (we didn't do a pci-level reset, thank god. + * + * We don't have to restore the contexts (we don't use them yet). + * So, if X is running we need to put the ringbuffer back first. + */ + if (dev_priv->mm.suspended == 0) { + struct drm_device *dev = (struct drm_device *)dev_priv->drmdev; + if (inteldrm_start_ring(dev_priv) != 0) + panic("can't restart ring, we're fucked"); + + /* put the hardware status page back */ + if (I915_NEED_GFX_HWS(dev_priv)) + I915_WRITE(HWS_PGA, ((struct inteldrm_obj *) + dev_priv->hws_obj)->gtt_offset); + else + I915_WRITE(HWS_PGA, + dev_priv->hws_dmamem->map->dm_segs[0].ds_addr); + I915_READ(HWS_PGA); /* posting read */ + + /* so we remove the handler and can put it back in */ + DRM_UNLOCK(); + drm_irq_uninstall(dev); + drm_irq_install(dev); + DRM_LOCK(); + } else + printf("not restarting ring...\n"); + + + if (flags == GDRST_FULL) + inteldrm_restore_display(dev_priv); +} + +/* + * Debug code from here. + */ +#ifdef WATCH_INACTIVE +void +inteldrm_verify_inactive(struct inteldrm_softc *dev_priv, char *file, + int line) +{ + struct drm_obj *obj; + struct inteldrm_obj *obj_priv; + + TAILQ_FOREACH(obj_priv, &dev_priv->mm.inactive_list, list) { + obj = (struct drm_obj *)obj_priv; + if (obj_priv->pin_count || inteldrm_is_active(obj_priv) || + obj->write_domain & I915_GEM_GPU_DOMAINS) + DRM_ERROR("inactive %p (p $d a $d w $x) %s:%d\n", + obj, obj_priv->pin_count, + inteldrm_is_active(obj_priv), + obj->write_domain, file, line); + } +} +#endif /* WATCH_INACTIVE */ + +#if (INTELDRM_DEBUG > 1) + +static const char *get_pin_flag(struct inteldrm_obj *obj_priv) +{ + if (obj_priv->pin_count > 0) + return "p"; + else + return " "; +} + +static const char *get_tiling_flag(struct inteldrm_obj *obj_priv) +{ + switch (obj_priv->tiling_mode) { + default: + case I915_TILING_NONE: return " "; + case I915_TILING_X: return "X"; + case I915_TILING_Y: return "Y"; + } +} + +void +i915_gem_seqno_info(int kdev) +{ + struct drm_device *dev = drm_get_device_from_kdev(kdev); + struct inteldrm_softc *dev_priv = dev->dev_private; + + if (dev_priv->hw_status_page != NULL) { + printf("Current sequence: %d\n", i915_get_gem_seqno(dev_priv)); + } else { + printf("Current sequence: hws uninitialized\n"); + } +} + +void +i915_interrupt_info(int kdev) +{ + struct drm_device *dev = drm_get_device_from_kdev(kdev); + struct inteldrm_softc *dev_priv = dev->dev_private; + + printf("Interrupt enable: %08x\n", + I915_READ(IER)); + printf("Interrupt identity: %08x\n", + I915_READ(IIR)); + printf("Interrupt mask: %08x\n", + I915_READ(IMR)); + printf("Pipe A stat: %08x\n", + I915_READ(PIPEASTAT)); + printf("Pipe B stat: %08x\n", + I915_READ(PIPEBSTAT)); + printf("Interrupts received: 0\n"); + if (dev_priv->hw_status_page != NULL) { + printf("Current sequence: %d\n", + i915_get_gem_seqno(dev_priv)); + } else { + printf("Current sequence: hws uninitialized\n"); + } +} + +void +i915_gem_fence_regs_info(int kdev) +{ + struct drm_device *dev = drm_get_device_from_kdev(kdev); + struct inteldrm_softc *dev_priv = dev->dev_private; + int i; + + printf("Reserved fences = %d\n", dev_priv->fence_reg_start); + printf("Total fences = %d\n", dev_priv->num_fence_regs); + for (i = 0; i < dev_priv->num_fence_regs; i++) { + struct drm_obj *obj = dev_priv->fence_regs[i].obj; + + if (obj == NULL) { + printf("Fenced object[%2d] = unused\n", i); + } else { + struct inteldrm_obj *obj_priv; + + obj_priv = (struct inteldrm_obj *)obj; + printf("Fenced object[%2d] = %p: %s " + "%08jx %08zx %08x %s %08x %08x %d", + i, obj, get_pin_flag(obj_priv), + (uintmax_t)obj_priv->gtt_offset, + obj->size, obj_priv->stride, + get_tiling_flag(obj_priv), + obj->read_domains, obj->write_domain, + obj_priv->last_rendering_seqno); + if (obj->name) + printf(" (name: %d)", obj->name); + printf("\n"); + } + } +} + +void +i915_hws_info(int kdev) +{ + struct drm_device *dev = drm_get_device_from_kdev(kdev); + struct inteldrm_softc *dev_priv = dev->dev_private; + int i; + volatile u32 *hws; + + hws = (volatile u32 *)dev_priv->hw_status_page; + if (hws == NULL) + return; + + for (i = 0; i < 4096 / sizeof(u32) / 4; i += 4) { + printf("0x%08x: 0x%08x 0x%08x 0x%08x 0x%08x\n", + i * 4, + hws[i], hws[i + 1], hws[i + 2], hws[i + 3]); + } +} + +static void +i915_dump_pages(bus_space_tag_t bst, bus_space_handle_t bsh, + bus_size_t size) +{ + bus_addr_t offset = 0; + int i = 0; + + /* + * this is a bit odd so i don't have to play with the intel + * tools too much. + */ + for (offset = 0; offset < size; offset += 4, i += 4) { + if (i == PAGE_SIZE) + i = 0; + printf("%08x : %08x\n", i, bus_space_read_4(bst, bsh, + offset)); + } +} + +void +i915_batchbuffer_info(int kdev) +{ + struct drm_device *dev = drm_get_device_from_kdev(kdev); + struct inteldrm_softc *dev_priv = dev->dev_private; + struct drm_obj *obj; + struct inteldrm_obj *obj_priv; + bus_space_handle_t bsh; + int ret; + + TAILQ_FOREACH(obj_priv, &dev_priv->mm.active_list, list) { + obj = &obj_priv->obj; + if (obj->read_domains & I915_GEM_DOMAIN_COMMAND) { + if ((ret = agp_map_subregion(dev_priv->agph, + obj_priv->gtt_offset, obj->size, &bsh)) != 0) { + DRM_ERROR("Failed to map pages: %d\n", ret); + return; + } + printf("--- gtt_offset = 0x%08jx\n", + (uintmax_t)obj_priv->gtt_offset); + i915_dump_pages(dev_priv->bst, bsh, obj->size); + agp_unmap_subregion(dev_priv->agph, dev_priv->ring.bsh, + obj->size); + } + } +} + +void +i915_ringbuffer_data(int kdev) +{ + struct drm_device *dev = drm_get_device_from_kdev(kdev); + struct inteldrm_softc *dev_priv = dev->dev_private; + bus_size_t off; + + if (!dev_priv->ring.ring_obj) { + printf("No ringbuffer setup\n"); + return; + } + + for (off = 0; off < dev_priv->ring.size; off += 4) + printf("%08zx : %08x\n", off, bus_space_read_4(dev_priv->bst, + dev_priv->ring.bsh, off)); +} + +void +i915_ringbuffer_info(int kdev) +{ + struct drm_device *dev = drm_get_device_from_kdev(kdev); + struct inteldrm_softc *dev_priv = dev->dev_private; + u_int32_t head, tail; + + head = I915_READ(PRB0_HEAD) & HEAD_ADDR; + tail = I915_READ(PRB0_TAIL) & TAIL_ADDR; + + printf("RingHead : %08x\n", head); + printf("RingTail : %08x\n", tail); + printf("RingMask : %08zx\n", dev_priv->ring.size - 1); + printf("RingSize : %08zx\n", dev_priv->ring.size); + printf("Acthd : %08x\n", I915_READ(IS_I965G(dev_priv) ? + ACTHD_I965 : ACTHD)); +} + +#endif diff -Naurp old/src/sys/dev/pci/drm/i915_drv.h new/src/sys/dev/pci/drm/i915_drv.h --- old/src/sys/dev/pci/drm/i915_drv.h 1970-01-01 01:00:00.000000000 +0100 +++ new/src/sys/dev/pci/drm/i915_drv.h 2011-02-08 11:14:00.000000000 +0100 @@ -0,0 +1,3261 @@ +/* i915_drv.h -- Private header for the I915 driver -*- linux-c -*- + */ +/* + * + * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. + * 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, sub license, 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 NON-INFRINGEMENT. + * IN NO EVENT SHALL TUNGSTEN GRAPHICS 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. + * + */ + +#ifndef _I915_DRV_H_ +#define _I915_DRV_H_ + +/* General customization: + */ + +#define DRIVER_AUTHOR "Tungsten Graphics, Inc." + +#define DRIVER_NAME "i915" +#define DRIVER_DESC "Intel Graphics" +#define DRIVER_DATE "20080730" + +enum pipe { + PIPE_A = 0, + PIPE_B, +}; + +/* Interface history: + * + * 1.1: Original. + * 1.2: Add Power Management + * 1.3: Add vblank support + * 1.4: Fix cmdbuffer path, add heap destroy + * 1.5: Add vblank pipe configuration + * 1.6: - New ioctl for scheduling buffer swaps on vertical blank + * - Support vertical blank on secondary display pipe + */ +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 6 +#define DRIVER_PATCHLEVEL 0 + +struct inteldrm_ring { + struct drm_obj *ring_obj; + bus_space_handle_t bsh; + bus_size_t size; + u_int32_t head; + int32_t space; + u_int32_t tail; + u_int32_t woffset; +}; + +#define I915_FENCE_REG_NONE -1 + +struct inteldrm_fence { + TAILQ_ENTRY(inteldrm_fence) list; + struct drm_obj *obj; + u_int32_t last_rendering_seqno; +}; + +#if defined(__NetBSD__) +struct workq; +#endif /* defined(__NetBSD__) */ + +/* + * lock ordering: + * exec lock, + * request lock + * list lock. + * + * XXX fence lock ,object lock + */ +struct inteldrm_softc { + struct device dev; + struct device *drmdev; + bus_dma_tag_t agpdmat; /* tag from intagp for GEM */ + bus_dma_tag_t dmat; + bus_space_tag_t bst; + struct agp_map *agph; + + u_long flags; + u_int16_t pci_device; + + pci_chipset_tag_t pc; + pcitag_t tag; + pci_intr_handle_t ih; + void *irqh; + +#if !defined(__NetBSD__) + struct vga_pci_bar *regs; +#else /* !defined(__NetBSD__) */ + struct { + bus_space_tag_t bst; + bus_space_handle_t bsh; + } regs[1]; +#endif /* !defined(__NetBSD__) */ + + union flush { + struct { + bus_space_tag_t bst; + bus_space_handle_t bsh; + bool valid; + } i9xx; + struct { + bus_dma_segment_t seg; + caddr_t kva; + } i8xx; + } ifp; + struct inteldrm_ring ring; + struct workq *workq; +#if !defined(__NetBSD__) /* XXX [GS] Cf. comment in inteldrm_attach(). */ + struct vm_page *pgs; +#endif /* !defined(__NetBSD__) */ + union hws { + struct drm_obj *obj; + struct drm_dmamem *dmamem; + } hws; +#define hws_obj hws.obj +#define hws_dmamem hws.dmamem + void *hw_status_page; + size_t max_gem_obj_size; /* XXX */ + + /* Protects user_irq_refcount and irq_mask reg */ +#if !defined(__NetBSD__) + struct mutex user_irq_lock; +#else /* !defined(__NetBSD__) */ + kmutex_t user_irq_lock; + kcondvar_t condvar; +#endif /* !defined(__NetBSD__) */ + /* Refcount for user irq, only enabled when needed */ + int user_irq_refcount; + /* Cached value of IMR to avoid reads in updating the bitfield */ + u_int32_t irq_mask_reg; + u_int32_t pipestat[2]; + /* these two ironlake only, we should union this with pipestat XXX */ + u_int32_t gt_irq_mask_reg; + u_int32_t pch_irq_mask_reg; + +#if !defined(__NetBSD__) + struct mutex fence_lock; +#else /* !defined(__NetBSD__) */ + kmutex_t fence_lock; +#endif /* !defined(__NetBSD__) */ + struct inteldrm_fence fence_regs[16]; /* 965 */ + int fence_reg_start; /* 4 by default */ + int num_fence_regs; /* 8 pre-965, 16 post */ + +#define INTELDRM_QUIET 0x01 /* suspend close, get off the hardware */ +#define INTELDRM_WEDGED 0x02 /* chipset hung pending reset */ +#define INTELDRM_SUSPENDED 0x04 /* in vt switch, no commands */ + int sc_flags; /* quiet, suspended, hung */ + /* number of ioctls + faults in flight */ + int entries; + + /* protects inactive, flushing, active and exec locks */ +#if !defined(__NetBSD__) + struct mutex list_lock; +#else /* !defined(__NetBSD__) */ + kmutex_t list_lock; +#endif /* !defined(__NetBSD__) */ + + /* protects access to request_list */ +#if !defined(__NetBSD__) + struct mutex request_lock; +#else /* !defined(__NetBSD__) */ + kmutex_t request_lock; +#endif /* !defined(__NetBSD__) */ + + /* Register state */ + u8 saveLBB; + u32 saveDSPACNTR; + u32 saveDSPBCNTR; + u32 saveDSPARB; + u32 saveHWS; + u32 savePIPEACONF; + u32 savePIPEBCONF; + u32 savePIPEASRC; + u32 savePIPEBSRC; + u32 saveFPA0; + u32 saveFPA1; + u32 saveDPLL_A; + u32 saveDPLL_A_MD; + u32 saveHTOTAL_A; + u32 saveHBLANK_A; + u32 saveHSYNC_A; + u32 saveVTOTAL_A; + u32 saveVBLANK_A; + u32 saveVSYNC_A; + u32 saveBCLRPAT_A; + u32 saveTRANSACONF; + u32 saveTRANS_HTOTAL_A; + u32 saveTRANS_HBLANK_A; + u32 saveTRANS_HSYNC_A; + u32 saveTRANS_VTOTAL_A; + u32 saveTRANS_VBLANK_A; + u32 saveTRANS_VSYNC_A; + u32 savePIPEASTAT; + u32 saveDSPASTRIDE; + u32 saveDSPASIZE; + u32 saveDSPAPOS; + u32 saveDSPAADDR; + u32 saveDSPASURF; + u32 saveDSPATILEOFF; + u32 savePFIT_PGM_RATIOS; + u32 saveBLC_HIST_CTL; + u32 saveBLC_PWM_CTL; + u32 saveBLC_PWM_CTL2; + u32 saveBLC_CPU_PWM_CTL; + u32 saveBLC_CPU_PWM_CTL2; + u32 saveFPB0; + u32 saveFPB1; + u32 saveDPLL_B; + u32 saveDPLL_B_MD; + u32 saveHTOTAL_B; + u32 saveHBLANK_B; + u32 saveHSYNC_B; + u32 saveVTOTAL_B; + u32 saveVBLANK_B; + u32 saveVSYNC_B; + u32 saveBCLRPAT_B; + u32 saveTRANSBCONF; + u32 saveTRANS_HTOTAL_B; + u32 saveTRANS_HBLANK_B; + u32 saveTRANS_HSYNC_B; + u32 saveTRANS_VTOTAL_B; + u32 saveTRANS_VBLANK_B; + u32 saveTRANS_VSYNC_B; + u32 savePIPEBSTAT; + u32 saveDSPBSTRIDE; + u32 saveDSPBSIZE; + u32 saveDSPBPOS; + u32 saveDSPBADDR; + u32 saveDSPBSURF; + u32 saveDSPBTILEOFF; + u32 saveVGA0; + u32 saveVGA1; + u32 saveVGA_PD; + u32 saveVGACNTRL; + u32 saveADPA; + u32 saveLVDS; + u32 savePP_ON_DELAYS; + u32 savePP_OFF_DELAYS; + u32 saveDVOA; + u32 saveDVOB; + u32 saveDVOC; + u32 savePP_ON; + u32 savePP_OFF; + u32 savePP_CONTROL; + u32 savePP_DIVISOR; + u32 savePFIT_CONTROL; + u32 save_palette_a[256]; + u32 save_palette_b[256]; + u32 saveDPFC_CB_BASE; + u32 saveFBC_CFB_BASE; + u32 saveFBC_LL_BASE; + u32 saveFBC_CONTROL; + u32 saveFBC_CONTROL2; + u32 saveIER; + u32 saveIIR; + u32 saveIMR; + u32 saveDEIER; + u32 saveDEIMR; + u32 saveGTIER; + u32 saveGTIMR; + u32 saveFDI_RXA_IMR; + u32 saveFDI_RXB_IMR; + u32 saveCACHE_MODE_0; + u32 saveD_STATE; + u32 saveDSPCLK_GATE_D; + u32 saveDSPCLK_GATE; + u32 saveRENCLK_GATE_D1; + u32 saveRENCLK_GATE_D2; + u32 saveRAMCLK_GATE_D; + u32 saveDEUC; + u32 saveMI_ARB_STATE; + u32 saveSWF0[16]; + u32 saveSWF1[16]; + u32 saveSWF2[3]; + u8 saveMSR; + u8 saveSR[8]; + u8 saveGR[25]; + u8 saveAR_INDEX; + u8 saveAR[21]; + u8 saveDACMASK; + u8 saveCR[37]; + uint64_t saveFENCE[16]; + u32 saveCURACNTR; + u32 saveCURAPOS; + u32 saveCURABASE; + u32 saveCURBCNTR; + u32 saveCURBPOS; + u32 saveCURBBASE; + u32 saveCURSIZE; + u32 saveDP_B; + u32 saveDP_C; + u32 saveDP_D; + u32 savePIPEA_GMCH_DATA_M; + u32 savePIPEB_GMCH_DATA_M; + u32 savePIPEA_GMCH_DATA_N; + u32 savePIPEB_GMCH_DATA_N; + u32 savePIPEA_DP_LINK_M; + u32 savePIPEB_DP_LINK_M; + u32 savePIPEA_DP_LINK_N; + u32 savePIPEB_DP_LINK_N; + u32 saveFDI_RXA_CTL; + u32 saveFDI_TXA_CTL; + u32 saveFDI_RXB_CTL; + u32 saveFDI_TXB_CTL; + u32 savePFA_CTL_1; + u32 savePFB_CTL_1; + u32 savePFA_WIN_SZ; + u32 savePFB_WIN_SZ; + u32 savePFA_WIN_POS; + u32 savePFB_WIN_POS; + u32 savePCH_DREF_CONTROL; + u32 saveDISP_ARB_CTL; + u32 savePIPEA_DATA_M1; + u32 savePIPEA_DATA_N1; + u32 savePIPEA_LINK_M1; + u32 savePIPEA_LINK_N1; + u32 savePIPEB_DATA_M1; + u32 savePIPEB_DATA_N1; + u32 savePIPEB_LINK_M1; + u32 savePIPEB_LINK_N1; + u32 saveMCHBAR_RENDER_STANDBY; + + struct { + /** + * List of objects currently involved in rendering from the + * ringbuffer. + * + * Includes buffers having the contents of their GPU caches + * flushed, not necessarily primitives. last_rendering_seqno + * represents when the rendering involved will be completed. + * + * A reference is held on the buffer while on this list. + */ + TAILQ_HEAD(i915_gem_list, inteldrm_obj) active_list; + + /** + * List of objects which are not in the ringbuffer but which + * still have a write_domain which needs to be flushed before + * unbinding. + * + * last_rendering_seqno is 0 while an object is in this list + * + * A reference is held on the buffer while on this list. + */ + struct i915_gem_list flushing_list; + + /* + * list of objects currently pending a GPU write flush. + * + * All elements on this list will either be on the active + * or flushing list, last rendiering_seqno differentiates the + * two. + */ + struct i915_gem_list gpu_write_list; + /** + * LRU list of objects which are not in the ringbuffer and + * are ready to unbind, but are still in the GTT. + * + * last_rendering_seqno is 0 while an object is in this list + * + * A reference is not held on the buffer while on this list, + * as merely being GTT-bound shouldn't prevent its being + * freed, and we'll pull it off the list in the free path. + */ + struct i915_gem_list inactive_list; + + /* Fence LRU */ + TAILQ_HEAD(i915_fence, inteldrm_fence) fence_list; + + /** + * List of breadcrumbs associated with GPU requests currently + * outstanding. + */ + TAILQ_HEAD(i915_request , inteldrm_request) request_list; + + /** + * We leave the user IRQ off as much as possible, + * but this means that requests will finish and never + * be retired once the system goes idle. Set a timer to + * fire periodically while the ring is running. When it + * fires, go retire requests in a workq. + */ +#if !defined(__NetBSD__) + struct timeout retire_timer; + struct timeout hang_timer; +#else /* !defined(__NetBSD__) */ + callout_t retire_timer; + callout_t hang_timer; +#endif /* !defined(__NetBSD__) */ + /* for hangcheck */ + int hang_cnt; + u_int32_t last_acthd; + u_int32_t last_instdone; + u_int32_t last_instdone1; + + uint32_t next_gem_seqno; + + /** + * Flag if the X Server, and thus DRM, is not currently in + * control of the device. + * + * This is set between LeaveVT and EnterVT. It needs to be + * replaced with a semaphore. It also needs to be + * transitioned away from for kernel modesetting. + */ + int suspended; + + /** + * Flag if the hardware appears to be wedged. + * + * This is set when attempts to idle the device timeout. + * It prevents command submission from occuring and makes + * every pending request fail + */ + int wedged; + + /** Bit 6 swizzling required for X tiling */ + uint32_t bit_6_swizzle_x; + /** Bit 6 swizzling required for Y tiling */ + uint32_t bit_6_swizzle_y; + } mm; +}; + +struct inteldrm_file { + struct drm_file file_priv; + struct { + } mm; +}; + +/* chip type flags */ +#define CHIP_I830 0x00001 +#define CHIP_I845G 0x00002 +#define CHIP_I85X 0x00004 +#define CHIP_I865G 0x00008 +#define CHIP_I9XX 0x00010 +#define CHIP_I915G 0x00020 +#define CHIP_I915GM 0x00040 +#define CHIP_I945G 0x00080 +#define CHIP_I945GM 0x00100 +#define CHIP_I965 0x00200 +#define CHIP_I965GM 0x00400 +#define CHIP_G33 0x00800 +#define CHIP_GM45 0x01000 +#define CHIP_G4X 0x02000 +#define CHIP_M 0x04000 +#define CHIP_HWS 0x08000 +#define CHIP_GEN2 0x10000 +#define CHIP_GEN3 0x20000 +#define CHIP_GEN4 0x40000 +#define CHIP_GEN6 0x80000 +#define CHIP_PINEVIEW 0x100000 +#define CHIP_IRONLAKE 0x200000 +#define CHIP_IRONLAKE_D 0x400000 +#define CHIP_IRONLAKE_M 0x800000 + +/* flags we use in drm_obj's do_flags */ +#define I915_ACTIVE 0x0010 /* being used by the gpu. */ +#define I915_IN_EXEC 0x0020 /* being processed in execbuffer */ +#define I915_USER_PINNED 0x0040 /* BO has been pinned from userland */ +#define I915_GPU_WRITE 0x0080 /* BO has been not flushed */ +#define I915_DONTNEED 0x0100 /* BO backing pages purgable */ +#define I915_PURGED 0x0200 /* BO backing pages purged */ +#define I915_DIRTY 0x0400 /* BO written to since last bound */ +#define I915_EXEC_NEEDS_FENCE 0x0800 /* being processed but will need fence*/ +#define I915_FENCED_EXEC 0x1000 /* Most recent exec needs fence */ +#define I915_FENCE_INVALID 0x2000 /* fence has been lazily invalidated */ + +/** driver private structure attached to each drm_gem_object */ +struct inteldrm_obj { + struct drm_obj obj; + + /** This object's place on the active/flushing/inactive lists */ + TAILQ_ENTRY(inteldrm_obj) list; + TAILQ_ENTRY(inteldrm_obj) write_list; + struct i915_gem_list *current_list; + /* GTT binding. */ + bus_dmamap_t dmamap; + bus_dma_segment_t *dma_segs; + /* Current offset of the object in GTT space. */ + bus_addr_t gtt_offset; + u_int32_t *bit_17; + /* extra flags to bus_dma */ + int dma_flags; + /* Fence register for this object. needed for tiling. */ + int fence_reg; + /** refcount for times pinned this object in GTT space */ + int pin_count; + /* number of times pinned by pin ioctl. */ + u_int user_pin_count; + + /** Breadcrumb of last rendering to the buffer. */ + u_int32_t last_rendering_seqno; + u_int32_t last_write_seqno; + /** Current tiling mode for the object. */ + u_int32_t tiling_mode; + u_int32_t stride; +}; + +/** + * Request queue structure. + * + * The request queue allows us to note sequence numbers that have been emitted + * and may be associated with active buffers to be retired. + * + * By keeping this list, we can avoid having to do questionable + * sequence-number comparisons on buffer last_rendering_seqnos, and associate + * an emission time with seqnos for tracking how far ahead of the GPU we are. + */ +struct inteldrm_request { + TAILQ_ENTRY(inteldrm_request) list; + /** GEM sequence number associated with this request. */ + uint32_t seqno; +}; + +u_int32_t inteldrm_read_hws(struct inteldrm_softc *, int); +int inteldrm_wait_ring(struct inteldrm_softc *dev, int n); +void inteldrm_begin_ring(struct inteldrm_softc *, int); +void inteldrm_out_ring(struct inteldrm_softc *, u_int32_t); +void inteldrm_advance_ring(struct inteldrm_softc *); +void inteldrm_update_ring(struct inteldrm_softc *); +int inteldrm_pipe_enabled(struct inteldrm_softc *, int); +int i915_init_phys_hws(struct inteldrm_softc *, bus_dma_tag_t); + +/* i915_irq.c */ + +extern int i915_driver_irq_install(struct drm_device * dev); +extern void i915_driver_irq_uninstall(struct drm_device * dev); +extern int i915_enable_vblank(struct drm_device *dev, int crtc); +extern void i915_disable_vblank(struct drm_device *dev, int crtc); +extern u32 i915_get_vblank_counter(struct drm_device *dev, int crtc); +extern void i915_user_irq_get(struct inteldrm_softc *); +extern void i915_user_irq_put(struct inteldrm_softc *); + +/* XXX need bus_space_write_8, this evaluated arguments twice */ +static __inline void +write64(struct inteldrm_softc *dev_priv, bus_size_t off, u_int64_t reg) +{ + bus_space_write_4(dev_priv->regs->bst, dev_priv->regs->bsh, + off, (u_int32_t)reg); + bus_space_write_4(dev_priv->regs->bst, dev_priv->regs->bsh, + off + 4, upper_32_bits(reg)); +} + +static __inline u_int64_t +read64(struct inteldrm_softc *dev_priv, bus_size_t off) +{ + u_int32_t low, high; + + low = bus_space_read_4(dev_priv->regs->bst, + dev_priv->regs->bsh, off); + high = bus_space_read_4(dev_priv->regs->bst, + dev_priv->regs->bsh, off + 4); + + return ((u_int64_t)low | ((u_int64_t)high << 32)); +} + +#define I915_READ64(off) read64(dev_priv, off) + +#define I915_WRITE64(off, reg) write64(dev_priv, off, reg) + +#define I915_READ(reg) bus_space_read_4(dev_priv->regs->bst, \ + dev_priv->regs->bsh, (reg)) +#define I915_WRITE(reg,val) bus_space_write_4(dev_priv->regs->bst, \ + dev_priv->regs->bsh, (reg), (val)) +#define I915_READ16(reg) bus_space_read_2(dev_priv->regs->bst, \ + dev_priv->regs->bsh, (reg)) +#define I915_WRITE16(reg,val) bus_space_write_2(dev_priv->regs->bst, \ + dev_priv->regs->bsh, (reg), (val)) +#define I915_READ8(reg) bus_space_read_1(dev_priv->regs->bst, \ + dev_priv->regs->bsh, (reg)) +#define I915_WRITE8(reg,val) bus_space_write_1(dev_priv->regs->bst, \ + dev_priv->regs->bsh, (reg), (val)) +#define INTELDRM_VERBOSE 0 +#if INTELDRM_VERBOSE > 0 +#define INTELDRM_VPRINTF(fmt, args...) DRM_INFO(fmt, ##args) +#else +#define INTELDRM_VPRINTF(fmt, args...) +#endif + +#define BEGIN_LP_RING(n) inteldrm_begin_ring(dev_priv, n) +#define OUT_RING(n) inteldrm_out_ring(dev_priv, n) +#define ADVANCE_LP_RING() inteldrm_advance_ring(dev_priv) + +/* MCH IFP BARs */ +#define I915_IFPADDR 0x60 +#define I965_IFPADDR 0x70 + +/* + * The Bridge device's PCI config space has information about the + * fb aperture size and the amount of pre-reserved memory. + */ +#define INTEL_GMCH_CTRL 0x52 +#define INTEL_GMCH_ENABLED 0x4 +#define INTEL_GMCH_MEM_MASK 0x1 +#define INTEL_GMCH_MEM_64M 0x1 +#define INTEL_GMCH_MEM_128M 0 + +#define INTEL_855_GMCH_GMS_MASK (0x7 << 4) +#define INTEL_855_GMCH_GMS_DISABLED (0x0 << 4) +#define INTEL_855_GMCH_GMS_STOLEN_1M (0x1 << 4) +#define INTEL_855_GMCH_GMS_STOLEN_4M (0x2 << 4) +#define INTEL_855_GMCH_GMS_STOLEN_8M (0x3 << 4) +#define INTEL_855_GMCH_GMS_STOLEN_16M (0x4 << 4) +#define INTEL_855_GMCH_GMS_STOLEN_32M (0x5 << 4) + +#define INTEL_915G_GMCH_GMS_STOLEN_48M (0x6 << 4) +#define INTEL_915G_GMCH_GMS_STOLEN_64M (0x7 << 4) + +/* PCI config space */ + +#define HPLLCC 0xc0 /* 855 only */ +#define GC_CLOCK_CONTROL_MASK (3 << 0) +#define GC_CLOCK_133_200 (0 << 0) +#define GC_CLOCK_100_200 (1 << 0) +#define GC_CLOCK_100_133 (2 << 0) +#define GC_CLOCK_166_250 (3 << 0) +#define GCFGC 0xf0 /* 915+ only */ +#define GC_LOW_FREQUENCY_ENABLE (1 << 7) +#define GC_DISPLAY_CLOCK_190_200_MHZ (0 << 4) +#define GC_DISPLAY_CLOCK_333_MHZ (4 << 4) +#define GC_DISPLAY_CLOCK_MASK (7 << 4) +#define LBB 0xf4 +/* reset register, 965+. this is byte-sized */ +#define GDRST 0xc0 +#define GDRST_FULL (0<<2) +#define GDRST_RENDER (1<<2) +#define GDRST_MEDIA (3<<2) + +/* VGA stuff */ + +#define VGA_ST01_MDA 0x3ba +#define VGA_ST01_CGA 0x3da + +#define VGA_MSR_WRITE 0x3c2 +#define VGA_MSR_READ 0x3cc +#define VGA_MSR_MEM_EN (1<<1) +#define VGA_MSR_CGA_MODE (1<<0) + +#define VGA_SR_INDEX 0x3c4 +#define VGA_SR_DATA 0x3c5 + +#define VGA_AR_INDEX 0x3c0 +#define VGA_AR_VID_EN (1<<5) +#define VGA_AR_DATA_WRITE 0x3c0 +#define VGA_AR_DATA_READ 0x3c1 + +#define VGA_GR_INDEX 0x3ce +#define VGA_GR_DATA 0x3cf +/* GR05 */ +#define VGA_GR_MEM_READ_MODE_SHIFT 3 +#define VGA_GR_MEM_READ_MODE_PLANE 1 +/* GR06 */ +#define VGA_GR_MEM_MODE_MASK 0xc +#define VGA_GR_MEM_MODE_SHIFT 2 +#define VGA_GR_MEM_A0000_AFFFF 0 +#define VGA_GR_MEM_A0000_BFFFF 1 +#define VGA_GR_MEM_B0000_B7FFF 2 +#define VGA_GR_MEM_B0000_BFFFF 3 + +#define VGA_DACMASK 0x3c6 +#define VGA_DACRX 0x3c7 +#define VGA_DACWX 0x3c8 +#define VGA_DACDATA 0x3c9 + +#define VGA_CR_INDEX_MDA 0x3b4 +#define VGA_CR_DATA_MDA 0x3b5 +#define VGA_CR_INDEX_CGA 0x3d4 +#define VGA_CR_DATA_CGA 0x3d5 + +/* + * Memory interface instructions used by the kernel + */ +#define MI_INSTR(opcode, flags) (((opcode) << 23) | (flags)) + +#define MI_NOOP MI_INSTR(0, 0) +#define MI_USER_INTERRUPT MI_INSTR(0x02, 0) +#define MI_WAIT_FOR_EVENT MI_INSTR(0x03, 0) +#define MI_WAIT_FOR_PLANE_B_FLIP (1<<6) +#define MI_WAIT_FOR_PLANE_A_FLIP (1<<2) +#define MI_WAIT_FOR_PLANE_A_SCANLINES (1<<1) +#define MI_FLUSH MI_INSTR(0x04, 0) +#define MI_READ_FLUSH (1 << 0) +#define MI_EXE_FLUSH (1 << 1) +#define MI_NO_WRITE_FLUSH (1 << 2) +#define MI_SCENE_COUNT (1 << 3) /* just increment scene count */ +#define MI_END_SCENE (1 << 4) /* flush binner and incr scene count */ +#define MI_BATCH_BUFFER_END MI_INSTR(0x0a, 0) +#define MI_REPORT_HEAD MI_INSTR(0x07, 0) +#define MI_LOAD_SCAN_LINES_INCL MI_INSTR(0x12, 0) +#define MI_STORE_DWORD_IMM MI_INSTR(0x20, 1) +#define MI_MEM_VIRTUAL (1 << 22) /* 965+ only */ +#define MI_STORE_DWORD_INDEX MI_INSTR(0x21, 1) +#define MI_STORE_DWORD_INDEX_SHIFT 2 +#define MI_LOAD_REGISTER_IMM MI_INSTR(0x22, 1) +#define MI_BATCH_BUFFER MI_INSTR(0x30, 1) +#define MI_BATCH_NON_SECURE (1) +#define MI_BATCH_NON_SECURE_I965 (1<<8) +#define MI_BATCH_BUFFER_START MI_INSTR(0x31, 0) + +/** + * Reads a dword out of the status page, which is written to from the command + * queue by automatic updates, MI_REPORT_HEAD, MI_STORE_DATA_INDEX, or + * MI_STORE_DATA_IMM. + * + * The following dwords have a reserved meaning: + * 0x00: ISR copy, updated when an ISR bit not set in the HWSTAM changes. + * 0x04: ring 0 head pointer + * 0x05: ring 1 head pointer (915-class) + * 0x06: ring 2 head pointer (915-class) + * 0x10-0x1b: Context status DWords (GM45) + * 0x1f: Last written status offset. (GM45) + * + * The area from dword 0x20 to 0x3ff is available for driver usage. + */ +#define READ_HWSP(dev_priv, reg) inteldrm_read_hws(dev_priv, reg) +#define I915_GEM_HWS_INDEX 0x20 + +/* + * 3D instructions used by the kernel + */ +#define GFX_INSTR(opcode, flags) ((0x3 << 29) | ((opcode) << 24) | (flags)) + +#define GFX_OP_RASTER_RULES ((0x3<<29)|(0x7<<24)) +#define GFX_OP_SCISSOR ((0x3<<29)|(0x1c<<24)|(0x10<<19)) +#define SC_UPDATE_SCISSOR (0x1<<1) +#define SC_ENABLE_MASK (0x1<<0) +#define SC_ENABLE (0x1<<0) +#define GFX_OP_LOAD_INDIRECT ((0x3<<29)|(0x1d<<24)|(0x7<<16)) +#define GFX_OP_SCISSOR_INFO ((0x3<<29)|(0x1d<<24)|(0x81<<16)|(0x1)) +#define SCI_YMIN_MASK (0xffff<<16) +#define SCI_XMIN_MASK (0xffff<<0) +#define SCI_YMAX_MASK (0xffff<<16) +#define SCI_XMAX_MASK (0xffff<<0) +#define GFX_OP_SCISSOR_ENABLE ((0x3<<29)|(0x1c<<24)|(0x10<<19)) +#define GFX_OP_SCISSOR_RECT ((0x3<<29)|(0x1d<<24)|(0x81<<16)|1) +#define GFX_OP_COLOR_FACTOR ((0x3<<29)|(0x1d<<24)|(0x1<<16)|0x0) +#define GFX_OP_STIPPLE ((0x3<<29)|(0x1d<<24)|(0x83<<16)) +#define GFX_OP_MAP_INFO ((0x3<<29)|(0x1d<<24)|0x4) +#define GFX_OP_DESTBUFFER_VARS ((0x3<<29)|(0x1d<<24)|(0x85<<16)|0x0) +#define GFX_OP_DESTBUFFER_INFO ((0x3<<29)|(0x1d<<24)|(0x8e<<16)|1) +#define GFX_OP_DRAWRECT_INFO ((0x3<<29)|(0x1d<<24)|(0x80<<16)|(0x3)) +#define GFX_OP_DRAWRECT_INFO_I965 ((0x7900<<16)|0x2) +#define SRC_COPY_BLT_CMD ((2<<29)|(0x43<<22)|4) +#define XY_SRC_COPY_BLT_CMD ((2<<29)|(0x53<<22)|6) +#define XY_MONO_SRC_COPY_IMM_BLT ((2<<29)|(0x71<<22)|5) +#define XY_SRC_COPY_BLT_WRITE_ALPHA (1<<21) +#define XY_SRC_COPY_BLT_WRITE_RGB (1<<20) +#define BLT_DEPTH_8 (0<<24) +#define BLT_DEPTH_16_565 (1<<24) +#define BLT_DEPTH_16_1555 (2<<24) +#define BLT_DEPTH_32 (3<<24) +#define BLT_ROP_GXCOPY (0xcc<<16) +#define XY_SRC_COPY_BLT_SRC_TILED (1<<15) /* 965+ only */ +#define XY_SRC_COPY_BLT_DST_TILED (1<<11) /* 965+ only */ +#define CMD_OP_DISPLAYBUFFER_INFO ((0x0<<29)|(0x14<<23)|2) +#define ASYNC_FLIP (1<<22) +#define DISPLAY_PLANE_A (0<<20) +#define DISPLAY_PLANE_B (1<<20) + +/* + * Fence registers + */ +#define FENCE_REG_830_0 0x2000 +#define FENCE_REG_945_8 0x3000 +#define I830_FENCE_START_MASK 0x07f80000 +#define I830_FENCE_TILING_Y_SHIFT 12 +#define I830_FENCE_SIZE_BITS(size) ((ffs((size) >> 19) - 1) << 8) +#define I830_FENCE_PITCH_SHIFT 4 +#define I830_FENCE_REG_VALID (1<<0) +#define I915_FENCE_MAX_PITCH_VAL 4 +#define I830_FENCE_MAX_PITCH_VAL 6 +#define I830_FENCE_MAX_SIZE_VAL (1<<8) + +#define I915_FENCE_START_MASK 0x0ff00000 +#define I915_FENCE_SIZE_BITS(size) ((ffs((size) >> 20) - 1) << 8) + +#define FENCE_REG_965_0 0x03000 +#define I965_FENCE_PITCH_SHIFT 2 +#define I965_FENCE_TILING_Y_SHIFT 1 +#define I965_FENCE_REG_VALID (1<<0) +#define I965_FENCE_MAX_PITCH_VAL 0x0400 + +/* + * Instruction and interrupt control regs + */ + +#define PGTBL_ER 0x02024 +#define PRB0_TAIL 0x02030 +#define PRB0_HEAD 0x02034 +#define PRB0_START 0x02038 +#define PRB0_CTL 0x0203c +#define TAIL_ADDR 0x001FFFF8 +#define HEAD_WRAP_COUNT 0xFFE00000 +#define HEAD_WRAP_ONE 0x00200000 +#define HEAD_ADDR 0x001FFFFC +#define RING_NR_PAGES 0x001FF000 +#define RING_REPORT_MASK 0x00000006 +#define RING_REPORT_64K 0x00000002 +#define RING_REPORT_128K 0x00000004 +#define RING_NO_REPORT 0x00000000 +#define RING_VALID_MASK 0x00000001 +#define RING_VALID 0x00000001 +#define RING_INVALID 0x00000000 +#define RING_WAIT_I8XX (1<<0) /* gen2, PRBx_HEAD */ +#define RING_WAIT (1<<11) /* gen3+, PRBx_CTL */ +#define PRB1_TAIL 0x02040 /* 915+ only */ +#define PRB1_HEAD 0x02044 /* 915+ only */ +#define PRB1_START 0x02048 /* 915+ only */ +#define PRB1_CTL 0x0204c /* 915+ only */ +#define IPEIR_I965 0x02064 +#define IPEHR_I965 0x02068 +#define INSTDONE_I965 0x0206c +#define INSTPS 0x02070 /* 965+ only */ +#define INSTDONE1 0x0207c /* 965+ only */ +#define ACTHD_I965 0x02074 +#define HWS_PGA 0x02080 +#define HWS_ADDRESS_MASK 0xfffff000 +#define HWS_START_ADDRESS_SHIFT 4 +#define IPEIR 0x02088 +#define IPEHR 0x0208c +#define INSTDONE 0x02090 +#define NOPID 0x02094 +#define HWSTAM 0x02098 + +#define MI_MODE 0x0209c +#define VS_TIMER_DISPATCH (1 << 6) + +#define SCPD0 0x0209c /* 915+ only */ +#define IER 0x020a0 +#define IIR 0x020a4 +#define IMR 0x020a8 +#define ISR 0x020ac +#define I915_PIPE_CONTROL_NOTIFY_INTERRUPT (1<<18) +#define I915_DISPLAY_PORT_INTERRUPT (1<<17) +#define I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT (1<<15) +#define I915_GMCH_THERMAL_SENSOR_EVENT_INTERRUPT (1<<14) +#define I915_HWB_OOM_INTERRUPT (1<<13) +#define I915_SYNC_STATUS_INTERRUPT (1<<12) +#define I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT (1<<11) +#define I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT (1<<10) +#define I915_OVERLAY_PLANE_FLIP_PENDING_INTERRUPT (1<<9) +#define I915_DISPLAY_PLANE_C_FLIP_PENDING_INTERRUPT (1<<8) +#define I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT (1<<7) +#define I915_DISPLAY_PIPE_A_EVENT_INTERRUPT (1<<6) +#define I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT (1<<5) +#define I915_DISPLAY_PIPE_B_EVENT_INTERRUPT (1<<4) +#define I915_DEBUG_INTERRUPT (1<<2) +#define I915_USER_INTERRUPT (1<<1) +#define I915_ASLE_INTERRUPT (1<<0) +#define EIR 0x020b0 +#define EMR 0x020b4 +#define ESR 0x020b8 +#define GM45_ERROR_PAGE_TABLE (1<<5) +#define GM45_ERROR_MEM_PRIV (1<<4) +#define I915_ERROR_PAGE_TABLE (1<<4) +#define GM45_ERROR_CP_PRIV (1<<3) +#define I915_ERROR_MEMORY_REFRESH (1<<1) +#define I915_ERROR_INSTRUCTION (1<<0) +/* ironlake error bits */ +#define GT_ERROR_PTE (1<<4) + /* memory privilege violation error */ +#define GT_ERROR_MPE (1<<3) + /* command privilege violation error */ +#define GT_ERROR_CPE (1<<2) +#define INSTPM 0x020c0 +#define ACTHD 0x020c8 +#define FW_BLC 0x020d8 +#define FW_BLC_SELF 0x020e0 /* 915+ only */ +#define MI_ARB_STATE 0x020e4 /* 915+ only */ +#define MI_ARB_MASK_SHIFT 16 /* shift for enable bits */ + +/* Make render/texture TLB fetches lower priorty than associated data + * fetches. This is not turned on by default + */ +#define MI_ARB_RENDER_TLB_LOW_PRIORITY (1 << 15) + +/* Isoch request wait on GTT enable (Display A/B/C streams). + * Make isoch requests stall on the TLB update. May cause + * display underruns (test mode only) + */ +#define MI_ARB_ISOCH_WAIT_GTT (1 << 14) + +/* Block grant count for isoch requests when block count is + * set to a finite value. + */ +#define MI_ARB_BLOCK_GRANT_MASK (3 << 12) +#define MI_ARB_BLOCK_GRANT_8 (0 << 12) /* for 3 display planes */ +#define MI_ARB_BLOCK_GRANT_4 (1 << 12) /* for 2 display planes */ +#define MI_ARB_BLOCK_GRANT_2 (2 << 12) /* for 1 display plane */ +#define MI_ARB_BLOCK_GRANT_0 (3 << 12) /* don't use */ + +/* Enable render writes to complete in C2/C3/C4 power states. + * If this isn't enabled, render writes are prevented in low + * power states. That seems bad to me. + */ +#define MI_ARB_C3_LP_WRITE_ENABLE (1 << 11) + +/* This acknowledges an async flip immediately instead + * of waiting for 2TLB fetches. + */ +#define MI_ARB_ASYNC_FLIP_ACK_IMMEDIATE (1 << 10) + +/* Enables non-sequential data reads through arbiter + */ +#define MI_ARB_DUAL_DATA_PHASE_DISABLE (1 << 9) + +/* Disable FSB snooping of cacheable write cycles from binner/render + * command stream + */ +#define MI_ARB_CACHE_SNOOP_DISABLE (1 << 8) + +/* Arbiter time slice for non-isoch streams */ +#define MI_ARB_TIME_SLICE_MASK (7 << 5) +#define MI_ARB_TIME_SLICE_1 (0 << 5) +#define MI_ARB_TIME_SLICE_2 (1 << 5) +#define MI_ARB_TIME_SLICE_4 (2 << 5) +#define MI_ARB_TIME_SLICE_6 (3 << 5) +#define MI_ARB_TIME_SLICE_8 (4 << 5) +#define MI_ARB_TIME_SLICE_10 (5 << 5) +#define MI_ARB_TIME_SLICE_14 (6 << 5) +#define MI_ARB_TIME_SLICE_16 (7 << 5) + +/* Low priority grace period page size */ +#define MI_ARB_LOW_PRIORITY_GRACE_4KB (0 << 4) /* default */ +#define MI_ARB_LOW_PRIORITY_GRACE_8KB (1 << 4) + +/* Disable display A/B trickle feed */ +#define MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE (1 << 2) + +/* Set display plane priority */ +#define MI_ARB_DISPLAY_PRIORITY_A_B (0 << 0) /* display A > display B */ +#define MI_ARB_DISPLAY_PRIORITY_B_A (1 << 0) /* display B > display A */ + +#define CACHE_MODE_0 0x02120 /* 915+ only */ +#define CM0_MASK_SHIFT 16 +#define CM0_IZ_OPT_DISABLE (1<<6) +#define CM0_ZR_OPT_DISABLE (1<<5) +#define CM0_DEPTH_EVICT_DISABLE (1<<4) +#define CM0_COLOR_EVICT_DISABLE (1<<3) +#define CM0_DEPTH_WRITE_DISABLE (1<<1) +#define CM0_RC_OP_FLUSH_DISABLE (1<<0) +#define GFX_FLSH_CNTL 0x02170 /* 915+ only */ + +/* + * Framebuffer compression (915+ only) + */ + +#define FBC_CFB_BASE 0x03200 /* 4k page aligned */ +#define FBC_LL_BASE 0x03204 /* 4k page aligned */ +#define FBC_CONTROL 0x03208 +#define FBC_CTL_EN (1<<31) +#define FBC_CTL_PERIODIC (1<<30) +#define FBC_CTL_INTERVAL_SHIFT (16) +#define FBC_CTL_UNCOMPRESSIBLE (1<<14) +#define FBC_CTL_STRIDE_SHIFT (5) +#define FBC_CTL_FENCENO (1<<0) +#define FBC_COMMAND 0x0320c +#define FBC_CMD_COMPRESS (1<<0) +#define FBC_STATUS 0x03210 +#define FBC_STAT_COMPRESSING (1<<31) +#define FBC_STAT_COMPRESSED (1<<30) +#define FBC_STAT_MODIFIED (1<<29) +#define FBC_STAT_CURRENT_LINE (1<<0) +#define FBC_CONTROL2 0x03214 +#define FBC_CTL_FENCE_DBL (0<<4) +#define FBC_CTL_IDLE_IMM (0<<2) +#define FBC_CTL_IDLE_FULL (1<<2) +#define FBC_CTL_IDLE_LINE (2<<2) +#define FBC_CTL_IDLE_DEBUG (3<<2) +#define FBC_CTL_CPU_FENCE (1<<1) +#define FBC_CTL_PLANEA (0<<0) +#define FBC_CTL_PLANEB (1<<0) +#define FBC_FENCE_OFF 0x0321b + +#define FBC_LL_SIZE (1536) + +/* Framebuffer compression for GM45+ */ +#define DPFC_CB_BASE 0x3200 +#define DPFC_CONTROL 0x3208 +#define DPFC_CTL_EN (1<<31) +#define DPFC_CTL_PLANEA (0<<30) +#define DPFC_CTL_PLANEB (1<<30) +#define DPFC_CTL_FENCE_EN (1<<29) +#define DPFC_SR_EN (1<<10) +#define DPFC_CTL_LIMIT_1X (0<<6) +#define DPFC_CTL_LIMIT_2X (1<<6) +#define DPFC_CTL_LIMIT_4X (2<<6) +#define DPFC_RECOMP_CTL 0x320c +#define DPFC_RECOMP_STALL_EN (1<<27) +#define DPFC_RECOMP_STALL_WM_SHIFT (16) +#define DPFC_RECOMP_STALL_WM_MASK (0x07ff0000) +#define DPFC_RECOMP_TIMER_COUNT_SHIFT (0) +#define DPFC_RECOMP_TIMER_COUNT_MASK (0x0000003f) +#define DPFC_STATUS 0x3210 +#define DPFC_INVAL_SEG_SHIFT (16) +#define DPFC_INVAL_SEG_MASK (0x07ff0000) +#define DPFC_COMP_SEG_SHIFT (0) +#define DPFC_COMP_SEG_MASK (0x000003ff) +#define DPFC_STATUS2 0x3214 +#define DPFC_FENCE_YOFF 0x3218 +#define DPFC_CHICKEN 0x3224 +#define DPFC_HT_MODIFY (1<<31) + +/* + * GPIO regs + */ +#define GPIOA 0x5010 +#define GPIOB 0x5014 +#define GPIOC 0x5018 +#define GPIOD 0x501c +#define GPIOE 0x5020 +#define GPIOF 0x5024 +#define GPIOG 0x5028 +#define GPIOH 0x502c +# define GPIO_CLOCK_DIR_MASK (1 << 0) +# define GPIO_CLOCK_DIR_IN (0 << 1) +# define GPIO_CLOCK_DIR_OUT (1 << 1) +# define GPIO_CLOCK_VAL_MASK (1 << 2) +# define GPIO_CLOCK_VAL_OUT (1 << 3) +# define GPIO_CLOCK_VAL_IN (1 << 4) +# define GPIO_CLOCK_PULLUP_DISABLE (1 << 5) +# define GPIO_DATA_DIR_MASK (1 << 8) +# define GPIO_DATA_DIR_IN (0 << 9) +# define GPIO_DATA_DIR_OUT (1 << 9) +# define GPIO_DATA_VAL_MASK (1 << 10) +# define GPIO_DATA_VAL_OUT (1 << 11) +# define GPIO_DATA_VAL_IN (1 << 12) +# define GPIO_DATA_PULLUP_DISABLE (1 << 13) + +/* + * Clock control & power management + */ + +#define VGA0 0x6000 +#define VGA1 0x6004 +#define VGA_PD 0x6010 +#define VGA0_PD_P2_DIV_4 (1 << 7) +#define VGA0_PD_P1_DIV_2 (1 << 5) +#define VGA0_PD_P1_SHIFT 0 +#define VGA0_PD_P1_MASK (0x1f << 0) +#define VGA1_PD_P2_DIV_4 (1 << 15) +#define VGA1_PD_P1_DIV_2 (1 << 13) +#define VGA1_PD_P1_SHIFT 8 +#define VGA1_PD_P1_MASK (0x1f << 8) +#define DPLL_A 0x06014 +#define DPLL_B 0x06018 +#define DPLL_VCO_ENABLE (1 << 31) +#define DPLL_DVO_HIGH_SPEED (1 << 30) +#define DPLL_SYNCLOCK_ENABLE (1 << 29) +#define DPLL_VGA_MODE_DIS (1 << 28) +#define DPLLB_MODE_DAC_SERIAL (1 << 26) /* i915 */ +#define DPLLB_MODE_LVDS (2 << 26) /* i915 */ +#define DPLL_MODE_MASK (3 << 26) +#define DPLL_DAC_SERIAL_P2_CLOCK_DIV_10 (0 << 24) /* i915 */ +#define DPLL_DAC_SERIAL_P2_CLOCK_DIV_5 (1 << 24) /* i915 */ +#define DPLLB_LVDS_P2_CLOCK_DIV_14 (0 << 24) /* i915 */ +#define DPLLB_LVDS_P2_CLOCK_DIV_7 (1 << 24) /* i915 */ +#define DPLL_P2_CLOCK_DIV_MASK 0x03000000 /* i915 */ +#define DPLL_FPA01_P1_POST_DIV_MASK 0x00ff0000 /* i915 */ + +#define I915_FIFO_UNDERRUN_STATUS (1UL<<31) +#define I915_CRC_ERROR_ENABLE (1UL<<29) +#define I915_CRC_DONE_ENABLE (1UL<<28) +#define I915_GMBUS_EVENT_ENABLE (1UL<<27) +#define I915_VSYNC_INTERRUPT_ENABLE (1UL<<25) +#define I915_DISPLAY_LINE_COMPARE_ENABLE (1UL<<24) +#define I915_DPST_EVENT_ENABLE (1UL<<23) +#define I915_LEGACY_BLC_EVENT_ENABLE (1UL<<22) +#define I915_ODD_FIELD_INTERRUPT_ENABLE (1UL<<21) +#define I915_EVEN_FIELD_INTERRUPT_ENABLE (1UL<<20) +#define I915_START_VBLANK_INTERRUPT_ENABLE (1UL<<18) /* 965 or later */ +#define I915_VBLANK_INTERRUPT_ENABLE (1UL<<17) +#define I915_OVERLAY_UPDATED_ENABLE (1UL<<16) +#define I915_CRC_ERROR_INTERRUPT_STATUS (1UL<<13) +#define I915_CRC_DONE_INTERRUPT_STATUS (1UL<<12) +#define I915_GMBUS_INTERRUPT_STATUS (1UL<<11) +#define I915_VSYNC_INTERRUPT_STATUS (1UL<<9) +#define I915_DISPLAY_LINE_COMPARE_STATUS (1UL<<8) +#define I915_DPST_EVENT_STATUS (1UL<<7) +#define I915_LEGACY_BLC_EVENT_STATUS (1UL<<6) +#define I915_ODD_FIELD_INTERRUPT_STATUS (1UL<<5) +#define I915_EVEN_FIELD_INTERRUPT_STATUS (1UL<<4) +#define I915_START_VBLANK_INTERRUPT_STATUS (1UL<<2) /* 965 or later */ +#define I915_VBLANK_INTERRUPT_STATUS (1UL<<1) +#define I915_OVERLAY_UPDATED_STATUS (1UL<<0) + +#define SRX_INDEX 0x3c4 +#define SRX_DATA 0x3c5 +#define SR01 1 +#define SR01_SCREEN_OFF (1<<5) + +#define PPCR 0x61204 +#define PPCR_ON (1<<0) + +#define DVOB 0x61140 +#define DVOB_ON (1<<31) +#define DVOC 0x61160 +#define DVOC_ON (1<<31) +#define LVDS 0x61180 +#define LVDS_ON (1<<31) + +#define ADPA 0x61100 +#define ADPA_DPMS_MASK (~(3<<10)) +#define ADPA_DPMS_ON (0<<10) +#define ADPA_DPMS_SUSPEND (1<<10) +#define ADPA_DPMS_STANDBY (2<<10) +#define ADPA_DPMS_OFF (3<<10) + +#define RING_TAIL 0x00 +#define TAIL_ADDR 0x001FFFF8 +#define RING_HEAD 0x04 +#define HEAD_WRAP_COUNT 0xFFE00000 +#define HEAD_WRAP_ONE 0x00200000 +#define HEAD_ADDR 0x001FFFFC +#define RING_START 0x08 +#define START_ADDR 0xFFFFF000 +#define RING_LEN 0x0C +#define RING_NR_PAGES 0x001FF000 +#define RING_REPORT_MASK 0x00000006 +#define RING_REPORT_64K 0x00000002 +#define RING_REPORT_128K 0x00000004 +#define RING_NO_REPORT 0x00000000 +#define RING_VALID_MASK 0x00000001 +#define RING_VALID 0x00000001 +#define RING_INVALID 0x00000000 + +/* Scratch pad debug 0 reg: + */ +#define DPLL_FPA01_P1_POST_DIV_MASK_I830 0x001f0000 +/* + * The i830 generation, in LVDS mode, defines P1 as the bit number set within + * this field (only one bit may be set). + */ +#define DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS 0x003f0000 +#define DPLL_FPA01_P1_POST_DIV_SHIFT 16 +#define DPLL_FPA01_P1_POST_DIV_SHIFT_IGD 15 +/* i830, required in DVO non-gang */ +#define PLL_P2_DIVIDE_BY_4 (1 << 23) +#define PLL_P1_DIVIDE_BY_TWO (1 << 21) /* i830 */ +#define PLL_REF_INPUT_DREFCLK (0 << 13) +#define PLL_REF_INPUT_TVCLKINA (1 << 13) /* i830 */ +#define PLL_REF_INPUT_TVCLKINBC (2 << 13) /* SDVO TVCLKIN */ +#define PLLB_REF_INPUT_SPREADSPECTRUMIN (3 << 13) +#define PLL_REF_INPUT_MASK (3 << 13) +#define PLL_LOAD_PULSE_PHASE_SHIFT 9 +/* IGDNG */ +# define PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT 9 +# define PLL_REF_SDVO_HDMI_MULTIPLIER_MASK (7 << 9) +# define PLL_REF_SDVO_HDMI_MULTIPLIER(x) (((x)-1) << 9) +# define DPLL_FPA1_P1_POST_DIV_SHIFT 0 +# define DPLL_FPA1_P1_POST_DIV_MASK 0xff + +/* + * Parallel to Serial Load Pulse phase selection. + * Selects the phase for the 10X DPLL clock for the PCIe + * digital display port. The range is 4 to 13; 10 or more + * is just a flip delay. The default is 6 + */ +#define PLL_LOAD_PULSE_PHASE_MASK (0xf << PLL_LOAD_PULSE_PHASE_SHIFT) +#define DISPLAY_RATE_SELECT_FPA1 (1 << 8) +/* + * SDVO multiplier for 945G/GM. Not used on 965. + */ +#define SDVO_MULTIPLIER_MASK 0x000000ff +#define SDVO_MULTIPLIER_SHIFT_HIRES 4 +#define SDVO_MULTIPLIER_SHIFT_VGA 0 +#define DPLL_A_MD 0x0601c /* 965+ only */ +/* + * UDI pixel divider, controlling how many pixels are stuffed into a packet. + * + * Value is pixels minus 1. Must be set to 1 pixel for SDVO. + */ +#define DPLL_MD_UDI_DIVIDER_MASK 0x3f000000 +#define DPLL_MD_UDI_DIVIDER_SHIFT 24 +/* UDI pixel divider for VGA, same as DPLL_MD_UDI_DIVIDER_MASK. */ +#define DPLL_MD_VGA_UDI_DIVIDER_MASK 0x003f0000 +#define DPLL_MD_VGA_UDI_DIVIDER_SHIFT 16 +/* + * SDVO/UDI pixel multiplier. + * + * SDVO requires that the bus clock rate be between 1 and 2 GHz, and the bus + * clock rate is 10 times the DPLL clock. At low resolution/refresh rate + * modes, the bus rate would be below the limits, so SDVO allows for stuffing + * dummy bytes in the datastream at an increased clock rate, with both sides of + * the link knowing how many bytes are fill. + * + * So, for a mode with a dotclock of 65MHz, we would want to double the clock + * rate to 130MHz to get a bus rate of 1.30GHz. The DPLL clock rate would be + * set to 130MHz, and the SDVO multiplier set to 2x in this register and + * through an SDVO command. + * + * This register field has values of multiplication factor minus 1, with + * a maximum multiplier of 5 for SDVO. + */ +#define DPLL_MD_UDI_MULTIPLIER_MASK 0x00003f00 +#define DPLL_MD_UDI_MULTIPLIER_SHIFT 8 +/* + * SDVO/UDI pixel multiplier for VGA, same as DPLL_MD_UDI_MULTIPLIER_MASK. + * This best be set to the default value (3) or the CRT won't work. No, + * I don't entirely understand what this does... + */ +#define DPLL_MD_VGA_UDI_MULTIPLIER_MASK 0x0000003f +#define DPLL_MD_VGA_UDI_MULTIPLIER_SHIFT 0 +#define DPLL_B_MD 0x06020 /* 965+ only */ +#define FPA0 0x06040 +#define FPA1 0x06044 +#define FPB0 0x06048 +#define FPB1 0x0604c +#define FP_N_DIV_MASK 0x003f0000 +#define FP_N_IGD_DIV_MASK 0x00ff0000 +#define FP_N_DIV_SHIFT 16 +#define FP_M1_DIV_MASK 0x00003f00 +#define FP_M1_DIV_SHIFT 8 +#define FP_M2_DIV_MASK 0x0000003f +#define FP_M2_IGD_DIV_MASK 0x000000ff +#define FP_M2_DIV_SHIFT 0 +#define DPLL_TEST 0x606c +#define DPLLB_TEST_SDVO_DIV_1 (0 << 22) +#define DPLLB_TEST_SDVO_DIV_2 (1 << 22) +#define DPLLB_TEST_SDVO_DIV_4 (2 << 22) +#define DPLLB_TEST_SDVO_DIV_MASK (3 << 22) +#define DPLLB_TEST_N_BYPASS (1 << 19) +#define DPLLB_TEST_M_BYPASS (1 << 18) +#define DPLLB_INPUT_BUFFER_ENABLE (1 << 16) +#define DPLLA_TEST_N_BYPASS (1 << 3) +#define DPLLA_TEST_M_BYPASS (1 << 2) +#define DPLLA_INPUT_BUFFER_ENABLE (1 << 0) +#define D_STATE 0x6104 +#define DSTATE_PLL_D3_OFF (1<<3) +#define DSTATE_GFX_CLOCK_GATING (1<<1) +#define DSTATE_DOT_CLOCK_GATING (1<<0) +#define DSPCLK_GATE_D 0x6200 +# define DPUNIT_B_CLOCK_GATE_DISABLE (1 << 30) /* 965 */ +# define VSUNIT_CLOCK_GATE_DISABLE (1 << 29) /* 965 */ +# define VRHUNIT_CLOCK_GATE_DISABLE (1 << 28) /* 965 */ +# define VRDUNIT_CLOCK_GATE_DISABLE (1 << 27) /* 965 */ +# define AUDUNIT_CLOCK_GATE_DISABLE (1 << 26) /* 965 */ +# define DPUNIT_A_CLOCK_GATE_DISABLE (1 << 25) /* 965 */ +# define DPCUNIT_CLOCK_GATE_DISABLE (1 << 24) /* 965 */ +# define TVRUNIT_CLOCK_GATE_DISABLE (1 << 23) /* 915-945 */ +# define TVCUNIT_CLOCK_GATE_DISABLE (1 << 22) /* 915-945 */ +# define TVFUNIT_CLOCK_GATE_DISABLE (1 << 21) /* 915-945 */ +# define TVEUNIT_CLOCK_GATE_DISABLE (1 << 20) /* 915-945 */ +# define DVSUNIT_CLOCK_GATE_DISABLE (1 << 19) /* 915-945 */ +# define DSSUNIT_CLOCK_GATE_DISABLE (1 << 18) /* 915-945 */ +# define DDBUNIT_CLOCK_GATE_DISABLE (1 << 17) /* 915-945 */ +# define DPRUNIT_CLOCK_GATE_DISABLE (1 << 16) /* 915-945 */ +# define DPFUNIT_CLOCK_GATE_DISABLE (1 << 15) /* 915-945 */ +# define DPBMUNIT_CLOCK_GATE_DISABLE (1 << 14) /* 915-945 */ +# define DPLSUNIT_CLOCK_GATE_DISABLE (1 << 13) /* 915-945 */ +# define DPLUNIT_CLOCK_GATE_DISABLE (1 << 12) /* 915-945 */ +# define DPOUNIT_CLOCK_GATE_DISABLE (1 << 11) +# define DPBUNIT_CLOCK_GATE_DISABLE (1 << 10) +# define DCUNIT_CLOCK_GATE_DISABLE (1 << 9) +# define DPUNIT_CLOCK_GATE_DISABLE (1 << 8) +# define VRUNIT_CLOCK_GATE_DISABLE (1 << 7) /* 915+: reserved */ +# define OVHUNIT_CLOCK_GATE_DISABLE (1 << 6) /* 830-865 */ +# define DPIOUNIT_CLOCK_GATE_DISABLE (1 << 6) /* 915-945 */ +# define OVFUNIT_CLOCK_GATE_DISABLE (1 << 5) +# define OVBUNIT_CLOCK_GATE_DISABLE (1 << 4) +/** + * This bit must be set on the 830 to prevent hangs when turning off the + * overlay scaler. + */ +# define OVRUNIT_CLOCK_GATE_DISABLE (1 << 3) +# define OVCUNIT_CLOCK_GATE_DISABLE (1 << 2) +# define OVUUNIT_CLOCK_GATE_DISABLE (1 << 1) +# define ZVUNIT_CLOCK_GATE_DISABLE (1 << 0) /* 830 */ +# define OVLUNIT_CLOCK_GATE_DISABLE (1 << 0) /* 845,865 */ + +#define RENCLK_GATE_D1 0x6204 +# define BLITTER_CLOCK_GATE_DISABLE (1 << 13) /* 945GM only */ +# define MPEG_CLOCK_GATE_DISABLE (1 << 12) /* 945GM only */ +# define PC_FE_CLOCK_GATE_DISABLE (1 << 11) +# define PC_BE_CLOCK_GATE_DISABLE (1 << 10) +# define WINDOWER_CLOCK_GATE_DISABLE (1 << 9) +# define INTERPOLATOR_CLOCK_GATE_DISABLE (1 << 8) +# define COLOR_CALCULATOR_CLOCK_GATE_DISABLE (1 << 7) +# define MOTION_COMP_CLOCK_GATE_DISABLE (1 << 6) +# define MAG_CLOCK_GATE_DISABLE (1 << 5) +/** This bit must be unset on 855,865 */ +# define MECI_CLOCK_GATE_DISABLE (1 << 4) +# define DCMP_CLOCK_GATE_DISABLE (1 << 3) +# define MEC_CLOCK_GATE_DISABLE (1 << 2) +# define MECO_CLOCK_GATE_DISABLE (1 << 1) +/** This bit must be set on 855,865. */ +# define SV_CLOCK_GATE_DISABLE (1 << 0) +# define I915_MPEG_CLOCK_GATE_DISABLE (1 << 16) +# define I915_VLD_IP_PR_CLOCK_GATE_DISABLE (1 << 15) +# define I915_MOTION_COMP_CLOCK_GATE_DISABLE (1 << 14) +# define I915_BD_BF_CLOCK_GATE_DISABLE (1 << 13) +# define I915_SF_SE_CLOCK_GATE_DISABLE (1 << 12) +# define I915_WM_CLOCK_GATE_DISABLE (1 << 11) +# define I915_IZ_CLOCK_GATE_DISABLE (1 << 10) +# define I915_PI_CLOCK_GATE_DISABLE (1 << 9) +# define I915_DI_CLOCK_GATE_DISABLE (1 << 8) +# define I915_SH_SV_CLOCK_GATE_DISABLE (1 << 7) +# define I915_PL_DG_QC_FT_CLOCK_GATE_DISABLE (1 << 6) +# define I915_SC_CLOCK_GATE_DISABLE (1 << 5) +# define I915_FL_CLOCK_GATE_DISABLE (1 << 4) +# define I915_DM_CLOCK_GATE_DISABLE (1 << 3) +# define I915_PS_CLOCK_GATE_DISABLE (1 << 2) +# define I915_CC_CLOCK_GATE_DISABLE (1 << 1) +# define I915_BY_CLOCK_GATE_DISABLE (1 << 0) + +# define I965_RCZ_CLOCK_GATE_DISABLE (1 << 30) +/** This bit must always be set on 965G/965GM */ +# define I965_RCC_CLOCK_GATE_DISABLE (1 << 29) +# define I965_RCPB_CLOCK_GATE_DISABLE (1 << 28) +# define I965_DAP_CLOCK_GATE_DISABLE (1 << 27) +# define I965_ROC_CLOCK_GATE_DISABLE (1 << 26) +# define I965_GW_CLOCK_GATE_DISABLE (1 << 25) +# define I965_TD_CLOCK_GATE_DISABLE (1 << 24) +/** This bit must always be set on 965G */ +# define I965_ISC_CLOCK_GATE_DISABLE (1 << 23) +# define I965_IC_CLOCK_GATE_DISABLE (1 << 22) +# define I965_EU_CLOCK_GATE_DISABLE (1 << 21) +# define I965_IF_CLOCK_GATE_DISABLE (1 << 20) +# define I965_TC_CLOCK_GATE_DISABLE (1 << 19) +# define I965_SO_CLOCK_GATE_DISABLE (1 << 17) +# define I965_FBC_CLOCK_GATE_DISABLE (1 << 16) +# define I965_MARI_CLOCK_GATE_DISABLE (1 << 15) +# define I965_MASF_CLOCK_GATE_DISABLE (1 << 14) +# define I965_MAWB_CLOCK_GATE_DISABLE (1 << 13) +# define I965_EM_CLOCK_GATE_DISABLE (1 << 12) +# define I965_UC_CLOCK_GATE_DISABLE (1 << 11) +# define I965_SI_CLOCK_GATE_DISABLE (1 << 6) +# define I965_MT_CLOCK_GATE_DISABLE (1 << 5) +# define I965_PL_CLOCK_GATE_DISABLE (1 << 4) +# define I965_DG_CLOCK_GATE_DISABLE (1 << 3) +# define I965_QC_CLOCK_GATE_DISABLE (1 << 2) +# define I965_FT_CLOCK_GATE_DISABLE (1 << 1) +# define I965_DM_CLOCK_GATE_DISABLE (1 << 0) + +#define RENCLK_GATE_D2 0x6208 +#define VF_UNIT_CLOCK_GATE_DISABLE (1 << 9) +#define GS_UNIT_CLOCK_GATE_DISABLE (1 << 7) +#define CL_UNIT_CLOCK_GATE_DISABLE (1 << 6) +#define RAMCLK_GATE_D 0x6210 /* CRL only */ +#define DEUC 0x6214 /* CRL only */ + +/* + * Palette regs + */ + +#define PALETTE_A 0x0a000 +#define PALETTE_B 0x0a800 + +/* MCH MMIO space */ + +/* + * MCHBAR mirror. + * + * This mirrors the MCHBAR MMIO space whose location is determined by + * device 0 function 0's pci config register 0x44 or 0x48 and matches it in + * every way. It is not accessible from the CP register read instructions. + * + */ +#define MCHBAR_MIRROR_BASE 0x10000 + +/** 915-945 and GM965 MCH register controlling DRAM channel access */ +#define DCC 0x10200 +#define DCC_ADDRESSING_MODE_SINGLE_CHANNEL (0 << 0) +#define DCC_ADDRESSING_MODE_DUAL_CHANNEL_ASYMMETRIC (1 << 0) +#define DCC_ADDRESSING_MODE_DUAL_CHANNEL_INTERLEAVED (2 << 0) +#define DCC_ADDRESSING_MODE_MASK (3 << 0) +#define DCC_CHANNEL_XOR_DISABLE (1 << 10) +#define DCC_CHANNEL_XOR_BIT_17 (1 << 9) + +/** 965 MCH register controlling DRAM channel configuration */ +#define C0DRB3 0x10206 +#define C1DRB3 0x10606 + +/* Clocking configuration register */ +#define CLKCFG 0x10c00 +#define CLKCFG_FSB_400 (5 << 0) /* hrawclk 100 */ +#define CLKCFG_FSB_533 (1 << 0) /* hrawclk 133 */ +#define CLKCFG_FSB_667 (3 << 0) /* hrawclk 166 */ +#define CLKCFG_FSB_800 (2 << 0) /* hrawclk 200 */ +#define CLKCFG_FSB_1067 (6 << 0) /* hrawclk 266 */ +#define CLKCFG_FSB_1333 (7 << 0) /* hrawclk 333 */ +/* Note, below two are guess */ +#define CLKCFG_FSB_1600 (4 << 0) /* hrawclk 400 */ +#define CLKCFG_FSB_1600_ALT (0 << 0) /* hrawclk 400 */ +#define CLKCFG_FSB_MASK (7 << 0) +#define CLKCFG_MEM_533 (1 << 4) +#define CLKCFG_MEM_667 (2 << 4) +#define CLKCFG_MEM_800 (3 << 4) +#define CLKCFG_MEM_MASK (7 << 4) + +/** GM965 GM45 render standby register */ +#define MCHBAR_RENDER_STANDBY 0x111B8 + +#define PEG_BAND_GAP_DATA 0x14d68 + +/* + * Overlay regs + */ + +#define OVADD 0x30000 +#define DOVSTA 0x30008 +#define OC_BUF (0x3<<20) +#define OGAMC5 0x30010 +#define OGAMC4 0x30014 +#define OGAMC3 0x30018 +#define OGAMC2 0x3001c +#define OGAMC1 0x30020 +#define OGAMC0 0x30024 + +/* + * Display engine regs + */ + +/* Pipe A timing regs */ +#define HTOTAL_A 0x60000 +#define HBLANK_A 0x60004 +#define HSYNC_A 0x60008 +#define VTOTAL_A 0x6000c +#define VBLANK_A 0x60010 +#define VSYNC_A 0x60014 +#define PIPEASRC 0x6001c +#define BCLRPAT_A 0x60020 + +/* Pipe B timing regs */ +#define HTOTAL_B 0x61000 +#define HBLANK_B 0x61004 +#define HSYNC_B 0x61008 +#define VTOTAL_B 0x6100c +#define VBLANK_B 0x61010 +#define VSYNC_B 0x61014 +#define PIPEBSRC 0x6101c +#define BCLRPAT_B 0x61020 + +/* VGA port control */ +#define ADPA 0x61100 +#define ADPA_DAC_ENABLE (1<<31) +#define ADPA_DAC_DISABLE 0 +#define ADPA_PIPE_SELECT_MASK (1<<30) +#define ADPA_PIPE_A_SELECT 0 +#define ADPA_PIPE_B_SELECT (1<<30) +#define ADPA_USE_VGA_HVPOLARITY (1<<15) +#define ADPA_SETS_HVPOLARITY 0 +#define ADPA_VSYNC_CNTL_DISABLE (1<<11) +#define ADPA_VSYNC_CNTL_ENABLE 0 +#define ADPA_HSYNC_CNTL_DISABLE (1<<10) +#define ADPA_HSYNC_CNTL_ENABLE 0 +#define ADPA_VSYNC_ACTIVE_HIGH (1<<4) +#define ADPA_VSYNC_ACTIVE_LOW 0 +#define ADPA_HSYNC_ACTIVE_HIGH (1<<3) +#define ADPA_HSYNC_ACTIVE_LOW 0 +#define ADPA_DPMS_MASK (~(3<<10)) +#define ADPA_DPMS_ON (0<<10) +#define ADPA_DPMS_SUSPEND (1<<10) +#define ADPA_DPMS_STANDBY (2<<10) +#define ADPA_DPMS_OFF (3<<10) + +/* Hotplug control (945+ only) */ +#define PORT_HOTPLUG_EN 0x61110 +#define SDVOB_HOTPLUG_INT_EN (1 << 26) +#define SDVOC_HOTPLUG_INT_EN (1 << 25) +#define TV_HOTPLUG_INT_EN (1 << 18) +#define CRT_HOTPLUG_INT_EN (1 << 9) +#define CRT_HOTPLUG_FORCE_DETECT (1 << 3) + +#define PORT_HOTPLUG_STAT 0x61114 +#define CRT_HOTPLUG_INT_STATUS (1 << 11) +#define TV_HOTPLUG_INT_STATUS (1 << 10) +#define CRT_HOTPLUG_MONITOR_MASK (3 << 8) +#define CRT_HOTPLUG_MONITOR_COLOR (3 << 8) +#define CRT_HOTPLUG_MONITOR_MONO (2 << 8) +#define CRT_HOTPLUG_MONITOR_NONE (0 << 8) +#define SDVOC_HOTPLUG_INT_STATUS (1 << 7) +#define SDVOB_HOTPLUG_INT_STATUS (1 << 6) + +/* SDVO port control */ +#define SDVOB 0x61140 +#define SDVOC 0x61160 +#define SDVO_ENABLE (1 << 31) +#define SDVO_PIPE_B_SELECT (1 << 30) +#define SDVO_STALL_SELECT (1 << 29) +#define SDVO_INTERRUPT_ENABLE (1 << 26) +/** + * 915G/GM SDVO pixel multiplier. + * + * Programmed value is multiplier - 1, up to 5x. + * + * \sa DPLL_MD_UDI_MULTIPLIER_MASK + */ +#define SDVO_PORT_MULTIPLY_MASK (7 << 23) +#define SDVO_PORT_MULTIPLY_SHIFT 23 +#define SDVO_PHASE_SELECT_MASK (15 << 19) +#define SDVO_PHASE_SELECT_DEFAULT (6 << 19) +#define SDVO_CLOCK_OUTPUT_INVERT (1 << 18) +#define SDVOC_GANG_MODE (1 << 16) +#define SDVO_BORDER_ENABLE (1 << 7) +#define SDVOB_PCIE_CONCURRENCY (1 << 3) +#define SDVO_DETECTED (1 << 2) +/* Bits to be preserved when writing */ +#define SDVOB_PRESERVE_MASK ((1 << 17) | (1 << 16) | (1 << 14) | (1 << 26)) +#define SDVOC_PRESERVE_MASK ((1 << 17) | (1 << 26)) + +/* DVO port control */ +#define DVOA 0x61120 +#define DVOB 0x61140 +#define DVOC 0x61160 +#define DVO_ENABLE (1 << 31) +#define DVO_PIPE_B_SELECT (1 << 30) +#define DVO_PIPE_STALL_UNUSED (0 << 28) +#define DVO_PIPE_STALL (1 << 28) +#define DVO_PIPE_STALL_TV (2 << 28) +#define DVO_PIPE_STALL_MASK (3 << 28) +#define DVO_USE_VGA_SYNC (1 << 15) +#define DVO_DATA_ORDER_I740 (0 << 14) +#define DVO_DATA_ORDER_FP (1 << 14) +#define DVO_VSYNC_DISABLE (1 << 11) +#define DVO_HSYNC_DISABLE (1 << 10) +#define DVO_VSYNC_TRISTATE (1 << 9) +#define DVO_HSYNC_TRISTATE (1 << 8) +#define DVO_BORDER_ENABLE (1 << 7) +#define DVO_DATA_ORDER_GBRG (1 << 6) +#define DVO_DATA_ORDER_RGGB (0 << 6) +#define DVO_DATA_ORDER_GBRG_ERRATA (0 << 6) +#define DVO_DATA_ORDER_RGGB_ERRATA (1 << 6) +#define DVO_VSYNC_ACTIVE_HIGH (1 << 4) +#define DVO_HSYNC_ACTIVE_HIGH (1 << 3) +#define DVO_BLANK_ACTIVE_HIGH (1 << 2) +#define DVO_OUTPUT_CSTATE_PIXELS (1 << 1) /* SDG only */ +#define DVO_OUTPUT_SOURCE_SIZE_PIXELS (1 << 0) /* SDG only */ +#define DVO_PRESERVE_MASK (0x7<<24) +#define DVOA_SRCDIM 0x61124 +#define DVOB_SRCDIM 0x61144 +#define DVOC_SRCDIM 0x61164 +#define DVO_SRCDIM_HORIZONTAL_SHIFT 12 +#define DVO_SRCDIM_VERTICAL_SHIFT 0 + +/* LVDS port control */ +#define LVDS 0x61180 +/* + * Enables the LVDS port. This bit must be set before DPLLs are enabled, as + * the DPLL semantics change when the LVDS is assigned to that pipe. + */ +#define LVDS_PORT_EN (1 << 31) +/* Selects pipe B for LVDS data. Must be set on pre-965. */ +#define LVDS_PIPEB_SELECT (1 << 30) +/* + * Enables the A0-A2 data pairs and CLKA, containing 18 bits of color data per + * pixel. + */ +#define LVDS_A0A2_CLKA_POWER_MASK (3 << 8) +#define LVDS_A0A2_CLKA_POWER_DOWN (0 << 8) +#define LVDS_A0A2_CLKA_POWER_UP (3 << 8) +/* + * Controls the A3 data pair, which contains the additional LSBs for 24 bit + * mode. Only enabled if LVDS_A0A2_CLKA_POWER_UP also indicates it should be + * on. + */ +#define LVDS_A3_POWER_MASK (3 << 6) +#define LVDS_A3_POWER_DOWN (0 << 6) +#define LVDS_A3_POWER_UP (3 << 6) +/* + * Controls the CLKB pair. This should only be set when LVDS_B0B3_POWER_UP + * is set. + */ +#define LVDS_CLKB_POWER_MASK (3 << 4) +#define LVDS_CLKB_POWER_DOWN (0 << 4) +#define LVDS_CLKB_POWER_UP (3 << 4) +/* + * Controls the B0-B3 data pairs. This must be set to match the DPLL p2 + * setting for whether we are in dual-channel mode. The B3 pair will + * additionally only be powered up when LVDS_A3_POWER_UP is set. + */ +#define LVDS_B0B3_POWER_MASK (3 << 2) +#define LVDS_B0B3_POWER_DOWN (0 << 2) +#define LVDS_B0B3_POWER_UP (3 << 2) + +/* Panel power sequencing */ +#define PP_STATUS 0x61200 +#define PP_ON (1 << 31) +/* + * Indicates that all dependencies of the panel are on: + * + * - PLL enabled + * - pipe enabled + * - LVDS/DVOB/DVOC on + */ +#define PP_READY (1 << 30) +#define PP_SEQUENCE_NONE (0 << 28) +#define PP_SEQUENCE_ON (1 << 28) +#define PP_SEQUENCE_OFF (2 << 28) +#define PP_SEQUENCE_MASK 0x30000000 +#define PP_CONTROL 0x61204 +#define POWER_TARGET_ON (1 << 0) +#define PP_ON_DELAYS 0x61208 +#define PP_OFF_DELAYS 0x6120c +#define PP_DIVISOR 0x61210 + +/* Panel fitting */ +#define PFIT_CONTROL 0x61230 +#define PFIT_ENABLE (1 << 31) +#define PFIT_PIPE_MASK (3 << 29) +#define PFIT_PIPE_SHIFT 29 +#define VERT_INTERP_DISABLE (0 << 10) +#define VERT_INTERP_BILINEAR (1 << 10) +#define VERT_INTERP_MASK (3 << 10) +#define VERT_AUTO_SCALE (1 << 9) +#define HORIZ_INTERP_DISABLE (0 << 6) +#define HORIZ_INTERP_BILINEAR (1 << 6) +#define HORIZ_INTERP_MASK (3 << 6) +#define HORIZ_AUTO_SCALE (1 << 5) +#define PANEL_8TO6_DITHER_ENABLE (1 << 3) +#define PFIT_PGM_RATIOS 0x61234 +#define PFIT_VERT_SCALE_MASK 0xfff00000 +#define PFIT_HORIZ_SCALE_MASK 0x0000fff0 +#define PFIT_AUTO_RATIOS 0x61238 + +/* Backlight control */ +#define BLC_PWM_CTL 0x61254 +#define BACKLIGHT_MODULATION_FREQ_SHIFT (17) +#define BLC_PWM_CTL2 0x61250 /* 965+ only */ +/* + * This is the most significant 15 bits of the number of backlight cycles in a + * complete cycle of the modulated backlight control. + * + * The actual value is this field multiplied by two. + */ +#define BACKLIGHT_MODULATION_FREQ_MASK (0x7fff << 17) +#define BLM_LEGACY_MODE (1 << 16) +/* + * This is the number of cycles out of the backlight modulation cycle for which + * the backlight is on. + * + * This field must be no greater than the number of cycles in the complete + * backlight modulation cycle. + */ +#define BACKLIGHT_DUTY_CYCLE_SHIFT (0) +#define BACKLIGHT_DUTY_CYCLE_MASK (0xffff) + +#define BLC_HIST_CTL 0x61260 + +/* TV port control */ +#define TV_CTL 0x68000 +/** Enables the TV encoder */ +# define TV_ENC_ENABLE (1 << 31) +/** Sources the TV encoder input from pipe B instead of A. */ +# define TV_ENC_PIPEB_SELECT (1 << 30) +/** Outputs composite video (DAC A only) */ +# define TV_ENC_OUTPUT_COMPOSITE (0 << 28) +/** Outputs SVideo video (DAC B/C) */ +# define TV_ENC_OUTPUT_SVIDEO (1 << 28) +/** Outputs Component video (DAC A/B/C) */ +# define TV_ENC_OUTPUT_COMPONENT (2 << 28) +/** Outputs Composite and SVideo (DAC A/B/C) */ +# define TV_ENC_OUTPUT_SVIDEO_COMPOSITE (3 << 28) +# define TV_TRILEVEL_SYNC (1 << 21) +/** Enables slow sync generation (945GM only) */ +# define TV_SLOW_SYNC (1 << 20) +/** Selects 4x oversampling for 480i and 576p */ +# define TV_OVERSAMPLE_4X (0 << 18) +/** Selects 2x oversampling for 720p and 1080i */ +# define TV_OVERSAMPLE_2X (1 << 18) +/** Selects no oversampling for 1080p */ +# define TV_OVERSAMPLE_NONE (2 << 18) +/** Selects 8x oversampling */ +# define TV_OVERSAMPLE_8X (3 << 18) +/** Selects progressive mode rather than interlaced */ +# define TV_PROGRESSIVE (1 << 17) +/** Sets the colorburst to PAL mode. Required for non-M PAL modes. */ +# define TV_PAL_BURST (1 << 16) +/** Field for setting delay of Y compared to C */ +# define TV_YC_SKEW_MASK (7 << 12) +/** Enables a fix for 480p/576p standard definition modes on the 915GM only */ +# define TV_ENC_SDP_FIX (1 << 11) +/** + * Enables a fix for the 915GM only. + * + * Not sure what it does. + */ +# define TV_ENC_C0_FIX (1 << 10) +/** Bits that must be preserved by software */ +# define TV_CTL_SAVE ((3 << 8) | (3 << 6)) +# define TV_FUSE_STATE_MASK (3 << 4) +/** Read-only state that reports all features enabled */ +# define TV_FUSE_STATE_ENABLED (0 << 4) +/** Read-only state that reports that Macrovision is disabled in hardware*/ +# define TV_FUSE_STATE_NO_MACROVISION (1 << 4) +/** Read-only state that reports that TV-out is disabled in hardware. */ +# define TV_FUSE_STATE_DISABLED (2 << 4) +/** Normal operation */ +# define TV_TEST_MODE_NORMAL (0 << 0) +/** Encoder test pattern 1 - combo pattern */ +# define TV_TEST_MODE_PATTERN_1 (1 << 0) +/** Encoder test pattern 2 - full screen vertical 75% color bars */ +# define TV_TEST_MODE_PATTERN_2 (2 << 0) +/** Encoder test pattern 3 - full screen horizontal 75% color bars */ +# define TV_TEST_MODE_PATTERN_3 (3 << 0) +/** Encoder test pattern 4 - random noise */ +# define TV_TEST_MODE_PATTERN_4 (4 << 0) +/** Encoder test pattern 5 - linear color ramps */ +# define TV_TEST_MODE_PATTERN_5 (5 << 0) +/** + * This test mode forces the DACs to 50% of full output. + * + * This is used for load detection in combination with TVDAC_SENSE_MASK + */ +# define TV_TEST_MODE_MONITOR_DETECT (7 << 0) +# define TV_TEST_MODE_MASK (7 << 0) + +#define TV_DAC 0x68004 +/** + * Reports that DAC state change logic has reported change (RO). + * + * This gets cleared when TV_DAC_STATE_EN is cleared +*/ +# define TVDAC_STATE_CHG (1 << 31) +# define TVDAC_SENSE_MASK (7 << 28) +/** Reports that DAC A voltage is above the detect threshold */ +# define TVDAC_A_SENSE (1 << 30) +/** Reports that DAC B voltage is above the detect threshold */ +# define TVDAC_B_SENSE (1 << 29) +/** Reports that DAC C voltage is above the detect threshold */ +# define TVDAC_C_SENSE (1 << 28) +/** + * Enables DAC state detection logic, for load-based TV detection. + * + * The PLL of the chosen pipe (in TV_CTL) must be running, and the encoder set + * to off, for load detection to work. + */ +# define TVDAC_STATE_CHG_EN (1 << 27) +/** Sets the DAC A sense value to high */ +# define TVDAC_A_SENSE_CTL (1 << 26) +/** Sets the DAC B sense value to high */ +# define TVDAC_B_SENSE_CTL (1 << 25) +/** Sets the DAC C sense value to high */ +# define TVDAC_C_SENSE_CTL (1 << 24) +/** Overrides the ENC_ENABLE and DAC voltage levels */ +# define DAC_CTL_OVERRIDE (1 << 7) +/** Sets the slew rate. Must be preserved in software */ +# define ENC_TVDAC_SLEW_FAST (1 << 6) +# define DAC_A_1_3_V (0 << 4) +# define DAC_A_1_1_V (1 << 4) +# define DAC_A_0_7_V (2 << 4) +# define DAC_A_OFF (3 << 4) +# define DAC_B_1_3_V (0 << 2) +# define DAC_B_1_1_V (1 << 2) +# define DAC_B_0_7_V (2 << 2) +# define DAC_B_OFF (3 << 2) +# define DAC_C_1_3_V (0 << 0) +# define DAC_C_1_1_V (1 << 0) +# define DAC_C_0_7_V (2 << 0) +# define DAC_C_OFF (3 << 0) + +/** + * CSC coefficients are stored in a floating point format with 9 bits of + * mantissa and 2 or 3 bits of exponent. The exponent is represented as 2**-n, + * where 2-bit exponents are unsigned n, and 3-bit exponents are signed n with + * -1 (0x3) being the only legal negative value. + */ +#define TV_CSC_Y 0x68010 +# define TV_RY_MASK 0x07ff0000 +# define TV_RY_SHIFT 16 +# define TV_GY_MASK 0x00000fff +# define TV_GY_SHIFT 0 + +#define TV_CSC_Y2 0x68014 +# define TV_BY_MASK 0x07ff0000 +# define TV_BY_SHIFT 16 +/** + * Y attenuation for component video. + * + * Stored in 1.9 fixed point. + */ +# define TV_AY_MASK 0x000003ff +# define TV_AY_SHIFT 0 + +#define TV_CSC_U 0x68018 +# define TV_RU_MASK 0x07ff0000 +# define TV_RU_SHIFT 16 +# define TV_GU_MASK 0x000007ff +# define TV_GU_SHIFT 0 + +#define TV_CSC_U2 0x6801c +# define TV_BU_MASK 0x07ff0000 +# define TV_BU_SHIFT 16 +/** + * U attenuation for component video. + * + * Stored in 1.9 fixed point. + */ +# define TV_AU_MASK 0x000003ff +# define TV_AU_SHIFT 0 + +#define TV_CSC_V 0x68020 +# define TV_RV_MASK 0x0fff0000 +# define TV_RV_SHIFT 16 +# define TV_GV_MASK 0x000007ff +# define TV_GV_SHIFT 0 + +#define TV_CSC_V2 0x68024 +# define TV_BV_MASK 0x07ff0000 +# define TV_BV_SHIFT 16 +/** + * V attenuation for component video. + * + * Stored in 1.9 fixed point. + */ +# define TV_AV_MASK 0x000007ff +# define TV_AV_SHIFT 0 + +#define TV_CLR_KNOBS 0x68028 +/** 2s-complement brightness adjustment */ +# define TV_BRIGHTNESS_MASK 0xff000000 +# define TV_BRIGHTNESS_SHIFT 24 +/** Contrast adjustment, as a 2.6 unsigned floating point number */ +# define TV_CONTRAST_MASK 0x00ff0000 +# define TV_CONTRAST_SHIFT 16 +/** Saturation adjustment, as a 2.6 unsigned floating point number */ +# define TV_SATURATION_MASK 0x0000ff00 +# define TV_SATURATION_SHIFT 8 +/** Hue adjustment, as an integer phase angle in degrees */ +# define TV_HUE_MASK 0x000000ff +# define TV_HUE_SHIFT 0 + +#define TV_CLR_LEVEL 0x6802c +/** Controls the DAC level for black */ +# define TV_BLACK_LEVEL_MASK 0x01ff0000 +# define TV_BLACK_LEVEL_SHIFT 16 +/** Controls the DAC level for blanking */ +# define TV_BLANK_LEVEL_MASK 0x000001ff +# define TV_BLANK_LEVEL_SHIFT 0 + +#define TV_H_CTL_1 0x68030 +/** Number of pixels in the hsync. */ +# define TV_HSYNC_END_MASK 0x1fff0000 +# define TV_HSYNC_END_SHIFT 16 +/** Total number of pixels minus one in the line (display and blanking). */ +# define TV_HTOTAL_MASK 0x00001fff +# define TV_HTOTAL_SHIFT 0 + +#define TV_H_CTL_2 0x68034 +/** Enables the colorburst (needed for non-component color) */ +# define TV_BURST_ENA (1 << 31) +/** Offset of the colorburst from the start of hsync, in pixels minus one. */ +# define TV_HBURST_START_SHIFT 16 +# define TV_HBURST_START_MASK 0x1fff0000 +/** Length of the colorburst */ +# define TV_HBURST_LEN_SHIFT 0 +# define TV_HBURST_LEN_MASK 0x0001fff + +#define TV_H_CTL_3 0x68038 +/** End of hblank, measured in pixels minus one from start of hsync */ +# define TV_HBLANK_END_SHIFT 16 +# define TV_HBLANK_END_MASK 0x1fff0000 +/** Start of hblank, measured in pixels minus one from start of hsync */ +# define TV_HBLANK_START_SHIFT 0 +# define TV_HBLANK_START_MASK 0x0001fff + +#define TV_V_CTL_1 0x6803c +/** XXX */ +# define TV_NBR_END_SHIFT 16 +# define TV_NBR_END_MASK 0x07ff0000 +/** XXX */ +# define TV_VI_END_F1_SHIFT 8 +# define TV_VI_END_F1_MASK 0x00003f00 +/** XXX */ +# define TV_VI_END_F2_SHIFT 0 +# define TV_VI_END_F2_MASK 0x0000003f + +#define TV_V_CTL_2 0x68040 +/** Length of vsync, in half lines */ +# define TV_VSYNC_LEN_MASK 0x07ff0000 +# define TV_VSYNC_LEN_SHIFT 16 +/** Offset of the start of vsync in field 1, measured in one less than the + * number of half lines. + */ +# define TV_VSYNC_START_F1_MASK 0x00007f00 +# define TV_VSYNC_START_F1_SHIFT 8 +/** + * Offset of the start of vsync in field 2, measured in one less than the + * number of half lines. + */ +# define TV_VSYNC_START_F2_MASK 0x0000007f +# define TV_VSYNC_START_F2_SHIFT 0 + +#define TV_V_CTL_3 0x68044 +/** Enables generation of the equalization signal */ +# define TV_EQUAL_ENA (1 << 31) +/** Length of vsync, in half lines */ +# define TV_VEQ_LEN_MASK 0x007f0000 +# define TV_VEQ_LEN_SHIFT 16 +/** Offset of the start of equalization in field 1, measured in one less than + * the number of half lines. + */ +# define TV_VEQ_START_F1_MASK 0x0007f00 +# define TV_VEQ_START_F1_SHIFT 8 +/** + * Offset of the start of equalization in field 2, measured in one less than + * the number of half lines. + */ +# define TV_VEQ_START_F2_MASK 0x000007f +# define TV_VEQ_START_F2_SHIFT 0 + +#define TV_V_CTL_4 0x68048 +/** + * Offset to start of vertical colorburst, measured in one less than the + * number of lines from vertical start. + */ +# define TV_VBURST_START_F1_MASK 0x003f0000 +# define TV_VBURST_START_F1_SHIFT 16 +/** + * Offset to the end of vertical colorburst, measured in one less than the + * number of lines from the start of NBR. + */ +# define TV_VBURST_END_F1_MASK 0x000000ff +# define TV_VBURST_END_F1_SHIFT 0 + +#define TV_V_CTL_5 0x6804c +/** + * Offset to start of vertical colorburst, measured in one less than the + * number of lines from vertical start. + */ +# define TV_VBURST_START_F2_MASK 0x003f0000 +# define TV_VBURST_START_F2_SHIFT 16 +/** + * Offset to the end of vertical colorburst, measured in one less than the + * number of lines from the start of NBR. + */ +# define TV_VBURST_END_F2_MASK 0x000000ff +# define TV_VBURST_END_F2_SHIFT 0 + +#define TV_V_CTL_6 0x68050 +/** + * Offset to start of vertical colorburst, measured in one less than the + * number of lines from vertical start. + */ +# define TV_VBURST_START_F3_MASK 0x003f0000 +# define TV_VBURST_START_F3_SHIFT 16 +/** + * Offset to the end of vertical colorburst, measured in one less than the + * number of lines from the start of NBR. + */ +# define TV_VBURST_END_F3_MASK 0x000000ff +# define TV_VBURST_END_F3_SHIFT 0 + +#define TV_V_CTL_7 0x68054 +/** + * Offset to start of vertical colorburst, measured in one less than the + * number of lines from vertical start. + */ +# define TV_VBURST_START_F4_MASK 0x003f0000 +# define TV_VBURST_START_F4_SHIFT 16 +/** + * Offset to the end of vertical colorburst, measured in one less than the + * number of lines from the start of NBR. + */ +# define TV_VBURST_END_F4_MASK 0x000000ff +# define TV_VBURST_END_F4_SHIFT 0 + +#define TV_SC_CTL_1 0x68060 +/** Turns on the first subcarrier phase generation DDA */ +# define TV_SC_DDA1_EN (1 << 31) +/** Turns on the first subcarrier phase generation DDA */ +# define TV_SC_DDA2_EN (1 << 30) +/** Turns on the first subcarrier phase generation DDA */ +# define TV_SC_DDA3_EN (1 << 29) +/** Sets the subcarrier DDA to reset frequency every other field */ +# define TV_SC_RESET_EVERY_2 (0 << 24) +/** Sets the subcarrier DDA to reset frequency every fourth field */ +# define TV_SC_RESET_EVERY_4 (1 << 24) +/** Sets the subcarrier DDA to reset frequency every eighth field */ +# define TV_SC_RESET_EVERY_8 (2 << 24) +/** Sets the subcarrier DDA to never reset the frequency */ +# define TV_SC_RESET_NEVER (3 << 24) +/** Sets the peak amplitude of the colorburst.*/ +# define TV_BURST_LEVEL_MASK 0x00ff0000 +# define TV_BURST_LEVEL_SHIFT 16 +/** Sets the increment of the first subcarrier phase generation DDA */ +# define TV_SCDDA1_INC_MASK 0x00000fff +# define TV_SCDDA1_INC_SHIFT 0 + +#define TV_SC_CTL_2 0x68064 +/** Sets the rollover for the second subcarrier phase generation DDA */ +# define TV_SCDDA2_SIZE_MASK 0x7fff0000 +# define TV_SCDDA2_SIZE_SHIFT 16 +/** Sets the increent of the second subcarrier phase generation DDA */ +# define TV_SCDDA2_INC_MASK 0x00007fff +# define TV_SCDDA2_INC_SHIFT 0 + +#define TV_SC_CTL_3 0x68068 +/** Sets the rollover for the third subcarrier phase generation DDA */ +# define TV_SCDDA3_SIZE_MASK 0x7fff0000 +# define TV_SCDDA3_SIZE_SHIFT 16 +/** Sets the increent of the third subcarrier phase generation DDA */ +# define TV_SCDDA3_INC_MASK 0x00007fff +# define TV_SCDDA3_INC_SHIFT 0 + +#define TV_WIN_POS 0x68070 +/** X coordinate of the display from the start of horizontal active */ +# define TV_XPOS_MASK 0x1fff0000 +# define TV_XPOS_SHIFT 16 +/** Y coordinate of the display from the start of vertical active (NBR) */ +# define TV_YPOS_MASK 0x00000fff +# define TV_YPOS_SHIFT 0 + +#define TV_WIN_SIZE 0x68074 +/** Horizontal size of the display window, measured in pixels*/ +# define TV_XSIZE_MASK 0x1fff0000 +# define TV_XSIZE_SHIFT 16 +/** + * Vertical size of the display window, measured in pixels. + * + * Must be even for interlaced modes. + */ +# define TV_YSIZE_MASK 0x00000fff +# define TV_YSIZE_SHIFT 0 + +#define TV_FILTER_CTL_1 0x68080 +/** + * Enables automatic scaling calculation. + * + * If set, the rest of the registers are ignored, and the calculated values can + * be read back from the register. + */ +# define TV_AUTO_SCALE (1 << 31) +/** + * Disables the vertical filter. + * + * This is required on modes more than 1024 pixels wide */ +# define TV_V_FILTER_BYPASS (1 << 29) +/** Enables adaptive vertical filtering */ +# define TV_VADAPT (1 << 28) +# define TV_VADAPT_MODE_MASK (3 << 26) +/** Selects the least adaptive vertical filtering mode */ +# define TV_VADAPT_MODE_LEAST (0 << 26) +/** Selects the moderately adaptive vertical filtering mode */ +# define TV_VADAPT_MODE_MODERATE (1 << 26) +/** Selects the most adaptive vertical filtering mode */ +# define TV_VADAPT_MODE_MOST (3 << 26) +/** + * Sets the horizontal scaling factor. + * + * This should be the fractional part of the horizontal scaling factor divided + * by the oversampling rate. TV_HSCALE should be less than 1, and set to: + * + * (src width - 1) / ((oversample * dest width) - 1) + */ +# define TV_HSCALE_FRAC_MASK 0x00003fff +# define TV_HSCALE_FRAC_SHIFT 0 + +#define TV_FILTER_CTL_2 0x68084 +/** + * Sets the integer part of the 3.15 fixed-point vertical scaling factor. + * + * TV_VSCALE should be (src height - 1) / ((interlace * dest height) - 1) + */ +# define TV_VSCALE_INT_MASK 0x00038000 +# define TV_VSCALE_INT_SHIFT 15 +/** + * Sets the fractional part of the 3.15 fixed-point vertical scaling factor. + * + * \sa TV_VSCALE_INT_MASK + */ +# define TV_VSCALE_FRAC_MASK 0x00007fff +# define TV_VSCALE_FRAC_SHIFT 0 + +#define TV_FILTER_CTL_3 0x68088 +/** + * Sets the integer part of the 3.15 fixed-point vertical scaling factor. + * + * TV_VSCALE should be (src height - 1) / (1/4 * (dest height - 1)) + * + * For progressive modes, TV_VSCALE_IP_INT should be set to zeroes. + */ +# define TV_VSCALE_IP_INT_MASK 0x00038000 +# define TV_VSCALE_IP_INT_SHIFT 15 +/** + * Sets the fractional part of the 3.15 fixed-point vertical scaling factor. + * + * For progressive modes, TV_VSCALE_IP_INT should be set to zeroes. + * + * \sa TV_VSCALE_IP_INT_MASK + */ +# define TV_VSCALE_IP_FRAC_MASK 0x00007fff +# define TV_VSCALE_IP_FRAC_SHIFT 0 + +#define TV_CC_CONTROL 0x68090 +# define TV_CC_ENABLE (1 << 31) +/** + * Specifies which field to send the CC data in. + * + * CC data is usually sent in field 0. + */ +# define TV_CC_FID_MASK (1 << 27) +# define TV_CC_FID_SHIFT 27 +/** Sets the horizontal position of the CC data. Usually 135. */ +# define TV_CC_HOFF_MASK 0x03ff0000 +# define TV_CC_HOFF_SHIFT 16 +/** Sets the vertical position of the CC data. Usually 21 */ +# define TV_CC_LINE_MASK 0x0000003f +# define TV_CC_LINE_SHIFT 0 + +#define TV_CC_DATA 0x68094 +# define TV_CC_RDY (1 << 31) +/** Second word of CC data to be transmitted. */ +# define TV_CC_DATA_2_MASK 0x007f0000 +# define TV_CC_DATA_2_SHIFT 16 +/** First word of CC data to be transmitted. */ +# define TV_CC_DATA_1_MASK 0x0000007f +# define TV_CC_DATA_1_SHIFT 0 + +#define TV_H_LUMA_0 0x68100 +#define TV_H_LUMA_59 0x681ec +#define TV_H_CHROMA_0 0x68200 +#define TV_H_CHROMA_59 0x682ec +#define TV_V_LUMA_0 0x68300 +#define TV_V_LUMA_42 0x683a8 +#define TV_V_CHROMA_0 0x68400 +#define TV_V_CHROMA_42 0x684a8 + +/* Display Port */ +#define DP_A 0x64000 /* eDP */ +#define DP_B 0x64100 +#define DP_C 0x64200 +#define DP_D 0x64300 + +#define DP_PORT_EN (1 << 31) +#define DP_PIPEB_SELECT (1 << 30) + +/* Link training mode - select a suitable mode for each stage */ +#define DP_LINK_TRAIN_PAT_1 (0 << 28) +#define DP_LINK_TRAIN_PAT_2 (1 << 28) +#define DP_LINK_TRAIN_PAT_IDLE (2 << 28) +#define DP_LINK_TRAIN_OFF (3 << 28) +#define DP_LINK_TRAIN_MASK (3 << 28) +#define DP_LINK_TRAIN_SHIFT 28 + +/* Signal voltages. These are mostly controlled by the other end */ +#define DP_VOLTAGE_0_4 (0 << 25) +#define DP_VOLTAGE_0_6 (1 << 25) +#define DP_VOLTAGE_0_8 (2 << 25) +#define DP_VOLTAGE_1_2 (3 << 25) +#define DP_VOLTAGE_MASK (7 << 25) +#define DP_VOLTAGE_SHIFT 25 + +/* Signal pre-emphasis levels, like voltages, the other end tells us what + * they want + */ +#define DP_PRE_EMPHASIS_0 (0 << 22) +#define DP_PRE_EMPHASIS_3_5 (1 << 22) +#define DP_PRE_EMPHASIS_6 (2 << 22) +#define DP_PRE_EMPHASIS_9_5 (3 << 22) +#define DP_PRE_EMPHASIS_MASK (7 << 22) +#define DP_PRE_EMPHASIS_SHIFT 22 + +/* How many wires to use. I guess 3 was too hard */ +#define DP_PORT_WIDTH_1 (0 << 19) +#define DP_PORT_WIDTH_2 (1 << 19) +#define DP_PORT_WIDTH_4 (3 << 19) +#define DP_PORT_WIDTH_MASK (7 << 19) + +/* Mystic DPCD version 1.1 special mode */ +#define DP_ENHANCED_FRAMING (1 << 18) + +/* eDP */ +#define DP_PLL_FREQ_270MHZ (0 << 16) +#define DP_PLL_FREQ_160MHZ (1 << 16) +#define DP_PLL_FREQ_MASK (3 << 16) + +/** locked once port is enabled */ +#define DP_PORT_REVERSAL (1 << 15) + +/* eDP */ +#define DP_PLL_ENABLE (1 << 14) + +/** sends the clock on lane 15 of the PEG for debug */ +#define DP_CLOCK_OUTPUT_ENABLE (1 << 13) + +#define DP_SCRAMBLING_DISABLE (1 << 12) +#define DP_SCRAMBLING_DISABLE_IGDNG (1 << 7) + +/** limit RGB values to avoid confusing TVs */ +#define DP_COLOR_RANGE_16_235 (1 << 8) + +/** Turn on the audio link */ +#define DP_AUDIO_OUTPUT_ENABLE (1 << 6) + +/** vs and hs sync polarity */ +#define DP_SYNC_VS_HIGH (1 << 4) +#define DP_SYNC_HS_HIGH (1 << 3) + +/** A fantasy */ +#define DP_DETECTED (1 << 2) + +/** The aux channel provides a way to talk to the + * signal sink for DDC etc. Max packet size supported + * is 20 bytes in each direction, hence the 5 fixed + * data registers + */ +#define DPA_AUX_CH_CTL 0x64010 +#define DPA_AUX_CH_DATA1 0x64014 +#define DPA_AUX_CH_DATA2 0x64018 +#define DPA_AUX_CH_DATA3 0x6401c +#define DPA_AUX_CH_DATA4 0x64020 +#define DPA_AUX_CH_DATA5 0x64024 + +#define DPB_AUX_CH_CTL 0x64110 +#define DPB_AUX_CH_DATA1 0x64114 +#define DPB_AUX_CH_DATA2 0x64118 +#define DPB_AUX_CH_DATA3 0x6411c +#define DPB_AUX_CH_DATA4 0x64120 +#define DPB_AUX_CH_DATA5 0x64124 + +#define DPC_AUX_CH_CTL 0x64210 +#define DPC_AUX_CH_DATA1 0x64214 +#define DPC_AUX_CH_DATA2 0x64218 +#define DPC_AUX_CH_DATA3 0x6421c +#define DPC_AUX_CH_DATA4 0x64220 +#define DPC_AUX_CH_DATA5 0x64224 + +#define DPD_AUX_CH_CTL 0x64310 +#define DPD_AUX_CH_DATA1 0x64314 +#define DPD_AUX_CH_DATA2 0x64318 +#define DPD_AUX_CH_DATA3 0x6431c +#define DPD_AUX_CH_DATA4 0x64320 +#define DPD_AUX_CH_DATA5 0x64324 + +#define DP_AUX_CH_CTL_SEND_BUSY (1 << 31) +#define DP_AUX_CH_CTL_DONE (1 << 30) +#define DP_AUX_CH_CTL_INTERRUPT (1 << 29) +#define DP_AUX_CH_CTL_TIME_OUT_ERROR (1 << 28) +#define DP_AUX_CH_CTL_TIME_OUT_400us (0 << 26) +#define DP_AUX_CH_CTL_TIME_OUT_600us (1 << 26) +#define DP_AUX_CH_CTL_TIME_OUT_800us (2 << 26) +#define DP_AUX_CH_CTL_TIME_OUT_1600us (3 << 26) +#define DP_AUX_CH_CTL_TIME_OUT_MASK (3 << 26) +#define DP_AUX_CH_CTL_RECEIVE_ERROR (1 << 25) +#define DP_AUX_CH_CTL_MESSAGE_SIZE_MASK (0x1f << 20) +#define DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT 20 +#define DP_AUX_CH_CTL_PRECHARGE_2US_MASK (0xf << 16) +#define DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT 16 +#define DP_AUX_CH_CTL_AUX_AKSV_SELECT (1 << 15) +#define DP_AUX_CH_CTL_MANCHESTER_TEST (1 << 14) +#define DP_AUX_CH_CTL_SYNC_TEST (1 << 13) +#define DP_AUX_CH_CTL_DEGLITCH_TEST (1 << 12) +#define DP_AUX_CH_CTL_PRECHARGE_TEST (1 << 11) +#define DP_AUX_CH_CTL_BIT_CLOCK_2X_MASK (0x7ff) +#define DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT 0 + +/* + * Computing GMCH M and N values for the Display Port link + * + * GMCH M/N = dot clock * bytes per pixel / ls_clk * # of lanes + * + * ls_clk (we assume) is the DP link clock (1.62 or 2.7 GHz) + * + * The GMCH value is used internally + * + * bytes_per_pixel is the number of bytes coming out of the plane, + * which is after the LUTs, so we want the bytes for our color format. + * For our current usage, this is always 3, one byte for R, G and B. + */ +#define PIPEA_GMCH_DATA_M 0x70050 +#define PIPEB_GMCH_DATA_M 0x71050 + +/* Transfer unit size for display port - 1, default is 0x3f (for TU size 64) */ +#define PIPE_GMCH_DATA_M_TU_SIZE_MASK (0x3f << 25) +#define PIPE_GMCH_DATA_M_TU_SIZE_SHIFT 25 + +#define PIPE_GMCH_DATA_M_MASK (0xffffff) + +#define PIPEA_GMCH_DATA_N 0x70054 +#define PIPEB_GMCH_DATA_N 0x71054 +#define PIPE_GMCH_DATA_N_MASK (0xffffff) + +/* + * Computing Link M and N values for the Display Port link + * + * Link M / N = pixel_clock / ls_clk + * + * (the DP spec calls pixel_clock the 'strm_clk') + * + * The Link value is transmitted in the Main Stream + * Attributes and VB-ID. + */ + +#define PIPEA_DP_LINK_M 0x70060 +#define PIPEB_DP_LINK_M 0x71060 +#define PIPEA_DP_LINK_M_MASK (0xffffff) + +#define PIPEA_DP_LINK_N 0x70064 +#define PIPEB_DP_LINK_N 0x71064 +#define PIPEA_DP_LINK_N_MASK (0xffffff) + +/* Display & cursor control */ + +/* Pipe A */ +#define PIPEADSL 0x70000 +#define PIPEACONF 0x70008 +#define PIPEACONF_ENABLE (1<<31) +#define PIPEACONF_DISABLE 0 +#define PIPEACONF_DOUBLE_WIDE (1<<30) +#define I965_PIPECONF_ACTIVE (1<<30) +#define PIPEACONF_SINGLE_WIDE 0 +#define PIPEACONF_PIPE_UNLOCKED 0 +#define PIPEACONF_PIPE_LOCKED (1<<25) +#define PIPEACONF_PALETTE 0 +#define PIPEACONF_GAMMA (1<<24) +#define PIPECONF_FORCE_BORDER (1<<25) +#define PIPECONF_PROGRESSIVE (0 << 21) +#define PIPECONF_INTERLACE_W_FIELD_INDICATION (6 << 21) +#define PIPECONF_INTERLACE_FIELD_0_ONLY (7 << 21) +#define PIPEASTAT 0x70024 +#define PIPE_FIFO_UNDERRUN_STATUS (1UL<<31) +#define PIPE_CRC_ERROR_ENABLE (1UL<<29) +#define PIPE_CRC_DONE_ENABLE (1UL<<28) +#define PIPE_GMBUS_EVENT_ENABLE (1UL<<27) +#define PIPE_HOTPLUG_INTERRUPT_ENABLE (1UL<<26) +#define PIPE_VSYNC_INTERRUPT_ENABLE (1UL<<25) +#define PIPE_DISPLAY_LINE_COMPARE_ENABLE (1UL<<24) +#define PIPE_DPST_EVENT_ENABLE (1UL<<23) +#define PIPE_LEGACY_BLC_EVENT_ENABLE (1UL<<22) +#define PIPE_ODD_FIELD_INTERRUPT_ENABLE (1UL<<21) +#define PIPE_EVEN_FIELD_INTERRUPT_ENABLE (1UL<<20) +#define PIPE_HOTPLUG_TV_INTERRUPT_ENABLE (1UL<<18) /* pre-965 */ +#define PIPE_START_VBLANK_INTERRUPT_ENABLE (1UL<<18) /* 965 or later */ +#define PIPE_VBLANK_INTERRUPT_ENABLE (1UL<<17) +#define PIPE_OVERLAY_UPDATED_ENABLE (1UL<<16) +#define PIPE_CRC_ERROR_INTERRUPT_STATUS (1UL<<13) +#define PIPE_CRC_DONE_INTERRUPT_STATUS (1UL<<12) +#define PIPE_GMBUS_INTERRUPT_STATUS (1UL<<11) +#define PIPE_HOTPLUG_INTERRUPT_STATUS (1UL<<10) +#define PIPE_VSYNC_INTERRUPT_STATUS (1UL<<9) +#define PIPE_DISPLAY_LINE_COMPARE_STATUS (1UL<<8) +#define PIPE_DPST_EVENT_STATUS (1UL<<7) +#define PIPE_LEGACY_BLC_EVENT_STATUS (1UL<<6) +#define PIPE_ODD_FIELD_INTERRUPT_STATUS (1UL<<5) +#define PIPE_EVEN_FIELD_INTERRUPT_STATUS (1UL<<4) +#define PIPE_HOTPLUG_TV_INTERRUPT_STATUS (1UL<<2) /* pre-965 */ +#define PIPE_START_VBLANK_INTERRUPT_STATUS (1UL<<2) /* 965 or later */ +#define PIPE_VBLANK_INTERRUPT_STATUS (1UL<<1) +#define PIPE_OVERLAY_UPDATED_STATUS (1UL<<0) + +#define DSPARB 0x70030 +#define DSPARB_CSTART_MASK (0x7f << 7) +#define DSPARB_CSTART_SHIFT 7 +#define DSPARB_BSTART_MASK (0x7f) +#define DSPARB_BSTART_SHIFT 0 +/* + * The two pipe frame counter registers are not synchronized, so + * reading a stable value is somewhat tricky. The following code + * should work: + * + * do { + * high1 = ((INREG(PIPEAFRAMEHIGH) & PIPE_FRAME_HIGH_MASK) >> + * PIPE_FRAME_HIGH_SHIFT; + * low1 = ((INREG(PIPEAFRAMEPIXEL) & PIPE_FRAME_LOW_MASK) >> + * PIPE_FRAME_LOW_SHIFT); + * high2 = ((INREG(PIPEAFRAMEHIGH) & PIPE_FRAME_HIGH_MASK) >> + * PIPE_FRAME_HIGH_SHIFT); + * } while (high1 != high2); + * frame = (high1 << 8) | low1; + */ +#define PIPEAFRAMEHIGH 0x70040 +#define PIPE_FRAME_HIGH_MASK 0x0000ffff +#define PIPE_FRAME_HIGH_SHIFT 0 +#define PIPEAFRAMEPIXEL 0x70044 +#define PIPE_FRAME_LOW_MASK 0xff000000 +#define PIPE_FRAME_LOW_SHIFT 24 +#define PIPE_PIXEL_MASK 0x00ffffff +#define PIPE_PIXEL_SHIFT 0 +/* GM45+ just has to be different */ +#define PIPEA_FRMCOUNT_GM45 0x70040 +#define PIPEA_FLIPCOUNT_GM45 0x70044 + +/* Cursor A & B regs */ +#define CURACNTR 0x70080 +#define CURSOR_MODE_DISABLE 0x00 +#define CURSOR_MODE_64_32B_AX 0x07 +#define CURSOR_MODE_64_ARGB_AX ((1 << 5) | CURSOR_MODE_64_32B_AX) +#define MCURSOR_GAMMA_ENABLE (1 << 26) +#define CURABASE 0x70084 +#define CURAPOS 0x70088 +#define CURSOR_POS_MASK 0x007FF +#define CURSOR_POS_SIGN 0x8000 +#define CURSOR_X_SHIFT 0 +#define CURSOR_Y_SHIFT 16 +#define CURSIZE 0x700a0 +#define CURBCNTR 0x700c0 +#define CURBBASE 0x700c4 +#define CURBPOS 0x700c8 + +/* Display A control */ +#define DSPACNTR 0x70180 +#define DISPLAY_PLANE_ENABLE (1<<31) +#define DISPLAY_PLANE_DISABLE 0 +#define DISPPLANE_GAMMA_ENABLE (1<<30) +#define DISPPLANE_GAMMA_DISABLE 0 +#define DISPPLANE_PIXFORMAT_MASK (0xf<<26) +#define DISPPLANE_8BPP (0x2<<26) +#define DISPPLANE_15_16BPP (0x4<<26) +#define DISPPLANE_16BPP (0x5<<26) +#define DISPPLANE_32BPP_NO_ALPHA (0x6<<26) +#define DISPPLANE_32BPP (0x7<<26) +#define DISPPLANE_STEREO_ENABLE (1<<25) +#define DISPPLANE_STEREO_DISABLE 0 +#define DISPPLANE_SEL_PIPE_MASK (1<<24) +#define DISPPLANE_SEL_PIPE_A 0 +#define DISPPLANE_SEL_PIPE_B (1<<24) +#define DISPPLANE_SRC_KEY_ENABLE (1<<22) +#define DISPPLANE_SRC_KEY_DISABLE 0 +#define DISPPLANE_LINE_DOUBLE (1<<20) +#define DISPPLANE_NO_LINE_DOUBLE 0 +#define DISPPLANE_STEREO_POLARITY_FIRST 0 +#define DISPPLANE_STEREO_POLARITY_SECOND (1<<18) +#define DSPAADDR 0x70184 +#define DSPASTRIDE 0x70188 +#define DSPAPOS 0x7018C /* reserved */ +#define DSPASIZE 0x70190 +#define DSPASURF 0x7019C /* 965+ only */ +#define DSPATILEOFF 0x701A4 /* 965+ only */ + +/* VBIOS flags */ +#define SWF00 0x71410 +#define SWF01 0x71414 +#define SWF02 0x71418 +#define SWF03 0x7141c +#define SWF04 0x71420 +#define SWF05 0x71424 +#define SWF06 0x71428 +#define SWF10 0x70410 +#define SWF11 0x70414 +#define SWF14 0x71420 +#define SWF30 0x72414 +#define SWF31 0x72418 +#define SWF32 0x7241c + +/* Pipe B */ +#define PIPEBDSL 0x71000 +#define PIPEBCONF 0x71008 +#define PIPEBSTAT 0x71024 +#define PIPEBFRAMEHIGH 0x71040 +#define PIPEBFRAMEPIXEL 0x71044 +#define PIPEB_FRMCOUNT_GM45 0x71040 +#define PIPEB_FLIPCOUNT_GM45 0x71044 + +/* Display B control */ +#define DSPBCNTR 0x71180 +#define DISPPLANE_ALPHA_TRANS_ENABLE (1<<15) +#define DISPPLANE_ALPHA_TRANS_DISABLE 0 +#define DISPPLANE_SPRITE_ABOVE_DISPLAY 0 +#define DISPPLANE_SPRITE_ABOVE_OVERLAY (1) +#define DSPBADDR 0x71184 +#define DSPBSTRIDE 0x71188 +#define DSPBPOS 0x7118C +#define DSPBSIZE 0x71190 +#define DSPBSURF 0x7119C +#define DSPBTILEOFF 0x711A4 + +/* VBIOS regs */ +#define VGACNTRL 0x71400 +# define VGA_DISP_DISABLE (1 << 31) +# define VGA_2X_MODE (1 << 30) +# define VGA_PIPE_B_SELECT (1 << 29) + +/* Ironlake */ + +#define CPU_VGACNTRL 0x41000 + +#define DIGITAL_PORT_HOTPLUG_CNTRL 0x44030 +#define DIGITAL_PORTA_HOTPLUG_ENABLE (1 << 4) +#define DIGITAL_PORTA_SHORT_PULSE_2MS (0 << 2) +#define DIGITAL_PORTA_SHORT_PULSE_4_5MS (1 << 2) +#define DIGITAL_PORTA_SHORT_PULSE_6MS (2 << 2) +#define DIGITAL_PORTA_SHORT_PULSE_100MS (3 << 2) +#define DIGITAL_PORTA_NO_DETECT (0 << 0) +#define DIGITAL_PORTA_LONG_PULSE_DETECT_MASK (1 << 1) +#define DIGITAL_PORTA_SHORT_PULSE_DETECT_MASK (1 << 0) + +/* refresh rate hardware control */ +#define RR_HW_CTL 0x45300 +#define RR_HW_LOW_POWER_FRAMES_MASK 0xff +#define RR_HW_HIGH_POWER_FRAMES_MASK 0xff00 + +#define FDI_PLL_BIOS_0 0x46000 +#define FDI_PLL_BIOS_1 0x46004 +#define FDI_PLL_BIOS_2 0x46008 +#define DISPLAY_PORT_PLL_BIOS_0 0x4600c +#define DISPLAY_PORT_PLL_BIOS_1 0x46010 +#define DISPLAY_PORT_PLL_BIOS_2 0x46014 + +#define PCH_DSPCLK_GATE_D 0x42020 +# define DPFDUNIT_CLOCK_GATE_DISABLE (1 << 7) +# define DPARBUNIT_CLOCK_GATE_DISABLE (1 << 5) + +#define PCH_3DCGDIS0 0x46020 +# define MARIUNIT_CLOCK_GATE_DISABLE (1 << 18) +# define SVSMUNIT_CLOCK_GATE_DISABLE (1 << 1) + +#define FDI_PLL_FREQ_CTL 0x46030 +#define FDI_PLL_FREQ_CHANGE_REQUEST (1<<24) +#define FDI_PLL_FREQ_LOCK_LIMIT_MASK 0xfff00 +#define FDI_PLL_FREQ_DISABLE_COUNT_LIMIT_MASK 0xff + + +#define PIPEA_DATA_M1 0x60030 +#define TU_SIZE(x) (((x)-1) << 25) /* default size 64 */ +#define TU_SIZE_MASK 0x7e000000 +#define PIPEA_DATA_M1_OFFSET 0 +#define PIPEA_DATA_N1 0x60034 +#define PIPEA_DATA_N1_OFFSET 0 + +#define PIPEA_DATA_M2 0x60038 +#define PIPEA_DATA_M2_OFFSET 0 +#define PIPEA_DATA_N2 0x6003c +#define PIPEA_DATA_N2_OFFSET 0 + +#define PIPEA_LINK_M1 0x60040 +#define PIPEA_LINK_M1_OFFSET 0 +#define PIPEA_LINK_N1 0x60044 +#define PIPEA_LINK_N1_OFFSET 0 + +#define PIPEA_LINK_M2 0x60048 +#define PIPEA_LINK_M2_OFFSET 0 +#define PIPEA_LINK_N2 0x6004c +#define PIPEA_LINK_N2_OFFSET 0 + +/* PIPEB timing regs are same start from 0x61000 */ + +#define PIPEB_DATA_M1 0x61030 +#define PIPEB_DATA_M1_OFFSET 0 +#define PIPEB_DATA_N1 0x61034 +#define PIPEB_DATA_N1_OFFSET 0 + +#define PIPEB_DATA_M2 0x61038 +#define PIPEB_DATA_M2_OFFSET 0 +#define PIPEB_DATA_N2 0x6103c +#define PIPEB_DATA_N2_OFFSET 0 + +#define PIPEB_LINK_M1 0x61040 +#define PIPEB_LINK_M1_OFFSET 0 +#define PIPEB_LINK_N1 0x61044 +#define PIPEB_LINK_N1_OFFSET 0 + +#define PIPEB_LINK_M2 0x61048 +#define PIPEB_LINK_M2_OFFSET 0 +#define PIPEB_LINK_N2 0x6104c +#define PIPEB_LINK_N2_OFFSET 0 + +/* CPU panel fitter */ +#define PFA_CTL_1 0x68080 +#define PFB_CTL_1 0x68880 +#define PF_ENABLE (1<<31) +#define PF_FILTER_MASK (3<<23) +#define PF_FILTER_PROGRAMMED (0<<23) +#define PF_FILTER_MED_3x3 (1<<23) +#define PF_FILTER_EDGE_ENHANCE (2<<23) +#define PF_FILTER_EDGE_SOFTEN (3<<23) +#define PFA_WIN_SZ 0x68074 +#define PFB_WIN_SZ 0x68874 +#define PFA_WIN_POS 0x68070 +#define PFB_WIN_POS 0x68870 + +/* legacy palette */ +#define LGC_PALETTE_A 0x4a000 +#define LGC_PALETTE_B 0x4a800 + +/* interrupts */ +#define DE_MASTER_IRQ_CONTROL (1 << 31) +#define DE_SPRITEB_FLIP_DONE (1 << 29) +#define DE_SPRITEA_FLIP_DONE (1 << 28) +#define DE_PLANEB_FLIP_DONE (1 << 27) +#define DE_PLANEA_FLIP_DONE (1 << 26) +#define DE_PCU_EVENT (1 << 25) +#define DE_GTT_FAULT (1 << 24) +#define DE_POISON (1 << 23) +#define DE_PERFORM_COUNTER (1 << 22) +#define DE_PCH_EVENT (1 << 21) +#define DE_AUX_CHANNEL_A (1 << 20) +#define DE_DP_A_HOTPLUG (1 << 19) +#define DE_GSE (1 << 18) +#define DE_PIPEB_VBLANK (1 << 15) +#define DE_PIPEB_EVEN_FIELD (1 << 14) +#define DE_PIPEB_ODD_FIELD (1 << 13) +#define DE_PIPEB_LINE_COMPARE (1 << 12) +#define DE_PIPEB_VSYNC (1 << 11) +#define DE_PIPEB_FIFO_UNDERRUN (1 << 8) +#define DE_PIPEA_VBLANK (1 << 7) +#define DE_PIPEA_EVEN_FIELD (1 << 6) +#define DE_PIPEA_ODD_FIELD (1 << 5) +#define DE_PIPEA_LINE_COMPARE (1 << 4) +#define DE_PIPEA_VSYNC (1 << 3) +#define DE_PIPEA_FIFO_UNDERRUN (1 << 0) + +#define DEISR 0x44000 +#define DEIMR 0x44004 +#define DEIIR 0x44008 +#define DEIER 0x4400c + +/* GT interrupt */ +#define GT_MASTER_ERROR (1 << 3) +#define GT_SYNC_STATUS (1 << 2) +#define GT_USER_INTERRUPT (1 << 0) + +#define GTISR 0x44010 +#define GTIMR 0x44014 +#define GTIIR 0x44018 +#define GTIER 0x4401c + +#define ILK_DISPLAY_CHICKEN2 0x42004 +#define ILK_DPARB_GATE (1<<22) +#define ILK_VSDPFD_FULL (1<<21) +#define ILK_DSPCLK_GATE 0x42020 +#define ILK_DPARB_CLK_GATE (1<<5) + +#define DISP_ARB_CTL 0x45000 +#define DISP_TILE_SURFACE_SWIZZLING (1<<13) +#define DISP_FBC_WM_DIS (1<<15) + +/* PCH */ + +/* south display engine interrupt */ +#define SDE_CRT_HOTPLUG (1 << 11) +#define SDE_PORTD_HOTPLUG (1 << 10) +#define SDE_PORTC_HOTPLUG (1 << 9) +#define SDE_PORTB_HOTPLUG (1 << 8) +#define SDE_SDVOB_HOTPLUG (1 << 6) +#define SDE_HOTPLUG_MASK (0xf << 8) +/* CPT */ +#define SDE_CRT_HOTPLUG_CPT (1 << 19) +#define SDE_PORTD_HOTPLUG_CPT (1 << 23) +#define SDE_PORTC_HOTPLUG_CPT (1 << 22) +#define SDE_PORTB_HOTPLUG_CPT (1 << 21) + +#define SDEISR 0xc4000 +#define SDEIMR 0xc4004 +#define SDEIIR 0xc4008 +#define SDEIER 0xc400c + +/* digital port hotplug */ +#define PCH_PORT_HOTPLUG 0xc4030 +#define PORTD_HOTPLUG_ENABLE (1 << 20) +#define PORTD_PULSE_DURATION_2ms (0) +#define PORTD_PULSE_DURATION_4_5ms (1 << 18) +#define PORTD_PULSE_DURATION_6ms (2 << 18) +#define PORTD_PULSE_DURATION_100ms (3 << 18) +#define PORTD_HOTPLUG_NO_DETECT (0) +#define PORTD_HOTPLUG_SHORT_DETECT (1 << 16) +#define PORTD_HOTPLUG_LONG_DETECT (1 << 17) +#define PORTC_HOTPLUG_ENABLE (1 << 12) +#define PORTC_PULSE_DURATION_2ms (0) +#define PORTC_PULSE_DURATION_4_5ms (1 << 10) +#define PORTC_PULSE_DURATION_6ms (2 << 10) +#define PORTC_PULSE_DURATION_100ms (3 << 10) +#define PORTC_HOTPLUG_NO_DETECT (0) +#define PORTC_HOTPLUG_SHORT_DETECT (1 << 8) +#define PORTC_HOTPLUG_LONG_DETECT (1 << 9) +#define PORTB_HOTPLUG_ENABLE (1 << 4) +#define PORTB_PULSE_DURATION_2ms (0) +#define PORTB_PULSE_DURATION_4_5ms (1 << 2) +#define PORTB_PULSE_DURATION_6ms (2 << 2) +#define PORTB_PULSE_DURATION_100ms (3 << 2) +#define PORTB_HOTPLUG_NO_DETECT (0) +#define PORTB_HOTPLUG_SHORT_DETECT (1 << 0) +#define PORTB_HOTPLUG_LONG_DETECT (1 << 1) + +#define PCH_GPIOA 0xc5010 +#define PCH_GPIOB 0xc5014 +#define PCH_GPIOC 0xc5018 +#define PCH_GPIOD 0xc501c +#define PCH_GPIOE 0xc5020 +#define PCH_GPIOF 0xc5024 + +#define PCH_GMBUS0 0xc5100 +#define PCH_GMBUS1 0xc5104 +#define PCH_GMBUS2 0xc5108 +#define PCH_GMBUS3 0xc510c +#define PCH_GMBUS4 0xc5110 +#define PCH_GMBUS5 0xc5120 + +#define PCH_DPLL_A 0xc6014 +#define PCH_DPLL_B 0xc6018 + +#define PCH_FPA0 0xc6040 +#define PCH_FPA1 0xc6044 +#define PCH_FPB0 0xc6048 +#define PCH_FPB1 0xc604c + +#define PCH_DPLL_TEST 0xc606c + +#define PCH_DREF_CONTROL 0xC6200 +#define DREF_CONTROL_MASK 0x7fc3 +#define DREF_CPU_SOURCE_OUTPUT_DISABLE (0<<13) +#define DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD (2<<13) +#define DREF_CPU_SOURCE_OUTPUT_NONSPREAD (3<<13) +#define DREF_CPU_SOURCE_OUTPUT_MASK (3<<13) +#define DREF_SSC_SOURCE_DISABLE (0<<11) +#define DREF_SSC_SOURCE_ENABLE (2<<11) +#define DREF_SSC_SOURCE_MASK (3<<11) +#define DREF_NONSPREAD_SOURCE_DISABLE (0<<9) +#define DREF_NONSPREAD_CK505_ENABLE (1<<9) +#define DREF_NONSPREAD_SOURCE_ENABLE (2<<9) +#define DREF_NONSPREAD_SOURCE_MASK (3<<9) +#define DREF_SUPERSPREAD_SOURCE_DISABLE (0<<7) +#define DREF_SUPERSPREAD_SOURCE_ENABLE (2<<7) +#define DREF_SSC4_DOWNSPREAD (0<<6) +#define DREF_SSC4_CENTERSPREAD (1<<6) +#define DREF_SSC1_DISABLE (0<<1) +#define DREF_SSC1_ENABLE (1<<1) +#define DREF_SSC4_DISABLE (0) +#define DREF_SSC4_ENABLE (1) + +#define PCH_RAWCLK_FREQ 0xc6204 +#define FDL_TP1_TIMER_SHIFT 12 +#define FDL_TP1_TIMER_MASK (3<<12) +#define FDL_TP2_TIMER_SHIFT 10 +#define FDL_TP2_TIMER_MASK (3<<10) +#define RAWCLK_FREQ_MASK 0x3ff + +#define PCH_DPLL_TMR_CFG 0xc6208 + +#define PCH_SSC4_PARMS 0xc6210 +#define PCH_SSC4_AUX_PARMS 0xc6214 + +#define PCH_DPLL_SEL 0xc7000 +#define TRANSA_DPLL_ENABLE (1<<3) +#define TRANSA_DPLLB_SEL (1<<0) +#define TRANSA_DPLLA_SEL 0 +#define TRANSB_DPLL_ENABLE (1<<7) +#define TRANSB_DPLLB_SEL (1<<4) +#define TRANSB_DPLLA_SEL (0) +#define TRANSC_DPLL_ENABLE (1<<11) +#define TRANSC_DPLLB_SEL (1<<8) +#define TRANSC_DPLLA_SEL (0) + +/* transcoder */ + +#define TRANS_HTOTAL_A 0xe0000 +#define TRANS_HTOTAL_SHIFT 16 +#define TRANS_HACTIVE_SHIFT 0 +#define TRANS_HBLANK_A 0xe0004 +#define TRANS_HBLANK_END_SHIFT 16 +#define TRANS_HBLANK_START_SHIFT 0 +#define TRANS_HSYNC_A 0xe0008 +#define TRANS_HSYNC_END_SHIFT 16 +#define TRANS_HSYNC_START_SHIFT 0 +#define TRANS_VTOTAL_A 0xe000c +#define TRANS_VTOTAL_SHIFT 16 +#define TRANS_VACTIVE_SHIFT 0 +#define TRANS_VBLANK_A 0xe0010 +#define TRANS_VBLANK_END_SHIFT 16 +#define TRANS_VBLANK_START_SHIFT 0 +#define TRANS_VSYNC_A 0xe0014 +#define TRANS_VSYNC_END_SHIFT 16 +#define TRANS_VSYNC_START_SHIFT 0 + +#define TRANSA_DATA_M1 0xe0030 +#define TRANSA_DATA_N1 0xe0034 +#define TRANSA_DATA_M2 0xe0038 +#define TRANSA_DATA_N2 0xe003c +#define TRANSA_DP_LINK_M1 0xe0040 +#define TRANSA_DP_LINK_N1 0xe0044 +#define TRANSA_DP_LINK_M2 0xe0048 +#define TRANSA_DP_LINK_N2 0xe004c + +#define TRANS_HTOTAL_B 0xe1000 +#define TRANS_HBLANK_B 0xe1004 +#define TRANS_HSYNC_B 0xe1008 +#define TRANS_VTOTAL_B 0xe100c +#define TRANS_VBLANK_B 0xe1010 +#define TRANS_VSYNC_B 0xe1014 + +#define TRANSB_DATA_M1 0xe1030 +#define TRANSB_DATA_N1 0xe1034 +#define TRANSB_DATA_M2 0xe1038 +#define TRANSB_DATA_N2 0xe103c +#define TRANSB_DP_LINK_M1 0xe1040 +#define TRANSB_DP_LINK_N1 0xe1044 +#define TRANSB_DP_LINK_M2 0xe1048 +#define TRANSB_DP_LINK_N2 0xe104c + +#define TRANSACONF 0xf0008 +#define TRANSBCONF 0xf1008 +#define TRANS_DISABLE (0<<31) +#define TRANS_ENABLE (1<<31) +#define TRANS_STATE_MASK (1<<30) +#define TRANS_STATE_DISABLE (0<<30) +#define TRANS_STATE_ENABLE (1<<30) +#define TRANS_FSYNC_DELAY_HB1 (0<<27) +#define TRANS_FSYNC_DELAY_HB2 (1<<27) +#define TRANS_FSYNC_DELAY_HB3 (2<<27) +#define TRANS_FSYNC_DELAY_HB4 (3<<27) +#define TRANS_DP_AUDIO_ONLY (1<<26) +#define TRANS_DP_VIDEO_AUDIO (0<<26) +#define TRANS_PROGRESSIVE (0<<21) +#define TRANS_8BPC (0<<5) +#define TRANS_10BPC (1<<5) +#define TRANS_6BPC (2<<5) +#define TRANS_12BPC (3<<5) + +#define FDI_RXA_CHICKEN 0xc200c +#define FDI_RXB_CHICKEN 0xc2010 +#define FDI_RX_PHASE_SYNC_POINTER_ENABLE (1) + +/* CPU: FDI_TX */ +#define FDI_TXA_CTL 0x60100 +#define FDI_TXB_CTL 0x61100 +#define FDI_TX_DISABLE (0<<31) +#define FDI_TX_ENABLE (1<<31) +#define FDI_LINK_TRAIN_PATTERN_1 (0<<28) +#define FDI_LINK_TRAIN_PATTERN_2 (1<<28) +#define FDI_LINK_TRAIN_PATTERN_IDLE (2<<28) +#define FDI_LINK_TRAIN_NONE (3<<28) +#define FDI_LINK_TRAIN_VOLTAGE_0_4V (0<<25) +#define FDI_LINK_TRAIN_VOLTAGE_0_6V (1<<25) +#define FDI_LINK_TRAIN_VOLTAGE_0_8V (2<<25) +#define FDI_LINK_TRAIN_VOLTAGE_1_2V (3<<25) +#define FDI_LINK_TRAIN_PRE_EMPHASIS_NONE (0<<22) +#define FDI_LINK_TRAIN_PRE_EMPHASIS_1_5X (1<<22) +#define FDI_LINK_TRAIN_PRE_EMPHASIS_2X (2<<22) +#define FDI_LINK_TRAIN_PRE_EMPHASIS_3X (3<<22) +/* ILK always use 400mV 0dB for voltage swing and pre-emphasis level. + SNB has different settings. */ +/* SNB A-stepping */ +#define FDI_LINK_TRAIN_400MV_0DB_SNB_A (0x38<<22) +#define FDI_LINK_TRAIN_400MV_6DB_SNB_A (0x02<<22) +#define FDI_LINK_TRAIN_600MV_3_5DB_SNB_A (0x01<<22) +#define FDI_LINK_TRAIN_800MV_0DB_SNB_A (0x0<<22) +/* SNB B-stepping */ +#define FDI_LINK_TRAIN_400MV_0DB_SNB_B (0x0<<22) +#define FDI_LINK_TRAIN_400MV_6DB_SNB_B (0x3a<<22) +#define FDI_LINK_TRAIN_600MV_3_5DB_SNB_B (0x39<<22) +#define FDI_LINK_TRAIN_800MV_0DB_SNB_B (0x38<<22) +#define FDI_LINK_TRAIN_VOL_EMP_MASK (0x3f<<22) +#define FDI_DP_PORT_WIDTH_X1 (0<<19) +#define FDI_DP_PORT_WIDTH_X2 (1<<19) +#define FDI_DP_PORT_WIDTH_X3 (2<<19) +#define FDI_DP_PORT_WIDTH_X4 (3<<19) +#define FDI_TX_ENHANCE_FRAME_ENABLE (1<<18) +/* Ironlake: hardwired to 1 */ +#define FDI_TX_PLL_ENABLE (1<<14) +/* both Tx and Rx */ +#define FDI_SCRAMBLING_ENABLE (0<<7) +#define FDI_SCRAMBLING_DISABLE (1<<7) + +/* FDI_RX, FDI_X is hard-wired to Transcoder_X */ +#define FDI_RXA_CTL 0xf000c +#define FDI_RXB_CTL 0xf100c +#define FDI_RX_ENABLE (1<<31) +#define FDI_RX_DISABLE (0<<31) +/* train, dp width same as FDI_TX */ +#define FDI_DP_PORT_WIDTH_X8 (7<<19) +#define FDI_8BPC (0<<16) +#define FDI_10BPC (1<<16) +#define FDI_6BPC (2<<16) +#define FDI_12BPC (3<<16) +#define FDI_LINK_REVERSE_OVERWRITE (1<<15) +#define FDI_DMI_LINK_REVERSE_MASK (1<<14) +#define FDI_RX_PLL_ENABLE (1<<13) +#define FDI_FS_ERR_CORRECT_ENABLE (1<<11) +#define FDI_FE_ERR_CORRECT_ENABLE (1<<10) +#define FDI_FS_ERR_REPORT_ENABLE (1<<9) +#define FDI_FE_ERR_REPORT_ENABLE (1<<8) +#define FDI_RX_ENHANCE_FRAME_ENABLE (1<<6) +#define FDI_SEL_RAWCLK (0<<4) +#define FDI_SEL_PCDCLK (1<<4) +/* CPT */ +#define FDI_AUTO_TRAINING (1<<10) +#define FDI_LINK_TRAIN_PATTERN_1_CPT (0<<8) +#define FDI_LINK_TRAIN_PATTERN_2_CPT (1<<8) +#define FDI_LINK_TRAIN_PATTERN_IDLE_CPT (2<<8) +#define FDI_LINK_TRAIN_NORMAL_CPT (3<<8) +#define FDI_LINK_TRAIN_PATTERN_MASK_CPT (3<<8) + +#define FDI_RXA_MISC 0xf0010 +#define FDI_RXB_MISC 0xf1010 +#define FDI_RXA_TUSIZE1 0xf0030 +#define FDI_RXA_TUSIZE2 0xf0038 +#define FDI_RXB_TUSIZE1 0xf1030 +#define FDI_RXB_TUSIZE2 0xf1038 + +/* FDI_RX interrupt register format */ +#define FDI_RX_INTER_LANE_ALIGN (1<<10) +#define FDI_RX_SYMBOL_LOCK (1<<9) /* train 2 */ +#define FDI_RX_BIT_LOCK (1<<8) /* train 1 */ +#define FDI_RX_TRAIN_PATTERN_2_FAIL (1<<7) +#define FDI_RX_FS_CODE_ERR (1<<6) +#define FDI_RX_FE_CODE_ERR (1<<5) +#define FDI_RX_SYMBOL_ERR_RATE_ABOVE (1<<4) +#define FDI_RX_HDCP_LINK_FAIL (1<<3) +#define FDI_RX_PIXEL_FIFO_OVERFLOW (1<<2) +#define FDI_RX_CROSS_CLOCK_OVERFLOW (1<<1) +#define FDI_RX_SYMBOL_QUEUE_OVERFLOW (1<<0) + +#define FDI_RXA_IIR 0xf0014 +#define FDI_RXA_IMR 0xf0018 +#define FDI_RXB_IIR 0xf1014 +#define FDI_RXB_IMR 0xf1018 + +#define FDI_PLL_CTL_1 0xfe000 +#define FDI_PLL_CTL_2 0xfe004 + +/* CRT */ +#define PCH_ADPA 0xe1100 +#define ADPA_TRANS_SELECT_MASK (1<<30) +#define ADPA_TRANS_A_SELECT 0 +#define ADPA_TRANS_B_SELECT (1<<30) +#define ADPA_CRT_HOTPLUG_MASK 0x03ff0000 /* bit 25-16 */ +#define ADPA_CRT_HOTPLUG_MONITOR_NONE (0<<24) +#define ADPA_CRT_HOTPLUG_MONITOR_MASK (3<<24) +#define ADPA_CRT_HOTPLUG_MONITOR_COLOR (3<<24) +#define ADPA_CRT_HOTPLUG_MONITOR_MONO (2<<24) +#define ADPA_CRT_HOTPLUG_ENABLE (1<<23) +#define ADPA_CRT_HOTPLUG_PERIOD_64 (0<<22) +#define ADPA_CRT_HOTPLUG_PERIOD_128 (1<<22) +#define ADPA_CRT_HOTPLUG_WARMUP_5MS (0<<21) +#define ADPA_CRT_HOTPLUG_WARMUP_10MS (1<<21) +#define ADPA_CRT_HOTPLUG_SAMPLE_2S (0<<20) +#define ADPA_CRT_HOTPLUG_SAMPLE_4S (1<<20) +#define ADPA_CRT_HOTPLUG_VOLTAGE_40 (0<<18) +#define ADPA_CRT_HOTPLUG_VOLTAGE_50 (1<<18) +#define ADPA_CRT_HOTPLUG_VOLTAGE_60 (2<<18) +#define ADPA_CRT_HOTPLUG_VOLTAGE_70 (3<<18) +#define ADPA_CRT_HOTPLUG_VOLREF_325MV (0<<17) +#define ADPA_CRT_HOTPLUG_VOLREF_475MV (1<<17) +#define ADPA_CRT_HOTPLUG_FORCE_TRIGGER (1<<16) + +/* or SDVOB */ +#define HDMIB 0xe1140 +#define PORT_ENABLE (1 << 31) +#define TRANSCODER_A (0) +#define TRANSCODER_B (1 << 30) +#define COLOR_FORMAT_8bpc (0) +#define COLOR_FORMAT_12bpc (3 << 26) +#define SDVOB_HOTPLUG_ENABLE (1 << 23) +#define SDVO_ENCODING (0) +#define TMDS_ENCODING (2 << 10) +#define NULL_PACKET_VSYNC_ENABLE (1 << 9) +#define SDVOB_BORDER_ENABLE (1 << 7) +#define AUDIO_ENABLE (1 << 6) +#define VSYNC_ACTIVE_HIGH (1 << 4) +#define HSYNC_ACTIVE_HIGH (1 << 3) +#define PORT_DETECTED (1 << 2) + +/* PCH SDVOB multiplex with HDMIB */ +#define PCH_SDVOB HDMIB + +#define HDMIC 0xe1150 +#define HDMID 0xe1160 + +#define PCH_LVDS 0xe1180 +#define LVDS_DETECTED (1 << 1) + +#define BLC_PWM_CPU_CTL2 0x48250 +#define PWM_ENABLE (1 << 31) +#define PWM_PIPE_A (0 << 29) +#define PWM_PIPE_B (1 << 29) +#define BLC_PWM_CPU_CTL 0x48254 + +#define BLC_PWM_PCH_CTL1 0xc8250 +#define PWM_PCH_ENABLE (1 << 31) +#define PWM_POLARITY_ACTIVE_LOW (1 << 29) +#define PWM_POLARITY_ACTIVE_HIGH (0 << 29) +#define PWM_POLARITY_ACTIVE_LOW2 (1 << 28) +#define PWM_POLARITY_ACTIVE_HIGH2 (0 << 28) + +#define BLC_PWM_PCH_CTL2 0xc8254 + +#define PCH_PP_STATUS 0xc7200 +#define PCH_PP_CONTROL 0xc7204 +#define EDP_FORCE_VDD (1 << 3) +#define EDP_BLC_ENABLE (1 << 2) +#define PANEL_POWER_RESET (1 << 1) +#define PANEL_POWER_OFF (0 << 0) +#define PANEL_POWER_ON (1 << 0) +#define PCH_PP_ON_DELAYS 0xc7208 +#define EDP_PANEL (1 << 30) +#define PCH_PP_OFF_DELAYS 0xc720c +#define PCH_PP_DIVISOR 0xc7210 + +#define PCH_DP_B 0xe4100 +#define PCH_DPB_AUX_CH_CTL 0xe4110 +#define PCH_DPB_AUX_CH_DATA1 0xe4114 +#define PCH_DPB_AUX_CH_DATA2 0xe4118 +#define PCH_DPB_AUX_CH_DATA3 0xe411c +#define PCH_DPB_AUX_CH_DATA4 0xe4120 +#define PCH_DPB_AUX_CH_DATA5 0xe4124 + +#define PCH_DP_C 0xe4200 +#define PCH_DPC_AUX_CH_CTL 0xe4210 +#define PCH_DPC_AUX_CH_DATA1 0xe4214 +#define PCH_DPC_AUX_CH_DATA2 0xe4218 +#define PCH_DPC_AUX_CH_DATA3 0xe421c +#define PCH_DPC_AUX_CH_DATA4 0xe4220 +#define PCH_DPC_AUX_CH_DATA5 0xe4224 + +#define PCH_DP_D 0xe4300 +#define PCH_DPD_AUX_CH_CTL 0xe4310 +#define PCH_DPD_AUX_CH_DATA1 0xe4314 +#define PCH_DPD_AUX_CH_DATA2 0xe4318 +#define PCH_DPD_AUX_CH_DATA3 0xe431c +#define PCH_DPD_AUX_CH_DATA4 0xe4320 +#define PCH_DPD_AUX_CH_DATA5 0xe4324 + +/* CPT */ +#define PORT_TRANS_A_SEL_CPT 0 +#define PORT_TRANS_B_SEL_CPT (1<<29) +#define PORT_TRANS_C_SEL_CPT (2<<29) +#define PORT_TRANS_SEL_MASK (3<<29) + +#define TRANS_DP_CTL_A 0xe0300 +#define TRANS_DP_CTL_B 0xe1300 +#define TRANS_DP_CTL_C 0xe2300 +#define TRANS_DP_OUTPUT_ENABLE (1<<31) +#define TRANS_DP_PORT_SEL_B (0<<29) +#define TRANS_DP_PORT_SEL_C (1<<29) +#define TRANS_DP_PORT_SEL_D (2<<29) +#define TRANS_DP_PORT_SEL_MASK (3<<29) +#define TRANS_DP_AUDIO_ONLY (1<<26) +#define TRANS_DP_ENH_FRAMING (1<<18) +#define TRANS_DP_8BPC (0<<9) +#define TRANS_DP_10BPC (1<<9) +#define TRANS_DP_6BPC (2<<9) +#define TRANS_DP_12BPC (3<<9) +#define TRANS_DP_VSYNC_ACTIVE_HIGH (1<<4) +#define TRANS_DP_VSYNC_ACTIVE_LOW 0 +#define TRANS_DP_HSYNC_ACTIVE_HIGH (1<<3) +#define TRANS_DP_HSYNC_ACTIVE_LOW 0 + +/* SNB eDP training params */ +/* SNB A-stepping */ +#define EDP_LINK_TRAIN_400MV_0DB_SNB_A (0x38<<22) +#define EDP_LINK_TRAIN_400MV_6DB_SNB_A (0x02<<22) +#define EDP_LINK_TRAIN_600MV_3_5DB_SNB_A (0x01<<22) +#define EDP_LINK_TRAIN_800MV_0DB_SNB_A (0x0<<22) +/* SNB B-stepping */ +#define EDP_LINK_TRAIN_400MV_0DB_SNB_B (0x0<<22) +#define EDP_LINK_TRAIN_400MV_6DB_SNB_B (0x3a<<22) +#define EDP_LINK_TRAIN_600MV_3_5DB_SNB_B (0x39<<22) +#define EDP_LINK_TRAIN_800MV_0DB_SNB_B (0x38<<22) +#define EDP_LINK_TRAIN_VOL_EMP_MASK_SNB (0x3f<<22) + + +/* Chipset type macros */ + +#define IS_I830(dev_priv) ((dev_priv)->flags & CHIP_I830) +#define IS_845G(dev_priv) ((dev_priv)->flags & CHIP_I845G) +#define IS_I85X(dev_priv) ((dev_priv)->flags & CHIP_I85X) +#define IS_I865G(dev_priv) ((dev_priv)->flags & CHIP_I865G) + +#define IS_I915G(dev_priv) ((dev_priv)->flags & CHIP_I915G) +#define IS_I915GM(dev_priv) ((dev_priv)->flags & CHIP_I915GM) +#define IS_I945G(dev_priv) ((dev_priv)->flags & CHIP_I945G) +#define IS_I945GM(dev_priv) ((dev_priv)->flags & CHIP_I945GM) +#define IS_I965G(dev_priv) ((dev_priv)->flags & CHIP_I965) +#define IS_I965GM(dev_priv) ((dev_priv)->flags & CHIP_I965GM) + +#define IS_GM45(dev_priv) ((dev_priv)->flags & CHIP_GM45) +#define IS_G4X(dev_priv) ((dev_priv)->flags & CHIP_G4X) + +#define IS_G33(dev_priv) ((dev_priv)->flags & CHIP_G33) + +#define IS_I9XX(dev_priv) ((dev_priv)->flags & CHIP_I9XX) + +#define IS_IRONLAKE(dev_priv) ((dev_priv)->flags & CHIP_IRONLAKE) +#define IS_IRONLAKE_D(dev_priv) ((dev_priv)->flags & CHIP_IRONLAKE_D) +#define IS_IRONLAKE_M(dev_priv) ((dev_priv)->flags & CHIP_IRONLAKE_M) + +#define IS_MOBILE(dev_priv) (dev_priv->flags & CHIP_M) + +#define I915_NEED_GFX_HWS(dev_priv) (dev_priv->flags & CHIP_HWS) + +#define HAS_RESET(dev_priv) IS_I965G(dev_priv) + +#define IS_GEN2(dev_priv) (dev_priv->flags & CHIP_GEN2) +#define IS_GEN3(dev_priv) (dev_priv->flags & CHIP_GEN3) +#define IS_GEN4(dev_priv) (dev_priv->flags & CHIP_GEN4) +#define IS_GEN6(dev_priv) (dev_priv->flags & CHIP_GEN6) + +#define HAS_PCH_SPLIT(dev_priv) (IS_IRONLAKE(dev_priv) || \ + IS_GEN6(dev_priv)) + +/* + * Interrupts that are always left unmasked. + * + * Since pipe events are edge-triggered from the PIPESTAT register to IIRC, + * we leave them always unmasked in IMR and then control enabling them through + * PIPESTAT alone. + */ +#define I915_INTERRUPT_ENABLE_FIX \ + (I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | \ + I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) + +/* Interrupts that we mask and unmask at runtime */ +#define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT) + +/* These are all of the interrupts used by the driver */ +#define I915_INTERRUPT_ENABLE_MASK \ + (I915_INTERRUPT_ENABLE_FIX | \ + I915_INTERRUPT_ENABLE_VAR) + +/* + * if kms we want pch event, gse, and plane flip masks too + */ +#define PCH_SPLIT_DISPLAY_INTR_FIX (DE_MASTER_IRQ_CONTROL) +#define PCH_SPLIT_DISPLAY_INTR_VAR (DE_PIPEA_VBLANK | DE_PIPEB_VBLANK) +#define PCH_SPLIT_DISPLAY_ENABLE_MASK \ + (PCH_SPLIT_DISPLAY_INTR_FIX | PCH_SPLIT_DISPLAY_INTR_VAR) +#define PCH_SPLIT_RENDER_INTR_FIX (0) +#define PCH_SPLIT_RENDER_INTR_VAR (GT_USER_INTERRUPT | GT_MASTER_ERROR) +#define PCH_SPLIT_RENDER_ENABLE_MASK \ + (PCH_SPLIT_RENDER_INTR_FIX | PCH_SPLIT_RENDER_INTR_VAR) +/* not yet */ +#define PCH_SPLIT_HOTPLUG_INTR_FIX (0) +#define PCH_SPLIT_HOTPLUG_INTR_VAR (0) +#define PCH_SPLIT_HOTPLUG_ENABLE_MASK \ + (PCH_SPLIT_HOTPLUG_INTR_FIX | PCH_SPLIT_HOTPLUG_INTR_VAR) + +#define printeir(val) printf("%s: error reg: %b\n", __func__, val, \ + "\20\x10PTEERR\x2REFRESHERR\x1INSTERR") + +/* + * With the i45 and later, Y tiling got adjusted so that it was 32 128-byte + * rows, which changes the alignment requirements and fence programming. + */ +#define HAS_128_BYTE_Y_TILING(dev_priv) (IS_I9XX(dev_priv) && \ + !(IS_I915G(dev_priv) || IS_I915GM(dev_priv))) + +#define PRIMARY_RINGBUFFER_SIZE (128*1024) + +/* Inlines */ + +/** + * Returns true if seq1 is later than seq2. + */ +static __inline int +i915_seqno_passed(uint32_t seq1, uint32_t seq2) +{ + return ((int32_t)(seq1 - seq2) >= 0); +} + +/* + * Read seqence number from the Hardware status page. + */ +static __inline u_int32_t +i915_get_gem_seqno(struct inteldrm_softc *dev_priv) +{ + return (READ_HWSP(dev_priv, I915_GEM_HWS_INDEX)); +} + +static __inline int +i915_obj_purgeable(struct inteldrm_obj *obj_priv) +{ + return (obj_priv->obj.do_flags & I915_DONTNEED); +} + +static __inline int +i915_obj_purged(struct inteldrm_obj *obj_priv) +{ + return (obj_priv->obj.do_flags & I915_PURGED); +} + +static __inline int +inteldrm_is_active(struct inteldrm_obj *obj_priv) +{ + return (obj_priv->obj.do_flags & I915_ACTIVE); +} + +static __inline int +inteldrm_is_dirty(struct inteldrm_obj *obj_priv) +{ + return (obj_priv->obj.do_flags & I915_DIRTY); +} + +static __inline int +inteldrm_exec_needs_fence(struct inteldrm_obj *obj_priv) +{ + return (obj_priv->obj.do_flags & I915_EXEC_NEEDS_FENCE); +} + +static __inline int +inteldrm_needs_fence(struct inteldrm_obj *obj_priv) +{ + return (obj_priv->obj.do_flags & I915_FENCED_EXEC); +} + +#if defined(__NetBSD__) +/* OpenBSD PCI IDs compatibility definitions. */ +#undef PCI_PRODUCT_INTEL_82830M_IGD +#undef PCI_PRODUCT_INTEL_82865G_IGD +#undef PCI_PRODUCT_INTEL_82915G_IGD_1 +#undef PCI_PRODUCT_INTEL_82915GM_IGD_1 +#undef PCI_PRODUCT_INTEL_82945G_IGD_1 +#undef PCI_PRODUCT_INTEL_82945GM_IGD_1 +#undef PCI_PRODUCT_INTEL_82945GME_IGD_1 +#undef PCI_PRODUCT_INTEL_82946GZ_IGD_1 +#undef PCI_PRODUCT_INTEL_82G35_IGD_1 +#undef PCI_PRODUCT_INTEL_82Q965_IGD_1 +#undef PCI_PRODUCT_INTEL_82G965_IGD_1 +#undef PCI_PRODUCT_INTEL_82GM965_IGD_1 +#undef PCI_PRODUCT_INTEL_82GME965_IGD_1 +#undef PCI_PRODUCT_INTEL_82G33_IGD_1 +#undef PCI_PRODUCT_INTEL_82Q35_IGD_1 +#undef PCI_PRODUCT_INTEL_82Q33_IGD_1 +#undef PCI_PRODUCT_INTEL_82GM45_IGD_1 +#undef PCI_PRODUCT_INTEL_82Q45_IGD_1 +#undef PCI_PRODUCT_INTEL_82G45_IGD_1 +#undef PCI_PRODUCT_INTEL_82G41_IGD_1 +#undef PCI_PRODUCT_INTEL_PINEVIEW_IGC_1 +#undef PCI_PRODUCT_INTEL_PINEVIEW_M_IGC_1 +#undef PCI_PRODUCT_INTEL_CLARKDALE_IGD +#undef PCI_PRODUCT_INTEL_ARRANDALE_IGD +#define PCI_PRODUCT_INTEL_82830M_IGD PCI_PRODUCT_INTEL_82830MP_IV +#define PCI_PRODUCT_INTEL_82865G_IGD PCI_PRODUCT_INTEL_82865_IGD +#define PCI_PRODUCT_INTEL_82915G_IGD_1 PCI_PRODUCT_INTEL_82915G_IGD +#define PCI_PRODUCT_INTEL_82915GM_IGD_1 PCI_PRODUCT_INTEL_82915GM_IGD +#define PCI_PRODUCT_INTEL_82945G_IGD_1 PCI_PRODUCT_INTEL_82945P_IGD +#define PCI_PRODUCT_INTEL_82945GM_IGD_1 PCI_PRODUCT_INTEL_82945GM_IGD +#define PCI_PRODUCT_INTEL_82945GME_IGD_1 PCI_PRODUCT_INTEL_82945GME_IGD +#define PCI_PRODUCT_INTEL_82946GZ_IGD_1 PCI_PRODUCT_INTEL_82946GZ_IGD +#define PCI_PRODUCT_INTEL_82G35_IGD_1 PCI_PRODUCT_INTEL_82G35_IGD +#define PCI_PRODUCT_INTEL_82Q965_IGD_1 PCI_PRODUCT_INTEL_82965Q_IGD +#define PCI_PRODUCT_INTEL_82Q965_IGD_1 PCI_PRODUCT_INTEL_82965Q_IGD +#define PCI_PRODUCT_INTEL_82G965_IGD_1 PCI_PRODUCT_INTEL_82965G_IGD +#define PCI_PRODUCT_INTEL_82GM965_IGD_1 PCI_PRODUCT_INTEL_82965PM_IGD +#define PCI_PRODUCT_INTEL_82GME965_IGD_1 PCI_PRODUCT_INTEL_82965GME_IGD +#define PCI_PRODUCT_INTEL_82G33_IGD_1 PCI_PRODUCT_INTEL_82G33_IGD +#define PCI_PRODUCT_INTEL_82Q35_IGD_1 PCI_PRODUCT_INTEL_82Q35_IGD +#define PCI_PRODUCT_INTEL_82Q33_IGD_1 PCI_PRODUCT_INTEL_82Q33_IGD +#define PCI_PRODUCT_INTEL_82GM45_IGD_1 PCI_PRODUCT_INTEL_82GM45_IGD +#define PCI_PRODUCT_INTEL_82Q45_IGD_1 PCI_PRODUCT_INTEL_82Q45_IGD +#define PCI_PRODUCT_INTEL_82G45_IGD_1 PCI_PRODUCT_INTEL_82G45_IGD +#define PCI_PRODUCT_INTEL_82G41_IGD_1 PCI_PRODUCT_INTEL_82G41_IGD +#define PCI_PRODUCT_INTEL_PINEVIEW_IGC_1 PCI_PRODUCT_INTEL_PINEVIEW_IGD +#define PCI_PRODUCT_INTEL_PINEVIEW_M_IGC_1 PCI_PRODUCT_INTEL_PINEVIEW_M_IGD +#define PCI_PRODUCT_INTEL_CLARKDALE_IGD PCI_PRODUCT_INTEL_IRONLAKE_D_IGD +#define PCI_PRODUCT_INTEL_ARRANDALE_IGD PCI_PRODUCT_INTEL_IRONLAKE_M_IGD +#endif /* defined(__NetBSD__) */ + +#endif diff -Naurp old/src/sys/dev/pci/drm/i915_irq.c new/src/sys/dev/pci/drm/i915_irq.c --- old/src/sys/dev/pci/drm/i915_irq.c 1970-01-01 01:00:00.000000000 +0100 +++ new/src/sys/dev/pci/drm/i915_irq.c 2011-02-08 11:14:00.000000000 +0100 @@ -0,0 +1,347 @@ +/* i915_irq.c -- IRQ support for the I915 -*- linux-c -*- + */ +/* + * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. + * 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, sub license, 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 NON-INFRINGEMENT. + * IN NO EVENT SHALL TUNGSTEN GRAPHICS 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. + * + */ + +#include "drmP.h" +#include "drm.h" +#include "i915_drm.h" +#include "i915_drv.h" + +int inteldrm_intr(void *); +void i915_enable_irq(struct inteldrm_softc *, u_int32_t); +void i915_disable_irq(struct inteldrm_softc *, u_int32_t); +void ironlake_enable_graphics_irq(struct inteldrm_softc *, u_int32_t); +void ironlake_disable_graphics_irq(struct inteldrm_softc *, u_int32_t); +void ironlake_enable_display_irq(struct inteldrm_softc *, u_int32_t); +void ironlake_disable_display_irq(struct inteldrm_softc *, u_int32_t); +void i915_enable_pipestat(struct inteldrm_softc *, int, u_int32_t); +void i915_disable_pipestat(struct inteldrm_softc *, int, u_int32_t); +int ironlake_irq_install(struct inteldrm_softc *); + +void +i915_enable_irq(struct inteldrm_softc *dev_priv, u_int32_t mask) +{ + if ((dev_priv->irq_mask_reg & mask) != 0) { + dev_priv->irq_mask_reg &= ~mask; + I915_WRITE(IMR, dev_priv->irq_mask_reg); + (void)I915_READ(IMR); + } +} + +void +i915_disable_irq(struct inteldrm_softc *dev_priv, u_int32_t mask) +{ + if ((dev_priv->irq_mask_reg & mask) != mask) { + dev_priv->irq_mask_reg |= mask; + I915_WRITE(IMR, dev_priv->irq_mask_reg); + (void)I915_READ(IMR); + } +} + +inline void +ironlake_enable_graphics_irq(struct inteldrm_softc *dev_priv, u_int32_t mask) +{ + if ((dev_priv->gt_irq_mask_reg & mask) != 0) { + dev_priv->gt_irq_mask_reg &= ~mask; + I915_WRITE(GTIMR, dev_priv->gt_irq_mask_reg); + (void)I915_READ(GTIMR); + } +} + +inline void +ironlake_disable_graphics_irq(struct inteldrm_softc *dev_priv, u_int32_t mask) +{ + if ((dev_priv->gt_irq_mask_reg & mask) != mask) { + dev_priv->gt_irq_mask_reg |= mask; + I915_WRITE(GTIMR, dev_priv->gt_irq_mask_reg); + (void)I915_READ(GTIMR); + } +} + +/* display hotplug and vblank irqs */ +inline void +ironlake_enable_display_irq(struct inteldrm_softc *dev_priv, u_int32_t mask) +{ + if ((dev_priv->irq_mask_reg & mask) != 0) { + dev_priv->irq_mask_reg &= ~mask; + I915_WRITE(DEIMR, dev_priv->irq_mask_reg); + (void)I915_READ(DEIMR); + } +} + +inline void +ironlake_disable_display_irq(struct inteldrm_softc *dev_priv, u_int32_t mask) +{ + if ((dev_priv->irq_mask_reg & mask) != mask) { + dev_priv->irq_mask_reg |= mask; + I915_WRITE(DEIMR, dev_priv->irq_mask_reg); + (void)I915_READ(DEIMR); + } +} + +void +i915_enable_pipestat(struct inteldrm_softc *dev_priv, int pipe, u_int32_t mask) +{ + if ((dev_priv->pipestat[pipe] & mask) != mask) { + bus_size_t reg = pipe == 0 ? PIPEASTAT : PIPEBSTAT; + + dev_priv->pipestat[pipe] |= mask; + /* Enable the interrupt, clear and pending status */ + I915_WRITE(reg, dev_priv->pipestat[pipe] | (mask >> 16)); + (void)I915_READ(reg); + } +} + +void +i915_disable_pipestat(struct inteldrm_softc *dev_priv, int pipe, u_int32_t mask) +{ + if ((dev_priv->pipestat[pipe] & mask) != 0) { + bus_size_t reg = pipe == 0 ? PIPEASTAT : PIPEBSTAT; + + dev_priv->pipestat[pipe] &= ~mask; + I915_WRITE(reg, dev_priv->pipestat[pipe]); + (void)I915_READ(reg); + } +} + +u_int32_t +i915_get_vblank_counter(struct drm_device *dev, int pipe) +{ + struct inteldrm_softc *dev_priv = dev->dev_private; + bus_size_t high_frame, low_frame; + u_int32_t high1, high2, low; + + high_frame = pipe ? PIPEBFRAMEHIGH : PIPEAFRAMEHIGH; + low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL; + + if (inteldrm_pipe_enabled(dev_priv, pipe) == 0) { + DRM_DEBUG("trying to get vblank count for disabled pipe %d\n", + pipe); + return (0); + } + + /* GM45 just had to be different... */ + if (IS_GM45(dev_priv) || IS_G4X(dev_priv) || IS_IRONLAKE(dev_priv)) { + return (I915_READ(pipe ? PIPEB_FRMCOUNT_GM45 : + PIPEA_FRMCOUNT_GM45)); + } + + /* + * High & low register fields aren't synchronized, so make sure + * we get a low value that's stable across two reads of the high + * register. + */ + do { + high1 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >> + PIPE_FRAME_HIGH_SHIFT); + low = ((I915_READ(low_frame) & PIPE_FRAME_LOW_MASK) >> + PIPE_FRAME_LOW_SHIFT); + high2 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >> + PIPE_FRAME_HIGH_SHIFT); + } while (high1 != high2); + + return ((high1 << 8) | low); +} + +void +i915_user_irq_get(struct inteldrm_softc *dev_priv) +{ + if (++dev_priv->user_irq_refcount == 1) { + if (HAS_PCH_SPLIT(dev_priv)) + ironlake_enable_graphics_irq(dev_priv, + GT_USER_INTERRUPT); + else + i915_enable_irq(dev_priv, I915_USER_INTERRUPT); + } +} + +void +i915_user_irq_put(struct inteldrm_softc *dev_priv) +{ + if (--dev_priv->user_irq_refcount == 0) { + if (HAS_PCH_SPLIT(dev_priv)) + ironlake_disable_graphics_irq(dev_priv, + GT_USER_INTERRUPT); + else + i915_disable_irq(dev_priv, I915_USER_INTERRUPT); + } +} + +int +i915_enable_vblank(struct drm_device *dev, int pipe) +{ + struct inteldrm_softc *dev_priv = dev->dev_private; + + if (inteldrm_pipe_enabled(dev_priv, pipe) == 0) + return (EINVAL); + + mtx_enter(&dev_priv->user_irq_lock); + if (HAS_PCH_SPLIT(dev_priv)) + ironlake_enable_display_irq(dev_priv, (pipe == 0) ? + DE_PIPEA_VBLANK : DE_PIPEB_VBLANK); + else + i915_enable_pipestat(dev_priv, pipe, (IS_I965G(dev_priv) ? + PIPE_START_VBLANK_INTERRUPT_ENABLE : + PIPE_VBLANK_INTERRUPT_ENABLE)); + mtx_leave(&dev_priv->user_irq_lock); + + return (0); +} + +void +i915_disable_vblank(struct drm_device *dev, int pipe) +{ + struct inteldrm_softc *dev_priv = dev->dev_private; + + mtx_enter(&dev_priv->user_irq_lock); + if (HAS_PCH_SPLIT(dev_priv)) + ironlake_disable_display_irq(dev_priv, (pipe == 0) ? + DE_PIPEA_VBLANK : DE_PIPEB_VBLANK); + else + i915_disable_pipestat(dev_priv, pipe, + PIPE_START_VBLANK_INTERRUPT_ENABLE | + PIPE_VBLANK_INTERRUPT_ENABLE); + mtx_leave(&dev_priv->user_irq_lock); +} + +/* drm_dma.h hooks +*/ +int +i915_driver_irq_install(struct drm_device *dev) +{ + struct inteldrm_softc *dev_priv = dev->dev_private; + + dev->vblank->vb_max = 0xffffff; /* only 24 bits of frame count */ + if (IS_G4X(dev_priv) || IS_IRONLAKE(dev_priv)) + dev->vblank->vb_max = 0xffffffff; + + I915_WRITE(HWSTAM, 0xeffe); + + if (HAS_PCH_SPLIT(dev_priv)) + return (ironlake_irq_install(dev_priv)); + + I915_WRITE(PIPEASTAT, 0); + I915_WRITE(PIPEBSTAT, 0); + I915_WRITE(IMR, 0xffffffff); + I915_WRITE(IER, 0x0); + (void)I915_READ(IER); + + + /* + * Enable some error detection, note the instruction error mask + * bit is reserved, so we leave it masked. + */ + I915_WRITE(EMR, IS_G4X(dev_priv) ? + ~(GM45_ERROR_PAGE_TABLE | GM45_ERROR_MEM_PRIV | + GM45_ERROR_CP_PRIV | I915_ERROR_MEMORY_REFRESH) : + ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH)); + + /* + * Disable pipe interrupt enables, clear pending pipe status + * add back in the enabled interrupts from previous iterations + * (say in the reset case where we want vblank interrupts etc to be + * switched back on if they were running + */ + I915_WRITE(PIPEASTAT, (I915_READ(PIPEASTAT) & 0x8000ffff) | + dev_priv->pipestat[0]); + I915_WRITE(PIPEBSTAT, (I915_READ(PIPEBSTAT) & 0x8000ffff) | + dev_priv->pipestat[1]); + /* Clear pending interrupt status */ + I915_WRITE(IIR, I915_READ(IIR)); + + I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK); + I915_WRITE(IMR, dev_priv->irq_mask_reg); + (void)I915_READ(IER); + + return (0); +} + +int +ironlake_irq_install(struct inteldrm_softc *dev_priv) +{ + /* mask and ack everything before we turn anything on. */ + /* + * XXX this is a legacy of the only preinstall/postinstall split. + * I wonder if we could avoid this now... + */ + I915_WRITE(DEIMR, 0xffffffff); + I915_WRITE(DEIER, 0x0); + (void)I915_READ(DEIER); + + /* GT */ + I915_WRITE(GTIMR, 0xfffffff); + I915_WRITE(GTIER, 0x0); + (void)I915_READ(GTIER); + + /* + * Everything is turned off now and everything acked. + * now we can set everything up + */ + + I915_WRITE(DEIIR, I915_READ(DEIIR)); + I915_WRITE(DEIMR, dev_priv->irq_mask_reg); + I915_WRITE(DEIER, PCH_SPLIT_DISPLAY_ENABLE_MASK); + + I915_WRITE(GTIIR, I915_READ(GTIIR)); + I915_WRITE(GTIMR, dev_priv->gt_irq_mask_reg); + I915_WRITE(GTIER, PCH_SPLIT_RENDER_ENABLE_MASK); + + /* south display irq -- hotplug off for now */ + I915_WRITE(SDEIIR, I915_READ(SDEIIR)); + I915_WRITE(SDEIMR, dev_priv->pch_irq_mask_reg); + I915_WRITE(SDEIER, PCH_SPLIT_HOTPLUG_ENABLE_MASK); + (void)I915_READ(SDEIER); + + return (0); +} + +void +i915_driver_irq_uninstall(struct drm_device *dev) +{ + struct inteldrm_softc *dev_priv = dev->dev_private; + + I915_WRITE(HWSTAM, 0xffffffff); + + if (HAS_PCH_SPLIT(dev_priv)) { + I915_WRITE(DEIMR, 0xffffffff); + I915_WRITE(DEIER, 0x0); + I915_WRITE(DEIIR, I915_READ(DEIIR)); + + I915_WRITE(GTIMR, 0xfffffff); + I915_WRITE(GTIER, 0x0); + I915_WRITE(GTIIR, I915_READ(GTIIR)); + } else { + I915_WRITE(PIPEASTAT, 0); + I915_WRITE(PIPEBSTAT, 0); + I915_WRITE(IMR, 0xffffffff); + I915_WRITE(IER, 0x0); + + I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff); + I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff); + I915_WRITE(IIR, I915_READ(IIR)); + } +} diff -Naurp old/src/sys/dev/pci/vga_pci.c new/src/sys/dev/pci/vga_pci.c --- old/src/sys/dev/pci/vga_pci.c 2011-01-22 16:14:28.000000000 +0100 +++ new/src/sys/dev/pci/vga_pci.c 2011-02-08 11:14:00.000000000 +0100 @@ -300,6 +300,15 @@ vga_drm_print(void *aux, const char *pnp return (UNCONF); } +bus_addr_t +vga_bar_base(device_t dv, int bar) +{ + struct vga_pci_softc *sc; + + KASSERT(dv != NULL && device_is_a(dv, "vga")); + sc = device_private(dv); + return sc->sc_bars[bar].vb_base; +} static int vga_pci_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) diff -Naurp old/src/sys/dev/pci/vga_pcivar.h new/src/sys/dev/pci/vga_pcivar.h --- old/src/sys/dev/pci/vga_pcivar.h 2007-03-20 19:05:31.000000000 +0100 +++ new/src/sys/dev/pci/vga_pcivar.h 2011-02-08 11:14:00.000000000 +0100 @@ -37,3 +37,5 @@ int vga_pci_cnattach(bus_space_tag_t, bu pci_chipset_tag_t, int, int, int); int vga_drm_print(void *aux, const char *pnp); + +bus_addr_t vga_bar_base(device_t, int); diff -Naurp old/src/sys/kern/sysv_shm.c new/src/sys/kern/sysv_shm.c --- old/src/sys/kern/sysv_shm.c 2010-07-27 16:25:23.000000000 +0200 +++ new/src/sys/kern/sysv_shm.c 2011-02-08 11:14:00.000000000 +0100 @@ -268,7 +268,7 @@ shm_memlock(struct lwp *l, struct shmid_ if (cmd == SHM_LOCK && (shmseg->shm_perm.mode & SHMSEG_WIRED) == 0) { /* Wire the object and map, then tag it */ - error = uobj_wirepages(shmseg->_shm_internal, 0, size); + error = uobj_wirepages(shmseg->_shm_internal, 0, size, NULL); if (error) return EIO; error = uvm_map_pageable(&p->p_vmspace->vm_map, @@ -730,7 +730,7 @@ sys_shmget(struct lwp *l, const struct s shmseg->_shm_internal = uao_create(size, 0); if (lockmem) { /* Wire the pages and tag it */ - error = uobj_wirepages(shmseg->_shm_internal, 0, size); + error = uobj_wirepages(shmseg->_shm_internal, 0, size, NULL); if (error) { uao_detach(shmseg->_shm_internal); mutex_enter(&shm_lock); diff -Naurp old/src/sys/modules/Makefile new/src/sys/modules/Makefile --- old/src/sys/modules/Makefile 2011-01-16 02:13:10.000000000 +0100 +++ new/src/sys/modules/Makefile 2011-02-08 11:14:00.000000000 +0100 @@ -122,8 +122,8 @@ SUBDIR+= azalia SUBDIR+= compat_linux SUBDIR+= compat_linux32 SUBDIR+= compat_netbsd32 -SUBDIR+= drm -SUBDIR+= i915drm +#SUBDIR+= drm +#SUBDIR+= i915drm SUBDIR+= pad .endif @@ -133,10 +133,10 @@ SUBDIR+= compat_freebsd SUBDIR+= compat_ibcs2 SUBDIR+= compat_linux SUBDIR+= compat_svr4 -SUBDIR+= drm -SUBDIR+= i915drm -SUBDIR+= radeondrm -SUBDIR+= viadrm +#SUBDIR+= drm +#SUBDIR+= i915drm +#SUBDIR+= radeondrm +#SUBDIR+= viadrm SUBDIR+= pad .endif diff -Naurp old/src/sys/uvm/uvm_extern.h new/src/sys/uvm/uvm_extern.h --- old/src/sys/uvm/uvm_extern.h 2011-01-04 09:26:33.000000000 +0100 +++ new/src/sys/uvm/uvm_extern.h 2011-02-08 11:14:00.000000000 +0100 @@ -705,10 +705,9 @@ int uvm_mremap(struct vm_map *, vaddr_ struct proc *, int); /* uvm_object.c */ -int uobj_wirepages(struct uvm_object *uobj, off_t start, - off_t end); -void uobj_unwirepages(struct uvm_object *uobj, off_t start, - off_t end); +int uobj_wirepages(struct uvm_object *, off_t, off_t, + struct pglist *); +void uobj_unwirepages(struct uvm_object *, off_t, off_t); /* uvm_page.c */ struct vm_page *uvm_pagealloc_strat(struct uvm_object *, diff -Naurp old/src/sys/uvm/uvm_object.c new/src/sys/uvm/uvm_object.c --- old/src/sys/uvm/uvm_object.c 2009-08-18 21:16:09.000000000 +0200 +++ new/src/sys/uvm/uvm_object.c 2011-02-08 11:14:00.000000000 +0100 @@ -60,7 +60,8 @@ __KERNEL_RCSID(0, "$NetBSD: uvm_object.c */ int -uobj_wirepages(struct uvm_object *uobj, off_t start, off_t end) +uobj_wirepages(struct uvm_object *uobj, off_t start, off_t end, + struct pglist *list) { int i, npages, error; struct vm_page *pgs[FETCH_PAGECOUNT], *pg = NULL; @@ -114,6 +115,8 @@ uobj_wirepages(struct uvm_object *uobj, mutex_enter(&uvm_pageqlock); for (i = 0; i < npages; i++) { uvm_pagewire(pgs[i]); + if (list != NULL) + TAILQ_INSERT_TAIL(list, pgs[i], pageq.queue); } mutex_exit(&uvm_pageqlock);