diff options
Diffstat (limited to 'linux-core')
144 files changed, 46979 insertions, 439 deletions
diff --git a/linux-core/Makefile b/linux-core/Makefile index 3af6f370..1790bdb0 100644 --- a/linux-core/Makefile +++ b/linux-core/Makefile @@ -58,7 +58,7 @@ endif  # Modules for all architectures  MODULE_LIST := drm.o tdfx.o r128.o radeon.o mga.o sis.o savage.o via.o \ -               mach64.o nv.o nouveau.o xgi.o +               mach64.o nv.o nouveau.o xgi.o radeon_ms.o  # Modules only for ix86 architectures  ifneq (,$(findstring 86,$(MACHINE))) @@ -90,8 +90,9 @@ VIAHEADERS =	via_drm.h via_drv.h via_3d_reg.h via_verifier.h $(DRMHEADERS)  MACH64HEADERS = mach64_drv.h mach64_drm.h $(DRMHEADERS)  NVHEADERS =     nv_drv.h $(DRMHEADERS)  FFBHEADERS = 	ffb_drv.h $(DRMHEADERS) -NOUVEAUHEADERS = nouveau_drv.h nouveau_drm.h nouveau_reg.h $(DRMHEADERS) +NOUVEAUHEADERS = nouveau_drv.h nouveau_drm.h nouveau_reg.h nouveau_display.h $(DRMHEADERS)  XGIHEADERS = xgi_cmdlist.h xgi_drv.h xgi_misc.h xgi_regs.h $(DRMHEADERS) +RADEONMSHEADERS = radeon_ms_driver.h $(DRMHEADERS)   PROGS = dristat drmstat @@ -116,7 +117,7 @@ V := $(shell if [ -f $(BOOTVERSION_PREFIX)version.h ]; then \  ifeq ($(V),"$(RUNNING_REL)")  HEADERFROMBOOT := 1 -GETCONFIG := MAKEFILES=$(shell pwd)/.config +GETCONFIG := MAKEFILES=$(shell /bin/pwd)/.config  HAVECONFIG := y  endif @@ -163,7 +164,7 @@ endif  all: modules  modules: includes -	+make -C $(LINUXDIR) $(GETCONFIG) SUBDIRS=`pwd` DRMSRCDIR=`pwd` modules +	+make -C $(LINUXDIR) $(GETCONFIG) SUBDIRS=`/bin/pwd` DRMSRCDIR=`/bin/pwd` modules  ifeq ($(HEADERFROMBOOT),1) @@ -239,7 +240,7 @@ drmstat: drmstat.c  	$(CC) $(PRGCFLAGS) $< -o $@ $(DRMSTATLIBS)  install: -	make -C $(LINUXDIR) $(GETCONFIG) SUBDIRS=`pwd` DRMSRCDIR=`pwd` modules_install +	make -C $(LINUXDIR) $(GETCONFIG) SUBDIRS=`/bin/pwd` DRMSRCDIR=`/bin/pwd` modules_install  else @@ -286,6 +287,7 @@ CONFIG_DRM_MACH64 := n  CONFIG_DRM_NV := n  CONFIG_DRM_NOUVEAU := n  CONFIG_DRM_XGI := n +CONFIG_DRM_RADEON_MS := n  # Enable module builds for the modules requested/supported. @@ -325,6 +327,9 @@ endif  ifneq (,$(findstring xgi,$(DRM_MODULES)))  CONFIG_DRM_XGI := m  endif +ifneq (,$(findstring radeon_ms,$(DRM_MODULES))) +#CONFIG_DRM_RADEON_MS := m +endif  # These require AGP support diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel index 45a6b1f9..b09697d3 100644 --- a/linux-core/Makefile.kernel +++ b/linux-core/Makefile.kernel @@ -12,16 +12,21 @@ drm-objs    := drm_auth.o drm_bufs.o drm_context.o drm_dma.o drm_drawable.o \  		drm_lock.o drm_memory.o drm_proc.o drm_stub.o drm_vm.o \  		drm_sysfs.o drm_pci.o drm_agpsupport.o drm_scatter.o \  		drm_memory_debug.o ati_pcigart.o drm_sman.o \ -		drm_hashtab.o drm_mm.o drm_object.o drm_compat.o \ +		drm_hashtab.o drm_memrange.o drm_object.o drm_compat.o \  	        drm_fence.o drm_ttm.o drm_bo.o drm_bo_move.o drm_bo_lock.o \ -		drm_regman.o drm_vm_nopage_compat.o +		drm_crtc.o drm_edid.o drm_modes.o drm_crtc_helper.o \ +		drm_regman.o drm_vm_nopage_compat.o drm_gem.o  tdfx-objs   := tdfx_drv.o  r128-objs   := r128_drv.o r128_cce.o r128_state.o r128_irq.o  mga-objs    := mga_drv.o mga_dma.o mga_state.o mga_warp.o mga_irq.o  i810-objs   := i810_drv.o i810_dma.o  i915-objs   := i915_drv.o i915_dma.o i915_irq.o i915_mem.o i915_fence.o \  		i915_buffer.o i915_compat.o i915_execbuf.o i915_suspend.o \ -		i915_opregion.o +		i915_gem.o i915_opregion.o \ +	        intel_display.o intel_crt.o intel_lvds.o intel_bios.o \ +		intel_sdvo.o intel_modes.o intel_i2c.o i915_init.o intel_fb.o \ +		intel_tv.o intel_dvo.o dvo_ch7xxx.o \ +		dvo_ch7017.o dvo_ivch.o dvo_tfp410.o dvo_sil164.o  nouveau-objs := nouveau_drv.o nouveau_state.o nouveau_fifo.o nouveau_mem.o \  		nouveau_object.o nouveau_irq.o nouveau_notifier.o nouveau_swmthd.o \  		nouveau_sgdma.o nouveau_dma.o nouveau_bo.o nouveau_fence.o \ @@ -31,8 +36,14 @@ nouveau-objs := nouveau_drv.o nouveau_state.o nouveau_fifo.o nouveau_mem.o \  		nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o \  		nv04_graph.o nv10_graph.o nv20_graph.o \  		nv40_graph.o nv50_graph.o \ -		nv04_instmem.o nv50_instmem.o -radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o r300_cmdbuf.o +		nv04_instmem.o nv50_instmem.o \ +		nouveau_bios.o \ +		nv50_crtc.o nv50_cursor.o nv50_lut.o nv50_fb.o nv50_output.o nv50_sor.o nv50_dac.o nv50_connector.o nv50_i2c.o nv50_display.o \ +		nv50_kms_wrapper.o \ +		nv50_fbcon.o +radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o r300_cmdbuf.o radeon_gem.o \ +	 radeon_buffer.o radeon_fence.o atom.o radeon_display.o radeon_atombios.o radeon_i2c.o radeon_connectors.o \ +	atombios_crtc.o radeon_encoders.o radeon_fb.o radeon_combios.o  sis-objs    := sis_drv.o sis_mm.o  ffb-objs    := ffb_drv.o ffb_context.o  savage-objs := savage_drv.o savage_bci.o savage_state.o @@ -46,6 +57,7 @@ xgi-objs    := xgi_cmdlist.o xgi_drv.o xgi_fb.o xgi_misc.o xgi_pcie.o \  ifeq ($(CONFIG_COMPAT),y)  drm-objs    += drm_ioc32.o  radeon-objs += radeon_ioc32.o +radeon_ms-objs += radeon_ms_compat.o  mga-objs    += mga_ioc32.o  r128-objs   += r128_ioc32.o  i915-objs   += i915_ioc32.o @@ -68,3 +80,4 @@ obj-$(CONFIG_DRM_MACH64)+= mach64.o  obj-$(CONFIG_DRM_NV)    += nv.o  obj-$(CONFIG_DRM_NOUVEAU) += nouveau.o  obj-$(CONFIG_DRM_XGI)   += xgi.o +obj-$(CONFIG_DRM_RADEON_MS) += radeon_ms.o diff --git a/linux-core/ObjectID.h b/linux-core/ObjectID.h new file mode 100644 index 00000000..4b106cf6 --- /dev/null +++ b/linux-core/ObjectID.h @@ -0,0 +1,484 @@ +/* +* Copyright 2006-2007 Advanced Micro Devices, Inc.   +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL +* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. +*/ +/* based on stg/asic_reg/drivers/inc/asic_reg/ObjectID.h ver 23 */ + +#ifndef _OBJECTID_H +#define _OBJECTID_H + +#if defined(_X86_) +#pragma pack(1) +#endif + +/****************************************************/ +/* Graphics Object Type Definition                  */ +/****************************************************/ +#define GRAPH_OBJECT_TYPE_NONE                    0x0 +#define GRAPH_OBJECT_TYPE_GPU                     0x1 +#define GRAPH_OBJECT_TYPE_ENCODER                 0x2 +#define GRAPH_OBJECT_TYPE_CONNECTOR               0x3 +#define GRAPH_OBJECT_TYPE_ROUTER                  0x4 +/* deleted */ + +/****************************************************/ +/* Encoder Object ID Definition                     */ +/****************************************************/ +#define ENCODER_OBJECT_ID_NONE                    0x00  + +/* Radeon Class Display Hardware */ +#define ENCODER_OBJECT_ID_INTERNAL_LVDS           0x01 +#define ENCODER_OBJECT_ID_INTERNAL_TMDS1          0x02 +#define ENCODER_OBJECT_ID_INTERNAL_TMDS2          0x03 +#define ENCODER_OBJECT_ID_INTERNAL_DAC1           0x04 +#define ENCODER_OBJECT_ID_INTERNAL_DAC2           0x05     /* TV/CV DAC */ +#define ENCODER_OBJECT_ID_INTERNAL_SDVOA          0x06 +#define ENCODER_OBJECT_ID_INTERNAL_SDVOB          0x07 + +/* External Third Party Encoders */ +#define ENCODER_OBJECT_ID_SI170B                  0x08 +#define ENCODER_OBJECT_ID_CH7303                  0x09 +#define ENCODER_OBJECT_ID_CH7301                  0x0A +#define ENCODER_OBJECT_ID_INTERNAL_DVO1           0x0B    /* This belongs to Radeon Class Display Hardware */ +#define ENCODER_OBJECT_ID_EXTERNAL_SDVOA          0x0C +#define ENCODER_OBJECT_ID_EXTERNAL_SDVOB          0x0D +#define ENCODER_OBJECT_ID_TITFP513                0x0E +#define ENCODER_OBJECT_ID_INTERNAL_LVTM1          0x0F    /* not used for Radeon */ +#define ENCODER_OBJECT_ID_VT1623                  0x10 +#define ENCODER_OBJECT_ID_HDMI_SI1930             0x11 +#define ENCODER_OBJECT_ID_HDMI_INTERNAL           0x12 +/* Kaleidoscope (KLDSCP) Class Display Hardware (internal) */ +#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1   0x13 +#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1    0x14 +#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1    0x15 +#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2    0x16  /* Shared with CV/TV and CRT */ +#define ENCODER_OBJECT_ID_SI178                   0X17  /* External TMDS (dual link, no HDCP.) */ +#define ENCODER_OBJECT_ID_MVPU_FPGA               0x18  /* MVPU FPGA chip */ +#define ENCODER_OBJECT_ID_INTERNAL_DDI            0x19 +#define ENCODER_OBJECT_ID_VT1625                  0x1A +#define ENCODER_OBJECT_ID_HDMI_SI1932             0x1B +#define ENCODER_OBJECT_ID_DP_AN9801               0x1C +#define ENCODER_OBJECT_ID_DP_DP501                0x1D +#define ENCODER_OBJECT_ID_INTERNAL_UNIPHY         0x1E +#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA   0x1F + +/****************************************************/ +/* Connector Object ID Definition                   */ +/****************************************************/ +#define CONNECTOR_OBJECT_ID_NONE                  0x00  +#define CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I     0x01 +#define CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I       0x02 +#define CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D     0x03 +#define CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D       0x04 +#define CONNECTOR_OBJECT_ID_VGA                   0x05 +#define CONNECTOR_OBJECT_ID_COMPOSITE             0x06 +#define CONNECTOR_OBJECT_ID_SVIDEO                0x07 +#define CONNECTOR_OBJECT_ID_YPbPr                 0x08 +#define CONNECTOR_OBJECT_ID_D_CONNECTOR           0x09 +#define CONNECTOR_OBJECT_ID_9PIN_DIN              0x0A  /* Supports both CV & TV */ +#define CONNECTOR_OBJECT_ID_SCART                 0x0B +#define CONNECTOR_OBJECT_ID_HDMI_TYPE_A           0x0C +#define CONNECTOR_OBJECT_ID_HDMI_TYPE_B           0x0D +#define CONNECTOR_OBJECT_ID_LVDS                  0x0E +#define CONNECTOR_OBJECT_ID_7PIN_DIN              0x0F +#define CONNECTOR_OBJECT_ID_PCIE_CONNECTOR        0x10 +#define CONNECTOR_OBJECT_ID_CROSSFIRE             0x11 +#define CONNECTOR_OBJECT_ID_HARDCODE_DVI          0x12 +#define CONNECTOR_OBJECT_ID_DISPLAYPORT           0x13 + +/* deleted */ + +/****************************************************/ +/* Router Object ID Definition                      */ +/****************************************************/ +#define ROUTER_OBJECT_ID_NONE											0x00 +#define ROUTER_OBJECT_ID_I2C_EXTENDER_CNTL				0x01 + +/****************************************************/ +// Graphics Object ENUM ID Definition               */ +/****************************************************/ +#define GRAPH_OBJECT_ENUM_ID1                     0x01 +#define GRAPH_OBJECT_ENUM_ID2                     0x02 +#define GRAPH_OBJECT_ENUM_ID3                     0x03 +#define GRAPH_OBJECT_ENUM_ID4                     0x04 + +/****************************************************/ +/* Graphics Object ID Bit definition                */ +/****************************************************/ +#define OBJECT_ID_MASK                            0x00FF +#define ENUM_ID_MASK                              0x0700 +#define RESERVED1_ID_MASK                         0x0800 +#define OBJECT_TYPE_MASK                          0x7000 +#define RESERVED2_ID_MASK                         0x8000 +                                                   +#define OBJECT_ID_SHIFT                           0x00 +#define ENUM_ID_SHIFT                             0x08 +#define OBJECT_TYPE_SHIFT                         0x0C + + +/****************************************************/ +/* Graphics Object family definition                */ +/****************************************************/ +#define CONSTRUCTOBJECTFAMILYID(GRAPHICS_OBJECT_TYPE, GRAPHICS_OBJECT_ID) (GRAPHICS_OBJECT_TYPE << OBJECT_TYPE_SHIFT | \ +                                                                           GRAPHICS_OBJECT_ID   << OBJECT_ID_SHIFT) +/****************************************************/ +/* GPU Object ID definition - Shared with BIOS      */ +/****************************************************/ +#define GPU_ENUM_ID1                            ( GRAPH_OBJECT_TYPE_GPU << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT) + +/****************************************************/ +/* Encoder Object ID definition - Shared with BIOS  */ +/****************************************************/ +/* +#define ENCODER_INTERNAL_LVDS_ENUM_ID1        0x2101       +#define ENCODER_INTERNAL_TMDS1_ENUM_ID1       0x2102 +#define ENCODER_INTERNAL_TMDS2_ENUM_ID1       0x2103 +#define ENCODER_INTERNAL_DAC1_ENUM_ID1        0x2104 +#define ENCODER_INTERNAL_DAC2_ENUM_ID1        0x2105 +#define ENCODER_INTERNAL_SDVOA_ENUM_ID1       0x2106 +#define ENCODER_INTERNAL_SDVOB_ENUM_ID1       0x2107 +#define ENCODER_SIL170B_ENUM_ID1              0x2108   +#define ENCODER_CH7303_ENUM_ID1               0x2109 +#define ENCODER_CH7301_ENUM_ID1               0x210A +#define ENCODER_INTERNAL_DVO1_ENUM_ID1        0x210B +#define ENCODER_EXTERNAL_SDVOA_ENUM_ID1       0x210C +#define ENCODER_EXTERNAL_SDVOB_ENUM_ID1       0x210D +#define ENCODER_TITFP513_ENUM_ID1             0x210E +#define ENCODER_INTERNAL_LVTM1_ENUM_ID1       0x210F +#define ENCODER_VT1623_ENUM_ID1               0x2110 +#define ENCODER_HDMI_SI1930_ENUM_ID1          0x2111 +#define ENCODER_HDMI_INTERNAL_ENUM_ID1        0x2112 +#define ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID1   0x2113 +#define ENCODER_INTERNAL_KLDSCP_DVO1_ENUM_ID1    0x2114 +#define ENCODER_INTERNAL_KLDSCP_DAC1_ENUM_ID1    0x2115 +#define ENCODER_INTERNAL_KLDSCP_DAC2_ENUM_ID1    0x2116   +#define ENCODER_SI178_ENUM_ID1                   0x2117  +#define ENCODER_MVPU_FPGA_ENUM_ID1               0x2118 +#define ENCODER_INTERNAL_DDI_ENUM_ID1            0x2119 +#define ENCODER_VT1625_ENUM_ID1               0x211A +#define ENCODER_HDMI_SI1932_ENUM_ID1             0x211B +#define ENCODER_ENCODER_DP_AN9801_ENUM_ID1       0x211C +#define ENCODER_DP_DP501_ENUM_ID1                0x211D +#define ENCODER_INTERNAL_UNIPHY_ENUM_ID1         0x211E +*/ +#define ENCODER_INTERNAL_LVDS_ENUM_ID1     ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_INTERNAL_LVDS << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_TMDS1_ENUM_ID1    ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_INTERNAL_TMDS1 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_TMDS2_ENUM_ID1    ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_INTERNAL_TMDS2 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_DAC1_ENUM_ID1     ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_INTERNAL_DAC1 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_DAC2_ENUM_ID1     ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_INTERNAL_DAC2 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_SDVOA_ENUM_ID1    ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_INTERNAL_SDVOA << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_SDVOA_ENUM_ID2    ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_INTERNAL_SDVOA << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_SDVOB_ENUM_ID1    ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_INTERNAL_SDVOB << OBJECT_ID_SHIFT) + +#define ENCODER_SIL170B_ENUM_ID1           ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_SI170B << OBJECT_ID_SHIFT) + +#define ENCODER_CH7303_ENUM_ID1            ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_CH7303 << OBJECT_ID_SHIFT) + +#define ENCODER_CH7301_ENUM_ID1            ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_CH7301 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_DVO1_ENUM_ID1     ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_INTERNAL_DVO1 << OBJECT_ID_SHIFT) + +#define ENCODER_EXTERNAL_SDVOA_ENUM_ID1    ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_EXTERNAL_SDVOA << OBJECT_ID_SHIFT) + +#define ENCODER_EXTERNAL_SDVOA_ENUM_ID2    ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_EXTERNAL_SDVOA << OBJECT_ID_SHIFT) + + +#define ENCODER_EXTERNAL_SDVOB_ENUM_ID1    ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_EXTERNAL_SDVOB << OBJECT_ID_SHIFT) + + +#define ENCODER_TITFP513_ENUM_ID1          ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_TITFP513 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_LVTM1_ENUM_ID1    ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_INTERNAL_LVTM1 << OBJECT_ID_SHIFT) + +#define ENCODER_VT1623_ENUM_ID1            ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_VT1623 << OBJECT_ID_SHIFT) + +#define ENCODER_HDMI_SI1930_ENUM_ID1       ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_HDMI_SI1930 << OBJECT_ID_SHIFT) + +#define ENCODER_HDMI_INTERNAL_ENUM_ID1     ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_HDMI_INTERNAL << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID1   ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                                   GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                   ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 << OBJECT_ID_SHIFT) + + +#define ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID2   ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                                   GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ +                                                   ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 << OBJECT_ID_SHIFT) + + +#define ENCODER_INTERNAL_KLDSCP_DVO1_ENUM_ID1    ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                                   GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                   ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_KLDSCP_DAC1_ENUM_ID1    ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                                   GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                   ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_KLDSCP_DAC2_ENUM_ID1    ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                                   GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                   ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2 << OBJECT_ID_SHIFT)  // Shared with CV/TV and CRT + +#define ENCODER_SI178_ENUM_ID1                    ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                                   GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                   ENCODER_OBJECT_ID_SI178 << OBJECT_ID_SHIFT)   + +#define ENCODER_MVPU_FPGA_ENUM_ID1                ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                                   GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                   ENCODER_OBJECT_ID_MVPU_FPGA << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_DDI_ENUM_ID1     (  GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_INTERNAL_DDI << OBJECT_ID_SHIFT)  + +#define ENCODER_VT1625_ENUM_ID1            ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_VT1625 << OBJECT_ID_SHIFT) + +#define ENCODER_HDMI_SI1932_ENUM_ID1       ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_HDMI_SI1932 << OBJECT_ID_SHIFT) + +#define ENCODER_DP_DP501_ENUM_ID1            ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_DP_DP501 << OBJECT_ID_SHIFT) + +#define ENCODER_DP_AN9801_ENUM_ID1            ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                             GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                             ENCODER_OBJECT_ID_DP_AN9801 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_UNIPHY_ENUM_ID1         ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                 ENCODER_OBJECT_ID_INTERNAL_UNIPHY << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_UNIPHY_ENUM_ID2         ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ +                                                 ENCODER_OBJECT_ID_INTERNAL_UNIPHY << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_KLDSCP_LVTMA_ENUM_ID1   ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                 ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA << OBJECT_ID_SHIFT)   + +/****************************************************/ +/* Connector Object ID definition - Shared with BIOS */ +/****************************************************/ +/* +#define CONNECTOR_SINGLE_LINK_DVI_I_ENUM_ID1        0x3101 +#define CONNECTOR_DUAL_LINK_DVI_I_ENUM_ID1          0x3102 +#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID1        0x3103 +#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID1          0x3104 +#define CONNECTOR_VGA_ENUM_ID1                      0x3105 +#define CONNECTOR_COMPOSITE_ENUM_ID1                0x3106 +#define CONNECTOR_SVIDEO_ENUM_ID1                   0x3107 +#define CONNECTOR_YPbPr_ENUM_ID1                    0x3108 +#define CONNECTOR_D_CONNECTORE_ENUM_ID1             0x3109 +#define CONNECTOR_9PIN_DIN_ENUM_ID1                 0x310A +#define CONNECTOR_SCART_ENUM_ID1                    0x310B +#define CONNECTOR_HDMI_TYPE_A_ENUM_ID1              0x310C +#define CONNECTOR_HDMI_TYPE_B_ENUM_ID1              0x310D +#define CONNECTOR_LVDS_ENUM_ID1                     0x310E +#define CONNECTOR_7PIN_DIN_ENUM_ID1                 0x310F +#define CONNECTOR_PCIE_CONNECTOR_ENUM_ID1           0x3110 +*/ +#define CONNECTOR_LVDS_ENUM_ID1                ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_LVDS << OBJECT_ID_SHIFT) + +#define CONNECTOR_SINGLE_LINK_DVI_I_ENUM_ID1   ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I << OBJECT_ID_SHIFT) + +#define CONNECTOR_SINGLE_LINK_DVI_I_ENUM_ID2   ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I << OBJECT_ID_SHIFT) + +#define CONNECTOR_DUAL_LINK_DVI_I_ENUM_ID1     ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I << OBJECT_ID_SHIFT) + +#define CONNECTOR_DUAL_LINK_DVI_I_ENUM_ID2     ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I << OBJECT_ID_SHIFT) + +#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID1   ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT) + +#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID2   ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT) + +#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID1     ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT) + +#define CONNECTOR_VGA_ENUM_ID1                 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_VGA << OBJECT_ID_SHIFT) + +#define CONNECTOR_VGA_ENUM_ID2                 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_VGA << OBJECT_ID_SHIFT) + +#define CONNECTOR_COMPOSITE_ENUM_ID1           ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_COMPOSITE << OBJECT_ID_SHIFT) + +#define CONNECTOR_SVIDEO_ENUM_ID1              ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_SVIDEO << OBJECT_ID_SHIFT) + +#define CONNECTOR_YPbPr_ENUM_ID1               ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_YPbPr << OBJECT_ID_SHIFT) + +#define CONNECTOR_D_CONNECTOR_ENUM_ID1         ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_D_CONNECTOR << OBJECT_ID_SHIFT) + +#define CONNECTOR_9PIN_DIN_ENUM_ID1            ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_9PIN_DIN << OBJECT_ID_SHIFT) + +#define CONNECTOR_SCART_ENUM_ID1               ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_SCART << OBJECT_ID_SHIFT) + +#define CONNECTOR_HDMI_TYPE_A_ENUM_ID1         ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT) + +#define CONNECTOR_HDMI_TYPE_B_ENUM_ID1         ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_HDMI_TYPE_B << OBJECT_ID_SHIFT) + +#define CONNECTOR_7PIN_DIN_ENUM_ID1            ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_7PIN_DIN << OBJECT_ID_SHIFT) + +#define CONNECTOR_PCIE_CONNECTOR_ENUM_ID1      ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_PCIE_CONNECTOR << OBJECT_ID_SHIFT) + +#define CONNECTOR_PCIE_CONNECTOR_ENUM_ID2      ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_PCIE_CONNECTOR << OBJECT_ID_SHIFT) + +#define CONNECTOR_CROSSFIRE_ENUM_ID1           ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_CROSSFIRE << OBJECT_ID_SHIFT) + +#define CONNECTOR_CROSSFIRE_ENUM_ID2           ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_CROSSFIRE << OBJECT_ID_SHIFT) + + +#define CONNECTOR_HARDCODE_DVI_ENUM_ID1        ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_HARDCODE_DVI << OBJECT_ID_SHIFT) + +#define CONNECTOR_HARDCODE_DVI_ENUM_ID2        ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_HARDCODE_DVI << OBJECT_ID_SHIFT) + +#define CONNECTOR_DISPLAYPORT_ENUM_ID1         ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT) + +#define CONNECTOR_DISPLAYPORT_ENUM_ID2         ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ +                                                 GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ +                                                 CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT) + +/****************************************************/ +/* Router Object ID definition - Shared with BIOS   */ +/****************************************************/ +#define ROUTER_I2C_EXTENDER_CNTL_ENUM_ID1      ( GRAPH_OBJECT_TYPE_ROUTER << OBJECT_TYPE_SHIFT |\ +                                                GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ +                                                ROUTER_OBJECT_ID_I2C_EXTENDER_CNTL << OBJECT_ID_SHIFT) + +/* deleted */ + +/****************************************************/ +/* Object Cap definition - Shared with BIOS         */ +/****************************************************/ +#define GRAPHICS_OBJECT_CAP_I2C                 0x00000001L +#define GRAPHICS_OBJECT_CAP_TABLE_ID            0x00000002L + + +#define GRAPHICS_OBJECT_I2CCOMMAND_TABLE_ID                   0x01 +#define GRAPHICS_OBJECT_HOTPLUGDETECTIONINTERUPT_TABLE_ID     0x02 +#define GRAPHICS_OBJECT_ENCODER_OUTPUT_PROTECTION_TABLE_ID    0x03 + +#if defined(_X86_) +#pragma pack() +#endif + +#endif  /*GRAPHICTYPE */ + + + + diff --git a/linux-core/amd.h b/linux-core/amd.h new file mode 120000 index 00000000..b4882447 --- /dev/null +++ b/linux-core/amd.h @@ -0,0 +1 @@ +../shared-core/amd.h
\ No newline at end of file diff --git a/linux-core/amd_legacy.h b/linux-core/amd_legacy.h new file mode 120000 index 00000000..1a7786fc --- /dev/null +++ b/linux-core/amd_legacy.h @@ -0,0 +1 @@ +../shared-core/amd_legacy.h
\ No newline at end of file diff --git a/linux-core/amd_legacy_cbuffer.c b/linux-core/amd_legacy_cbuffer.c new file mode 120000 index 00000000..eab329b5 --- /dev/null +++ b/linux-core/amd_legacy_cbuffer.c @@ -0,0 +1 @@ +../shared-core/amd_legacy_cbuffer.c
\ No newline at end of file diff --git a/linux-core/amd_legacy_fence.h b/linux-core/amd_legacy_fence.h new file mode 120000 index 00000000..e7b30f25 --- /dev/null +++ b/linux-core/amd_legacy_fence.h @@ -0,0 +1 @@ +../shared-core/amd_legacy_fence.h
\ No newline at end of file diff --git a/linux-core/ati_pcigart.c b/linux-core/ati_pcigart.c index 40f8f8dd..3aa445e8 100644 --- a/linux-core/ati_pcigart.c +++ b/linux-core/ati_pcigart.c @@ -61,8 +61,28 @@ static __inline__ void gart_insert_page_into_table(struct drm_ati_pcigart_info *  	*pci_gart = cpu_to_le32(page_base);  } -static int drm_ati_alloc_pcigart_table(struct drm_device *dev, -				       struct drm_ati_pcigart_info *gart_info) +static __inline__ dma_addr_t gart_get_page_from_table(struct drm_ati_pcigart_info *gart_info, u32 *pci_gart) +{ +	dma_addr_t retval; +	switch(gart_info->gart_reg_if) { +	case DRM_ATI_GART_IGP: +		retval = (*pci_gart & ATI_PCIGART_PAGE_MASK); +		retval += (((*pci_gart & 0xf0) >> 4) << 16) << 16; +		break; +	case DRM_ATI_GART_PCIE: +		retval = (*pci_gart & ~0xc); +		retval <<= 8; +		break; +	case DRM_ATI_GART_PCI: +		retval = *pci_gart; +		break; +	} +	 +	return retval; +} + +int drm_ati_alloc_pcigart_table(struct drm_device *dev, +				struct drm_ati_pcigart_info *gart_info)  {  	gart_info->table_handle = drm_pci_alloc(dev, gart_info->table_size,  						PAGE_SIZE, @@ -70,8 +90,10 @@ static int drm_ati_alloc_pcigart_table(struct drm_device *dev,  	if (gart_info->table_handle == NULL)  		return -ENOMEM; +	memset(gart_info->table_handle, 0, gart_info->table_size);  	return 0;  } +EXPORT_SYMBOL(drm_ati_alloc_pcigart_table);  static void drm_ati_free_pcigart_table(struct drm_device *dev,  				       struct drm_ati_pcigart_info *gart_info) @@ -132,12 +154,8 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga  	int max_pages;  	dma_addr_t entry_addr; -	if (!entry) { -		DRM_ERROR("no scatter/gather memory!\n"); -		goto done; -	} -	if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) { +	if (gart_info->gart_table_location == DRM_ATI_GART_MAIN && gart_info->table_handle == NULL) {  		DRM_DEBUG("PCI: no table in VRAM: using normal RAM\n");  		ret = drm_ati_alloc_pcigart_table(dev, gart_info); @@ -145,14 +163,19 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga  			DRM_ERROR("cannot allocate PCI GART page!\n");  			goto done;  		} +	} +	if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) {  		address = gart_info->table_handle->vaddr;  		bus_address = gart_info->table_handle->busaddr;  	} else {  		address = gart_info->addr;  		bus_address = gart_info->bus_addr; -		DRM_DEBUG("PCI: Gart Table: VRAM %08X mapped at %08lX\n", -			  bus_address, (unsigned long)address); +	} + +	if (!entry) { +		DRM_ERROR("no scatter/gather memory!\n"); +		goto done;  	}  	pci_gart = (u32 *) address; @@ -161,8 +184,6 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga  	pages = (entry->pages <= max_pages)  	    ? entry->pages : max_pages; -	memset(pci_gart, 0, max_pages * sizeof(u32)); -  	for (i = 0; i < pages; i++) {  		/* we need to support large memory configurations */  		entry->busaddr[i] = pci_map_page(dev->pdev, entry->pagelist[i], @@ -197,3 +218,145 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga  	return ret;  }  EXPORT_SYMBOL(drm_ati_pcigart_init); + +static int ati_pcigart_needs_unbind_cache_adjust(struct drm_ttm_backend *backend) +{ +	return ((backend->flags & DRM_BE_FLAG_BOUND_CACHED) ? 0 : 1); +} + +static int ati_pcigart_populate(struct drm_ttm_backend *backend, +				unsigned long num_pages, +				struct page **pages, +				struct page *dummy_read_page) +{ +	struct ati_pcigart_ttm_backend *atipci_be = +		container_of(backend, struct ati_pcigart_ttm_backend, backend); + +	atipci_be->pages = pages; +	atipci_be->num_pages = num_pages; +	atipci_be->populated = 1; +	return 0; +} + +static int ati_pcigart_bind_ttm(struct drm_ttm_backend *backend, +				struct drm_bo_mem_reg *bo_mem) +{ +	struct ati_pcigart_ttm_backend *atipci_be = +		container_of(backend, struct ati_pcigart_ttm_backend, backend); +        off_t j; +	int i; +	struct drm_ati_pcigart_info *info = atipci_be->gart_info; +	u32 *pci_gart; +	dma_addr_t offset = bo_mem->mm_node->start; +	dma_addr_t page_base; + +	pci_gart = info->addr; + +        j = offset; +        while (j < (offset + atipci_be->num_pages)) { +		if (gart_get_page_from_table(info, pci_gart+j)) +			return -EBUSY; +                j++; +        } + +        for (i = 0, j = offset; i < atipci_be->num_pages; i++, j++) { +		struct page *cur_page = atipci_be->pages[i]; +                /* write value */ +		page_base = page_to_phys(cur_page); +		gart_insert_page_into_table(info, page_base, pci_gart + j); +        } + +#if defined(__i386__) || defined(__x86_64__) +	wbinvd(); +#else +	mb(); +#endif + +	atipci_be->gart_flush_fn(atipci_be->dev); + +	atipci_be->bound = 1; +	atipci_be->offset = offset; +        /* need to traverse table and add entries */ +	DRM_DEBUG("\n"); +	return 0; +} + +static int ati_pcigart_unbind_ttm(struct drm_ttm_backend *backend) +{ +	struct ati_pcigart_ttm_backend *atipci_be = +		container_of(backend, struct ati_pcigart_ttm_backend, backend); +	struct drm_ati_pcigart_info *info = atipci_be->gart_info;	 +	unsigned long offset = atipci_be->offset; +	int i; +	off_t j; +	u32 *pci_gart = info->addr; + +	if (atipci_be->bound != 1) +		return -EINVAL; + +	for (i = 0, j = offset; i < atipci_be->num_pages; i++, j++) { +		*(pci_gart + j) = 0; +	} +	atipci_be->gart_flush_fn(atipci_be->dev); +	atipci_be->bound = 0; +	atipci_be->offset = 0; +	return 0; +} + +static void ati_pcigart_clear_ttm(struct drm_ttm_backend *backend) +{ +	struct ati_pcigart_ttm_backend *atipci_be = +		container_of(backend, struct ati_pcigart_ttm_backend, backend); + +	DRM_DEBUG("\n");	 +	if (atipci_be->pages) { +		backend->func->unbind(backend); +		atipci_be->pages = NULL; + +	} +	atipci_be->num_pages = 0; +} + +static void ati_pcigart_destroy_ttm(struct drm_ttm_backend *backend) +{ +	struct ati_pcigart_ttm_backend *atipci_be; +	if (backend) { +		DRM_DEBUG("\n"); +		atipci_be = container_of(backend, struct ati_pcigart_ttm_backend, backend); +		if (atipci_be) { +			if (atipci_be->pages) { +				backend->func->clear(backend); +			} +			drm_ctl_free(atipci_be, sizeof(*atipci_be), DRM_MEM_TTM); +		} +	} +} + +static struct drm_ttm_backend_func ati_pcigart_ttm_backend =  +{ +	.needs_ub_cache_adjust = ati_pcigart_needs_unbind_cache_adjust, +	.populate = ati_pcigart_populate, +	.clear = ati_pcigart_clear_ttm, +	.bind = ati_pcigart_bind_ttm, +	.unbind = ati_pcigart_unbind_ttm, +	.destroy =  ati_pcigart_destroy_ttm, +}; + +struct drm_ttm_backend *ati_pcigart_init_ttm(struct drm_device *dev, struct drm_ati_pcigart_info *info, void (*gart_flush_fn)(struct drm_device *dev)) +{ +	struct ati_pcigart_ttm_backend *atipci_be; + +	atipci_be = drm_ctl_calloc(1, sizeof (*atipci_be), DRM_MEM_TTM); +	if (!atipci_be) +		return NULL; +	 +	atipci_be->populated = 0; +	atipci_be->backend.func = &ati_pcigart_ttm_backend; +//	atipci_be->backend.mem_type = DRM_BO_MEM_TT; +	atipci_be->gart_info = info; +	atipci_be->gart_flush_fn = gart_flush_fn; +	atipci_be->dev = dev; + +	return &atipci_be->backend; +} +EXPORT_SYMBOL(ati_pcigart_init_ttm); diff --git a/linux-core/atom-bits.h b/linux-core/atom-bits.h new file mode 100644 index 00000000..f94d2e27 --- /dev/null +++ b/linux-core/atom-bits.h @@ -0,0 +1,48 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc.   + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: Stanislaw Skowronek + */ + +#ifndef ATOM_BITS_H +#define ATOM_BITS_H + +static inline uint8_t get_u8(void *bios, int ptr) +{ +    return ((unsigned char *)bios)[ptr]; +} +#define U8(ptr) get_u8(ctx->ctx->bios,(ptr)) +#define CU8(ptr) get_u8(ctx->bios,(ptr)) +static inline uint16_t get_u16(void *bios, int ptr) +{ +    return get_u8(bios,ptr)|(((uint16_t)get_u8(bios,ptr+1))<<8); +} +#define U16(ptr) get_u16(ctx->ctx->bios,(ptr)) +#define CU16(ptr) get_u16(ctx->bios,(ptr)) +static inline uint32_t get_u32(void *bios, int ptr) +{ +    return get_u16(bios,ptr)|(((uint32_t)get_u16(bios,ptr+2))<<16); +} +#define U32(ptr) get_u32(ctx->ctx->bios,(ptr)) +#define CU32(ptr) get_u32(ctx->bios,(ptr)) +#define CSTR(ptr) (((char *)(ctx->bios))+(ptr)) + +#endif diff --git a/linux-core/atom-names.h b/linux-core/atom-names.h new file mode 100644 index 00000000..2cdc170b --- /dev/null +++ b/linux-core/atom-names.h @@ -0,0 +1,100 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc.   + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: Stanislaw Skowronek + */ + +#ifndef ATOM_NAMES_H +#define ATOM_NAMES_H + +#include "atom.h" + +#ifdef ATOM_DEBUG + +#define ATOM_OP_NAMES_CNT 123 +static char *atom_op_names[ATOM_OP_NAMES_CNT]={ +"RESERVED", "MOVE_REG", "MOVE_PS", "MOVE_WS", "MOVE_FB", "MOVE_PLL", +"MOVE_MC", "AND_REG", "AND_PS", "AND_WS", "AND_FB", "AND_PLL", "AND_MC", +"OR_REG", "OR_PS", "OR_WS", "OR_FB", "OR_PLL", "OR_MC", "SHIFT_LEFT_REG", +"SHIFT_LEFT_PS", "SHIFT_LEFT_WS", "SHIFT_LEFT_FB", "SHIFT_LEFT_PLL", +"SHIFT_LEFT_MC", "SHIFT_RIGHT_REG", "SHIFT_RIGHT_PS", "SHIFT_RIGHT_WS", +"SHIFT_RIGHT_FB", "SHIFT_RIGHT_PLL", "SHIFT_RIGHT_MC", "MUL_REG", +"MUL_PS", "MUL_WS", "MUL_FB", "MUL_PLL", "MUL_MC", "DIV_REG", "DIV_PS", +"DIV_WS", "DIV_FB", "DIV_PLL", "DIV_MC", "ADD_REG", "ADD_PS", "ADD_WS", +"ADD_FB", "ADD_PLL", "ADD_MC", "SUB_REG", "SUB_PS", "SUB_WS", "SUB_FB", +"SUB_PLL", "SUB_MC", "SET_ATI_PORT", "SET_PCI_PORT", "SET_SYS_IO_PORT", +"SET_REG_BLOCK", "SET_FB_BASE", "COMPARE_REG", "COMPARE_PS", +"COMPARE_WS", "COMPARE_FB", "COMPARE_PLL", "COMPARE_MC", "SWITCH", +"JUMP", "JUMP_EQUAL", "JUMP_BELOW", "JUMP_ABOVE", "JUMP_BELOW_OR_EQUAL", +"JUMP_ABOVE_OR_EQUAL", "JUMP_NOT_EQUAL", "TEST_REG", "TEST_PS", "TEST_WS", +"TEST_FB", "TEST_PLL", "TEST_MC", "DELAY_MILLISEC", "DELAY_MICROSEC", +"CALL_TABLE", "REPEAT", "CLEAR_REG", "CLEAR_PS", "CLEAR_WS", "CLEAR_FB", +"CLEAR_PLL", "CLEAR_MC", "NOP", "EOT", "MASK_REG", "MASK_PS", "MASK_WS", +"MASK_FB", "MASK_PLL", "MASK_MC", "POST_CARD", "BEEP", "SAVE_REG", +"RESTORE_REG", "SET_DATA_BLOCK", "XOR_REG", "XOR_PS", "XOR_WS", "XOR_FB", +"XOR_PLL", "XOR_MC", "SHL_REG", "SHL_PS", "SHL_WS", "SHL_FB", "SHL_PLL", +"SHL_MC", "SHR_REG", "SHR_PS", "SHR_WS", "SHR_FB", "SHR_PLL", "SHR_MC", +"DEBUG", "CTB_DS", +}; + +#define ATOM_TABLE_NAMES_CNT 74 +static char *atom_table_names[ATOM_TABLE_NAMES_CNT]={ +"ASIC_Init", "GetDisplaySurfaceSize", "ASIC_RegistersInit", +"VRAM_BlockVenderDetection", "SetClocksRatio", "MemoryControllerInit", +"GPIO_PinInit", "MemoryParamAdjust", "DVOEncoderControl", +"GPIOPinControl", "SetEngineClock", "SetMemoryClock", "SetPixelClock", +"DynamicClockGating", "ResetMemoryDLL", "ResetMemoryDevice", +"MemoryPLLInit", "EnableMemorySelfRefresh", "AdjustMemoryController", +"EnableASIC_StaticPwrMgt", "ASIC_StaticPwrMgtStatusChange", +"DAC_LoadDetection", "TMDS2EncoderControl", "LCD1OutputControl", +"DAC1EncoderControl", "DAC2EncoderControl", "DVOOutputControl", +"CV1OutputControl", "SetCRTC_DPM_State", "TVEncoderControl", +"TMDS1EncoderControl", "LVDSEncoderControl", "TV1OutputControl", +"EnableScaler", "BlankCRTC", "EnableCRTC", "GetPixelClock", +"EnableVGA_Render", "EnableVGA_Access", "SetCRTC_Timing", +"SetCRTC_OverScan", "SetCRTC_Replication", "SelectCRTC_Source", +"EnableGraphSurfaces", "UpdateCRTC_DoubleBufferRegisters", +"LUT_AutoFill", "EnableHW_IconCursor", "GetMemoryClock", +"GetEngineClock", "SetCRTC_UsingDTDTiming", "TVBootUpStdPinDetection", +"DFP2OutputControl", "VRAM_BlockDetectionByStrap", "MemoryCleanUp", +"ReadEDIDFromHWAssistedI2C", "WriteOneByteToHWAssistedI2C", +"ReadHWAssistedI2CStatus", "SpeedFanControl", "PowerConnectorDetection", +"MC_Synchronization", "ComputeMemoryEnginePLL", "MemoryRefreshConversion", +"VRAM_GetCurrentInfoBlock", "DynamicMemorySettings", "MemoryTraining", +"EnableLVDS_SS", "DFP1OutputControl", "SetVoltage", "CRT1OutputControl", +"CRT2OutputControl", "SetupHWAssistedI2CStatus", "ClockSource", +"MemoryDeviceInit", "EnableYUV", +}; + +#define ATOM_IO_NAMES_CNT 5 +static char *atom_io_names[ATOM_IO_NAMES_CNT]={ +"MM", "PLL", "MC", "PCIE", "PCIE PORT", +}; + +#else + +#define ATOM_OP_NAMES_CNT 0 +#define ATOM_TABLE_NAMES_CNT 0 +#define ATOM_IO_NAMES_CNT 0 + +#endif + +#endif diff --git a/linux-core/atom-types.h b/linux-core/atom-types.h new file mode 100644 index 00000000..1125b866 --- /dev/null +++ b/linux-core/atom-types.h @@ -0,0 +1,42 @@ +/* + * Copyright 2008 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: Dave Airlie + */ + +#ifndef ATOM_TYPES_H +#define ATOM_TYPES_H + +/* sync atom types to kernel types */ + +typedef uint16_t USHORT; +typedef uint32_t ULONG; +typedef uint8_t UCHAR; + + +#ifndef ATOM_BIG_ENDIAN +#if defined(__BIG_ENDIAN) +#define ATOM_BIG_ENDIAN 1 +#else +#define ATOM_BIG_ENDIAN 0 +#endif +#endif +#endif diff --git a/linux-core/atom.c b/linux-core/atom.c new file mode 100644 index 00000000..33fb02f0 --- /dev/null +++ b/linux-core/atom.c @@ -0,0 +1,1145 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc.   + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: Stanislaw Skowronek + */ + +#include <linux/module.h> +#include <linux/sched.h> + +#define ATOM_DEBUG + +#include "atom.h" +#include "atom-names.h" +#include "atom-bits.h" + +#define ATOM_COND_ABOVE		0 +#define ATOM_COND_ABOVEOREQUAL	1 +#define ATOM_COND_ALWAYS	2 +#define ATOM_COND_BELOW		3 +#define ATOM_COND_BELOWOREQUAL	4 +#define ATOM_COND_EQUAL		5 +#define ATOM_COND_NOTEQUAL	6 + +#define ATOM_PORT_ATI	0 +#define ATOM_PORT_PCI	1 +#define ATOM_PORT_SYSIO	2 + +#define ATOM_UNIT_MICROSEC	0 +#define ATOM_UNIT_MILLISEC	1 + +#define PLL_INDEX	2 +#define PLL_DATA	3 + +typedef struct { +    struct atom_context *ctx; + +    uint32_t *ps, *ws; +    int ps_shift; +    uint16_t start; +} atom_exec_context; + +int atom_debug = 0; +void atom_execute_table(struct atom_context *ctx, int index, uint32_t *params); + +static uint32_t atom_arg_mask[8] = {0xFFFFFFFF, 0xFFFF, 0xFFFF00, 0xFFFF0000, 0xFF, 0xFF00, 0xFF0000, 0xFF000000}; +static int atom_arg_shift[8] = {0, 0, 8, 16, 0, 8, 16, 24}; +static int atom_dst_to_src[8][4] = {	// translate destination alignment field to the source alignment encoding +    { 0, 0, 0, 0 }, +    { 1, 2, 3, 0 }, +    { 1, 2, 3, 0 }, +    { 1, 2, 3, 0 }, +    { 4, 5, 6, 7 }, +    { 4, 5, 6, 7 }, +    { 4, 5, 6, 7 }, +    { 4, 5, 6, 7 }, +}; +static int atom_def_dst[8] = { 0, 0, 1, 2, 0, 1, 2, 3 }; + +static int debug_depth = 0; +#ifdef ATOM_DEBUG +static void debug_print_spaces(int n) +{ +    while(n--) +	printk("   "); +} +#define DEBUG(...) do if(atom_debug) { printk(KERN_DEBUG __VA_ARGS__); } while(0) +#define SDEBUG(...) do if(atom_debug) { printk(KERN_DEBUG); debug_print_spaces(debug_depth); printk(__VA_ARGS__); } while(0) +#else +#define DEBUG(...) do { } while(0) +#define SDEBUG(...) do { } while(0) +#endif + +static uint32_t atom_iio_execute(struct atom_context *ctx, int base, uint32_t index, uint32_t data) +{ +    uint32_t temp = 0xCDCDCDCD; +    while(1) +	switch(CU8(base)) { +	case ATOM_IIO_NOP: +	    base++; +	    break; +	case ATOM_IIO_READ: +	    temp = ctx->card->reg_read(ctx->card, CU16(base+1)); +	    base+=3; +	    break; +	case ATOM_IIO_WRITE: +	    ctx->card->reg_write(ctx->card, CU16(base+1), temp); +	    base+=3; +	    break; +	case ATOM_IIO_CLEAR: +	    temp &= ~((0xFFFFFFFF >> (32-CU8(base+1))) << CU8(base+2)); +	    base+=3; +	    break; +	case ATOM_IIO_SET: +	    temp |= (0xFFFFFFFF >> (32-CU8(base+1))) << CU8(base+2); +	    base+=3; +	    break; +	case ATOM_IIO_MOVE_INDEX: +	    temp &= ~((0xFFFFFFFF >> (32-CU8(base+1))) << CU8(base+2)); +	    temp |= ((index >> CU8(base+2)) & (0xFFFFFFFF >> (32-CU8(base+1)))) << CU8(base+3); +	    base+=4; +	    break; +	case ATOM_IIO_MOVE_DATA: +	    temp &= ~((0xFFFFFFFF >> (32-CU8(base+1))) << CU8(base+2)); +	    temp |= ((data >> CU8(base+2)) & (0xFFFFFFFF >> (32-CU8(base+1)))) << CU8(base+3); +	    base+=4; +	    break; +	case ATOM_IIO_MOVE_ATTR: +	    temp &= ~((0xFFFFFFFF >> (32-CU8(base+1))) << CU8(base+2)); +	    temp |= ((ctx->io_attr >> CU8(base+2)) & (0xFFFFFFFF >> (32-CU8(base+1)))) << CU8(base+3); +	    base+=4; +	    break; +	case ATOM_IIO_END: +	    return temp; +	default: +	    printk(KERN_INFO "Unknown IIO opcode.\n"); +	    return 0; +	} +} + +static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr, int *ptr, uint32_t *saved, int print) +{ +    uint32_t idx, val = 0xCDCDCDCD, align, arg; +    struct atom_context *gctx = ctx->ctx; +    arg = attr & 7; +    align = (attr >> 3) & 7; +    switch(arg) { +    case ATOM_ARG_REG: +	idx = U16(*ptr); +	(*ptr)+=2; +	if(print) +	    DEBUG("REG[0x%04X]", idx); +	idx += gctx->reg_block; +	switch(gctx->io_mode) { +	case ATOM_IO_MM: +	    val = gctx->card->reg_read(gctx->card, idx); +	    break; +	case ATOM_IO_PCI: +	    printk(KERN_INFO "PCI registers are not implemented.\n"); +	    return 0; +	case ATOM_IO_SYSIO: +	    printk(KERN_INFO "SYSIO registers are not implemented.\n"); +	    return 0; +	default: +	    if(!(gctx->io_mode&0x80)) { +		printk(KERN_INFO "Bad IO mode.\n"); +		return 0; +	    } +	    if(!gctx->iio[gctx->io_mode&0x7F]) { +		printk(KERN_INFO "Undefined indirect IO read method %d.\n", gctx->io_mode&0x7F); +		return 0; +	    } +	    val = atom_iio_execute(gctx, gctx->iio[gctx->io_mode&0x7F], idx, 0); +	} +	break; +    case ATOM_ARG_PS: +	idx = U8(*ptr); +	(*ptr)++; +	val = ctx->ps[idx]; +	if(print) +	    DEBUG("PS[0x%02X,0x%04X]", idx, val); +	break; +    case ATOM_ARG_WS: +	idx = U8(*ptr); +	(*ptr)++; +	if(print) +	    DEBUG("WS[0x%02X]", idx); +	switch(idx) { +	case ATOM_WS_QUOTIENT: +	    val = gctx->divmul[0]; +	    break; +	case ATOM_WS_REMAINDER: +	    val = gctx->divmul[1]; +	    break; +	case ATOM_WS_DATAPTR: +	    val = gctx->data_block; +	    break; +	case ATOM_WS_SHIFT: +	    val = gctx->shift; +	    break; +	case ATOM_WS_OR_MASK: +	    val = 1<<gctx->shift; +	    break; +	case ATOM_WS_AND_MASK: +	    val = ~(1<<gctx->shift); +	    break; +	case ATOM_WS_FB_WINDOW: +	    val = gctx->fb_base; +	    break; +	case ATOM_WS_ATTRIBUTES: +	    val = gctx->io_attr; +	    break; +	default: +	    val = ctx->ws[idx]; +	} +	break; +    case ATOM_ARG_ID: +	idx = U16(*ptr); +	(*ptr)+=2; +	if(print) { +	    if(gctx->data_block) +		DEBUG("ID[0x%04X+%04X]", idx, gctx->data_block); +	    else +		DEBUG("ID[0x%04X]", idx); +	} +	val = U32(idx + gctx->data_block); +	break; +    case ATOM_ARG_FB: +	idx = U8(*ptr); +	(*ptr)++; +	if(print) +	    DEBUG("FB[0x%02X]", idx); +	printk(KERN_INFO "FB access is not implemented.\n"); +	return 0; +    case ATOM_ARG_IMM: +	switch(align) { +	case ATOM_SRC_DWORD: +	    val = U32(*ptr); +	    (*ptr)+=4; +	    if(print) +		DEBUG("IMM 0x%08X\n", val); +	    return val; +	case ATOM_SRC_WORD0: +	case ATOM_SRC_WORD8: +	case ATOM_SRC_WORD16: +	    val = U16(*ptr); +	    (*ptr)+=2; +	    if(print) +		DEBUG("IMM 0x%04X\n", val); +	    return val; +	case ATOM_SRC_BYTE0: +	case ATOM_SRC_BYTE8: +	case ATOM_SRC_BYTE16: +	case ATOM_SRC_BYTE24: +	    val = U8(*ptr); +	    (*ptr)++; +	    if(print) +		DEBUG("IMM 0x%02X\n", val); +	    return val; +	} +	return 0; +    case ATOM_ARG_PLL: +	idx = U8(*ptr); +	(*ptr)++; +	if(print) +	    DEBUG("PLL[0x%02X]", idx); +	gctx->card->reg_write(gctx->card, PLL_INDEX, idx); +	val = gctx->card->reg_read(gctx->card, PLL_DATA); +	break; +    case ATOM_ARG_MC: +	idx = U8(*ptr); +	(*ptr)++; +	if(print) +	    DEBUG("MC[0x%02X]", idx); +	val = gctx->card->mc_read(gctx->card, idx); +	printk(KERN_INFO "MC registers are not implemented.\n"); +	return 0; +    } +    if(saved) +	*saved = val; +    val &= atom_arg_mask[align]; +    val >>= atom_arg_shift[align]; +    if(print) +	switch(align) { +	case ATOM_SRC_DWORD: +	    DEBUG(".[31:0] -> 0x%08X\n", val); +	    break; +	case ATOM_SRC_WORD0: +	    DEBUG(".[15:0] -> 0x%04X\n", val); +	    break; +	case ATOM_SRC_WORD8: +	    DEBUG(".[23:8] -> 0x%04X\n", val); +	    break; +	case ATOM_SRC_WORD16: +	    DEBUG(".[31:16] -> 0x%04X\n", val); +	    break; +	case ATOM_SRC_BYTE0: +	    DEBUG(".[7:0] -> 0x%02X\n", val); +	    break; +	case ATOM_SRC_BYTE8: +	    DEBUG(".[15:8] -> 0x%02X\n", val); +	    break; +	case ATOM_SRC_BYTE16: +	    DEBUG(".[23:16] -> 0x%02X\n", val); +	    break; +	case ATOM_SRC_BYTE24: +	    DEBUG(".[31:24] -> 0x%02X\n", val); +	    break; +	} +    return val; +} + +static void atom_skip_src_int(atom_exec_context *ctx, uint8_t attr, int *ptr) +{ +    uint32_t align = (attr >> 3) & 7, arg = attr & 7; +    switch(arg) { +    case ATOM_ARG_REG: +    case ATOM_ARG_ID: +	(*ptr)+=2; +	break; +    case ATOM_ARG_PLL: +    case ATOM_ARG_MC: +    case ATOM_ARG_PS: +    case ATOM_ARG_WS: +    case ATOM_ARG_FB: +	(*ptr)++; +	break; +    case ATOM_ARG_IMM: +	switch(align) { +	case ATOM_SRC_DWORD: +	    (*ptr)+=4; +	    return; +	case ATOM_SRC_WORD0: +	case ATOM_SRC_WORD8: +	case ATOM_SRC_WORD16: +	    (*ptr)+=2; +	    return; +	case ATOM_SRC_BYTE0: +	case ATOM_SRC_BYTE8: +	case ATOM_SRC_BYTE16: +	case ATOM_SRC_BYTE24: +	    (*ptr)++; +	    return; +	} +	return; +    } +} + +static uint32_t atom_get_src(atom_exec_context *ctx, uint8_t attr, int *ptr) +{ +    return atom_get_src_int(ctx, attr, ptr, NULL, 1); +} + +static uint32_t atom_get_dst(atom_exec_context *ctx, int arg, uint8_t attr, int *ptr, uint32_t *saved, int print) +{ +    return atom_get_src_int(ctx, arg|atom_dst_to_src[(attr>>3)&7][(attr>>6)&3]<<3, ptr, saved, print); +} + +static void atom_skip_dst(atom_exec_context *ctx, int arg, uint8_t attr, int *ptr) +{ +    atom_skip_src_int(ctx, arg|atom_dst_to_src[(attr>>3)&7][(attr>>6)&3]<<3, ptr); +} + +static void atom_put_dst(atom_exec_context *ctx, int arg, uint8_t attr, int *ptr, uint32_t val, uint32_t saved) +{ +    uint32_t align = atom_dst_to_src[(attr>>3)&7][(attr>>6)&3], old_val = val, idx; +    struct atom_context *gctx = ctx->ctx; +    old_val &= atom_arg_mask[align] >> atom_arg_shift[align]; +    val <<= atom_arg_shift[align]; +    val &= atom_arg_mask[align]; +    saved &= ~atom_arg_mask[align]; +    val |= saved; +    switch(arg) { +    case ATOM_ARG_REG: +	idx = U16(*ptr); +	(*ptr)+=2; +	DEBUG("REG[0x%04X]", idx); +	idx += gctx->reg_block; +	switch(gctx->io_mode) { +	case ATOM_IO_MM: +	    if(idx == 0) +		gctx->card->reg_write(gctx->card, idx, val<<2); +	    else +		gctx->card->reg_write(gctx->card, idx, val); +	    break; +	case ATOM_IO_PCI: +	    printk(KERN_INFO "PCI registers are not implemented.\n"); +	    return; +	case ATOM_IO_SYSIO: +	    printk(KERN_INFO "SYSIO registers are not implemented.\n"); +	    return; +	default: +	    if(!(gctx->io_mode&0x80)) { +		printk(KERN_INFO "Bad IO mode.\n"); +		return; +	    } +	    if(!gctx->iio[gctx->io_mode&0xFF]) { +		printk(KERN_INFO "Undefined indirect IO write method %d.\n", gctx->io_mode&0x7F); +		return; +	    } +	    atom_iio_execute(gctx, gctx->iio[gctx->io_mode&0xFF], idx, val); +	} +	break; +    case ATOM_ARG_PS: +	idx = U8(*ptr); +	(*ptr)++; +	DEBUG("PS[0x%02X]", idx); +	ctx->ps[idx] = val; +	break; +    case ATOM_ARG_WS: +	idx = U8(*ptr); +	(*ptr)++; +	DEBUG("WS[0x%02X]", idx); +	switch(idx) { +	case ATOM_WS_QUOTIENT: +	    gctx->divmul[0] = val; +	    break; +	case ATOM_WS_REMAINDER: +	    gctx->divmul[1] = val; +	    break; +	case ATOM_WS_DATAPTR: +	    gctx->data_block = val; +	    break; +	case ATOM_WS_SHIFT: +	    gctx->shift = val; +	    break; +	case ATOM_WS_OR_MASK: +	case ATOM_WS_AND_MASK: +	    break; +	case ATOM_WS_FB_WINDOW: +	    gctx->fb_base = val; +	    break; +	case ATOM_WS_ATTRIBUTES: +	    gctx->io_attr = val; +	    break; +	default: +	    ctx->ws[idx] = val; +	} +	break; +    case ATOM_ARG_FB: +	idx = U8(*ptr); +	(*ptr)++; +	DEBUG("FB[0x%02X]", idx); +	printk(KERN_INFO "FB access is not implemented.\n"); +	return; +    case ATOM_ARG_PLL: +	idx = U8(*ptr); +	(*ptr)++; +	DEBUG("PLL[0x%02X]", idx); +	gctx->card->reg_write(gctx->card, PLL_INDEX, idx); +	gctx->card->reg_write(gctx->card, PLL_DATA, val); +	break; +    case ATOM_ARG_MC: +	idx = U8(*ptr); +	(*ptr)++; +	DEBUG("MC[0x%02X]", idx); +	gctx->card->mc_write(gctx->card, idx, val); +	printk(KERN_INFO "MC registers are not implemented.\n"); +	return; +    } +    switch(align) { +    case ATOM_SRC_DWORD: +	DEBUG(".[31:0] <- 0x%08X\n", old_val); +	break; +    case ATOM_SRC_WORD0: +	DEBUG(".[15:0] <- 0x%04X\n", old_val); +	break; +    case ATOM_SRC_WORD8: +	DEBUG(".[23:8] <- 0x%04X\n", old_val); +	break; +    case ATOM_SRC_WORD16: +	DEBUG(".[31:16] <- 0x%04X\n", old_val); +	break; +    case ATOM_SRC_BYTE0: +	DEBUG(".[7:0] <- 0x%02X\n", old_val); +	break; +    case ATOM_SRC_BYTE8: +	DEBUG(".[15:8] <- 0x%02X\n", old_val); +	break; +    case ATOM_SRC_BYTE16: +	DEBUG(".[23:16] <- 0x%02X\n", old_val); +	break; +    case ATOM_SRC_BYTE24: +	DEBUG(".[31:24] <- 0x%02X\n", old_val); +	break; +    } +} + +static void atom_op_add(atom_exec_context *ctx, int *ptr, int arg) +{ +    uint8_t attr = U8((*ptr)++); +    uint32_t dst, src, saved; +    int dptr = *ptr; +    SDEBUG("   dst: "); +    dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); +    SDEBUG("   src: "); +    src = atom_get_src(ctx, attr, ptr); +    dst += src; +    SDEBUG("   dst: "); +    atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_and(atom_exec_context *ctx, int *ptr, int arg) +{ +    uint8_t attr = U8((*ptr)++); +    uint32_t dst, src, saved; +    int dptr = *ptr; +    SDEBUG("   dst: "); +    dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); +    SDEBUG("   src: "); +    src = atom_get_src(ctx, attr, ptr); +    dst &= src; +    SDEBUG("   dst: "); +    atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_beep(atom_exec_context *ctx, int *ptr, int arg) +{ +    printk("ATOM BIOS beeped!\n"); +} + +static void atom_op_calltable(atom_exec_context *ctx, int *ptr, int arg) +{ +    int idx = U8((*ptr)++); +    if(idx < ATOM_TABLE_NAMES_CNT) +	SDEBUG("   table: %d (%s)\n", idx, atom_table_names[idx]); +    else +	SDEBUG("   table: %d\n", idx); +    if(U16(ctx->ctx->cmd_table + 4 + 2*idx)) +	atom_execute_table(ctx->ctx, idx, ctx->ps+ctx->ps_shift); +} + +static void atom_op_clear(atom_exec_context *ctx, int *ptr, int arg) +{ +    uint8_t attr = U8((*ptr)++); +    uint32_t saved; +    int dptr = *ptr; +    attr &= 0x38; +    attr |= atom_def_dst[attr>>3]<<6; +    atom_get_dst(ctx, arg, attr, ptr, &saved, 0); +    SDEBUG("   dst: "); +    atom_put_dst(ctx, arg, attr, &dptr, 0, saved); +} + +static void atom_op_compare(atom_exec_context *ctx, int *ptr, int arg) +{ +    uint8_t attr = U8((*ptr)++); +    uint32_t dst, src; +    SDEBUG("   src1: "); +    dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1); +    SDEBUG("   src2: "); +    src = atom_get_src(ctx, attr, ptr); +    ctx->ctx->cs_equal = (dst == src); +    ctx->ctx->cs_above = (dst > src); +    SDEBUG("   result: %s %s\n", ctx->ctx->cs_equal?"EQ":"NE", ctx->ctx->cs_above?"GT":"LE"); +} + +static void atom_op_delay(atom_exec_context *ctx, int *ptr, int arg) +{ +    uint8_t count = U8((*ptr)++); +    SDEBUG("   count: %d\n", count); +    if(arg == ATOM_UNIT_MICROSEC) +	schedule_timeout_uninterruptible(usecs_to_jiffies(count)); +    else +	schedule_timeout_uninterruptible(msecs_to_jiffies(count)); +} + +static void atom_op_div(atom_exec_context *ctx, int *ptr, int arg) +{ +    uint8_t attr = U8((*ptr)++); +    uint32_t dst, src; +    SDEBUG("   src1: "); +    dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1); +    SDEBUG("   src2: "); +    src = atom_get_src(ctx, attr, ptr); +    if(src != 0) { +	ctx->ctx->divmul[0] = dst/src; +	ctx->ctx->divmul[1] = dst%src; +    } else { +	ctx->ctx->divmul[0] = 0; +	ctx->ctx->divmul[1] = 0; +    } +} + +static void atom_op_eot(atom_exec_context *ctx, int *ptr, int arg) +{ +    /* functionally, a nop */ +} + +static void atom_op_jump(atom_exec_context *ctx, int *ptr, int arg) +{ +    int execute = 0, target = U16(*ptr); +    (*ptr)+=2; +    switch(arg) { +    case ATOM_COND_ABOVE: +	execute = ctx->ctx->cs_above; +	break; +    case ATOM_COND_ABOVEOREQUAL: +	execute = ctx->ctx->cs_above || ctx->ctx->cs_equal; +	break; +    case ATOM_COND_ALWAYS: +	execute = 1; +	break; +    case ATOM_COND_BELOW: +	execute = !(ctx->ctx->cs_above || ctx->ctx->cs_equal); +	break; +    case ATOM_COND_BELOWOREQUAL: +	execute = !ctx->ctx->cs_above; +	break; +    case ATOM_COND_EQUAL: +	execute = ctx->ctx->cs_equal; +	break; +    case ATOM_COND_NOTEQUAL: +	execute = !ctx->ctx->cs_equal; +	break; +    } +    if(arg != ATOM_COND_ALWAYS) +	SDEBUG("   taken: %s\n", execute?"yes":"no"); +    SDEBUG("   target: 0x%04X\n", target); +    if(execute) +	*ptr = ctx->start+target; +} + +static void atom_op_mask(atom_exec_context *ctx, int *ptr, int arg) +{ +    uint8_t attr = U8((*ptr)++); +    uint32_t dst, src1, src2, saved; +    int dptr = *ptr; +    SDEBUG("   dst: "); +    dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); +    SDEBUG("   src1: "); +    src1 = atom_get_src(ctx, attr, ptr); +    SDEBUG("   src2: "); +    src2 = atom_get_src(ctx, attr, ptr); +    dst &= src1; +    dst |= src2; +    SDEBUG("   dst: "); +    atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_move(atom_exec_context *ctx, int *ptr, int arg) +{ +    uint8_t attr = U8((*ptr)++); +    uint32_t src, saved; +    int dptr = *ptr; +    if(((attr>>3)&7) != ATOM_SRC_DWORD) +	atom_get_dst(ctx, arg, attr, ptr, &saved, 0); +    else { +	atom_skip_dst(ctx, arg, attr, ptr); +	saved = 0xCDCDCDCD; +    } +    SDEBUG("   src: "); +    src = atom_get_src(ctx, attr, ptr); +    SDEBUG("   dst: "); +    atom_put_dst(ctx, arg, attr, &dptr, src, saved); +} + +static void atom_op_mul(atom_exec_context *ctx, int *ptr, int arg) +{ +    uint8_t attr = U8((*ptr)++); +    uint32_t dst, src; +    SDEBUG("   src1: "); +    dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1); +    SDEBUG("   src2: "); +    src = atom_get_src(ctx, attr, ptr); +    ctx->ctx->divmul[0] = dst*src; +} + +static void atom_op_nop(atom_exec_context *ctx, int *ptr, int arg) +{ +    /* nothing */ +} + +static void atom_op_or(atom_exec_context *ctx, int *ptr, int arg) +{ +    uint8_t attr = U8((*ptr)++); +    uint32_t dst, src, saved; +    int dptr = *ptr; +    SDEBUG("   dst: "); +    dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); +    SDEBUG("   src: "); +    src = atom_get_src(ctx, attr, ptr); +    dst |= src; +    SDEBUG("   dst: "); +    atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_postcard(atom_exec_context *ctx, int *ptr, int arg) +{ +    uint8_t val = U8((*ptr)++); +    SDEBUG("POST card output: 0x%02X\n", val); +} + +static void atom_op_repeat(atom_exec_context *ctx, int *ptr, int arg) +{ +    printk(KERN_INFO "unimplemented!\n"); +} + +static void atom_op_restorereg(atom_exec_context *ctx, int *ptr, int arg) +{ +    printk(KERN_INFO "unimplemented!\n"); +} + +static void atom_op_savereg(atom_exec_context *ctx, int *ptr, int arg) +{ +    printk(KERN_INFO "unimplemented!\n"); +} + +static void atom_op_setdatablock(atom_exec_context *ctx, int *ptr, int arg) +{ +    int idx = U8(*ptr); +    (*ptr)++; +    SDEBUG("   block: %d\n", idx); +    if(!idx) +	ctx->ctx->data_block = 0; +    else if(idx==255) +	ctx->ctx->data_block = ctx->start; +    else +	ctx->ctx->data_block = U16(ctx->ctx->data_table + 4 + 2*idx); +    SDEBUG("   base: 0x%04X\n", ctx->ctx->data_block); +} + +static void atom_op_setfbbase(atom_exec_context *ctx, int *ptr, int arg) +{ +    uint8_t attr = U8((*ptr)++); +    SDEBUG("   fb_base: "); +    ctx->ctx->fb_base = atom_get_src(ctx, attr, ptr); +} + +static void atom_op_setport(atom_exec_context *ctx, int *ptr, int arg) +{ +    int port; +    switch(arg) { +    case ATOM_PORT_ATI: +	port = U16(*ptr); +	if(port < ATOM_IO_NAMES_CNT) +		SDEBUG("   port: %d (%s)\n", port, atom_io_names[port]); +	else +		SDEBUG("   port: %d\n", port); +	if(!port) +	    ctx->ctx->io_mode = ATOM_IO_MM; +	else +	    ctx->ctx->io_mode = ATOM_IO_IIO|port; +	(*ptr)+=2; +	break; +    case ATOM_PORT_PCI: +	ctx->ctx->io_mode = ATOM_IO_PCI; +	(*ptr)++; +	break; +    case ATOM_PORT_SYSIO: +	ctx->ctx->io_mode = ATOM_IO_SYSIO; +	(*ptr)++; +	break; +    } +} + +static void atom_op_setregblock(atom_exec_context *ctx, int *ptr, int arg) +{ +    ctx->ctx->reg_block = U16(*ptr); +    (*ptr)+=2; +    SDEBUG("   base: 0x%04X\n", ctx->ctx->reg_block); +} + +static void atom_op_shl(atom_exec_context *ctx, int *ptr, int arg) +{ +    uint8_t attr = U8((*ptr)++), shift; +    uint32_t saved, dst; +    int dptr = *ptr; +    attr &= 0x38; +    attr |= atom_def_dst[attr>>3]<<6; +    SDEBUG("   dst: "); +    dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); +    shift = U8((*ptr)++); +    SDEBUG("   shift: %d\n", shift); +    dst <<= shift; +    SDEBUG("   dst: "); +    atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_shr(atom_exec_context *ctx, int *ptr, int arg) +{ +    uint8_t attr = U8((*ptr)++), shift; +    uint32_t saved, dst; +    int dptr = *ptr; +    attr &= 0x38; +    attr |= atom_def_dst[attr>>3]<<6; +    SDEBUG("   dst: "); +    dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); +    shift = U8((*ptr)++); +    SDEBUG("   shift: %d\n", shift); +    dst >>= shift; +    SDEBUG("   dst: "); +    atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_sub(atom_exec_context *ctx, int *ptr, int arg) +{ +    uint8_t attr = U8((*ptr)++); +    uint32_t dst, src, saved; +    int dptr = *ptr; +    SDEBUG("   dst: "); +    dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); +    SDEBUG("   src: "); +    src = atom_get_src(ctx, attr, ptr); +    dst -= src; +    SDEBUG("   dst: "); +    atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_switch(atom_exec_context *ctx, int *ptr, int arg) +{ +    uint8_t attr = U8((*ptr)++); +    uint32_t src, val, target; +    SDEBUG("   switch: "); +    src = atom_get_src(ctx, attr, ptr); +    while(U16(*ptr) != ATOM_CASE_END) +	if(U8(*ptr) == ATOM_CASE_MAGIC) { +	    (*ptr)++; +	    SDEBUG("   case: "); +	    val = atom_get_src(ctx, (attr&0x38)|ATOM_ARG_IMM, ptr); +	    target = U16(*ptr); +	    if(val == src) { +		SDEBUG("   target: %04X\n", target); +		*ptr = ctx->start+target; +		return; +	    } +	    (*ptr) += 2; +	} else { +	    printk(KERN_INFO "Bad case.\n"); +	    return; +	} +    (*ptr) += 2; +} + +static void atom_op_test(atom_exec_context *ctx, int *ptr, int arg) +{ +    uint8_t attr = U8((*ptr)++); +    uint32_t dst, src; +    SDEBUG("   src1: "); +    dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1); +    SDEBUG("   src2: "); +    src = atom_get_src(ctx, attr, ptr); +    ctx->ctx->cs_equal = ((dst & src) == 0); +    SDEBUG("   result: %s\n", ctx->ctx->cs_equal?"EQ":"NE"); +} + +static void atom_op_xor(atom_exec_context *ctx, int *ptr, int arg) +{ +    uint8_t attr = U8((*ptr)++); +    uint32_t dst, src, saved; +    int dptr = *ptr; +    SDEBUG("   dst: "); +    dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); +    SDEBUG("   src: "); +    src = atom_get_src(ctx, attr, ptr); +    dst ^= src; +    SDEBUG("   dst: "); +    atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_debug(atom_exec_context *ctx, int *ptr, int arg) +{ +    printk(KERN_INFO "unimplemented!\n"); +} + +static struct { +    void (*func)(atom_exec_context *, int *, int); +    int arg; +} opcode_table[ATOM_OP_CNT] = { +    { NULL, 0 }, +    { atom_op_move, ATOM_ARG_REG }, +    { atom_op_move, ATOM_ARG_PS }, +    { atom_op_move, ATOM_ARG_WS }, +    { atom_op_move, ATOM_ARG_FB }, +    { atom_op_move, ATOM_ARG_PLL }, +    { atom_op_move, ATOM_ARG_MC }, +    { atom_op_and, ATOM_ARG_REG }, +    { atom_op_and, ATOM_ARG_PS }, +    { atom_op_and, ATOM_ARG_WS }, +    { atom_op_and, ATOM_ARG_FB }, +    { atom_op_and, ATOM_ARG_PLL }, +    { atom_op_and, ATOM_ARG_MC }, +    { atom_op_or, ATOM_ARG_REG }, +    { atom_op_or, ATOM_ARG_PS }, +    { atom_op_or, ATOM_ARG_WS }, +    { atom_op_or, ATOM_ARG_FB }, +    { atom_op_or, ATOM_ARG_PLL }, +    { atom_op_or, ATOM_ARG_MC }, +    { atom_op_shl, ATOM_ARG_REG }, +    { atom_op_shl, ATOM_ARG_PS }, +    { atom_op_shl, ATOM_ARG_WS }, +    { atom_op_shl, ATOM_ARG_FB }, +    { atom_op_shl, ATOM_ARG_PLL }, +    { atom_op_shl, ATOM_ARG_MC }, +    { atom_op_shr, ATOM_ARG_REG }, +    { atom_op_shr, ATOM_ARG_PS }, +    { atom_op_shr, ATOM_ARG_WS }, +    { atom_op_shr, ATOM_ARG_FB }, +    { atom_op_shr, ATOM_ARG_PLL }, +    { atom_op_shr, ATOM_ARG_MC }, +    { atom_op_mul, ATOM_ARG_REG }, +    { atom_op_mul, ATOM_ARG_PS }, +    { atom_op_mul, ATOM_ARG_WS }, +    { atom_op_mul, ATOM_ARG_FB }, +    { atom_op_mul, ATOM_ARG_PLL }, +    { atom_op_mul, ATOM_ARG_MC }, +    { atom_op_div, ATOM_ARG_REG }, +    { atom_op_div, ATOM_ARG_PS }, +    { atom_op_div, ATOM_ARG_WS }, +    { atom_op_div, ATOM_ARG_FB }, +    { atom_op_div, ATOM_ARG_PLL }, +    { atom_op_div, ATOM_ARG_MC }, +    { atom_op_add, ATOM_ARG_REG }, +    { atom_op_add, ATOM_ARG_PS }, +    { atom_op_add, ATOM_ARG_WS }, +    { atom_op_add, ATOM_ARG_FB }, +    { atom_op_add, ATOM_ARG_PLL }, +    { atom_op_add, ATOM_ARG_MC }, +    { atom_op_sub, ATOM_ARG_REG }, +    { atom_op_sub, ATOM_ARG_PS }, +    { atom_op_sub, ATOM_ARG_WS }, +    { atom_op_sub, ATOM_ARG_FB }, +    { atom_op_sub, ATOM_ARG_PLL }, +    { atom_op_sub, ATOM_ARG_MC }, +    { atom_op_setport, ATOM_PORT_ATI }, +    { atom_op_setport, ATOM_PORT_PCI }, +    { atom_op_setport, ATOM_PORT_SYSIO }, +    { atom_op_setregblock, 0 }, +    { atom_op_setfbbase, 0 }, +    { atom_op_compare, ATOM_ARG_REG }, +    { atom_op_compare, ATOM_ARG_PS }, +    { atom_op_compare, ATOM_ARG_WS }, +    { atom_op_compare, ATOM_ARG_FB }, +    { atom_op_compare, ATOM_ARG_PLL }, +    { atom_op_compare, ATOM_ARG_MC }, +    { atom_op_switch, 0 }, +    { atom_op_jump, ATOM_COND_ALWAYS }, +    { atom_op_jump, ATOM_COND_EQUAL }, +    { atom_op_jump, ATOM_COND_BELOW }, +    { atom_op_jump, ATOM_COND_ABOVE }, +    { atom_op_jump, ATOM_COND_BELOWOREQUAL }, +    { atom_op_jump, ATOM_COND_ABOVEOREQUAL }, +    { atom_op_jump, ATOM_COND_NOTEQUAL }, +    { atom_op_test, ATOM_ARG_REG }, +    { atom_op_test, ATOM_ARG_PS }, +    { atom_op_test, ATOM_ARG_WS }, +    { atom_op_test, ATOM_ARG_FB }, +    { atom_op_test, ATOM_ARG_PLL }, +    { atom_op_test, ATOM_ARG_MC }, +    { atom_op_delay, ATOM_UNIT_MILLISEC }, +    { atom_op_delay, ATOM_UNIT_MICROSEC }, +    { atom_op_calltable, 0 }, +    { atom_op_repeat, 0 }, +    { atom_op_clear, ATOM_ARG_REG }, +    { atom_op_clear, ATOM_ARG_PS }, +    { atom_op_clear, ATOM_ARG_WS }, +    { atom_op_clear, ATOM_ARG_FB }, +    { atom_op_clear, ATOM_ARG_PLL }, +    { atom_op_clear, ATOM_ARG_MC }, +    { atom_op_nop, 0 }, +    { atom_op_eot, 0 }, +    { atom_op_mask, ATOM_ARG_REG }, +    { atom_op_mask, ATOM_ARG_PS }, +    { atom_op_mask, ATOM_ARG_WS }, +    { atom_op_mask, ATOM_ARG_FB }, +    { atom_op_mask, ATOM_ARG_PLL }, +    { atom_op_mask, ATOM_ARG_MC }, +    { atom_op_postcard, 0 }, +    { atom_op_beep, 0 }, +    { atom_op_savereg, 0 }, +    { atom_op_restorereg, 0 }, +    { atom_op_setdatablock, 0 }, +    { atom_op_xor, ATOM_ARG_REG }, +    { atom_op_xor, ATOM_ARG_PS }, +    { atom_op_xor, ATOM_ARG_WS }, +    { atom_op_xor, ATOM_ARG_FB }, +    { atom_op_xor, ATOM_ARG_PLL }, +    { atom_op_xor, ATOM_ARG_MC }, +    { atom_op_shl, ATOM_ARG_REG }, +    { atom_op_shl, ATOM_ARG_PS }, +    { atom_op_shl, ATOM_ARG_WS }, +    { atom_op_shl, ATOM_ARG_FB }, +    { atom_op_shl, ATOM_ARG_PLL }, +    { atom_op_shl, ATOM_ARG_MC }, +    { atom_op_shr, ATOM_ARG_REG }, +    { atom_op_shr, ATOM_ARG_PS }, +    { atom_op_shr, ATOM_ARG_WS }, +    { atom_op_shr, ATOM_ARG_FB }, +    { atom_op_shr, ATOM_ARG_PLL }, +    { atom_op_shr, ATOM_ARG_MC }, +    { atom_op_debug, 0 }, +}; + +void atom_execute_table(struct atom_context *ctx, int index, uint32_t *params) +{ +    int base = CU16(ctx->cmd_table+4+2*index); +    int len, ws, ps, ptr; +    unsigned char op; +    atom_exec_context ectx; + +    if(!base) +    	return; + +    len = CU16(base+ATOM_CT_SIZE_PTR); +    ws = CU8(base+ATOM_CT_WS_PTR); +    ps = CU8(base+ATOM_CT_PS_PTR) & ATOM_CT_PS_MASK; +    ptr = base+ATOM_CT_CODE_PTR; + +    SDEBUG(">> execute %04X (len %d, WS %d, PS %d)\n", base, len, ws, ps); + +    /* reset reg block */ +    ctx->reg_block = 0; +    ectx.ctx = ctx; +    ectx.ps_shift = ps/4; +    ectx.start = base; +    ectx.ps = params; +    if(ws) + 	ectx.ws = kzalloc(4*ws, GFP_KERNEL); +    else +	ectx.ws = NULL; + +    debug_depth++; +    while(1) { +	op = CU8(ptr++); +	if(op<ATOM_OP_NAMES_CNT) +	    SDEBUG("%s @ 0x%04X\n", atom_op_names[op], ptr-1); +	else +	    SDEBUG("[%d] @ 0x%04X\n", op, ptr-1); + +	if(op<ATOM_OP_CNT && op>0) +	    opcode_table[op].func(&ectx, &ptr, opcode_table[op].arg); +	else +	    break; + +	if(op == ATOM_OP_EOT) +	    break; +    } +    debug_depth--; +    SDEBUG("<<\n"); + +    if(ws) +	kfree(ectx.ws); +} + +static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 }; +static void atom_index_iio(struct atom_context *ctx, int base) +{ +    ctx->iio = kzalloc(2*256, GFP_KERNEL); +    while(CU8(base) == ATOM_IIO_START) { +	ctx->iio[CU8(base+1)] = base+2; +	base += 2; +	while(CU8(base) != ATOM_IIO_END) +	    base += atom_iio_len[CU8(base)]; +	base += 3; +    } +} + +struct atom_context *atom_parse(struct card_info *card, void *bios) +{ +    int base; +    struct atom_context *ctx = kzalloc(sizeof(struct atom_context), GFP_KERNEL); +    char *str; + +    ctx->card = card; +    ctx->bios = bios; + +    if(CU16(0) != ATOM_BIOS_MAGIC) { +	printk(KERN_INFO "Invalid BIOS magic.\n"); +	kfree(ctx); +	return NULL; +    } +    if(strncmp(CSTR(ATOM_ATI_MAGIC_PTR), ATOM_ATI_MAGIC, strlen(ATOM_ATI_MAGIC))) { +	printk(KERN_INFO "Invalid ATI magic.\n"); +	kfree(ctx); +	return NULL; +    } + +    base = CU16(ATOM_ROM_TABLE_PTR); +    if(strncmp(CSTR(base+ATOM_ROM_MAGIC_PTR), ATOM_ROM_MAGIC, strlen(ATOM_ROM_MAGIC))) { +	printk(KERN_INFO "Invalid ATOM magic.\n"); +	kfree(ctx); +	return NULL; +    } + +    ctx->cmd_table = CU16(base+ATOM_ROM_CMD_PTR); +    ctx->data_table = CU16(base+ATOM_ROM_DATA_PTR); +    atom_index_iio(ctx, CU16(ctx->data_table+ATOM_DATA_IIO_PTR)+4); + +    str = CSTR(CU16(base+ATOM_ROM_MSG_PTR)); +    while(*str && ((*str == '\n') || (*str == '\r'))) +    	str++; +    printk(KERN_INFO "ATOM BIOS: %s", str); + +    return ctx; +} + +int atom_asic_init(struct atom_context *ctx) +{ +    int hwi = CU16(ctx->data_table + ATOM_DATA_FWI_PTR); +    uint32_t ps[16]; +    memset(ps, 0, 64); + +    ps[0] = CU32(hwi + ATOM_FWI_DEFSCLK_PTR); +    ps[1] = CU32(hwi + ATOM_FWI_DEFMCLK_PTR); +    if(!ps[0] || !ps[1]) +	return 1; + +    if(!CU16(ctx->cmd_table+4+2*ATOM_CMD_INIT)) +	return 1; +    atom_execute_table(ctx, ATOM_CMD_INIT, ps); + +    return 0; +} + +void atom_destroy(struct atom_context *ctx) +{ +    if(ctx->iio) +	kfree(ctx->iio); +    kfree(ctx); +} + + +void atom_parse_data_header(struct atom_context *ctx, int index, uint16_t *size, uint8_t *frev, uint8_t *crev, uint16_t *data_start) +{ +	int offset = index * 2 + 4; +	int idx = CU16(ctx->data_table + offset); + +	if (size) +		*size = CU16(idx); +	if (frev) +		*frev = CU8(idx + 2); +	if (crev) +		*crev = CU8(idx + 3); +	*data_start = idx; +	return; +} + +void atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t *frev, uint8_t *crev) +{ +	int offset = index * 2 + 4; +	int idx = CU16(ctx->cmd_table + offset); + +	if (frev) +		*frev = CU8(idx + 2); +	if (crev) +		*crev = CU8(idx + 3); +	return; +} diff --git a/linux-core/atom.h b/linux-core/atom.h new file mode 100644 index 00000000..a5d93322 --- /dev/null +++ b/linux-core/atom.h @@ -0,0 +1,148 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc.   + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: Stanislaw Skowronek + */ + +#ifndef ATOM_H +#define ATOM_H + +#include <linux/types.h> +#include "drmP.h" + +#define ATOM_BIOS_MAGIC		0xAA55 +#define ATOM_ATI_MAGIC_PTR	0x30 +#define ATOM_ATI_MAGIC		" 761295520" +#define ATOM_ROM_TABLE_PTR	0x48 + +#define ATOM_ROM_MAGIC		"ATOM" +#define ATOM_ROM_MAGIC_PTR	4 + +#define ATOM_ROM_MSG_PTR	0x10 +#define ATOM_ROM_CMD_PTR	0x1E +#define ATOM_ROM_DATA_PTR	0x20 + +#define ATOM_CMD_INIT		0 +#define ATOM_CMD_SETSCLK	0x0A +#define ATOM_CMD_SETMCLK	0x0B +#define ATOM_CMD_SETPCLK	0x0C + +#define ATOM_DATA_FWI_PTR	0xC +#define ATOM_DATA_IIO_PTR	0x32 + +#define ATOM_FWI_DEFSCLK_PTR	8 +#define ATOM_FWI_DEFMCLK_PTR	0xC +#define ATOM_FWI_MAXSCLK_PTR	0x24 +#define ATOM_FWI_MAXMCLK_PTR	0x28 + +#define ATOM_CT_SIZE_PTR	0 +#define ATOM_CT_WS_PTR		4 +#define ATOM_CT_PS_PTR		5 +#define ATOM_CT_PS_MASK		0x7F +#define ATOM_CT_CODE_PTR	6 + +#define ATOM_OP_CNT		123 +#define ATOM_OP_EOT		91 + +#define ATOM_CASE_MAGIC		0x63 +#define ATOM_CASE_END		0x5A5A + +#define ATOM_ARG_REG		0 +#define ATOM_ARG_PS		1 +#define ATOM_ARG_WS		2 +#define ATOM_ARG_ID		4 +#define ATOM_ARG_FB		3 +#define ATOM_ARG_IMM		5 +#define ATOM_ARG_PLL		6 +#define ATOM_ARG_MC		7 + +#define ATOM_SRC_DWORD		0 +#define ATOM_SRC_WORD0		1 +#define ATOM_SRC_WORD8		2 +#define ATOM_SRC_WORD16		3 +#define ATOM_SRC_BYTE0		4 +#define ATOM_SRC_BYTE8		5 +#define ATOM_SRC_BYTE16		6 +#define ATOM_SRC_BYTE24		7 + +#define ATOM_WS_QUOTIENT	0x40 +#define ATOM_WS_REMAINDER	0x41 +#define ATOM_WS_DATAPTR		0x42 +#define ATOM_WS_SHIFT		0x43 +#define ATOM_WS_OR_MASK		0x44 +#define ATOM_WS_AND_MASK	0x45 +#define ATOM_WS_FB_WINDOW	0x46 +#define ATOM_WS_ATTRIBUTES	0x47 + +#define ATOM_IIO_NOP		0 +#define ATOM_IIO_START		1 +#define ATOM_IIO_READ		2 +#define ATOM_IIO_WRITE		3 +#define ATOM_IIO_CLEAR		4 +#define ATOM_IIO_SET		5 +#define ATOM_IIO_MOVE_INDEX	6 +#define ATOM_IIO_MOVE_ATTR	7 +#define ATOM_IIO_MOVE_DATA	8 +#define ATOM_IIO_END		9 + +#define ATOM_IO_MM		0 +#define ATOM_IO_PCI		1 +#define ATOM_IO_SYSIO		2 +#define ATOM_IO_IIO		0x80 + +struct card_info { +	struct drm_device *dev; +	void (* reg_write)(struct card_info *, uint32_t, uint32_t);   // filled by driver +        uint32_t (* reg_read)(struct card_info *, uint32_t);          // filled by driver +	void (* mc_write)(struct card_info *, uint32_t, uint32_t);   // filled by driver +        uint32_t (* mc_read)(struct card_info *, uint32_t);          // filled by driver +//        int (* read_rom)(struct card_info *, uint8_t *);      // filled by driver +}; + +struct atom_context { +	struct card_info *card; +	void *bios; +	uint32_t cmd_table, data_table; +	uint16_t *iio; + +	uint16_t data_block; +	uint32_t fb_base; +	uint32_t divmul[2]; +	uint16_t io_attr; +	uint16_t reg_block; +	uint8_t shift; +	int cs_equal, cs_above; +	int io_mode; +}; + +extern int atom_debug; + +struct atom_context *atom_parse(struct card_info *, void *); +void atom_execute_table(struct atom_context *, int, uint32_t *); +int atom_asic_init(struct atom_context *); +void atom_destroy(struct atom_context *); +void atom_parse_data_header(struct atom_context *ctx, int index, uint16_t *size, uint8_t *frev, uint8_t *crev, uint16_t *data_start); +void atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t *frev, uint8_t *crev); +#include "atom-types.h" +#include "atombios.h" +#include "ObjectID.h" + +#endif diff --git a/linux-core/atombios.h b/linux-core/atombios.h new file mode 100644 index 00000000..2e7dc6c2 --- /dev/null +++ b/linux-core/atombios.h @@ -0,0 +1,4498 @@ +/* + * Copyright 2006-2007 Advanced Micro Devices, Inc.   + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + */ + + +/****************************************************************************/	 +/*Portion I: Definitions  shared between VBIOS and Driver                   */ +/****************************************************************************/ + + +#ifndef _ATOMBIOS_H +#define _ATOMBIOS_H + +#define ATOM_VERSION_MAJOR                   0x00020000 +#define ATOM_VERSION_MINOR                   0x00000002 + +#define ATOM_HEADER_VERSION (ATOM_VERSION_MAJOR | ATOM_VERSION_MINOR) + +/* Endianness should be specified before inclusion, + * default to little endian + */ +#ifndef ATOM_BIG_ENDIAN +#error Endian not specified +#endif + +#ifdef _H2INC +  #ifndef ULONG  +    typedef unsigned long ULONG; +  #endif + +  #ifndef UCHAR +    typedef unsigned char UCHAR; +  #endif + +  #ifndef USHORT  +    typedef unsigned short USHORT; +  #endif +#endif +       +#define ATOM_DAC_A            0  +#define ATOM_DAC_B            1 +#define ATOM_EXT_DAC          2 + +#define ATOM_CRTC1            0 +#define ATOM_CRTC2            1 + +#define ATOM_DIGA             0 +#define ATOM_DIGB             1 + +#define ATOM_PPLL1            0 +#define ATOM_PPLL2            1 + +#define ATOM_SCALER1          0 +#define ATOM_SCALER2          1 + +#define ATOM_SCALER_DISABLE   0    +#define ATOM_SCALER_CENTER    1    +#define ATOM_SCALER_EXPANSION 2    +#define ATOM_SCALER_MULTI_EX  3    + +#define ATOM_DISABLE          0 +#define ATOM_ENABLE           1 +#define ATOM_LCD_BLOFF                          (ATOM_DISABLE+2) +#define ATOM_LCD_BLON                           (ATOM_ENABLE+2) +#define ATOM_LCD_BL_BRIGHTNESS_CONTROL          (ATOM_ENABLE+3) +#define ATOM_LCD_SELFTEST_START									(ATOM_DISABLE+5) +#define ATOM_LCD_SELFTEST_STOP									(ATOM_ENABLE+5) +#define ATOM_ENCODER_INIT			                  (ATOM_DISABLE+7) + +#define ATOM_BLANKING         1 +#define ATOM_BLANKING_OFF     0 + +#define ATOM_CURSOR1          0 +#define ATOM_CURSOR2          1 + +#define ATOM_ICON1            0 +#define ATOM_ICON2            1 + +#define ATOM_CRT1             0 +#define ATOM_CRT2             1 + +#define ATOM_TV_NTSC          1 +#define ATOM_TV_NTSCJ         2 +#define ATOM_TV_PAL           3 +#define ATOM_TV_PALM          4 +#define ATOM_TV_PALCN         5 +#define ATOM_TV_PALN          6 +#define ATOM_TV_PAL60         7 +#define ATOM_TV_SECAM         8 +#define ATOM_TV_CV            16 + +#define ATOM_DAC1_PS2         1 +#define ATOM_DAC1_CV          2 +#define ATOM_DAC1_NTSC        3 +#define ATOM_DAC1_PAL         4 + +#define ATOM_DAC2_PS2         ATOM_DAC1_PS2 +#define ATOM_DAC2_CV          ATOM_DAC1_CV +#define ATOM_DAC2_NTSC        ATOM_DAC1_NTSC +#define ATOM_DAC2_PAL         ATOM_DAC1_PAL +  +#define ATOM_PM_ON            0 +#define ATOM_PM_STANDBY       1 +#define ATOM_PM_SUSPEND       2 +#define ATOM_PM_OFF           3 + +/* Bit0:{=0:single, =1:dual}, +   Bit1 {=0:666RGB, =1:888RGB}, +   Bit2:3:{Grey level} +   Bit4:{=0:LDI format for RGB888, =1 FPDI format for RGB888}*/ + +#define ATOM_PANEL_MISC_DUAL               0x00000001 +#define ATOM_PANEL_MISC_888RGB             0x00000002 +#define ATOM_PANEL_MISC_GREY_LEVEL         0x0000000C +#define ATOM_PANEL_MISC_FPDI               0x00000010 +#define ATOM_PANEL_MISC_GREY_LEVEL_SHIFT   2 +#define ATOM_PANEL_MISC_SPATIAL            0x00000020 +#define ATOM_PANEL_MISC_TEMPORAL           0x00000040 +#define ATOM_PANEL_MISC_API_ENABLED        0x00000080 + + +#define MEMTYPE_DDR1              "DDR1" +#define MEMTYPE_DDR2              "DDR2" +#define MEMTYPE_DDR3              "DDR3" +#define MEMTYPE_DDR4              "DDR4" + +#define ASIC_BUS_TYPE_PCI         "PCI" +#define ASIC_BUS_TYPE_AGP         "AGP" +#define ASIC_BUS_TYPE_PCIE        "PCI_EXPRESS" + +/* Maximum size of that FireGL flag string */ + +#define ATOM_FIREGL_FLAG_STRING     "FGL"             //Flag used to enable FireGL Support +#define ATOM_MAX_SIZE_OF_FIREGL_FLAG_STRING  3        //sizeof( ATOM_FIREGL_FLAG_STRING ) + +#define ATOM_FAKE_DESKTOP_STRING    "DSK"             //Flag used to enable mobile ASIC on Desktop +#define ATOM_MAX_SIZE_OF_FAKE_DESKTOP_STRING  ATOM_MAX_SIZE_OF_FIREGL_FLAG_STRING  + +#define ATOM_M54T_FLAG_STRING       "M54T"            //Flag used to enable M54T Support +#define ATOM_MAX_SIZE_OF_M54T_FLAG_STRING    4        //sizeof( ATOM_M54T_FLAG_STRING ) + +#define HW_ASSISTED_I2C_STATUS_FAILURE          2 +#define HW_ASSISTED_I2C_STATUS_SUCCESS          1 + +#pragma pack(1)                                       /* BIOS data must use byte aligment */ + +/*  Define offset to location of ROM header. */ + +#define OFFSET_TO_POINTER_TO_ATOM_ROM_HEADER		0x00000048L +#define OFFSET_TO_ATOM_ROM_IMAGE_SIZE				    0x00000002L + +#define OFFSET_TO_ATOMBIOS_ASIC_BUS_MEM_TYPE    0x94 +#define MAXSIZE_OF_ATOMBIOS_ASIC_BUS_MEM_TYPE   20    /* including the terminator 0x0! */ +#define	OFFSET_TO_GET_ATOMBIOS_STRINGS_NUMBER		0x002f +#define	OFFSET_TO_GET_ATOMBIOS_STRINGS_START		0x006e + +/* Common header for all ROM Data tables. +  Every table pointed  _ATOM_MASTER_DATA_TABLE has this common header.  +  And the pointer actually points to this header. */ + +typedef struct _ATOM_COMMON_TABLE_HEADER +{ +  USHORT usStructureSize; +  UCHAR  ucTableFormatRevision;   /*Change it when the Parser is not backward compatible */ +  UCHAR  ucTableContentRevision;  /*Change it only when the table needs to change but the firmware */ +                                  /*Image can't be updated, while Driver needs to carry the new table! */ +}ATOM_COMMON_TABLE_HEADER; + +typedef struct _ATOM_ROM_HEADER +{ +  ATOM_COMMON_TABLE_HEADER		sHeader; +  UCHAR	 uaFirmWareSignature[4];    /*Signature to distinguish between Atombios and non-atombios,  +                                      atombios should init it as "ATOM", don't change the position */ +  USHORT usBiosRuntimeSegmentAddress; +  USHORT usProtectedModeInfoOffset; +  USHORT usConfigFilenameOffset; +  USHORT usCRC_BlockOffset; +  USHORT usBIOS_BootupMessageOffset; +  USHORT usInt10Offset; +  USHORT usPciBusDevInitCode; +  USHORT usIoBaseAddress; +  USHORT usSubsystemVendorID; +  USHORT usSubsystemID; +  USHORT usPCI_InfoOffset;  +  USHORT usMasterCommandTableOffset; /*Offset for SW to get all command table offsets, Don't change the position */ +  USHORT usMasterDataTableOffset;   /*Offset for SW to get all data table offsets, Don't change the position */ +  UCHAR  ucExtendedFunctionCode; +  UCHAR  ucReserved; +}ATOM_ROM_HEADER; + +/*==============================Command Table Portion==================================== */ + +#ifdef	UEFI_BUILD +	#define	UTEMP	USHORT +	#define	USHORT	void* +#endif + +typedef struct _ATOM_MASTER_LIST_OF_COMMAND_TABLES{ +  USHORT ASIC_Init;                              //Function Table, used by various SW components,latest version 1.1 +  USHORT GetDisplaySurfaceSize;                  //Atomic Table,  Used by Bios when enabling HW ICON +  USHORT ASIC_RegistersInit;                     //Atomic Table,  indirectly used by various SW components,called from ASIC_Init +  USHORT VRAM_BlockVenderDetection;              //Atomic Table,  used only by Bios +  USHORT DIGxEncoderControl;										 //Only used by Bios +  USHORT MemoryControllerInit;                   //Atomic Table,  indirectly used by various SW components,called from ASIC_Init +  USHORT EnableCRTCMemReq;                       //Function Table,directly used by various SW components,latest version 2.1 +  USHORT MemoryParamAdjust; 										 //Atomic Table,  indirectly used by various SW components,called from SetMemoryClock if needed +  USHORT DVOEncoderControl;                      //Function Table,directly used by various SW components,latest version 1.2 +  USHORT GPIOPinControl;												 //Atomic Table,  only used by Bios +  USHORT SetEngineClock;                         //Function Table,directly used by various SW components,latest version 1.1 +  USHORT SetMemoryClock;                         //Function Table,directly used by various SW components,latest version 1.1 +  USHORT SetPixelClock;                          //Function Table,directly used by various SW components,latest version 1.2   +  USHORT DynamicClockGating;                     //Atomic Table,  indirectly used by various SW components,called from ASIC_Init +  USHORT ResetMemoryDLL;                         //Atomic Table,  indirectly used by various SW components,called from SetMemoryClock +  USHORT ResetMemoryDevice;                      //Atomic Table,  indirectly used by various SW components,called from SetMemoryClock +  USHORT MemoryPLLInit; +  USHORT AdjustDisplayPll;												//only used by Bios +  USHORT AdjustMemoryController;                 //Atomic Table,  indirectly used by various SW components,called from SetMemoryClock                 +  USHORT EnableASIC_StaticPwrMgt;                //Atomic Table,  only used by Bios +  USHORT ASIC_StaticPwrMgtStatusChange;          //Obsolete ,     only used by Bios    +  USHORT DAC_LoadDetection;                      //Atomic Table,  directly used by various SW components,latest version 1.2   +  USHORT LVTMAEncoderControl;                    //Atomic Table,directly used by various SW components,latest version 1.3 +  USHORT LCD1OutputControl;                      //Atomic Table,  directly used by various SW components,latest version 1.1  +  USHORT DAC1EncoderControl;                     //Atomic Table,  directly used by various SW components,latest version 1.1   +  USHORT DAC2EncoderControl;                     //Atomic Table,  directly used by various SW components,latest version 1.1  +  USHORT DVOOutputControl;                       //Atomic Table,  directly used by various SW components,latest version 1.1  +  USHORT CV1OutputControl;                       //Atomic Table,  directly used by various SW components,latest version 1.1  +  USHORT GetConditionalGoldenSetting;            //only used by Bios +  USHORT TVEncoderControl;                       //Function Table,directly used by various SW components,latest version 1.1 +  USHORT TMDSAEncoderControl;                    //Atomic Table,  directly used by various SW components,latest version 1.3 +  USHORT LVDSEncoderControl;                     //Atomic Table,  directly used by various SW components,latest version 1.3 +  USHORT TV1OutputControl;                       //Atomic Table,  directly used by various SW components,latest version 1.1 +  USHORT EnableScaler;                           //Atomic Table,  used only by Bios +  USHORT BlankCRTC;                              //Atomic Table,  directly used by various SW components,latest version 1.1  +  USHORT EnableCRTC;                             //Atomic Table,  directly used by various SW components,latest version 1.1  +  USHORT GetPixelClock;                          //Atomic Table,  directly used by various SW components,latest version 1.1  +  USHORT EnableVGA_Render;                       //Function Table,directly used by various SW components,latest version 1.1 +  USHORT EnableVGA_Access;                       //Obsolete ,     only used by Bios +  USHORT SetCRTC_Timing;                         //Atomic Table,  directly used by various SW components,latest version 1.1 +  USHORT SetCRTC_OverScan;                       //Atomic Table,  used by various SW components,latest version 1.1  +  USHORT SetCRTC_Replication;                    //Atomic Table,  used only by Bios +  USHORT SelectCRTC_Source;                      //Atomic Table,  directly used by various SW components,latest version 1.1  +  USHORT EnableGraphSurfaces;                    //Atomic Table,  used only by Bios +  USHORT UpdateCRTC_DoubleBufferRegisters; +  USHORT LUT_AutoFill;                           //Atomic Table,  only used by Bios +  USHORT EnableHW_IconCursor;                    //Atomic Table,  only used by Bios +  USHORT GetMemoryClock;                         //Atomic Table,  directly used by various SW components,latest version 1.1  +  USHORT GetEngineClock;                         //Atomic Table,  directly used by various SW components,latest version 1.1  +  USHORT SetCRTC_UsingDTDTiming;                 //Atomic Table,  directly used by various SW components,latest version 1.1 +  USHORT ExternalEncoderControl;                 //Atomic Table,  directly used by various SW components,latest version 2.1 +  USHORT LVTMAOutputControl;                     //Atomic Table,  directly used by various SW components,latest version 1.1 +  USHORT VRAM_BlockDetectionByStrap; +  USHORT MemoryCleanUp;                          //Atomic Table,  only used by Bios     +  USHORT ProcessI2cChannelTransaction;           //Function Table,only used by Bios +  USHORT WriteOneByteToHWAssistedI2C;            //Function Table,indirectly used by various SW components  +  USHORT ReadHWAssistedI2CStatus;                //Atomic Table,  indirectly used by various SW components +  USHORT SpeedFanControl;                        //Function Table,indirectly used by various SW components,called from ASIC_Init +  USHORT PowerConnectorDetection;                //Atomic Table,  directly used by various SW components,latest version 1.1 +  USHORT MC_Synchronization;                     //Atomic Table,  indirectly used by various SW components,called from SetMemoryClock +  USHORT ComputeMemoryEnginePLL;                 //Atomic Table,  indirectly used by various SW components,called from SetMemory/EngineClock +  USHORT MemoryRefreshConversion;                //Atomic Table,  indirectly used by various SW components,called from SetMemory or SetEngineClock +  USHORT VRAM_GetCurrentInfoBlock; +  USHORT DynamicMemorySettings;                  //Atomic Table,  indirectly used by various SW components,called from SetMemoryClock +  USHORT MemoryTraining; +  USHORT EnableSpreadSpectrumOnPPLL;             //Atomic Table,  directly used by various SW components,latest version 1.2 +  USHORT TMDSAOutputControl;                     //Atomic Table,  directly used by various SW components,latest version 1.1 +  USHORT SetVoltage;                             //Function Table,directly and/or indirectly used by various SW components,latest version 1.1 +  USHORT DAC1OutputControl;                      //Atomic Table,  directly used by various SW components,latest version 1.1 +  USHORT DAC2OutputControl;                      //Atomic Table,  directly used by various SW components,latest version 1.1 +  USHORT SetupHWAssistedI2CStatus;               //Function Table,only used by Bios, obsolete soon.Switch to use "ReadEDIDFromHWAssistedI2C" +  USHORT ClockSource;                            //Atomic Table,  indirectly used by various SW components,called from ASIC_Init +  USHORT MemoryDeviceInit;                       //Atomic Table,  indirectly used by various SW components,called from SetMemoryClock +  USHORT EnableYUV;                              //Atomic Table,  indirectly used by various SW components,called from EnableVGARender +  USHORT DIG1EncoderControl;                     //Atomic Table,directly used by various SW components,latest version 1.1 +  USHORT DIG2EncoderControl;                     //Atomic Table,directly used by various SW components,latest version 1.1 +  USHORT DIG1TransmitterControl;                 //Atomic Table,directly used by various SW components,latest version 1.1 +  USHORT DIG2TransmitterControl;	               //Atomic Table,directly used by various SW components,latest version 1.1  +  USHORT ProcessAuxChannelTransaction;					 //Function Table,only used by Bios +  USHORT DPEncoderService;											 //Function Table,only used by Bios +}ATOM_MASTER_LIST_OF_COMMAND_TABLES;    + +#define ReadEDIDFromHWAssistedI2C                ProcessI2cChannelTransaction + +#define UNIPHYTransmitterControl						     DIG1TransmitterControl +#define LVTMATransmitterControl							     DIG2TransmitterControl +#define SetCRTC_DPM_State                                    GetConditionalGoldenSetting + +typedef struct _ATOM_MASTER_COMMAND_TABLE +{ +  ATOM_COMMON_TABLE_HEADER           sHeader; +  ATOM_MASTER_LIST_OF_COMMAND_TABLES ListOfCommandTables; +}ATOM_MASTER_COMMAND_TABLE; + +typedef struct _ATOM_TABLE_ATTRIBUTE +{ +#if ATOM_BIG_ENDIAN +  USHORT  UpdatedByUtility:1;         //[15]=Table updated by utility flag +  USHORT  PS_SizeInBytes:7;           //[14:8]=Size of parameter space in Bytes (multiple of a dword),  +  USHORT  WS_SizeInBytes:8;           //[7:0]=Size of workspace in Bytes (in multiple of a dword),  +#else +  USHORT  WS_SizeInBytes:8;           //[7:0]=Size of workspace in Bytes (in multiple of a dword),  +  USHORT  PS_SizeInBytes:7;           //[14:8]=Size of parameter space in Bytes (multiple of a dword),  +  USHORT  UpdatedByUtility:1;         //[15]=Table updated by utility flag +#endif +}ATOM_TABLE_ATTRIBUTE; + +typedef union _ATOM_TABLE_ATTRIBUTE_ACCESS +{ +  ATOM_TABLE_ATTRIBUTE sbfAccess; +  USHORT               susAccess; +}ATOM_TABLE_ATTRIBUTE_ACCESS; + +// Common header for all command tables. +//Every table pointed by _ATOM_MASTER_COMMAND_TABLE has this common header.  +//And the pointer actually points to this header. + +typedef struct _ATOM_COMMON_ROM_COMMAND_TABLE_HEADER +{ +  ATOM_COMMON_TABLE_HEADER CommonHeader; +  ATOM_TABLE_ATTRIBUTE     TableAttribute;	 +}ATOM_COMMON_ROM_COMMAND_TABLE_HEADER; + + +typedef struct _ASIC_INIT_PARAMETERS +{ +  ULONG ulDefaultEngineClock;         //In 10Khz unit +  ULONG ulDefaultMemoryClock;         //In 10Khz unit +}ASIC_INIT_PARAMETERS; + +#define COMPUTE_MEMORY_PLL_PARAM        1 +#define COMPUTE_ENGINE_PLL_PARAM        2 + +typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS +{ +  ULONG   ulClock;        //When returen, it's the re-calculated clock based on given Fb_div Post_Div and ref_div +  UCHAR   ucAction;       //0:reserved //1:Memory //2:Engine   +  UCHAR   ucReserved;     //may expand to return larger Fbdiv later +  UCHAR   ucFbDiv;        //return value +  UCHAR   ucPostDiv;      //return value +}COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS; + +typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V2 +{ +  ULONG   ulClock;        //When return, [23:0] return real clock  +  UCHAR   ucAction;       //0:reserved;COMPUTE_MEMORY_PLL_PARAM:Memory;COMPUTE_ENGINE_PLL_PARAM:Engine. it return ref_div to be written to register +  USHORT  usFbDiv;		    //return Feedback value to be written to register +  UCHAR   ucPostDiv;      //return post div to be written to register +}COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V2; +#define COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_PS_ALLOCATION   COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS + + +#define SET_CLOCK_FREQ_MASK                     0x00FFFFFF  //Clock change tables only take bit [23:0] as the requested clock value +#define USE_NON_BUS_CLOCK_MASK                  0x01000000  //Applicable to both memory and engine clock change, when set, it uses another clock as the temporary clock (engine uses memory and vice versa) +#define USE_MEMORY_SELF_REFRESH_MASK            0x02000000	//Only applicable to memory clock change, when set, using memory self refresh during clock transition +#define SKIP_INTERNAL_MEMORY_PARAMETER_CHANGE   0x04000000  //Only applicable to memory clock change, when set, the table will skip predefined internal memory parameter change +#define FIRST_TIME_CHANGE_CLOCK									0x08000000	//Applicable to both memory and engine clock change,when set, it means this is 1st time to change clock after ASIC bootup +#define SKIP_SW_PROGRAM_PLL											0x10000000	//Applicable to both memory and engine clock change, when set, it means the table will not program SPLL/MPLL +#define USE_SS_ENABLED_PIXEL_CLOCK  USE_NON_BUS_CLOCK_MASK + +#define b3USE_NON_BUS_CLOCK_MASK                  0x01       //Applicable to both memory and engine clock change, when set, it uses another clock as the temporary clock (engine uses memory and vice versa) +#define b3USE_MEMORY_SELF_REFRESH                 0x02	     //Only applicable to memory clock change, when set, using memory self refresh during clock transition +#define b3SKIP_INTERNAL_MEMORY_PARAMETER_CHANGE   0x04       //Only applicable to memory clock change, when set, the table will skip predefined internal memory parameter change +#define b3FIRST_TIME_CHANGE_CLOCK									0x08       //Applicable to both memory and engine clock change,when set, it means this is 1st time to change clock after ASIC bootup +#define b3SKIP_SW_PROGRAM_PLL											0x10			 //Applicable to both memory and engine clock change, when set, it means the table will not program SPLL/MPLL + +typedef struct _SET_ENGINE_CLOCK_PARAMETERS +{ +  ULONG ulTargetEngineClock;          //In 10Khz unit +}SET_ENGINE_CLOCK_PARAMETERS; + +typedef struct _SET_ENGINE_CLOCK_PS_ALLOCATION +{ +  ULONG ulTargetEngineClock;          //In 10Khz unit +  COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_PS_ALLOCATION sReserved; +}SET_ENGINE_CLOCK_PS_ALLOCATION; + + +typedef struct _SET_MEMORY_CLOCK_PARAMETERS +{ +  ULONG ulTargetMemoryClock;          //In 10Khz unit +}SET_MEMORY_CLOCK_PARAMETERS; + +typedef struct _SET_MEMORY_CLOCK_PS_ALLOCATION +{ +  ULONG ulTargetMemoryClock;          //In 10Khz unit +  COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_PS_ALLOCATION sReserved; +}SET_MEMORY_CLOCK_PS_ALLOCATION; + +typedef struct _ASIC_INIT_PS_ALLOCATION +{ +  ASIC_INIT_PARAMETERS sASICInitClocks; +  SET_ENGINE_CLOCK_PS_ALLOCATION sReserved; //Caller doesn't need to init this structure +}ASIC_INIT_PS_ALLOCATION; + + +typedef struct _DYNAMIC_CLOCK_GATING_PARAMETERS  +{ +  UCHAR ucEnable;                     // ATOM_ENABLE or ATOM_DISABLE +  UCHAR ucPadding[3]; +}DYNAMIC_CLOCK_GATING_PARAMETERS; +#define  DYNAMIC_CLOCK_GATING_PS_ALLOCATION  DYNAMIC_CLOCK_GATING_PARAMETERS + + +typedef struct _ENABLE_ASIC_STATIC_PWR_MGT_PARAMETERS +{ +  UCHAR ucEnable;                     // ATOM_ENABLE or ATOM_DISABLE +  UCHAR ucPadding[3]; +}ENABLE_ASIC_STATIC_PWR_MGT_PARAMETERS; +#define ENABLE_ASIC_STATIC_PWR_MGT_PS_ALLOCATION  ENABLE_ASIC_STATIC_PWR_MGT_PARAMETERS + + +typedef struct _DAC_LOAD_DETECTION_PARAMETERS +{ +  USHORT usDeviceID;                  //{ATOM_DEVICE_CRTx_SUPPORT,ATOM_DEVICE_TVx_SUPPORT,ATOM_DEVICE_CVx_SUPPORT} +  UCHAR  ucDacType;                   //{ATOM_DAC_A,ATOM_DAC_B, ATOM_EXT_DAC} +  UCHAR  ucMisc;											//Valid only when table revision =1.3 and above +}DAC_LOAD_DETECTION_PARAMETERS; + +// DAC_LOAD_DETECTION_PARAMETERS.ucMisc +#define DAC_LOAD_MISC_YPrPb						0x01 + + +typedef struct _DAC_LOAD_DETECTION_PS_ALLOCATION +{ +  DAC_LOAD_DETECTION_PARAMETERS            sDacload; +  ULONG                                    Reserved[2];// Don't set this one, allocation for EXT DAC +}DAC_LOAD_DETECTION_PS_ALLOCATION; + + +typedef struct _DAC_ENCODER_CONTROL_PARAMETERS  +{ +  USHORT usPixelClock;                // in 10KHz; for bios convenient +  UCHAR  ucDacStandard;               // See definition of ATOM_DACx_xxx, For DEC3.0, bit 7 used as internal flag to indicate DAC2 (==1) or DAC1 (==0) +  UCHAR  ucAction;                    // 0: turn off encoder +                                      // 1: setup and turn on encoder +                                      // 7: ATOM_ENCODER_INIT Initialize DAC +}DAC_ENCODER_CONTROL_PARAMETERS; + +#define DAC_ENCODER_CONTROL_PS_ALLOCATION  DAC_ENCODER_CONTROL_PARAMETERS + +typedef struct _TV_ENCODER_CONTROL_PARAMETERS +{ +  USHORT usPixelClock;                // in 10KHz; for bios convenient +  UCHAR  ucTvStandard;                // See definition "ATOM_TV_NTSC ..." +  UCHAR  ucAction;                    // 0: turn off encoder +                                      // 1: setup and turn on encoder +}TV_ENCODER_CONTROL_PARAMETERS; + +typedef struct _DIG_ENCODER_CONTROL_PARAMETERS +{ +  USHORT usPixelClock;		// in 10KHz; for bios convenient +  UCHAR  ucConfig;		   +                            // [2] Link Select: +                            // =0: PHY linkA if bfLane<3 +                            // =1: PHY linkB if bfLanes<3 +                            // =0: PHY linkA+B if bfLanes=3 +                            // [3] Transmitter Sel +                            // =0: UNIPHY or PCIEPHY +                            // =1: LVTMA 					 +  UCHAR ucAction;           // =0: turn off encoder					 +                            // =1: turn on encoder			 +  UCHAR ucEncoderMode; +                            // =0: DP   encoder       +                            // =1: LVDS encoder           +                            // =2: DVI  encoder   +                            // =3: HDMI encoder +                            // =4: SDVO encoder +  UCHAR ucLaneNum;          // how many lanes to enable +  UCHAR ucReserved[2]; +}DIG_ENCODER_CONTROL_PARAMETERS; +#define DIG_ENCODER_CONTROL_PS_ALLOCATION			  DIG_ENCODER_CONTROL_PARAMETERS +#define EXTERNAL_ENCODER_CONTROL_PARAMETER			DIG_ENCODER_CONTROL_PARAMETERS +#define EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION	DIG_ENCODER_CONTROL_PS_ALLOCATION + +//ucConfig +#define ATOM_ENCODER_CONFIG_DPLINKRATE_MASK				0x01 +#define ATOM_ENCODER_CONFIG_DPLINKRATE_1_62GHZ		0x00 +#define ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ		0x01 +#define ATOM_ENCODER_CONFIG_LINK_SEL_MASK				  0x04 +#define ATOM_ENCODER_CONFIG_LINKA								  0x00 +#define ATOM_ENCODER_CONFIG_LINKB								  0x04 +#define ATOM_ENCODER_CONFIG_LINKA_B							  ATOM_TRANSMITTER_CONFIG_LINKA +#define ATOM_ENCODER_CONFIG_LINKB_A							  ATOM_ENCODER_CONFIG_LINKB +#define ATOM_ENCODER_CONFIG_TRANSMITTER_SEL_MASK	0x08 +#define ATOM_ENCODER_CONFIG_UNIPHY							  0x00 +#define ATOM_ENCODER_CONFIG_LVTMA								  0x08 +#define ATOM_ENCODER_CONFIG_TRANSMITTER1				  0x00 +#define ATOM_ENCODER_CONFIG_TRANSMITTER2				  0x08 +#define ATOM_ENCODER_CONFIG_DIGB								  0x80			// VBIOS Internal use, outside SW should set this bit=0 +// ucAction +// ATOM_ENABLE:  Enable Encoder +// ATOM_DISABLE: Disable Encoder + +//ucEncoderMode +#define ATOM_ENCODER_MODE_DP											0 +#define ATOM_ENCODER_MODE_LVDS										1 +#define ATOM_ENCODER_MODE_DVI											2 +#define ATOM_ENCODER_MODE_HDMI										3 +#define ATOM_ENCODER_MODE_SDVO										4 +#define ATOM_ENCODER_MODE_TV											13 +#define ATOM_ENCODER_MODE_CV											14 +#define ATOM_ENCODER_MODE_CRT											15 + +typedef struct _ATOM_DP_VS_MODE +{ +  UCHAR ucLaneSel; +  UCHAR ucLaneSet; +}ATOM_DP_VS_MODE; + +typedef struct _DIG_TRANSMITTER_CONTROL_PARAMETERS +{ +	union +	{ +  USHORT usPixelClock;		// in 10KHz; for bios convenient +	USHORT usInitInfo;			// when init uniphy,lower 8bit is used for connector type defined in objectid.h +  ATOM_DP_VS_MODE asMode; // DP Voltage swing mode +	}; +  UCHAR ucConfig; +													// [0]=0: 4 lane Link,       +													//    =1: 8 lane Link ( Dual Links TMDS )  +                          // [1]=0: InCoherent mode    +													//    =1: Coherent Mode										 +													// [2] Link Select: +  												// =0: PHY linkA   if bfLane<3 +													// =1: PHY linkB   if bfLanes<3 +		  										// =0: PHY linkA+B if bfLanes=3		 +                          // [5:4]PCIE lane Sel +                          // =0: lane 0~3 or 0~7 +                          // =1: lane 4~7 +                          // =2: lane 8~11 or 8~15 +                          // =3: lane 12~15  +	UCHAR ucAction;				  // =0: turn off encoder					 +	                        // =1: turn on encoder			 +  UCHAR ucReserved[4]; +}DIG_TRANSMITTER_CONTROL_PARAMETERS; + +#define DIG_TRANSMITTER_CONTROL_PS_ALLOCATION		DIG_TRANSMITTER_CONTROL_PARAMETERS					 + +//ucInitInfo +#define ATOM_TRAMITTER_INITINFO_CONNECTOR_MASK	0x00ff			 + +//ucConfig  +#define ATOM_TRANSMITTER_CONFIG_8LANE_LINK			0x01 +#define ATOM_TRANSMITTER_CONFIG_COHERENT				0x02 +#define ATOM_TRANSMITTER_CONFIG_LINK_SEL_MASK		0x04 +#define ATOM_TRANSMITTER_CONFIG_LINKA						0x00 +#define ATOM_TRANSMITTER_CONFIG_LINKB						0x04 +#define ATOM_TRANSMITTER_CONFIG_LINKA_B					0x00			 +#define ATOM_TRANSMITTER_CONFIG_LINKB_A					0x04 + +#define ATOM_TRANSMITTER_CONFIG_ENCODER_SEL_MASK	0x08			// only used when ATOM_TRANSMITTER_ACTION_ENABLE +#define ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER		0x00				// only used when ATOM_TRANSMITTER_ACTION_ENABLE +#define ATOM_TRANSMITTER_CONFIG_DIG2_ENCODER		0x08				// only used when ATOM_TRANSMITTER_ACTION_ENABLE + +#define ATOM_TRANSMITTER_CONFIG_CLKSRC_MASK			0x30 +#define ATOM_TRANSMITTER_CONFIG_CLKSRC_PPLL			0x00 +#define ATOM_TRANSMITTER_CONFIG_CLKSRC_PCIE			0x20 +#define ATOM_TRANSMITTER_CONFIG_CLKSRC_XTALIN		0x30 +#define ATOM_TRANSMITTER_CONFIG_LANE_SEL_MASK		0xc0 +#define ATOM_TRANSMITTER_CONFIG_LANE_0_3				0x00 +#define ATOM_TRANSMITTER_CONFIG_LANE_0_7				0x00 +#define ATOM_TRANSMITTER_CONFIG_LANE_4_7				0x40 +#define ATOM_TRANSMITTER_CONFIG_LANE_8_11				0x80 +#define ATOM_TRANSMITTER_CONFIG_LANE_8_15				0x80 +#define ATOM_TRANSMITTER_CONFIG_LANE_12_15			0xc0 + +//ucAction +#define ATOM_TRANSMITTER_ACTION_DISABLE					       0 +#define ATOM_TRANSMITTER_ACTION_ENABLE					       1 +#define ATOM_TRANSMITTER_ACTION_LCD_BLOFF				       2 +#define ATOM_TRANSMITTER_ACTION_LCD_BLON				       3 +#define ATOM_TRANSMITTER_ACTION_BL_BRIGHTNESS_CONTROL  4 +#define ATOM_TRANSMITTER_ACTION_LCD_SELFTEST_START		 5 +#define ATOM_TRANSMITTER_ACTION_LCD_SELFTEST_STOP			 6 +#define ATOM_TRANSMITTER_ACTION_INIT						       7 +#define ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT	       8 +#define ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT		       9 +#define ATOM_TRANSMITTER_ACTION_SETUP						       10 +#define ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH           11 + +/****************************Device Output Control Command Table Definitions**********************/ +typedef struct _DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +{ +  UCHAR  ucAction;                    // Possible input:ATOM_ENABLE||ATOMDISABLE +                                      // When the display is LCD, in addition to above: +                                      // ATOM_LCD_BLOFF|| ATOM_LCD_BLON ||ATOM_LCD_BL_BRIGHTNESS_CONTROL||ATOM_LCD_SELFTEST_START|| +                                      // ATOM_LCD_SELFTEST_STOP +                                       +  UCHAR  aucPadding[3];               // padding to DWORD aligned +}DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS; + +#define DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS + + +#define CRT1_OUTPUT_CONTROL_PARAMETERS     DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS  +#define CRT1_OUTPUT_CONTROL_PS_ALLOCATION  DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION + +#define CRT2_OUTPUT_CONTROL_PARAMETERS     DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS  +#define CRT2_OUTPUT_CONTROL_PS_ALLOCATION  DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION + +#define CV1_OUTPUT_CONTROL_PARAMETERS      DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +#define CV1_OUTPUT_CONTROL_PS_ALLOCATION   DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION + +#define TV1_OUTPUT_CONTROL_PARAMETERS      DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +#define TV1_OUTPUT_CONTROL_PS_ALLOCATION   DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION + +#define DFP1_OUTPUT_CONTROL_PARAMETERS     DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +#define DFP1_OUTPUT_CONTROL_PS_ALLOCATION  DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION + +#define DFP2_OUTPUT_CONTROL_PARAMETERS     DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +#define DFP2_OUTPUT_CONTROL_PS_ALLOCATION  DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION + +#define LCD1_OUTPUT_CONTROL_PARAMETERS     DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +#define LCD1_OUTPUT_CONTROL_PS_ALLOCATION  DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION + +#define DVO_OUTPUT_CONTROL_PARAMETERS      DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS +#define DVO_OUTPUT_CONTROL_PS_ALLOCATION   DIG_TRANSMITTER_CONTROL_PS_ALLOCATION +#define DVO_OUTPUT_CONTROL_PARAMETERS_V3	 DIG_TRANSMITTER_CONTROL_PARAMETERS + +/**************************************************************************/ +typedef struct _BLANK_CRTC_PARAMETERS +{ +  UCHAR  ucCRTC;                    	// ATOM_CRTC1 or ATOM_CRTC2 +  UCHAR  ucBlanking;                  // ATOM_BLANKING or ATOM_BLANKINGOFF +  USHORT usBlackColorRCr; +  USHORT usBlackColorGY; +  USHORT usBlackColorBCb; +}BLANK_CRTC_PARAMETERS; +#define BLANK_CRTC_PS_ALLOCATION    BLANK_CRTC_PARAMETERS + + +typedef struct _ENABLE_CRTC_PARAMETERS +{ +  UCHAR ucCRTC;                    	  // ATOM_CRTC1 or ATOM_CRTC2 +  UCHAR ucEnable;                     // ATOM_ENABLE or ATOM_DISABLE  +  UCHAR ucPadding[2]; +}ENABLE_CRTC_PARAMETERS; +#define ENABLE_CRTC_PS_ALLOCATION   ENABLE_CRTC_PARAMETERS + + +typedef struct _SET_CRTC_OVERSCAN_PARAMETERS +{ +  USHORT usOverscanRight;             // right +  USHORT usOverscanLeft;              // left +  USHORT usOverscanBottom;            // bottom +  USHORT usOverscanTop;               // top +  UCHAR  ucCRTC;                      // ATOM_CRTC1 or ATOM_CRTC2 +  UCHAR  ucPadding[3]; +}SET_CRTC_OVERSCAN_PARAMETERS; +#define SET_CRTC_OVERSCAN_PS_ALLOCATION  SET_CRTC_OVERSCAN_PARAMETERS + + +typedef struct _SET_CRTC_REPLICATION_PARAMETERS +{ +  UCHAR ucH_Replication;              // horizontal replication +  UCHAR ucV_Replication;              // vertical replication +  UCHAR usCRTC;                       // ATOM_CRTC1 or ATOM_CRTC2 +  UCHAR ucPadding; +}SET_CRTC_REPLICATION_PARAMETERS; +#define SET_CRTC_REPLICATION_PS_ALLOCATION  SET_CRTC_REPLICATION_PARAMETERS + + +typedef struct _SELECT_CRTC_SOURCE_PARAMETERS +{ +  UCHAR ucCRTC;                    	  // ATOM_CRTC1 or ATOM_CRTC2 +  UCHAR ucDevice;                     // ATOM_DEVICE_CRT1|ATOM_DEVICE_CRT2|.... +  UCHAR ucPadding[2]; +}SELECT_CRTC_SOURCE_PARAMETERS; +#define SELECT_CRTC_SOURCE_PS_ALLOCATION  SELECT_CRTC_SOURCE_PARAMETERS + +typedef struct _SELECT_CRTC_SOURCE_PARAMETERS_V2 +{ +  UCHAR ucCRTC;                    	  // ATOM_CRTC1 or ATOM_CRTC2 +  UCHAR ucEncoderID;                  // DAC1/DAC2/TVOUT/DIG1/DIG2/DVO +  UCHAR ucEncodeMode;									// Encoding mode, only valid when using DIG1/DIG2/DVO +  UCHAR ucPadding; +}SELECT_CRTC_SOURCE_PARAMETERS_V2; + +//ucEncoderID +//#define ASIC_INT_DAC1_ENCODER_ID    						0x00  +//#define ASIC_INT_TV_ENCODER_ID									0x02 +//#define ASIC_INT_DIG1_ENCODER_ID								0x03 +//#define ASIC_INT_DAC2_ENCODER_ID								0x04 +//#define ASIC_EXT_TV_ENCODER_ID									0x06 +//#define ASIC_INT_DVO_ENCODER_ID									0x07 +//#define ASIC_INT_DIG2_ENCODER_ID								0x09 +//#define ASIC_EXT_DIG_ENCODER_ID									0x05 + +//ucEncodeMode +//#define ATOM_ENCODER_MODE_DP										0 +//#define ATOM_ENCODER_MODE_LVDS									1 +//#define ATOM_ENCODER_MODE_DVI										2 +//#define ATOM_ENCODER_MODE_HDMI									3 +//#define ATOM_ENCODER_MODE_SDVO									4 +//#define ATOM_ENCODER_MODE_TV										13 +//#define ATOM_ENCODER_MODE_CV										14 +//#define ATOM_ENCODER_MODE_CRT										15 + +//Major revision=1., Minor revision=1 +typedef struct _PIXEL_CLOCK_PARAMETERS +{ +  USHORT usPixelClock;                // in 10kHz unit; for bios convenient = (RefClk*FB_Div)/(Ref_Div*Post_Div) +                                      // 0 means disable PPLL +  USHORT usRefDiv;                    // Reference divider +  USHORT usFbDiv;                     // feedback divider +  UCHAR  ucPostDiv;                   // post divider	 +  UCHAR  ucFracFbDiv;                 // fractional feedback divider +  UCHAR  ucPpll;                      // ATOM_PPLL1 or ATOM_PPL2 +  UCHAR  ucRefDivSrc;                 // ATOM_PJITTER or ATO_NONPJITTER +  UCHAR  ucCRTC;                      // Which CRTC uses this Ppll +  UCHAR  ucPadding; +}PIXEL_CLOCK_PARAMETERS; + + +//Major revision=1., Minor revision=2, add ucMiscIfno +//ucMiscInfo: +#define MISC_FORCE_REPROG_PIXEL_CLOCK 0x1 +#define MISC_DEVICE_INDEX_MASK        0xF0 +#define MISC_DEVICE_INDEX_SHIFT       4 + +typedef struct _PIXEL_CLOCK_PARAMETERS_V2 +{ +  USHORT usPixelClock;                // in 10kHz unit; for bios convenient = (RefClk*FB_Div)/(Ref_Div*Post_Div) +                                      // 0 means disable PPLL +  USHORT usRefDiv;                    // Reference divider +  USHORT usFbDiv;                     // feedback divider +  UCHAR  ucPostDiv;                   // post divider	 +  UCHAR  ucFracFbDiv;                 // fractional feedback divider +  UCHAR  ucPpll;                      // ATOM_PPLL1 or ATOM_PPL2 +  UCHAR  ucRefDivSrc;                 // ATOM_PJITTER or ATO_NONPJITTER +  UCHAR  ucCRTC;                      // Which CRTC uses this Ppll +  UCHAR  ucMiscInfo;                  // Different bits for different purpose, bit [7:4] as device index, bit[0]=Force prog +}PIXEL_CLOCK_PARAMETERS_V2; + +//Major revision=1., Minor revision=3, structure/definition change +//ucEncoderMode: +//ATOM_ENCODER_MODE_DP +//ATOM_ENOCDER_MODE_LVDS +//ATOM_ENOCDER_MODE_DVI +//ATOM_ENOCDER_MODE_HDMI +//ATOM_ENOCDER_MODE_SDVO +//ATOM_ENCODER_MODE_TV										13 +//ATOM_ENCODER_MODE_CV										14 +//ATOM_ENCODER_MODE_CRT										15 + +//ucDVOConfig +//#define DVO_ENCODER_CONFIG_RATE_SEL							0x01 +//#define DVO_ENCODER_CONFIG_DDR_SPEED						0x00 +//#define DVO_ENCODER_CONFIG_SDR_SPEED						0x01 +//#define DVO_ENCODER_CONFIG_OUTPUT_SEL						0x0c +//#define DVO_ENCODER_CONFIG_LOW12BIT							0x00 +//#define DVO_ENCODER_CONFIG_UPPER12BIT						0x04 +//#define DVO_ENCODER_CONFIG_24BIT								0x08 + +//ucMiscInfo: also changed, see below +#define PIXEL_CLOCK_MISC_FORCE_PROG_PPLL						0x01 +#define PIXEL_CLOCK_MISC_VGA_MODE										0x02 +#define PIXEL_CLOCK_MISC_CRTC_SEL_MASK							0x04 +#define PIXEL_CLOCK_MISC_CRTC_SEL_CRTC1							0x00 +#define PIXEL_CLOCK_MISC_CRTC_SEL_CRTC2							0x04 +#define PIXEL_CLOCK_MISC_USE_ENGINE_FOR_DISPCLK			0x08 + +typedef struct _PIXEL_CLOCK_PARAMETERS_V3 +{ +  USHORT usPixelClock;                // in 10kHz unit; for bios convenient = (RefClk*FB_Div)/(Ref_Div*Post_Div) +                                      // 0 means disable PPLL. For VGA PPLL,make sure this value is not 0. +  USHORT usRefDiv;                    // Reference divider +  USHORT usFbDiv;                     // feedback divider +  UCHAR  ucPostDiv;                   // post divider	 +  UCHAR  ucFracFbDiv;                 // fractional feedback divider +  UCHAR  ucPpll;                      // ATOM_PPLL1 or ATOM_PPL2 +  UCHAR  ucTransmitterId;             // graphic encoder id defined in objectId.h +	union +	{ +  UCHAR  ucEncoderMode;               // encoder type defined as ATOM_ENCODER_MODE_DP/DVI/HDMI/ +	UCHAR  ucDVOConfig;									// when use DVO, need to know SDR/DDR, 12bit or 24bit +	}; +  UCHAR  ucMiscInfo;                  // bit[0]=Force program, bit[1]= set pclk for VGA, b[2]= CRTC sel +                                      // bit[3]=0:use PPLL for dispclk source, =1: use engine clock for dispclock source +}PIXEL_CLOCK_PARAMETERS_V3; + +#define PIXEL_CLOCK_PARAMETERS_LAST			PIXEL_CLOCK_PARAMETERS_V2 +#define GET_PIXEL_CLOCK_PS_ALLOCATION		PIXEL_CLOCK_PARAMETERS_LAST + +typedef struct _ADJUST_DISPLAY_PLL_PARAMETERS +{ +	USHORT usPixelClock; +	UCHAR ucTransmitterID; +	UCHAR ucEncodeMode; +	union +	{ +		UCHAR ucDVOConfig;									//if DVO, need passing link rate and output 12bitlow or 24bit +		UCHAR ucConfig;											//if none DVO, not defined yet +	}; +	UCHAR ucReserved[3]; +}ADJUST_DISPLAY_PLL_PARAMETERS; + +#define ADJUST_DISPLAY_CONFIG_SS_ENABLE       0x10 + +#define ADJUST_DISPLAY_PLL_PS_ALLOCATION			ADJUST_DISPLAY_PLL_PARAMETERS + +typedef struct _ENABLE_YUV_PARAMETERS +{ +  UCHAR ucEnable;                     // ATOM_ENABLE:Enable YUV or ATOM_DISABLE:Disable YUV (RGB) +  UCHAR ucCRTC;                       // Which CRTC needs this YUV or RGB format +  UCHAR ucPadding[2]; +}ENABLE_YUV_PARAMETERS; +#define ENABLE_YUV_PS_ALLOCATION ENABLE_YUV_PARAMETERS + +typedef struct _GET_MEMORY_CLOCK_PARAMETERS +{ +  ULONG ulReturnMemoryClock;          // current memory speed in 10KHz unit +} GET_MEMORY_CLOCK_PARAMETERS; +#define GET_MEMORY_CLOCK_PS_ALLOCATION  GET_MEMORY_CLOCK_PARAMETERS + + +typedef struct _GET_ENGINE_CLOCK_PARAMETERS +{ +  ULONG ulReturnEngineClock;          // current engine speed in 10KHz unit +} GET_ENGINE_CLOCK_PARAMETERS; +#define GET_ENGINE_CLOCK_PS_ALLOCATION  GET_ENGINE_CLOCK_PARAMETERS + + +//Maxium 8 bytes,the data read in will be placed in the parameter space. +//Read operaion successeful when the paramter space is non-zero, otherwise read operation failed +typedef struct _READ_EDID_FROM_HW_I2C_DATA_PARAMETERS +{ +  USHORT    usPrescale;         //Ratio between Engine clock and I2C clock +  USHORT    usVRAMAddress;      //Adress in Frame Buffer where to pace raw EDID +  USHORT    usStatus;           //When use output: lower byte EDID checksum, high byte hardware status +                                //WHen use input:  lower byte as 'byte to read':currently limited to 128byte or 1byte +  UCHAR     ucSlaveAddr;        //Read from which slave +  UCHAR     ucLineNumber;       //Read from which HW assisted line +}READ_EDID_FROM_HW_I2C_DATA_PARAMETERS; +#define READ_EDID_FROM_HW_I2C_DATA_PS_ALLOCATION  READ_EDID_FROM_HW_I2C_DATA_PARAMETERS + + +#define  ATOM_WRITE_I2C_FORMAT_PSOFFSET_PSDATABYTE                  0 +#define  ATOM_WRITE_I2C_FORMAT_PSOFFSET_PSTWODATABYTES              1 +#define  ATOM_WRITE_I2C_FORMAT_PSCOUNTER_PSOFFSET_IDDATABLOCK       2 +#define  ATOM_WRITE_I2C_FORMAT_PSCOUNTER_IDOFFSET_PLUS_IDDATABLOCK  3 +#define  ATOM_WRITE_I2C_FORMAT_IDCOUNTER_IDOFFSET_IDDATABLOCK       4 + +typedef struct _WRITE_ONE_BYTE_HW_I2C_DATA_PARAMETERS +{ +  USHORT    usPrescale;         //Ratio between Engine clock and I2C clock +  USHORT    usByteOffset;       //Write to which byte +                                //Upper portion of usByteOffset is Format of data  +                                //1bytePS+offsetPS +                                //2bytesPS+offsetPS +                                //blockID+offsetPS +                                //blockID+offsetID +                                //blockID+counterID+offsetID +  UCHAR     ucData;             //PS data1 +  UCHAR     ucStatus;           //Status byte 1=success, 2=failure, Also is used as PS data2 +  UCHAR     ucSlaveAddr;        //Write to which slave +  UCHAR     ucLineNumber;       //Write from which HW assisted line +}WRITE_ONE_BYTE_HW_I2C_DATA_PARAMETERS; + +#define WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION  WRITE_ONE_BYTE_HW_I2C_DATA_PARAMETERS + +typedef struct _SET_UP_HW_I2C_DATA_PARAMETERS +{ +  USHORT    usPrescale;         //Ratio between Engine clock and I2C clock +  UCHAR     ucSlaveAddr;        //Write to which slave +  UCHAR     ucLineNumber;       //Write from which HW assisted line +}SET_UP_HW_I2C_DATA_PARAMETERS; + + +/**************************************************************************/ +#define SPEED_FAN_CONTROL_PS_ALLOCATION   WRITE_ONE_BYTE_HW_I2C_DATA_PARAMETERS + +typedef struct	_POWER_CONNECTOR_DETECTION_PARAMETERS +{ +  UCHAR   ucPowerConnectorStatus;      //Used for return value 0: detected, 1:not detected +	UCHAR   ucPwrBehaviorId;							 +	USHORT	usPwrBudget;								 //how much power currently boot to in unit of watt +}POWER_CONNECTOR_DETECTION_PARAMETERS; + +typedef struct POWER_CONNECTOR_DETECTION_PS_ALLOCATION +{                                +  UCHAR   ucPowerConnectorStatus;      //Used for return value 0: detected, 1:not detected +	UCHAR   ucReserved; +	USHORT	usPwrBudget;								 //how much power currently boot to in unit of watt +  WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION    sReserved; +}POWER_CONNECTOR_DETECTION_PS_ALLOCATION; + +/****************************LVDS SS Command Table Definitions**********************/ +typedef struct	_ENABLE_LVDS_SS_PARAMETERS +{ +  USHORT  usSpreadSpectrumPercentage;        +  UCHAR   ucSpreadSpectrumType;           //Bit1=0 Down Spread,=1 Center Spread. Bit1=1 Ext. =0 Int. Others:TBD +  UCHAR   ucSpreadSpectrumStepSize_Delay; //bits3:2 SS_STEP_SIZE; bit 6:4 SS_DELAY +  UCHAR   ucEnable;                       //ATOM_ENABLE or ATOM_DISABLE +  UCHAR   ucPadding[3]; +}ENABLE_LVDS_SS_PARAMETERS; + +//ucTableFormatRevision=1,ucTableContentRevision=2 +typedef struct	_ENABLE_LVDS_SS_PARAMETERS_V2 +{ +  USHORT  usSpreadSpectrumPercentage;        +  UCHAR   ucSpreadSpectrumType;           //Bit1=0 Down Spread,=1 Center Spread. Bit1=1 Ext. =0 Int. Others:TBD +  UCHAR   ucSpreadSpectrumStep;           // +  UCHAR   ucEnable;                       //ATOM_ENABLE or ATOM_DISABLE +  UCHAR   ucSpreadSpectrumDelay; +  UCHAR   ucSpreadSpectrumRange; +  UCHAR   ucPadding; +}ENABLE_LVDS_SS_PARAMETERS_V2; + +//This new structure is based on ENABLE_LVDS_SS_PARAMETERS but expands to SS on PPLL, so other devices can use SS. +typedef struct	_ENABLE_SPREAD_SPECTRUM_ON_PPLL +{ +  USHORT  usSpreadSpectrumPercentage; +  UCHAR   ucSpreadSpectrumType;           // Bit1=0 Down Spread,=1 Center Spread. Bit1=1 Ext. =0 Int. Others:TBD +  UCHAR   ucSpreadSpectrumStep;           // +  UCHAR   ucEnable;                       // ATOM_ENABLE or ATOM_DISABLE +  UCHAR   ucSpreadSpectrumDelay; +  UCHAR   ucSpreadSpectrumRange; +  UCHAR   ucPpll;												  // ATOM_PPLL1/ATOM_PPLL2 +}ENABLE_SPREAD_SPECTRUM_ON_PPLL; + +#define ENABLE_SPREAD_SPECTRUM_ON_PPLL_PS_ALLOCATION  ENABLE_SPREAD_SPECTRUM_ON_PPLL + +/**************************************************************************/ + +typedef struct _SET_PIXEL_CLOCK_PS_ALLOCATION +{ +  PIXEL_CLOCK_PARAMETERS sPCLKInput; +  ENABLE_SPREAD_SPECTRUM_ON_PPLL sReserved;//Caller doesn't need to init this portion  +}SET_PIXEL_CLOCK_PS_ALLOCATION; + +#define ENABLE_VGA_RENDER_PS_ALLOCATION   SET_PIXEL_CLOCK_PS_ALLOCATION + +typedef struct	_MEMORY_TRAINING_PARAMETERS +{ +  ULONG ulTargetMemoryClock;          //In 10Khz unit +}MEMORY_TRAINING_PARAMETERS; +#define MEMORY_TRAINING_PS_ALLOCATION MEMORY_TRAINING_PARAMETERS + + + +/****************************LVDS and other encoder command table definitions **********************/ +typedef struct _LVDS_ENCODER_CONTROL_PARAMETERS +{ +  USHORT usPixelClock;  // in 10KHz; for bios convenient +  UCHAR  ucMisc;        // bit0=0: Enable single link +                        //     =1: Enable dual link +                        // Bit1=0: 666RGB +                        //     =1: 888RGB +  UCHAR  ucAction;      // 0: turn off encoder +                        // 1: setup and turn on encoder +}LVDS_ENCODER_CONTROL_PARAMETERS; + +#define LVDS_ENCODER_CONTROL_PS_ALLOCATION  LVDS_ENCODER_CONTROL_PARAMETERS +    +#define TMDS1_ENCODER_CONTROL_PARAMETERS    LVDS_ENCODER_CONTROL_PARAMETERS +#define TMDS1_ENCODER_CONTROL_PS_ALLOCATION TMDS1_ENCODER_CONTROL_PARAMETERS + +#define TMDS2_ENCODER_CONTROL_PARAMETERS    TMDS1_ENCODER_CONTROL_PARAMETERS +#define TMDS2_ENCODER_CONTROL_PS_ALLOCATION TMDS2_ENCODER_CONTROL_PARAMETERS + +typedef struct _ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS +{                                +  UCHAR    ucEnable;            // Enable or Disable External TMDS encoder +  UCHAR    ucMisc;              // Bit0=0:Enable Single link;=1:Enable Dual link;Bit1 {=0:666RGB, =1:888RGB} +  UCHAR    ucPadding[2]; +}ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS; + +typedef struct _ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION +{                                +  ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS    sXTmdsEncoder; +  WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION   sReserved;     //Caller doesn't need to init this portion +}ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION; + + +//ucTableFormatRevision=1,ucTableContentRevision=2 +typedef struct _LVDS_ENCODER_CONTROL_PARAMETERS_V2 +{ +  USHORT usPixelClock;  // in 10KHz; for bios convenient +  UCHAR  ucMisc;        // see PANEL_ENCODER_MISC_xx defintions below +  UCHAR  ucAction;      // 0: turn off encoder +                        // 1: setup and turn on encoder +  UCHAR  ucTruncate;    // bit0=0: Disable truncate +                        //     =1: Enable truncate +                        // bit4=0: 666RGB +                        //     =1: 888RGB +  UCHAR  ucSpatial;     // bit0=0: Disable spatial dithering +                        //     =1: Enable spatial dithering +                        // bit4=0: 666RGB +                        //     =1: 888RGB +  UCHAR  ucTemporal;    // bit0=0: Disable temporal dithering +                        //     =1: Enable temporal dithering +                        // bit4=0: 666RGB +                        //     =1: 888RGB +                        // bit5=0: Gray level 2 +                        //     =1: Gray level 4 +  UCHAR  ucFRC;         // bit4=0: 25FRC_SEL pattern E +                        //     =1: 25FRC_SEL pattern F +                        // bit6:5=0: 50FRC_SEL pattern A +                        //       =1: 50FRC_SEL pattern B +                        //       =2: 50FRC_SEL pattern C +                        //       =3: 50FRC_SEL pattern D +                        // bit7=0: 75FRC_SEL pattern E +                        //     =1: 75FRC_SEL pattern F +}LVDS_ENCODER_CONTROL_PARAMETERS_V2; + +#define LVDS_ENCODER_CONTROL_PS_ALLOCATION_V2  LVDS_ENCODER_CONTROL_PARAMETERS_V2 +    +#define TMDS1_ENCODER_CONTROL_PARAMETERS_V2    LVDS_ENCODER_CONTROL_PARAMETERS_V2 +#define TMDS1_ENCODER_CONTROL_PS_ALLOCATION_V2 TMDS1_ENCODER_CONTROL_PARAMETERS_V2 +   +#define TMDS2_ENCODER_CONTROL_PARAMETERS_V2    TMDS1_ENCODER_CONTROL_PARAMETERS_V2 +#define TMDS2_ENCODER_CONTROL_PS_ALLOCATION_V2 TMDS2_ENCODER_CONTROL_PARAMETERS_V2 +#define ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS_V2  LVDS_ENCODER_CONTROL_PARAMETERS_V2 + +typedef struct _ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION_V2 +{                                +  ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS_V2    sXTmdsEncoder; +  WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION      sReserved;     //Caller doesn't need to init this portion +}ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION_V2; + + +//ucTableFormatRevision=1,ucTableContentRevision=3 + +//ucDVOConfig: +#define DVO_ENCODER_CONFIG_RATE_SEL							0x01 +#define DVO_ENCODER_CONFIG_DDR_SPEED						0x00 +#define DVO_ENCODER_CONFIG_SDR_SPEED						0x01 +#define DVO_ENCODER_CONFIG_OUTPUT_SEL						0x0c +#define DVO_ENCODER_CONFIG_LOW12BIT							0x00 +#define DVO_ENCODER_CONFIG_UPPER12BIT						0x04 +#define DVO_ENCODER_CONFIG_24BIT								0x08 + +typedef struct _DVO_ENCODER_CONTROL_PARAMETERS_V3 +{ +  USHORT usPixelClock;  +  UCHAR  ucDVOConfig; +  UCHAR  ucAction;														//ATOM_ENABLE/ATOM_DISABLE/ATOM_HPD_INIT +  UCHAR  ucReseved[4]; +}DVO_ENCODER_CONTROL_PARAMETERS_V3; +#define DVO_ENCODER_CONTROL_PS_ALLOCATION_V3	DVO_ENCODER_CONTROL_PARAMETERS_V3 + +//ucTableFormatRevision=1 +//ucTableContentRevision=3 structure is not changed but usMisc add bit 1 as another input for  +// bit1=0: non-coherent mode +//     =1: coherent mode + +#define LVDS_ENCODER_CONTROL_PARAMETERS_V3     LVDS_ENCODER_CONTROL_PARAMETERS_V2 +#define LVDS_ENCODER_CONTROL_PS_ALLOCATION_V3  LVDS_ENCODER_CONTROL_PARAMETERS_V3 + +#define TMDS1_ENCODER_CONTROL_PARAMETERS_V3    LVDS_ENCODER_CONTROL_PARAMETERS_V3 +#define TMDS1_ENCODER_CONTROL_PS_ALLOCATION_V3 TMDS1_ENCODER_CONTROL_PARAMETERS_V3 + +#define TMDS2_ENCODER_CONTROL_PARAMETERS_V3    LVDS_ENCODER_CONTROL_PARAMETERS_V3 +#define TMDS2_ENCODER_CONTROL_PS_ALLOCATION_V3 TMDS2_ENCODER_CONTROL_PARAMETERS_V3 + +//========================================================================================== +//Only change is here next time when changing encoder parameter definitions again! +#define LVDS_ENCODER_CONTROL_PARAMETERS_LAST     LVDS_ENCODER_CONTROL_PARAMETERS_V3 +#define LVDS_ENCODER_CONTROL_PS_ALLOCATION_LAST  LVDS_ENCODER_CONTROL_PARAMETERS_LAST + +#define TMDS1_ENCODER_CONTROL_PARAMETERS_LAST    LVDS_ENCODER_CONTROL_PARAMETERS_V3 +#define TMDS1_ENCODER_CONTROL_PS_ALLOCATION_LAST TMDS1_ENCODER_CONTROL_PARAMETERS_LAST + +#define TMDS2_ENCODER_CONTROL_PARAMETERS_LAST    LVDS_ENCODER_CONTROL_PARAMETERS_V3 +#define TMDS2_ENCODER_CONTROL_PS_ALLOCATION_LAST TMDS2_ENCODER_CONTROL_PARAMETERS_LAST + +#define DVO_ENCODER_CONTROL_PARAMETERS_LAST      DVO_ENCODER_CONTROL_PARAMETERS +#define DVO_ENCODER_CONTROL_PS_ALLOCATION_LAST   DVO_ENCODER_CONTROL_PS_ALLOCATION + +//========================================================================================== +#define PANEL_ENCODER_MISC_DUAL                0x01 +#define PANEL_ENCODER_MISC_COHERENT            0x02 +#define	PANEL_ENCODER_MISC_TMDS_LINKB					 0x04 +#define	PANEL_ENCODER_MISC_HDMI_TYPE					 0x08 + +#define PANEL_ENCODER_ACTION_DISABLE           ATOM_DISABLE +#define PANEL_ENCODER_ACTION_ENABLE            ATOM_ENABLE +#define PANEL_ENCODER_ACTION_COHERENTSEQ       (ATOM_ENABLE+1) + +#define PANEL_ENCODER_TRUNCATE_EN              0x01 +#define PANEL_ENCODER_TRUNCATE_DEPTH           0x10 +#define PANEL_ENCODER_SPATIAL_DITHER_EN        0x01 +#define PANEL_ENCODER_SPATIAL_DITHER_DEPTH     0x10 +#define PANEL_ENCODER_TEMPORAL_DITHER_EN       0x01 +#define PANEL_ENCODER_TEMPORAL_DITHER_DEPTH    0x10 +#define PANEL_ENCODER_TEMPORAL_LEVEL_4         0x20 +#define PANEL_ENCODER_25FRC_MASK               0x10 +#define PANEL_ENCODER_25FRC_E                  0x00 +#define PANEL_ENCODER_25FRC_F                  0x10 +#define PANEL_ENCODER_50FRC_MASK               0x60 +#define PANEL_ENCODER_50FRC_A                  0x00 +#define PANEL_ENCODER_50FRC_B                  0x20 +#define PANEL_ENCODER_50FRC_C                  0x40 +#define PANEL_ENCODER_50FRC_D                  0x60 +#define PANEL_ENCODER_75FRC_MASK               0x80 +#define PANEL_ENCODER_75FRC_E                  0x00 +#define PANEL_ENCODER_75FRC_F                  0x80 + +/**************************************************************************/ + +#define SET_VOLTAGE_TYPE_ASIC_VDDC             1 +#define SET_VOLTAGE_TYPE_ASIC_MVDDC            2 +#define SET_VOLTAGE_TYPE_ASIC_MVDDQ            3 +#define SET_VOLTAGE_TYPE_ASIC_VDDCI            4 + +#define SET_ASIC_VOLTAGE_MODE_ALL_SOURCE       0x1 +#define SET_ASIC_VOLTAGE_MODE_SOURCE_A         0x2 +#define SET_ASIC_VOLTAGE_MODE_SOURCE_B         0x4 + +#define	SET_ASIC_VOLTAGE_MODE_SET_VOLTAGE			 0x0 +#define	SET_ASIC_VOLTAGE_MODE_GET_GPIOVAL			 0x1	 +#define	SET_ASIC_VOLTAGE_MODE_GET_GPIOMASK		 0x2 + +typedef struct	_SET_VOLTAGE_PARAMETERS +{ +  UCHAR    ucVoltageType;               // To tell which voltage to set up, VDDC/MVDDC/MVDDQ +  UCHAR    ucVoltageMode;               // To set all, to set source A or source B or ... +  UCHAR    ucVoltageIndex;              // An index to tell which voltage level +  UCHAR    ucReserved;           +}SET_VOLTAGE_PARAMETERS; + + +typedef struct	_SET_VOLTAGE_PARAMETERS_V2 +{ +  UCHAR    ucVoltageType;               // To tell which voltage to set up, VDDC/MVDDC/MVDDQ +  UCHAR    ucVoltageMode;               // Not used, maybe use for state machine for differen power mode +  USHORT   usVoltageLevel;              // real voltage level +}SET_VOLTAGE_PARAMETERS_V2; + + +typedef struct _SET_VOLTAGE_PS_ALLOCATION +{ +  SET_VOLTAGE_PARAMETERS sASICSetVoltage; +  WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved; +}SET_VOLTAGE_PS_ALLOCATION; + +typedef struct _TV_ENCODER_CONTROL_PS_ALLOCATION +{ +  TV_ENCODER_CONTROL_PARAMETERS sTVEncoder;           +  WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION    sReserved; // Don't set this one +}TV_ENCODER_CONTROL_PS_ALLOCATION; + +//==============================Data Table Portion==================================== + +#ifdef	UEFI_BUILD +	#define	UTEMP	USHORT +	#define	USHORT	void* +#endif + +typedef struct _ATOM_MASTER_LIST_OF_DATA_TABLES +{ +  USHORT        UtilityPipeLine;	        // Offest for the utility to get parser info,Don't change this position! +  USHORT        MultimediaCapabilityInfo; // Only used by MM Lib,latest version 1.1, not configuable from Bios, need to include the table to build Bios  +  USHORT        MultimediaConfigInfo;     // Only used by MM Lib,latest version 2.1, not configuable from Bios, need to include the table to build Bios +  USHORT        StandardVESA_Timing;      // Only used by Bios +  USHORT        FirmwareInfo;             // Shared by various SW components,latest version 1.4 +  USHORT        DAC_Info;                 // Will be obsolete from R600 +  USHORT        LVDS_Info;                // Shared by various SW components,latest version 1.1  +  USHORT        TMDS_Info;                // Will be obsolete from R600 +  USHORT        AnalogTV_Info;            // Shared by various SW components,latest version 1.1  +  USHORT        SupportedDevicesInfo;     // Will be obsolete from R600 +  USHORT        GPIO_I2C_Info;            // Shared by various SW components,latest version 1.2 will be used from R600            +  USHORT        VRAM_UsageByFirmware;     // Shared by various SW components,latest version 1.3 will be used from R600 +  USHORT        GPIO_Pin_LUT;             // Shared by various SW components,latest version 1.1 +  USHORT        VESA_ToInternalModeLUT;   // Only used by Bios +  USHORT        ComponentVideoInfo;       // Shared by various SW components,latest version 2.1 will be used from R600 +  USHORT        PowerPlayInfo;            // Shared by various SW components,latest version 2.1,new design from R600 +  USHORT        CompassionateData;        // Will be obsolete from R600 +  USHORT        SaveRestoreInfo;          // Only used by Bios +  USHORT        PPLL_SS_Info;             // Shared by various SW components,latest version 1.2, used to call SS_Info, change to new name because of int ASIC SS info +  USHORT        OemInfo;                  // Defined and used by external SW, should be obsolete soon +  USHORT        XTMDS_Info;               // Will be obsolete from R600 +  USHORT        MclkSS_Info;              // Shared by various SW components,latest version 1.1, only enabled when ext SS chip is used +  USHORT        Object_Header;            // Shared by various SW components,latest version 1.1 +  USHORT        IndirectIOAccess;         // Only used by Bios,this table position can't change at all!! +  USHORT        MC_InitParameter;         // Only used by command table +  USHORT        ASIC_VDDC_Info;						// Will be obsolete from R600 +  USHORT        ASIC_InternalSS_Info;			// New tabel name from R600, used to be called "ASIC_MVDDC_Info" +  USHORT        TV_VideoMode;							// Only used by command table +  USHORT        VRAM_Info;								// Only used by command table, latest version 1.3 +  USHORT        MemoryTrainingInfo;				// Used for VBIOS and Diag utility for memory training purpose since R600. the new table rev start from 2.1 +  USHORT        IntegratedSystemInfo;			// Shared by various SW components +  USHORT        ASIC_ProfilingInfo;				// New table name from R600, used to be called "ASIC_VDDCI_Info" for pre-R600 +  USHORT        VoltageObjectInfo;				// Shared by various SW components, latest version 1.1 +	USHORT				PowerSourceInfo;					// Shared by various SW components, latest versoin 1.1 +}ATOM_MASTER_LIST_OF_DATA_TABLES; + +#ifdef	UEFI_BUILD +	#define	USHORT	UTEMP +#endif + + +typedef struct _ATOM_MASTER_DATA_TABLE +{  +  ATOM_COMMON_TABLE_HEADER sHeader;   +  ATOM_MASTER_LIST_OF_DATA_TABLES   ListOfDataTables; +}ATOM_MASTER_DATA_TABLE; + + +typedef struct _ATOM_MULTIMEDIA_CAPABILITY_INFO +{ +  ATOM_COMMON_TABLE_HEADER sHeader;   +  ULONG                    ulSignature;      // HW info table signature string "$ATI" +  UCHAR                    ucI2C_Type;       // I2C type (normal GP_IO, ImpactTV GP_IO, Dedicated I2C pin, etc) +  UCHAR                    ucTV_OutInfo;     // Type of TV out supported (3:0) and video out crystal frequency (6:4) and TV data port (7) +  UCHAR                    ucVideoPortInfo;  // Provides the video port capabilities +  UCHAR                    ucHostPortInfo;   // Provides host port configuration information +}ATOM_MULTIMEDIA_CAPABILITY_INFO; + + +typedef struct _ATOM_MULTIMEDIA_CONFIG_INFO +{ +  ATOM_COMMON_TABLE_HEADER sHeader; +  ULONG                    ulSignature;      // MM info table signature sting "$MMT" +  UCHAR                    ucTunerInfo;      // Type of tuner installed on the adapter (4:0) and video input for tuner (7:5) +  UCHAR                    ucAudioChipInfo;  // List the audio chip type (3:0) product type (4) and OEM revision (7:5) +  UCHAR                    ucProductID;      // Defines as OEM ID or ATI board ID dependent on product type setting +  UCHAR                    ucMiscInfo1;      // Tuner voltage (1:0) HW teletext support (3:2) FM audio decoder (5:4) reserved (6) audio scrambling (7) +  UCHAR                    ucMiscInfo2;      // I2S input config (0) I2S output config (1) I2S Audio Chip (4:2) SPDIF Output Config (5) reserved (7:6) +  UCHAR                    ucMiscInfo3;      // Video Decoder Type (3:0) Video In Standard/Crystal (7:4) +  UCHAR                    ucMiscInfo4;      // Video Decoder Host Config (2:0) reserved (7:3) +  UCHAR                    ucVideoInput0Info;// Video Input 0 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6) +  UCHAR                    ucVideoInput1Info;// Video Input 1 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6) +  UCHAR                    ucVideoInput2Info;// Video Input 2 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6) +  UCHAR                    ucVideoInput3Info;// Video Input 3 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6) +  UCHAR                    ucVideoInput4Info;// Video Input 4 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6) +}ATOM_MULTIMEDIA_CONFIG_INFO; + +/****************************Firmware Info Table Definitions**********************/ + +// usBIOSCapability Defintion: +// Bit 0 = 0: Bios image is not Posted, =1:Bios image is Posted;  +// Bit 1 = 0: Dual CRTC is not supported, =1: Dual CRTC is supported;  +// Bit 2 = 0: Extended Desktop is not supported, =1: Extended Desktop is supported;  +// Others: Reserved +#define ATOM_BIOS_INFO_ATOM_FIRMWARE_POSTED         0x0001 +#define ATOM_BIOS_INFO_DUAL_CRTC_SUPPORT            0x0002 +#define ATOM_BIOS_INFO_EXTENDED_DESKTOP_SUPPORT     0x0004 +#define ATOM_BIOS_INFO_MEMORY_CLOCK_SS_SUPPORT      0x0008 +#define ATOM_BIOS_INFO_ENGINE_CLOCK_SS_SUPPORT      0x0010 +#define ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU         0x0020 +#define ATOM_BIOS_INFO_WMI_SUPPORT                  0x0040 +#define ATOM_BIOS_INFO_PPMODE_ASSIGNGED_BY_SYSTEM   0x0080 +#define ATOM_BIOS_INFO_HYPERMEMORY_SUPPORT          0x0100 +#define ATOM_BIOS_INFO_HYPERMEMORY_SIZE_MASK        0x1E00 +#define ATOM_BIOS_INFO_VPOST_WITHOUT_FIRST_MODE_SET 0x2000 +#define ATOM_BIOS_INFO_BIOS_SCRATCH6_SCL2_REDEFINE  0x4000 + + +#ifndef _H2INC + +//Please don't add or expand this bitfield structure below, this one will retire soon.! +typedef struct _ATOM_FIRMWARE_CAPABILITY +{ +#if ATOM_BIG_ENDIAN +  USHORT Reserved:3; +  USHORT HyperMemory_Size:4; +  USHORT HyperMemory_Support:1; +  USHORT PPMode_Assigned:1; +  USHORT WMI_SUPPORT:1; +  USHORT GPUControlsBL:1; +  USHORT EngineClockSS_Support:1; +  USHORT MemoryClockSS_Support:1; +  USHORT ExtendedDesktopSupport:1; +  USHORT DualCRTC_Support:1; +  USHORT FirmwarePosted:1; +#else +  USHORT FirmwarePosted:1; +  USHORT DualCRTC_Support:1; +  USHORT ExtendedDesktopSupport:1; +  USHORT MemoryClockSS_Support:1; +  USHORT EngineClockSS_Support:1; +  USHORT GPUControlsBL:1; +  USHORT WMI_SUPPORT:1; +  USHORT PPMode_Assigned:1; +  USHORT HyperMemory_Support:1; +  USHORT HyperMemory_Size:4; +  USHORT Reserved:3; +#endif +}ATOM_FIRMWARE_CAPABILITY; + +typedef union _ATOM_FIRMWARE_CAPABILITY_ACCESS +{ +  ATOM_FIRMWARE_CAPABILITY sbfAccess; +  USHORT                   susAccess; +}ATOM_FIRMWARE_CAPABILITY_ACCESS; + +#else + +typedef union _ATOM_FIRMWARE_CAPABILITY_ACCESS +{ +  USHORT                   susAccess; +}ATOM_FIRMWARE_CAPABILITY_ACCESS; + +#endif + +typedef struct _ATOM_FIRMWARE_INFO +{ +  ATOM_COMMON_TABLE_HEADER        sHeader;  +  ULONG                           ulFirmwareRevision; +  ULONG                           ulDefaultEngineClock;       //In 10Khz unit +  ULONG                           ulDefaultMemoryClock;       //In 10Khz unit +  ULONG                           ulDriverTargetEngineClock;  //In 10Khz unit +  ULONG                           ulDriverTargetMemoryClock;  //In 10Khz unit +  ULONG                           ulMaxEngineClockPLL_Output; //In 10Khz unit +  ULONG                           ulMaxMemoryClockPLL_Output; //In 10Khz unit +  ULONG                           ulMaxPixelClockPLL_Output;  //In 10Khz unit +  ULONG                           ulASICMaxEngineClock;       //In 10Khz unit +  ULONG                           ulASICMaxMemoryClock;       //In 10Khz unit +  UCHAR                           ucASICMaxTemperature; +  UCHAR                           ucPadding[3];               //Don't use them +  ULONG                           aulReservedForBIOS[3];      //Don't use them +  USHORT                          usMinEngineClockPLL_Input;  //In 10Khz unit +  USHORT                          usMaxEngineClockPLL_Input;  //In 10Khz unit +  USHORT                          usMinEngineClockPLL_Output; //In 10Khz unit +  USHORT                          usMinMemoryClockPLL_Input;  //In 10Khz unit +  USHORT                          usMaxMemoryClockPLL_Input;  //In 10Khz unit +  USHORT                          usMinMemoryClockPLL_Output; //In 10Khz unit +  USHORT                          usMaxPixelClock;            //In 10Khz unit, Max.  Pclk +  USHORT                          usMinPixelClockPLL_Input;   //In 10Khz unit +  USHORT                          usMaxPixelClockPLL_Input;   //In 10Khz unit +  USHORT                          usMinPixelClockPLL_Output;  //In 10Khz unit, the definitions above can't change!!! +  ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability; +  USHORT                          usReferenceClock;           //In 10Khz unit	 +  USHORT                          usPM_RTS_Location;          //RTS PM4 starting location in ROM in 1Kb unit  +  UCHAR                           ucPM_RTS_StreamSize;        //RTS PM4 packets in Kb unit +  UCHAR                           ucDesign_ID;                //Indicate what is the board design +  UCHAR                           ucMemoryModule_ID;          //Indicate what is the board design +}ATOM_FIRMWARE_INFO; + +typedef struct _ATOM_FIRMWARE_INFO_V1_2 +{ +  ATOM_COMMON_TABLE_HEADER        sHeader;  +  ULONG                           ulFirmwareRevision; +  ULONG                           ulDefaultEngineClock;       //In 10Khz unit +  ULONG                           ulDefaultMemoryClock;       //In 10Khz unit +  ULONG                           ulDriverTargetEngineClock;  //In 10Khz unit +  ULONG                           ulDriverTargetMemoryClock;  //In 10Khz unit +  ULONG                           ulMaxEngineClockPLL_Output; //In 10Khz unit +  ULONG                           ulMaxMemoryClockPLL_Output; //In 10Khz unit +  ULONG                           ulMaxPixelClockPLL_Output;  //In 10Khz unit +  ULONG                           ulASICMaxEngineClock;       //In 10Khz unit +  ULONG                           ulASICMaxMemoryClock;       //In 10Khz unit +  UCHAR                           ucASICMaxTemperature; +  UCHAR                           ucMinAllowedBL_Level; +  UCHAR                           ucPadding[2];               //Don't use them +  ULONG                           aulReservedForBIOS[2];      //Don't use them +  ULONG                           ulMinPixelClockPLL_Output;  //In 10Khz unit +  USHORT                          usMinEngineClockPLL_Input;  //In 10Khz unit +  USHORT                          usMaxEngineClockPLL_Input;  //In 10Khz unit +  USHORT                          usMinEngineClockPLL_Output; //In 10Khz unit +  USHORT                          usMinMemoryClockPLL_Input;  //In 10Khz unit +  USHORT                          usMaxMemoryClockPLL_Input;  //In 10Khz unit +  USHORT                          usMinMemoryClockPLL_Output; //In 10Khz unit +  USHORT                          usMaxPixelClock;            //In 10Khz unit, Max.  Pclk +  USHORT                          usMinPixelClockPLL_Input;   //In 10Khz unit +  USHORT                          usMaxPixelClockPLL_Input;   //In 10Khz unit +  USHORT                          usMinPixelClockPLL_Output;  //In 10Khz unit - lower 16bit of ulMinPixelClockPLL_Output +  ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability; +  USHORT                          usReferenceClock;           //In 10Khz unit	 +  USHORT                          usPM_RTS_Location;          //RTS PM4 starting location in ROM in 1Kb unit  +  UCHAR                           ucPM_RTS_StreamSize;        //RTS PM4 packets in Kb unit +  UCHAR                           ucDesign_ID;                //Indicate what is the board design +  UCHAR                           ucMemoryModule_ID;          //Indicate what is the board design +}ATOM_FIRMWARE_INFO_V1_2; + +typedef struct _ATOM_FIRMWARE_INFO_V1_3 +{ +  ATOM_COMMON_TABLE_HEADER        sHeader;  +  ULONG                           ulFirmwareRevision; +  ULONG                           ulDefaultEngineClock;       //In 10Khz unit +  ULONG                           ulDefaultMemoryClock;       //In 10Khz unit +  ULONG                           ulDriverTargetEngineClock;  //In 10Khz unit +  ULONG                           ulDriverTargetMemoryClock;  //In 10Khz unit +  ULONG                           ulMaxEngineClockPLL_Output; //In 10Khz unit +  ULONG                           ulMaxMemoryClockPLL_Output; //In 10Khz unit +  ULONG                           ulMaxPixelClockPLL_Output;  //In 10Khz unit +  ULONG                           ulASICMaxEngineClock;       //In 10Khz unit +  ULONG                           ulASICMaxMemoryClock;       //In 10Khz unit +  UCHAR                           ucASICMaxTemperature; +  UCHAR                           ucMinAllowedBL_Level; +  UCHAR                           ucPadding[2];               //Don't use them +  ULONG                           aulReservedForBIOS;         //Don't use them +  ULONG                           ul3DAccelerationEngineClock;//In 10Khz unit +  ULONG                           ulMinPixelClockPLL_Output;  //In 10Khz unit +  USHORT                          usMinEngineClockPLL_Input;  //In 10Khz unit +  USHORT                          usMaxEngineClockPLL_Input;  //In 10Khz unit +  USHORT                          usMinEngineClockPLL_Output; //In 10Khz unit +  USHORT                          usMinMemoryClockPLL_Input;  //In 10Khz unit +  USHORT                          usMaxMemoryClockPLL_Input;  //In 10Khz unit +  USHORT                          usMinMemoryClockPLL_Output; //In 10Khz unit +  USHORT                          usMaxPixelClock;            //In 10Khz unit, Max.  Pclk +  USHORT                          usMinPixelClockPLL_Input;   //In 10Khz unit +  USHORT                          usMaxPixelClockPLL_Input;   //In 10Khz unit +  USHORT                          usMinPixelClockPLL_Output;  //In 10Khz unit - lower 16bit of ulMinPixelClockPLL_Output +  ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability; +  USHORT                          usReferenceClock;           //In 10Khz unit	 +  USHORT                          usPM_RTS_Location;          //RTS PM4 starting location in ROM in 1Kb unit  +  UCHAR                           ucPM_RTS_StreamSize;        //RTS PM4 packets in Kb unit +  UCHAR                           ucDesign_ID;                //Indicate what is the board design +  UCHAR                           ucMemoryModule_ID;          //Indicate what is the board design +}ATOM_FIRMWARE_INFO_V1_3; + +typedef struct _ATOM_FIRMWARE_INFO_V1_4 +{ +  ATOM_COMMON_TABLE_HEADER        sHeader;  +  ULONG                           ulFirmwareRevision; +  ULONG                           ulDefaultEngineClock;       //In 10Khz unit +  ULONG                           ulDefaultMemoryClock;       //In 10Khz unit +  ULONG                           ulDriverTargetEngineClock;  //In 10Khz unit +  ULONG                           ulDriverTargetMemoryClock;  //In 10Khz unit +  ULONG                           ulMaxEngineClockPLL_Output; //In 10Khz unit +  ULONG                           ulMaxMemoryClockPLL_Output; //In 10Khz unit +  ULONG                           ulMaxPixelClockPLL_Output;  //In 10Khz unit +  ULONG                           ulASICMaxEngineClock;       //In 10Khz unit +  ULONG                           ulASICMaxMemoryClock;       //In 10Khz unit +  UCHAR                           ucASICMaxTemperature; +  UCHAR                           ucMinAllowedBL_Level; +  USHORT                          usBootUpVDDCVoltage;        //In MV unit +  USHORT                          usLcdMinPixelClockPLL_Output; // In MHz unit +  USHORT                          usLcdMaxPixelClockPLL_Output; // In MHz unit +  ULONG                           ul3DAccelerationEngineClock;//In 10Khz unit +  ULONG                           ulMinPixelClockPLL_Output;  //In 10Khz unit +  USHORT                          usMinEngineClockPLL_Input;  //In 10Khz unit +  USHORT                          usMaxEngineClockPLL_Input;  //In 10Khz unit +  USHORT                          usMinEngineClockPLL_Output; //In 10Khz unit +  USHORT                          usMinMemoryClockPLL_Input;  //In 10Khz unit +  USHORT                          usMaxMemoryClockPLL_Input;  //In 10Khz unit +  USHORT                          usMinMemoryClockPLL_Output; //In 10Khz unit +  USHORT                          usMaxPixelClock;            //In 10Khz unit, Max.  Pclk +  USHORT                          usMinPixelClockPLL_Input;   //In 10Khz unit +  USHORT                          usMaxPixelClockPLL_Input;   //In 10Khz unit +  USHORT                          usMinPixelClockPLL_Output;  //In 10Khz unit - lower 16bit of ulMinPixelClockPLL_Output +  ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability; +  USHORT                          usReferenceClock;           //In 10Khz unit	 +  USHORT                          usPM_RTS_Location;          //RTS PM4 starting location in ROM in 1Kb unit  +  UCHAR                           ucPM_RTS_StreamSize;        //RTS PM4 packets in Kb unit +  UCHAR                           ucDesign_ID;                //Indicate what is the board design +  UCHAR                           ucMemoryModule_ID;          //Indicate what is the board design +}ATOM_FIRMWARE_INFO_V1_4; + +#define ATOM_FIRMWARE_INFO_LAST  ATOM_FIRMWARE_INFO_V1_4 + +#define IGP_CAP_FLAG_DYNAMIC_CLOCK_EN      0x2 +#define IGP_CAP_FLAG_AC_CARD               0x4 +#define IGP_CAP_FLAG_SDVO_CARD             0x8 +#define IGP_CAP_FLAG_POSTDIV_BY_2_MODE     0x10 + +typedef struct _ATOM_INTEGRATED_SYSTEM_INFO +{ +  ATOM_COMMON_TABLE_HEADER        sHeader;  +  ULONG	                          ulBootUpEngineClock;		    //in 10kHz unit +  ULONG	                          ulBootUpMemoryClock;		    //in 10kHz unit +  ULONG	                          ulMaxSystemMemoryClock;	    //in 10kHz unit +  ULONG	                          ulMinSystemMemoryClock;	    //in 10kHz unit +  UCHAR                           ucNumberOfCyclesInPeriodHi; +  UCHAR                           ucLCDTimingSel;             //=0:not valid.!=0 sel this timing descriptor from LCD EDID. +  USHORT                          usReserved1; +  USHORT                          usInterNBVoltageLow;        //An intermidiate PMW value to set the voltage  +  USHORT                          usInterNBVoltageHigh;       //Another intermidiate PMW value to set the voltage  +  ULONG	                          ulReserved[2]; + +  USHORT	                        usFSBClock;			            //In MHz unit +  USHORT                          usCapabilityFlag;		        //Bit0=1 indicates the fake HDMI support,Bit1=0/1 for Dynamic clocking dis/enable +																                              //Bit[3:2]== 0:No PCIE card, 1:AC card, 2:SDVO card +                                                              //Bit[4]==1: P/2 mode, ==0: P/1 mode +  USHORT	                        usPCIENBCfgReg7;				    //bit[7:0]=MUX_Sel, bit[9:8]=MUX_SEL_LEVEL2, bit[10]=Lane_Reversal +  USHORT	                        usK8MemoryClock;            //in MHz unit +  USHORT	                        usK8SyncStartDelay;         //in 0.01 us unit +  USHORT	                        usK8DataReturnTime;         //in 0.01 us unit +  UCHAR                           ucMaxNBVoltage; +  UCHAR                           ucMinNBVoltage; +  UCHAR                           ucMemoryType;					      //[7:4]=1:DDR1;=2:DDR2;=3:DDR3.[3:0] is reserved +  UCHAR                           ucNumberOfCyclesInPeriod;		//CG.FVTHROT_PWM_CTRL_REG0.NumberOfCyclesInPeriod  +  UCHAR                           ucStartingPWM_HighTime;     //CG.FVTHROT_PWM_CTRL_REG0.StartingPWM_HighTime +  UCHAR                           ucHTLinkWidth;              //16 bit vs. 8 bit +  UCHAR                           ucMaxNBVoltageHigh;     +  UCHAR                           ucMinNBVoltageHigh; +}ATOM_INTEGRATED_SYSTEM_INFO; + +/* Explanation on entries in ATOM_INTEGRATED_SYSTEM_INFO +ulBootUpMemoryClock:    For Intel IGP,it's the UMA system memory clock  +                        For AMD IGP,it's 0 if no SidePort memory installed or it's the boot-up SidePort memory clock +ulMaxSystemMemoryClock: For Intel IGP,it's the Max freq from memory SPD if memory runs in ASYNC mode or otherwise (SYNC mode) it's 0 +                        For AMD IGP,for now this can be 0 +ulMinSystemMemoryClock: For Intel IGP,it's 133MHz if memory runs in ASYNC mode or otherwise (SYNC mode) it's 0  +                        For AMD IGP,for now this can be 0 + +usFSBClock:             For Intel IGP,it's FSB Freq  +                        For AMD IGP,it's HT Link Speed + +usK8MemoryClock:        For AMD IGP only. For RevF CPU, set it to 200 +usK8SyncStartDelay:     For AMD IGP only. Memory access latency in K8, required for watermark calculation +usK8DataReturnTime:     For AMD IGP only. Memory access latency in K8, required for watermark calculation + +VC:Voltage Control +ucMaxNBVoltage:         Voltage regulator dependent PWM value. Low 8 bits of the value for the max voltage.Set this one to 0xFF if VC without PWM. Set this to 0x0 if no VC at all. +ucMinNBVoltage:         Voltage regulator dependent PWM value. Low 8 bits of the value for the min voltage.Set this one to 0x00 if VC without PWM or no VC at all. + +ucNumberOfCyclesInPeriod:   Indicate how many cycles when PWM duty is 100%. low 8 bits of the value.  +ucNumberOfCyclesInPeriodHi: Indicate how many cycles when PWM duty is 100%. high 8 bits of the value.If the PWM has an inverter,set bit [7]==1,otherwise set it 0  + +ucMaxNBVoltageHigh:     Voltage regulator dependent PWM value. High 8 bits of  the value for the max voltage.Set this one to 0xFF if VC without PWM. Set this to 0x0 if no VC at all. +ucMinNBVoltageHigh:     Voltage regulator dependent PWM value. High 8 bits of the value for the min voltage.Set this one to 0x00 if VC without PWM or no VC at all. + + +usInterNBVoltageLow:    Voltage regulator dependent PWM value. The value makes the the voltage >=Min NB voltage but <=InterNBVoltageHigh. Set this to 0x0000 if VC without PWM or no VC at all. +usInterNBVoltageHigh:   Voltage regulator dependent PWM value. The value makes the the voltage >=InterNBVoltageLow but <=Max NB voltage.Set this to 0x0000 if VC without PWM or no VC at all. +*/ + + +/* +The following IGP table is introduced from RS780, which is supposed to be put by SBIOS in FB before IGP VBIOS starts VPOST; +Then VBIOS will copy the whole structure to its image so all GPU SW components can access this data structure to get whatever they need.  +The enough reservation should allow us to never change table revisions. Whenever needed, a GPU SW component can use reserved portion for new data entries. + +SW components can access the IGP system infor structure in the same way as before +*/ + + +typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 +{ +  ATOM_COMMON_TABLE_HEADER   sHeader; +  ULONG	                     ulBootUpEngineClock;       //in 10kHz unit +  ULONG			                 ulReserved1[2];            //must be 0x0 for the reserved +  ULONG	                     ulBootUpUMAClock;          //in 10kHz unit +  ULONG	                     ulBootUpSidePortClock;     //in 10kHz unit +  ULONG	                     ulMinSidePortClock;        //in 10kHz unit +  ULONG			                 ulReserved2[6];            //must be 0x0 for the reserved +  ULONG                      ulSystemConfig;            //see explanation below +  ULONG                      ulBootUpReqDisplayVector; +  ULONG                      ulOtherDisplayMisc; +  ULONG                      ulDDISlot1Config; +  ULONG                      ulDDISlot2Config; +  UCHAR                      ucMemoryType;              //[3:0]=1:DDR1;=2:DDR2;=3:DDR3.[7:4] is reserved +  UCHAR                      ucUMAChannelNumber; +  UCHAR                      ucDockingPinBit; +  UCHAR                      ucDockingPinPolarity; +  ULONG                      ulDockingPinCFGInfo; +  ULONG                      ulCPUCapInfo; +  USHORT                     usNumberOfCyclesInPeriod; +  USHORT                     usMaxNBVoltage; +  USHORT                     usMinNBVoltage; +  USHORT                     usBootUpNBVoltage; +  ULONG                      ulHTLinkFreq;              //in 10Khz +  USHORT                     usMinHTLinkWidth; +  USHORT                     usMaxHTLinkWidth; +  USHORT                     usUMASyncStartDelay; +  USHORT                     usUMADataReturnTime; +  USHORT                     usLinkStatusZeroTime; +  USHORT                     usReserved; +  ULONG                      ulReserved3[101];          //must be 0x0 +}ATOM_INTEGRATED_SYSTEM_INFO_V2;    + +/* +ulBootUpEngineClock:   Boot-up Engine Clock in 10Khz; +ulBootUpUMAClock:      Boot-up UMA Clock in 10Khz; it must be 0x0 when UMA is not present +ulBootUpSidePortClock: Boot-up SidePort Clock in 10Khz; it must be 0x0 when SidePort Memory is not present,this could be equal to or less than maximum supported Sideport memory clock + +ulSystemConfig:   +Bit[0]: =1 PowerExpress mode =0 Non-PowerExpress mode;  +Bit[1]=1: system is running at overdrived engine clock =0:system is not running at overdrived engine clock + +ulBootUpReqDisplayVector: This dword is a bit vector indicates what display devices are requested during boot-up. Refer to ATOM_DEVICE_xxx_SUPPORT for the bit vector definitions. + +ulOtherDisplayMisc: [15:8]- Bootup LCD Expansion selection; 0-center, 1-full panel size expansion; +			              [7:0] - BootupTV standard selection; This is a bit vector to indicate what TV standards are supported by the system. Refer to ucTVSuppportedStd definition; + +ulDDISlot1Config: Describes the PCIE lane configuration on this DDI PCIE slot (ADD2 card) or connector (Mobile design). +      [3:0]  - Bit vector to indicate PCIE lane config of the DDI slot/connector on chassis (bit 0=1 lane 3:0; bit 1=1 lane 7:4; bit 2=1 lane 11:8; bit 3=1 lane 15:12) +			[7:4]  - Bit vector to indicate PCIE lane config of the same DDI slot/connector on docking station (bit 0=1 lane 3:0; bit 1=1 lane 7:4; bit 2=1 lane 11:8; bit 3=1 lane 15:12)			 +			[15:8] - Lane configuration attribute;  +      [23:16]- Connector type, possible value: +               CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D +               CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D +               CONNECTOR_OBJECT_ID_HDMI_TYPE_A +               CONNECTOR_OBJECT_ID_DISPLAYPORT +			[31:24]- Reserved + +ulDDISlot2Config: Same as Slot1. +ucMemoryType: SidePort memory type, set it to 0x0 when Sideport memory is not installed. Driver needs this info to change sideport memory clock. Not for display in CCC. +For IGP, Hypermemory is the only memory type showed in CCC. + +ucUMAChannelNumber:  how many channels for the UMA; + +ulDockingPinCFGInfo: [15:0]-Bus/Device/Function # to CFG to read this Docking Pin; [31:16]-reg offset in CFG to read this pin  +ucDockingPinBit:     which bit in this register to read the pin status; +ucDockingPinPolarity:Polarity of the pin when docked; + +ulCPUCapInfo:        [7:0]=1:Griffin;[7:0]=2:Greyhound;[7:0]=3:K8, other bits reserved for now and must be 0x0 +  +usNumberOfCyclesInPeriod:Indicate how many cycles when PWM duty is 100%. +usMaxNBVoltage:Voltage regulator dependent PWM value.Set this one to 0xFF if VC without PWM. Set this to 0x0 if no VC at all. +usMinNBVoltage:Voltage regulator dependent PWM value.Set this one to 0x00 if VC without PWM or no VC at all. +usBootUpNBVoltage:Boot-up voltage regulator dependent PWM value. + + +ulHTLinkFreq:        Current HT link Frequency in 10Khz. +usMinHTLinkWidth:    +usMaxHTLinkWidth: +usUMASyncStartDelay: Memory access latency, required for watermark calculation  +usUMADataReturnTime: Memory access latency, required for watermark calculation +usLinkStatusZeroTime:Memory access latency required for watermark calculation, set this to 0x0 for K8 CPU, set a proper value in 0.01 the unit of us  +for Griffin or Greyhound. SBIOS needs to convert to actual time by: +                     if T0Ttime [5:4]=00b, then usLinkStatusZeroTime=T0Ttime [3:0]*0.1us (0.0 to 1.5us) +                     if T0Ttime [5:4]=01b, then usLinkStatusZeroTime=T0Ttime [3:0]*0.5us (0.0 to 7.5us) +                     if T0Ttime [5:4]=10b, then usLinkStatusZeroTime=T0Ttime [3:0]*2.0us (0.0 to 30us) +                     if T0Ttime [5:4]=11b, and T0Ttime [3:0]=0x0 to 0xa, then usLinkStatusZeroTime=T0Ttime [3:0]*20us (0.0 to 200us) +*/ + +#define SYSTEM_CONFIG_POWEREXPRESS_ENABLE                 0x00000001 +#define SYSTEM_CONFIG_RUN_AT_OVERDRIVE_ENGINE             0x00000002 + +#define IGP_DDI_SLOT_LANE_CONFIG_MASK                     0x000000FF + +#define b0IGP_DDI_SLOT_LANE_MAP_MASK                      0x0F +#define b0IGP_DDI_SLOT_DOCKING_LANE_MAP_MASK              0xF0 +#define b0IGP_DDI_SLOT_CONFIG_LANE_0_3                    0x01 +#define b0IGP_DDI_SLOT_CONFIG_LANE_4_7                    0x02 +#define b0IGP_DDI_SLOT_CONFIG_LANE_8_11                   0x04 +#define b0IGP_DDI_SLOT_CONFIG_LANE_12_15                  0x08 + +#define IGP_DDI_SLOT_ATTRIBUTE_MASK                       0x0000FF00 +#define IGP_DDI_SLOT_CONFIG_REVERSED                      0x00000100 +#define b1IGP_DDI_SLOT_CONFIG_REVERSED                    0x01 + +#define IGP_DDI_SLOT_CONNECTOR_TYPE_MASK                  0x00FF0000 + +#define ATOM_CRT_INT_ENCODER1_INDEX                       0x00000000 +#define ATOM_LCD_INT_ENCODER1_INDEX                       0x00000001 +#define ATOM_TV_INT_ENCODER1_INDEX                        0x00000002 +#define ATOM_DFP_INT_ENCODER1_INDEX                       0x00000003 +#define ATOM_CRT_INT_ENCODER2_INDEX                       0x00000004 +#define ATOM_LCD_EXT_ENCODER1_INDEX                       0x00000005 +#define ATOM_TV_EXT_ENCODER1_INDEX                        0x00000006 +#define ATOM_DFP_EXT_ENCODER1_INDEX                       0x00000007 +#define ATOM_CV_INT_ENCODER1_INDEX                        0x00000008 +#define ATOM_DFP_INT_ENCODER2_INDEX                       0x00000009 +#define ATOM_CRT_EXT_ENCODER1_INDEX                       0x0000000A +#define ATOM_CV_EXT_ENCODER1_INDEX                        0x0000000B +#define ATOM_DFP_INT_ENCODER3_INDEX                       0x0000000C +#define ATOM_DFP_INT_ENCODER4_INDEX                       0x0000000D + +// define ASIC internal encoder id ( bit vector ) +#define ASIC_INT_DAC1_ENCODER_ID    											0x00  +#define ASIC_INT_TV_ENCODER_ID														0x02 +#define ASIC_INT_DIG1_ENCODER_ID													0x03 +#define ASIC_INT_DAC2_ENCODER_ID													0x04 +#define ASIC_EXT_TV_ENCODER_ID														0x06 +#define ASIC_INT_DVO_ENCODER_ID														0x07 +#define ASIC_INT_DIG2_ENCODER_ID													0x09 +#define ASIC_EXT_DIG_ENCODER_ID														0x05 + +//define Encoder attribute +#define ATOM_ANALOG_ENCODER																0 +#define ATOM_DIGITAL_ENCODER															1		 + +#define ATOM_DEVICE_CRT1_INDEX                            0x00000000 +#define ATOM_DEVICE_LCD1_INDEX                            0x00000001 +#define ATOM_DEVICE_TV1_INDEX                             0x00000002 +#define ATOM_DEVICE_DFP1_INDEX                            0x00000003 +#define ATOM_DEVICE_CRT2_INDEX                            0x00000004 +#define ATOM_DEVICE_LCD2_INDEX                            0x00000005 +#define ATOM_DEVICE_TV2_INDEX                             0x00000006 +#define ATOM_DEVICE_DFP2_INDEX                            0x00000007 +#define ATOM_DEVICE_CV_INDEX                              0x00000008 +#define ATOM_DEVICE_DFP3_INDEX														0x00000009 +#define ATOM_DEVICE_RESERVEDA_INDEX                       0x0000000A +#define ATOM_DEVICE_RESERVEDB_INDEX                       0x0000000B +#define ATOM_DEVICE_RESERVEDC_INDEX                       0x0000000C +#define ATOM_DEVICE_RESERVEDD_INDEX                       0x0000000D +#define ATOM_DEVICE_RESERVEDE_INDEX                       0x0000000E +#define ATOM_DEVICE_RESERVEDF_INDEX                       0x0000000F +#define ATOM_MAX_SUPPORTED_DEVICE_INFO                    (ATOM_DEVICE_CV_INDEX+2) +#define ATOM_MAX_SUPPORTED_DEVICE_INFO_2                  ATOM_MAX_SUPPORTED_DEVICE_INFO +#define ATOM_MAX_SUPPORTED_DEVICE                         (ATOM_DEVICE_RESERVEDF_INDEX+1) + +#define ATOM_DEVICE_CRT1_SUPPORT                          (0x1L << ATOM_DEVICE_CRT1_INDEX ) +#define ATOM_DEVICE_LCD1_SUPPORT                          (0x1L << ATOM_DEVICE_LCD1_INDEX ) +#define ATOM_DEVICE_TV1_SUPPORT                           (0x1L << ATOM_DEVICE_TV1_INDEX  ) +#define ATOM_DEVICE_DFP1_SUPPORT                          (0x1L << ATOM_DEVICE_DFP1_INDEX) +#define ATOM_DEVICE_CRT2_SUPPORT                          (0x1L << ATOM_DEVICE_CRT2_INDEX ) +#define ATOM_DEVICE_LCD2_SUPPORT                          (0x1L << ATOM_DEVICE_LCD2_INDEX ) +#define ATOM_DEVICE_TV2_SUPPORT                           (0x1L << ATOM_DEVICE_TV2_INDEX  ) +#define ATOM_DEVICE_DFP2_SUPPORT                          (0x1L << ATOM_DEVICE_DFP2_INDEX) +#define ATOM_DEVICE_CV_SUPPORT                            (0x1L << ATOM_DEVICE_CV_INDEX   ) +#define ATOM_DEVICE_DFP3_SUPPORT													(0x1L << ATOM_DEVICE_DFP3_INDEX ) + +#define ATOM_DEVICE_CRT_SUPPORT                           ATOM_DEVICE_CRT1_SUPPORT | ATOM_DEVICE_CRT2_SUPPORT +#define ATOM_DEVICE_DFP_SUPPORT                           ATOM_DEVICE_DFP1_SUPPORT | ATOM_DEVICE_DFP2_SUPPORT |  ATOM_DEVICE_DFP3_SUPPORT +#define ATOM_DEVICE_TV_SUPPORT                            ATOM_DEVICE_TV1_SUPPORT  | ATOM_DEVICE_TV2_SUPPORT +#define ATOM_DEVICE_LCD_SUPPORT                           ATOM_DEVICE_LCD1_SUPPORT | ATOM_DEVICE_LCD2_SUPPORT + +#define ATOM_DEVICE_CONNECTOR_TYPE_MASK                   0x000000F0 +#define ATOM_DEVICE_CONNECTOR_TYPE_SHIFT                  0x00000004 +#define ATOM_DEVICE_CONNECTOR_VGA                         0x00000001 +#define ATOM_DEVICE_CONNECTOR_DVI_I                       0x00000002 +#define ATOM_DEVICE_CONNECTOR_DVI_D                       0x00000003 +#define ATOM_DEVICE_CONNECTOR_DVI_A                       0x00000004 +#define ATOM_DEVICE_CONNECTOR_SVIDEO                      0x00000005 +#define ATOM_DEVICE_CONNECTOR_COMPOSITE                   0x00000006 +#define ATOM_DEVICE_CONNECTOR_LVDS                        0x00000007 +#define ATOM_DEVICE_CONNECTOR_DIGI_LINK                   0x00000008 +#define ATOM_DEVICE_CONNECTOR_SCART                       0x00000009 +#define ATOM_DEVICE_CONNECTOR_HDMI_TYPE_A                 0x0000000A +#define ATOM_DEVICE_CONNECTOR_HDMI_TYPE_B                 0x0000000B +#define ATOM_DEVICE_CONNECTOR_CASE_1                      0x0000000E +#define ATOM_DEVICE_CONNECTOR_DISPLAYPORT                 0x0000000F + + +#define ATOM_DEVICE_DAC_INFO_MASK                         0x0000000F +#define ATOM_DEVICE_DAC_INFO_SHIFT                        0x00000000 +#define ATOM_DEVICE_DAC_INFO_NODAC                        0x00000000 +#define ATOM_DEVICE_DAC_INFO_DACA                         0x00000001 +#define ATOM_DEVICE_DAC_INFO_DACB                         0x00000002 +#define ATOM_DEVICE_DAC_INFO_EXDAC                        0x00000003 + +#define ATOM_DEVICE_I2C_ID_NOI2C                          0x00000000 + +#define ATOM_DEVICE_I2C_LINEMUX_MASK                      0x0000000F +#define ATOM_DEVICE_I2C_LINEMUX_SHIFT                     0x00000000 + +#define ATOM_DEVICE_I2C_ID_MASK                           0x00000070 +#define ATOM_DEVICE_I2C_ID_SHIFT                          0x00000004 +#define ATOM_DEVICE_I2C_ID_IS_FOR_NON_MM_USE              0x00000001 +#define ATOM_DEVICE_I2C_ID_IS_FOR_MM_USE                  0x00000002 +#define ATOM_DEVICE_I2C_ID_IS_FOR_SDVO_USE                0x00000003    //For IGP RS600 +#define ATOM_DEVICE_I2C_ID_IS_FOR_DAC_SCL                 0x00000004    //For IGP RS690 + +#define ATOM_DEVICE_I2C_HARDWARE_CAP_MASK                 0x00000080 +#define ATOM_DEVICE_I2C_HARDWARE_CAP_SHIFT                0x00000007 +#define	ATOM_DEVICE_USES_SOFTWARE_ASSISTED_I2C            0x00000000 +#define	ATOM_DEVICE_USES_HARDWARE_ASSISTED_I2C            0x00000001 + +//  usDeviceSupport: +//  Bits0	= 0 - no CRT1 support= 1- CRT1 is supported +//  Bit 1	= 0 - no LCD1 support= 1- LCD1 is supported +//  Bit 2	= 0 - no TV1  support= 1- TV1  is supported +//  Bit 3	= 0 - no DFP1 support= 1- DFP1 is supported +//  Bit 4	= 0 - no CRT2 support= 1- CRT2 is supported +//  Bit 5	= 0 - no LCD2 support= 1- LCD2 is supported +//  Bit 6	= 0 - no TV2  support= 1- TV2  is supported +//  Bit 7	= 0 - no DFP2 support= 1- DFP2 is supported +//  Bit 8	= 0 - no CV   support= 1- CV   is supported +//  Bit 9	= 0 - no DFP3 support= 1- DFP3 is supported +//  Byte1 (Supported Device Info) +//  Bit 0	= = 0 - no CV support= 1- CV is supported +//    +//   + +//		ucI2C_ConfigID +//    [7:0] - I2C LINE Associate ID +//          = 0   - no I2C +//    [7]		-	HW_Cap        =	1,  [6:0]=HW assisted I2C ID(HW line selection) +//                          =	0,  [6:0]=SW assisted I2C ID +//    [6-4]	- HW_ENGINE_ID  =	1,  HW engine for NON multimedia use +//                          =	2,	HW engine for Multimedia use +//                          =	3-7	Reserved for future I2C engines +//		[3-0] - I2C_LINE_MUX  = A Mux number when it's HW assisted I2C or GPIO ID when it's SW I2C + + +typedef struct _ATOM_I2C_ID_CONFIG +{ +#if ATOM_BIG_ENDIAN +  UCHAR   bfHW_Capable:1; +  UCHAR   bfHW_EngineID:3; +  UCHAR   bfI2C_LineMux:4; +#else +  UCHAR   bfI2C_LineMux:4; +  UCHAR   bfHW_EngineID:3; +  UCHAR   bfHW_Capable:1; +#endif +}ATOM_I2C_ID_CONFIG; + +typedef union _ATOM_I2C_ID_CONFIG_ACCESS +{ +  ATOM_I2C_ID_CONFIG sbfAccess; +  UCHAR              ucAccess; +}ATOM_I2C_ID_CONFIG_ACCESS; +    + +typedef struct _ATOM_GPIO_I2C_ASSIGMENT +{ +  USHORT                    usClkMaskRegisterIndex; +  USHORT                    usClkEnRegisterIndex; +  USHORT                    usClkY_RegisterIndex; +  USHORT                    usClkA_RegisterIndex; +  USHORT                    usDataMaskRegisterIndex; +  USHORT                    usDataEnRegisterIndex; +  USHORT                    usDataY_RegisterIndex; +  USHORT                    usDataA_RegisterIndex; +  ATOM_I2C_ID_CONFIG_ACCESS sucI2cId; +  UCHAR                     ucClkMaskShift; +  UCHAR                     ucClkEnShift; +  UCHAR                     ucClkY_Shift; +  UCHAR                     ucClkA_Shift; +  UCHAR                     ucDataMaskShift; +  UCHAR                     ucDataEnShift; +  UCHAR                     ucDataY_Shift; +  UCHAR                     ucDataA_Shift; +  UCHAR                     ucReserved1; +  UCHAR                     ucReserved2; +}ATOM_GPIO_I2C_ASSIGMENT; + +typedef struct _ATOM_GPIO_I2C_INFO +{  +  ATOM_COMMON_TABLE_HEADER	sHeader; +  ATOM_GPIO_I2C_ASSIGMENT   asGPIO_Info[ATOM_MAX_SUPPORTED_DEVICE]; +}ATOM_GPIO_I2C_INFO; + + +#ifndef _H2INC +   +//Please don't add or expand this bitfield structure below, this one will retire soon.! +typedef struct _ATOM_MODE_MISC_INFO +{  +#if ATOM_BIG_ENDIAN +  USHORT Reserved:6; +  USHORT RGB888:1; +  USHORT DoubleClock:1; +  USHORT Interlace:1; +  USHORT CompositeSync:1; +  USHORT V_ReplicationBy2:1; +  USHORT H_ReplicationBy2:1; +  USHORT VerticalCutOff:1; +  USHORT VSyncPolarity:1;      //0=Active High, 1=Active Low +  USHORT HSyncPolarity:1;      //0=Active High, 1=Active Low +  USHORT HorizontalCutOff:1; +#else +  USHORT HorizontalCutOff:1; +  USHORT HSyncPolarity:1;      //0=Active High, 1=Active Low +  USHORT VSyncPolarity:1;      //0=Active High, 1=Active Low +  USHORT VerticalCutOff:1; +  USHORT H_ReplicationBy2:1; +  USHORT V_ReplicationBy2:1; +  USHORT CompositeSync:1; +  USHORT Interlace:1; +  USHORT DoubleClock:1; +  USHORT RGB888:1; +  USHORT Reserved:6;            +#endif +}ATOM_MODE_MISC_INFO; +   +typedef union _ATOM_MODE_MISC_INFO_ACCESS +{  +  ATOM_MODE_MISC_INFO sbfAccess; +  USHORT              usAccess; +}ATOM_MODE_MISC_INFO_ACCESS; +   +#else +   +typedef union _ATOM_MODE_MISC_INFO_ACCESS +{  +  USHORT              usAccess; +}ATOM_MODE_MISC_INFO_ACCESS; +    +#endif + +// usModeMiscInfo- +#define ATOM_H_CUTOFF           0x01 +#define ATOM_HSYNC_POLARITY     0x02             //0=Active High, 1=Active Low +#define ATOM_VSYNC_POLARITY     0x04             //0=Active High, 1=Active Low +#define ATOM_V_CUTOFF           0x08 +#define ATOM_H_REPLICATIONBY2   0x10 +#define ATOM_V_REPLICATIONBY2   0x20 +#define ATOM_COMPOSITESYNC      0x40 +#define ATOM_INTERLACE          0x80 +#define ATOM_DOUBLE_CLOCK_MODE  0x100 +#define ATOM_RGB888_MODE        0x200 + +//usRefreshRate- +#define ATOM_REFRESH_43         43 +#define ATOM_REFRESH_47         47 +#define ATOM_REFRESH_56         56	 +#define ATOM_REFRESH_60         60 +#define ATOM_REFRESH_65         65 +#define ATOM_REFRESH_70         70 +#define ATOM_REFRESH_72         72 +#define ATOM_REFRESH_75         75 +#define ATOM_REFRESH_85         85 + +// ATOM_MODE_TIMING data are exactly the same as VESA timing data. +// Translation from EDID to ATOM_MODE_TIMING, use the following formula. +// +//	VESA_HTOTAL			=	VESA_ACTIVE + 2* VESA_BORDER + VESA_BLANK +//						=	EDID_HA + EDID_HBL +//	VESA_HDISP			=	VESA_ACTIVE	=	EDID_HA +//	VESA_HSYNC_START	=	VESA_ACTIVE + VESA_BORDER + VESA_FRONT_PORCH +//						=	EDID_HA + EDID_HSO +//	VESA_HSYNC_WIDTH	=	VESA_HSYNC_TIME	=	EDID_HSPW +//	VESA_BORDER			=	EDID_BORDER + + +typedef struct _SET_CRTC_USING_DTD_TIMING_PARAMETERS +{ +  USHORT  usH_Size; +  USHORT  usH_Blanking_Time; +  USHORT  usV_Size; +  USHORT  usV_Blanking_Time;			 +  USHORT  usH_SyncOffset; +  USHORT  usH_SyncWidth; +  USHORT  usV_SyncOffset; +  USHORT  usV_SyncWidth; +  ATOM_MODE_MISC_INFO_ACCESS  susModeMiscInfo;   +  UCHAR   ucH_Border;         // From DFP EDID +  UCHAR   ucV_Border; +  UCHAR   ucCRTC;             // ATOM_CRTC1 or ATOM_CRTC2   +  UCHAR   ucPadding[3]; +}SET_CRTC_USING_DTD_TIMING_PARAMETERS; + +typedef struct _SET_CRTC_TIMING_PARAMETERS +{ +  USHORT                      usH_Total;        // horizontal total +  USHORT                      usH_Disp;         // horizontal display +  USHORT                      usH_SyncStart;    // horozontal Sync start +  USHORT                      usH_SyncWidth;    // horizontal Sync width +  USHORT                      usV_Total;        // vertical total +  USHORT                      usV_Disp;         // vertical display +  USHORT                      usV_SyncStart;    // vertical Sync start +  USHORT                      usV_SyncWidth;    // vertical Sync width +  ATOM_MODE_MISC_INFO_ACCESS  susModeMiscInfo; +  UCHAR                       ucCRTC;           // ATOM_CRTC1 or ATOM_CRTC2 +  UCHAR                       ucOverscanRight;  // right +  UCHAR                       ucOverscanLeft;   // left +  UCHAR                       ucOverscanBottom; // bottom +  UCHAR                       ucOverscanTop;    // top +  UCHAR                       ucReserved; +}SET_CRTC_TIMING_PARAMETERS; +#define SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION SET_CRTC_TIMING_PARAMETERS + + +typedef struct _ATOM_MODE_TIMING +{ +  USHORT  usCRTC_H_Total; +  USHORT  usCRTC_H_Disp; +  USHORT  usCRTC_H_SyncStart; +  USHORT  usCRTC_H_SyncWidth; +  USHORT  usCRTC_V_Total; +  USHORT  usCRTC_V_Disp; +  USHORT  usCRTC_V_SyncStart; +  USHORT  usCRTC_V_SyncWidth; +  USHORT  usPixelClock;					                 //in 10Khz unit +  ATOM_MODE_MISC_INFO_ACCESS  susModeMiscInfo; +  USHORT  usCRTC_OverscanRight; +  USHORT  usCRTC_OverscanLeft; +  USHORT  usCRTC_OverscanBottom; +  USHORT  usCRTC_OverscanTop; +  USHORT  usReserve; +  UCHAR   ucInternalModeNumber; +  UCHAR   ucRefreshRate; +}ATOM_MODE_TIMING; + +  +typedef struct _ATOM_DTD_FORMAT +{ +  USHORT  usPixClk; +  USHORT  usHActive; +  USHORT  usHBlanking_Time; +  USHORT  usVActive; +  USHORT  usVBlanking_Time;			 +  USHORT  usHSyncOffset; +  USHORT  usHSyncWidth; +  USHORT  usVSyncOffset; +  USHORT  usVSyncWidth; +  USHORT  usImageHSize; +  USHORT  usImageVSize; +  UCHAR   ucHBorder; +  UCHAR   ucVBorder; +  ATOM_MODE_MISC_INFO_ACCESS susModeMiscInfo; +  UCHAR   ucInternalModeNumber; +  UCHAR   ucRefreshRate; +}ATOM_DTD_FORMAT; + +#define SUPPORTED_LCD_REFRESHRATE_30Hz          0x0004 +#define SUPPORTED_LCD_REFRESHRATE_40Hz          0x0008 +#define SUPPORTED_LCD_REFRESHRATE_50Hz          0x0010 +#define SUPPORTED_LCD_REFRESHRATE_60Hz          0x0020 + +/****************************LVDS Info Table Definitions **********************/ +//ucTableFormatRevision=1 +//ucTableContentRevision=1 +typedef struct _ATOM_LVDS_INFO +{ +  ATOM_COMMON_TABLE_HEADER sHeader;   +  ATOM_DTD_FORMAT     sLCDTiming; +  USHORT              usModePatchTableOffset; +  USHORT              usSupportedRefreshRate;     //Refer to panel info table in ATOMBIOS extension Spec. +  USHORT              usOffDelayInMs; +  UCHAR               ucPowerSequenceDigOntoDEin10Ms; +  UCHAR               ucPowerSequenceDEtoBLOnin10Ms; +  UCHAR               ucLVDS_Misc;               // Bit0:{=0:single, =1:dual},Bit1 {=0:666RGB, =1:888RGB},Bit2:3:{Grey level} +                                                 // Bit4:{=0:LDI format for RGB888, =1 FPDI format for RGB888} +                                                 // Bit5:{=0:Spatial Dithering disabled;1 Spatial Dithering enabled} +                                                 // Bit6:{=0:Temporal Dithering disabled;1 Temporal Dithering enabled} +  UCHAR               ucPanelDefaultRefreshRate; +  UCHAR               ucPanelIdentification; +  UCHAR               ucSS_Id; +}ATOM_LVDS_INFO; + +//ucTableFormatRevision=1 +//ucTableContentRevision=2 +typedef struct _ATOM_LVDS_INFO_V12 +{ +  ATOM_COMMON_TABLE_HEADER sHeader;   +  ATOM_DTD_FORMAT     sLCDTiming; +  USHORT              usExtInfoTableOffset; +  USHORT              usSupportedRefreshRate;     //Refer to panel info table in ATOMBIOS extension Spec. +  USHORT              usOffDelayInMs; +  UCHAR               ucPowerSequenceDigOntoDEin10Ms; +  UCHAR               ucPowerSequenceDEtoBLOnin10Ms; +  UCHAR               ucLVDS_Misc;               // Bit0:{=0:single, =1:dual},Bit1 {=0:666RGB, =1:888RGB},Bit2:3:{Grey level} +                                                 // Bit4:{=0:LDI format for RGB888, =1 FPDI format for RGB888} +                                                 // Bit5:{=0:Spatial Dithering disabled;1 Spatial Dithering enabled} +                                                 // Bit6:{=0:Temporal Dithering disabled;1 Temporal Dithering enabled} +  UCHAR               ucPanelDefaultRefreshRate; +  UCHAR               ucPanelIdentification; +  UCHAR               ucSS_Id; +  USHORT              usLCDVenderID; +  USHORT              usLCDProductID; +  UCHAR               ucLCDPanel_SpecialHandlingCap;  +	UCHAR								ucPanelInfoSize;					//  start from ATOM_DTD_FORMAT to end of panel info, include ExtInfoTable +  UCHAR               ucReserved[2]; +}ATOM_LVDS_INFO_V12; + +#define ATOM_LVDS_INFO_LAST  ATOM_LVDS_INFO_V12 + +typedef struct  _ATOM_PATCH_RECORD_MODE +{ +  UCHAR     ucRecordType; +  USHORT    usHDisp; +  USHORT    usVDisp; +}ATOM_PATCH_RECORD_MODE; + +typedef struct  _ATOM_LCD_RTS_RECORD +{ +  UCHAR     ucRecordType; +  UCHAR     ucRTSValue; +}ATOM_LCD_RTS_RECORD; + +//!! If the record below exits, it shoud always be the first record for easy use in command table!!!  +typedef struct  _ATOM_LCD_MODE_CONTROL_CAP +{ +  UCHAR     ucRecordType; +  USHORT    usLCDCap; +}ATOM_LCD_MODE_CONTROL_CAP; + +#define LCD_MODE_CAP_BL_OFF                   1 +#define LCD_MODE_CAP_CRTC_OFF                 2 +#define LCD_MODE_CAP_PANEL_OFF                4 + +typedef struct _ATOM_FAKE_EDID_PATCH_RECORD +{ +  UCHAR ucRecordType; +  UCHAR ucFakeEDIDLength; +  UCHAR ucFakeEDIDString[1];    // This actually has ucFakeEdidLength elements. +} ATOM_FAKE_EDID_PATCH_RECORD; + +typedef struct  _ATOM_PANEL_RESOLUTION_PATCH_RECORD +{ +   UCHAR    ucRecordType; +   USHORT		usHSize; +   USHORT		usVSize; +}ATOM_PANEL_RESOLUTION_PATCH_RECORD; + +#define LCD_MODE_PATCH_RECORD_MODE_TYPE       1 +#define LCD_RTS_RECORD_TYPE                   2 +#define LCD_CAP_RECORD_TYPE                   3 +#define LCD_FAKE_EDID_PATCH_RECORD_TYPE       4 +#define LCD_PANEL_RESOLUTION_RECORD_TYPE      5 +#define ATOM_RECORD_END_TYPE                  0xFF + +/****************************Spread Spectrum Info Table Definitions **********************/ + +//ucTableFormatRevision=1 +//ucTableContentRevision=2 +typedef struct _ATOM_SPREAD_SPECTRUM_ASSIGNMENT +{ +  USHORT              usSpreadSpectrumPercentage;  +  UCHAR               ucSpreadSpectrumType;	    //Bit1=0 Down Spread,=1 Center Spread. Bit1=1 Ext. =0 Int. Others:TBD +  UCHAR               ucSS_Step; +  UCHAR               ucSS_Delay; +  UCHAR               ucSS_Id; +  UCHAR               ucRecommandedRef_Div; +  UCHAR               ucSS_Range;               //it was reserved for V11 +}ATOM_SPREAD_SPECTRUM_ASSIGNMENT; + +#define ATOM_MAX_SS_ENTRY                      16 +#define ATOM_DP_SS_ID1												 0x0f1			// SS modulation freq=30k +#define ATOM_DP_SS_ID2												 0x0f2			// SS modulation freq=33k + + +#define ATOM_SS_DOWN_SPREAD_MODE_MASK          0x00000000 +#define ATOM_SS_DOWN_SPREAD_MODE               0x00000000 +#define ATOM_SS_CENTRE_SPREAD_MODE_MASK        0x00000001 +#define ATOM_SS_CENTRE_SPREAD_MODE             0x00000001 +#define ATOM_INTERNAL_SS_MASK                  0x00000000 +#define ATOM_EXTERNAL_SS_MASK                  0x00000002 +#define EXEC_SS_STEP_SIZE_SHIFT                2 +#define EXEC_SS_DELAY_SHIFT                    4     +#define ACTIVEDATA_TO_BLON_DELAY_SHIFT         4 + +typedef struct _ATOM_SPREAD_SPECTRUM_INFO +{  +  ATOM_COMMON_TABLE_HEADER	sHeader; +  ATOM_SPREAD_SPECTRUM_ASSIGNMENT   asSS_Info[ATOM_MAX_SS_ENTRY]; +}ATOM_SPREAD_SPECTRUM_INFO; + + + + +//ucTVBootUpDefaultStd definiton: + +//ATOM_TV_NTSC                1 +//ATOM_TV_NTSCJ               2 +//ATOM_TV_PAL                 3 +//ATOM_TV_PALM                4 +//ATOM_TV_PALCN               5 +//ATOM_TV_PALN                6 +//ATOM_TV_PAL60               7 +//ATOM_TV_SECAM               8 + + +//ucTVSuppportedStd definition: +#define NTSC_SUPPORT          0x1 +#define NTSCJ_SUPPORT         0x2 + +#define PAL_SUPPORT           0x4 +#define PALM_SUPPORT          0x8 +#define PALCN_SUPPORT         0x10 +#define PALN_SUPPORT          0x20 +#define PAL60_SUPPORT         0x40 +#define SECAM_SUPPORT         0x80 + +#define MAX_SUPPORTED_TV_TIMING    2 + +typedef struct _ATOM_ANALOG_TV_INFO +{ +  ATOM_COMMON_TABLE_HEADER sHeader;   +  UCHAR                    ucTV_SupportedStandard; +  UCHAR                    ucTV_BootUpDefaultStandard;  +  UCHAR                    ucExt_TV_ASIC_ID; +  UCHAR                    ucExt_TV_ASIC_SlaveAddr; +  /*ATOM_DTD_FORMAT          aModeTimings[MAX_SUPPORTED_TV_TIMING];*/ +  ATOM_MODE_TIMING         aModeTimings[MAX_SUPPORTED_TV_TIMING]; +}ATOM_ANALOG_TV_INFO; + + +/**************************************************************************/ +// VRAM usage and their defintions + +// One chunk of VRAM used by Bios are for HWICON surfaces,EDID data. +// Current Mode timing and Dail Timing and/or STD timing data EACH device. They can be broken down as below. +// All the addresses below are the offsets from the frame buffer start.They all MUST be Dword aligned! +// To driver: The physical address of this memory portion=mmFB_START(4K aligned)+ATOMBIOS_VRAM_USAGE_START_ADDR+ATOM_x_ADDR +// To Bios:  ATOMBIOS_VRAM_USAGE_START_ADDR+ATOM_x_ADDR->MM_INDEX  + +#ifndef VESA_MEMORY_IN_64K_BLOCK +#define VESA_MEMORY_IN_64K_BLOCK        0x100       //256*64K=16Mb (Max. VESA memory is 16Mb!) +#endif + +#define ATOM_EDID_RAW_DATASIZE          256         //In Bytes +#define ATOM_HWICON_SURFACE_SIZE        4096        //In Bytes +#define ATOM_HWICON_INFOTABLE_SIZE      32 +#define MAX_DTD_MODE_IN_VRAM            6 +#define ATOM_DTD_MODE_SUPPORT_TBL_SIZE  (MAX_DTD_MODE_IN_VRAM*28)    //28= (SIZEOF ATOM_DTD_FORMAT)  +#define ATOM_STD_MODE_SUPPORT_TBL_SIZE  32*8                         //32 is a predefined number,8= (SIZEOF ATOM_STD_FORMAT) +#define DFP_ENCODER_TYPE_OFFSET					0x80 +#define DP_ENCODER_LANE_NUM_OFFSET			0x84 +#define DP_ENCODER_LINK_RATE_OFFSET			0x88 + +#define ATOM_HWICON1_SURFACE_ADDR       0 +#define ATOM_HWICON2_SURFACE_ADDR       (ATOM_HWICON1_SURFACE_ADDR + ATOM_HWICON_SURFACE_SIZE) +#define ATOM_HWICON_INFOTABLE_ADDR      (ATOM_HWICON2_SURFACE_ADDR + ATOM_HWICON_SURFACE_SIZE) +#define ATOM_CRT1_EDID_ADDR             (ATOM_HWICON_INFOTABLE_ADDR + ATOM_HWICON_INFOTABLE_SIZE) +#define ATOM_CRT1_DTD_MODE_TBL_ADDR     (ATOM_CRT1_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_CRT1_STD_MODE_TBL_ADDR	    (ATOM_CRT1_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_LCD1_EDID_ADDR             (ATOM_CRT1_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_LCD1_DTD_MODE_TBL_ADDR     (ATOM_LCD1_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_LCD1_STD_MODE_TBL_ADDR   	(ATOM_LCD1_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_TV1_DTD_MODE_TBL_ADDR      (ATOM_LCD1_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_DFP1_EDID_ADDR             (ATOM_TV1_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_DFP1_DTD_MODE_TBL_ADDR     (ATOM_DFP1_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_DFP1_STD_MODE_TBL_ADDR	    (ATOM_DFP1_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_CRT2_EDID_ADDR             (ATOM_DFP1_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_CRT2_DTD_MODE_TBL_ADDR     (ATOM_CRT2_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_CRT2_STD_MODE_TBL_ADDR	    (ATOM_CRT2_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_LCD2_EDID_ADDR             (ATOM_CRT2_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_LCD2_DTD_MODE_TBL_ADDR     (ATOM_LCD2_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_LCD2_STD_MODE_TBL_ADDR   	(ATOM_LCD2_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_TV2_EDID_ADDR              (ATOM_LCD2_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_TV2_DTD_MODE_TBL_ADDR      (ATOM_TV2_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_TV2_STD_MODE_TBL_ADDR  	  (ATOM_TV2_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_DFP2_EDID_ADDR             (ATOM_TV2_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_DFP2_DTD_MODE_TBL_ADDR     (ATOM_DFP2_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_DFP2_STD_MODE_TBL_ADDR     (ATOM_DFP2_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_CV_EDID_ADDR               (ATOM_DFP2_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_CV_DTD_MODE_TBL_ADDR       (ATOM_CV_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_CV_STD_MODE_TBL_ADDR       (ATOM_CV_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_DFP3_EDID_ADDR             (ATOM_CV_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE) +#define ATOM_DFP3_DTD_MODE_TBL_ADDR     (ATOM_DFP3_EDID_ADDR + ATOM_EDID_RAW_DATASIZE) +#define ATOM_DFP3_STD_MODE_TBL_ADDR     (ATOM_DFP3_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE) + +#define ATOM_DP_TRAINING_TBL_ADDR				(ATOM_DFP3_STD_MODE_TBL_ADDR+ATOM_STD_MODE_SUPPORT_TBL_SIZE)        + +#define ATOM_STACK_STORAGE_START        (ATOM_DP_TRAINING_TBL_ADDR+256)        +#define ATOM_STACK_STORAGE_END          ATOM_STACK_STORAGE_START+512         + +//The size below is in Kb! +#define ATOM_VRAM_RESERVE_SIZE         ((((ATOM_STACK_STORAGE_END - ATOM_HWICON1_SURFACE_ADDR)>>10)+4)&0xFFFC) +    +#define	ATOM_VRAM_OPERATION_FLAGS_MASK         0xC0000000L +#define ATOM_VRAM_OPERATION_FLAGS_SHIFT        30 +#define	ATOM_VRAM_BLOCK_NEEDS_NO_RESERVATION   0x1 +#define	ATOM_VRAM_BLOCK_NEEDS_RESERVATION      0x0 + +#define ATOM_MAX_FIRMWARE_VRAM_USAGE_INFO			1 + +typedef struct _ATOM_FIRMWARE_VRAM_RESERVE_INFO +{ +  ULONG   ulStartAddrUsedByFirmware; +  USHORT  usFirmwareUseInKb; +  USHORT  usReserved; +}ATOM_FIRMWARE_VRAM_RESERVE_INFO; + +typedef struct _ATOM_VRAM_USAGE_BY_FIRMWARE +{ +  ATOM_COMMON_TABLE_HEADER sHeader;   +  ATOM_FIRMWARE_VRAM_RESERVE_INFO	asFirmwareVramReserveInfo[ATOM_MAX_FIRMWARE_VRAM_USAGE_INFO]; +}ATOM_VRAM_USAGE_BY_FIRMWARE; + +/**************************************************************************/ +//GPIO Pin lut table definition +typedef struct _ATOM_GPIO_PIN_ASSIGNMENT +{ +  USHORT                   usGpioPin_AIndex; +  UCHAR                    ucGpioPinBitShift; +  UCHAR                    ucGPIO_ID; +}ATOM_GPIO_PIN_ASSIGNMENT; + +typedef struct _ATOM_GPIO_PIN_LUT +{ +  ATOM_COMMON_TABLE_HEADER  sHeader; +  ATOM_GPIO_PIN_ASSIGNMENT	asGPIO_Pin[1]; +}ATOM_GPIO_PIN_LUT; + +/**************************************************************************/ + + +#define GPIO_PIN_ACTIVE_HIGH          0x1 + +#define MAX_SUPPORTED_CV_STANDARDS    5 + +// definitions for ATOM_D_INFO.ucSettings +#define ATOM_GPIO_SETTINGS_BITSHIFT_MASK  0x1F    // [4:0] +#define ATOM_GPIO_SETTINGS_RESERVED_MASK  0x60    // [6:5] = must be zeroed out +#define ATOM_GPIO_SETTINGS_ACTIVE_MASK    0x80    // [7] + +typedef struct _ATOM_GPIO_INFO +{ +  USHORT  usAOffset; +  UCHAR   ucSettings; +  UCHAR   ucReserved; +}ATOM_GPIO_INFO; + +// definitions for ATOM_COMPONENT_VIDEO_INFO.ucMiscInfo (bit vector) +#define ATOM_CV_RESTRICT_FORMAT_SELECTION           0x2 + +// definitions for ATOM_COMPONENT_VIDEO_INFO.uc480i/uc480p/uc720p/uc1080i +#define ATOM_GPIO_DEFAULT_MODE_EN                   0x80 //[7]; +#define ATOM_GPIO_SETTING_PERMODE_MASK              0x7F //[6:0] + +// definitions for ATOM_COMPONENT_VIDEO_INFO.ucLetterBoxMode +//Line 3 out put 5V. +#define ATOM_CV_LINE3_ASPECTRATIO_16_9_GPIO_A       0x01     //represent gpio 3 state for 16:9 +#define ATOM_CV_LINE3_ASPECTRATIO_16_9_GPIO_B       0x02     //represent gpio 4 state for 16:9 +#define ATOM_CV_LINE3_ASPECTRATIO_16_9_GPIO_SHIFT   0x0    + +//Line 3 out put 2.2V               +#define ATOM_CV_LINE3_ASPECTRATIO_4_3_LETBOX_GPIO_A 0x04     //represent gpio 3 state for 4:3 Letter box +#define ATOM_CV_LINE3_ASPECTRATIO_4_3_LETBOX_GPIO_B 0x08     //represent gpio 4 state for 4:3 Letter box +#define ATOM_CV_LINE3_ASPECTRATIO_4_3_LETBOX_GPIO_SHIFT 0x2      + +//Line 3 out put 0V +#define ATOM_CV_LINE3_ASPECTRATIO_4_3_GPIO_A        0x10     //represent gpio 3 state for 4:3 +#define ATOM_CV_LINE3_ASPECTRATIO_4_3_GPIO_B        0x20     //represent gpio 4 state for 4:3 +#define ATOM_CV_LINE3_ASPECTRATIO_4_3_GPIO_SHIFT    0x4  + +#define ATOM_CV_LINE3_ASPECTRATIO_MASK              0x3F     // bit [5:0] + +#define ATOM_CV_LINE3_ASPECTRATIO_EXIST             0x80     //bit 7 + +//GPIO bit index in gpio setting per mode value, also represend the block no. in gpio blocks. +#define ATOM_GPIO_INDEX_LINE3_ASPECRATIO_GPIO_A   3   //bit 3 in uc480i/uc480p/uc720p/uc1080i, which represend the default gpio bit setting for the mode. +#define ATOM_GPIO_INDEX_LINE3_ASPECRATIO_GPIO_B   4   //bit 4 in uc480i/uc480p/uc720p/uc1080i, which represend the default gpio bit setting for the mode. + + +typedef struct _ATOM_COMPONENT_VIDEO_INFO +{ +  ATOM_COMMON_TABLE_HEADER sHeader; +  USHORT             usMask_PinRegisterIndex; +  USHORT             usEN_PinRegisterIndex; +  USHORT             usY_PinRegisterIndex; +  USHORT             usA_PinRegisterIndex; +  UCHAR              ucBitShift; +  UCHAR              ucPinActiveState;  //ucPinActiveState: Bit0=1 active high, =0 active low +  ATOM_DTD_FORMAT    sReserved;         // must be zeroed out +  UCHAR              ucMiscInfo; +  UCHAR              uc480i; +  UCHAR              uc480p; +  UCHAR              uc720p; +  UCHAR              uc1080i; +  UCHAR              ucLetterBoxMode; +  UCHAR              ucReserved[3]; +  UCHAR              ucNumOfWbGpioBlocks; //For Component video D-Connector support. If zere, NTSC type connector +  ATOM_GPIO_INFO     aWbGpioStateBlock[MAX_SUPPORTED_CV_STANDARDS]; +  ATOM_DTD_FORMAT    aModeTimings[MAX_SUPPORTED_CV_STANDARDS]; +}ATOM_COMPONENT_VIDEO_INFO; + +//ucTableFormatRevision=2 +//ucTableContentRevision=1 +typedef struct _ATOM_COMPONENT_VIDEO_INFO_V21 +{ +  ATOM_COMMON_TABLE_HEADER sHeader; +  UCHAR              ucMiscInfo; +  UCHAR              uc480i; +  UCHAR              uc480p; +  UCHAR              uc720p; +  UCHAR              uc1080i; +  UCHAR              ucReserved; +  UCHAR              ucLetterBoxMode; +  UCHAR              ucNumOfWbGpioBlocks; //For Component video D-Connector support. If zere, NTSC type connector +  ATOM_GPIO_INFO     aWbGpioStateBlock[MAX_SUPPORTED_CV_STANDARDS]; +  ATOM_DTD_FORMAT    aModeTimings[MAX_SUPPORTED_CV_STANDARDS]; +}ATOM_COMPONENT_VIDEO_INFO_V21; + +#define ATOM_COMPONENT_VIDEO_INFO_LAST  ATOM_COMPONENT_VIDEO_INFO_V21 + +/**************************************************************************/ +//Object table starts here +typedef struct _ATOM_OBJECT_HEADER +{  +  ATOM_COMMON_TABLE_HEADER	sHeader; +  USHORT                    usDeviceSupport; +  USHORT                    usConnectorObjectTableOffset; +  USHORT                    usRouterObjectTableOffset; +  USHORT                    usEncoderObjectTableOffset; +  USHORT                    usProtectionObjectTableOffset; //only available when Protection block is independent. +  USHORT                    usDisplayPathTableOffset; +}ATOM_OBJECT_HEADER; + + +typedef struct  _ATOM_DISPLAY_OBJECT_PATH +{ +  USHORT    usDeviceTag;                                   //supported device  +  USHORT    usSize;                                        //the size of ATOM_DISPLAY_OBJECT_PATH +  USHORT    usConnObjectId;                                //Connector Object ID  +  USHORT    usGPUObjectId;                                 //GPU ID  +  USHORT    usGraphicObjIds[1];                             //1st Encoder Obj source from GPU to last Graphic Obj destinate to connector. +}ATOM_DISPLAY_OBJECT_PATH; + +typedef struct _ATOM_DISPLAY_OBJECT_PATH_TABLE +{ +  UCHAR                           ucNumOfDispPath; +  UCHAR                           ucVersion; +  UCHAR                           ucPadding[2]; +  ATOM_DISPLAY_OBJECT_PATH        asDispPath[1]; +}ATOM_DISPLAY_OBJECT_PATH_TABLE; + + +typedef struct _ATOM_OBJECT                                //each object has this structure     +{ +  USHORT              usObjectID; +  USHORT              usSrcDstTableOffset; +  USHORT              usRecordOffset;                     //this pointing to a bunch of records defined below +  USHORT              usReserved; +}ATOM_OBJECT; + +typedef struct _ATOM_OBJECT_TABLE                         //Above 4 object table offset pointing to a bunch of objects all have this structure      +{ +  UCHAR               ucNumberOfObjects; +  UCHAR               ucPadding[3]; +  ATOM_OBJECT         asObjects[1]; +}ATOM_OBJECT_TABLE; + +typedef struct _ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT         //usSrcDstTableOffset pointing to this structure +{ +  UCHAR               ucNumberOfSrc; +  USHORT              usSrcObjectID[1]; +  UCHAR               ucNumberOfDst; +  USHORT              usDstObjectID[1]; +}ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT; + + +//Related definitions, all records are differnt but they have a commond header +typedef struct _ATOM_COMMON_RECORD_HEADER +{ +  UCHAR               ucRecordType;                      //An emun to indicate the record type +  UCHAR               ucRecordSize;                      //The size of the whole record in byte +}ATOM_COMMON_RECORD_HEADER; + + +#define ATOM_I2C_RECORD_TYPE                           1          +#define ATOM_HPD_INT_RECORD_TYPE                       2 +#define ATOM_OUTPUT_PROTECTION_RECORD_TYPE             3 +#define ATOM_CONNECTOR_DEVICE_TAG_RECORD_TYPE          4 +#define	ATOM_CONNECTOR_DVI_EXT_INPUT_RECORD_TYPE	     5 //Obsolete, switch to use GPIO_CNTL_RECORD_TYPE +#define ATOM_ENCODER_FPGA_CONTROL_RECORD_TYPE          6 //Obsolete, switch to use GPIO_CNTL_RECORD_TYPE +#define ATOM_CONNECTOR_CVTV_SHARE_DIN_RECORD_TYPE      7 +#define ATOM_JTAG_RECORD_TYPE                          8 //Obsolete, switch to use GPIO_CNTL_RECORD_TYPE +#define ATOM_OBJECT_GPIO_CNTL_RECORD_TYPE              9 +#define ATOM_ENCODER_DVO_CF_RECORD_TYPE               10 +#define ATOM_CONNECTOR_CF_RECORD_TYPE                 11 +#define	ATOM_CONNECTOR_HARDCODE_DTD_RECORD_TYPE	      12 +#define ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD_TYPE  13 +#define ATOM_ROUTER_DDC_PATH_SELECT_RECORD_TYPE				14 +#define ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD_TYPE					15 + +//Must be updated when new record type is added,equal to that record definition! +#define ATOM_MAX_OBJECT_RECORD_NUMBER             ATOM_CONNECTOR_CF_RECORD_TYPE      + +typedef struct  _ATOM_I2C_RECORD +{ +  ATOM_COMMON_RECORD_HEADER   sheader; +  ATOM_I2C_ID_CONFIG          sucI2cId;  +  UCHAR                       ucI2CAddr;              //The slave address, it's 0 when the record is attached to connector for DDC +}ATOM_I2C_RECORD; + +typedef struct  _ATOM_HPD_INT_RECORD +{ +  ATOM_COMMON_RECORD_HEADER   sheader; +  UCHAR                       ucHPDIntGPIOID;         //Corresponding block in GPIO_PIN_INFO table gives the pin info            +  UCHAR                       ucPluggged_PinState; +}ATOM_HPD_INT_RECORD; + + +typedef struct  _ATOM_OUTPUT_PROTECTION_RECORD  +{ +  ATOM_COMMON_RECORD_HEADER   sheader; +  UCHAR                       ucProtectionFlag; +  UCHAR                       ucReserved; +}ATOM_OUTPUT_PROTECTION_RECORD; + +typedef struct  _ATOM_CONNECTOR_DEVICE_TAG +{ +  ULONG                       ulACPIDeviceEnum;       //Reserved for now +  USHORT                      usDeviceID;             //This Id is same as "ATOM_DEVICE_XXX_SUPPORT" +  USHORT                      usPadding; +}ATOM_CONNECTOR_DEVICE_TAG; + +typedef struct  _ATOM_CONNECTOR_DEVICE_TAG_RECORD +{ +  ATOM_COMMON_RECORD_HEADER   sheader; +  UCHAR                       ucNumberOfDevice; +  UCHAR                       ucReserved; +  ATOM_CONNECTOR_DEVICE_TAG   asDeviceTag[1];         //This Id is same as "ATOM_DEVICE_XXX_SUPPORT", 1 is only for allocation +}ATOM_CONNECTOR_DEVICE_TAG_RECORD; + + +typedef struct  _ATOM_CONNECTOR_DVI_EXT_INPUT_RECORD +{ +  ATOM_COMMON_RECORD_HEADER   sheader; +  UCHAR						            ucConfigGPIOID; +  UCHAR						            ucConfigGPIOState;	    //Set to 1 when it's active high to enable external flow in +  UCHAR                       ucFlowinGPIPID; +  UCHAR                       ucExtInGPIPID; +}ATOM_CONNECTOR_DVI_EXT_INPUT_RECORD; + +typedef struct  _ATOM_ENCODER_FPGA_CONTROL_RECORD +{ +  ATOM_COMMON_RECORD_HEADER   sheader; +  UCHAR                       ucCTL1GPIO_ID; +  UCHAR                       ucCTL1GPIOState;        //Set to 1 when it's active high +  UCHAR                       ucCTL2GPIO_ID; +  UCHAR                       ucCTL2GPIOState;        //Set to 1 when it's active high +  UCHAR                       ucCTL3GPIO_ID; +  UCHAR                       ucCTL3GPIOState;        //Set to 1 when it's active high +  UCHAR                       ucCTLFPGA_IN_ID; +  UCHAR                       ucPadding[3]; +}ATOM_ENCODER_FPGA_CONTROL_RECORD; + +typedef struct  _ATOM_CONNECTOR_CVTV_SHARE_DIN_RECORD +{ +  ATOM_COMMON_RECORD_HEADER   sheader; +  UCHAR                       ucGPIOID;               //Corresponding block in GPIO_PIN_INFO table gives the pin info  +  UCHAR                       ucTVActiveState;        //Indicating when the pin==0 or 1 when TV is connected +}ATOM_CONNECTOR_CVTV_SHARE_DIN_RECORD; + +typedef struct  _ATOM_JTAG_RECORD +{ +  ATOM_COMMON_RECORD_HEADER   sheader; +  UCHAR                       ucTMSGPIO_ID; +  UCHAR                       ucTMSGPIOState;         //Set to 1 when it's active high +  UCHAR                       ucTCKGPIO_ID; +  UCHAR                       ucTCKGPIOState;         //Set to 1 when it's active high +  UCHAR                       ucTDOGPIO_ID; +  UCHAR                       ucTDOGPIOState;         //Set to 1 when it's active high +  UCHAR                       ucTDIGPIO_ID; +  UCHAR                       ucTDIGPIOState;         //Set to 1 when it's active high +  UCHAR                       ucPadding[2]; +}ATOM_JTAG_RECORD; + + +//The following generic object gpio pin control record type will replace JTAG_RECORD/FPGA_CONTROL_RECORD/DVI_EXT_INPUT_RECORD above gradually +typedef struct _ATOM_GPIO_PIN_CONTROL_PAIR +{ +  UCHAR                       ucGPIOID;               // GPIO_ID, find the corresponding ID in GPIO_LUT table +  UCHAR                       ucGPIO_PinState;        // Pin state showing how to set-up the pin +}ATOM_GPIO_PIN_CONTROL_PAIR; + +typedef struct  _ATOM_OBJECT_GPIO_CNTL_RECORD +{ +  ATOM_COMMON_RECORD_HEADER   sheader; +  UCHAR                       ucFlags;                // Future expnadibility +  UCHAR                       ucNumberOfPins;         // Number of GPIO pins used to control the object +  ATOM_GPIO_PIN_CONTROL_PAIR  asGpio[1];              // the real gpio pin pair determined by number of pins ucNumberOfPins +}ATOM_OBJECT_GPIO_CNTL_RECORD; + +//Definitions for GPIO pin state  +#define GPIO_PIN_TYPE_INPUT             0x00 +#define GPIO_PIN_TYPE_OUTPUT            0x10 +#define GPIO_PIN_TYPE_HW_CONTROL        0x20 + +//For GPIO_PIN_TYPE_OUTPUT the following is defined  +#define GPIO_PIN_OUTPUT_STATE_MASK      0x01 +#define GPIO_PIN_OUTPUT_STATE_SHIFT     0 +#define GPIO_PIN_STATE_ACTIVE_LOW       0x0 +#define GPIO_PIN_STATE_ACTIVE_HIGH      0x1 + +typedef struct  _ATOM_ENCODER_DVO_CF_RECORD +{ +  ATOM_COMMON_RECORD_HEADER   sheader; +  ULONG                       ulStrengthControl;      // DVOA strength control for CF +  UCHAR                       ucPadding[2]; +}ATOM_ENCODER_DVO_CF_RECORD; + +// value for ATOM_CONNECTOR_CF_RECORD.ucConnectedDvoBundle +#define ATOM_CONNECTOR_CF_RECORD_CONNECTED_UPPER12BITBUNDLEA   1 +#define ATOM_CONNECTOR_CF_RECORD_CONNECTED_LOWER12BITBUNDLEB   2 + +typedef struct  _ATOM_CONNECTOR_CF_RECORD +{ +  ATOM_COMMON_RECORD_HEADER   sheader; +  USHORT                      usMaxPixClk; +  UCHAR                       ucFlowCntlGpioId; +  UCHAR                       ucSwapCntlGpioId; +  UCHAR                       ucConnectedDvoBundle; +  UCHAR                       ucPadding; +}ATOM_CONNECTOR_CF_RECORD; + +typedef struct  _ATOM_CONNECTOR_HARDCODE_DTD_RECORD +{ +  ATOM_COMMON_RECORD_HEADER   sheader; +	ATOM_DTD_FORMAT							asTiming; +}ATOM_CONNECTOR_HARDCODE_DTD_RECORD; + +typedef struct _ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD +{ +  ATOM_COMMON_RECORD_HEADER   sheader;                //ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD_TYPE +  UCHAR                       ucSubConnectorType;     //CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D|X_ID_DUAL_LINK_DVI_D|HDMI_TYPE_A +  UCHAR                       ucReserved; +}ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD; + + +typedef struct _ATOM_ROUTER_DDC_PATH_SELECT_RECORD +{ +	ATOM_COMMON_RECORD_HEADER   sheader;                 +	UCHAR												ucMuxType;							//decide the number of ucMuxState, =0, no pin state, =1: single state with complement, >1: multiple state +	UCHAR												ucMuxControlPin; +	UCHAR												ucMuxState[2];					//for alligment purpose +}ATOM_ROUTER_DDC_PATH_SELECT_RECORD; + +typedef struct _ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD +{ +	ATOM_COMMON_RECORD_HEADER   sheader;                 +	UCHAR												ucMuxType; +	UCHAR												ucMuxControlPin; +	UCHAR												ucMuxState[2];					//for alligment purpose +}ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD; + +// define ucMuxType +#define ATOM_ROUTER_MUX_PIN_STATE_MASK								0x0f +#define ATOM_ROUTER_MUX_PIN_SINGLE_STATE_COMPLEMENT		0x01 + +/**************************************************************************/ +//ASIC voltage data table starts here + +typedef struct  _ATOM_VOLTAGE_INFO_HEADER +{ +   USHORT   usVDDCBaseLevel;                //In number of 50mv unit +   USHORT   usReserved;                     //For possible extension table offset +   UCHAR    ucNumOfVoltageEntries; +   UCHAR    ucBytesPerVoltageEntry; +   UCHAR    ucVoltageStep;                  //Indicating in how many mv increament is one step, 0.5mv unit +   UCHAR    ucDefaultVoltageEntry; +   UCHAR    ucVoltageControlI2cLine; +   UCHAR    ucVoltageControlAddress; +   UCHAR    ucVoltageControlOffset; +}ATOM_VOLTAGE_INFO_HEADER; + +typedef struct  _ATOM_VOLTAGE_INFO +{ +   ATOM_COMMON_TABLE_HEADER	sHeader;  +   ATOM_VOLTAGE_INFO_HEADER viHeader; +   UCHAR    ucVoltageEntries[64];            //64 is for allocation, the actual number of entry is present at ucNumOfVoltageEntries*ucBytesPerVoltageEntry +}ATOM_VOLTAGE_INFO; + + +typedef struct  _ATOM_VOLTAGE_FORMULA +{ +   USHORT   usVoltageBaseLevel;             // In number of 1mv unit +   USHORT   usVoltageStep;                  // Indicating in how many mv increament is one step, 1mv unit +	 UCHAR		ucNumOfVoltageEntries;					// Number of Voltage Entry, which indicate max Voltage +	 UCHAR		ucFlag;													// bit0=0 :step is 1mv =1 0.5mv +	 UCHAR		ucBaseVID;											// if there is no lookup table, VID= BaseVID + ( Vol - BaseLevle ) /VoltageStep +	 UCHAR		ucReserved; +	 UCHAR		ucVIDAdjustEntries[32];					// 32 is for allocation, the actual number of entry is present at ucNumOfVoltageEntries +}ATOM_VOLTAGE_FORMULA; + +typedef struct _ATOM_VOLTAGE_CONTROL +{ +	UCHAR		 ucVoltageControlId;							//Indicate it is controlled by I2C or GPIO or HW state machine		 +  UCHAR    ucVoltageControlI2cLine; +  UCHAR    ucVoltageControlAddress; +  UCHAR    ucVoltageControlOffset;	 	 +  USHORT   usGpioPin_AIndex;								//GPIO_PAD register index +  UCHAR    ucGpioPinBitShift[9];						//at most 8 pin support 255 VIDs, termintate with 0xff +	UCHAR		 ucReserved; +}ATOM_VOLTAGE_CONTROL; + +// Define ucVoltageControlId +#define	VOLTAGE_CONTROLLED_BY_HW							0x00 +#define	VOLTAGE_CONTROLLED_BY_I2C_MASK				0x7F +#define	VOLTAGE_CONTROLLED_BY_GPIO						0x80 +#define	VOLTAGE_CONTROL_ID_LM64								0x01									//I2C control, used for R5xx Core Voltage +#define	VOLTAGE_CONTROL_ID_DAC								0x02									//I2C control, used for R5xx/R6xx MVDDC,MVDDQ or VDDCI +#define	VOLTAGE_CONTROL_ID_VT116xM						0x03									//I2C control, used for R6xx Core Voltage +#define VOLTAGE_CONTROL_ID_DS4402							0x04									 + +typedef struct  _ATOM_VOLTAGE_OBJECT +{ + 	 UCHAR		ucVoltageType;									//Indicate Voltage Source: VDDC, MVDDC, MVDDQ or MVDDCI	  +	 UCHAR		ucSize;													//Size of Object	 +	 ATOM_VOLTAGE_CONTROL			asControl;			//describ how to control 	  + 	 ATOM_VOLTAGE_FORMULA			asFormula;			//Indicate How to convert real Voltage to VID  +}ATOM_VOLTAGE_OBJECT; + +typedef struct  _ATOM_VOLTAGE_OBJECT_INFO +{ +   ATOM_COMMON_TABLE_HEADER	sHeader;  +	 ATOM_VOLTAGE_OBJECT			asVoltageObj[3];	//Info for Voltage control	  	  +}ATOM_VOLTAGE_OBJECT_INFO; + +typedef struct  _ATOM_LEAKID_VOLTAGE +{ +	UCHAR		ucLeakageId; +	UCHAR		ucReserved; +	USHORT	usVoltage; +}ATOM_LEAKID_VOLTAGE; + +typedef struct  _ATOM_ASIC_PROFILE_VOLTAGE +{ +	UCHAR		ucProfileId; +	UCHAR		ucReserved; +	USHORT	usSize; +	USHORT	usEfuseSpareStartAddr; +	USHORT	usFuseIndex[8];												//from LSB to MSB, Max 8bit,end of 0xffff if less than 8 efuse id,  +	ATOM_LEAKID_VOLTAGE					asLeakVol[2];			//Leakid and relatd voltage +}ATOM_ASIC_PROFILE_VOLTAGE; + +//ucProfileId +#define	ATOM_ASIC_PROFILE_ID_EFUSE_VOLTAGE			1		 +#define	ATOM_ASIC_PROFILE_ID_EFUSE_PERFORMANCE_VOLTAGE			1 +#define	ATOM_ASIC_PROFILE_ID_EFUSE_THERMAL_VOLTAGE					2 + +typedef struct  _ATOM_ASIC_PROFILING_INFO +{ +  ATOM_COMMON_TABLE_HEADER			asHeader;  +	ATOM_ASIC_PROFILE_VOLTAGE			asVoltage; +}ATOM_ASIC_PROFILING_INFO; + +typedef struct _ATOM_POWER_SOURCE_OBJECT +{ +	UCHAR	ucPwrSrcId;													// Power source +	UCHAR	ucPwrSensorType;										// GPIO, I2C or none +	UCHAR	ucPwrSensId;											  // if GPIO detect, it is GPIO id,  if I2C detect, it is I2C id +	UCHAR	ucPwrSensSlaveAddr;									// Slave address if I2C detect +	UCHAR ucPwrSensRegIndex;									// I2C register Index if I2C detect +	UCHAR ucPwrSensRegBitMask;								// detect which bit is used if I2C detect +	UCHAR	ucPwrSensActiveState;								// high active or low active +	UCHAR	ucReserve[3];												// reserve		 +	USHORT usSensPwr;													// in unit of watt +}ATOM_POWER_SOURCE_OBJECT; + +typedef struct _ATOM_POWER_SOURCE_INFO +{ +		ATOM_COMMON_TABLE_HEADER		asHeader; +		UCHAR												asPwrbehave[16]; +		ATOM_POWER_SOURCE_OBJECT		asPwrObj[1]; +}ATOM_POWER_SOURCE_INFO; + + +//Define ucPwrSrcId +#define POWERSOURCE_PCIE_ID1						0x00 +#define POWERSOURCE_6PIN_CONNECTOR_ID1	0x01 +#define POWERSOURCE_8PIN_CONNECTOR_ID1	0x02 +#define POWERSOURCE_6PIN_CONNECTOR_ID2	0x04 +#define POWERSOURCE_8PIN_CONNECTOR_ID2	0x08 + +//define ucPwrSensorId +#define POWER_SENSOR_ALWAYS							0x00 +#define POWER_SENSOR_GPIO								0x01 +#define POWER_SENSOR_I2C								0x02 + +/**************************************************************************/ +// This portion is only used when ext thermal chip or engine/memory clock SS chip is populated on a design +//Memory SS Info Table +//Define Memory Clock SS chip ID +#define ICS91719  1 +#define ICS91720  2 + +//Define one structure to inform SW a "block of data" writing to external SS chip via I2C protocol +typedef struct _ATOM_I2C_DATA_RECORD +{ +  UCHAR         ucNunberOfBytes;                                              //Indicates how many bytes SW needs to write to the external ASIC for one block, besides to "Start" and "Stop" +  UCHAR         ucI2CData[1];                                                 //I2C data in bytes, should be less than 16 bytes usually +}ATOM_I2C_DATA_RECORD; + + +//Define one structure to inform SW how many blocks of data writing to external SS chip via I2C protocol, in addition to other information +typedef struct _ATOM_I2C_DEVICE_SETUP_INFO +{ +  ATOM_I2C_ID_CONFIG_ACCESS       sucI2cId;               //I2C line and HW/SW assisted cap. +  UCHAR		                        ucSSChipID;             //SS chip being used +  UCHAR		                        ucSSChipSlaveAddr;      //Slave Address to set up this SS chip +  UCHAR                           ucNumOfI2CDataRecords;  //number of data block +  ATOM_I2C_DATA_RECORD            asI2CData[1];   +}ATOM_I2C_DEVICE_SETUP_INFO; + +//========================================================================================== +typedef struct  _ATOM_ASIC_MVDD_INFO +{ +  ATOM_COMMON_TABLE_HEADER	      sHeader;  +  ATOM_I2C_DEVICE_SETUP_INFO      asI2CSetup[1]; +}ATOM_ASIC_MVDD_INFO; + +//========================================================================================== +#define ATOM_MCLK_SS_INFO         ATOM_ASIC_MVDD_INFO + +//========================================================================================== +/**************************************************************************/ + +typedef struct _ATOM_ASIC_SS_ASSIGNMENT +{ +	ULONG								ulTargetClockRange;						//Clock Out frequence (VCO ), in unit of 10Khz +  USHORT              usSpreadSpectrumPercentage;		//in unit of 0.01% +	USHORT							usSpreadRateInKhz;						//in unit of kHz, modulation freq +  UCHAR               ucClockIndication;					  //Indicate which clock source needs SS +	UCHAR								ucSpreadSpectrumMode;					//Bit1=0 Down Spread,=1 Center Spread. +	UCHAR								ucReserved[2]; +}ATOM_ASIC_SS_ASSIGNMENT; + +//Define ucSpreadSpectrumType +#define ASIC_INTERNAL_MEMORY_SS			1 +#define ASIC_INTERNAL_ENGINE_SS			2 +#define ASIC_INTERNAL_UVD_SS				3 + +typedef struct _ATOM_ASIC_INTERNAL_SS_INFO{ +  ATOM_COMMON_TABLE_HEADER	      sHeader;  +  ATOM_ASIC_SS_ASSIGNMENT		      asSpreadSpectrum[4]; +}ATOM_ASIC_INTERNAL_SS_INFO; + +//==============================Scratch Pad Definition Portion=============================== +#define ATOM_DEVICE_CONNECT_INFO_DEF  0 +#define ATOM_ROM_LOCATION_DEF         1 +#define ATOM_TV_STANDARD_DEF          2 +#define ATOM_ACTIVE_INFO_DEF          3 +#define ATOM_LCD_INFO_DEF             4 +#define ATOM_DOS_REQ_INFO_DEF         5 +#define ATOM_ACC_CHANGE_INFO_DEF      6 +#define ATOM_DOS_MODE_INFO_DEF        7 +#define ATOM_I2C_CHANNEL_STATUS_DEF   8 +#define ATOM_I2C_CHANNEL_STATUS1_DEF  9 + + +// BIOS_0_SCRATCH Definition  +#define ATOM_S0_CRT1_MONO               0x00000001L +#define ATOM_S0_CRT1_COLOR              0x00000002L +#define ATOM_S0_CRT1_MASK               (ATOM_S0_CRT1_MONO+ATOM_S0_CRT1_COLOR) + +#define ATOM_S0_TV1_COMPOSITE_A         0x00000004L +#define ATOM_S0_TV1_SVIDEO_A            0x00000008L +#define ATOM_S0_TV1_MASK_A              (ATOM_S0_TV1_COMPOSITE_A+ATOM_S0_TV1_SVIDEO_A) + +#define ATOM_S0_CV_A                    0x00000010L +#define ATOM_S0_CV_DIN_A                0x00000020L +#define ATOM_S0_CV_MASK_A               (ATOM_S0_CV_A+ATOM_S0_CV_DIN_A) + + +#define ATOM_S0_CRT2_MONO               0x00000100L +#define ATOM_S0_CRT2_COLOR              0x00000200L +#define ATOM_S0_CRT2_MASK               (ATOM_S0_CRT2_MONO+ATOM_S0_CRT2_COLOR) + +#define ATOM_S0_TV1_COMPOSITE           0x00000400L +#define ATOM_S0_TV1_SVIDEO              0x00000800L +#define ATOM_S0_TV1_SCART               0x00004000L +#define ATOM_S0_TV1_MASK                (ATOM_S0_TV1_COMPOSITE+ATOM_S0_TV1_SVIDEO+ATOM_S0_TV1_SCART) + +#define ATOM_S0_CV                      0x00001000L +#define ATOM_S0_CV_DIN                  0x00002000L +#define ATOM_S0_CV_MASK                 (ATOM_S0_CV+ATOM_S0_CV_DIN) + + +#define ATOM_S0_DFP1                    0x00010000L +#define ATOM_S0_DFP2                    0x00020000L +#define ATOM_S0_LCD1                    0x00040000L +#define ATOM_S0_LCD2                    0x00080000L +#define ATOM_S0_TV2                     0x00100000L +#define ATOM_S0_DFP3										0x00200000L + +#define ATOM_S0_FAD_REGISTER_BUG        0x02000000L // If set, indicates we are running a PCIE asic with  +                                                    // the FAD/HDP reg access bug.  Bit is read by DAL + +#define ATOM_S0_THERMAL_STATE_MASK      0x1C000000L +#define ATOM_S0_THERMAL_STATE_SHIFT     26 + +#define ATOM_S0_SYSTEM_POWER_STATE_MASK 0xE0000000L +#define ATOM_S0_SYSTEM_POWER_STATE_SHIFT 29  + +#define ATOM_S0_SYSTEM_POWER_STATE_VALUE_AC     1 +#define ATOM_S0_SYSTEM_POWER_STATE_VALUE_DC     2 +#define ATOM_S0_SYSTEM_POWER_STATE_VALUE_LITEAC 3 + +//Byte aligned defintion for BIOS usage +#define ATOM_S0_CRT1_MONOb0             0x01 +#define ATOM_S0_CRT1_COLORb0            0x02 +#define ATOM_S0_CRT1_MASKb0             (ATOM_S0_CRT1_MONOb0+ATOM_S0_CRT1_COLORb0) + +#define ATOM_S0_TV1_COMPOSITEb0         0x04 +#define ATOM_S0_TV1_SVIDEOb0            0x08 +#define ATOM_S0_TV1_MASKb0              (ATOM_S0_TV1_COMPOSITEb0+ATOM_S0_TV1_SVIDEOb0) + +#define ATOM_S0_CVb0                    0x10 +#define ATOM_S0_CV_DINb0                0x20 +#define ATOM_S0_CV_MASKb0               (ATOM_S0_CVb0+ATOM_S0_CV_DINb0) + +#define ATOM_S0_CRT2_MONOb1             0x01 +#define ATOM_S0_CRT2_COLORb1            0x02 +#define ATOM_S0_CRT2_MASKb1             (ATOM_S0_CRT2_MONOb1+ATOM_S0_CRT2_COLORb1) + +#define ATOM_S0_TV1_COMPOSITEb1         0x04 +#define ATOM_S0_TV1_SVIDEOb1            0x08 +#define ATOM_S0_TV1_SCARTb1             0x40 +#define ATOM_S0_TV1_MASKb1              (ATOM_S0_TV1_COMPOSITEb1+ATOM_S0_TV1_SVIDEOb1+ATOM_S0_TV1_SCARTb1) + +#define ATOM_S0_CVb1                    0x10 +#define ATOM_S0_CV_DINb1                0x20 +#define ATOM_S0_CV_MASKb1               (ATOM_S0_CVb1+ATOM_S0_CV_DINb1) + +#define ATOM_S0_DFP1b2                  0x01 +#define ATOM_S0_DFP2b2                  0x02 +#define ATOM_S0_LCD1b2                  0x04 +#define ATOM_S0_LCD2b2                  0x08 +#define ATOM_S0_TV2b2                   0x10 +#define ATOM_S0_DFP3b2									0x20 + +#define ATOM_S0_THERMAL_STATE_MASKb3    0x1C +#define ATOM_S0_THERMAL_STATE_SHIFTb3   2 + +#define ATOM_S0_SYSTEM_POWER_STATE_MASKb3 0xE0 +#define ATOM_S0_LCD1_SHIFT              18 + +// BIOS_1_SCRATCH Definition +#define ATOM_S1_ROM_LOCATION_MASK       0x0000FFFFL +#define ATOM_S1_PCI_BUS_DEV_MASK        0xFFFF0000L + +      +//	BIOS_2_SCRATCH Definition +#define ATOM_S2_TV1_STANDARD_MASK       0x0000000FL +#define ATOM_S2_CURRENT_BL_LEVEL_MASK   0x0000FF00L +#define ATOM_S2_CURRENT_BL_LEVEL_SHIFT  8 + +#define ATOM_S2_CRT1_DPMS_STATE         0x00010000L +#define ATOM_S2_LCD1_DPMS_STATE	        0x00020000L +#define ATOM_S2_TV1_DPMS_STATE          0x00040000L +#define ATOM_S2_DFP1_DPMS_STATE         0x00080000L +#define ATOM_S2_CRT2_DPMS_STATE         0x00100000L +#define ATOM_S2_LCD2_DPMS_STATE         0x00200000L +#define ATOM_S2_TV2_DPMS_STATE          0x00400000L +#define ATOM_S2_DFP2_DPMS_STATE         0x00800000L +#define ATOM_S2_CV_DPMS_STATE           0x01000000L +#define ATOM_S2_DFP3_DPMS_STATE					0x02000000L + +#define ATOM_S2_DEVICE_DPMS_STATE       (ATOM_S2_CRT1_DPMS_STATE+ATOM_S2_LCD1_DPMS_STATE+ATOM_S2_TV1_DPMS_STATE+\ +                                        ATOM_S2_DFP1I_DPMS_STATE+ATOM_S2_CRT2_DPMS_STATE+ATOM_S2_LCD2_DPMS_STATE+\ +                                        ATOM_S2_TV2_DPMS_STATE+ATOM_S2_DFP1X_DPMS_STATE+ATOM_S2_CV_DPMS_STATE+\ +                                        ATOM_S2_DFP3_DPMS_STATE) + + +#define ATOM_S2_FORCEDLOWPWRMODE_STATE_MASK       0x0C000000L +#define ATOM_S2_FORCEDLOWPWRMODE_STATE_MASK_SHIFT 26 +#define ATOM_S2_FORCEDLOWPWRMODE_STATE_CHANGE     0x10000000L + +#define ATOM_S2_VRI_BRIGHT_ENABLE       0x20000000L + +#define ATOM_S2_DISPLAY_ROTATION_0_DEGREE     0x0 +#define ATOM_S2_DISPLAY_ROTATION_90_DEGREE    0x1 +#define ATOM_S2_DISPLAY_ROTATION_180_DEGREE   0x2 +#define ATOM_S2_DISPLAY_ROTATION_270_DEGREE   0x3 +#define ATOM_S2_DISPLAY_ROTATION_DEGREE_SHIFT 30 +#define ATOM_S2_DISPLAY_ROTATION_ANGLE_MASK   0xC0000000L + + +//Byte aligned defintion for BIOS usage +#define ATOM_S2_TV1_STANDARD_MASKb0     0x0F +#define ATOM_S2_CURRENT_BL_LEVEL_MASKb1 0xFF +#define ATOM_S2_CRT1_DPMS_STATEb2       0x01 +#define ATOM_S2_LCD1_DPMS_STATEb2       0x02 +#define ATOM_S2_TV1_DPMS_STATEb2        0x04 +#define ATOM_S2_DFP1_DPMS_STATEb2       0x08 +#define ATOM_S2_CRT2_DPMS_STATEb2       0x10 +#define ATOM_S2_LCD2_DPMS_STATEb2       0x20 +#define ATOM_S2_TV2_DPMS_STATEb2        0x40 +#define ATOM_S2_DFP2_DPMS_STATEb2       0x80 +#define ATOM_S2_CV_DPMS_STATEb3         0x01 +#define ATOM_S2_DFP3_DPMS_STATEb3				0x02 + +#define ATOM_S2_DEVICE_DPMS_MASKw1      0x3FF +#define ATOM_S2_FORCEDLOWPWRMODE_STATE_MASKb3     0x0C +#define ATOM_S2_FORCEDLOWPWRMODE_STATE_CHANGEb3   0x10 +#define ATOM_S2_VRI_BRIGHT_ENABLEb3     0x20 +#define ATOM_S2_ROTATION_STATE_MASKb3   0xC0 + + +// BIOS_3_SCRATCH Definition +#define ATOM_S3_CRT1_ACTIVE             0x00000001L +#define ATOM_S3_LCD1_ACTIVE             0x00000002L +#define ATOM_S3_TV1_ACTIVE              0x00000004L +#define ATOM_S3_DFP1_ACTIVE             0x00000008L +#define ATOM_S3_CRT2_ACTIVE             0x00000010L +#define ATOM_S3_LCD2_ACTIVE             0x00000020L +#define ATOM_S3_TV2_ACTIVE              0x00000040L +#define ATOM_S3_DFP2_ACTIVE             0x00000080L +#define ATOM_S3_CV_ACTIVE               0x00000100L +#define ATOM_S3_DFP3_ACTIVE							0x00000200L + +#define ATOM_S3_DEVICE_ACTIVE_MASK      0x000003FFL + +#define ATOM_S3_LCD_FULLEXPANSION_ACTIVE         0x00001000L +#define ATOM_S3_LCD_EXPANSION_ASPEC_RATIO_ACTIVE 0x00002000L + +#define ATOM_S3_CRT1_CRTC_ACTIVE        0x00010000L +#define ATOM_S3_LCD1_CRTC_ACTIVE        0x00020000L +#define ATOM_S3_TV1_CRTC_ACTIVE         0x00040000L +#define ATOM_S3_DFP1_CRTC_ACTIVE        0x00080000L +#define ATOM_S3_CRT2_CRTC_ACTIVE        0x00100000L +#define ATOM_S3_LCD2_CRTC_ACTIVE        0x00200000L +#define ATOM_S3_TV2_CRTC_ACTIVE         0x00400000L +#define ATOM_S3_DFP2_CRTC_ACTIVE        0x00800000L +#define ATOM_S3_CV_CRTC_ACTIVE          0x01000000L +#define ATOM_S3_DFP3_CRTC_ACTIVE				0x02000000L + +#define ATOM_S3_DEVICE_CRTC_ACTIVE_MASK 0x03FF0000L +#define ATOM_S3_ASIC_GUI_ENGINE_HUNG    0x20000000L +#define ATOM_S3_ALLOW_FAST_PWR_SWITCH   0x40000000L +#define ATOM_S3_RQST_GPU_USE_MIN_PWR    0x80000000L + +//Byte aligned defintion for BIOS usage +#define ATOM_S3_CRT1_ACTIVEb0           0x01 +#define ATOM_S3_LCD1_ACTIVEb0           0x02 +#define ATOM_S3_TV1_ACTIVEb0            0x04 +#define ATOM_S3_DFP1_ACTIVEb0           0x08 +#define ATOM_S3_CRT2_ACTIVEb0           0x10 +#define ATOM_S3_LCD2_ACTIVEb0           0x20 +#define ATOM_S3_TV2_ACTIVEb0            0x40 +#define ATOM_S3_DFP2_ACTIVEb0           0x80 +#define ATOM_S3_CV_ACTIVEb1             0x01 +#define ATOM_S3_DFP3_ACTIVEb1						0x02 + +#define ATOM_S3_ACTIVE_CRTC1w0          0x3FF + +#define ATOM_S3_CRT1_CRTC_ACTIVEb2      0x01 +#define ATOM_S3_LCD1_CRTC_ACTIVEb2      0x02 +#define ATOM_S3_TV1_CRTC_ACTIVEb2       0x04 +#define ATOM_S3_DFP1_CRTC_ACTIVEb2      0x08 +#define ATOM_S3_CRT2_CRTC_ACTIVEb2      0x10 +#define ATOM_S3_LCD2_CRTC_ACTIVEb2      0x20 +#define ATOM_S3_TV2_CRTC_ACTIVEb2       0x40 +#define ATOM_S3_DFP2_CRTC_ACTIVEb2      0x80 +#define ATOM_S3_CV_CRTC_ACTIVEb3        0x01 +#define ATOM_S3_DFP3_CRTC_ACTIVEb3			0x02 + +#define ATOM_S3_ACTIVE_CRTC2w1          0x3FF + +#define ATOM_S3_ASIC_GUI_ENGINE_HUNGb3	0x20 +#define ATOM_S3_ALLOW_FAST_PWR_SWITCHb3 0x40 +#define ATOM_S3_RQST_GPU_USE_MIN_PWRb3  0x80 + +// BIOS_4_SCRATCH Definition +#define ATOM_S4_LCD1_PANEL_ID_MASK      0x000000FFL +#define ATOM_S4_LCD1_REFRESH_MASK       0x0000FF00L +#define ATOM_S4_LCD1_REFRESH_SHIFT      8 + + +//Byte aligned defintion for BIOS usage +#define ATOM_S4_LCD1_PANEL_ID_MASKb0	  0x0FF +#define ATOM_S4_LCD1_REFRESH_MASKb1		  ATOM_S4_LCD1_PANEL_ID_MASKb0 +#define ATOM_S4_VRAM_INFO_MASKb2        ATOM_S4_LCD1_PANEL_ID_MASKb0 + + +// BIOS_5_SCRATCH Definition, BIOS_5_SCRATCH is used by Firmware only !!!! +#define ATOM_S5_DOS_REQ_CRT1b0          0x01 +#define ATOM_S5_DOS_REQ_LCD1b0          0x02 +#define ATOM_S5_DOS_REQ_TV1b0           0x04 +#define ATOM_S5_DOS_REQ_DFP1b0          0x08 +#define ATOM_S5_DOS_REQ_CRT2b0          0x10 +#define ATOM_S5_DOS_REQ_LCD2b0          0x20 +#define ATOM_S5_DOS_REQ_TV2b0           0x40 +#define ATOM_S5_DOS_REQ_DFP2b0          0x80 +#define ATOM_S5_DOS_REQ_CVb1            0x01 +#define ATOM_S5_DOS_REQ_DFP3b1					0x02 + +#define ATOM_S5_DOS_REQ_DEVICEw0        0x03FF + +#define ATOM_S5_DOS_REQ_CRT1            0x0001 +#define ATOM_S5_DOS_REQ_LCD1            0x0002 +#define ATOM_S5_DOS_REQ_TV1             0x0004 +#define ATOM_S5_DOS_REQ_DFP1            0x0008 +#define ATOM_S5_DOS_REQ_CRT2            0x0010 +#define ATOM_S5_DOS_REQ_LCD2            0x0020 +#define ATOM_S5_DOS_REQ_TV2             0x0040 +#define ATOM_S5_DOS_REQ_DFP2            0x0080 +#define ATOM_S5_DOS_REQ_CV              0x0100 +#define ATOM_S5_DOS_REQ_DFP3						0x0200 + +#define ATOM_S5_DOS_FORCE_CRT1b2        ATOM_S5_DOS_REQ_CRT1b0 +#define ATOM_S5_DOS_FORCE_TV1b2         ATOM_S5_DOS_REQ_TV1b0 +#define ATOM_S5_DOS_FORCE_CRT2b2        ATOM_S5_DOS_REQ_CRT2b0 +#define ATOM_S5_DOS_FORCE_CVb3          ATOM_S5_DOS_REQ_CVb1 +#define ATOM_S5_DOS_FORCE_DEVICEw1      (ATOM_S5_DOS_FORCE_CRT1b2+ATOM_S5_DOS_FORCE_TV1b2+ATOM_S5_DOS_FORCE_CRT2b2+\ +                                        (ATOM_S5_DOS_FORCE_CVb3<<8)) + +// BIOS_6_SCRATCH Definition +#define ATOM_S6_DEVICE_CHANGE           0x00000001L +#define ATOM_S6_SCALER_CHANGE           0x00000002L +#define ATOM_S6_LID_CHANGE              0x00000004L +#define ATOM_S6_DOCKING_CHANGE          0x00000008L +#define ATOM_S6_ACC_MODE                0x00000010L +#define ATOM_S6_EXT_DESKTOP_MODE        0x00000020L +#define ATOM_S6_LID_STATE               0x00000040L +#define ATOM_S6_DOCK_STATE              0x00000080L +#define ATOM_S6_CRITICAL_STATE          0x00000100L +#define ATOM_S6_HW_I2C_BUSY_STATE       0x00000200L +#define ATOM_S6_THERMAL_STATE_CHANGE    0x00000400L +#define ATOM_S6_INTERRUPT_SET_BY_BIOS   0x00000800L +#define ATOM_S6_REQ_LCD_EXPANSION_FULL         0x00001000L //Normal expansion Request bit for LCD +#define ATOM_S6_REQ_LCD_EXPANSION_ASPEC_RATIO  0x00002000L //Aspect ratio expansion Request bit for LCD + +#define ATOM_S6_DISPLAY_STATE_CHANGE    0x00004000L        //This bit is recycled when ATOM_BIOS_INFO_BIOS_SCRATCH6_SCL2_REDEFINE is set,previously it's SCL2_H_expansion +#define ATOM_S6_I2C_STATE_CHANGE        0x00008000L        //This bit is recycled,when ATOM_BIOS_INFO_BIOS_SCRATCH6_SCL2_REDEFINE is set,previously it's SCL2_V_expansion + + +#define ATOM_S6_ACC_REQ_CRT1            0x00010000L +#define ATOM_S6_ACC_REQ_LCD1            0x00020000L +#define ATOM_S6_ACC_REQ_TV1             0x00040000L +#define ATOM_S6_ACC_REQ_DFP1            0x00080000L +#define ATOM_S6_ACC_REQ_CRT2            0x00100000L +#define ATOM_S6_ACC_REQ_LCD2            0x00200000L +#define ATOM_S6_ACC_REQ_TV2             0x00400000L +#define ATOM_S6_ACC_REQ_DFP2            0x00800000L +#define ATOM_S6_ACC_REQ_CV              0x01000000L +#define ATOM_S6_ACC_REQ_DFP3						0x02000000L + +#define ATOM_S6_ACC_REQ_MASK                0x03FF0000L +#define ATOM_S6_SYSTEM_POWER_MODE_CHANGE    0x10000000L +#define ATOM_S6_ACC_BLOCK_DISPLAY_SWITCH    0x20000000L +#define ATOM_S6_VRI_BRIGHTNESS_CHANGE       0x40000000L +#define ATOM_S6_CONFIG_DISPLAY_CHANGE_MASK  0x80000000L + +//Byte aligned defintion for BIOS usage +#define ATOM_S6_DEVICE_CHANGEb0         0x01 +#define ATOM_S6_SCALER_CHANGEb0         0x02 +#define ATOM_S6_LID_CHANGEb0            0x04 +#define ATOM_S6_DOCKING_CHANGEb0        0x08 +#define ATOM_S6_ACC_MODEb0              0x10 +#define ATOM_S6_EXT_DESKTOP_MODEb0      0x20 +#define ATOM_S6_LID_STATEb0             0x40 +#define ATOM_S6_DOCK_STATEb0            0x80 +#define ATOM_S6_CRITICAL_STATEb1        0x01 +#define ATOM_S6_HW_I2C_BUSY_STATEb1     0x02   +#define ATOM_S6_THERMAL_STATE_CHANGEb1  0x04 +#define ATOM_S6_INTERRUPT_SET_BY_BIOSb1 0x08 +#define ATOM_S6_REQ_LCD_EXPANSION_FULLb1        0x10     +#define ATOM_S6_REQ_LCD_EXPANSION_ASPEC_RATIOb1 0x20  + +#define ATOM_S6_ACC_REQ_CRT1b2          0x01 +#define ATOM_S6_ACC_REQ_LCD1b2          0x02 +#define ATOM_S6_ACC_REQ_TV1b2           0x04 +#define ATOM_S6_ACC_REQ_DFP1b2          0x08 +#define ATOM_S6_ACC_REQ_CRT2b2          0x10 +#define ATOM_S6_ACC_REQ_LCD2b2          0x20 +#define ATOM_S6_ACC_REQ_TV2b2           0x40 +#define ATOM_S6_ACC_REQ_DFP2b2          0x80 +#define ATOM_S6_ACC_REQ_CVb3            0x01 +#define ATOM_S6_ACC_REQ_DFP3b3					0x02 + +#define ATOM_S6_ACC_REQ_DEVICEw1        ATOM_S5_DOS_REQ_DEVICEw0 +#define ATOM_S6_SYSTEM_POWER_MODE_CHANGEb3 0x10 +#define ATOM_S6_ACC_BLOCK_DISPLAY_SWITCHb3 0x20 +#define ATOM_S6_VRI_BRIGHTNESS_CHANGEb3    0x40 +#define ATOM_S6_CONFIG_DISPLAY_CHANGEb3    0x80 + +#define ATOM_S6_DEVICE_CHANGE_SHIFT             0 +#define ATOM_S6_SCALER_CHANGE_SHIFT             1 +#define ATOM_S6_LID_CHANGE_SHIFT                2 +#define ATOM_S6_DOCKING_CHANGE_SHIFT            3 +#define ATOM_S6_ACC_MODE_SHIFT                  4 +#define ATOM_S6_EXT_DESKTOP_MODE_SHIFT          5 +#define ATOM_S6_LID_STATE_SHIFT                 6 +#define ATOM_S6_DOCK_STATE_SHIFT                7 +#define ATOM_S6_CRITICAL_STATE_SHIFT            8 +#define ATOM_S6_HW_I2C_BUSY_STATE_SHIFT         9 +#define ATOM_S6_THERMAL_STATE_CHANGE_SHIFT      10 +#define ATOM_S6_INTERRUPT_SET_BY_BIOS_SHIFT     11 +#define ATOM_S6_REQ_SCALER_SHIFT                12 +#define ATOM_S6_REQ_SCALER_ARATIO_SHIFT         13 +#define ATOM_S6_DISPLAY_STATE_CHANGE_SHIFT      14 +#define ATOM_S6_I2C_STATE_CHANGE_SHIFT          15 +#define ATOM_S6_SYSTEM_POWER_MODE_CHANGE_SHIFT  28 +#define ATOM_S6_ACC_BLOCK_DISPLAY_SWITCH_SHIFT  29 +#define ATOM_S6_VRI_BRIGHTNESS_CHANGE_SHIFT     30 +#define ATOM_S6_CONFIG_DISPLAY_CHANGE_SHIFT     31 + +// BIOS_7_SCRATCH Definition, BIOS_7_SCRATCH is used by Firmware only !!!! +#define ATOM_S7_DOS_MODE_TYPEb0             0x03 +#define ATOM_S7_DOS_MODE_VGAb0              0x00 +#define ATOM_S7_DOS_MODE_VESAb0             0x01 +#define ATOM_S7_DOS_MODE_EXTb0              0x02 +#define ATOM_S7_DOS_MODE_PIXEL_DEPTHb0      0x0C +#define ATOM_S7_DOS_MODE_PIXEL_FORMATb0     0xF0 +#define ATOM_S7_DOS_8BIT_DAC_ENb1           0x01 +#define ATOM_S7_DOS_MODE_NUMBERw1           0x0FFFF + +#define ATOM_S7_DOS_8BIT_DAC_EN_SHIFT       8 + +// BIOS_8_SCRATCH Definition +#define ATOM_S8_I2C_CHANNEL_BUSY_MASK       0x00000FFFF +#define ATOM_S8_I2C_HW_ENGINE_BUSY_MASK     0x0FFFF0000    + +#define ATOM_S8_I2C_CHANNEL_BUSY_SHIFT      0 +#define ATOM_S8_I2C_ENGINE_BUSY_SHIFT       16 + +// BIOS_9_SCRATCH Definition +#ifndef ATOM_S9_I2C_CHANNEL_COMPLETED_MASK  +#define ATOM_S9_I2C_CHANNEL_COMPLETED_MASK  0x0000FFFF +#endif +#ifndef ATOM_S9_I2C_CHANNEL_ABORTED_MASK   +#define ATOM_S9_I2C_CHANNEL_ABORTED_MASK    0xFFFF0000 +#endif +#ifndef ATOM_S9_I2C_CHANNEL_COMPLETED_SHIFT  +#define ATOM_S9_I2C_CHANNEL_COMPLETED_SHIFT 0 +#endif +#ifndef ATOM_S9_I2C_CHANNEL_ABORTED_SHIFT    +#define ATOM_S9_I2C_CHANNEL_ABORTED_SHIFT   16 +#endif + +  +#define ATOM_FLAG_SET                         0x20 +#define ATOM_FLAG_CLEAR                       0 +#define CLEAR_ATOM_S6_ACC_MODE                ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_ACC_MODE_SHIFT | ATOM_FLAG_CLEAR) +#define SET_ATOM_S6_DEVICE_CHANGE             ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_DEVICE_CHANGE_SHIFT | ATOM_FLAG_SET) +#define SET_ATOM_S6_VRI_BRIGHTNESS_CHANGE     ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_VRI_BRIGHTNESS_CHANGE_SHIFT | ATOM_FLAG_SET) +#define SET_ATOM_S6_SCALER_CHANGE             ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_SCALER_CHANGE_SHIFT | ATOM_FLAG_SET) +#define SET_ATOM_S6_LID_CHANGE                ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_LID_CHANGE_SHIFT | ATOM_FLAG_SET) + +#define SET_ATOM_S6_LID_STATE                 ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_LID_STATE_SHIFT | ATOM_FLAG_SET) +#define CLEAR_ATOM_S6_LID_STATE               ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_LID_STATE_SHIFT | ATOM_FLAG_CLEAR) + +#define SET_ATOM_S6_DOCK_CHANGE			          ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_DOCKING_CHANGE_SHIFT | ATOM_FLAG_SET) +#define SET_ATOM_S6_DOCK_STATE                ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_DOCK_STATE_SHIFT | ATOM_FLAG_SET) +#define CLEAR_ATOM_S6_DOCK_STATE              ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_DOCK_STATE_SHIFT | ATOM_FLAG_CLEAR) + +#define SET_ATOM_S6_THERMAL_STATE_CHANGE      ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_THERMAL_STATE_CHANGE_SHIFT | ATOM_FLAG_SET) +#define SET_ATOM_S6_SYSTEM_POWER_MODE_CHANGE  ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_SYSTEM_POWER_MODE_CHANGE_SHIFT | ATOM_FLAG_SET) +#define SET_ATOM_S6_INTERRUPT_SET_BY_BIOS     ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_INTERRUPT_SET_BY_BIOS_SHIFT | ATOM_FLAG_SET) + +#define SET_ATOM_S6_CRITICAL_STATE            ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_CRITICAL_STATE_SHIFT | ATOM_FLAG_SET) +#define CLEAR_ATOM_S6_CRITICAL_STATE          ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_CRITICAL_STATE_SHIFT | ATOM_FLAG_CLEAR) + +#define SET_ATOM_S6_REQ_SCALER                ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_REQ_SCALER_SHIFT | ATOM_FLAG_SET)   +#define CLEAR_ATOM_S6_REQ_SCALER              ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_REQ_SCALER_SHIFT | ATOM_FLAG_CLEAR ) + +#define SET_ATOM_S6_REQ_SCALER_ARATIO         ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_REQ_SCALER_ARATIO_SHIFT | ATOM_FLAG_SET ) +#define CLEAR_ATOM_S6_REQ_SCALER_ARATIO       ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_REQ_SCALER_ARATIO_SHIFT | ATOM_FLAG_CLEAR ) + +#define SET_ATOM_S6_I2C_STATE_CHANGE          ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_I2C_STATE_CHANGE_SHIFT | ATOM_FLAG_SET ) + +#define SET_ATOM_S6_DISPLAY_STATE_CHANGE      ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_DISPLAY_STATE_CHANGE_SHIFT | ATOM_FLAG_SET ) + +#define SET_ATOM_S6_DEVICE_RECONFIG           ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_CONFIG_DISPLAY_CHANGE_SHIFT | ATOM_FLAG_SET) +#define CLEAR_ATOM_S0_LCD1                    ((ATOM_DEVICE_CONNECT_INFO_DEF << 8 )|  ATOM_S0_LCD1_SHIFT | ATOM_FLAG_CLEAR ) +#define SET_ATOM_S7_DOS_8BIT_DAC_EN           ((ATOM_DOS_MODE_INFO_DEF << 8 )|ATOM_S7_DOS_8BIT_DAC_EN_SHIFT | ATOM_FLAG_SET ) +#define CLEAR_ATOM_S7_DOS_8BIT_DAC_EN         ((ATOM_DOS_MODE_INFO_DEF << 8 )|ATOM_S7_DOS_8BIT_DAC_EN_SHIFT | ATOM_FLAG_CLEAR ) + +/****************************************************************************/	 +//Portion II: Definitinos only used in Driver +/****************************************************************************/ + +// Macros used by driver + +#define	GetIndexIntoMasterTable(MasterOrData, FieldName) (((char*)(&((ATOM_MASTER_LIST_OF_##MasterOrData##_TABLES*)0)->FieldName)-(char*)0)/sizeof(USHORT)) + +#define GET_COMMAND_TABLE_COMMANDSET_REVISION(TABLE_HEADER_OFFSET) ((((ATOM_COMMON_TABLE_HEADER*)TABLE_HEADER_OFFSET)->ucTableFormatRevision)&0x3F) +#define GET_COMMAND_TABLE_PARAMETER_REVISION(TABLE_HEADER_OFFSET)  ((((ATOM_COMMON_TABLE_HEADER*)TABLE_HEADER_OFFSET)->ucTableContentRevision)&0x3F) + +#define GET_DATA_TABLE_MAJOR_REVISION GET_COMMAND_TABLE_COMMANDSET_REVISION +#define GET_DATA_TABLE_MINOR_REVISION GET_COMMAND_TABLE_PARAMETER_REVISION + +/****************************************************************************/	 +//Portion III: Definitinos only used in VBIOS +/****************************************************************************/ +#define ATOM_DAC_SRC					0x80 +#define ATOM_SRC_DAC1					0 +#define ATOM_SRC_DAC2					0x80 + + +#ifdef	UEFI_BUILD +	#define	USHORT	UTEMP +#endif + +typedef struct _MEMORY_PLLINIT_PARAMETERS +{ +  ULONG ulTargetMemoryClock; //In 10Khz unit +  UCHAR   ucAction;					 //not define yet +  UCHAR   ucFbDiv_Hi;				 //Fbdiv Hi byte +  UCHAR   ucFbDiv;					 //FB value +  UCHAR   ucPostDiv;				 //Post div +}MEMORY_PLLINIT_PARAMETERS; + +#define MEMORY_PLLINIT_PS_ALLOCATION  MEMORY_PLLINIT_PARAMETERS + + +#define	GPIO_PIN_WRITE													0x01			 +#define	GPIO_PIN_READ														0x00 + +typedef struct  _GPIO_PIN_CONTROL_PARAMETERS +{ +  UCHAR ucGPIO_ID;           //return value, read from GPIO pins +  UCHAR ucGPIOBitShift;	     //define which bit in uGPIOBitVal need to be update  +	UCHAR ucGPIOBitVal;		     //Set/Reset corresponding bit defined in ucGPIOBitMask +  UCHAR ucAction;				     //=GPIO_PIN_WRITE: Read; =GPIO_PIN_READ: Write +}GPIO_PIN_CONTROL_PARAMETERS; + +typedef struct _ENABLE_SCALER_PARAMETERS +{ +  UCHAR ucScaler;            // ATOM_SCALER1, ATOM_SCALER2 +  UCHAR ucEnable;            // ATOM_SCALER_DISABLE or ATOM_SCALER_CENTER or ATOM_SCALER_EXPANSION +  UCHAR ucTVStandard;        //  +  UCHAR ucPadding[1]; +}ENABLE_SCALER_PARAMETERS;  +#define ENABLE_SCALER_PS_ALLOCATION ENABLE_SCALER_PARAMETERS  + +//ucEnable: +#define SCALER_BYPASS_AUTO_CENTER_NO_REPLICATION    0 +#define SCALER_BYPASS_AUTO_CENTER_AUTO_REPLICATION  1 +#define SCALER_ENABLE_2TAP_ALPHA_MODE               2 +#define SCALER_ENABLE_MULTITAP_MODE                 3 + +typedef struct _ENABLE_HARDWARE_ICON_CURSOR_PARAMETERS +{ +  ULONG  usHWIconHorzVertPosn;        // Hardware Icon Vertical position +  UCHAR  ucHWIconVertOffset;          // Hardware Icon Vertical offset +  UCHAR  ucHWIconHorzOffset;          // Hardware Icon Horizontal offset +  UCHAR  ucSelection;                 // ATOM_CURSOR1 or ATOM_ICON1 or ATOM_CURSOR2 or ATOM_ICON2 +  UCHAR  ucEnable;                    // ATOM_ENABLE or ATOM_DISABLE +}ENABLE_HARDWARE_ICON_CURSOR_PARAMETERS; + +typedef struct _ENABLE_HARDWARE_ICON_CURSOR_PS_ALLOCATION +{ +  ENABLE_HARDWARE_ICON_CURSOR_PARAMETERS  sEnableIcon; +  ENABLE_CRTC_PARAMETERS                  sReserved;   +}ENABLE_HARDWARE_ICON_CURSOR_PS_ALLOCATION; + +typedef struct _ENABLE_GRAPH_SURFACE_PARAMETERS +{ +  USHORT usHight;                     // Image Hight +  USHORT usWidth;                     // Image Width +  UCHAR  ucSurface;                   // Surface 1 or 2	 +  UCHAR  ucPadding[3]; +}ENABLE_GRAPH_SURFACE_PARAMETERS; + +typedef struct _ENABLE_GRAPH_SURFACE_PARAMETERS_V1_2 +{ +  USHORT usHight;                     // Image Hight +  USHORT usWidth;                     // Image Width +  UCHAR  ucSurface;                   // Surface 1 or 2 +  UCHAR  ucEnable;                    // ATOM_ENABLE or ATOM_DISABLE +  UCHAR  ucPadding[2]; +}ENABLE_GRAPH_SURFACE_PARAMETERS_V1_2; + +typedef struct _ENABLE_GRAPH_SURFACE_PS_ALLOCATION +{ +  ENABLE_GRAPH_SURFACE_PARAMETERS sSetSurface;           +  ENABLE_YUV_PS_ALLOCATION        sReserved; // Don't set this one +}ENABLE_GRAPH_SURFACE_PS_ALLOCATION; + +typedef struct _MEMORY_CLEAN_UP_PARAMETERS +{ +  USHORT  usMemoryStart;                //in 8Kb boundry, offset from memory base address +  USHORT  usMemorySize;                 //8Kb blocks aligned +}MEMORY_CLEAN_UP_PARAMETERS; +#define MEMORY_CLEAN_UP_PS_ALLOCATION MEMORY_CLEAN_UP_PARAMETERS + +typedef struct  _GET_DISPLAY_SURFACE_SIZE_PARAMETERS +{ +  USHORT  usX_Size;                     //When use as input parameter, usX_Size indicates which CRTC                  +  USHORT  usY_Size; +}GET_DISPLAY_SURFACE_SIZE_PARAMETERS;  + +typedef struct _INDIRECT_IO_ACCESS +{ +  ATOM_COMMON_TABLE_HEADER sHeader;   +  UCHAR                    IOAccessSequence[256]; +} INDIRECT_IO_ACCESS; + +#define INDIRECT_READ              0x00 +#define INDIRECT_WRITE             0x80 + +#define INDIRECT_IO_MM             0 +#define INDIRECT_IO_PLL            1 +#define INDIRECT_IO_MC             2 +#define INDIRECT_IO_PCIE           3 +#define INDIRECT_IO_PCIEP          4 +#define INDIRECT_IO_NBMISC         5 + +#define INDIRECT_IO_PLL_READ       INDIRECT_IO_PLL   | INDIRECT_READ +#define INDIRECT_IO_PLL_WRITE      INDIRECT_IO_PLL   | INDIRECT_WRITE +#define INDIRECT_IO_MC_READ        INDIRECT_IO_MC    | INDIRECT_READ +#define INDIRECT_IO_MC_WRITE       INDIRECT_IO_MC    | INDIRECT_WRITE +#define INDIRECT_IO_PCIE_READ      INDIRECT_IO_PCIE  | INDIRECT_READ +#define INDIRECT_IO_PCIE_WRITE     INDIRECT_IO_PCIE  | INDIRECT_WRITE +#define INDIRECT_IO_PCIEP_READ     INDIRECT_IO_PCIEP | INDIRECT_READ +#define INDIRECT_IO_PCIEP_WRITE    INDIRECT_IO_PCIEP | INDIRECT_WRITE +#define INDIRECT_IO_NBMISC_READ    INDIRECT_IO_NBMISC | INDIRECT_READ +#define INDIRECT_IO_NBMISC_WRITE   INDIRECT_IO_NBMISC | INDIRECT_WRITE + +typedef struct _ATOM_OEM_INFO +{  +  ATOM_COMMON_TABLE_HEADER	sHeader; +  ATOM_I2C_ID_CONFIG_ACCESS sucI2cId; +}ATOM_OEM_INFO; + +typedef struct _ATOM_TV_MODE +{ +   UCHAR	ucVMode_Num;			  //Video mode number +   UCHAR	ucTV_Mode_Num;			//Internal TV mode number +}ATOM_TV_MODE; + +typedef struct _ATOM_BIOS_INT_TVSTD_MODE +{ +  ATOM_COMMON_TABLE_HEADER sHeader;   +   USHORT	usTV_Mode_LUT_Offset;	// Pointer to standard to internal number conversion table +   USHORT	usTV_FIFO_Offset;		  // Pointer to FIFO entry table +   USHORT	usNTSC_Tbl_Offset;		// Pointer to SDTV_Mode_NTSC table +   USHORT	usPAL_Tbl_Offset;		  // Pointer to SDTV_Mode_PAL table  +   USHORT	usCV_Tbl_Offset;		  // Pointer to SDTV_Mode_PAL table  +}ATOM_BIOS_INT_TVSTD_MODE; + + +typedef struct _ATOM_TV_MODE_SCALER_PTR +{ +   USHORT	ucFilter0_Offset;		//Pointer to filter format 0 coefficients +   USHORT	usFilter1_Offset;		//Pointer to filter format 0 coefficients +   UCHAR	ucTV_Mode_Num; +}ATOM_TV_MODE_SCALER_PTR; + +typedef struct _ATOM_STANDARD_VESA_TIMING +{ +  ATOM_COMMON_TABLE_HEADER sHeader;   +  ATOM_MODE_TIMING				 aModeTimings[16];      // 16 is not the real array number, just for initial allocation +}ATOM_STANDARD_VESA_TIMING; + + +typedef struct _ATOM_STD_FORMAT +{  +  USHORT    usSTD_HDisp; +  USHORT    usSTD_VDisp; +  USHORT    usSTD_RefreshRate; +  USHORT    usReserved; +}ATOM_STD_FORMAT; + +typedef struct _ATOM_VESA_TO_EXTENDED_MODE +{ +  USHORT  usVESA_ModeNumber; +  USHORT  usExtendedModeNumber; +}ATOM_VESA_TO_EXTENDED_MODE; + +typedef struct _ATOM_VESA_TO_INTENAL_MODE_LUT +{  +  ATOM_COMMON_TABLE_HEADER   sHeader;   +  ATOM_VESA_TO_EXTENDED_MODE asVESA_ToExtendedModeInfo[76]; +}ATOM_VESA_TO_INTENAL_MODE_LUT; + +/*************** ATOM Memory Related Data Structure ***********************/ +typedef struct _ATOM_MEMORY_VENDOR_BLOCK{ +	UCHAR												ucMemoryType; +	UCHAR												ucMemoryVendor; +	UCHAR												ucAdjMCId; +	UCHAR												ucDynClkId; +	ULONG												ulDllResetClkRange; +}ATOM_MEMORY_VENDOR_BLOCK; + + +typedef struct _ATOM_MEMORY_SETTING_ID_CONFIG{ +#if ATOM_BIG_ENDIAN +	ULONG												ucMemBlkId:8; +	ULONG												ulMemClockRange:24; +#else +	ULONG												ulMemClockRange:24; +	ULONG												ucMemBlkId:8; +#endif +}ATOM_MEMORY_SETTING_ID_CONFIG; + +typedef union _ATOM_MEMORY_SETTING_ID_CONFIG_ACCESS +{ +  ATOM_MEMORY_SETTING_ID_CONFIG slAccess; +  ULONG                         ulAccess; +}ATOM_MEMORY_SETTING_ID_CONFIG_ACCESS; + + +typedef struct _ATOM_MEMORY_SETTING_DATA_BLOCK{ +	ATOM_MEMORY_SETTING_ID_CONFIG_ACCESS			ulMemoryID; +	ULONG															        aulMemData[1]; +}ATOM_MEMORY_SETTING_DATA_BLOCK; + + +typedef struct _ATOM_INIT_REG_INDEX_FORMAT{ +	 USHORT											usRegIndex;                                     // MC register index +	 UCHAR											ucPreRegDataLength;                             // offset in ATOM_INIT_REG_DATA_BLOCK.saRegDataBuf +}ATOM_INIT_REG_INDEX_FORMAT; + + +typedef struct _ATOM_INIT_REG_BLOCK{ +	USHORT													usRegIndexTblSize;													//size of asRegIndexBuf +	USHORT													usRegDataBlkSize;														//size of ATOM_MEMORY_SETTING_DATA_BLOCK +	ATOM_INIT_REG_INDEX_FORMAT			asRegIndexBuf[1]; +	ATOM_MEMORY_SETTING_DATA_BLOCK	asRegDataBuf[1]; +}ATOM_INIT_REG_BLOCK; + +#define END_OF_REG_INDEX_BLOCK  0x0ffff +#define END_OF_REG_DATA_BLOCK   0x00000000 +#define ATOM_INIT_REG_MASK_FLAG 0x80 +#define	CLOCK_RANGE_HIGHEST			0x00ffffff + +#define VALUE_DWORD             SIZEOF ULONG +#define VALUE_SAME_AS_ABOVE     0 +#define VALUE_MASK_DWORD        0x84 + +typedef struct _ATOM_MC_INIT_PARAM_TABLE +{  +  ATOM_COMMON_TABLE_HEADER		sHeader; +  USHORT											usAdjustARB_SEQDataOffset; +  USHORT											usMCInitMemTypeTblOffset; +  USHORT											usMCInitCommonTblOffset; +  USHORT											usMCInitPowerDownTblOffset; +	ULONG												ulARB_SEQDataBuf[32]; +	ATOM_INIT_REG_BLOCK					asMCInitMemType; +	ATOM_INIT_REG_BLOCK					asMCInitCommon; +}ATOM_MC_INIT_PARAM_TABLE; + + +#define _4Mx16              0x2 +#define _4Mx32              0x3 +#define _8Mx16              0x12 +#define _8Mx32              0x13 +#define _16Mx16             0x22 +#define _16Mx32             0x23 +#define _32Mx16             0x32 +#define _32Mx32             0x33 +#define _64Mx8              0x41 +#define _64Mx16             0x42 + +#define SAMSUNG             0x1 +#define INFINEON            0x2 +#define ELPIDA              0x3 +#define ETRON               0x4 +#define NANYA               0x5 +#define HYNIX               0x6 +#define MOSEL               0x7 +#define WINBOND             0x8 +#define ESMT                0x9 +#define MICRON              0xF + +#define QIMONDA             INFINEON +#define PROMOS              MOSEL + +#define ATOM_MAX_NUMBER_OF_VRAM_MODULE	16 + +#define ATOM_VRAM_MODULE_MEMORY_VENDOR_ID_MASK	0xF +typedef struct _ATOM_VRAM_MODULE_V1 +{ +  ULONG                      ulReserved; +  USHORT                     usEMRSValue;   +  USHORT                     usMRSValue; +  USHORT                     usReserved; +  UCHAR                      ucExtMemoryID;     // An external indicator (by hardcode, callback or pin) to tell what is the current memory module +  UCHAR                      ucMemoryType;      // [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4;[3:0] reserved; +  UCHAR                      ucMemoryVenderID;  // Predefined,never change across designs or memory type/vender  +  UCHAR                      ucMemoryDeviceCfg; // [7:4]=0x0:4M;=0x1:8M;=0x2:16M;0x3:32M....[3:0]=0x0:x4;=0x1:x8;=0x2:x16;=0x3:x32... +  UCHAR                      ucRow;             // Number of Row,in power of 2; +  UCHAR                      ucColumn;          // Number of Column,in power of 2; +  UCHAR                      ucBank;            // Nunber of Bank; +  UCHAR                      ucRank;            // Number of Rank, in power of 2 +  UCHAR                      ucChannelNum;      // Number of channel; +  UCHAR                      ucChannelConfig;   // [3:0]=Indication of what channel combination;[4:7]=Channel bit width, in number of 2 +  UCHAR                      ucDefaultMVDDQ_ID; // Default MVDDQ setting for this memory block, ID linking to MVDDQ info table to find real set-up data; +  UCHAR                      ucDefaultMVDDC_ID; // Default MVDDC setting for this memory block, ID linking to MVDDC info table to find real set-up data; +  UCHAR                      ucReserved[2]; +}ATOM_VRAM_MODULE_V1; + + +typedef struct _ATOM_VRAM_MODULE_V2 +{ +  ULONG                      ulReserved; +  ULONG                      ulFlags;     			// To enable/disable functionalities based on memory type +  ULONG                      ulEngineClock;     // Override of default engine clock for particular memory type +  ULONG                      ulMemoryClock;     // Override of default memory clock for particular memory type +  USHORT                     usEMRS2Value;      // EMRS2 Value is used for GDDR2 and GDDR4 memory type +  USHORT                     usEMRS3Value;      // EMRS3 Value is used for GDDR2 and GDDR4 memory type +  USHORT                     usEMRSValue;   +  USHORT                     usMRSValue; +  USHORT                     usReserved; +  UCHAR                      ucExtMemoryID;     // An external indicator (by hardcode, callback or pin) to tell what is the current memory module +  UCHAR                      ucMemoryType;      // [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4;[3:0] - must not be used for now; +  UCHAR                      ucMemoryVenderID;  // Predefined,never change across designs or memory type/vender. If not predefined, vendor detection table gets executed +  UCHAR                      ucMemoryDeviceCfg; // [7:4]=0x0:4M;=0x1:8M;=0x2:16M;0x3:32M....[3:0]=0x0:x4;=0x1:x8;=0x2:x16;=0x3:x32... +  UCHAR                      ucRow;             // Number of Row,in power of 2; +  UCHAR                      ucColumn;          // Number of Column,in power of 2; +  UCHAR                      ucBank;            // Nunber of Bank; +  UCHAR                      ucRank;            // Number of Rank, in power of 2 +  UCHAR                      ucChannelNum;      // Number of channel; +  UCHAR                      ucChannelConfig;   // [3:0]=Indication of what channel combination;[4:7]=Channel bit width, in number of 2 +  UCHAR                      ucDefaultMVDDQ_ID; // Default MVDDQ setting for this memory block, ID linking to MVDDQ info table to find real set-up data; +  UCHAR                      ucDefaultMVDDC_ID; // Default MVDDC setting for this memory block, ID linking to MVDDC info table to find real set-up data; +  UCHAR                      ucRefreshRateFactor; +  UCHAR                      ucReserved[3]; +}ATOM_VRAM_MODULE_V2; + + +typedef	struct _ATOM_MEMORY_TIMING_FORMAT +{ +	ULONG											 ulClkRange;				// memory clock in 10kHz unit, when target memory clock is below this clock, use this memory timing 	 +	USHORT										 usMRS;							// mode register						 +	USHORT										 usEMRS;						// extended mode register +	UCHAR											 ucCL;							// CAS latency +	UCHAR											 ucWL;							// WRITE Latency				 +	UCHAR											 uctRAS;						// tRAS +	UCHAR											 uctRC;							// tRC	 +	UCHAR											 uctRFC;						// tRFC +	UCHAR											 uctRCDR;						// tRCDR	 +	UCHAR											 uctRCDW;						// tRCDW +	UCHAR											 uctRP;							// tRP +	UCHAR											 uctRRD;						// tRRD	 +	UCHAR											 uctWR;							// tWR +	UCHAR											 uctWTR;						// tWTR +	UCHAR											 uctPDIX;						// tPDIX +	UCHAR											 uctFAW;						// tFAW +	UCHAR											 uctAOND;						// tAOND +	UCHAR											 ucflag;						// flag to control memory timing calculation. bit0= control EMRS2 Infineon  +	UCHAR											 ucReserved;				//  +}ATOM_MEMORY_TIMING_FORMAT; + +#define	MEM_TIMING_FLAG_APP_MODE								0x01									// =0 mid clock range  =1 high clock range + +typedef	struct _ATOM_MEMORY_FORMAT +{ +	ULONG											 ulDllDisClock;			// memory DLL will be disable when target memory clock is below this clock +  USHORT                     usEMRS2Value;      // EMRS2 Value is used for GDDR2 and GDDR4 memory type +  USHORT                     usEMRS3Value;      // EMRS3 Value is used for GDDR2 and GDDR4 memory type +  UCHAR                      ucMemoryType;      // [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4;[3:0] - must not be used for now; +  UCHAR                      ucMemoryVenderID;  // Predefined,never change across designs or memory type/vender. If not predefined, vendor detection table gets executed +  UCHAR                      ucRow;             // Number of Row,in power of 2; +  UCHAR                      ucColumn;          // Number of Column,in power of 2; +  UCHAR                      ucBank;            // Nunber of Bank; +  UCHAR                      ucRank;            // Number of Rank, in power of 2 +	UCHAR											 ucBurstSize;				// burst size, 0= burst size=4  1= burst size=8 +  UCHAR                      ucDllDisBit;				// position of DLL Enable/Disable bit in EMRS ( Extended Mode Register ) +  UCHAR                      ucRefreshRateFactor;	// memory refresh rate in unit of ms	 +	UCHAR											 ucDensity;					// _8Mx32, _16Mx32, _16Mx16, _32Mx16 +	UCHAR											 ucPreamble;				//[7:4] Write Preamble, [3:0] Read Preamble +  UCHAR											 ucMemAttrib;				// Memory Device Addribute, like RDBI/WDBI etc +	ATOM_MEMORY_TIMING_FORMAT	 asMemTiming[5];		//Memory Timing block sort from lower clock to higher clock +}ATOM_MEMORY_FORMAT; + + +typedef struct _ATOM_VRAM_MODULE_V3 +{ +	ULONG											 ulChannelMapCfg;		// board dependent paramenter:Channel combination +	USHORT										 usSize;						// size of ATOM_VRAM_MODULE_V3 +  USHORT                     usDefaultMVDDQ;		// board dependent parameter:Default Memory Core Voltage +  USHORT                     usDefaultMVDDC;		// board dependent parameter:Default Memory IO Voltage +	UCHAR                      ucExtMemoryID;     // An external indicator (by hardcode, callback or pin) to tell what is the current memory module +  UCHAR                      ucChannelNum;      // board dependent parameter:Number of channel; +	UCHAR											 ucChannelSize;			// board dependent parameter:32bit or 64bit	 +	UCHAR											 ucVREFI;						// board dependnt parameter: EXT or INT +160mv to -140mv +	UCHAR											 ucNPL_RT;					// board dependent parameter:NPL round trip delay, used for calculate memory timing parameters +	UCHAR											 ucFlag;						// To enable/disable functionalities based on memory type +	ATOM_MEMORY_FORMAT				 asMemory;					// describ all of video memory parameters from memory spec +}ATOM_VRAM_MODULE_V3; + + +//ATOM_VRAM_MODULE_V3.ucNPL_RT +#define NPL_RT_MASK															0x0f +#define BATTERY_ODT_MASK												0xc0 + +#define ATOM_VRAM_MODULE		 ATOM_VRAM_MODULE_V3 + +typedef struct _ATOM_VRAM_INFO_V2 +{ +  ATOM_COMMON_TABLE_HEADER   sHeader; +  UCHAR                      ucNumOfVRAMModule; +  ATOM_VRAM_MODULE           aVramInfo[ATOM_MAX_NUMBER_OF_VRAM_MODULE];      // just for allocation, real number of blocks is in ucNumOfVRAMModule; +}ATOM_VRAM_INFO_V2; + +typedef struct _ATOM_VRAM_INFO_V3 +{ +  ATOM_COMMON_TABLE_HEADER   sHeader; +	USHORT										 usMemAdjustTblOffset;													 // offset of ATOM_INIT_REG_BLOCK structure for memory vendor specific MC adjust setting +	USHORT										 usMemClkPatchTblOffset;												 //	offset of ATOM_INIT_REG_BLOCK structure for memory clock specific MC setting +	USHORT										 usRerseved; +	UCHAR           	         aVID_PinsShift[9];															 // 8 bit strap maximum+terminator +  UCHAR                      ucNumOfVRAMModule; +  ATOM_VRAM_MODULE		       aVramInfo[ATOM_MAX_NUMBER_OF_VRAM_MODULE];      // just for allocation, real number of blocks is in ucNumOfVRAMModule; +	ATOM_INIT_REG_BLOCK				 asMemPatch;																		 // for allocation +																																						 //	ATOM_INIT_REG_BLOCK				 aMemAdjust; +}ATOM_VRAM_INFO_V3; + +#define	ATOM_VRAM_INFO_LAST	     ATOM_VRAM_INFO_V3 + +typedef struct _ATOM_VRAM_GPIO_DETECTION_INFO +{ +  ATOM_COMMON_TABLE_HEADER   sHeader; +  UCHAR           	         aVID_PinsShift[9];   //8 bit strap maximum+terminator +}ATOM_VRAM_GPIO_DETECTION_INFO; + + +typedef struct _ATOM_MEMORY_TRAINING_INFO +{ +	ATOM_COMMON_TABLE_HEADER   sHeader; +	UCHAR											 ucTrainingLoop; +	UCHAR											 ucReserved[3]; +	ATOM_INIT_REG_BLOCK				 asMemTrainingSetting; +}ATOM_MEMORY_TRAINING_INFO; + + +typedef struct SW_I2C_CNTL_DATA_PARAMETERS +{ +  UCHAR    ucControl; +  UCHAR    ucData;  +  UCHAR    ucSatus;  +  UCHAR    ucTemp;  +} SW_I2C_CNTL_DATA_PARAMETERS; + +#define SW_I2C_CNTL_DATA_PS_ALLOCATION  SW_I2C_CNTL_DATA_PARAMETERS + +typedef struct _SW_I2C_IO_DATA_PARAMETERS +{                                +  USHORT   GPIO_Info; +  UCHAR    ucAct;  +  UCHAR    ucData;  + } SW_I2C_IO_DATA_PARAMETERS; + +#define SW_I2C_IO_DATA_PS_ALLOCATION  SW_I2C_IO_DATA_PARAMETERS + +/****************************SW I2C CNTL DEFINITIONS**********************/ +#define SW_I2C_IO_RESET       0 +#define SW_I2C_IO_GET         1 +#define SW_I2C_IO_DRIVE       2 +#define SW_I2C_IO_SET         3 +#define SW_I2C_IO_START       4 + +#define SW_I2C_IO_CLOCK       0 +#define SW_I2C_IO_DATA        0x80 + +#define SW_I2C_IO_ZERO        0 +#define SW_I2C_IO_ONE         0x100 + +#define SW_I2C_CNTL_READ      0 +#define SW_I2C_CNTL_WRITE     1 +#define SW_I2C_CNTL_START     2 +#define SW_I2C_CNTL_STOP      3 +#define SW_I2C_CNTL_OPEN      4 +#define SW_I2C_CNTL_CLOSE     5 +#define SW_I2C_CNTL_WRITE1BIT 6 + +//==============================VESA definition Portion=============================== +#define VESA_OEM_PRODUCT_REV			            '01.00' +#define VESA_MODE_ATTRIBUTE_MODE_SUPPORT	     0xBB	//refer to VBE spec p.32, no TTY support +#define VESA_MODE_WIN_ATTRIBUTE						     7 +#define VESA_WIN_SIZE											     64 + +typedef struct _PTR_32_BIT_STRUCTURE +{ +	USHORT	Offset16;			 +	USHORT	Segment16;				 +} PTR_32_BIT_STRUCTURE; + +typedef union _PTR_32_BIT_UNION +{ +	PTR_32_BIT_STRUCTURE	SegmentOffset; +	ULONG					        Ptr32_Bit; +} PTR_32_BIT_UNION; + +typedef struct _VBE_1_2_INFO_BLOCK_UPDATABLE +{ +	UCHAR				      VbeSignature[4]; +	USHORT				    VbeVersion; +	PTR_32_BIT_UNION	OemStringPtr; +	UCHAR				      Capabilities[4]; +	PTR_32_BIT_UNION	VideoModePtr; +	USHORT				    TotalMemory; +} VBE_1_2_INFO_BLOCK_UPDATABLE; + + +typedef struct _VBE_2_0_INFO_BLOCK_UPDATABLE +{ +	VBE_1_2_INFO_BLOCK_UPDATABLE	CommonBlock; +	USHORT							    OemSoftRev; +	PTR_32_BIT_UNION				OemVendorNamePtr; +	PTR_32_BIT_UNION				OemProductNamePtr; +	PTR_32_BIT_UNION				OemProductRevPtr; +} VBE_2_0_INFO_BLOCK_UPDATABLE; + +typedef union _VBE_VERSION_UNION +{ +	VBE_2_0_INFO_BLOCK_UPDATABLE	VBE_2_0_InfoBlock; +	VBE_1_2_INFO_BLOCK_UPDATABLE	VBE_1_2_InfoBlock; +} VBE_VERSION_UNION; + +typedef struct _VBE_INFO_BLOCK +{ +	VBE_VERSION_UNION			UpdatableVBE_Info; +	UCHAR						      Reserved[222]; +	UCHAR						      OemData[256]; +} VBE_INFO_BLOCK; + +typedef struct _VBE_FP_INFO +{ +  USHORT	HSize; +	USHORT	VSize; +	USHORT	FPType; +	UCHAR		RedBPP; +	UCHAR		GreenBPP; +	UCHAR		BlueBPP; +	UCHAR		ReservedBPP; +	ULONG		RsvdOffScrnMemSize; +	ULONG		RsvdOffScrnMEmPtr; +	UCHAR		Reserved[14]; +} VBE_FP_INFO; + +typedef struct _VESA_MODE_INFO_BLOCK +{ +// Mandatory information for all VBE revisions +  USHORT    ModeAttributes;  //			dw	?	; mode attributes +	UCHAR     WinAAttributes;  //			db	?	; window A attributes +	UCHAR     WinBAttributes;  //			db	?	; window B attributes +	USHORT    WinGranularity;  //			dw	?	; window granularity +	USHORT    WinSize;         //			dw	?	; window size +	USHORT    WinASegment;     //			dw	?	; window A start segment +	USHORT    WinBSegment;     //			dw	?	; window B start segment +	ULONG     WinFuncPtr;      //			dd	?	; real mode pointer to window function +	USHORT    BytesPerScanLine;//			dw	?	; bytes per scan line + +//; Mandatory information for VBE 1.2 and above +  USHORT    XResolution;      //			dw	?	; horizontal resolution in pixels or characters +	USHORT    YResolution;      //			dw	?	; vertical resolution in pixels or characters +	UCHAR     XCharSize;        //			db	?	; character cell width in pixels +	UCHAR     YCharSize;        //			db	?	; character cell height in pixels +	UCHAR     NumberOfPlanes;   //			db	?	; number of memory planes +	UCHAR     BitsPerPixel;     //			db	?	; bits per pixel +	UCHAR     NumberOfBanks;    //			db	?	; number of banks +	UCHAR     MemoryModel;      //			db	?	; memory model type +	UCHAR     BankSize;         //			db	?	; bank size in KB +	UCHAR     NumberOfImagePages;//		  db	?	; number of images +	UCHAR     ReservedForPageFunction;//db	1	; reserved for page function + +//; Direct Color fields(required for direct/6 and YUV/7 memory models) +	UCHAR			RedMaskSize;        //		db	?	; size of direct color red mask in bits +	UCHAR			RedFieldPosition;   //		db	?	; bit position of lsb of red mask +	UCHAR			GreenMaskSize;      //		db	?	; size of direct color green mask in bits +	UCHAR			GreenFieldPosition; //		db	?	; bit position of lsb of green mask +	UCHAR			BlueMaskSize;       //		db	?	; size of direct color blue mask in bits +	UCHAR			BlueFieldPosition;  //		db	?	; bit position of lsb of blue mask +	UCHAR			RsvdMaskSize;       //		db	?	; size of direct color reserved mask in bits +	UCHAR			RsvdFieldPosition;  //		db	?	; bit position of lsb of reserved mask +	UCHAR			DirectColorModeInfo;//		db	?	; direct color mode attributes + +//; Mandatory information for VBE 2.0 and above +	ULONG			PhysBasePtr;        //		dd	?	; physical address for flat memory frame buffer +	ULONG			Reserved_1;         //		dd	0	; reserved - always set to 0 +	USHORT		Reserved_2;         //	  dw	0	; reserved - always set to 0 + +//; Mandatory information for VBE 3.0 and above +	USHORT		LinBytesPerScanLine;  //	dw	?	; bytes per scan line for linear modes +	UCHAR			BnkNumberOfImagePages;//	db	?	; number of images for banked modes +	UCHAR			LinNumberOfImagPages; //	db	?	; number of images for linear modes +	UCHAR			LinRedMaskSize;       //	db	?	; size of direct color red mask(linear modes) +	UCHAR			LinRedFieldPosition;  //	db	?	; bit position of lsb of red mask(linear modes) +	UCHAR			LinGreenMaskSize;     //	db	?	; size of direct color green mask(linear modes) +	UCHAR			LinGreenFieldPosition;//	db	?	; bit position of lsb of green mask(linear modes) +	UCHAR			LinBlueMaskSize;      //	db	?	; size of direct color blue mask(linear modes) +	UCHAR			LinBlueFieldPosition; //	db	?	; bit position of lsb of blue mask(linear modes) +	UCHAR			LinRsvdMaskSize;      //	db	?	; size of direct color reserved mask(linear modes) +	UCHAR			LinRsvdFieldPosition; //	db	?	; bit position of lsb of reserved mask(linear modes) +	ULONG			MaxPixelClock;        //	dd	?	; maximum pixel clock(in Hz) for graphics mode +	UCHAR			Reserved;             //	db	190 dup (0) +} VESA_MODE_INFO_BLOCK; + +// BIOS function CALLS +#define ATOM_BIOS_EXTENDED_FUNCTION_CODE        0xA0	        // ATI Extended Function code +#define ATOM_BIOS_FUNCTION_COP_MODE             0x00 +#define ATOM_BIOS_FUNCTION_SHORT_QUERY1         0x04 +#define ATOM_BIOS_FUNCTION_SHORT_QUERY2         0x05 +#define ATOM_BIOS_FUNCTION_SHORT_QUERY3         0x06 +#define ATOM_BIOS_FUNCTION_GET_DDC              0x0B    +#define ATOM_BIOS_FUNCTION_ASIC_DSTATE          0x0E +#define ATOM_BIOS_FUNCTION_DEBUG_PLAY           0x0F +#define ATOM_BIOS_FUNCTION_STV_STD              0x16 +#define ATOM_BIOS_FUNCTION_DEVICE_DET           0x17 +#define ATOM_BIOS_FUNCTION_DEVICE_SWITCH        0x18 + +#define ATOM_BIOS_FUNCTION_PANEL_CONTROL        0x82 +#define ATOM_BIOS_FUNCTION_OLD_DEVICE_DET       0x83 +#define ATOM_BIOS_FUNCTION_OLD_DEVICE_SWITCH    0x84 +#define ATOM_BIOS_FUNCTION_HW_ICON              0x8A  +#define ATOM_BIOS_FUNCTION_SET_CMOS             0x8B +#define SUB_FUNCTION_UPDATE_DISPLAY_INFO        0x8000          // Sub function 80 +#define SUB_FUNCTION_UPDATE_EXPANSION_INFO      0x8100          // Sub function 80 + +#define ATOM_BIOS_FUNCTION_DISPLAY_INFO         0x8D +#define ATOM_BIOS_FUNCTION_DEVICE_ON_OFF        0x8E +#define ATOM_BIOS_FUNCTION_VIDEO_STATE          0x8F  +#define ATOM_SUB_FUNCTION_GET_CRITICAL_STATE    0x0300          // Sub function 03   +#define ATOM_SUB_FUNCTION_GET_LIDSTATE          0x0700          // Sub function 7 +#define ATOM_SUB_FUNCTION_THERMAL_STATE_NOTICE  0x1400          // Notify caller the current thermal state +#define ATOM_SUB_FUNCTION_CRITICAL_STATE_NOTICE 0x8300          // Notify caller the current critical state +#define ATOM_SUB_FUNCTION_SET_LIDSTATE          0x8500          // Sub function 85 +#define ATOM_SUB_FUNCTION_GET_REQ_DISPLAY_FROM_SBIOS_MODE 0x8900// Sub function 89 +#define ATOM_SUB_FUNCTION_INFORM_ADC_SUPPORT    0x9400          // Notify caller that ADC is supported +      + +#define ATOM_BIOS_FUNCTION_VESA_DPMS            0x4F10          // Set DPMS  +#define ATOM_SUB_FUNCTION_SET_DPMS              0x0001          // BL: Sub function 01  +#define ATOM_SUB_FUNCTION_GET_DPMS              0x0002          // BL: Sub function 02  +#define ATOM_PARAMETER_VESA_DPMS_ON             0x0000          // BH Parameter for DPMS ON.   +#define ATOM_PARAMETER_VESA_DPMS_STANDBY        0x0100          // BH Parameter for DPMS STANDBY   +#define ATOM_PARAMETER_VESA_DPMS_SUSPEND        0x0200          // BH Parameter for DPMS SUSPEND +#define ATOM_PARAMETER_VESA_DPMS_OFF            0x0400          // BH Parameter for DPMS OFF +#define ATOM_PARAMETER_VESA_DPMS_REDUCE_ON      0x0800          // BH Parameter for DPMS REDUCE ON (NOT SUPPORTED) + +#define ATOM_BIOS_RETURN_CODE_MASK              0x0000FF00L +#define ATOM_BIOS_REG_HIGH_MASK                 0x0000FF00L +#define ATOM_BIOS_REG_LOW_MASK                  0x000000FFL + +// structure used for VBIOS only + +//DispOutInfoTable +typedef struct _ASIC_TRANSMITTER_INFO +{ +	USHORT usTransmitterObjId; +	USHORT usSupportDevice; +  UCHAR  ucTransmitterCmdTblId; +	UCHAR  ucConfig; +	UCHAR  ucEncoderID;					 //available 1st encoder ( default ) +	UCHAR  ucOptionEncoderID;    //available 2nd encoder ( optional ) +	UCHAR  uc2ndEncoderID; +	UCHAR  ucReserved; +}ASIC_TRANSMITTER_INFO; + +typedef struct _ASIC_ENCODER_INFO +{ +	UCHAR ucEncoderID; +	UCHAR ucEncoderConfig; +  USHORT usEncoderCmdTblId; +}ASIC_ENCODER_INFO; + +typedef struct _ATOM_DISP_OUT_INFO +{ +  ATOM_COMMON_TABLE_HEADER sHeader;   +	USHORT ptrTransmitterInfo; +	USHORT ptrEncoderInfo; +	ASIC_TRANSMITTER_INFO  asTransmitterInfo[1]; +	ASIC_ENCODER_INFO      asEncoderInfo[1]; +}ATOM_DISP_OUT_INFO; + +// DispDevicePriorityInfo +typedef struct _ATOM_DISPLAY_DEVICE_PRIORITY_INFO +{ +  ATOM_COMMON_TABLE_HEADER sHeader;   +	USHORT asDevicePriority[16]; +}ATOM_DISPLAY_DEVICE_PRIORITY_INFO; + +//ProcessAuxChannelTransactionTable +typedef struct _PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS +{ +	USHORT	lpAuxRequest; +	USHORT  lpDataOut; +	UCHAR		ucChannelID; +	union +	{ +  UCHAR   ucReplyStatus; +	UCHAR   ucDelay; +	}; +  UCHAR   ucDataOutLen; +	UCHAR   ucReserved; +}PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS; + +#define PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION			PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS + +//GetSinkType + +typedef struct _DP_ENCODER_SERVICE_PARAMETERS +{ +	USHORT ucLinkClock; +	union  +	{ +	UCHAR ucConfig;				// for DP training command +	UCHAR ucI2cId;				// use for GET_SINK_TYPE command +	}; +	UCHAR ucAction; +	UCHAR ucStatus; +	UCHAR ucLaneNum; +	UCHAR ucReserved[2]; +}DP_ENCODER_SERVICE_PARAMETERS; + +// ucAction +#define ATOM_DP_ACTION_GET_SINK_TYPE							0x01 +#define ATOM_DP_ACTION_TRAINING_START							0x02 +#define ATOM_DP_ACTION_TRAINING_COMPLETE					0x03 +#define ATOM_DP_ACTION_TRAINING_PATTERN_SEL				0x04 +#define ATOM_DP_ACTION_SET_VSWING_PREEMP					0x05 +#define ATOM_DP_ACTION_GET_VSWING_PREEMP					0x06 + +// ucConfig +#define ATOM_DP_CONFIG_ENCODER_SEL_MASK						0x03 +#define ATOM_DP_CONFIG_DIG1_ENCODER								0x00 +#define ATOM_DP_CONFIG_DIG2_ENCODER								0x01 +#define ATOM_DP_CONFIG_EXTERNAL_ENCODER						0x02 +#define ATOM_DP_CONFIG_LINK_SEL_MASK							0x04 +#define ATOM_DP_CONFIG_LINK_A											0x00 +#define ATOM_DP_CONFIG_LINK_B											0x04 + +#define DP_ENCODER_SERVICE_PS_ALLOCATION				WRITE_ONE_BYTE_HW_I2C_DATA_PARAMETERS + +// DP_TRAINING_TABLE +#define DPCD_SET_LINKRATE_LANENUM_PATTERN1_TBL_ADDR				ATOM_DP_TRAINING_TBL_ADDR		 +#define DPCD_SET_SS_CNTL_TBL_ADDR													(ATOM_DP_TRAINING_TBL_ADDR + 8 ) +#define DPCD_SET_LANE_VSWING_PREEMP_TBL_ADDR							(ATOM_DP_TRAINING_TBL_ADDR + 16 ) +#define DPCD_SET_TRAINING_PATTERN0_TBL_ADDR								(ATOM_DP_TRAINING_TBL_ADDR + 24 ) +#define DPCD_SET_TRAINING_PATTERN2_TBL_ADDR								(ATOM_DP_TRAINING_TBL_ADDR + 32) +#define DPCD_GET_LINKRATE_LANENUM_SS_TBL_ADDR							(ATOM_DP_TRAINING_TBL_ADDR + 40) +#define	DPCD_GET_LANE_STATUS_ADJUST_TBL_ADDR							(ATOM_DP_TRAINING_TBL_ADDR + 48) +#define DP_I2C_AUX_DDC_WRITE_START_TBL_ADDR								(ATOM_DP_TRAINING_TBL_ADDR + 60) +#define DP_I2C_AUX_DDC_WRITE_TBL_ADDR											(ATOM_DP_TRAINING_TBL_ADDR + 64) +#define DP_I2C_AUX_DDC_READ_START_TBL_ADDR								(ATOM_DP_TRAINING_TBL_ADDR + 72) +#define DP_I2C_AUX_DDC_READ_TBL_ADDR											(ATOM_DP_TRAINING_TBL_ADDR + 76) +#define DP_I2C_AUX_DDC_READ_END_TBL_ADDR									(ATOM_DP_TRAINING_TBL_ADDR + 80) + + +typedef struct _PROCESS_I2C_CHANNEL_TRANSACTION_PARAMETERS +{ +	UCHAR   ucI2CSpeed; + 	union +	{ +   UCHAR ucRegIndex; +   UCHAR ucStatus; +	}; +	USHORT  lpI2CDataOut; +  UCHAR   ucFlag;                +  UCHAR   ucTransBytes; +  UCHAR   ucSlaveAddr; +  UCHAR   ucLineNumber; +}PROCESS_I2C_CHANNEL_TRANSACTION_PARAMETERS; + +#define PROCESS_I2C_CHANNEL_TRANSACTION_PS_ALLOCATION       PROCESS_I2C_CHANNEL_TRANSACTION_PARAMETERS + +//ucFlag +#define HW_I2C_WRITE        1 +#define HW_I2C_READ         0 + + +/****************************************************************************/	 +//Portion VI: Definitinos being oboselete +/****************************************************************************/ + +//========================================================================================== +//Remove the definitions below when driver is ready! +typedef struct _ATOM_DAC_INFO +{ +  ATOM_COMMON_TABLE_HEADER sHeader;   +  USHORT                   usMaxFrequency;      // in 10kHz unit +  USHORT                   usReserved; +}ATOM_DAC_INFO; + + +typedef struct  _COMPASSIONATE_DATA            +{ +  ATOM_COMMON_TABLE_HEADER sHeader;  + +  //==============================  DAC1 portion +  UCHAR   ucDAC1_BG_Adjustment; +  UCHAR   ucDAC1_DAC_Adjustment; +  USHORT  usDAC1_FORCE_Data; +  //==============================  DAC2 portion +  UCHAR   ucDAC2_CRT2_BG_Adjustment; +  UCHAR   ucDAC2_CRT2_DAC_Adjustment; +  USHORT  usDAC2_CRT2_FORCE_Data; +  USHORT  usDAC2_CRT2_MUX_RegisterIndex; +  UCHAR   ucDAC2_CRT2_MUX_RegisterInfo;     //Bit[4:0]=Bit position,Bit[7]=1:Active High;=0 Active Low +  UCHAR   ucDAC2_NTSC_BG_Adjustment; +  UCHAR   ucDAC2_NTSC_DAC_Adjustment; +  USHORT  usDAC2_TV1_FORCE_Data; +  USHORT  usDAC2_TV1_MUX_RegisterIndex; +  UCHAR   ucDAC2_TV1_MUX_RegisterInfo;      //Bit[4:0]=Bit position,Bit[7]=1:Active High;=0 Active Low +  UCHAR   ucDAC2_CV_BG_Adjustment; +  UCHAR   ucDAC2_CV_DAC_Adjustment; +  USHORT  usDAC2_CV_FORCE_Data; +  USHORT  usDAC2_CV_MUX_RegisterIndex; +  UCHAR   ucDAC2_CV_MUX_RegisterInfo;       //Bit[4:0]=Bit position,Bit[7]=1:Active High;=0 Active Low +  UCHAR   ucDAC2_PAL_BG_Adjustment; +  UCHAR   ucDAC2_PAL_DAC_Adjustment; +  USHORT  usDAC2_TV2_FORCE_Data; +}COMPASSIONATE_DATA; + +/****************************Supported Device Info Table Definitions**********************/ +//  ucConnectInfo: +//    [7:4] - connector type +//      = 1   - VGA connector    +//      = 2   - DVI-I +//      = 3   - DVI-D +//      = 4   - DVI-A +//      = 5   - SVIDEO +//      = 6   - COMPOSITE +//      = 7   - LVDS +//      = 8   - DIGITAL LINK +//      = 9   - SCART +//      = 0xA - HDMI_type A +//      = 0xB - HDMI_type B +//      = 0xE - Special case1 (DVI+DIN) +//      Others=TBD +//    [3:0] - DAC Associated +//      = 0   - no DAC +//      = 1   - DACA +//      = 2   - DACB +//      = 3   - External DAC +//      Others=TBD +//     + +typedef struct _ATOM_CONNECTOR_INFO +{ +#if ATOM_BIG_ENDIAN +  UCHAR   bfConnectorType:4; +  UCHAR   bfAssociatedDAC:4; +#else +  UCHAR   bfAssociatedDAC:4; +  UCHAR   bfConnectorType:4; +#endif +}ATOM_CONNECTOR_INFO; + +typedef union _ATOM_CONNECTOR_INFO_ACCESS +{ +  ATOM_CONNECTOR_INFO sbfAccess; +  UCHAR               ucAccess; +}ATOM_CONNECTOR_INFO_ACCESS; + +typedef struct _ATOM_CONNECTOR_INFO_I2C +{ +  ATOM_CONNECTOR_INFO_ACCESS sucConnectorInfo; +  ATOM_I2C_ID_CONFIG_ACCESS  sucI2cId; +}ATOM_CONNECTOR_INFO_I2C; + + +typedef struct _ATOM_SUPPORTED_DEVICES_INFO +{  +  ATOM_COMMON_TABLE_HEADER	sHeader; +  USHORT                    usDeviceSupport; +  ATOM_CONNECTOR_INFO_I2C   asConnInfo[ATOM_MAX_SUPPORTED_DEVICE_INFO]; +}ATOM_SUPPORTED_DEVICES_INFO; + +#define NO_INT_SRC_MAPPED       0xFF + +typedef struct _ATOM_CONNECTOR_INC_SRC_BITMAP +{ +  UCHAR   ucIntSrcBitmap; +}ATOM_CONNECTOR_INC_SRC_BITMAP; + +typedef struct _ATOM_SUPPORTED_DEVICES_INFO_2 +{  +  ATOM_COMMON_TABLE_HEADER      sHeader; +  USHORT                        usDeviceSupport; +  ATOM_CONNECTOR_INFO_I2C       asConnInfo[ATOM_MAX_SUPPORTED_DEVICE_INFO_2]; +  ATOM_CONNECTOR_INC_SRC_BITMAP asIntSrcInfo[ATOM_MAX_SUPPORTED_DEVICE_INFO_2]; +}ATOM_SUPPORTED_DEVICES_INFO_2; + +typedef struct _ATOM_SUPPORTED_DEVICES_INFO_2d1 +{  +  ATOM_COMMON_TABLE_HEADER      sHeader; +  USHORT                        usDeviceSupport; +  ATOM_CONNECTOR_INFO_I2C       asConnInfo[ATOM_MAX_SUPPORTED_DEVICE]; +  ATOM_CONNECTOR_INC_SRC_BITMAP asIntSrcInfo[ATOM_MAX_SUPPORTED_DEVICE]; +}ATOM_SUPPORTED_DEVICES_INFO_2d1; + +#define ATOM_SUPPORTED_DEVICES_INFO_LAST ATOM_SUPPORTED_DEVICES_INFO_2d1 + + + +typedef struct _ATOM_MISC_CONTROL_INFO +{ +   USHORT usFrequency; +   UCHAR  ucPLL_ChargePump;				                // PLL charge-pump gain control +   UCHAR  ucPLL_DutyCycle;				                // PLL duty cycle control +   UCHAR  ucPLL_VCO_Gain;				                  // PLL VCO gain control +   UCHAR  ucPLL_VoltageSwing;			                // PLL driver voltage swing control +}ATOM_MISC_CONTROL_INFO;   + + +#define ATOM_MAX_MISC_INFO       4 + +typedef struct _ATOM_TMDS_INFO +{ +  ATOM_COMMON_TABLE_HEADER sHeader;   +  USHORT							usMaxFrequency;             // in 10Khz +  ATOM_MISC_CONTROL_INFO				asMiscInfo[ATOM_MAX_MISC_INFO]; +}ATOM_TMDS_INFO; + + +typedef struct _ATOM_ENCODER_ANALOG_ATTRIBUTE +{ +  UCHAR ucTVStandard;     //Same as TV standards defined above,  +  UCHAR ucPadding[1]; +}ATOM_ENCODER_ANALOG_ATTRIBUTE; + +typedef struct _ATOM_ENCODER_DIGITAL_ATTRIBUTE +{ +  UCHAR ucAttribute;      //Same as other digital encoder attributes defined above +  UCHAR ucPadding[1];		 +}ATOM_ENCODER_DIGITAL_ATTRIBUTE; + +typedef union _ATOM_ENCODER_ATTRIBUTE +{ +  ATOM_ENCODER_ANALOG_ATTRIBUTE sAlgAttrib; +  ATOM_ENCODER_DIGITAL_ATTRIBUTE sDigAttrib; +}ATOM_ENCODER_ATTRIBUTE; + + +typedef struct _DVO_ENCODER_CONTROL_PARAMETERS +{ +  USHORT usPixelClock;  +  USHORT usEncoderID;  +  UCHAR  ucDeviceType;												//Use ATOM_DEVICE_xxx1_Index to indicate device type only.	 +  UCHAR  ucAction;														//ATOM_ENABLE/ATOM_DISABLE/ATOM_HPD_INIT +  ATOM_ENCODER_ATTRIBUTE usDevAttr;     		 +}DVO_ENCODER_CONTROL_PARAMETERS; + +typedef struct _DVO_ENCODER_CONTROL_PS_ALLOCATION +{                                +  DVO_ENCODER_CONTROL_PARAMETERS    sDVOEncoder; +  WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION      sReserved;     //Caller doesn't need to init this portion +}DVO_ENCODER_CONTROL_PS_ALLOCATION; + + +#define ATOM_XTMDS_ASIC_SI164_ID        1 +#define ATOM_XTMDS_ASIC_SI178_ID        2 +#define ATOM_XTMDS_ASIC_TFP513_ID       3 +#define ATOM_XTMDS_SUPPORTED_SINGLELINK 0x00000001 +#define ATOM_XTMDS_SUPPORTED_DUALLINK   0x00000002 +#define ATOM_XTMDS_MVPU_FPGA            0x00000004 + +                            +typedef struct _ATOM_XTMDS_INFO +{ +  ATOM_COMMON_TABLE_HEADER   sHeader;   +  USHORT                     usSingleLinkMaxFrequency;  +  ATOM_I2C_ID_CONFIG_ACCESS  sucI2cId;           //Point the ID on which I2C is used to control external chip +  UCHAR                      ucXtransimitterID;           +  UCHAR                      ucSupportedLink;    // Bit field, bit0=1, single link supported;bit1=1,dual link supported +  UCHAR                      ucSequnceAlterID;   // Even with the same external TMDS asic, it's possible that the program seqence alters  +                                                 // due to design. This ID is used to alert driver that the sequence is not "standard"!               +  UCHAR                      ucMasterAddress;    // Address to control Master xTMDS Chip +  UCHAR                      ucSlaveAddress;     // Address to control Slave xTMDS Chip +}ATOM_XTMDS_INFO; + +typedef struct _DFP_DPMS_STATUS_CHANGE_PARAMETERS +{   +  UCHAR ucEnable;                     // ATOM_ENABLE=On or ATOM_DISABLE=Off +  UCHAR ucDevice;                     // ATOM_DEVICE_DFP1_INDEX.... +  UCHAR ucPadding[2];              +}DFP_DPMS_STATUS_CHANGE_PARAMETERS; + +/****************************Legacy Power Play Table Definitions **********************/ + +//Definitions for ulPowerPlayMiscInfo +#define ATOM_PM_MISCINFO_SPLIT_CLOCK                     0x00000000L +#define ATOM_PM_MISCINFO_USING_MCLK_SRC                  0x00000001L +#define ATOM_PM_MISCINFO_USING_SCLK_SRC                  0x00000002L + +#define ATOM_PM_MISCINFO_VOLTAGE_DROP_SUPPORT            0x00000004L +#define ATOM_PM_MISCINFO_VOLTAGE_DROP_ACTIVE_HIGH        0x00000008L + +#define ATOM_PM_MISCINFO_LOAD_PERFORMANCE_EN             0x00000010L + +#define ATOM_PM_MISCINFO_ENGINE_CLOCK_CONTRL_EN          0x00000020L +#define ATOM_PM_MISCINFO_MEMORY_CLOCK_CONTRL_EN          0x00000040L +#define ATOM_PM_MISCINFO_PROGRAM_VOLTAGE                 0x00000080L  //When this bit set, ucVoltageDropIndex is not an index for GPIO pin, but a voltage ID that SW needs program   +  +#define ATOM_PM_MISCINFO_ASIC_REDUCED_SPEED_SCLK_EN      0x00000100L +#define ATOM_PM_MISCINFO_ASIC_DYNAMIC_VOLTAGE_EN         0x00000200L +#define ATOM_PM_MISCINFO_ASIC_SLEEP_MODE_EN              0x00000400L +#define ATOM_PM_MISCINFO_LOAD_BALANCE_EN                 0x00000800L +#define ATOM_PM_MISCINFO_DEFAULT_DC_STATE_ENTRY_TRUE     0x00001000L +#define ATOM_PM_MISCINFO_DEFAULT_LOW_DC_STATE_ENTRY_TRUE 0x00002000L +#define ATOM_PM_MISCINFO_LOW_LCD_REFRESH_RATE            0x00004000L + +#define ATOM_PM_MISCINFO_DRIVER_DEFAULT_MODE             0x00008000L +#define ATOM_PM_MISCINFO_OVER_CLOCK_MODE                 0x00010000L  +#define ATOM_PM_MISCINFO_OVER_DRIVE_MODE                 0x00020000L +#define ATOM_PM_MISCINFO_POWER_SAVING_MODE               0x00040000L +#define ATOM_PM_MISCINFO_THERMAL_DIODE_MODE              0x00080000L + +#define ATOM_PM_MISCINFO_FRAME_MODULATION_MASK           0x00300000L  //0-FM Disable, 1-2 level FM, 2-4 level FM, 3-Reserved +#define ATOM_PM_MISCINFO_FRAME_MODULATION_SHIFT          20  + +#define ATOM_PM_MISCINFO_DYN_CLK_3D_IDLE                 0x00400000L +#define ATOM_PM_MISCINFO_DYNAMIC_CLOCK_DIVIDER_BY_2      0x00800000L +#define ATOM_PM_MISCINFO_DYNAMIC_CLOCK_DIVIDER_BY_4      0x01000000L +#define ATOM_PM_MISCINFO_DYNAMIC_HDP_BLOCK_EN            0x02000000L  //When set, Dynamic  +#define ATOM_PM_MISCINFO_DYNAMIC_MC_HOST_BLOCK_EN        0x04000000L  //When set, Dynamic +#define ATOM_PM_MISCINFO_3D_ACCELERATION_EN              0x08000000L  //When set, This mode is for acceleated 3D mode + +#define ATOM_PM_MISCINFO_POWERPLAY_SETTINGS_GROUP_MASK   0x70000000L  //1-Optimal Battery Life Group, 2-High Battery, 3-Balanced, 4-High Performance, 5- Optimal Performance (Default state with Default clocks)  +#define ATOM_PM_MISCINFO_POWERPLAY_SETTINGS_GROUP_SHIFT  28 +#define ATOM_PM_MISCINFO_ENABLE_BACK_BIAS                0x80000000L + +#define ATOM_PM_MISCINFO2_SYSTEM_AC_LITE_MODE            0x00000001L +#define ATOM_PM_MISCINFO2_MULTI_DISPLAY_SUPPORT          0x00000002L +#define ATOM_PM_MISCINFO2_DYNAMIC_BACK_BIAS_EN           0x00000004L +#define ATOM_PM_MISCINFO2_FS3D_OVERDRIVE_INFO            0x00000008L +#define ATOM_PM_MISCINFO2_FORCEDLOWPWR_MODE              0x00000010L +#define ATOM_PM_MISCINFO2_VDDCI_DYNAMIC_VOLTAGE_EN       0x00000020L +#define ATOM_PM_MISCINFO2_VIDEO_PLAYBACK_CAPABLE         0x00000040L  //If this bit is set in multi-pp mode, then driver will pack up one with the minior power consumption.  +                                                                      //If it's not set in any pp mode, driver will use its default logic to pick a pp mode in video playback +#define ATOM_PM_MISCINFO2_NOT_VALID_ON_DC                0x00000080L +#define ATOM_PM_MISCINFO2_STUTTER_MODE_EN                0x00000100L +#define ATOM_PM_MISCINFO2_UVD_SUPPORT_MODE               0x00000200L  + +//ucTableFormatRevision=1 +//ucTableContentRevision=1 +typedef struct  _ATOM_POWERMODE_INFO +{ +  ULONG     ulMiscInfo;                 //The power level should be arranged in ascending order +  ULONG     ulReserved1;                // must set to 0 +  ULONG     ulReserved2;                // must set to 0 +  USHORT    usEngineClock; +  USHORT    usMemoryClock; +  UCHAR     ucVoltageDropIndex;         // index to GPIO table +  UCHAR     ucSelectedPanel_RefreshRate;// panel refresh rate +  UCHAR     ucMinTemperature; +  UCHAR     ucMaxTemperature; +  UCHAR     ucNumPciELanes;             // number of PCIE lanes +}ATOM_POWERMODE_INFO; + +//ucTableFormatRevision=2 +//ucTableContentRevision=1 +typedef struct  _ATOM_POWERMODE_INFO_V2 +{ +  ULONG     ulMiscInfo;                 //The power level should be arranged in ascending order +  ULONG     ulMiscInfo2;                 +  ULONG     ulEngineClock;                 +  ULONG     ulMemoryClock; +  UCHAR     ucVoltageDropIndex;         // index to GPIO table +  UCHAR     ucSelectedPanel_RefreshRate;// panel refresh rate +  UCHAR     ucMinTemperature; +  UCHAR     ucMaxTemperature; +  UCHAR     ucNumPciELanes;             // number of PCIE lanes +}ATOM_POWERMODE_INFO_V2; + +//ucTableFormatRevision=2 +//ucTableContentRevision=2 +typedef struct  _ATOM_POWERMODE_INFO_V3 +{ +  ULONG     ulMiscInfo;                 //The power level should be arranged in ascending order +  ULONG     ulMiscInfo2;                 +  ULONG     ulEngineClock;                 +  ULONG     ulMemoryClock; +  UCHAR     ucVoltageDropIndex;         // index to Core (VDDC) votage table +  UCHAR     ucSelectedPanel_RefreshRate;// panel refresh rate +  UCHAR     ucMinTemperature; +  UCHAR     ucMaxTemperature; +  UCHAR     ucNumPciELanes;             // number of PCIE lanes +  UCHAR     ucVDDCI_VoltageDropIndex;   // index to VDDCI votage table +}ATOM_POWERMODE_INFO_V3; + + +#define ATOM_MAX_NUMBEROF_POWER_BLOCK  8 + +#define ATOM_PP_OVERDRIVE_INTBITMAP_AUXWIN            0x01 +#define ATOM_PP_OVERDRIVE_INTBITMAP_OVERDRIVE         0x02 + +#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_LM63      0x01 +#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_ADM1032   0x02 +#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_ADM1030   0x03 +#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_MUA6649   0x04 +#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_LM64      0x05 +#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_F75375    0x06 +#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_ASC7512   0x07	// Andigilog + + +typedef struct  _ATOM_POWERPLAY_INFO +{ +  ATOM_COMMON_TABLE_HEADER	sHeader;  +  UCHAR    ucOverdriveThermalController; +  UCHAR    ucOverdriveI2cLine; +  UCHAR    ucOverdriveIntBitmap; +  UCHAR    ucOverdriveControllerAddress; +  UCHAR    ucSizeOfPowerModeEntry; +  UCHAR    ucNumOfPowerModeEntries; +  ATOM_POWERMODE_INFO asPowerPlayInfo[ATOM_MAX_NUMBEROF_POWER_BLOCK]; +}ATOM_POWERPLAY_INFO; + +typedef struct  _ATOM_POWERPLAY_INFO_V2 +{ +  ATOM_COMMON_TABLE_HEADER	sHeader;  +  UCHAR    ucOverdriveThermalController; +  UCHAR    ucOverdriveI2cLine; +  UCHAR    ucOverdriveIntBitmap; +  UCHAR    ucOverdriveControllerAddress; +  UCHAR    ucSizeOfPowerModeEntry; +  UCHAR    ucNumOfPowerModeEntries; +  ATOM_POWERMODE_INFO_V2 asPowerPlayInfo[ATOM_MAX_NUMBEROF_POWER_BLOCK]; +}ATOM_POWERPLAY_INFO_V2; +   +typedef struct  _ATOM_POWERPLAY_INFO_V3 +{ +  ATOM_COMMON_TABLE_HEADER	sHeader;  +  UCHAR    ucOverdriveThermalController; +  UCHAR    ucOverdriveI2cLine; +  UCHAR    ucOverdriveIntBitmap; +  UCHAR    ucOverdriveControllerAddress; +  UCHAR    ucSizeOfPowerModeEntry; +  UCHAR    ucNumOfPowerModeEntries; +  ATOM_POWERMODE_INFO_V3 asPowerPlayInfo[ATOM_MAX_NUMBEROF_POWER_BLOCK]; +}ATOM_POWERPLAY_INFO_V3; + + + +/**************************************************************************/ + + +// Following definitions are for compatiblity issue in different SW components.  +#define ATOM_MASTER_DATA_TABLE_REVISION   0x01 +#define Object_Info												Object_Header			 +#define	AdjustARB_SEQ											MC_InitParameter +#define	VRAM_GPIO_DetectionInfo						VoltageObjectInfo +#define	ASIC_VDDCI_Info                   ASIC_ProfilingInfo														 +#define ASIC_MVDDQ_Info										MemoryTrainingInfo +#define SS_Info                           PPLL_SS_Info                       +#define ASIC_MVDDC_Info                   ASIC_InternalSS_Info +#define DispDevicePriorityInfo						SaveRestoreInfo +#define DispOutInfo												TV_VideoMode + + +#define ATOM_ENCODER_OBJECT_TABLE         ATOM_OBJECT_TABLE +#define ATOM_CONNECTOR_OBJECT_TABLE       ATOM_OBJECT_TABLE + +//New device naming, remove them when both DAL/VBIOS is ready +#define DFP2I_OUTPUT_CONTROL_PARAMETERS    CRT1_OUTPUT_CONTROL_PARAMETERS +#define DFP2I_OUTPUT_CONTROL_PS_ALLOCATION DFP2I_OUTPUT_CONTROL_PARAMETERS + +#define DFP1X_OUTPUT_CONTROL_PARAMETERS    CRT1_OUTPUT_CONTROL_PARAMETERS +#define DFP1X_OUTPUT_CONTROL_PS_ALLOCATION DFP1X_OUTPUT_CONTROL_PARAMETERS + +#define DFP1I_OUTPUT_CONTROL_PARAMETERS    DFP1_OUTPUT_CONTROL_PARAMETERS +#define DFP1I_OUTPUT_CONTROL_PS_ALLOCATION DFP1_OUTPUT_CONTROL_PS_ALLOCATION + +#define ATOM_DEVICE_DFP1I_SUPPORT          ATOM_DEVICE_DFP1_SUPPORT +#define ATOM_DEVICE_DFP1X_SUPPORT          ATOM_DEVICE_DFP2_SUPPORT + +#define ATOM_DEVICE_DFP1I_INDEX            ATOM_DEVICE_DFP1_INDEX +#define ATOM_DEVICE_DFP1X_INDEX            ATOM_DEVICE_DFP2_INDEX +  +#define ATOM_DEVICE_DFP2I_INDEX            0x00000009 +#define ATOM_DEVICE_DFP2I_SUPPORT          (0x1L << ATOM_DEVICE_DFP2I_INDEX) + +#define ATOM_S0_DFP1I                      ATOM_S0_DFP1 +#define ATOM_S0_DFP1X                      ATOM_S0_DFP2 + +#define ATOM_S0_DFP2I                      0x00200000L +#define ATOM_S0_DFP2Ib2                    0x20 + +#define ATOM_S2_DFP1I_DPMS_STATE           ATOM_S2_DFP1_DPMS_STATE +#define ATOM_S2_DFP1X_DPMS_STATE           ATOM_S2_DFP2_DPMS_STATE + +#define ATOM_S2_DFP2I_DPMS_STATE           0x02000000L +#define ATOM_S2_DFP2I_DPMS_STATEb3         0x02 + +#define ATOM_S3_DFP2I_ACTIVEb1             0x02 + +#define ATOM_S3_DFP1I_ACTIVE               ATOM_S3_DFP1_ACTIVE  +#define ATOM_S3_DFP1X_ACTIVE               ATOM_S3_DFP2_ACTIVE + +#define ATOM_S3_DFP2I_ACTIVE               0x00000200L + +#define ATOM_S3_DFP1I_CRTC_ACTIVE          ATOM_S3_DFP1_CRTC_ACTIVE +#define ATOM_S3_DFP1X_CRTC_ACTIVE          ATOM_S3_DFP2_CRTC_ACTIVE +#define ATOM_S3_DFP2I_CRTC_ACTIVE          0x02000000L + +#define ATOM_S3_DFP2I_CRTC_ACTIVEb3        0x02 +#define ATOM_S5_DOS_REQ_DFP2Ib1            0x02 + +#define ATOM_S5_DOS_REQ_DFP2I              0x0200 +#define ATOM_S6_ACC_REQ_DFP1I              ATOM_S6_ACC_REQ_DFP1 +#define ATOM_S6_ACC_REQ_DFP1X              ATOM_S6_ACC_REQ_DFP2 + +#define ATOM_S6_ACC_REQ_DFP2Ib3            0x02 +#define ATOM_S6_ACC_REQ_DFP2I              0x02000000L + +#define TMDS1XEncoderControl               DVOEncoderControl            +#define DFP1XOutputControl                 DVOOutputControl + +#define ExternalDFPOutputControl           DFP1XOutputControl +#define EnableExternalTMDS_Encoder         TMDS1XEncoderControl + +#define DFP1IOutputControl                 TMDSAOutputControl +#define DFP2IOutputControl                 LVTMAOutputControl       + +#define DAC1_ENCODER_CONTROL_PARAMETERS    DAC_ENCODER_CONTROL_PARAMETERS +#define DAC1_ENCODER_CONTROL_PS_ALLOCATION DAC_ENCODER_CONTROL_PS_ALLOCATION + +#define DAC2_ENCODER_CONTROL_PARAMETERS    DAC_ENCODER_CONTROL_PARAMETERS +#define DAC2_ENCODER_CONTROL_PS_ALLOCATION DAC_ENCODER_CONTROL_PS_ALLOCATION + +#define ucDac1Standard  ucDacStandard +#define ucDac2Standard  ucDacStandard   + +#define TMDS1EncoderControl TMDSAEncoderControl +#define TMDS2EncoderControl LVTMAEncoderControl + +#define DFP1OutputControl   TMDSAOutputControl +#define DFP2OutputControl   LVTMAOutputControl +#define CRT1OutputControl   DAC1OutputControl +#define CRT2OutputControl   DAC2OutputControl + +//These two lines will be removed for sure in a few days, will follow up with Michael V. +#define EnableLVDS_SS   EnableSpreadSpectrumOnPPLL +#define ENABLE_LVDS_SS_PARAMETERS_V3  ENABLE_SPREAD_SPECTRUM_ON_PPLL   + +/*********************************************************************************/ +#define ATOM_S3_SCALER2_ACTIVE_H          0x00004000L +#define ATOM_S3_SCALER2_ACTIVE_V          0x00008000L +#define ATOM_S6_REQ_SCALER2_H             0x00004000L +#define ATOM_S6_REQ_SCALER2_V             0x00008000L +  +#define ATOM_S3_SCALER1_ACTIVE_H          ATOM_S3_LCD_FULLEXPANSION_ACTIVE +#define ATOM_S3_SCALER1_ACTIVE_V          ATOM_S3_LCD_EXPANSION_ASPEC_RATIO_ACTIVE +  +#define ATOM_S6_REQ_SCALER1_H             ATOM_S6_REQ_LCD_EXPANSION_FULL +#define ATOM_S6_REQ_SCALER1_V             ATOM_S6_REQ_LCD_EXPANSION_ASPEC_RATIO +//========================================================================================== + +#pragma pack() // BIOS data must use byte aligment + +#endif /* _ATOMBIOS_H */ diff --git a/linux-core/atombios_crtc.c b/linux-core/atombios_crtc.c new file mode 100644 index 00000000..1f372045 --- /dev/null +++ b/linux-core/atombios_crtc.c @@ -0,0 +1,382 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: Dave Airlie + *          Alex Deucher + */ +#include "drmP.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + +#include "drm_crtc_helper.h" +#include "atom.h" +#include "atom-bits.h" + +static void atombios_enable_crtc(struct drm_crtc *crtc, int state) +{ +	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); +	struct drm_device *dev = crtc->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	int index = GetIndexIntoMasterTable(COMMAND, EnableCRTC); +	ENABLE_CRTC_PS_ALLOCATION args; + +	memset(&args, 0, sizeof(args)); + +	args.ucCRTC = radeon_crtc->crtc_id; +	args.ucEnable = state; + +	atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +} + +static void atombios_enable_crtc_memreq(struct drm_crtc *crtc, int state) +{ +	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); +	struct drm_device *dev = crtc->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	int index = GetIndexIntoMasterTable(COMMAND, EnableCRTCMemReq); +	ENABLE_CRTC_PS_ALLOCATION args; + +	memset(&args, 0, sizeof(args)); + +	args.ucCRTC = radeon_crtc->crtc_id; +	args.ucEnable = state; + +	atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +} + +static void atombios_blank_crtc(struct drm_crtc *crtc, int state) +{ +	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); +	struct drm_device *dev = crtc->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	int index = GetIndexIntoMasterTable(COMMAND, BlankCRTC); +	BLANK_CRTC_PS_ALLOCATION args; + +	memset(&args, 0, sizeof(args)); + +	args.ucCRTC = radeon_crtc->crtc_id; +	args.ucBlanking = state; + +	atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +} + +void atombios_crtc_dpms(struct drm_crtc *crtc, int mode) +{ +	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); +	struct drm_device *dev = crtc->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; + +	switch(mode) { +	case DRM_MODE_DPMS_ON: +	case DRM_MODE_DPMS_STANDBY: +	case DRM_MODE_DPMS_SUSPEND: +		if (radeon_is_dce3(dev_priv)) +			atombios_enable_crtc_memreq(crtc, 1); +		atombios_enable_crtc(crtc, 1); +		atombios_blank_crtc(crtc, 0); + +		radeon_crtc_load_lut(crtc); +		break; +	case DRM_MODE_DPMS_OFF: +		atombios_blank_crtc(crtc, 1); +		atombios_enable_crtc(crtc, 0); +		if (radeon_is_dce3(dev_priv)) +			atombios_enable_crtc_memreq(crtc, 0); +		break; +	} +} + + +void atombios_crtc_set_timing(struct drm_crtc *crtc, SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION *crtc_param) +{ +	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); +	struct drm_device *dev = crtc->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION conv_param; +	int index = GetIndexIntoMasterTable(COMMAND, SetCRTC_Timing); + +	conv_param.usH_Total                = cpu_to_le16(crtc_param->usH_Total); +	conv_param.usH_Disp                 = cpu_to_le16(crtc_param->usH_Disp); +	conv_param.usH_SyncStart            = cpu_to_le16(crtc_param->usH_SyncStart); +	conv_param.usH_SyncWidth            = cpu_to_le16(crtc_param->usH_SyncWidth); +	conv_param.usV_Total                = cpu_to_le16(crtc_param->usV_Total); +	conv_param.usV_Disp                 = cpu_to_le16(crtc_param->usV_Disp); +	conv_param.usV_SyncStart            = cpu_to_le16(crtc_param->usV_SyncStart); +	conv_param.usV_SyncWidth            = cpu_to_le16(crtc_param->usV_SyncWidth); +	conv_param.susModeMiscInfo.usAccess = cpu_to_le16(crtc_param->susModeMiscInfo.usAccess); +	conv_param.ucCRTC                   = crtc_param->ucCRTC; +	conv_param.ucOverscanRight          = crtc_param->ucOverscanRight; +	conv_param.ucOverscanLeft           = crtc_param->ucOverscanLeft; +	conv_param.ucOverscanBottom         = crtc_param->ucOverscanBottom; +	conv_param.ucOverscanTop            = crtc_param->ucOverscanTop;  +	conv_param.ucReserved               = crtc_param->ucReserved; + +	printk("executing set crtc timing\n"); +	atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&conv_param); +} + +void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode, +			   int pll_flags) +{ +	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); +	struct drm_device *dev = crtc->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	uint8_t frev, crev; +	int index = GetIndexIntoMasterTable(COMMAND, SetPixelClock); +	SET_PIXEL_CLOCK_PS_ALLOCATION spc_param; +	PIXEL_CLOCK_PARAMETERS_V2 *spc2_ptr; +	PIXEL_CLOCK_PARAMETERS_V3 *spc3_ptr; +	uint32_t sclock = mode->clock; +	uint32_t ref_div = 0, fb_div = 0, post_div = 0; + +	memset(&spc_param, 0, sizeof(SET_PIXEL_CLOCK_PS_ALLOCATION)); + +	if (radeon_is_avivo(dev_priv)) { +		uint32_t temp; + +		pll_flags |= RADEON_PLL_PREFER_LOW_REF_DIV; + +		radeon_compute_pll(&dev_priv->mode_info.pll, mode->clock, +				   &temp, &fb_div, &ref_div, &post_div, pll_flags); +		sclock = temp; + +		if (radeon_crtc->crtc_id == 0) { +			temp = RADEON_READ(AVIVO_P1PLL_INT_SS_CNTL); +			RADEON_WRITE(AVIVO_P1PLL_INT_SS_CNTL, temp & ~1); +		} else { +			temp = RADEON_READ(AVIVO_P2PLL_INT_SS_CNTL); +			RADEON_WRITE(AVIVO_P2PLL_INT_SS_CNTL, temp & ~1); +		} +	} else { +#if 0 // TODO r400 +		sclock = save->dot_clock_freq; +		fb_div = save->feedback_div; +		post_div = save->post_div; +		ref_div = save->ppll_ref_div; +#endif +	} + +	/* */ + +	atom_parse_cmd_header(dev_priv->mode_info.atom_context, index, &frev, &crev); + +	switch(frev) { +	case 1: +		switch(crev) { +		case 1: +		case 2: +			spc2_ptr = (PIXEL_CLOCK_PARAMETERS_V2*)&spc_param.sPCLKInput; +			spc2_ptr->usPixelClock = cpu_to_le16(sclock); +			spc2_ptr->usRefDiv = cpu_to_le16(ref_div); +			spc2_ptr->usFbDiv = cpu_to_le16(fb_div); +			spc2_ptr->ucPostDiv = post_div; +			spc2_ptr->ucPpll = radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1; +			spc2_ptr->ucCRTC = radeon_crtc->crtc_id; +			spc2_ptr->ucRefDivSrc = 1; +			break; +		case 3: +			spc3_ptr = (PIXEL_CLOCK_PARAMETERS_V3*)&spc_param.sPCLKInput; +			spc3_ptr->usPixelClock = cpu_to_le16(sclock); +			spc3_ptr->usRefDiv = cpu_to_le16(ref_div); +			spc3_ptr->usFbDiv = cpu_to_le16(fb_div); +			spc3_ptr->ucPostDiv = post_div; +			spc3_ptr->ucPpll = radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1; +			spc3_ptr->ucMiscInfo = (radeon_crtc->crtc_id << 2); +			 +			/* TODO insert output encoder object stuff herre for r600 */ +			break; +		default: +			DRM_ERROR("Unknown table version %d %d\n", frev, crev); +			return; +		} +		break; +	default: +		DRM_ERROR("Unknown table version %d %d\n", frev, crev); +		return; +	} + +	printk("executing set pll\n"); +	atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&spc_param); +} + +void atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y) +{ +	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);	 +	struct drm_device *dev = crtc->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_framebuffer *radeon_fb; +	struct drm_radeon_gem_object *obj_priv; +	uint32_t fb_location, fb_format, fb_pitch_pixels; + +	if (!crtc->fb) +		return; + +	radeon_fb = to_radeon_framebuffer(crtc->fb); + +	obj_priv = radeon_fb->obj->driver_private; + +	fb_location = obj_priv->bo->offset + dev_priv->fb_location; + +	switch(crtc->fb->bits_per_pixel) { +	case 15: +		fb_format = AVIVO_D1GRPH_CONTROL_DEPTH_16BPP | AVIVO_D1GRPH_CONTROL_16BPP_ARGB1555; +		break; +	case 16: +		fb_format = AVIVO_D1GRPH_CONTROL_DEPTH_16BPP | AVIVO_D1GRPH_CONTROL_16BPP_RGB565; +		break; +	case 24: +	case 32: +		fb_format = AVIVO_D1GRPH_CONTROL_DEPTH_32BPP | AVIVO_D1GRPH_CONTROL_32BPP_ARGB8888; +		break; +	default: +		DRM_ERROR("Unsupported screen depth %d\n", crtc->fb->bits_per_pixel); +		return; +	} +	 +	/* TODO tiling */ +	if (radeon_crtc->crtc_id == 0) +		RADEON_WRITE(AVIVO_D1VGA_CONTROL, 0); +	else +		RADEON_WRITE(AVIVO_D2VGA_CONTROL, 0); +	 +	RADEON_WRITE(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset, AVIVO_D1GRPH_UPDATE_LOCK); +	 +	RADEON_WRITE(AVIVO_D1GRPH_PRIMARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, fb_location); +	RADEON_WRITE(AVIVO_D1GRPH_SECONDARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, fb_location); +	RADEON_WRITE(AVIVO_D1GRPH_CONTROL + radeon_crtc->crtc_offset, fb_format); +	 +	RADEON_WRITE(AVIVO_D1GRPH_SURFACE_OFFSET_X + radeon_crtc->crtc_offset, 0); +	RADEON_WRITE(AVIVO_D1GRPH_SURFACE_OFFSET_Y + radeon_crtc->crtc_offset, 0); +	RADEON_WRITE(AVIVO_D1GRPH_X_START + radeon_crtc->crtc_offset, x); +	RADEON_WRITE(AVIVO_D1GRPH_Y_START + radeon_crtc->crtc_offset, y); +	RADEON_WRITE(AVIVO_D1GRPH_X_END + radeon_crtc->crtc_offset, x + crtc->mode.hdisplay); +	RADEON_WRITE(AVIVO_D1GRPH_Y_END + radeon_crtc->crtc_offset, y + crtc->mode.vdisplay); + +	fb_pitch_pixels = crtc->fb->pitch / (crtc->fb->bits_per_pixel / 8); +	RADEON_WRITE(AVIVO_D1GRPH_PITCH + radeon_crtc->crtc_offset, fb_pitch_pixels); +	RADEON_WRITE(AVIVO_D1GRPH_ENABLE + radeon_crtc->crtc_offset, 1); +	 +	/* unlock the grph regs */ +	RADEON_WRITE(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset, 0); +	 +	/* lock the mode regs */ +	RADEON_WRITE(AVIVO_D1SCL_UPDATE + radeon_crtc->crtc_offset, AVIVO_D1SCL_UPDATE_LOCK); +	 +	RADEON_WRITE(AVIVO_D1MODE_DESKTOP_HEIGHT + radeon_crtc->crtc_offset, +		     crtc->mode.vdisplay); +	RADEON_WRITE(AVIVO_D1MODE_VIEWPORT_START + radeon_crtc->crtc_offset, (x << 16) | y); +	RADEON_WRITE(AVIVO_D1MODE_VIEWPORT_SIZE + radeon_crtc->crtc_offset, +		     (crtc->mode.hdisplay << 16) | crtc->mode.vdisplay); +	/* unlock the mode regs */ +	RADEON_WRITE(AVIVO_D1SCL_UPDATE + radeon_crtc->crtc_offset, 0); +} + +void atombios_crtc_mode_set(struct drm_crtc *crtc, +			    struct drm_display_mode *mode, +			    struct drm_display_mode *adjusted_mode, +			    int x, int y) +{ +	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); +	struct drm_device *dev = crtc->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct drm_encoder *encoder; +	SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION crtc_timing; +	int pll_flags = 0; +	/* TODO color tiling */ + +	memset(&crtc_timing, 0, sizeof(crtc_timing)); + +	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { +		 +		 + +	} + +	crtc_timing.ucCRTC = radeon_crtc->crtc_id; +	crtc_timing.usH_Total = adjusted_mode->crtc_htotal; +	crtc_timing.usH_Disp = adjusted_mode->crtc_hdisplay; +	crtc_timing.usH_SyncStart = adjusted_mode->crtc_hsync_start; +	crtc_timing.usH_SyncWidth = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start; + +	crtc_timing.usV_Total = adjusted_mode->crtc_vtotal; +	crtc_timing.usV_Disp = adjusted_mode->crtc_vdisplay; +	crtc_timing.usV_SyncStart = adjusted_mode->crtc_vsync_start; +	crtc_timing.usV_SyncWidth = adjusted_mode->crtc_vsync_end - adjusted_mode->crtc_vsync_start; + +	if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) +		crtc_timing.susModeMiscInfo.usAccess |= ATOM_VSYNC_POLARITY; +	 +	if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) +		crtc_timing.susModeMiscInfo.usAccess |= ATOM_HSYNC_POLARITY; + +	if (adjusted_mode->flags & DRM_MODE_FLAG_CSYNC) +		crtc_timing.susModeMiscInfo.usAccess |= ATOM_COMPOSITESYNC; + +	if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) +		crtc_timing.susModeMiscInfo.usAccess |= ATOM_INTERLACE; + +	if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) +		crtc_timing.susModeMiscInfo.usAccess |= ATOM_DOUBLE_CLOCK_MODE; + +	if (radeon_is_avivo(dev_priv)) { +		atombios_crtc_set_base(crtc, x, y); +	} + +	atombios_crtc_set_pll(crtc, adjusted_mode, pll_flags); + +	atombios_crtc_set_timing(crtc, &crtc_timing); +} + +static bool atombios_crtc_mode_fixup(struct drm_crtc *crtc, +				   struct drm_display_mode *mode, +				   struct drm_display_mode *adjusted_mode) +{ +	return true; +} + + +static void atombios_crtc_prepare(struct drm_crtc *crtc) +{ +	atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); +} + +static void atombios_crtc_commit(struct drm_crtc *crtc) +{ +	atombios_crtc_dpms(crtc, DRM_MODE_DPMS_ON); +} + +static const struct drm_crtc_helper_funcs atombios_helper_funcs = { +	.dpms = atombios_crtc_dpms, +	.mode_fixup = atombios_crtc_mode_fixup, +	.mode_set = atombios_crtc_mode_set, +	.mode_set_base = atombios_crtc_set_base, +	.prepare = atombios_crtc_prepare, +	.commit = atombios_crtc_commit, +}; + +void radeon_atombios_init_crtc(struct drm_device *dev, +			       struct radeon_crtc *radeon_crtc) +{ +	if (radeon_crtc->crtc_id == 1) +		radeon_crtc->crtc_offset = AVIVO_D2CRTC_H_TOTAL - AVIVO_D1CRTC_H_TOTAL; +	drm_crtc_helper_add(&radeon_crtc->base, &atombios_helper_funcs); +} diff --git a/linux-core/drm-gem.txt b/linux-core/drm-gem.txt new file mode 100644 index 00000000..5cda87f8 --- /dev/null +++ b/linux-core/drm-gem.txt @@ -0,0 +1,805 @@ +                  The Graphics Execution Manager +	      Part of the Direct Rendering Manager +                  ============================== +		   +		 Keith Packard <keithp@keithp.com> +		   Eric Anholt <eric@anholt.net> +			   2008-5-9 + +Contents: + + 1. GEM Overview + 2. API overview and conventions + 3. Object Creation/Destruction + 4. Reading/writing contents + 5. Mapping objects to userspace + 6. Memory Domains + 7. Execution (Intel specific) + 8. Other misc Intel-specific functions + +1. Graphics Execution Manager Overview + +Gem is designed to manage graphics memory, control access to the graphics +device execution context and handle the essentially NUMA environment unique +to modern graphics hardware. Gem allows multiple applications to share +graphics device resources without the need to constantly reload the entire +graphics card. Data may be shared between multiple applications with gem +ensuring that the correct memory synchronization occurs. + +Graphics data can consume arbitrary amounts of memory, with 3D applications +constructing ever larger sets of textures and vertices. With graphics cards +memory space growing larger every year, and graphics APIs growing more +complex, we can no longer insist that each application save a complete copy +of their graphics state so that the card can be re-initialized from user +space at each context switch. Ensuring that graphics data remains persistent +across context switches allows applications significant new functionality +while also improving performance for existing APIs. + +Modern linux desktops include significant 3D rendering as a fundemental +component of the desktop image construction process. 2D and 3D applications +paint their content to offscreen storage and the central 'compositing +manager' constructs the final screen image from those window contents.  This +means that pixel image data from these applications must move within reach +of the compositing manager and used as source operands for screen image +rendering operations. + +Gem provides simple mechanisms to manage graphics data and control execution +flow within the linux operating system. Using many existing kernel +subsystems, it does this with a modest amount of code. + +2. API Overview and Conventions + +All APIs here are defined in terms of ioctls appplied to the DRM file +descriptor. To create and manipulate objects, an application must be +'authorized' using the DRI or DRI2 protocols with the X server. To relax +that, we will need to implement some better access control mechanisms within +the hardware portion of the driver to prevent inappropriate +cross-application data access. + +Any DRM driver which does not support GEM will return -ENODEV for all of +these ioctls. Invalid object handles return -EINVAL. Invalid object names +return -ENOENT. Other errors are as documented in the specific API below. + +To avoid the need to translate ioctl contents on mixed-size systems (with +32-bit user space running on a 64-bit kernel), the ioctl data structures +contain explicitly sized objects, using 64-bits for all size and pointer +data and 32-bits for identifiers. In addition, the 64-bit objects are all +carefully aligned on 64-bit boundaries. Because of this, all pointers in the +ioctl data structures are passed as uint64_t values. Suitable casts will +be necessary. + +One significant operation which is explicitly left out of this API is object +locking. Applications are expected to perform locking of shared objects +outside of the GEM api. This kind of locking is not necessary to safely +manipulate the graphics engine, and with multiple objects interacting in +unknown ways, per-object locking would likely introduce all kinds of +lock-order issues. Punting this to the application seems like the only +sensible plan. Given that DRM already offers a global lock on the hardware, +this doesn't change the current situation. + +3. Object Creation and Destruction + +Gem provides explicit memory management primitives. System pages are +allocated when the object is created, either as the fundemental storage for +hardware where system memory is used by the graphics processor directly, or +as backing store for graphics-processor resident memory. + +Objects are referenced from user space using handles. These are, for all +intents and purposes, equivalent to file descriptors. We could simply use +file descriptors were it not for the small limit (1024) of file descriptors +available to applications, and for the fact that the X server (a rather +significant user of this API) uses 'select' and has a limited maximum file +descriptor for that operation. Given the ability to allocate more file +descriptors, and given the ability to place these 'higher' in the file +descriptor space, we'd love to simply use file descriptors. + +Objects may be published with a name so that other applications can access +them. The name remains valid as long as the object exists. Right now, our +DRI APIs use 32-bit integer names, so that's what we expose here + + A. Creation + +		struct drm_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; +		}; +	 +		/* usage */ +    		create.size = 16384; +		ret = ioctl (fd, DRM_IOCTL_GEM_CREATE, &create); +		if (ret == 0) +			return create.handle; + +	Note that the size is rounded up to a page boundary, and that +	the rounded-up size is returned in 'size'. No name is assigned to +	this object, making it local to this process. + +	If insufficient memory is availabe, -ENOMEM will be returned. + + B. Closing + +		struct drm_gem_close { +			/** Handle of the object to be closed. */ +			uint32_t handle; +			uint32_t pad; +		}; +		 + +		/* usage */ +		close.handle = <handle>; +		ret = ioctl (fd, DRM_IOCTL_GEM_CLOSE, &close); + +	This call makes the specified handle invalid, and if no other +	applications are using the object, any necessary graphics hardware +	synchronization is performed and the resources used by the object +	released. + + C. Naming + +		struct drm_gem_flink { +			/** Handle for the object being named */ +			uint32_t handle; +		 +			/** Returned global name */ +			uint32_t name; +		}; +		 +		/* usage */ +		flink.handle = <handle>; +		ret = ioctl (fd, DRM_IOCTL_GEM_FLINK, &flink); +		if (ret == 0) +			return flink.name; + +	Flink creates a name for the object and returns it to the +	application. This name can be used by other applications to gain +	access to the same object. + + D. Opening by name + +		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; +		}; +		 +		/* usage */ +		open.name = <name>; +		ret = ioctl (fd, DRM_IOCTL_GEM_OPEN, &open); +		if (ret == 0) { +			*sizep = open.size; +			return open.handle; +		} + +	Open accesses an existing object and returns a handle for it. If the +	object doesn't exist, -ENOENT is returned. The size of the object is +	also returned. This handle has all the same capabilities as the +	handle used to create the object. In particular, the object is not +	destroyed until all handles are closed. + +4. Basic read/write operations + +By default, gem objects are not mapped to the applications address space, +getting data in and out of them is done with I/O operations instead. This +allows the data to reside in otherwise unmapped pages, including pages in +video memory on an attached discrete graphics card. In addition, using +explicit I/O operations allows better control over cache contents, as +graphics devices are generally not cache coherent with the CPU, mapping +pages used for graphics into an application address space requires the use +of expensive cache flushing operations. Providing direct control over +graphics data access ensures that data are handled in the most efficient +possible fashion. + + A. Reading + +		struct drm_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. */ +			uint64_t data_ptr;	/* void * */ +		}; + +	This copies data into the specified object at the specified +	position. Any necessary graphics device synchronization and +	flushing will be done automatically. +		 +		struct drm_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. */ +			uint64_t data_ptr;	/* void * */ +		}; +		 +	This copies data out of the specified object into the +	waiting user memory. Again, device synchronization will +	be handled by the kernel to ensure user space sees a +	consistent view of the graphics device. + +5. Mapping objects to user space + +For most objects, reading/writing is the preferred interaction mode. +However, when the CPU is involved in rendering to cover deficiencies in +hardware support for particular operations, the CPU will want to directly +access the relevant objects.  + +Because mmap is fairly heavyweight, we allow applications to retain maps to +objects persistently and then update how they're using the memory through a +separate interface. Applications which fail to use this separate interface +may exhibit unpredictable behaviour as memory consistency will not be +preserved. + + A. Mapping + +		struct drm_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 */ +			uint64_t addr_ptr;	/* void * */ +		}; +		 +		/* usage */ +		mmap.handle = <handle>; +		mmap.offset = <offset>; +		mmap.size = <size>; +		ret = ioctl (fd, DRM_IOCTL_GEM_MMAP, &mmap); +		if (ret == 0) +			return (void *) (uintptr_t) mmap.addr_ptr; + + + B. Unmapping + +		munmap (addr, length); + +	Nothing strange here, just use the normal munmap syscall. + +6. Memory Domains + +Graphics devices remain a strong bastion of non cache-coherent memory. As a +result, accessing data through one functional unit will end up loading that +cache with data which then needs to be manually synchronized when that data +is used with another functional unit. + +Tracking where data are resident is done by identifying how functional units +deal with caches. Each cache is labeled as a separate memory domain. Then, +each sequence of operations is expected to load data into various read +domains and leave data in at most one write domain. Gem tracks the read and +write memory domains of each object and performs the necessary +synchronization operations when objects move from one domain set to another. + +For example, if operation 'A' constructs an image that is immediately used +by operation 'B', then when the read domain for 'B' is not the same as the +write domain for 'A', then the write domain must be flushed, and the read +domain invalidated. If these two operations are both executed in the same +command queue, then the flush operation can go inbetween them in the same +queue, avoiding any kind of CPU-based synchronization and leaving the GPU to +do the work itself. + +6.1 Memory Domains (GPU-independent) + + * DRM_GEM_DOMAIN_CPU. + + Objects in this domain are using caches which are connected to the CPU. + Moving objects from non-CPU domains into the CPU domain can involve waiting + for the GPU to finish with operations using this object. Moving objects + from this domain to a GPU domain can involve flushing CPU caches and chipset + buffers. + +6.1 GPU-independent memory domain ioctl + +This ioctl is independent of the GPU in use. So far, no use other than +synchronizing objects to the CPU domain have been found; if that turns out +to be generally true, this ioctl may be simplified further. +    + A. Explicit domain control + +		struct drm_gem_set_domain { +			/** Handle for the object */ +			uint32_t handle; +		 +			/** New read domains */ +			uint32_t read_domains; +		 +			/** New write domain */ +			uint32_t write_domain; +		}; + +		/* usage */ +		set_domain.handle = <handle>; +		set_domain.read_domains = <read_domains>; +		set_domain.write_domain = <write_domain>; +		ret = ioctl (fd, DRM_IOCTL_GEM_SET_DOMAIN, &set_domain); +		 +	When the application wants to explicitly manage memory domains for +	an object, it can use this function. Usually, this is only used +	when the application wants to synchronize object contents between +	the GPU and CPU-based application rendering. In that case, +	the <read_domains> would be set to DRM_GEM_DOMAIN_CPU, and if the +	application were going to write to the object, the <write_domain> +	would also be set to DRM_GEM_DOMAIN_CPU. After the call, gem +	guarantees that all previous rendering operations involving this +	object are complete. The application is then free to access the +	object through the address returned by the mmap call. Afterwards, +	when the application again uses the object through the GPU, any +	necessary CPU flushing will occur and the object will be correctly +	synchronized with the GPU. + +	Note that this synchronization is not required for any accesses +	going through the driver itself. The pread, pwrite and execbuffer +	ioctls all perform the necessary domain management internally. +	Explicit synchronization is only necessary when accessing the object +	through the mmap'd address. + +7. Execution (Intel specific) + +Managing the command buffers is inherently chip-specific, so the core of gem +doesn't have any intrinsic functions. Rather, execution is left to the +device-specific portions of the driver. + +The Intel DRM_I915_GEM_EXECBUFFER ioctl takes a list of gem objects, all of +which are mapped to the graphics device. The last object in the list is the +command buffer. + +7.1. Relocations +  +Command buffers often refer to other objects, and to allow the kernel driver +to move objects around, a sequence of relocations is associated with each +object. Device-specific relocation operations are used to place the +target-object relative value into the object. + +The Intel driver has a single relocation type: + +		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; +		}; +		 +	'target_handle', the handle to the target object. This object must +	be one of the objects listed in the execbuffer request or +	bad things will happen. The kernel doesn't check for this. + +	'offset' is where, in the source object, the relocation data +	are written. Each relocation value is a 32-bit value consisting +	of the location of the target object in the GPU memory space plus +	the 'delta' value included in the relocation. + +	'presumed_offset' is where user-space believes the target object +	lies in GPU memory space. If this value matches where the object +	actually is, then no relocation data are written, the kernel +	assumes that user space has set up data in the source object +	using this presumption. This offers a fairly important optimization +	as writing relocation data requires mapping of the source object +	into the kernel memory space. + +	'read_domains' and 'write_domains' list the usage by the source +	object of the target object. The kernel unions all of the domain +	information from all relocations in the execbuffer request. No more +	than one write_domain is allowed, otherwise an EINVAL error is +	returned. read_domains must contain write_domain. This domain +	information is used to synchronize buffer contents as described +	above in the section on domains. + +7.1.1 Memory Domains (Intel specific) + +The Intel GPU has several internal caches which are not coherent and hence +require explicit synchronization. Memory domains provide the necessary data +to synchronize what is needed while leaving other cache contents intact. + + * DRM_GEM_DOMAIN_I915_RENDER. +   The GPU 3D and 2D rendering operations use a unified rendering cache, so +   operations doing 3D painting and 2D blts will use this domain +    + * DRM_GEM_DOMAIN_I915_SAMPLER +   Textures are loaded by the sampler through a separate cache, so +   any texture reading will use this domain. Note that the sampler +   and renderer use different caches, so moving an object from render target +   to texture source will require a domain transfer. +    + * DRM_GEM_DOMAIN_I915_COMMAND +   The command buffer doesn't have an explicit cache (although it does +   read ahead quite a bit), so this domain just indicates that the object +   needs to be flushed to the GPU. +    + * DRM_GEM_DOMAIN_I915_INSTRUCTION +   All of the programs on Gen4 and later chips use an instruction cache to +   speed program execution. It must be explicitly flushed when new programs +   are written to memory by the CPU. + + * DRM_GEM_DOMAIN_I915_VERTEX +   Vertex data uses two different vertex caches, but they're +   both flushed with the same instruction. + +7.2 Execution object list (Intel specific) + +		struct drm_i915_gem_exec_object { +			/** +			 * User's handle for a buffer to be bound into the GTT +			 * for this operation. +			 */ +			uint32_t handle; +			 +			/** +			 * List of relocations to be performed on this buffer +			 */ +			uint32_t relocation_count; +			/* struct drm_i915_gem_relocation_entry *relocs */ +			uint64_t relocs_ptr; +			 +			/**  +			 * Required alignment in graphics aperture  +			 */ +			uint64_t alignment; +		 +			/** +			 * Returned value of the updated offset of the object, +			 * for future presumed_offset writes. +			 */ +			uint64_t offset; +		}; +			 +	Each object involved in a particular execution operation must be +	listed using one of these structures. + +	'handle' references the object. + +	'relocs_ptr' is a user-mode pointer to a array of 'relocation_count' +	drm_i915_gem_relocation_entry structs (see above) that +	define the relocations necessary in this buffer. Note that all +	relocations must reference other exec_object structures in the same +	execbuffer ioctl and that those other buffers must come earlier in +	the exec_object array. In other words, the dependencies mapped by the +	exec_object relocations must form a directed acyclic graph. + +	'alignment' is the byte alignment necessary for this buffer. Each +	object has specific alignment requirements, as the kernel doesn't +	know what each object is being used for, those requirements must be +	provided by user mode. If an object is used in two different ways, +	it's quite possible that the alignment requirements will differ. + +	'offset' is a return value, receiving the location of the object +	during this execbuffer operation. The application should use this +	as the presumed offset in future operations; if the object does not +	move, then kernel need not write relocation data. + +7.3 Execbuffer ioctl (Intel specific) + +		struct drm_i915_gem_execbuffer { +			/** +			 * List of buffers to be validated with their +			 * relocations to be performend on them. +			 * +			 * These buffers must be listed in an order such that +			 * all relocations a buffer is performing refer to +			 * buffers that have already appeared in the validate +			 * list. +			 */ +			/* struct drm_i915_gem_validate_entry *buffers */ +			uint64_t buffers_ptr; +			uint32_t buffer_count; +		 +			/** +			 * Offset in the batchbuffer to start execution from. +			 */ +			uint32_t batch_start_offset; +			 +			/** +			 * Bytes used in batchbuffer from batch_start_offset +			 */ +			uint32_t batch_len; +			uint32_t DR1; +			uint32_t DR4; +			uint32_t num_cliprects; +			uint64_t cliprects_ptr;	/* struct drm_clip_rect *cliprects */ +		}; +		 + +	'buffers_ptr' is a user-mode pointer to an array of 'buffer_count' +	drm_i915_gem_exec_object structures which contains the complete set +	of objects required for this execbuffer operation. The last entry in +	this array, the 'batch buffer', is the buffer of commands which will +	be linked to the ring and executed. + +	'batch_start_offset' is the byte offset within the batch buffer which +	contains the first command to execute. So far, we haven't found a +	reason to use anything other than '0' here, but the thought was that +	some space might be allocated for additional initialization which +	could be skipped in some cases. This must be a multiple of 4. + +	'batch_len' is the length, in bytes, of the data to be executed +	(i.e., the amount of data after batch_start_offset). This must +	be a multiple of 4. + +	'num_cliprects' and 'cliprects_ptr' reference an array of +	drm_clip_rect structures that is num_cliprects long. The entire +	batch buffer will be executed multiple times, once for each +	rectangle in this list. If num_cliprects is 0, then no clipping +	rectangle will be set. + +	'DR1' and 'DR4' are portions of the 3DSTATE_DRAWING_RECTANGLE +	command which will be queued when this operation is clipped +	(num_cliprects != 0). + +		DR1 bit		definition +		31		Fast Scissor Clip Disable (debug only). +				Disables a hardware optimization that +				improves performance. This should have +				no visible effect, other than reducing +				performance +				 +		30		Depth Buffer Coordinate Offset Disable. +				This disables the addition of the +				depth buffer offset bits which are used +				to change the location of the depth buffer +				relative to the front buffer. + +		27:26		X Dither Offset. Specifies the X pixel +				offset to use when accessing the dither table +				 +		25:24		Y Dither Offset. Specifies the Y pixel +				offset to use when accessing the dither +				table. + +		DR4 bit		definition +		31:16		Drawing Rectangle Origin Y. Specifies the Y +				origin of coordinates relative to the +				draw buffer. + +		15:0		Drawing Rectangle Origin X. Specifies the X +				origin of coordinates relative to the +				draw buffer. + +	As you can see, these two fields are necessary for correctly +	offsetting drawing within a buffer which contains multiple surfaces. +	Note that DR1 is only used on Gen3 and earlier hardware and that +	newer hardware sticks the dither offset elsewhere. + +7.3.1 Detailed Execution Description + +	Execution of a single batch buffer requires several preparatory +	steps to make the objects visible to the graphics engine and resolve +	relocations to account for their current addresses. + + A. Mapping and Relocation + +	Each exec_object structure in the array is examined in turn.  +	 +	If the object is not already bound to the GTT, it is assigned a +	location in the graphics address space. If no space is available in +	the GTT, some other object will be evicted. This may require waiting +	for previous execbuffer requests to complete before that object can +	be unmapped. With the location assigned, the pages for the object +	are pinned in memory using find_or_create_page and the GTT entries +	updated to point at the relevant pages using drm_agp_bind_pages. +	 +	Then the array of relocations is traversed. Each relocation record +	looks up the target object and, if the presumed offset does not +	match the current offset (remember that this buffer has already been +	assigned an address as it must have been mapped earlier), the +	relocation value is computed using the current offset.  If the +	object is currently in use by the graphics engine, writing the data +	out must be preceeded by a delay while the object is still busy. +	Once it is idle, then the page containing the relocation is mapped +	by the CPU and the updated relocation data written out. + +	The read_domains and write_domain entries in each relocation are +	used to compute the new read_domains and write_domain values for the +	target buffers. The actual execution of the domain changes must wait +	until all of the exec_object entries have been evaluated as the +	complete set of domain information will not be available until then. +	 + B. Memory Domain Resolution + +	After all of the new memory domain data has been pulled out of the +	relocations and computed for each object, the list of objects is +	again traversed and the new memory domains compared against the +	current memory domains. There are two basic operations involved here: + + 	 * Flushing the current write domain. If the new read domains +	   are not equal to the current write domain, then the current +	   write domain must be flushed. Otherwise, reads will not see data +	   present in the write domain cache. In addition, any new read domains +	   other than the current write domain must be invalidated to ensure +	   that the flushed data are re-read into their caches. + +	 * Invaliding new read domains. Any domains which were not currently +	   used for this object must be invalidated as old objects which +	   were mapped at the same location may have stale data in the new +	   domain caches. + +	If the CPU cache is being invalidated and some GPU cache is being +	flushed, then we'll have to wait for rendering to complete so that +	any pending GPU writes will be complete before we flush the GPU +	cache. + +	If the CPU cache is being flushed, then we use 'clflush' to get data +	written from the CPU. + +	Because the GPU caches cannot be partially flushed or invalidated, +	we don't actually flush them during this traversal stage. Rather, we +	gather the invalidate and flush bits up in the device structure. + +	Once all of the object domain changes have been evaluated, then the +	gathered invalidate and flush bits are examined. For any GPU flush +	operations, we emit a single MI_FLUSH command that performs all of +	the necessary flushes. We then look to see if the CPU cache was +	flushed. If so, we use the chipset flush magic (writing to a special +	page) to get the data out of the chipset and into memory. + + C. Queuing Batch Buffer to the Ring + +	With all of the objects resident in graphics memory space, and all +	of the caches prepared with appropriate data, the batch buffer +	object can be queued to the ring. If there are clip rectangles, then +	the buffer is queued once per rectangle, with suitable clipping +	inserted into the ring just before the batch buffer. + + D. Creating an IRQ Cookie + +	Right after the batch buffer is placed in the ring, a request to +	generate an IRQ is added to the ring along with a command to write a +	marker into memory. When the IRQ fires, the driver can look at the +	memory location to see where in the ring the GPU has passed. This +	magic cookie value is stored in each object used in this execbuffer +	command; it is used whereever you saw 'wait for rendering' above in +	this document. + + E. Writing back the new object offsets + +	So that the application has a better idea what to use for +	'presumed_offset' values later, the current object offsets are +	written back to the exec_object structures. + + +8. Other misc Intel-specific functions. + +To complete the driver, a few other functions were necessary. + +8.1 Initialization from the X server + +As the X server is currently responsible for apportioning memory between 2D +and 3D, it must tell the kernel which region of the GTT aperture is +available for 3D objects to be mapped into. + +		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; +		}; +		/* usage */ +		init.gtt_start = <gtt_start>; +		init.gtt_end = <gtt_end>; +		ret = ioctl (fd, DRM_IOCTL_I915_GEM_INIT, &init); + +	The GTT aperture between gtt_start and gtt_end will be used to map +	objects. This also tells the kernel that the ring can be used, +	pulling the ring addresses from the device registers. + +8.2 Pinning objects in the GTT + +For scan-out buffers and the current shared depth and back buffers, we need +to have them always available in the GTT, at least for now. Pinning means to +lock their pages in memory along with keeping them at a fixed offset in the +graphics aperture. These operations are available only to root. +		 +		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; +		}; + +		/* usage */ +		pin.handle = <handle>; +		pin.alignment = <alignment>; +		ret = ioctl (fd, DRM_IOCTL_I915_GEM_PIN, &pin); +		if (ret == 0) +			return pin.offset; + +	Pinning an object ensures that it will not be evicted from the GTT +	or moved. It will stay resident until destroyed or unpinned. +		 +		struct drm_i915_gem_unpin { +			/** Handle of the buffer to be unpinned. */ +			uint32_t handle; +			uint32_t pad; +		}; +		 +		/* usage */ +		unpin.handle = <handle>; +		ret = ioctl (fd, DRM_IOCTL_I915_GEM_UNPIN, &unpin); +		 +	Unpinning an object makes it possible to evict this object from the +	GTT. It doesn't ensure that it will be evicted, just that it may. + diff --git a/linux-core/drmP.h b/linux-core/drmP.h index 19168cd7..1e5838ee 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -54,6 +54,8 @@  #include <linux/smp_lock.h>	/* For (un)lock_kernel */  #include <linux/dma-mapping.h>  #include <linux/mm.h> +#include <linux/swap.h> +#include <linux/kref.h>  #include <linux/pagemap.h>  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)  #include <linux/mutex.h> @@ -89,6 +91,10 @@  struct drm_device;  struct drm_file; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) +typedef unsigned long uintptr_t; +#endif +  /* If you want the memory alloc debug functionality, change define below */  /* #define DEBUG_MEMORY */ @@ -107,7 +113,8 @@ struct drm_file;  #define DRIVER_IRQ_SHARED  0x80  #define DRIVER_DMA_QUEUE   0x100  #define DRIVER_FB_DMA      0x200 - +#define DRIVER_MODESET     0x400 +#define DRIVER_GEM	   0x800  /*@}*/ @@ -261,11 +268,11 @@ struct drm_file;   */  #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 )	{			\ +	if ( !_DRM_LOCK_IS_HELD( file_priv->master->lock.hw_lock->lock ) ||		\ +	     file_priv->master->lock.file_priv != file_priv )	{			\  		DRM_ERROR( "%s called without lock held, held  %d owner %p %p\n",\ -			   __FUNCTION__, _DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ),\ -			   dev->lock.file_priv, file_priv );		\ +			   __FUNCTION__, _DRM_LOCK_IS_HELD( file_priv->master->lock.hw_lock->lock ),\ +			   file_priv->master->lock.file_priv, file_priv );		\  		return -EINVAL;						\  	}								\  } while (0) @@ -299,6 +306,7 @@ typedef int drm_ioctl_compat_t(struct file *filp, unsigned int cmd,  #define DRM_AUTH        0x1  #define DRM_MASTER      0x2  #define DRM_ROOT_ONLY   0x4 +#define DRM_CONTROL_ALLOW 0x8 // allow ioctl to operate on control node   struct drm_ioctl_desc {  	unsigned int cmd; @@ -408,14 +416,12 @@ enum drm_ref_type {  /** File private data */  struct drm_file {  	int authenticated; -	int master;  	pid_t pid;  	uid_t uid;  	drm_magic_t magic;  	unsigned long ioctl_count;  	struct list_head lhead;  	struct drm_minor *minor; -	int remove_auth_on_close;  	unsigned long lock_count;  	/* @@ -427,9 +433,19 @@ struct drm_file {  	struct list_head refd_objects; +	/** Mapping of mm object handles to object pointers. */ +	struct idr object_idr; +	/** Lock for synchronization of access to object_idr. */ +	spinlock_t table_lock; +  	struct drm_open_hash refd_object_hash[_DRM_NO_REF_TYPES];  	struct file *filp;  	void *driver_priv; + +	int is_master; /* this file private is a master for a minor */ +	struct drm_master *master; /* master this node is currently associated with +				      N.B. not always minor->master */ +	struct list_head fbs;  };  /** Wait queue */ @@ -464,6 +480,11 @@ struct drm_lock_data {  	uint32_t kernel_waiters;  	uint32_t user_waiters;  	int idle_has_lock; +	/** +	 * Boolean signaling that the lock is held on behalf of the +	 * file_priv client by the kernel in an ioctl handler. +	 */ +	int kernel_held;  };  /** @@ -539,17 +560,17 @@ struct drm_sigdata {   * Generic memory manager structs   */ -struct drm_mm_node { +struct drm_memrange_node {  	struct list_head fl_entry;  	struct list_head ml_entry;  	int free;  	unsigned long start;  	unsigned long size; -	struct drm_mm *mm; +	struct drm_memrange *mm;  	void *private;  }; -struct drm_mm { +struct drm_memrange {  	struct list_head fl_entry;  	struct list_head ml_entry;  }; @@ -563,7 +584,9 @@ struct drm_map_list {  	struct drm_hash_item hash;  	struct drm_map *map;			/**< mapping */  	uint64_t user_token; -	struct drm_mm_node *file_offset_node; +	struct drm_master *master; /** if this map is associated with a specific +				       master */ +	struct drm_memrange_node *file_offset_node;  };  typedef struct drm_map drm_local_map_t; @@ -584,6 +607,13 @@ struct drm_vbl_sig {  	struct task_struct *task;  }; +struct drm_hotplug_sig { +	struct list_head head; +	unsigned int counter; +	struct siginfo info; +	struct task_struct *task; +}; +  /* location of GART table */  #define DRM_ATI_GART_MAIN 1  #define DRM_ATI_GART_FB   2 @@ -604,7 +634,80 @@ struct drm_ati_pcigart_info {  	int table_size;  }; +/** + * This structure defines the drm_mm memory object, which will be used by the + * DRM for its buffer objects. + */ +struct drm_gem_object { +	/** Reference count of this object */ +	struct kref refcount; + +	/** Handle count of this object. Each handle also holds a reference */ +	struct kref handlecount; + +	/** Related drm device */ +	struct drm_device *dev; +	 +	/** File representing the shmem storage */ +	struct file *filp; + +	/** +	 * Size of the object, in bytes.  Immutable over the object's +	 * lifetime. +	 */ +	size_t size; + +	/** +	 * Global name for this object, starts at 1. 0 means unnamed. +	 * Access is covered by the object_name_lock in the related drm_device +	 */ +	int name; + +	/** +	 * Memory domains. These monitor which caches contain read/write data +	 * related to the object. When transitioning from one set of domains +	 * to another, the driver is called to ensure that caches are suitably +	 * flushed and invalidated +	 */ +	uint32_t	read_domains; +	uint32_t	write_domain; + +	/** +	 * While validating an exec operation, the +	 * new read/write domain values are computed here. +	 * They will be transferred to the above values +	 * at the point that any cache flushing occurs +	 */ +	uint32_t	pending_read_domains; +	uint32_t	pending_write_domain; + +	void *driver_private; +}; +  #include "drm_objects.h" +#include "drm_crtc.h" + +/* per-master structure */ +struct drm_master { +	 +	struct list_head head; /**< each minor contains a list of masters */ +	struct drm_minor *minor; /**< link back to minor we are a master for */ + +	char *unique;			/**< Unique identifier: e.g., busid */ +	int unique_len;			/**< Length of unique field */ + +	int blocked;			/**< Blocked due to VC switch? */ + +	/** \name Authentication */ +	/*@{ */ +	struct drm_open_hash magiclist; +	struct list_head magicfree; +	/*@} */ + +	struct drm_lock_data lock;		/**< Information on hardware lock */ + +	void *driver_priv; /**< Private structure for driver to use */ +};  /**   * DRM driver structure. This structure represent the common code for @@ -705,6 +808,22 @@ struct drm_driver {  	void (*set_version) (struct drm_device *dev,  			     struct drm_set_version *sv); +	/* Master routines */ +	int (*master_create)(struct drm_device *dev, struct drm_master *master); +	void (*master_destroy)(struct drm_device *dev, struct drm_master *master); + +	int (*proc_init)(struct drm_minor *minor); +	void (*proc_cleanup)(struct drm_minor *minor); + +	/** +	 * Driver-specific constructor for drm_gem_objects, to set up +	 * obj->driver_private. +	 * +	 * Returns 0 on success. +	 */ +	int (*gem_init_object) (struct drm_gem_object *obj); +	void (*gem_free_object) (struct drm_gem_object *obj); +  	struct drm_fence_driver *fence_driver;  	struct drm_bo_driver *bo_driver; @@ -726,6 +845,8 @@ struct drm_driver {  #define DRM_MINOR_UNASSIGNED 0  #define DRM_MINOR_LEGACY 1 +#define DRM_MINOR_CONTROL 2 +#define DRM_MINOR_RENDER 3  /**   * DRM minor structure. This structure represents a drm minor number. @@ -736,8 +857,15 @@ struct drm_minor {  	dev_t device;			/**< Device number for mknod */  	struct device kdev;		/**< Linux device */  	struct drm_device *dev; +	/* for render nodes */  	struct proc_dir_entry *dev_root;  /**< proc directory entry */  	struct class_device *dev_class; + +	/* for control nodes - a pointer to the current master for this control node */ +	struct drm_master *master; /* currently active master for this node */ +	struct list_head master_list; + +	struct drm_mode_group mode_group;  }; @@ -746,13 +874,9 @@ struct drm_minor {   * may contain multiple heads.   */  struct drm_device { -	char *unique;			/**< Unique identifier: e.g., busid */ -	int unique_len;			/**< Length of unique field */  	char *devname;			/**< For /proc/interrupts */  	int if_version;			/**< Highest interface version set */ -	int blocked;			/**< Blocked due to VC switch? */ -  	/** \name Locks */  	/*@{ */  	spinlock_t count_lock;		/**< For inuse, drm_device::open_count, drm_device::buf_use */ @@ -775,19 +899,13 @@ struct drm_device {  	atomic_t counts[15];  	/*@} */ -	/** \name Authentication */ -	/*@{ */ -	struct list_head filelist; -	struct drm_open_hash magiclist; -	struct list_head magicfree; -	/*@} */  	/** \name Memory management */  	/*@{ */  	struct list_head maplist;	/**< Linked list of regions */  	int map_count;			/**< Number of mappable regions */  	struct drm_open_hash map_hash;       /**< User token hash table for maps */ -	struct drm_mm offset_manager;        /**< User token manager */ +	struct drm_memrange offset_manager;  /**< User token manager */  	struct drm_open_hash object_hash;    /**< User token hash table for objects */  	struct address_space *dev_mapping;  /**< For unmap_mapping_range() */  	struct page *ttm_dummy_page; @@ -801,7 +919,9 @@ struct drm_device {  	struct idr ctx_idr;  	struct list_head vmalist;	/**< List of vmas (for debugging) */ -	struct drm_lock_data lock;		/**< Information on hardware lock */ + +	struct list_head filelist; +  	/*@} */  	/** \name DMA queues (contexts) */ @@ -813,6 +933,7 @@ struct drm_device {  	struct drm_device_dma *dma;		/**< Optional pointer for DMA support */  	/*@} */ +  	/** \name Context support */  	/*@{ */  	int irq;			/**< Interrupt used by board */ @@ -829,6 +950,15 @@ struct drm_device {  	struct work_struct work; +	/** \name HOTPLUG IRQ support */ +	/*@{ */ +	wait_queue_head_t hotplug_queue;	/**< HOTPLUG wait queue */ +	spinlock_t hotplug_lock; +	struct list_head *hotplug_sigs;		/**< signal list to send on HOTPLUG */ +	atomic_t hotplug_signal_pending;	/* number of signals pending on all crtcs*/ + +	/*@} */ +  	/** \name VBLANK IRQ support */  	/*@{ */ @@ -882,6 +1012,9 @@ struct drm_device {  	struct drm_driver *driver;  	drm_local_map_t *agp_buffer_map;  	unsigned int agp_buffer_token; + +	/* minor number for control node */ +	struct drm_minor *control;  	struct drm_minor *primary;		/**< render type primary screen head */  	struct drm_fence_manager fm; @@ -892,6 +1025,24 @@ struct drm_device {  	spinlock_t drw_lock;  	struct idr drw_idr;  	/*@} */ + +	/* DRM mode setting */ +	struct drm_mode_config mode_config; + +	/** \name GEM information */ +	/*@{ */ +	spinlock_t object_name_lock; +	struct idr object_name_idr; +	atomic_t object_count; +	atomic_t object_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;	/* domains pending invalidation */ +	uint32_t flush_domains;		/* domains pending flush */ +	/*@} */  };  #if __OS_HAS_AGP @@ -902,7 +1053,18 @@ struct drm_agp_ttm_backend {  	int populated;  };  #endif - +struct ati_pcigart_ttm_backend { +	struct drm_ttm_backend backend; +	int populated; +	void (*gart_flush_fn)(struct drm_device *dev); +	struct drm_ati_pcigart_info *gart_info; +	unsigned long offset; +	struct page **pages; +	int num_pages; +	int bound; +	struct drm_device *dev; +}; +extern struct drm_ttm_backend *ati_pcigart_init_ttm(struct drm_device *dev, struct drm_ati_pcigart_info *info, void (*gart_flush_fn)(struct drm_device *dev));  static __inline__ int drm_core_check_feature(struct drm_device *dev,  					     int feature) @@ -1007,6 +1169,10 @@ extern void drm_free_pages(unsigned long address, int order, int area);  extern DRM_AGP_MEM *drm_alloc_agp(struct drm_device *dev, int pages, u32 type);  extern int drm_free_agp(DRM_AGP_MEM * handle, int pages);  extern int drm_bind_agp(DRM_AGP_MEM * handle, unsigned int start); +extern DRM_AGP_MEM *drm_agp_bind_pages(struct drm_device *dev, +					      struct page **pages, +					      unsigned long num_pages, +					      uint32_t gtt_offset);  extern int drm_unbind_agp(DRM_AGP_MEM * handle);  extern void drm_free_memctl(size_t size); @@ -1147,12 +1313,16 @@ extern void drm_driver_irq_preinstall(struct drm_device *dev);  extern void drm_driver_irq_postinstall(struct drm_device *dev);  extern void drm_driver_irq_uninstall(struct drm_device *dev); +extern int drm_hotplug_init(struct drm_device *dev); +extern int drm_wait_hotplug(struct drm_device *dev, void *data, struct drm_file *filp);  extern int drm_vblank_init(struct drm_device *dev, int num_crtcs);  extern int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *filp); +extern int drm_wait_hotplug(struct drm_device *dev, void *data, struct drm_file *filp);  extern int drm_vblank_wait(struct drm_device * dev, unsigned int *vbl_seq);  extern void drm_locked_tasklet(struct drm_device *dev, void(*func)(struct drm_device*));  extern u32 drm_vblank_count(struct drm_device *dev, int crtc);  extern void drm_handle_vblank(struct drm_device *dev, int crtc); +extern void drm_handle_hotplug(struct drm_device *dev);  extern int drm_vblank_get(struct drm_device *dev, int crtc);  extern void drm_vblank_put(struct drm_device *dev, int crtc); @@ -1197,10 +1367,16 @@ extern int drm_agp_unbind_memory(DRM_AGP_MEM * handle);  extern struct drm_ttm_backend *drm_agp_init_ttm(struct drm_device *dev);  extern void drm_agp_chipset_flush(struct drm_device *dev);  				/* Stub support (drm_stub.h) */ +extern int drm_setmaster_ioctl(struct drm_device *dev, void *data, +			       struct drm_file *file_priv); +extern int drm_dropmaster_ioctl(struct drm_device *dev, void *data, +				struct drm_file *file_priv); +extern struct drm_master *drm_get_master(struct drm_minor *minor); +extern void drm_put_master(struct drm_master *master);  extern int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent,  		     struct drm_driver *driver);  extern int drm_put_dev(struct drm_device *dev); -extern int drm_put_minor(struct drm_minor **minor); +extern int drm_put_minor(struct drm_device *dev, struct drm_minor **p);  extern unsigned int drm_debug; /* 1 to enable debug output */  extern struct class *drm_class; @@ -1226,6 +1402,8 @@ extern int drm_sg_free(struct drm_device *dev, void *data,  			       /* ATI PCIGART support (ati_pcigart.h) */  extern int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info);  extern int drm_ati_pcigart_cleanup(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info); +extern int drm_ati_alloc_pcigart_table(struct drm_device *dev, +				       struct drm_ati_pcigart_info *gart_info);  extern drm_dma_handle_t *drm_pci_alloc(struct drm_device *dev, size_t size,  			   size_t align, dma_addr_t maxaddr); @@ -1237,29 +1415,101 @@ struct drm_sysfs_class;  extern struct class *drm_sysfs_create(struct module *owner, char *name);  extern void drm_sysfs_destroy(void);  extern int drm_sysfs_device_add(struct drm_minor *minor); +extern void drm_sysfs_hotplug_event(struct drm_device *dev);  extern void drm_sysfs_device_remove(struct drm_minor *minor); +extern char *drm_get_connector_status_name(enum drm_connector_status status); +extern int drm_sysfs_connector_add(struct drm_connector *connector); +extern void drm_sysfs_connector_remove(struct drm_connector *connector);  /* - * Basic memory manager support (drm_mm.c) + * Basic memory manager support (drm_memrange.c)   */ -extern struct drm_mm_node * drm_mm_get_block(struct drm_mm_node * parent, unsigned long size, -					       unsigned alignment); -extern void drm_mm_put_block(struct drm_mm_node *cur); -extern struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm, unsigned long size, -						unsigned alignment, int best_match); -extern int drm_mm_init(struct drm_mm *mm, unsigned long start, unsigned long size); -extern void drm_mm_takedown(struct drm_mm *mm); -extern int drm_mm_clean(struct drm_mm *mm); -extern unsigned long drm_mm_tail_space(struct drm_mm *mm); -extern int drm_mm_remove_space_from_tail(struct drm_mm *mm, unsigned long size); -extern int drm_mm_add_space_to_tail(struct drm_mm *mm, unsigned long size); - -static inline struct drm_mm *drm_get_mm(struct drm_mm_node *block) +extern struct drm_memrange_node *drm_memrange_get_block(struct drm_memrange_node * parent, +							unsigned long size, +							unsigned alignment); +extern void drm_memrange_put_block(struct drm_memrange_node *cur); +extern struct drm_memrange_node *drm_memrange_search_free(const struct drm_memrange *mm, +							  unsigned long size, +							  unsigned alignment, int best_match); +extern int drm_memrange_init(struct drm_memrange *mm, +			     unsigned long start, unsigned long size); +extern void drm_memrange_takedown(struct drm_memrange *mm); +extern int drm_memrange_clean(struct drm_memrange *mm); +extern unsigned long drm_memrange_tail_space(struct drm_memrange *mm); +extern int drm_memrange_remove_space_from_tail(struct drm_memrange *mm, +					       unsigned long size); +extern int drm_memrange_add_space_to_tail(struct drm_memrange *mm, +					  unsigned long size); +static inline struct drm_memrange *drm_get_mm(struct drm_memrange_node *block)  {  	return block->mm;  } +/* Graphics Execution Manager library functions (drm_gem.c) */ +int +drm_gem_init (struct drm_device *dev); + +void +drm_gem_object_free (struct kref *kref); + +struct drm_gem_object * +drm_gem_object_alloc(struct drm_device *dev, size_t size); + +void +drm_gem_object_handle_free (struct kref *kref); +     +static inline void drm_gem_object_reference(struct drm_gem_object *obj) +{ +	kref_get(&obj->refcount); +} + +static inline void drm_gem_object_unreference(struct drm_gem_object *obj) +{ +	if (obj == NULL) +		return; + +	kref_put (&obj->refcount, drm_gem_object_free); +} + +int +drm_gem_handle_create(struct drm_file *file_priv, +		      struct drm_gem_object *obj, +		      int *handlep); + +static inline void drm_gem_object_handle_reference (struct drm_gem_object *obj) +{ +	drm_gem_object_reference (obj); +	kref_get(&obj->handlecount); +} + +static inline void drm_gem_object_handle_unreference (struct drm_gem_object *obj) +{ +	if (obj == NULL) +		return; +	 +	/* +	 * Must bump handle count first as this may be the last +	 * ref, in which case the object would disappear before we +	 * checked for a name +	 */ +	kref_put (&obj->handlecount, drm_gem_object_handle_free); +	drm_gem_object_unreference (obj); +} + +struct drm_gem_object * +drm_gem_object_lookup(struct drm_device *dev, struct drm_file *filp, +		      int handle); +int drm_gem_close_ioctl(struct drm_device *dev, void *data, +			struct drm_file *file_priv); +int drm_gem_flink_ioctl(struct drm_device *dev, void *data, +			struct drm_file *file_priv); +int drm_gem_open_ioctl(struct drm_device *dev, void *data, +		       struct drm_file *file_priv); + +void drm_gem_open(struct drm_device *dev, struct drm_file *file_private); +void drm_gem_release(struct drm_device *dev, struct drm_file *file_private); +  extern void drm_core_ioremap(struct drm_map *map, struct drm_device *dev);  extern void drm_core_ioremap_wc(struct drm_map *map, struct drm_device *dev);  extern void drm_core_ioremapfree(struct drm_map *map, struct drm_device *dev); diff --git a/linux-core/drm_agpsupport.c b/linux-core/drm_agpsupport.c index 80663717..3cc94ff7 100644 --- a/linux-core/drm_agpsupport.c +++ b/linux-core/drm_agpsupport.c @@ -484,7 +484,50 @@ int drm_agp_unbind_memory(DRM_AGP_MEM * handle)  	return agp_unbind_memory(handle);  } +/** + * Binds a collection of pages into AGP memory at the given offset, returning + * the AGP memory structure containing them. + * + * No reference is held on the pages during this time -- it is up to the + * caller to handle that. + */ +DRM_AGP_MEM * +drm_agp_bind_pages(struct drm_device *dev, +		   struct page **pages, +		   unsigned long num_pages, +		   uint32_t gtt_offset) +{ +	DRM_AGP_MEM *mem; +	int ret, i; + +	DRM_DEBUG("drm_agp_populate_ttm\n"); +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,11) +	mem = drm_agp_allocate_memory(num_pages, AGP_USER_MEMORY); +#else +	mem = drm_agp_allocate_memory(dev->agp->bridge, num_pages, +				      AGP_USER_MEMORY); +#endif +	if (mem == NULL) { +		DRM_ERROR("Failed to allocate memory for %ld pages\n", +			  num_pages); +		return NULL; +	} + +	for (i = 0; i < num_pages; i++) +		mem->memory[i] = phys_to_gart(page_to_phys(pages[i])); +	mem->page_count = num_pages; + +	mem->is_flushed = true; +	ret = drm_agp_bind_memory(mem, gtt_offset / PAGE_SIZE); +	if (ret != 0) { +		DRM_ERROR("Failed to bind AGP memory: %d\n", ret); +		agp_free_memory(mem); +		return NULL; +	} +	return mem; +} +EXPORT_SYMBOL(drm_agp_bind_pages);  /*   * AGP ttm backend interface. diff --git a/linux-core/drm_auth.c b/linux-core/drm_auth.c index c904a91d..20c8a634 100644 --- a/linux-core/drm_auth.c +++ b/linux-core/drm_auth.c @@ -45,14 +45,15 @@   * the one with matching magic number, while holding the drm_device::struct_mutex   * lock.   */ -static struct drm_file *drm_find_file(struct drm_device * dev, drm_magic_t magic) +static struct drm_file *drm_find_file(struct drm_master *master , drm_magic_t magic)  {  	struct drm_file *retval = NULL;  	struct drm_magic_entry *pt;  	struct drm_hash_item *hash; +	struct drm_device *dev = master->minor->dev;  	mutex_lock(&dev->struct_mutex); -	if (!drm_ht_find_item(&dev->magiclist, (unsigned long)magic, &hash)) { +	if (!drm_ht_find_item(&master->magiclist, (unsigned long)magic, &hash)) {  		pt = drm_hash_entry(hash, struct drm_magic_entry, hash_item);  		retval = pt->priv;  	} @@ -71,11 +72,11 @@ static struct drm_file *drm_find_file(struct drm_device * dev, drm_magic_t magic   * associated the magic number hash key in drm_device::magiclist, while holding   * the drm_device::struct_mutex lock.   */ -static int drm_add_magic(struct drm_device * dev, struct drm_file * priv, +static int drm_add_magic(struct drm_master *master, struct drm_file * priv,  			 drm_magic_t magic)  {  	struct drm_magic_entry *entry; - +	struct drm_device *dev = master->minor->dev;  	DRM_DEBUG("%d\n", magic);  	entry = drm_alloc(sizeof(*entry), DRM_MEM_MAGIC); @@ -85,8 +86,8 @@ static int drm_add_magic(struct drm_device * dev, struct drm_file * priv,  	entry->priv = priv;  	entry->hash_item.key = (unsigned long)magic;  	mutex_lock(&dev->struct_mutex); -	drm_ht_insert_item(&dev->magiclist, &entry->hash_item); -	list_add_tail(&entry->head, &dev->magicfree); +	drm_ht_insert_item(&master->magiclist, &entry->hash_item); +	list_add_tail(&entry->head, &master->magicfree);  	mutex_unlock(&dev->struct_mutex);  	return 0; @@ -101,20 +102,21 @@ static int drm_add_magic(struct drm_device * dev, struct drm_file * priv,   * Searches and unlinks the entry in drm_device::magiclist with the magic   * number hash key, while holding the drm_device::struct_mutex lock.   */ -static int drm_remove_magic(struct drm_device * dev, drm_magic_t magic) +static int drm_remove_magic(struct drm_master *master, drm_magic_t magic)  {  	struct drm_magic_entry *pt;  	struct drm_hash_item *hash; +	struct drm_device *dev = master->minor->dev;  	DRM_DEBUG("%d\n", magic);  	mutex_lock(&dev->struct_mutex); -	if (drm_ht_find_item(&dev->magiclist, (unsigned long)magic, &hash)) { +	if (drm_ht_find_item(&master->magiclist, (unsigned long)magic, &hash)) {  		mutex_unlock(&dev->struct_mutex);  		return -EINVAL;  	}  	pt = drm_hash_entry(hash, struct drm_magic_entry, hash_item); -	drm_ht_remove_item(&dev->magiclist, hash); +	drm_ht_remove_item(&master->magiclist, hash);  	list_del(&pt->head);  	mutex_unlock(&dev->struct_mutex); @@ -152,9 +154,9 @@ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv)  				++sequence;	/* reserve 0 */  			auth->magic = sequence++;  			spin_unlock(&lock); -		} while (drm_find_file(dev, auth->magic)); +		} while (drm_find_file(file_priv->master, auth->magic));  		file_priv->magic = auth->magic; -		drm_add_magic(dev, file_priv, auth->magic); +		drm_add_magic(file_priv->master, file_priv, auth->magic);  	}  	DRM_DEBUG("%u\n", auth->magic); @@ -180,9 +182,9 @@ int drm_authmagic(struct drm_device *dev, void *data,  	struct drm_file *file;  	DRM_DEBUG("%u\n", auth->magic); -	if ((file = drm_find_file(dev, auth->magic))) { +	if ((file = drm_find_file(file_priv->master, auth->magic))) {  		file->authenticated = 1; -		drm_remove_magic(dev, auth->magic); +		drm_remove_magic(file_priv->master, auth->magic);  		return 0;  	}  	return -EINVAL; diff --git a/linux-core/drm_bo.c b/linux-core/drm_bo.c index 88b2ee66..0021530b 100644 --- a/linux-core/drm_bo.c +++ b/linux-core/drm_bo.c @@ -51,7 +51,6 @@  static void drm_bo_destroy_locked(struct drm_buffer_object *bo);  static int drm_bo_setup_vm_locked(struct drm_buffer_object *bo); -static void drm_bo_takedown_vm_locked(struct drm_buffer_object *bo);  static void drm_bo_unmap_virtual(struct drm_buffer_object *bo);  static inline uint64_t drm_bo_type_flags(unsigned type) @@ -418,14 +417,14 @@ static void drm_bo_cleanup_refs(struct drm_buffer_object *bo, int remove_all)  	if (!bo->fence) {  		list_del_init(&bo->lru);  		if (bo->mem.mm_node) { -			drm_mm_put_block(bo->mem.mm_node); +			drm_memrange_put_block(bo->mem.mm_node);  			if (bo->pinned_node == bo->mem.mm_node)  				bo->pinned_node = NULL;  			bo->mem.mm_node = NULL;  		}  		list_del_init(&bo->pinned_lru);  		if (bo->pinned_node) { -			drm_mm_put_block(bo->pinned_node); +			drm_memrange_put_block(bo->pinned_node);  			bo->pinned_node = NULL;  		}  		list_del_init(&bo->ddestroy); @@ -458,6 +457,7 @@ static void drm_bo_destroy_locked(struct drm_buffer_object *bo)  	DRM_ASSERT_LOCKED(&dev->struct_mutex); +	DRM_DEBUG("freeing %p\n", bo);  	if (list_empty(&bo->lru) && bo->mem.mm_node == NULL &&  	    list_empty(&bo->pinned_lru) && bo->pinned_node == NULL &&  	    list_empty(&bo->ddestroy) && atomic_read(&bo->usage) == 0) { @@ -624,7 +624,6 @@ void drm_putback_buffer_objects(struct drm_device *dev)  }  EXPORT_SYMBOL(drm_putback_buffer_objects); -  /*   * Note. The caller has to register (if applicable)   * and deregister fence object usage. @@ -791,7 +790,7 @@ out:  	mutex_lock(&dev->struct_mutex);  	if (evict_mem.mm_node) {  		if (evict_mem.mm_node != bo->pinned_node) -			drm_mm_put_block(evict_mem.mm_node); +			drm_memrange_put_block(evict_mem.mm_node);  		evict_mem.mm_node = NULL;  	}  	drm_bo_add_to_lru(bo); @@ -810,7 +809,7 @@ static int drm_bo_mem_force_space(struct drm_device *dev,  				  struct drm_bo_mem_reg *mem,  				  uint32_t mem_type, int no_wait)  { -	struct drm_mm_node *node; +	struct drm_memrange_node *node;  	struct drm_buffer_manager *bm = &dev->bm;  	struct drm_buffer_object *entry;  	struct drm_mem_type_manager *man = &bm->man[mem_type]; @@ -820,7 +819,7 @@ static int drm_bo_mem_force_space(struct drm_device *dev,  	mutex_lock(&dev->struct_mutex);  	do { -		node = drm_mm_search_free(&man->manager, num_pages, +		node = drm_memrange_search_free(&man->manager, num_pages,  					  mem->page_alignment, 1);  		if (node)  			break; @@ -846,7 +845,7 @@ static int drm_bo_mem_force_space(struct drm_device *dev,  		return -ENOMEM;  	} -	node = drm_mm_get_block(node, num_pages, mem->page_alignment); +	node = drm_memrange_get_block(node, num_pages, mem->page_alignment);  	if (unlikely(!node)) {  		mutex_unlock(&dev->struct_mutex);  		return -ENOMEM; @@ -924,7 +923,7 @@ int drm_bo_mem_space(struct drm_buffer_object *bo,  	int type_found = 0;  	int type_ok = 0;  	int has_eagain = 0; -	struct drm_mm_node *node = NULL; +	struct drm_memrange_node *node = NULL;  	int ret;  	mem->mm_node = NULL; @@ -952,10 +951,10 @@ int drm_bo_mem_space(struct drm_buffer_object *bo,  		mutex_lock(&dev->struct_mutex);  		if (man->has_type && man->use_type) {  			type_found = 1; -			node = drm_mm_search_free(&man->manager, mem->num_pages, +			node = drm_memrange_search_free(&man->manager, mem->num_pages,  						  mem->page_alignment, 1);  			if (node) -				node = drm_mm_get_block(node, mem->num_pages, +				node = drm_memrange_get_block(node, mem->num_pages,  							mem->page_alignment);  		}  		mutex_unlock(&dev->struct_mutex); @@ -1340,7 +1339,7 @@ out_unlock:  	if (ret || !move_unfenced) {  		if (mem.mm_node) {  			if (mem.mm_node != bo->pinned_node) -				drm_mm_put_block(mem.mm_node); +				drm_memrange_put_block(mem.mm_node);  			mem.mm_node = NULL;  		}  		drm_bo_add_to_lru(bo); @@ -1432,7 +1431,7 @@ static int drm_buffer_object_validate(struct drm_buffer_object *bo,  		if (bo->pinned_node != bo->mem.mm_node) {  			if (bo->pinned_node != NULL) -				drm_mm_put_block(bo->pinned_node); +				drm_memrange_put_block(bo->pinned_node);  			bo->pinned_node = bo->mem.mm_node;  		} @@ -1443,7 +1442,7 @@ static int drm_buffer_object_validate(struct drm_buffer_object *bo,  		mutex_lock(&dev->struct_mutex);  		if (bo->pinned_node != bo->mem.mm_node) -			drm_mm_put_block(bo->pinned_node); +			drm_memrange_put_block(bo->pinned_node);  		list_del_init(&bo->pinned_lru);  		bo->pinned_node = NULL; @@ -1766,7 +1765,7 @@ int drm_buffer_object_create(struct drm_device *dev,  	size += buffer_start & ~PAGE_MASK;  	num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;  	if (num_pages == 0) { -		DRM_ERROR("Illegal buffer object size.\n"); +		DRM_ERROR("Illegal buffer object size %ld.\n", size);  		return -EINVAL;  	} @@ -1839,8 +1838,8 @@ out_err_unlocked:  EXPORT_SYMBOL(drm_buffer_object_create); -static int drm_bo_add_user_object(struct drm_file *file_priv, -				  struct drm_buffer_object *bo, int shareable) +int drm_bo_add_user_object(struct drm_file *file_priv, +			   struct drm_buffer_object *bo, int shareable)  {  	struct drm_device *dev = file_priv->minor->dev;  	int ret; @@ -1859,6 +1858,7 @@ out:  	mutex_unlock(&dev->struct_mutex);  	return ret;  } +EXPORT_SYMBOL(drm_bo_add_user_object);  int drm_bo_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)  { @@ -2082,7 +2082,7 @@ static int drm_bo_leave_list(struct drm_buffer_object *bo,  		if (bo->pinned_node == bo->mem.mm_node)  			bo->pinned_node = NULL;  		if (bo->pinned_node != NULL) { -			drm_mm_put_block(bo->pinned_node); +			drm_memrange_put_block(bo->pinned_node);  			bo->pinned_node = NULL;  		}  		mutex_unlock(&dev->struct_mutex); @@ -2223,8 +2223,8 @@ int drm_bo_clean_mm(struct drm_device *dev, unsigned mem_type, int kern_clean)  		drm_bo_force_list_clean(dev, &man->lru, mem_type, 1, 0, 0);  		drm_bo_force_list_clean(dev, &man->pinned, mem_type, 1, 0, 1); -		if (drm_mm_clean(&man->manager)) { -			drm_mm_takedown(&man->manager); +		if (drm_memrange_clean(&man->manager)) { +			drm_memrange_takedown(&man->manager);  		} else {  			ret = -EBUSY;  		} @@ -2295,7 +2295,7 @@ int drm_bo_init_mm(struct drm_device *dev, unsigned type,  			DRM_ERROR("Zero size memory manager type %d\n", type);  			return ret;  		} -		ret = drm_mm_init(&man->manager, p_offset, p_size); +		ret = drm_memrange_init(&man->manager, p_offset, p_size);  		if (ret)  			return ret;  	} @@ -2362,15 +2362,18 @@ int drm_bo_driver_finish(struct drm_device *dev)  	if (list_empty(&bm->unfenced))  		DRM_DEBUG("Unfenced list was clean\n"); +	if (bm->dummy_read_page) {  #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) -	ClearPageReserved(bm->dummy_read_page); +		ClearPageReserved(bm->dummy_read_page);  #endif -	__free_page(bm->dummy_read_page); +		__free_page(bm->dummy_read_page); +	}  out:  	mutex_unlock(&dev->struct_mutex);  	return ret;  } +EXPORT_SYMBOL(drm_bo_driver_finish);  /*   * This function is intended to be called on drm driver load. @@ -2406,8 +2409,14 @@ int drm_bo_driver_init(struct drm_device *dev)  	 * Other types need to be driver / IOCTL initialized.  	 */  	ret = drm_bo_init_mm(dev, DRM_BO_MEM_LOCAL, 0, 0, 1); -	if (ret) +	if (ret) { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) +		ClearPageReserved(bm->dummy_read_page); +#endif +		__free_page(bm->dummy_read_page); +		bm->dummy_read_page = NULL;  		goto out_unlock; +	}  #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)  	INIT_WORK(&bm->wq, &drm_bo_delayed_workqueue, dev); @@ -2697,7 +2706,7 @@ void drm_bo_unmap_virtual(struct drm_buffer_object *bo)   * Remove any associated vm mapping on the drm device node that   * would have been created for a drm_bo_type_device buffer   */ -static void drm_bo_takedown_vm_locked(struct drm_buffer_object *bo) +void drm_bo_takedown_vm_locked(struct drm_buffer_object *bo)  {  	struct drm_map_list *list;  	drm_local_map_t *map; @@ -2713,7 +2722,7 @@ static void drm_bo_takedown_vm_locked(struct drm_buffer_object *bo)  		list->user_token = 0;  	}  	if (list->file_offset_node) { -		drm_mm_put_block(list->file_offset_node); +		drm_memrange_put_block(list->file_offset_node);  		list->file_offset_node = NULL;  	} @@ -2726,6 +2735,7 @@ static void drm_bo_takedown_vm_locked(struct drm_buffer_object *bo)  	list->user_token = 0ULL;  	drm_bo_usage_deref_locked(&bo);  } +EXPORT_SYMBOL(drm_bo_takedown_vm_locked);  /**   * drm_bo_setup_vm_locked: @@ -2756,7 +2766,7 @@ static int drm_bo_setup_vm_locked(struct drm_buffer_object *bo)  	atomic_inc(&bo->usage);  	map->handle = (void *)bo; -	list->file_offset_node = drm_mm_search_free(&dev->offset_manager, +	list->file_offset_node = drm_memrange_search_free(&dev->offset_manager,  						    bo->mem.num_pages, 0, 0);  	if (unlikely(!list->file_offset_node)) { @@ -2764,7 +2774,7 @@ static int drm_bo_setup_vm_locked(struct drm_buffer_object *bo)  		return -ENOMEM;  	} -	list->file_offset_node = drm_mm_get_block(list->file_offset_node, +	list->file_offset_node = drm_memrange_get_block(list->file_offset_node,  						  bo->mem.num_pages, 0);  	if (unlikely(!list->file_offset_node)) { diff --git a/linux-core/drm_bo_move.c b/linux-core/drm_bo_move.c index bf0e1b74..9147a475 100644 --- a/linux-core/drm_bo_move.c +++ b/linux-core/drm_bo_move.c @@ -41,7 +41,7 @@ static void drm_bo_free_old_node(struct drm_buffer_object *bo)  	if (old_mem->mm_node && (old_mem->mm_node != bo->pinned_node)) {  		mutex_lock(&bo->dev->struct_mutex); -		drm_mm_put_block(old_mem->mm_node); +		drm_memrange_put_block(old_mem->mm_node);  		mutex_unlock(&bo->dev->struct_mutex);  	}  	old_mem->mm_node = NULL; @@ -146,6 +146,7 @@ void drm_mem_reg_iounmap(struct drm_device *dev, struct drm_bo_mem_reg *mem,  	if (virtual && (man->flags & _DRM_FLAG_NEEDS_IOREMAP))  		iounmap(virtual);  } +EXPORT_SYMBOL(drm_mem_reg_iounmap);  static int drm_copy_io_page(void *dst, void *src, unsigned long page)  { diff --git a/linux-core/drm_bufs.c b/linux-core/drm_bufs.c index 75c75c2f..e9052570 100644 --- a/linux-core/drm_bufs.c +++ b/linux-core/drm_bufs.c @@ -52,9 +52,9 @@ struct drm_map_list *drm_find_matching_map(struct drm_device *dev, drm_local_map  {  	struct drm_map_list *entry;  	list_for_each_entry(entry, &dev->maplist, head) { -		if (entry->map && map->type == entry->map->type && -		    ((entry->map->offset == map->offset) || -		     (map->type == _DRM_SHM && map->flags==_DRM_CONTAINS_LOCK))) { +		if (entry->map && (entry->master == dev->primary->master) && (map->type == entry->map->type) && +		    ((entry->map->offset == map->offset) ||  +		     ((map->type == _DRM_SHM) && (map->flags&_DRM_CONTAINS_LOCK)))) {  			return entry;  		}  	} @@ -209,12 +209,12 @@ static int drm_addmap_core(struct drm_device *dev, unsigned int offset,  		map->offset = (unsigned long)map->handle;  		if (map->flags & _DRM_CONTAINS_LOCK) {  			/* Prevent a 2nd X Server from creating a 2nd lock */ -			if (dev->lock.hw_lock != NULL) { +			if (dev->primary->master->lock.hw_lock != NULL) {  				vfree(map->handle);  				drm_free(map, sizeof(*map), DRM_MEM_MAPS);  				return -EBUSY;  			} -			dev->sigdata.lock = dev->lock.hw_lock = map->handle;	/* Pointer to lock */ +			dev->sigdata.lock = dev->primary->master->lock.hw_lock = map->handle;	/* Pointer to lock */  		}  		break;  	case _DRM_AGP: { @@ -318,6 +318,7 @@ static int drm_addmap_core(struct drm_device *dev, unsigned int offset,  	list->user_token = list->hash.key << PAGE_SHIFT;  	mutex_unlock(&dev->struct_mutex); +	list->master = dev->primary->master;  	*maplist = list;  	return 0;  } @@ -344,7 +345,7 @@ int drm_addmap_ioctl(struct drm_device *dev, void *data,  	struct drm_map_list *maplist;  	int err; -	if (!(capable(CAP_SYS_ADMIN) || map->type == _DRM_AGP)) +	if (!(capable(CAP_SYS_ADMIN) || map->type == _DRM_AGP || map->type == _DRM_SHM))  		return -EPERM;  	err = drm_addmap_core(dev, map->offset, map->size, map->type, @@ -379,10 +380,12 @@ int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map)  	struct drm_map_list *r_list = NULL, *list_t;  	drm_dma_handle_t dmah;  	int found = 0; +	struct drm_master *master;  	/* Find the list entry for the map and remove it */  	list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) {  		if (r_list->map == map) { +			master = r_list->master;  			list_del(&r_list->head);  			drm_ht_remove_key(&dev->map_hash,  					  r_list->user_token >> PAGE_SHIFT); @@ -412,6 +415,13 @@ int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map)  		break;  	case _DRM_SHM:  		vfree(map->handle); +		if (master) { +			if (dev->sigdata.lock == master->lock.hw_lock) +				dev->sigdata.lock = NULL; +			master->lock.hw_lock = NULL;   /* SHM removed */ +			master->lock.file_priv = NULL; +			wake_up_interruptible(&master->lock.lock_queue); +		}  		break;  	case _DRM_AGP:  	case _DRM_SCATTER_GATHER: diff --git a/linux-core/drm_compat.c b/linux-core/drm_compat.c index 3d082e7e..aa825f32 100644 --- a/linux-core/drm_compat.c +++ b/linux-core/drm_compat.c @@ -217,7 +217,7 @@ static struct page *drm_bo_vm_fault(struct vm_area_struct *vma,  	mutex_lock(&bo->mutex); -	err = drm_bo_wait(bo, 0, 1, 0); +	err = drm_bo_wait(bo, 0, 1, 0, 1);  	if (err) {  		data->type = (err == -EAGAIN) ?  			VM_FAULT_MINOR : VM_FAULT_SIGBUS; @@ -730,20 +730,68 @@ void *idr_replace(struct idr *idp, void *ptr, int id)  EXPORT_SYMBOL(idr_replace);  #endif -#if defined(DRM_KMAP_ATOMIC_PROT_PFN) -#define drm_kmap_get_fixmap_pte(vaddr)					\ -	pte_offset_kernel(pmd_offset(pud_offset(pgd_offset_k(vaddr), vaddr), (vaddr)), (vaddr)) +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) +static __inline__ unsigned long __round_jiffies(unsigned long j, int cpu) +{ +	int rem; +	unsigned long original = j; + +	j += cpu * 3; + +	rem = j % HZ; + +	if (rem < HZ/4) /* round down */ +		j = j - rem; +	else /* round up */ +		j = j - rem + HZ; + +	/* now that we have rounded, subtract the extra skew again */ +	j -= cpu * 3; + +	if (j <= jiffies) /* rounding ate our timeout entirely; */ +		return original; +	return j; +} + +static __inline__ unsigned long __round_jiffies_relative(unsigned long j, int cpu) +{ +	return  __round_jiffies(j + jiffies, cpu) - jiffies; +} +unsigned long round_jiffies_relative(unsigned long j) +{ +	return __round_jiffies_relative(j, raw_smp_processor_id()); +} +EXPORT_SYMBOL(round_jiffies_relative); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) +struct pci_dev * pci_get_bus_and_slot(unsigned int bus, unsigned int devfn) +{ +    struct pci_dev *dev = NULL; + +    while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { +        if (pci_domain_nr(dev->bus) == 0 && +           (dev->bus->number == bus && dev->devfn == devfn)) +            return dev; +   } +   return NULL; +} +EXPORT_SYMBOL(pci_get_bus_and_slot); +#endif + +#if defined(DRM_KMAP_ATOMIC_PROT_PFN)  void *kmap_atomic_prot_pfn(unsigned long pfn, enum km_type type,  			   pgprot_t protection)  {  	enum fixed_addresses idx;  	unsigned long vaddr;  	static pte_t *km_pte; +	int level;  	static int initialized = 0;  	if (unlikely(!initialized)) { -		km_pte = drm_kmap_get_fixmap_pte(__fix_to_virt(FIX_KMAP_BEGIN)); +		km_pte = lookup_address(__fix_to_virt(FIX_KMAP_BEGIN), &level);  		initialized = 1;  	} @@ -756,6 +804,5 @@ void *kmap_atomic_prot_pfn(unsigned long pfn, enum km_type type,  }  EXPORT_SYMBOL(kmap_atomic_prot_pfn); -  #endif diff --git a/linux-core/drm_compat.h b/linux-core/drm_compat.h index 3339219d..08dd5f8d 100644 --- a/linux-core/drm_compat.h +++ b/linux-core/drm_compat.h @@ -60,6 +60,13 @@  #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))  #undef DRM_IRQ_ARGS  #define DRM_IRQ_ARGS		int irq, void *arg, struct pt_regs *regs + +typedef _Bool bool; +enum { +        false   = 0, +        true    = 1 +}; +  #endif  #ifndef list_for_each_safe @@ -152,7 +159,7 @@ static __inline__ void *kcalloc(size_t nmemb, size_t size, int flags)  #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)  #define vmalloc_user(_size) ({void * tmp = vmalloc(_size);   \ -      if (tmp) memset(tmp, 0, size);			     \ +      if (tmp) memset(tmp, 0, _size);			     \        (tmp);})  #endif @@ -325,11 +332,28 @@ void *idr_replace(struct idr *idp, void *ptr, int id);  #endif  #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) -typedef _Bool                   bool; +extern unsigned long round_jiffies_relative(unsigned long j);  #endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) +extern struct pci_dev * pci_get_bus_and_slot(unsigned int bus, unsigned int devfn); +#endif -#if (defined(CONFIG_X86) && defined(CONFIG_X86_32) && defined(CONFIG_HIMEM)) +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) +static inline int kobject_uevent_env(struct kobject *kobj, +                                     enum kobject_action action, +                                     char *envp[]) +{ +    return 0; +} +#endif + +#ifndef PM_EVENT_PRETHAW  +#define PM_EVENT_PRETHAW 3 +#endif + + +#if (defined(CONFIG_X86) && defined(CONFIG_X86_32) && defined(CONFIG_HIGHMEM))  #define DRM_KMAP_ATOMIC_PROT_PFN  extern void *kmap_atomic_prot_pfn(unsigned long pfn, enum km_type type,  				  pgprot_t protection); @@ -362,6 +386,14 @@ extern struct page *drm_vm_sg_nopage(struct vm_area_struct *vma,  				     unsigned long address, int *type);  #endif +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,26) +#define drm_on_each_cpu(handler, data, wait) \ +	on_each_cpu(handler, data, wait) +#else +#define drm_on_each_cpu(handler, data, wait) \ +	on_each_cpu(handler, data, wait, 1) +#endif +  #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26))  #define drm_core_ioremap_wc drm_core_ioremap  #endif diff --git a/linux-core/drm_context.c b/linux-core/drm_context.c index 83ad291e..f3d69ff7 100644 --- a/linux-core/drm_context.c +++ b/linux-core/drm_context.c @@ -257,12 +257,13 @@ static int drm_context_switch(struct drm_device *dev, int old, int new)   * hardware lock is held, clears the drm_device::context_flag and wakes up   * drm_device::context_wait.   */ -static int drm_context_switch_complete(struct drm_device *dev, int new) +static int drm_context_switch_complete(struct drm_device *dev,  +				       struct drm_file *file_priv, int new)  {  	dev->last_context = new;	/* PRE/POST: This is the _only_ writer. */  	dev->last_switch = jiffies; -	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { +	if (!_DRM_LOCK_IS_HELD(file_priv->master->lock.hw_lock->lock)) {  		DRM_ERROR("Lock isn't held after context switch\n");  	} @@ -421,7 +422,7 @@ int drm_newctx(struct drm_device *dev, void *data,  	struct drm_ctx *ctx = data;  	DRM_DEBUG("%d\n", ctx->handle); -	drm_context_switch_complete(dev, ctx->handle); +	drm_context_switch_complete(dev, file_priv, ctx->handle);  	return 0;  } @@ -443,9 +444,6 @@ int drm_rmctx(struct drm_device *dev, void *data,  	struct drm_ctx *ctx = data;  	DRM_DEBUG("%d\n", ctx->handle); -	if (ctx->handle == DRM_KERNEL_CONTEXT + 1) { -		file_priv->remove_auth_on_close = 1; -	}  	if (ctx->handle != DRM_KERNEL_CONTEXT) {  		if (dev->driver->context_dtor)  			dev->driver->context_dtor(dev, ctx->handle); diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c new file mode 100644 index 00000000..8375bf9a --- /dev/null +++ b/linux-core/drm_crtc.c @@ -0,0 +1,2391 @@ +/* + * Copyright (c) 2006-2007 Intel Corporation + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> + * Copyright (c) 2008 Red Hat Inc. + * + * DRM core CRTC related functions + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission.  The copyright holders make no representations + * about the suitability of this software for any purpose.  It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, 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. + * + * Authors: + *      Keith Packard + *	Eric Anholt <eric@anholt.net> + *      Dave Airlie <airlied@linux.ie> + *      Jesse Barnes <jesse.barnes@intel.com> + */ +#include <linux/list.h> +#include "drm.h" +#include "drmP.h" +#include "drm_crtc.h" + +struct drm_prop_enum_list { +	int type; +	char *name; +}; + +/* + * Global properties + */ +static struct drm_prop_enum_list drm_dpms_enum_list[] = +{	{ DRM_MODE_DPMS_ON, "On" }, +	{ DRM_MODE_DPMS_STANDBY, "Standby" }, +	{ DRM_MODE_DPMS_SUSPEND, "Suspend" }, +	{ DRM_MODE_DPMS_OFF, "Off" } +}; + +char *drm_get_dpms_name(int val) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(drm_dpms_enum_list); i++) +		if (drm_dpms_enum_list[i].type == val) +			return drm_dpms_enum_list[i].name; + +	return "unknown"; +} + +/* + * Optional properties + */ +static struct drm_prop_enum_list drm_scaling_mode_enum_list[] = +{ +	{ DRM_MODE_SCALE_NON_GPU, "Non-GPU" }, +	{ DRM_MODE_SCALE_FULLSCREEN, "Fullscreen" }, +	{ DRM_MODE_SCALE_NO_SCALE, "No scale" }, +	{ DRM_MODE_SCALE_ASPECT, "Aspect" }, +}; + +static struct drm_prop_enum_list drm_dithering_mode_enum_list[] = +{ +	{ DRM_MODE_DITHERING_OFF, "Off" }, +	{ DRM_MODE_DITHERING_ON, "On" }, +}; + +/* + * Non-global properties, but "required" for certain connectors. + */ +static struct drm_prop_enum_list drm_select_subconnector_enum_list[] = +{ +	{ DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ +	{ DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */ +	{ DRM_MODE_SUBCONNECTOR_DVIA,      "DVI-A"     }, /* DVI-I  */ +	{ DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ +	{ DRM_MODE_SUBCONNECTOR_SVIDEO,    "SVIDEO"    }, /* TV-out */ +	{ DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ +}; + +char *drm_get_select_subconnector_name(int val) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(drm_select_subconnector_enum_list); i++) +		if (drm_select_subconnector_enum_list[i].type == val) +			return drm_select_subconnector_enum_list[i].name; + +	return "unknown"; +} + +static struct drm_prop_enum_list drm_subconnector_enum_list[] = +{ +	{ DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"    }, /* DVI-I and TV-out */ +	{ DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */ +	{ DRM_MODE_SUBCONNECTOR_DVIA,      "DVI-A"     }, /* DVI-I  */ +	{ DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ +	{ DRM_MODE_SUBCONNECTOR_SVIDEO,    "SVIDEO"    }, /* TV-out */ +	{ DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ +}; + +char *drm_get_subconnector_name(int val) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(drm_subconnector_enum_list); i++) +		if (drm_subconnector_enum_list[i].type == val) +			return drm_subconnector_enum_list[i].name; + +	return "unknown"; +} + +struct drm_conn_prop_enum_list { +	int type; +	char *name; +	int count; +}; + +/* + * Connector and encoder types. + */ +static struct drm_conn_prop_enum_list drm_connector_enum_list[] =  +{	{ DRM_MODE_CONNECTOR_Unknown, "Unknown", 0 }, +	{ DRM_MODE_CONNECTOR_VGA, "VGA", 0 }, +	{ DRM_MODE_CONNECTOR_DVII, "DVI-I", 0 }, +	{ DRM_MODE_CONNECTOR_DVID, "DVI-D", 0 }, +	{ DRM_MODE_CONNECTOR_DVIA, "DVI-A", 0 }, +	{ DRM_MODE_CONNECTOR_Composite, "Composite", 0 }, +	{ DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO", 0 }, +	{ DRM_MODE_CONNECTOR_LVDS, "LVDS", 0 }, +	{ DRM_MODE_CONNECTOR_Component, "Component", 0 }, +	{ DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN", 0 }, +	{ DRM_MODE_CONNECTOR_DisplayPort, "DisplayPort", 0 }, +	{ DRM_MODE_CONNECTOR_HDMIA, "HDMI Type A", 0 }, +	{ DRM_MODE_CONNECTOR_HDMIB, "HDMI Type B", 0 }, +}; + +static struct drm_prop_enum_list drm_encoder_enum_list[] = +{	{ DRM_MODE_ENCODER_NONE, "None" }, +	{ DRM_MODE_ENCODER_DAC, "DAC" }, +	{ DRM_MODE_ENCODER_TMDS, "TMDS" }, +	{ DRM_MODE_ENCODER_LVDS, "LVDS" }, +	{ DRM_MODE_ENCODER_TVDAC, "TV" }, +}; + +char *drm_get_encoder_name(struct drm_encoder *encoder) +{ +	static char buf[32]; + +	snprintf(buf, 32, "%s-%d", drm_encoder_enum_list[encoder->encoder_type].name, +		 encoder->base.id); +	return buf; +} + +char *drm_get_connector_name(struct drm_connector *connector) +{ +	static char buf[32]; + +	snprintf(buf, 32, "%s-%d", drm_connector_enum_list[connector->connector_type].name, +		 connector->connector_type_id); +	return buf; +} +EXPORT_SYMBOL(drm_get_connector_name); + +char *drm_get_connector_status_name(enum drm_connector_status status) +{ +	if (status == connector_status_connected) +		return "connected"; +	else if (status == connector_status_disconnected) +		return "disconnected"; +	else +		return "unknown"; +} + +/** + * drm_idr_get - allocate a new identifier + * @dev: DRM device + * @ptr: object pointer, used to generate unique ID + * + * LOCKING: + * Caller must hold DRM mode_config lock. + * + * Create a unique identifier based on @ptr in @dev's identifier space.  Used + * for tracking modes, CRTCs and connectors. + * + * RETURNS: + * New unique (relative to other objects in @dev) integer identifier for the + * object. + */ +static int drm_mode_object_get(struct drm_device *dev, struct drm_mode_object *obj, uint32_t obj_type) +{ +	int new_id = 0; +	int ret; +again: +	if (idr_pre_get(&dev->mode_config.crtc_idr, GFP_KERNEL) == 0) { +		DRM_ERROR("Ran out memory getting a mode number\n"); +		return -EINVAL; +	} + +	ret = idr_get_new_above(&dev->mode_config.crtc_idr, obj, 1, &new_id); +	if (ret == -EAGAIN) +		goto again; + +	obj->id = new_id; +	obj->type = obj_type; +	return 0; +} + +/** + * drm_mode_object_put - free an identifer + * @dev: DRM device + * @id: ID to free + * + * LOCKING: + * Caller must hold DRM mode_config lock. + * + * Free @id from @dev's unique identifier pool. + */ +static void drm_mode_object_put(struct drm_device *dev, struct drm_mode_object *object) +{ +	idr_remove(&dev->mode_config.crtc_idr, object->id); +} + +void *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type) +{ +	struct drm_mode_object *obj; + +	obj = idr_find(&dev->mode_config.crtc_idr, id); +	if (!obj || (obj->type != type) || (obj->id != id)) +		return NULL; + +	return obj; +} +EXPORT_SYMBOL(drm_mode_object_find); + +/** + * drm_crtc_from_fb - find the CRTC structure associated with an fb + * @dev: DRM device + * @fb: framebuffer in question + * + * LOCKING: + * Caller must hold mode_config lock. + * + * Find CRTC in the mode_config structure that matches @fb. + * + * RETURNS: + * Pointer to the CRTC or NULL if it wasn't found. + */ +struct drm_crtc *drm_crtc_from_fb(struct drm_device *dev, +				  struct drm_framebuffer *fb) +{ +	struct drm_crtc *crtc; + +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		if (crtc->fb == fb) +			return crtc; +	} +	return NULL; +} + +/** + * drm_framebuffer_create - create a new framebuffer object + * @dev: DRM device + * + * LOCKING: + * Caller must hold mode config lock. + * + * Creates a new framebuffer objects and adds it to @dev's DRM mode_config. + * + * RETURNS: + * Pointer to new framebuffer or NULL on error. + */ +struct drm_framebuffer *drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, +					     const struct drm_framebuffer_funcs *funcs) +{ +	drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB); +	fb->dev = dev; +	fb->funcs = funcs; +	dev->mode_config.num_fb++; +	list_add(&fb->head, &dev->mode_config.fb_list); + +	return fb; +} +EXPORT_SYMBOL(drm_framebuffer_init); + +/** + * drm_framebuffer_cleanup - remove a framebuffer object + * @fb: framebuffer to remove + * + * LOCKING: + * Caller must hold mode config lock. + * + * Scans all the CRTCs in @dev's mode_config.  If they're using @fb, removes + * it, setting it to NULL. + */ +void drm_framebuffer_cleanup(struct drm_framebuffer *fb) +{ +	struct drm_device *dev = fb->dev; +	struct drm_crtc *crtc; + +	/* remove from any CRTC */ +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		if (crtc->fb == fb) +			crtc->fb = NULL; +	} + +	drm_mode_object_put(dev, &fb->base); +	list_del(&fb->head); +	dev->mode_config.num_fb--; +} +EXPORT_SYMBOL(drm_framebuffer_cleanup); + +/** + * drm_crtc_init - Initialise a new CRTC object + * @dev: DRM device + * @crtc: CRTC object to init + * @funcs: callbacks for the new CRTC + * + * LOCKING: + * Caller must hold mode config lock. + * + * Inits a new object created as base part of an driver crtc object. + */ +void drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, +		   const struct drm_crtc_funcs *funcs) +{ +	crtc->dev = dev; +	crtc->funcs = funcs; + +	drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); + +	list_add_tail(&crtc->head, &dev->mode_config.crtc_list); +	dev->mode_config.num_crtc++; +} +EXPORT_SYMBOL(drm_crtc_init); + +/** + * drm_crtc_cleanup - Cleans up the core crtc usage. + * @crtc: CRTC to cleanup + * + * LOCKING: + * Caller must hold mode config lock. + * + * Cleanup @crtc. Removes from drm modesetting space + * does NOT free object, caller does that. + */ +void drm_crtc_cleanup(struct drm_crtc *crtc) +{ +	struct drm_device *dev = crtc->dev; + +	if (crtc->gamma_store) { +		kfree(crtc->gamma_store); +		crtc->gamma_store = NULL; +	} + +	drm_mode_object_put(dev, &crtc->base); +	list_del(&crtc->head); +	dev->mode_config.num_crtc--; +} +EXPORT_SYMBOL(drm_crtc_cleanup); + +/** + * drm_mode_probed_add - add a mode to the specified connector's probed mode list + * @connector: connector the new mode + * @mode: mode data + * + * LOCKING: + * Caller must hold mode config lock. + *  + * Add @mode to @connector's mode list for later use. + */ +void drm_mode_probed_add(struct drm_connector *connector, +			 struct drm_display_mode *mode) +{ +	list_add(&mode->head, &connector->probed_modes); +} +EXPORT_SYMBOL(drm_mode_probed_add); + +/** + * drm_mode_remove - remove and free a mode + * @connector: connector list to modify + * @mode: mode to remove + * + * LOCKING: + * Caller must hold mode config lock. + *  + * Remove @mode from @connector's mode list, then free it. + */ +void drm_mode_remove(struct drm_connector *connector, struct drm_display_mode *mode) +{ +	list_del(&mode->head); +	kfree(mode); +} +EXPORT_SYMBOL(drm_mode_remove); + +/** + * drm_connector_init - Init a preallocated connector + * @dev: DRM device + * @connector: the connector to init + * @funcs: callbacks for this connector + * @name: user visible name of the connector + * + * LOCKING: + * Caller must hold @dev's mode_config lock. + * + * Initialises a preallocated connector. Connectors should be + * subclassed as part of driver connector objects. + */ +void drm_connector_init(struct drm_device *dev, +		     struct drm_connector *connector, +		     const struct drm_connector_funcs *funcs, +		     int connector_type) +{ +	connector->dev = dev; +	connector->funcs = funcs; +	drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR); +	connector->connector_type = connector_type; +	connector->connector_type_id = ++drm_connector_enum_list[connector_type].count; /* TODO */ +	INIT_LIST_HEAD(&connector->user_modes); +	INIT_LIST_HEAD(&connector->probed_modes); +	INIT_LIST_HEAD(&connector->modes); +	connector->edid_blob_ptr = NULL; +	/* randr_connector? */ +	/* connector_set_monitor(connector)? */ +	/* check for connector_ignored(connector)? */ + +	mutex_lock(&dev->mode_config.mutex); +	list_add_tail(&connector->head, &dev->mode_config.connector_list); +	dev->mode_config.num_connector++; + +	drm_connector_attach_property(connector, dev->mode_config.edid_property, 0); + +	drm_connector_attach_property(connector, dev->mode_config.dpms_property, 0); + +	mutex_unlock(&dev->mode_config.mutex); +} +EXPORT_SYMBOL(drm_connector_init); + +/** + * drm_connector_cleanup - cleans up an initialised connector + * @connector: connector to cleanup + * + * LOCKING: + * Caller must hold @dev's mode_config lock. + * + * Cleans up the connector but doesn't free the object. + */ +void drm_connector_cleanup(struct drm_connector *connector) +{ +	struct drm_device *dev = connector->dev; +	struct drm_display_mode *mode, *t; + +	list_for_each_entry_safe(mode, t, &connector->probed_modes, head) +		drm_mode_remove(connector, mode); + +	list_for_each_entry_safe(mode, t, &connector->modes, head) +		drm_mode_remove(connector, mode); + +	list_for_each_entry_safe(mode, t, &connector->user_modes, head) +		drm_mode_remove(connector, mode); + +	mutex_lock(&dev->mode_config.mutex); +	drm_mode_object_put(dev, &connector->base); +	list_del(&connector->head); +	mutex_unlock(&dev->mode_config.mutex); +} +EXPORT_SYMBOL(drm_connector_cleanup); + +void drm_encoder_init(struct drm_device *dev, +		      struct drm_encoder *encoder, +		      const struct drm_encoder_funcs *funcs, +		      int encoder_type) +{ +	encoder->dev = dev; + +	drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER); +	encoder->encoder_type = encoder_type; +	encoder->funcs = funcs; + +	mutex_lock(&dev->mode_config.mutex); +	list_add_tail(&encoder->head, &dev->mode_config.encoder_list); +	dev->mode_config.num_encoder++; + +	mutex_unlock(&dev->mode_config.mutex); +} +EXPORT_SYMBOL(drm_encoder_init); + +void drm_encoder_cleanup(struct drm_encoder *encoder) +{ +	struct drm_device *dev = encoder->dev; +	mutex_lock(&dev->mode_config.mutex); +	drm_mode_object_put(dev, &encoder->base); +	list_del(&encoder->head); +	mutex_unlock(&dev->mode_config.mutex); +} +EXPORT_SYMBOL(drm_encoder_cleanup); + +/** + * drm_mode_create - create a new display mode + * @dev: DRM device + * + * LOCKING: + * None. + * + * Create a new drm_display_mode, give it an ID, and return it. + * + * RETURNS: + * Pointer to new mode on success, NULL on error. + */ +struct drm_display_mode *drm_mode_create(struct drm_device *dev) +{ +	struct drm_display_mode *nmode; + +	nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); +	if (!nmode) +		return NULL; + +	drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE); +	return nmode; +} +EXPORT_SYMBOL(drm_mode_create); + +/** + * drm_mode_destroy - remove a mode + * @dev: DRM device + * @mode: mode to remove + * + * LOCKING: + * Caller must hold mode config lock. + * + * Free @mode's unique identifier, then free it. + */ +void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) +{ +	drm_mode_object_put(dev, &mode->base); + +	kfree(mode); +} +EXPORT_SYMBOL(drm_mode_destroy); + +static int drm_mode_create_standard_connector_properties(struct drm_device *dev) +{ +	int i; + +	/* +	 * Standard properties (apply to all connectors) +	 */ +	dev->mode_config.edid_property = +		drm_property_create(dev, DRM_MODE_PROP_BLOB | DRM_MODE_PROP_IMMUTABLE, +				    "EDID", 0); + +	dev->mode_config.dpms_property = +		drm_property_create(dev, DRM_MODE_PROP_ENUM,  +			"DPMS", ARRAY_SIZE(drm_dpms_enum_list)); +	for (i = 0; i < ARRAY_SIZE(drm_dpms_enum_list); i++) +		drm_property_add_enum(dev->mode_config.dpms_property, i, drm_dpms_enum_list[i].type, drm_dpms_enum_list[i].name); + +	return 0; +} + +/** + * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties + * @dev: DRM device + * + * Called by a driver the first time a DVI-I connector is made. + */ +int drm_mode_create_dvi_i_properties(struct drm_device *dev) +{ +	int i; + +	if (dev->mode_config.dvi_i_select_subconnector_property) +		return 0; + +	dev->mode_config.dvi_i_select_subconnector_property = drm_property_create(dev, DRM_MODE_PROP_ENUM, +				    "select subconnector", 3); +	/* add enum element 0-2 */ +	for (i = 0; i < 3; i++) +		drm_property_add_enum(dev->mode_config.dvi_i_select_subconnector_property, i, drm_select_subconnector_enum_list[i].type, +				drm_select_subconnector_enum_list[i].name); + +	/* This is a property which indicates the most likely thing to be connected. */ +	dev->mode_config.dvi_i_subconnector_property = drm_property_create(dev, DRM_MODE_PROP_ENUM | DRM_MODE_PROP_IMMUTABLE, +				    "subconnector", 3); +	/* add enum element 0-2 */ +	for (i = 0; i < 3; i++) +		drm_property_add_enum(dev->mode_config.dvi_i_subconnector_property, i, drm_subconnector_enum_list[i].type, +				drm_subconnector_enum_list[i].name); + +	return 0; +} +EXPORT_SYMBOL(drm_mode_create_dvi_i_properties); + +/** + * drm_create_tv_properties - create TV specific connector properties + * @dev: DRM device + * @num_modes: number of different TV formats (modes) supported + * @modes: array of pointers to strings containing name of each format + * + * Called by a driver's TV initialization routine, this function creates + * the TV specific connector properties for a given device.  Caller is + * responsible for allocating a list of format names and passing them to + * this routine. + */ +int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes, +			      char *modes[]) +{ +	int i; + +	if (dev->mode_config.tv_select_subconnector_property) /* already done */ +		return 0; + +	dev->mode_config.tv_select_subconnector_property = drm_property_create(dev, DRM_MODE_PROP_ENUM, +				    "select subconnector", 4); +	/* add enum element 0 */ +	drm_property_add_enum(dev->mode_config.tv_select_subconnector_property, 0, drm_select_subconnector_enum_list[0].type, +				drm_select_subconnector_enum_list[0].name); +	/* add enum element 3-5 */ +	for (i = 1; i < 4; i++) +		drm_property_add_enum(dev->mode_config.tv_select_subconnector_property, i, drm_select_subconnector_enum_list[i + 2].type, +				drm_select_subconnector_enum_list[i + 2].name); + +	/* This is a property which indicates the most likely thing to be connected. */ +	dev->mode_config.tv_subconnector_property = drm_property_create(dev, DRM_MODE_PROP_ENUM | DRM_MODE_PROP_IMMUTABLE, +				    "subconnector", 4); +	/* add enum element 0 */ +	drm_property_add_enum(dev->mode_config.tv_subconnector_property, 0, drm_subconnector_enum_list[0].type, +				drm_subconnector_enum_list[0].name); +	/* add enum element 3-5 */ +	for (i = 1; i < 4; i++) +		drm_property_add_enum(dev->mode_config.tv_subconnector_property, i, drm_subconnector_enum_list[i + 2].type, +				drm_subconnector_enum_list[i + 2].name); + +	dev->mode_config.tv_left_margin_property = +		drm_property_create(dev, DRM_MODE_PROP_RANGE, +				    "left margin", 2); +	dev->mode_config.tv_left_margin_property->values[0] = 0; +	dev->mode_config.tv_left_margin_property->values[1] = 100; + +	dev->mode_config.tv_right_margin_property = +		drm_property_create(dev, DRM_MODE_PROP_RANGE, +				    "right margin", 2); +	dev->mode_config.tv_right_margin_property->values[0] = 0; +	dev->mode_config.tv_right_margin_property->values[1] = 100; + +	dev->mode_config.tv_top_margin_property = +		drm_property_create(dev, DRM_MODE_PROP_RANGE, +				    "top margin", 2); +	dev->mode_config.tv_top_margin_property->values[0] = 0; +	dev->mode_config.tv_top_margin_property->values[1] = 100; + +	dev->mode_config.tv_bottom_margin_property = +		drm_property_create(dev, DRM_MODE_PROP_RANGE, +				    "bottom margin", 2); +	dev->mode_config.tv_bottom_margin_property->values[0] = 0; +	dev->mode_config.tv_bottom_margin_property->values[1] = 100; + +	dev->mode_config.tv_mode_property = +		drm_property_create(dev, DRM_MODE_PROP_ENUM, +				    "mode", num_modes); +	for (i = 0; i < num_modes; i++) +		drm_property_add_enum(dev->mode_config.tv_mode_property, i, +				      i, modes[i]); + +	return 0; +} +EXPORT_SYMBOL(drm_mode_create_tv_properties); + +/** + * drm_mode_create_scaling_mode_property - create scaling mode property + * @dev: DRM device + * + * Called by a driver the first time it's needed, must be attached to desired connectors. + */ +int drm_mode_create_scaling_mode_property(struct drm_device *dev) +{ +	int i; + +	if (dev->mode_config.scaling_mode_property) /* already done */ +		return 0; + +	dev->mode_config.scaling_mode_property = +		drm_property_create(dev, DRM_MODE_PROP_ENUM,  +			"scaling mode", ARRAY_SIZE(drm_scaling_mode_enum_list)); +	for (i = 0; i < ARRAY_SIZE(drm_scaling_mode_enum_list); i++) +		drm_property_add_enum(dev->mode_config.scaling_mode_property, i, drm_scaling_mode_enum_list[i].type, drm_scaling_mode_enum_list[i].name); + +	return 0; +} +EXPORT_SYMBOL(drm_mode_create_scaling_mode_property); + +/** + * drm_mode_create_dithering_property - create dithering property + * @dev: DRM device + * + * Called by a driver the first time it's needed, must be attached to desired connectors. + */ +int drm_mode_create_dithering_property(struct drm_device *dev) +{ +	int i; + +	if (dev->mode_config.dithering_mode_property) /* already done */ +		return 0; + +	dev->mode_config.dithering_mode_property = +		drm_property_create(dev, DRM_MODE_PROP_ENUM,  +			"dithering", ARRAY_SIZE(drm_dithering_mode_enum_list)); +	for (i = 0; i < ARRAY_SIZE(drm_dithering_mode_enum_list); i++) +		drm_property_add_enum(dev->mode_config.dithering_mode_property, i, drm_dithering_mode_enum_list[i].type, drm_dithering_mode_enum_list[i].name); + +	return 0; +} +EXPORT_SYMBOL(drm_mode_create_dithering_property); + +/** + * drm_mode_config_init - initialize DRM mode_configuration structure + * @dev: DRM device + * + * LOCKING: + * None, should happen single threaded at init time. + * + * Initialize @dev's mode_config structure, used for tracking the graphics + * configuration of @dev. + */ +void drm_mode_config_init(struct drm_device *dev) +{ +	mutex_init(&dev->mode_config.mutex); +	INIT_LIST_HEAD(&dev->mode_config.fb_list); +	INIT_LIST_HEAD(&dev->mode_config.fb_kernel_list); +	INIT_LIST_HEAD(&dev->mode_config.crtc_list); +	INIT_LIST_HEAD(&dev->mode_config.connector_list); +	INIT_LIST_HEAD(&dev->mode_config.encoder_list); +	INIT_LIST_HEAD(&dev->mode_config.property_list); +	INIT_LIST_HEAD(&dev->mode_config.property_blob_list); +	idr_init(&dev->mode_config.crtc_idr); + +	drm_mode_create_standard_connector_properties(dev); + +	/* Just to be sure */ +	dev->mode_config.num_fb = 0; +	dev->mode_config.num_connector = 0; +	dev->mode_config.num_crtc = 0; +	dev->mode_config.num_encoder = 0; +	dev->mode_config.hotplug_counter = 0; +} +EXPORT_SYMBOL(drm_mode_config_init); + +int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *group) +{ +	uint32_t total_objects = 0; + +	total_objects += dev->mode_config.num_crtc; +	total_objects += dev->mode_config.num_connector; +	total_objects += dev->mode_config.num_encoder; + +	if (total_objects == 0) +		return -EINVAL; + +	group->id_list = kzalloc(total_objects * sizeof(uint32_t), GFP_KERNEL); +	if (!group->id_list) +		return -ENOMEM; + +	group->num_crtcs = 0; +	group->num_connectors = 0; +	group->num_encoders = 0; +	return 0; +} + +int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group) +{ +	struct drm_crtc *crtc; +	struct drm_encoder *encoder; +	struct drm_connector *connector; +	int ret; + +	if ((ret = drm_mode_group_init(dev, group))) +		return ret; + +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) +		group->id_list[group->num_crtcs++] = crtc->base.id; + +	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) +		group->id_list[group->num_crtcs + group->num_encoders++] = encoder->base.id; + +	list_for_each_entry(connector, &dev->mode_config.connector_list, head) +		group->id_list[group->num_crtcs + group->num_encoders + group->num_connectors++] = connector->base.id; + +	return 0; +} + +/** + * drm_mode_config_cleanup - free up DRM mode_config info + * @dev: DRM device + * + * LOCKING: + * Caller must hold mode config lock. + * + * Free up all the connectors and CRTCs associated with this DRM device, then + * free up the framebuffers and associated buffer objects. + * + * FIXME: cleanup any dangling user buffer objects too + */ +void drm_mode_config_cleanup(struct drm_device *dev) +{ +	struct drm_connector *connector, *ot; +	struct drm_crtc *crtc, *ct; +	struct drm_encoder *encoder, *enct; +	struct drm_framebuffer *fb, *fbt; +	struct drm_property *property, *pt; + +	list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list, head) { +		encoder->funcs->destroy(encoder); +	} + +	list_for_each_entry_safe(connector, ot, &dev->mode_config.connector_list, head) { +		connector->funcs->destroy(connector); +	} + +	list_for_each_entry_safe(property, pt, &dev->mode_config.property_list, head) { +		drm_property_destroy(dev, property); +	} + +	list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { +		fb->funcs->destroy(fb); +	} + +	list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) { +		crtc->funcs->destroy(crtc); +	} + +} +EXPORT_SYMBOL(drm_mode_config_cleanup); + +int drm_mode_hotplug_ioctl(struct drm_device *dev, +			   void *data, struct drm_file *file_priv) +{ +	struct drm_mode_hotplug *arg = data; + +	arg->counter = dev->mode_config.hotplug_counter; + +	return 0; +} + +/** + * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo + * @out: drm_mode_modeinfo struct to return to the user + * @in: drm_display_mode to use + * + * LOCKING: + * None. + * + * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to + * the user. + */ +void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, struct drm_display_mode *in) +{ +	out->clock = in->clock; +	out->hdisplay = in->hdisplay; +	out->hsync_start = in->hsync_start; +	out->hsync_end = in->hsync_end; +	out->htotal = in->htotal; +	out->hskew = in->hskew; +	out->vdisplay = in->vdisplay; +	out->vsync_start = in->vsync_start; +	out->vsync_end = in->vsync_end; +	out->vtotal = in->vtotal; +	out->vscan = in->vscan; +	out->vrefresh = in->vrefresh; +	out->flags = in->flags; +	out->type = in->type; +	strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); +	out->name[DRM_DISPLAY_MODE_LEN-1] = 0; +} + +/** + * drm_crtc_convert_to_umode - convert a modeinfo into a drm_display_mode + * @out: drm_display_mode to return to the user + * @in: drm_mode_modeinfo to use + * + * LOCKING: + * None. + * + * Convert a drmo_mode_modeinfo into a drm_display_mode structure to return to + * the caller. + */ +void drm_crtc_convert_umode(struct drm_display_mode *out, struct drm_mode_modeinfo *in) +{ +	out->clock = in->clock; +	out->hdisplay = in->hdisplay; +	out->hsync_start = in->hsync_start; +	out->hsync_end = in->hsync_end; +	out->htotal = in->htotal; +	out->hskew = in->hskew; +	out->vdisplay = in->vdisplay; +	out->vsync_start = in->vsync_start; +	out->vsync_end = in->vsync_end; +	out->vtotal = in->vtotal; +	out->vscan = in->vscan; +	out->vrefresh = in->vrefresh; +	out->flags = in->flags; +	out->type = in->type; +	strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); +	out->name[DRM_DISPLAY_MODE_LEN-1] = 0; +} + +/** + * drm_mode_getresources - get graphics configuration + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * LOCKING: + * Takes mode config lock. + * + * Construct a set of configuration description structures and return + * them to the user, including CRTC, connector and framebuffer configuration. + * + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_getresources(struct drm_device *dev, +			  void *data, struct drm_file *file_priv) +{ +	struct drm_mode_card_res *card_res = data; +	struct list_head *lh; +	struct drm_framebuffer *fb; +	struct drm_connector *connector; +	struct drm_crtc *crtc; +	struct drm_encoder *encoder; +	int ret = 0; +	int connector_count = 0; +	int crtc_count = 0; +	int fb_count = 0; +	int encoder_count = 0; +	int copied = 0, i; +	uint32_t __user *fb_id; +	uint32_t __user *crtc_id; +	uint32_t __user *connector_id; +	uint32_t __user *encoder_id; +	struct drm_mode_group *mode_group; + +	mutex_lock(&dev->mode_config.mutex); + +	/* for the non-control nodes we need to limit the list of resources by IDs in the +	   group list for this node */ +	list_for_each(lh, &file_priv->fbs) +		fb_count++; + +	mode_group = &file_priv->master->minor->mode_group; +	if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { + +		list_for_each(lh, &dev->mode_config.crtc_list) +			crtc_count++; + +		list_for_each(lh, &dev->mode_config.connector_list) +			connector_count++; + +		list_for_each(lh, &dev->mode_config.encoder_list) +			encoder_count++; +	} else { + +		crtc_count = mode_group->num_crtcs; +		connector_count = mode_group->num_connectors; +		encoder_count = mode_group->num_encoders; +	} + +	card_res->max_height = dev->mode_config.max_height; +	card_res->min_height = dev->mode_config.min_height; +	card_res->max_width = dev->mode_config.max_width; +	card_res->min_width = dev->mode_config.min_width; + +	/* handle this in 4 parts */ +	/* FBs */ +	if (card_res->count_fbs >= fb_count) { +		copied = 0; +		fb_id = (uint32_t *)(unsigned long)card_res->fb_id_ptr; +		list_for_each_entry(fb, &file_priv->fbs, head) { +			if (put_user(fb->base.id, fb_id + copied)) { +				ret = -EFAULT; +				goto out; +			} +			copied++; +		} +	} +	card_res->count_fbs = fb_count; + +	/* CRTCs */ +	if (card_res->count_crtcs >= crtc_count) { +		copied = 0; +		crtc_id = (uint32_t *)(unsigned long)card_res->crtc_id_ptr; +		if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { +			list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +				DRM_DEBUG("CRTC ID is %d\n", crtc->base.id); +				if (put_user(crtc->base.id, crtc_id + copied)) { +					ret = -EFAULT; +					goto out; +				} +				copied++; +			} +		} else { +			for (i = 0; i < mode_group->num_crtcs; i++) { +				if (put_user(mode_group->id_list[i], crtc_id + copied)) { +					ret = -EFAULT; +					goto out; +				} +				copied++; +			} +		} +	} +	card_res->count_crtcs = crtc_count; + +	/* Encoders */ +	if (card_res->count_encoders >= encoder_count) { +		copied = 0; +		encoder_id = (uint32_t *)(unsigned long)card_res->encoder_id_ptr; +		if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { +			list_for_each_entry(encoder, &dev->mode_config.encoder_list, +					    head) { +				DRM_DEBUG("ENCODER ID is %d\n", encoder->base.id); +				if (put_user(encoder->base.id, encoder_id + copied)) { +					ret = -EFAULT; +					goto out; +				} +				copied++; +			} +		} else { +			for (i = mode_group->num_crtcs; i < mode_group->num_crtcs + mode_group->num_encoders; i++) { +				if (put_user(mode_group->id_list[i], encoder_id + copied)) { +					ret = -EFAULT; +					goto out; +				} +				copied++; +			} + +		} +	} +	card_res->count_encoders = encoder_count; + +	/* Connectors */ +	if (card_res->count_connectors >= connector_count) { +		copied = 0; +		connector_id = (uint32_t *)(unsigned long)card_res->connector_id_ptr; +		if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { +			list_for_each_entry(connector, &dev->mode_config.connector_list, +					    head) { +				DRM_DEBUG("CONNECTOR ID is %d\n", connector->base.id); +				if (put_user(connector->base.id, connector_id + copied)) { +					ret = -EFAULT; +					goto out; +				} +				copied++; +			} +		} else { +			int start = mode_group->num_crtcs + mode_group->num_encoders; +			for (i = start; i < start + mode_group->num_connectors; i++) { +				if (put_user(mode_group->id_list[i], connector_id + copied)) { +					ret = -EFAULT; +					goto out; +				} +				copied++; +			} +		} +	} +	card_res->count_connectors = connector_count; + +	DRM_DEBUG("Counted %d %d %d\n", card_res->count_crtcs, +		  card_res->count_connectors, card_res->count_encoders); + +out: +	mutex_unlock(&dev->mode_config.mutex); +	return ret; +} + +/** + * drm_mode_getcrtc - get CRTC configuration + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * LOCKING: + * Caller? (FIXME) + * + * Construct a CRTC configuration structure to return to the user. + * + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_getcrtc(struct drm_device *dev, +		     void *data, struct drm_file *file_priv) +{ +	struct drm_mode_crtc *crtc_resp = data; +	struct drm_crtc *crtc; +	struct drm_mode_object *obj; +	int ret = 0; + +	mutex_lock(&dev->mode_config.mutex); + +	obj = drm_mode_object_find(dev, crtc_resp->crtc_id, DRM_MODE_OBJECT_CRTC); +	if (!obj) { +		ret = -EINVAL; +		goto out; +	} +	crtc = obj_to_crtc(obj); + +	crtc_resp->x = crtc->x; +	crtc_resp->y = crtc->y; +	crtc_resp->gamma_size = crtc->gamma_size; +	if (crtc->fb) +		crtc_resp->fb_id = crtc->fb->base.id; +	else +		crtc_resp->fb_id = 0; + +	if (crtc->enabled) { + +		drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->mode); +		crtc_resp->mode_valid = 1; + +	} else { +		crtc_resp->mode_valid = 0; +	} + +out: +	mutex_unlock(&dev->mode_config.mutex); +	return ret; +} + +/** + * drm_mode_getconnector - get connector configuration + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * LOCKING: + * Caller? (FIXME) + * + * Construct a connector configuration structure to return to the user. + * + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_getconnector(struct drm_device *dev, +		       void *data, struct drm_file *file_priv) +{ +	struct drm_mode_get_connector *out_resp = data; +	struct drm_mode_object *obj; +	struct drm_connector *connector; +	struct drm_display_mode *mode; +	int mode_count = 0; +	int props_count = 0; +	int encoders_count = 0; +	int ret = 0; +	int copied = 0; +	int i; +	struct drm_mode_modeinfo u_mode; +	struct drm_mode_modeinfo __user *mode_ptr; +	uint32_t __user *prop_ptr; +	uint64_t __user *prop_values; +	uint32_t __user *encoder_ptr; + +	memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); + +	DRM_DEBUG("connector id %d:\n", out_resp->connector_id); + +	mutex_lock(&dev->mode_config.mutex); + +	obj = drm_mode_object_find(dev, out_resp->connector_id, DRM_MODE_OBJECT_CONNECTOR); +	if (!obj) { +		ret = -EINVAL; +		goto out; +	} +	connector = obj_to_connector(obj); + +	for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { +		if (connector->property_ids[i] != 0) { +			props_count++; +		} +	} + +	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { +		if (connector->encoder_ids[i] != 0) { +			encoders_count++; +		} +	} + +	if (out_resp->count_modes == 0) { +		connector->funcs->fill_modes(connector, dev->mode_config.max_width, dev->mode_config.max_height); +	} + +	/* delayed so we get modes regardless of pre-fill_modes state */ +	list_for_each_entry(mode, &connector->modes, head) +		mode_count++; + +	out_resp->connector_id = connector->base.id; +	out_resp->connector_type = connector->connector_type; +	out_resp->connector_type_id = connector->connector_type_id; +	out_resp->mm_width = connector->display_info.width_mm; +	out_resp->mm_height = connector->display_info.height_mm; +	out_resp->subpixel = connector->display_info.subpixel_order; +	out_resp->connection = connector->status; +	if (connector->encoder) +		out_resp->encoder_id = connector->encoder->base.id; +	else +		out_resp->encoder_id = 0; + +	/* this ioctl is called twice, once to determine how much space is needed, and the 2nd time to fill it */ +	if ((out_resp->count_modes >= mode_count) && mode_count) { +		copied = 0; +		mode_ptr = (struct drm_mode_modeinfo *)(unsigned long)out_resp->modes_ptr; +		list_for_each_entry(mode, &connector->modes, head) { +			drm_crtc_convert_to_umode(&u_mode, mode); +			if (copy_to_user(mode_ptr + copied, +					 &u_mode, sizeof(u_mode))) { +				ret = -EFAULT; +				goto out; +			} +			copied++; +		} +	} +	out_resp->count_modes = mode_count; + +	if ((out_resp->count_props >= props_count) && props_count) { +		copied = 0; +		prop_ptr = (uint32_t *)(unsigned long)(out_resp->props_ptr); +		prop_values = (uint64_t *)(unsigned long)(out_resp->prop_values_ptr); +		for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { +			if (connector->property_ids[i] != 0) { +				if (put_user(connector->property_ids[i], prop_ptr + copied)) { +					ret = -EFAULT; +					goto out; +				} + +				if (put_user(connector->property_values[i], prop_values + copied)) { +					ret = -EFAULT; +					goto out; +				} +				copied++; +			} +		} +	} +	out_resp->count_props = props_count; + +	if ((out_resp->count_encoders >= encoders_count) && encoders_count) { +		copied = 0; +		encoder_ptr = (uint32_t *)(unsigned long)(out_resp->encoders_ptr); +		for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { +			if (connector->encoder_ids[i] != 0) { +				if (put_user(connector->encoder_ids[i], encoder_ptr + copied)) { +					ret = -EFAULT; +					goto out; +				} +				copied++; +			} +		} +	} +	out_resp->count_encoders = encoders_count; + +out: +	mutex_unlock(&dev->mode_config.mutex); +	return ret; +} + +int drm_mode_getencoder(struct drm_device *dev, +			void *data, struct drm_file *file_priv) +{ +	struct drm_mode_get_encoder *enc_resp = data; +	struct drm_mode_object *obj; +	struct drm_encoder *encoder; +	int ret = 0; + +	mutex_lock(&dev->mode_config.mutex); +	obj = drm_mode_object_find(dev, enc_resp->encoder_id, DRM_MODE_OBJECT_ENCODER); +	if (!obj) { +		ret = -EINVAL; +		goto out; +	} +	encoder = obj_to_encoder(obj); + +	if (encoder->crtc) +		enc_resp->crtc_id = encoder->crtc->base.id; +	else +		enc_resp->crtc_id = 0; +	enc_resp->encoder_type = encoder->encoder_type; +	enc_resp->encoder_id = encoder->base.id; +	enc_resp->possible_crtcs = encoder->possible_crtcs; +	enc_resp->possible_clones = encoder->possible_clones; + +out: +	mutex_unlock(&dev->mode_config.mutex); +	return ret; +} + +/** + * drm_mode_setcrtc - set CRTC configuration + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * LOCKING: + * Caller? (FIXME) + * + * Build a new CRTC configuration based on user request. + * + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_setcrtc(struct drm_device *dev, +		     void *data, struct drm_file *file_priv) +{ +	struct drm_mode_crtc *crtc_req = data; +	struct drm_mode_object *obj; +	struct drm_crtc *crtc, *crtcfb; +	struct drm_connector **connector_set = NULL, *connector; +	struct drm_framebuffer *fb = NULL; +	struct drm_display_mode *mode = NULL; +	struct drm_mode_set set; +	uint32_t __user *set_connectors_ptr; +	int ret = 0; +	int i; + +	mutex_lock(&dev->mode_config.mutex); +	obj = drm_mode_object_find(dev, crtc_req->crtc_id, DRM_MODE_OBJECT_CRTC); +	if (!obj) { +		DRM_DEBUG("Unknown CRTC ID %d\n", crtc_req->crtc_id); +		ret = -EINVAL; +		goto out; +	} +	crtc = obj_to_crtc(obj); + +	if (crtc_req->mode_valid) { +		/* If we have a mode we need a framebuffer. */ +		/* If we pass -1, set the mode with the currently bound fb */ +		if (crtc_req->fb_id == -1) { +			list_for_each_entry(crtcfb, &dev->mode_config.crtc_list, head) { +				if (crtcfb == crtc) { +					DRM_DEBUG("Using current fb for setmode\n"); +					fb = crtc->fb; +				} +			} +		} else { +			obj = drm_mode_object_find(dev, crtc_req->fb_id, DRM_MODE_OBJECT_FB); +			if (!obj) { +				DRM_DEBUG("Unknown FB ID%d\n", crtc_req->fb_id); +				ret = -EINVAL; +				goto out; +			} +			fb = obj_to_fb(obj); +		} + +		mode = drm_mode_create(dev); +		drm_crtc_convert_umode(mode, &crtc_req->mode); +		drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); +	} + +	if (crtc_req->count_connectors == 0 && mode) { +		DRM_DEBUG("Count connectors is 0 but mode set\n"); +		ret = -EINVAL; +		goto out; +	} + +	if (crtc_req->count_connectors > 0 && !mode && !fb) { +		DRM_DEBUG("Count connectors is %d but no mode or fb set\n", crtc_req->count_connectors); +		ret = -EINVAL; +		goto out; +	} + +	if (crtc_req->count_connectors > 0) { +		u32 out_id; +		/* Maybe we should check that count_connectors is a sensible value. */ +		connector_set = kmalloc(crtc_req->count_connectors * +				     sizeof(struct drm_connector *), GFP_KERNEL); +		if (!connector_set) { +			ret = -ENOMEM; +			goto out; +		} + +		for (i = 0; i < crtc_req->count_connectors; i++) { +			set_connectors_ptr = (uint32_t *)(unsigned long)crtc_req->set_connectors_ptr; +			if (get_user(out_id, &set_connectors_ptr[i])) { +				ret = -EFAULT; +				goto out; +			} + +			obj = drm_mode_object_find(dev, out_id, DRM_MODE_OBJECT_CONNECTOR); +			if (!obj) { +				DRM_DEBUG("Connector id %d unknown\n", out_id); +				ret = -EINVAL; +				goto out; +			} +			connector = obj_to_connector(obj); + +			connector_set[i] = connector; +		} +	} + +	set.crtc = crtc; +	set.x = crtc_req->x; +	set.y = crtc_req->y; +	set.mode = mode; +	set.connectors = connector_set; +	set.num_connectors = crtc_req->count_connectors; +	set.fb =fb; +	ret = crtc->funcs->set_config(&set); + +out: +	kfree(connector_set); +	mutex_unlock(&dev->mode_config.mutex); +	return ret; +} + +int drm_mode_cursor_ioctl(struct drm_device *dev, +			void *data, struct drm_file *file_priv) +{ +	struct drm_mode_cursor *req = data; +	struct drm_mode_object *obj; +	struct drm_crtc *crtc; +	int ret = 0; + +	DRM_DEBUG("\n"); + +	if (!req->flags) { +		DRM_ERROR("no operation set\n"); +		return -EINVAL; +	} + +	mutex_lock(&dev->mode_config.mutex); +	obj = drm_mode_object_find(dev, req->crtc, DRM_MODE_OBJECT_CRTC); +	if (!obj) { +		DRM_DEBUG("Unknown CRTC ID %d\n", req->crtc); +		ret = -EINVAL; +		goto out; +	} +	crtc = obj_to_crtc(obj); + +	if (req->flags & DRM_MODE_CURSOR_BO) { +		/* Turn of the cursor if handle is 0 */ +		if (crtc->funcs->cursor_set) { +			ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle, req->width, req->height); +		} else { +			DRM_ERROR("crtc does not support cursor\n"); +			ret = -EFAULT; +			goto out; +		} +	} + +	if (req->flags & DRM_MODE_CURSOR_MOVE) { +		if (crtc->funcs->cursor_move) { +			ret = crtc->funcs->cursor_move(crtc, req->x, req->y); +		} else { +			DRM_ERROR("crtc does not support cursor\n"); +			ret = -EFAULT; +			goto out; +		} +	} +out: +	mutex_unlock(&dev->mode_config.mutex); +	return ret; +} + +/** + * drm_mode_addfb - add an FB to the graphics configuration + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * LOCKING: + * Takes mode config lock. + * + * Add a new FB to the specified CRTC, given a user request. + * + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_addfb(struct drm_device *dev, +		   void *data, struct drm_file *file_priv) +{ +	struct drm_mode_fb_cmd *r = data; +	struct drm_mode_config *config = &dev->mode_config; +	struct drm_framebuffer *fb; +	int ret = 0; + +	if ((config->min_width > r->width) || (r->width > config->max_width)) { +		DRM_ERROR("mode new framebuffer width not within limits\n"); +		return -EINVAL; +	} +	if ((config->min_height > r->height) || (r->height > config->max_height)) { +		DRM_ERROR("mode new framebuffer height not within limits\n"); +		return -EINVAL; +	} + +	mutex_lock(&dev->mode_config.mutex); + +	/* TODO check buffer is sufficently large */ +	/* TODO setup destructor callback */ + +	fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); +	if (!fb) { +		DRM_ERROR("could not create framebuffer\n"); +		ret = -EINVAL; +		goto out; +	} + +	r->buffer_id = fb->base.id; +	list_add(&fb->filp_head, &file_priv->fbs); + +out: +	mutex_unlock(&dev->mode_config.mutex); +	return ret; +} + +/** + * drm_mode_rmfb - remove an FB from the configuration + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * LOCKING: + * Takes mode config lock. + * + * Remove the FB specified by the user. + * + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_rmfb(struct drm_device *dev, +		   void *data, struct drm_file *file_priv) +{ +	struct drm_mode_object *obj; +	struct drm_framebuffer *fb = NULL; +	struct drm_framebuffer *fbl = NULL; +	uint32_t *id = data; +	int ret = 0; +	int found = 0; + +	mutex_lock(&dev->mode_config.mutex); +	obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB); +	/* TODO check that we realy get a framebuffer back. */ +	if (!obj) { +		DRM_ERROR("mode invalid framebuffer id\n"); +		ret = -EINVAL; +		goto out; +	} +	fb = obj_to_fb(obj); + +	list_for_each_entry(fbl, &file_priv->fbs, filp_head) +		if (fb == fbl) +			found = 1; + +	if (!found) { +		DRM_ERROR("tried to remove a fb that we didn't own\n"); +		ret = -EINVAL; +		goto out; +	} + +	/* TODO release all crtc connected to the framebuffer */ +	/* TODO unhock the destructor from the buffer object */ + +	list_del(&fb->filp_head); +	fb->funcs->destroy(fb); + +out: +	mutex_unlock(&dev->mode_config.mutex); +	return ret; +} + +/** + * drm_mode_getfb - get FB info + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * LOCKING: + * Caller? (FIXME) + * + * Lookup the FB given its ID and return info about it. + * + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_getfb(struct drm_device *dev, +		   void *data, struct drm_file *file_priv) +{ +	struct drm_mode_fb_cmd *r = data; +	struct drm_mode_object *obj; +	struct drm_framebuffer *fb; +	int ret = 0; + +	mutex_lock(&dev->mode_config.mutex); +	obj = drm_mode_object_find(dev, r->buffer_id, DRM_MODE_OBJECT_FB); +	if (!obj) { +		DRM_ERROR("invalid framebuffer id\n"); +		ret = -EINVAL; +		goto out; +	} +	fb = obj_to_fb(obj); + +	r->height = fb->height; +	r->width = fb->width; +	r->depth = fb->depth; +	r->bpp = fb->bits_per_pixel; +	r->handle = fb->mm_handle; +	r->pitch = fb->pitch; + +out: +	mutex_unlock(&dev->mode_config.mutex); +	return ret; +} + +/** + * drm_fb_release - remove and free the FBs on this file + * @filp: file * from the ioctl + * + * LOCKING: + * Takes mode config lock. + * + * Destroy all the FBs associated with @filp. + * + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +void drm_fb_release(struct file *filp) +{ +	struct drm_file *priv = filp->private_data; +	struct drm_device *dev = priv->minor->dev; +	struct drm_framebuffer *fb, *tfb; + +	mutex_lock(&dev->mode_config.mutex); +	list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { +		list_del(&fb->filp_head); +		fb->funcs->destroy(fb); +	} +	mutex_unlock(&dev->mode_config.mutex); +} + +/* + * + */ + +static int drm_mode_attachmode(struct drm_device *dev, +			       struct drm_connector *connector, +			       struct drm_display_mode *mode) +{ +	int ret = 0; + +	list_add_tail(&mode->head, &connector->user_modes); +	return ret; +} + +int drm_mode_attachmode_crtc(struct drm_device *dev, struct drm_crtc *crtc, +			     struct drm_display_mode *mode) +{ +	struct drm_connector *connector; +	int ret = 0; +	struct drm_display_mode *dup_mode; +	int need_dup = 0; +	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { +		if (!connector->encoder) +			break; +		if (connector->encoder->crtc == crtc) { +			if (need_dup) +				dup_mode = drm_mode_duplicate(dev, mode); +			else +				dup_mode = mode; +			ret = drm_mode_attachmode(dev, connector, dup_mode);  +			if (ret) +				return ret; +			need_dup = 1; +		} +	} +	return 0; +} +EXPORT_SYMBOL(drm_mode_attachmode_crtc); + +static int drm_mode_detachmode(struct drm_device *dev, +			       struct drm_connector *connector, +			       struct drm_display_mode *mode) +{ +	int found = 0; +	int ret = 0; +	struct drm_display_mode *match_mode, *t; + +	list_for_each_entry_safe(match_mode, t, &connector->user_modes, head) { +		if (drm_mode_equal(match_mode, mode)) { +			list_del(&match_mode->head); +			drm_mode_destroy(dev, match_mode); +			found = 1; +			break; +		} +	} + +	if (!found) +		ret = -EINVAL; + +	return ret; +} + +int drm_mode_detachmode_crtc(struct drm_device *dev, struct drm_display_mode *mode) +{ +	struct drm_connector *connector; + +	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { +		drm_mode_detachmode(dev, connector, mode); +	} +	return 0; +} +EXPORT_SYMBOL(drm_mode_detachmode_crtc); + +/** + * drm_fb_attachmode - Attach a user mode to an connector + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * This attaches a user specified mode to an connector. + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_attachmode_ioctl(struct drm_device *dev, +			      void *data, struct drm_file *file_priv) +{ +	struct drm_mode_mode_cmd *mode_cmd = data; +	struct drm_connector *connector; +	struct drm_display_mode *mode; +	struct drm_mode_object *obj; +	struct drm_mode_modeinfo *umode = &mode_cmd->mode; +	int ret = 0; + +	mutex_lock(&dev->mode_config.mutex); + +	obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); +	if (!obj) { +		ret = -EINVAL; +		goto out; +	} +	connector = obj_to_connector(obj); + +	mode = drm_mode_create(dev); +	if (!mode) { +		ret = -ENOMEM; +		goto out; +	} + +	drm_crtc_convert_umode(mode, umode); + +	ret = drm_mode_attachmode(dev, connector, mode); +out: +	mutex_unlock(&dev->mode_config.mutex); +	return ret; +} + + +/** + * drm_fb_detachmode - Detach a user specified mode from an connector + * @inode: inode from the ioctl + * @filp: file * from the ioctl + * @cmd: cmd from ioctl + * @arg: arg from ioctl + * + * Called by the user via ioctl. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_mode_detachmode_ioctl(struct drm_device *dev, +			      void *data, struct drm_file *file_priv) +{ +	struct drm_mode_object *obj; +	struct drm_mode_mode_cmd *mode_cmd = data; +	struct drm_connector *connector; +	struct drm_display_mode mode; +	struct drm_mode_modeinfo *umode = &mode_cmd->mode; +	int ret = 0; + +	mutex_lock(&dev->mode_config.mutex); + +	obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); +	if (!obj) { +		ret = -EINVAL; +		goto out; +	} +	connector = obj_to_connector(obj); + +	drm_crtc_convert_umode(&mode, umode); +	ret = drm_mode_detachmode(dev, connector, &mode); +out:	        +	mutex_unlock(&dev->mode_config.mutex); +	return ret; +} + +struct drm_property *drm_property_create(struct drm_device *dev, int flags, +					 const char *name, int num_values) +{ +	struct drm_property *property = NULL; + +	property = kzalloc(sizeof(struct drm_property), GFP_KERNEL); +	if (!property) +		return NULL; + +	if (num_values) { +		property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL); +		if (!property->values) +			goto fail; +	} + +	drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY); +	property->flags = flags; +	property->num_values = num_values; +	INIT_LIST_HEAD(&property->enum_blob_list); + +	if (name) +		strncpy(property->name, name, DRM_PROP_NAME_LEN); + +	list_add_tail(&property->head, &dev->mode_config.property_list); +	return property; +fail: +	kfree(property); +	return NULL; +} +EXPORT_SYMBOL(drm_property_create); + +int drm_property_add_enum(struct drm_property *property, int index, +			  uint64_t value, const char *name) +{ +	struct drm_property_enum *prop_enum; + +	if (!(property->flags & DRM_MODE_PROP_ENUM)) +		return -EINVAL; + +	if (!list_empty(&property->enum_blob_list)) { +		list_for_each_entry(prop_enum, &property->enum_blob_list, head) { +			if (prop_enum->value == value) { +				strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN);  +				prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; +				return 0; +			} +		} +	} + +	prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL); +	if (!prop_enum) +		return -ENOMEM; + +	strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN);  +	prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; +	prop_enum->value = value; + +	property->values[index] = value; +	list_add_tail(&prop_enum->head, &property->enum_blob_list); +	return 0; +} +EXPORT_SYMBOL(drm_property_add_enum); + +void drm_property_destroy(struct drm_device *dev, struct drm_property *property) +{ +	struct drm_property_enum *prop_enum, *pt; + +	list_for_each_entry_safe(prop_enum, pt, &property->enum_blob_list, head) { +		list_del(&prop_enum->head); +		kfree(prop_enum); +	} + +	if (property->num_values) +		kfree(property->values); +	drm_mode_object_put(dev, &property->base); +	list_del(&property->head); +	kfree(property); +} +EXPORT_SYMBOL(drm_property_destroy); + +int drm_connector_attach_property(struct drm_connector *connector, +			       struct drm_property *property, uint64_t init_val) +{ +	int i; + +	for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { +		if (connector->property_ids[i] == 0) { +			connector->property_ids[i] = property->base.id; +			connector->property_values[i] = init_val; +			break; +		} +	} + +	if (i == DRM_CONNECTOR_MAX_PROPERTY) +		return -EINVAL; +	return 0; +} +EXPORT_SYMBOL(drm_connector_attach_property); + +int drm_connector_property_set_value(struct drm_connector *connector, +				  struct drm_property *property, uint64_t value) +{ +	int i; + +	for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { +		if (connector->property_ids[i] == property->base.id) { +			connector->property_values[i] = value; +			break; +		} +	} + +	if (i == DRM_CONNECTOR_MAX_PROPERTY) +		return -EINVAL; +	return 0; +} +EXPORT_SYMBOL(drm_connector_property_set_value); + +int drm_connector_property_get_value(struct drm_connector *connector, +				  struct drm_property *property, uint64_t *val) +{ +	int i; + +	for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { +		if (connector->property_ids[i] == property->base.id) { +			*val = connector->property_values[i]; +			break; +		} +	} + +	if (i == DRM_CONNECTOR_MAX_PROPERTY) +		return -EINVAL; +	return 0; +} +EXPORT_SYMBOL(drm_connector_property_get_value); + +int drm_mode_getproperty_ioctl(struct drm_device *dev, +			       void *data, struct drm_file *file_priv) +{ +	struct drm_mode_object *obj; +	struct drm_mode_get_property *out_resp = data; +	struct drm_property *property; +	int enum_count = 0; +	int blob_count = 0; +	int value_count = 0; +	int ret = 0, i; +	int copied; +	struct drm_property_enum *prop_enum; +	struct drm_mode_property_enum __user *enum_ptr; +	struct drm_property_blob *prop_blob; +	uint32_t *blob_id_ptr; +	uint64_t __user *values_ptr; +	uint32_t __user *blob_length_ptr; + +	mutex_lock(&dev->mode_config.mutex); +	obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); +	if (!obj) { +		ret = -EINVAL; +		goto done; +	} +	property = obj_to_property(obj); + +	if (property->flags & DRM_MODE_PROP_ENUM) { +		list_for_each_entry(prop_enum, &property->enum_blob_list, head) +			enum_count++; +	} else if (property->flags & DRM_MODE_PROP_BLOB) { +		list_for_each_entry(prop_blob, &property->enum_blob_list, head) +			blob_count++; +	} + +	value_count = property->num_values; + +	strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN); +	out_resp->name[DRM_PROP_NAME_LEN-1] = 0; +	out_resp->flags = property->flags; + +	if ((out_resp->count_values >= value_count) && value_count) { +		values_ptr = (uint64_t *)(unsigned long)out_resp->values_ptr; +		for (i = 0; i < value_count; i++) { +			if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) { +				ret = -EFAULT; +				goto done; +			} +		} +	} +	out_resp->count_values = value_count; + +	if (property->flags & DRM_MODE_PROP_ENUM) { +		if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { +			copied = 0; +			enum_ptr = (struct drm_mode_property_enum *)(unsigned long)out_resp->enum_blob_ptr; +			list_for_each_entry(prop_enum, &property->enum_blob_list, head) { + +				if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) { +					ret = -EFAULT; +					goto done; +				} + +				if (copy_to_user(&enum_ptr[copied].name, +						 &prop_enum->name, DRM_PROP_NAME_LEN)) { +					ret = -EFAULT; +					goto done; +				} +				copied++; +			} +		} +		out_resp->count_enum_blobs = enum_count; +	} + +	if (property->flags & DRM_MODE_PROP_BLOB) { +		if ((out_resp->count_enum_blobs >= blob_count) && blob_count) { +			copied = 0; +			blob_id_ptr = (uint32_t *)(unsigned long)out_resp->enum_blob_ptr; +			blob_length_ptr = (uint32_t *)(unsigned long)out_resp->values_ptr; + +			list_for_each_entry(prop_blob, &property->enum_blob_list, head) { +				if (put_user(prop_blob->base.id, blob_id_ptr + copied)) { +					ret = -EFAULT; +					goto done; +				} + +				if (put_user(prop_blob->length, blob_length_ptr + copied)) { +					ret = -EFAULT; +					goto done; +				} + +				copied++; +			} +		} +		out_resp->count_enum_blobs = blob_count; +	} +done: +	mutex_unlock(&dev->mode_config.mutex); +	return ret; +} + +static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, int length, +							  void *data) +{ +	struct drm_property_blob *blob; + +	if (!length || !data) +		return NULL; + +	blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL); +	if (!blob) +		return NULL; + +	blob->data = (void *)((char *)blob + sizeof(struct drm_property_blob)); +	blob->length = length; + +	memcpy(blob->data, data, length); + +	drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB); + +	list_add_tail(&blob->head, &dev->mode_config.property_blob_list); +	return blob; +} + +static void drm_property_destroy_blob(struct drm_device *dev, +			       struct drm_property_blob *blob) +{ +	drm_mode_object_put(dev, &blob->base); +	list_del(&blob->head); +	kfree(blob); +} + +int drm_mode_getblob_ioctl(struct drm_device *dev, +			   void *data, struct drm_file *file_priv) +{ +	struct drm_mode_object *obj; +	struct drm_mode_get_blob *out_resp = data; +	struct drm_property_blob *blob; +	int ret = 0; +	void *blob_ptr; + +	mutex_lock(&dev->mode_config.mutex); +	obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB); +	if (!obj) { +		ret = -EINVAL; +		goto done; +	} +	blob = obj_to_blob(obj); + +	if (out_resp->length == blob->length) { +		blob_ptr = (void *)(unsigned long)out_resp->data; +		if (copy_to_user(blob_ptr, blob->data, blob->length)){ +			ret = -EFAULT; +			goto done; +		} +	} +	out_resp->length = blob->length; + +done: +	mutex_unlock(&dev->mode_config.mutex); +	return ret; +} + +int drm_mode_connector_update_edid_property(struct drm_connector *connector, struct edid *edid) +{ +	struct drm_device *dev = connector->dev; +	int ret = 0; +	if (connector->edid_blob_ptr) +		drm_property_destroy_blob(dev, connector->edid_blob_ptr); + +	/* Delete edid, when there is none. */ +	if (!edid) { +		connector->edid_blob_ptr = NULL; +		ret = drm_connector_property_set_value(connector, dev->mode_config.edid_property, 0); +		return ret; +	} + +	connector->edid_blob_ptr = drm_property_create_blob(connector->dev, 128, edid); + +	ret = drm_connector_property_set_value(connector, dev->mode_config.edid_property, connector->edid_blob_ptr->base.id); +	return ret; +} +EXPORT_SYMBOL(drm_mode_connector_update_edid_property); + +int drm_mode_connector_property_set_ioctl(struct drm_device *dev, +				       void *data, struct drm_file *file_priv) +{ +	struct drm_mode_connector_set_property *out_resp = data; +	struct drm_mode_object *obj; +	struct drm_property *property; +	struct drm_connector *connector; +	int ret = -EINVAL; +	int i; + +	mutex_lock(&dev->mode_config.mutex); + +	obj = drm_mode_object_find(dev, out_resp->connector_id, DRM_MODE_OBJECT_CONNECTOR); +	if (!obj) { +		goto out; +	} +	connector = obj_to_connector(obj); + +	for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { +		if (connector->property_ids[i] == out_resp->prop_id) +			break; +	} + +	if (i == DRM_CONNECTOR_MAX_PROPERTY) { +		goto out; +	} + +	obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); +	if (!obj) { +		goto out; +	} +	property = obj_to_property(obj); + +	if (property->flags & DRM_MODE_PROP_IMMUTABLE) +		goto out; + +	if (property->flags & DRM_MODE_PROP_RANGE) { +		if (out_resp->value < property->values[0]) +			goto out; + +		if (out_resp->value > property->values[1]) +			goto out; +	} else { +		int found = 0; +		for (i = 0; i < property->num_values; i++) { +			if (property->values[i] == out_resp->value) { +				found = 1; +				break; +			} +		} +		if (!found) { +			goto out; +		} +	} + +	if (connector->funcs->set_property) +		ret = connector->funcs->set_property(connector, property, out_resp->value); + +	/* store the property value if succesful */ +	if (!ret) +		drm_connector_property_set_value(connector, property, out_resp->value); +out: +	mutex_unlock(&dev->mode_config.mutex); +	return ret; +} + + +int drm_mode_replacefb(struct drm_device *dev, +		       void *data, struct drm_file *file_priv) +{ +	struct drm_mode_fb_cmd *r = data; +	struct drm_mode_object *obj; +	struct drm_framebuffer *fb; +	int found = 0; +	struct drm_framebuffer *fbl = NULL; +	int ret = 0; + +	/* right replace the current bo attached to this fb with a new bo */ +	mutex_lock(&dev->mode_config.mutex); +	obj = drm_mode_object_find(dev, r->buffer_id, DRM_MODE_OBJECT_FB); +	if (!obj) { +		ret = -EINVAL; +		goto out; +	} +	fb = obj_to_fb(obj); + +	list_for_each_entry(fbl, &file_priv->fbs, filp_head) +		if (fb == fbl) +			found = 1; + +	if (!found) { +		DRM_ERROR("tried to replace an fb we didn't own\n"); +		ret = -EINVAL; +		goto out; +	} + +	if (dev->mode_config.funcs->resize_fb) +		ret = dev->mode_config.funcs->resize_fb(dev, file_priv, fb, r); +	else +		ret = -EINVAL; +out: +	mutex_unlock(&dev->mode_config.mutex); +	return ret; + +} + +int drm_mode_connector_attach_encoder(struct drm_connector *connector, +				      struct drm_encoder *encoder) +{ +	int i; + +	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { +		if (connector->encoder_ids[i] == 0) { +			connector->encoder_ids[i] = encoder->base.id; +			return 0; +		} +	} +	return -ENOMEM; +} +EXPORT_SYMBOL(drm_mode_connector_attach_encoder); + +void drm_mode_connector_detach_encoder(struct drm_connector *connector, +				    struct drm_encoder *encoder) +{ +	int i; +	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { +		if (connector->encoder_ids[i] == encoder->base.id) { +			connector->encoder_ids[i] = 0; +			if (connector->encoder == encoder) +				connector->encoder = NULL; +			break; +		} +	} +} +EXPORT_SYMBOL(drm_mode_connector_detach_encoder); + +bool drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, +				  int gamma_size) +{ +	crtc->gamma_size = gamma_size; + +	crtc->gamma_store = kzalloc(gamma_size * sizeof(uint16_t) * 3, GFP_KERNEL); +	if (!crtc->gamma_store) { +		crtc->gamma_size = 0; +		return false; +	} + +	return true; +} +EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size); + +int drm_mode_gamma_set_ioctl(struct drm_device *dev, +			     void *data, struct drm_file *file_priv) +{ +	struct drm_mode_crtc_lut *crtc_lut = data; +	struct drm_mode_object *obj; +	struct drm_crtc *crtc; +	void *r_base, *g_base, *b_base; +	int size; +	int ret = 0; + +	mutex_lock(&dev->mode_config.mutex); +	obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); +	if (!obj) { +		ret = -EINVAL; +		goto out; +	} +	crtc = obj_to_crtc(obj); + +	/* memcpy into gamma store */ +	if (crtc_lut->gamma_size != crtc->gamma_size) { +		ret = -EINVAL; +		goto out; +	} + +	size = crtc_lut->gamma_size * (sizeof(uint16_t)); +	r_base = crtc->gamma_store; +	if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) { +		ret = -EFAULT; +		goto out; +	} + +	g_base = r_base + size; +	if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) { +		ret = -EFAULT; +		goto out; +	} + +	b_base = g_base + size; +	if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) { +		ret = -EFAULT; +		goto out; +	} + +	crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, crtc->gamma_size); + +out: +	mutex_unlock(&dev->mode_config.mutex); +	return ret; + +} + +int drm_mode_gamma_get_ioctl(struct drm_device *dev, +			     void *data, struct drm_file *file_priv) +{ +	struct drm_mode_crtc_lut *crtc_lut = data; +	struct drm_mode_object *obj; +	struct drm_crtc *crtc; +	void *r_base, *g_base, *b_base; +	int size; +	int ret = 0; + +	mutex_lock(&dev->mode_config.mutex); +	obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); +	if (!obj) { +		ret = -EINVAL; +		goto out; +	} +	crtc = obj_to_crtc(obj); + +	/* memcpy into gamma store */ +	if (crtc_lut->gamma_size != crtc->gamma_size) { +		ret = -EINVAL; +		goto out; +	} + +	size = crtc_lut->gamma_size * (sizeof(uint16_t)); +	r_base = crtc->gamma_store; +	if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) { +		ret = -EFAULT; +		goto out; +	} + +	g_base = r_base + size; +	if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) { +		ret = -EFAULT; +		goto out; +	} + +	b_base = g_base + size; +	if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) { +		ret = -EFAULT; +		goto out; +	} +out: +	mutex_unlock(&dev->mode_config.mutex); +	return ret; +} diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h new file mode 100644 index 00000000..3a3a09aa --- /dev/null +++ b/linux-core/drm_crtc.h @@ -0,0 +1,712 @@ +/* + * Copyright © 2006 Keith Packard + * Copyright © 2007 Intel Corporation + *   Jesse Barnes <jesse.barnes@intel.com> + */ +#ifndef __DRM_CRTC_H__ +#define __DRM_CRTC_H__ + +#include <linux/i2c.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/idr.h> + +#include <linux/fb.h> + +struct drm_device; +struct drm_mode_set; +struct drm_framebuffer; + + +#define DRM_MODE_OBJECT_CRTC 0xcccccccc +#define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0 +#define DRM_MODE_OBJECT_ENCODER 0xe0e0e0e0 +#define DRM_MODE_OBJECT_MODE 0xdededede +#define DRM_MODE_OBJECT_PROPERTY 0xb0b0b0b0 +#define DRM_MODE_OBJECT_FB 0xfbfbfbfb +#define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb + +struct drm_mode_object { +	uint32_t id; +	uint32_t type; +}; + +/* + * Note on terminology:  here, for brevity and convenience, we refer to connector + * control chips as 'CRTCs'.  They can control any type of connector, VGA, LVDS, + * DVI, etc.  And 'screen' refers to the whole of the visible display, which + * may span multiple monitors (and therefore multiple CRTC and connector + * structures). + */ + +enum drm_mode_status { +    MODE_OK	= 0,	/* Mode OK */ +    MODE_HSYNC,		/* hsync out of range */ +    MODE_VSYNC,		/* vsync out of range */ +    MODE_H_ILLEGAL,	/* mode has illegal horizontal timings */ +    MODE_V_ILLEGAL,	/* mode has illegal horizontal timings */ +    MODE_BAD_WIDTH,	/* requires an unsupported linepitch */ +    MODE_NOMODE,	/* no mode with a maching name */ +    MODE_NO_INTERLACE,	/* interlaced mode not supported */ +    MODE_NO_DBLESCAN,	/* doublescan mode not supported */ +    MODE_NO_VSCAN,	/* multiscan mode not supported */ +    MODE_MEM,		/* insufficient video memory */ +    MODE_VIRTUAL_X,	/* mode width too large for specified virtual size */ +    MODE_VIRTUAL_Y,	/* mode height too large for specified virtual size */ +    MODE_MEM_VIRT,	/* insufficient video memory given virtual size */ +    MODE_NOCLOCK,	/* no fixed clock available */ +    MODE_CLOCK_HIGH,	/* clock required is too high */ +    MODE_CLOCK_LOW,	/* clock required is too low */ +    MODE_CLOCK_RANGE,	/* clock/mode isn't in a ClockRange */ +    MODE_BAD_HVALUE,	/* horizontal timing was out of range */ +    MODE_BAD_VVALUE,	/* vertical timing was out of range */ +    MODE_BAD_VSCAN,	/* VScan value out of range */ +    MODE_HSYNC_NARROW,	/* horizontal sync too narrow */ +    MODE_HSYNC_WIDE,	/* horizontal sync too wide */ +    MODE_HBLANK_NARROW,	/* horizontal blanking too narrow */ +    MODE_HBLANK_WIDE,	/* horizontal blanking too wide */ +    MODE_VSYNC_NARROW,	/* vertical sync too narrow */ +    MODE_VSYNC_WIDE,	/* vertical sync too wide */ +    MODE_VBLANK_NARROW,	/* vertical blanking too narrow */ +    MODE_VBLANK_WIDE,	/* vertical blanking too wide */ +    MODE_PANEL,         /* exceeds panel dimensions */ +    MODE_INTERLACE_WIDTH, /* width too large for interlaced mode */ +    MODE_ONE_WIDTH,     /* only one width is supported */ +    MODE_ONE_HEIGHT,    /* only one height is supported */ +    MODE_ONE_SIZE,      /* only one resolution is supported */ +    MODE_NO_REDUCED,    /* monitor doesn't accept reduced blanking */ +    MODE_UNVERIFIED = -3, /* mode needs to reverified */ +    MODE_BAD = -2,	/* unspecified reason */ +    MODE_ERROR	= -1	/* error condition */ +}; + +#define DRM_MODE_TYPE_CLOCK_CRTC_C (DRM_MODE_TYPE_CLOCK_C | \ +				    DRM_MODE_TYPE_CRTC_C) + +#define DRM_MODE(nm, t, c, hd, hss, hse, ht, hsk, vd, vss, vse, vt, vs, f) \ +	.name = nm, .status = 0, .type = (t), .clock = (c), \ +	.hdisplay = (hd), .hsync_start = (hss), .hsync_end = (hse), \ +	.htotal = (ht), .hskew = (hsk), .vdisplay = (vd), \ +	.vsync_start = (vss), .vsync_end = (vse), .vtotal = (vt), \ +	.vscan = (vs), .flags = (f), .vrefresh = 0 + +#define CRTC_INTERLACE_HALVE_V 0x1 /* halve V values for interlacing */ + +struct drm_display_mode { +	/* Header */ +	struct list_head head; +	struct drm_mode_object base; + +	char name[DRM_DISPLAY_MODE_LEN]; + +	int connector_count; +	enum drm_mode_status status; +	int type; + +	/* Proposed mode values */ +	int clock; +	int hdisplay; +	int hsync_start; +	int hsync_end; +	int htotal; +	int hskew; +	int vdisplay; +	int vsync_start; +	int vsync_end; +	int vtotal; +	int vscan; +	unsigned int flags; + +	/* Addressable image size (may be 0 for projectors, etc.) */ +	int width_mm; +	int height_mm; + +	/* Actual mode we give to hw */ +	int clock_index; +	int synth_clock; +	int crtc_hdisplay; +	int crtc_hblank_start; +	int crtc_hblank_end; +	int crtc_hsync_start; +	int crtc_hsync_end; +	int crtc_htotal; +	int crtc_hskew; +	int crtc_vdisplay; +	int crtc_vblank_start; +	int crtc_vblank_end; +	int crtc_vsync_start; +	int crtc_vsync_end; +	int crtc_vtotal; +	int crtc_hadjusted; +	int crtc_vadjusted; + +	/* Driver private mode info */ +	int private_size; +	int *private; +	int private_flags; + +	int vrefresh; +	float hsync; +}; + +enum drm_connector_status { +	connector_status_connected = 1, +	connector_status_disconnected = 2, +	connector_status_unknown = 3, +}; + +enum subpixel_order { +	SubPixelUnknown = 0, +	SubPixelHorizontalRGB, +	SubPixelHorizontalBGR, +	SubPixelVerticalRGB, +	SubPixelVerticalBGR, +	SubPixelNone, +}; + + +/* + * Describes a given display (e.g. CRT or flat panel) and its limitations. + */ +struct drm_display_info { +	char name[DRM_DISPLAY_INFO_LEN]; +	/* Input info */ +	bool serration_vsync; +	bool sync_on_green; +	bool composite_sync; +	bool separate_syncs; +	bool blank_to_black; +	unsigned char video_level; +	bool digital; +	/* Physical size */ +        unsigned int width_mm; +	unsigned int height_mm; + +	/* Display parameters */ +	unsigned char gamma; /* FIXME: storage format */ +	bool gtf_supported; +	bool standard_color; +	enum { +		monochrome = 0, +		rgb, +		other, +		unknown, +	} display_type; +	bool active_off_supported; +	bool suspend_supported; +	bool standby_supported; + +	/* Color info FIXME: storage format */ +	unsigned short redx, redy; +	unsigned short greenx, greeny; +	unsigned short bluex, bluey; +	unsigned short whitex, whitey; + +	/* Clock limits FIXME: storage format */ +	unsigned int min_vfreq, max_vfreq; +	unsigned int min_hfreq, max_hfreq; +	unsigned int pixel_clock; + +	/* White point indices FIXME: storage format */ +	unsigned int wpx1, wpy1; +	unsigned int wpgamma1; +	unsigned int wpx2, wpy2; +	unsigned int wpgamma2; + +	enum subpixel_order subpixel_order; + +	char *raw_edid; /* if any */ +}; + +struct drm_framebuffer_funcs { +	void (*destroy)(struct drm_framebuffer *framebuffer); +}; + +struct drm_framebuffer { +	struct drm_device *dev; +	struct list_head head; +	struct drm_mode_object base; +	const struct drm_framebuffer_funcs *funcs; +	unsigned int pitch; +	unsigned int width; +	unsigned int height; +	/* depth can be 15 or 16 */ +	unsigned int depth; +	int bits_per_pixel; +	int flags; +	void *fbdev; +	u32 pseudo_palette[17]; +	struct list_head filp_head; +	uint32_t mm_handle; +}; + +struct drm_property_blob { +	struct drm_mode_object base; +	struct list_head head; +	unsigned int length; +	void *data; +}; + +struct drm_property_enum { +	uint64_t value; +	struct list_head head; +	char name[DRM_PROP_NAME_LEN]; +}; + +struct drm_property { +	struct list_head head; +	struct drm_mode_object base; +	uint32_t flags; +	char name[DRM_PROP_NAME_LEN]; +	uint32_t num_values; +	uint64_t *values; + +	struct list_head enum_blob_list; +}; + +struct drm_crtc; +struct drm_connector; +struct drm_encoder; + +/** + * drm_crtc_funcs - control CRTCs for a given device + * @dpms: control display power levels + * @save: save CRTC state + * @resore: restore CRTC state + * @lock: lock the CRTC + * @unlock: unlock the CRTC + * @shadow_allocate: allocate shadow pixmap + * @shadow_create: create shadow pixmap for rotation support + * @shadow_destroy: free shadow pixmap + * @mode_fixup: fixup proposed mode + * @mode_set: set the desired mode on the CRTC + * @gamma_set: specify color ramp for CRTC + * @destroy: deinit and free object. + * + * The drm_crtc_funcs structure is the central CRTC management structure + * in the DRM.  Each CRTC controls one or more connectors (note that the name + * CRTC is simply historical, a CRTC may control LVDS, VGA, DVI, TV out, etc. + * connectors, not just CRTs). + * + * Each driver is responsible for filling out this structure at startup time, + * in addition to providing other modesetting features, like i2c and DDC + * bus accessors. + */ +struct drm_crtc_funcs { +	/* Save CRTC state */ +	void (*save)(struct drm_crtc *crtc); /* suspend? */ +	/* Restore CRTC state */ +	void (*restore)(struct drm_crtc *crtc); /* resume? */ + +	/* cursor controls */ +	int (*cursor_set)(struct drm_crtc *crtc, struct drm_file *file_priv, +			  uint32_t handle, uint32_t width, uint32_t height); +	int (*cursor_move)(struct drm_crtc *crtc, int x, int y); + +	/* Set gamma on the CRTC */ +	void (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, +			  uint32_t size); +	/* Object destroy routine */ +	void (*destroy)(struct drm_crtc *crtc); + +	int (*set_config)(struct drm_mode_set *set); +}; + +/** + * drm_crtc - central CRTC control structure + * @enabled: is this CRTC enabled? + * @x: x position on screen + * @y: y position on screen + * @desired_mode: new desired mode + * @desired_x: desired x for desired_mode + * @desired_y: desired y for desired_mode + * @funcs: CRTC control functions + * + * Each CRTC may have one or more connectors associated with it.  This structure + * allows the CRTC to be controlled. + */ +struct drm_crtc { +	struct drm_device *dev; +	struct list_head head; + +	struct drm_mode_object base; + +	/* framebuffer the connector is currently bound to */ +	struct drm_framebuffer *fb; + +	bool enabled; + +	struct drm_display_mode mode; + +	int x, y; +	struct drm_display_mode *desired_mode; +	int desired_x, desired_y; +	const struct drm_crtc_funcs *funcs; + +	/* CRTC gamma size for reporting to userspace */ +	uint32_t gamma_size; +	uint16_t *gamma_store; + +	/* if you are using the helper */ +	void *helper_private; +}; + + +/** + * drm_connector_funcs - control connectors on a given device + * @dpms: set power state (see drm_crtc_funcs above) + * @save: save connector state + * @restore: restore connector state + * @mode_valid: is this mode valid on the given connector? + * @mode_fixup: try to fixup proposed mode for this connector + * @mode_set: set this mode + * @detect: is this connector active? + * @get_modes: get mode list for this connector + * @set_property: property for this connector may need update + * @destroy: make object go away + * + * Each CRTC may have one or more connectors attached to it.  The functions + * below allow the core DRM code to control connectors, enumerate available modes, + * etc. + */ +struct drm_connector_funcs { +	void (*dpms)(struct drm_connector *connector, int mode); +	void (*save)(struct drm_connector *connector); +	void (*restore)(struct drm_connector *connector); +	enum drm_connector_status (*detect)(struct drm_connector *connector); +	void (*fill_modes)(struct drm_connector *connector, uint32_t max_width, uint32_t max_height); +	int (*set_property)(struct drm_connector *connector, struct drm_property *property, +			     uint64_t val); +	void (*destroy)(struct drm_connector *connector); +}; + +struct drm_encoder_funcs { +	void (*destroy)(struct drm_encoder *encoder); +}; + +#define DRM_CONNECTOR_MAX_UMODES 16 +#define DRM_CONNECTOR_MAX_PROPERTY 16 +#define DRM_CONNECTOR_LEN 32 +#define DRM_CONNECTOR_MAX_ENCODER 2 + +/** + * drm_encoder - central DRM encoder structure  + */ +struct drm_encoder { +	struct drm_device *dev; +	struct list_head head; + +	struct drm_mode_object base; +	int encoder_type; +	uint32_t possible_crtcs; +	uint32_t possible_clones; + +	struct drm_crtc *crtc; +	const struct drm_encoder_funcs *funcs; +	void *helper_private; +}; + +/** + * drm_connector - central DRM connector control structure + * @crtc: CRTC this connector is currently connected to, NULL if none + * @interlace_allowed: can this connector handle interlaced modes? + * @doublescan_allowed: can this connector handle doublescan? + * @available_modes: modes available on this connector (from get_modes() + user) + * @initial_x: initial x position for this connector + * @initial_y: initial y position for this connector + * @status: connector connected? + * @funcs: connector control functions + * + * Each connector may be connected to one or more CRTCs, or may be clonable by + * another connector if they can share a CRTC.  Each connector also has a specific + * position in the broader display (referred to as a 'screen' though it could + * span multiple monitors). + */ +struct drm_connector { +	struct drm_device *dev; +	struct device kdev; +	struct device_attribute *attr; +	struct list_head head; + +	struct drm_mode_object base; + +	int connector_type; +	int connector_type_id; +	bool interlace_allowed; +	bool doublescan_allowed; +	struct list_head modes; /* list of modes on this connector */ + +	int initial_x, initial_y; +	enum drm_connector_status status; + +	/* these are modes added by probing with DDC or the BIOS */ +	struct list_head probed_modes; +	 +	struct drm_display_info display_info; +  	const struct drm_connector_funcs *funcs; + +	struct list_head user_modes; +	struct drm_property_blob *edid_blob_ptr; +	u32 property_ids[DRM_CONNECTOR_MAX_PROPERTY]; +	uint64_t property_values[DRM_CONNECTOR_MAX_PROPERTY]; + +	void *helper_private; + +	uint32_t encoder_ids[DRM_CONNECTOR_MAX_ENCODER]; +	uint32_t force_encoder_id; +	struct drm_encoder *encoder; /* currently active encoder */ +}; + +/** + * struct drm_mode_set + * + * Represents a single crtc the connectors that it drives with what mode + * and from which framebuffer it scans out from. + * + * This is used to set modes. + */ +struct drm_mode_set { +	struct list_head head; + +	struct drm_framebuffer *fb; +	struct drm_crtc *crtc; +	struct drm_display_mode *mode; + +	uint32_t x; +	uint32_t y; + +	struct drm_connector **connectors; +	size_t num_connectors; +}; + +/** + * struct drm_mode_config_funcs - configure CRTCs for a given screen layout + * @resize: adjust CRTCs as necessary for the proposed layout + * + * Currently only a resize hook is available.  DRM will call back into the + * driver with a new screen width and height.  If the driver can't support + * the proposed size, it can return false.  Otherwise it should adjust + * the CRTC<->connector mappings as needed and update its view of the screen. + */ +struct drm_mode_config_funcs { +	int (*resize_fb)(struct drm_device *dev, struct drm_file *file_priv, struct drm_framebuffer *fb, struct drm_mode_fb_cmd *mode_cmd); +	struct drm_framebuffer *(*fb_create)(struct drm_device *dev, struct drm_file *file_priv, struct drm_mode_fb_cmd *mode_cmd); +	int (*fb_changed)(struct drm_device *dev); +}; + +struct drm_mode_group { +	uint32_t num_crtcs; +	uint32_t num_encoders; +	uint32_t num_connectors; + +	/* list of object IDs for this group */ +	uint32_t *id_list; +}; + +/** + * drm_mode_config - Mode configuration control structure + * + */ +struct drm_mode_config { +	struct mutex mutex; /* protects configuration and IDR */ +	struct idr crtc_idr; /* use this idr for all IDs, fb, crtc, connector, modes - just makes life easier */ +	/* this is limited to one for now */ +	int num_fb; +	struct list_head fb_list; +	int num_connector; +	struct list_head connector_list; +	int num_encoder; +	struct list_head encoder_list; + +	int num_crtc; +	struct list_head crtc_list; + +	struct list_head property_list; + +	/* in-kernel framebuffers - hung of filp_head in drm_framebuffer */ +	struct list_head fb_kernel_list; + +	int min_width, min_height; +	int max_width, max_height; +	struct drm_mode_config_funcs *funcs; +	unsigned long fb_base; + +	/* pointers to standard properties */ +	struct list_head property_blob_list; +	struct drm_property *edid_property; +	struct drm_property *dpms_property; + +	/* DVI-I properties */ +	struct drm_property *dvi_i_subconnector_property; +	struct drm_property *dvi_i_select_subconnector_property; + +	/* TV properties */ +	struct drm_property *tv_subconnector_property; +	struct drm_property *tv_select_subconnector_property; +	struct drm_property *tv_mode_property; +	struct drm_property *tv_left_margin_property; +	struct drm_property *tv_right_margin_property; +	struct drm_property *tv_top_margin_property; +	struct drm_property *tv_bottom_margin_property; + +	/* Optional properties */ +	struct drm_property *scaling_mode_property; +	struct drm_property *dithering_mode_property; + +	/* hotplug */ +	uint32_t hotplug_counter; +}; + +#define obj_to_crtc(x) container_of(x, struct drm_crtc, base) +#define obj_to_connector(x) container_of(x, struct drm_connector, base) +#define obj_to_encoder(x) container_of(x, struct drm_encoder, base) +#define obj_to_mode(x) container_of(x, struct drm_display_mode, base) +#define obj_to_fb(x) container_of(x, struct drm_framebuffer, base) +#define obj_to_property(x) container_of(x, struct drm_property, base) +#define obj_to_blob(x) container_of(x, struct drm_property_blob, base) + + +extern void drm_crtc_init(struct drm_device *dev, +			  struct drm_crtc *crtc, +			  const struct drm_crtc_funcs *funcs); +extern void drm_crtc_cleanup(struct drm_crtc *crtc); + +extern void drm_connector_init(struct drm_device *dev, +			    struct drm_connector *connector, +			    const struct drm_connector_funcs *funcs, +			    int connector_type); + +extern void drm_connector_cleanup(struct drm_connector *connector); + +extern void drm_encoder_init(struct drm_device *dev, +			     struct drm_encoder *encoder, +			     const struct drm_encoder_funcs *funcs, +			     int encoder_type); + +extern void drm_encoder_cleanup(struct drm_encoder *encoder); + +extern char *drm_get_connector_name(struct drm_connector *connector); +extern char *drm_get_dpms_name(int val); +extern char *drm_get_select_subconnector_name(int val); +extern char *drm_get_subconnector_name(int val); +extern void drm_fb_release(struct file *filp); +extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group); +extern struct edid *drm_get_edid(struct drm_connector *connector, +				 struct i2c_adapter *adapter); +extern unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter); +extern int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid); +extern void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode); +extern void drm_mode_remove(struct drm_connector *connector, struct drm_display_mode *mode); +extern struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, +						   struct drm_display_mode *mode); +extern void drm_mode_debug_printmodeline(struct drm_display_mode *mode); +extern void drm_mode_config_init(struct drm_device *dev); +extern void drm_mode_config_cleanup(struct drm_device *dev); +extern void drm_mode_set_name(struct drm_display_mode *mode); +extern bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2); + +/* for us by fb module */ +extern int drm_mode_attachmode_crtc(struct drm_device *dev, +				    struct drm_crtc *crtc, +				    struct drm_display_mode *mode); +extern int drm_mode_detachmode_crtc(struct drm_device *dev, struct drm_display_mode *mode); + +extern struct drm_display_mode *drm_mode_create(struct drm_device *dev); +extern void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode); +extern void drm_mode_list_concat(struct list_head *head, +				 struct list_head *new); +extern void drm_mode_validate_size(struct drm_device *dev, +				   struct list_head *mode_list, +				   int maxX, int maxY, int maxPitch); +extern void drm_mode_prune_invalid(struct drm_device *dev, +				   struct list_head *mode_list, bool verbose); +extern void drm_mode_sort(struct list_head *mode_list); +extern int drm_mode_vrefresh(struct drm_display_mode *mode); +extern void drm_mode_set_crtcinfo(struct drm_display_mode *p, +				  int adjust_flags); +extern void drm_mode_connector_list_update(struct drm_connector *connector); +extern int drm_mode_connector_update_edid_property(struct drm_connector *connector, +						struct edid *edid); +extern int drm_connector_property_set_value(struct drm_connector *connector, +					 struct drm_property *property, +					 uint64_t value); +extern int drm_connector_property_get_value(struct drm_connector *connector, +					 struct drm_property *property, +					 uint64_t *value); +extern struct drm_display_mode *drm_crtc_mode_create(struct drm_device *dev); +extern void drm_framebuffer_set_object(struct drm_device *dev, +				       unsigned long handle); +extern struct drm_framebuffer *drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, +						    const struct drm_framebuffer_funcs *funcs); +extern void drm_framebuffer_cleanup(struct drm_framebuffer *fb); +extern int drmfb_probe(struct drm_device *dev, struct drm_crtc *crtc); +extern int drmfb_remove(struct drm_device *dev, struct drm_framebuffer *fb); +extern void drm_crtc_probe_connector_modes(struct drm_device *dev, int maxX, int maxY); +extern bool drm_crtc_in_use(struct drm_crtc *crtc); + +extern int drm_connector_attach_property(struct drm_connector *connector, +				      struct drm_property *property, uint64_t init_val); +extern struct drm_property *drm_property_create(struct drm_device *dev, int flags, +						const char *name, int num_values); +extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property); +extern int drm_property_add_enum(struct drm_property *property, int index,  +				 uint64_t value, const char *name); +extern int drm_mode_create_dvi_i_properties(struct drm_device *dev); +extern int drm_mode_create_tv_properties(struct drm_device *dev, int num_formats, +				     char *formats[]); +extern int drm_mode_create_scaling_mode_property(struct drm_device *dev); +extern int drm_mode_create_dithering_property(struct drm_device *dev); +extern char *drm_get_encoder_name(struct drm_encoder *encoder); + +extern int drm_mode_connector_attach_encoder(struct drm_connector *connector, +					     struct drm_encoder *encoder); +extern void drm_mode_connector_detach_encoder(struct drm_connector *connector, +					   struct drm_encoder *encoder); +extern bool drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, +					 int gamma_size); +extern void *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type); +/* IOCTLs */ +extern int drm_mode_getresources(struct drm_device *dev, +				 void *data, struct drm_file *file_priv); + +extern int drm_mode_getcrtc(struct drm_device *dev, +			    void *data, struct drm_file *file_priv); +extern int drm_mode_getconnector(struct drm_device *dev, +			      void *data, struct drm_file *file_priv); +extern int drm_mode_setcrtc(struct drm_device *dev, +			    void *data, struct drm_file *file_priv); +extern int drm_mode_cursor_ioctl(struct drm_device *dev, +				void *data, struct drm_file *file_priv); +extern int drm_mode_addfb(struct drm_device *dev, +			  void *data, struct drm_file *file_priv); +extern int drm_mode_rmfb(struct drm_device *dev, +			 void *data, struct drm_file *file_priv); +extern int drm_mode_getfb(struct drm_device *dev, +			  void *data, struct drm_file *file_priv); +extern int drm_mode_addmode_ioctl(struct drm_device *dev, +				  void *data, struct drm_file *file_priv); +extern int drm_mode_rmmode_ioctl(struct drm_device *dev, +				 void *data, struct drm_file *file_priv); +extern int drm_mode_attachmode_ioctl(struct drm_device *dev, +				     void *data, struct drm_file *file_priv); +extern int drm_mode_detachmode_ioctl(struct drm_device *dev, +				     void *data, struct drm_file *file_priv); + +extern int drm_mode_getproperty_ioctl(struct drm_device *dev, +				      void *data, struct drm_file *file_priv); +extern int drm_mode_getblob_ioctl(struct drm_device *dev, +				  void *data, struct drm_file *file_priv); +extern int drm_mode_connector_property_set_ioctl(struct drm_device *dev, +					      void *data, struct drm_file *file_priv); +extern int drm_mode_hotplug_ioctl(struct drm_device *dev, +				  void *data, struct drm_file *file_priv); +extern int drm_mode_replacefb(struct drm_device *dev, +			      void *data, struct drm_file *file_priv); +extern int drm_mode_getencoder(struct drm_device *dev, +			       void *data, struct drm_file *file_priv); +extern int drm_mode_gamma_get_ioctl(struct drm_device *dev, +				    void *data, struct drm_file *file_priv); +extern int drm_mode_gamma_set_ioctl(struct drm_device *dev, +				    void *data, struct drm_file *file_priv); +#endif /* __DRM_CRTC_H__ */ + diff --git a/linux-core/drm_crtc_helper.c b/linux-core/drm_crtc_helper.c new file mode 100644 index 00000000..e0d93606 --- /dev/null +++ b/linux-core/drm_crtc_helper.c @@ -0,0 +1,762 @@ +/* (c) 2006-2007 Intel Corporation + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> + * + * DRM core CRTC related functions + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission.  The copyright holders make no representations + * about the suitability of this software for any purpose.  It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, 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. + * + * Authors: + *      Keith Packard + *	Eric Anholt <eric@anholt.net> + *      Dave Airlie <airlied@linux.ie> + *      Jesse Barnes <jesse.barnes@intel.com> + */ + +#include "drmP.h" +#include "drm_crtc.h" +#include "drm_crtc_helper.h" + +/* + * Detailed mode info for a standard 640x480@60Hz monitor + */ +static struct drm_display_mode std_mode[] = { +	{ DRM_MODE("640x480", DRM_MODE_TYPE_DEFAULT, 25200, 640, 656, +		   752, 800, 0, 480, 490, 492, 525, 0, +		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */ +}; + +/** + * drm_helper_probe_connector_modes - get complete set of display modes + * @dev: DRM device + * @maxX: max width for modes + * @maxY: max height for modes + * + * LOCKING: + * Caller must hold mode config lock. + * + * Based on @dev's mode_config layout, scan all the connectors and try to detect + * modes on them.  Modes will first be added to the connector's probed_modes + * list, then culled (based on validity and the @maxX, @maxY parameters) and + * put into the normal modes list. + * + * Intended to be used either at bootup time or when major configuration + * changes have occurred. + * + * FIXME: take into account monitor limits + */ +void drm_helper_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, uint32_t maxY) +{ +	struct drm_device *dev = connector->dev; +	struct drm_display_mode *mode, *t; +	struct drm_connector_helper_funcs *connector_funcs = connector->helper_private; +	int ret; + +	DRM_DEBUG("%s\n", drm_get_connector_name(connector)); +	/* set all modes to the unverified state */ +	list_for_each_entry_safe(mode, t, &connector->modes, head) +		mode->status = MODE_UNVERIFIED; +		 +	connector->status = (*connector->funcs->detect)(connector); +	 +	if (connector->status == connector_status_disconnected) { +		DRM_DEBUG("%s is disconnected\n", drm_get_connector_name(connector)); +		/* TODO set EDID to NULL */ +		return; +	} +	 +	ret = (*connector_funcs->get_modes)(connector); +	 +	if (ret) { +		drm_mode_connector_list_update(connector); +	} +	 +	if (maxX && maxY) +		drm_mode_validate_size(dev, &connector->modes, maxX, +				       maxY, 0); +	list_for_each_entry_safe(mode, t, &connector->modes, head) { +		if (mode->status == MODE_OK) +			mode->status = (*connector_funcs->mode_valid)(connector,mode); +	} +	 +	 +	drm_mode_prune_invalid(dev, &connector->modes, true); +	 +	if (list_empty(&connector->modes)) { +		struct drm_display_mode *stdmode; +		 +		DRM_DEBUG("No valid modes on %s\n", drm_get_connector_name(connector)); +		 +		/* Should we do this here ??? +		 * When no valid EDID modes are available we end up +		 * here and bailed in the past, now we add a standard +		 * 640x480@60Hz mode and carry on. +		 */ +		stdmode = drm_mode_duplicate(dev, &std_mode[0]); +		drm_mode_probed_add(connector, stdmode); +		drm_mode_list_concat(&connector->probed_modes, +				     &connector->modes); +		 +		DRM_DEBUG("Adding standard 640x480 @ 60Hz to %s\n", +			  drm_get_connector_name(connector)); +	} +	 +	drm_mode_sort(&connector->modes); +	 +	DRM_DEBUG("Probed modes for %s\n", drm_get_connector_name(connector)); +	list_for_each_entry_safe(mode, t, &connector->modes, head) { +		mode->vrefresh = drm_mode_vrefresh(mode); +		 +		drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); +		drm_mode_debug_printmodeline(mode); +	} +} +EXPORT_SYMBOL(drm_helper_probe_single_connector_modes); + +void drm_helper_probe_connector_modes(struct drm_device *dev, uint32_t maxX, uint32_t maxY) +{ +	struct drm_connector *connector; + +	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { +		drm_helper_probe_single_connector_modes(connector, maxX, maxY); +	} +} +EXPORT_SYMBOL(drm_helper_probe_connector_modes); + + +/** + * drm_helper_crtc_in_use - check if a given CRTC is in a mode_config + * @crtc: CRTC to check + * + * LOCKING: + * Caller must hold mode config lock. + * + * Walk @crtc's DRM device's mode_config and see if it's in use. + * + * RETURNS: + * True if @crtc is part of the mode_config, false otherwise. + */ +bool drm_helper_crtc_in_use(struct drm_crtc *crtc) +{ +	struct drm_encoder *encoder; +	struct drm_device *dev = crtc->dev; +	/* FIXME: Locking around list access? */ +	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) +		if (encoder->crtc == crtc) +			return true; +	return false; +} +EXPORT_SYMBOL(drm_helper_crtc_in_use); + +/** + * drm_disable_unused_functions - disable unused objects + * @dev: DRM device + * + * LOCKING: + * Caller must hold mode config lock. + * + * If an connector or CRTC isn't part of @dev's mode_config, it can be disabled + * by calling its dpms function, which should power it off. + */ +void drm_helper_disable_unused_functions(struct drm_device *dev) +{ +	struct drm_encoder *encoder; +	struct drm_encoder_helper_funcs *encoder_funcs; +	struct drm_crtc *crtc; + +	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { +		encoder_funcs = encoder->helper_private; +		if (!encoder->crtc) +			(*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); +	} + +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; +		crtc->enabled = drm_helper_crtc_in_use(crtc); +		if (!crtc->enabled) { +			crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); +			crtc->fb = NULL; +		} +	} +} +EXPORT_SYMBOL(drm_helper_disable_unused_functions); + +/** + * drm_pick_crtcs - pick crtcs for connector devices + * @dev: DRM device + * + * LOCKING: + * Caller must hold mode config lock. + */ +static void drm_pick_crtcs (struct drm_device *dev) +{ +	int c, o, assigned; +	struct drm_connector *connector, *connector_equal; +	struct drm_encoder *encoder, *encoder_equal; +	struct drm_crtc   *crtc; +	struct drm_display_mode *des_mode = NULL, *modes, *modes_equal; +	struct drm_connector_helper_funcs *connector_funcs; +	int found; + +	DRM_DEBUG("\n"); +	/* clean out all the encoder/crtc combos */ +	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { +		encoder->crtc = NULL; +	} +	 +	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { +		connector_funcs = connector->helper_private; +		connector->encoder = NULL; + +    		/* Don't hook up connectors that are disconnected ?? +		 * +		 * This is debateable. Do we want fixed /dev/fbX or +		 * dynamic on hotplug (need mode code for that though) ? +		 * +		 * If we don't hook up connectors now, then we only create +		 * /dev/fbX for the connector that's enabled, that's good as +		 * the users console will be on that connector. +		 * +		 * If we do hook up connectors that are disconnected now, then +		 * the user may end up having to muck about with the fbcon +		 * map flags to assign his console to the enabled connector. Ugh. +		 */ +    		if (connector->status != connector_status_connected) +			continue; + +		if (list_empty(&connector->modes)) +			continue; + +		des_mode = NULL; +		found = 0; +		list_for_each_entry(des_mode, &connector->modes, head) { +			if (des_mode->type & DRM_MODE_TYPE_PREFERRED) { +				found = 1; +				break; +			} +		} + +		/* No preferred mode, let's just select the first available */ +		if (!found) { +			des_mode = NULL; +			list_for_each_entry(des_mode, &connector->modes, head) { +				break; +			} +		} + +		encoder = connector_funcs->best_encoder(connector); +		if (!encoder) +			continue; + +		connector->encoder = encoder; + +		c = -1; +		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +			assigned = 0; + +			c++; +			if ((encoder->possible_crtcs & (1 << c)) == 0) +		    		continue; +	 +			list_for_each_entry(encoder_equal, &dev->mode_config.encoder_list, head) { +				if (encoder->base.id == encoder_equal->base.id) +					continue; + +				/* Find out if crtc has been assigned before */ +				if (encoder_equal->crtc == crtc) +					assigned = 1; +			} + +#if 1 /* continue for now */ +			if (assigned) +				continue; +#endif + +			o = -1; +			list_for_each_entry(connector_equal, &dev->mode_config.connector_list, head) { +				o++; +				if (connector->base.id == connector_equal->base.id) +					continue; + +				encoder_equal = connector_equal->encoder; + +				if (!encoder_equal) +					continue; + +				list_for_each_entry(modes, &connector->modes, head) { +					list_for_each_entry(modes_equal, &connector_equal->modes, head) { +						if (drm_mode_equal (modes, modes_equal)) { +							if ((encoder->possible_clones & encoder_equal->possible_clones) && (connector_equal->encoder->crtc == crtc)) { +								printk("Cloning %s (0x%x) to %s (0x%x)\n",drm_get_connector_name(connector),encoder->possible_clones,drm_get_connector_name(connector_equal),encoder_equal->possible_clones); +								des_mode = modes; +								assigned = 0; +								goto clone; +							} +						} +					} +				} +			} + +clone: +			/* crtc has been assigned skip it */ +			if (assigned) +				continue; + +			/* Found a CRTC to attach to, do it ! */ +			encoder->crtc = crtc; +			encoder->crtc->desired_mode = des_mode; +			connector->initial_x = 0; +			connector->initial_y = 0; +			DRM_DEBUG("Desired mode for CRTC %d is 0x%x:%s\n",c,des_mode->base.id, des_mode->name); +			break; +    		} +	} +} +EXPORT_SYMBOL(drm_pick_crtcs); + +/** + * drm_crtc_set_mode - set a mode + * @crtc: CRTC to program + * @mode: mode to use + * @x: width of mode + * @y: height of mode + * + * LOCKING: + * Caller must hold mode config lock. + * + * Try to set @mode on @crtc.  Give @crtc and its associated connectors a chance + * to fixup or reject the mode prior to trying to set it. + * + * RETURNS: + * True if the mode was set successfully, or false otherwise. + */ +bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, +			      int x, int y) +{ +	struct drm_device *dev = crtc->dev; +	struct drm_display_mode *adjusted_mode, saved_mode; +	struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; +	struct drm_encoder_helper_funcs *encoder_funcs; +	int saved_x, saved_y; +	struct drm_encoder *encoder; +	bool ret = true; + +	adjusted_mode = drm_mode_duplicate(dev, mode); + +	crtc->enabled = drm_helper_crtc_in_use(crtc); + +	if (!crtc->enabled) +		return true; + +	saved_mode = crtc->mode; +	saved_x = crtc->x; +	saved_y = crtc->y; +	 +	/* Update crtc values up front so the driver can rely on them for mode +	 * setting. +	 */ +	crtc->mode = *mode; +	crtc->x = x; +	crtc->y = y; + +	if (drm_mode_equal(&saved_mode, &crtc->mode)) { +		if (saved_x != crtc->x || saved_y != crtc->y) { +			crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y); +			goto done; +		} +	} + +	/* Pass our mode to the connectors and the CRTC to give them a chance to +	 * adjust it according to limitations or connector properties, and also +	 * a chance to reject the mode entirely. +	 */ +	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { +		 +		if (encoder->crtc != crtc) +			continue; + 		encoder_funcs = encoder->helper_private; +		if (!(ret = encoder_funcs->mode_fixup(encoder, mode, adjusted_mode))) { +			goto done; +		} +	} +	 +	if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) { +		goto done; +	} + +	/* Prepare the encoders and CRTCs before setting the mode. */ +	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + +		if (encoder->crtc != crtc) +			continue; +		encoder_funcs = encoder->helper_private; +		/* Disable the encoders as the first thing we do. */ +		encoder_funcs->prepare(encoder); +	} +	 +	crtc_funcs->prepare(crtc); +	 +	/* Set up the DPLL and any encoders state that needs to adjust or depend +	 * on the DPLL. +	 */ +	crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y); + +	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + +		if (encoder->crtc != crtc) +			continue; +		 +		DRM_INFO("%s: set mode %s %x\n", drm_get_encoder_name(encoder), mode->name, mode->base.id); +		encoder_funcs = encoder->helper_private; +		encoder_funcs->mode_set(encoder, mode, adjusted_mode); +	} +	 +	/* Now, enable the clocks, plane, pipe, and connectors that we set up. */ +	crtc_funcs->commit(crtc); + +	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + +		if (encoder->crtc != crtc) +			continue; + +		encoder_funcs = encoder->helper_private; +		encoder_funcs->commit(encoder); + +	} +	 +	/* XXX free adjustedmode */ +	drm_mode_destroy(dev, adjusted_mode); +	/* TODO */ +//	if (scrn->pScreen) +//		drm_crtc_set_screen_sub_pixel_order(dev); + +done: +	if (!ret) {  +		crtc->mode = saved_mode; +		crtc->x = saved_x; +		crtc->y = saved_y; +	} + +	return ret; +} +EXPORT_SYMBOL(drm_crtc_helper_set_mode); + + +/** + * drm_crtc_helper_set_config - set a new config from userspace + * @crtc: CRTC to setup + * @crtc_info: user provided configuration + * @new_mode: new mode to set + * @connector_set: set of connectors for the new config + * @fb: new framebuffer + * + * LOCKING: + * Caller must hold mode config lock. + * + * Setup a new configuration, provided by the user in @crtc_info, and enable + * it. + * + * RETURNS: + * Zero. (FIXME) + */ +int drm_crtc_helper_set_config(struct drm_mode_set *set) +{ +	struct drm_device *dev; +	struct drm_crtc **save_crtcs, *new_crtc; +	struct drm_encoder **save_encoders, *new_encoder; +	bool save_enabled; +	bool changed = false; +	bool flip_or_move = false; +	struct drm_connector *connector; +	int count = 0, ro, fail = 0; +	struct drm_crtc_helper_funcs *crtc_funcs; +	int ret = 0; + +	DRM_DEBUG("\n"); + +	if (!set) +		return -EINVAL; + +	if (!set->crtc) +		return -EINVAL; + +	if (!set->crtc->helper_private) +		return -EINVAL; +	 +	crtc_funcs = set->crtc->helper_private; +        +	DRM_DEBUG("crtc: %p %d fb: %p connectors: %p num_connectors: %i (x, y) (%i, %i)\n", set->crtc, set->crtc->base.id, set->fb, set->connectors, set->num_connectors, set->x, set->y); + +	dev = set->crtc->dev; + +	/* save previous config */ +	save_enabled = set->crtc->enabled; + +	/* this is meant to be num_connector not num_crtc */ +	save_crtcs = kzalloc(dev->mode_config.num_connector * sizeof(struct drm_crtc *), GFP_KERNEL); +	if (!save_crtcs) +		return -ENOMEM; + +	save_encoders = kzalloc(dev->mode_config.num_connector * sizeof(struct drm_encoders *), GFP_KERNEL); +	if (!save_encoders) { +		kfree(save_crtcs); +		return -ENOMEM; +	} + +	/* We should be able to check here if the fb has the same properties +	 * and then just flip_or_move it */ +	if (set->crtc->fb != set->fb) { +		/* if we have no fb then its a change not a flip */ +		if (set->crtc->fb == NULL) +			changed = true; +		else +			flip_or_move = true; +	} + +	if (set->x != set->crtc->x || set->y != set->crtc->y) +		flip_or_move = true; + +	if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { +		DRM_DEBUG("modes are different\n"); +		drm_mode_debug_printmodeline(&set->crtc->mode); +		drm_mode_debug_printmodeline(set->mode); +		changed = true; +	} + +	/* a) traverse passed in connector list and get encoders for them */ +	count = 0; +	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { +		struct drm_connector_helper_funcs *connector_funcs = connector->helper_private; +		save_encoders[count++] = connector->encoder; +		new_encoder = connector->encoder; +		for (ro = 0; ro < set->num_connectors; ro++) { +			if (set->connectors[ro] == connector) { +				new_encoder = connector_funcs->best_encoder(connector); +				/* if we can't get an encoder for a connector +				   we are setting now - then fail */ +				if (new_encoder == NULL) +					/* don't break so fail path works correct */ +					fail = 1; +				break; +			} +		} + +		if (new_encoder != connector->encoder) { +			changed = true; +			connector->encoder = new_encoder; +		} +	} + +	if (fail) { +		ret = -EINVAL; +		goto fail_no_encoder; +	} + +	count = 0; +	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { +		if (!connector->encoder) +			continue; + +		save_crtcs[count++] = connector->encoder->crtc; + +		if (connector->encoder->crtc == set->crtc) +			new_crtc = NULL; +		else +			new_crtc = connector->encoder->crtc; + +		for (ro = 0; ro < set->num_connectors; ro++) { +			if (set->connectors[ro] == connector) +				new_crtc = set->crtc; +		} +		if (new_crtc != connector->encoder->crtc) { +			changed = true; +			connector->encoder->crtc = new_crtc; +		} +	} + +	/* mode_set_base is not a required function */ +	if (flip_or_move && !crtc_funcs->mode_set_base) +		changed = true; + +	if (changed) { +		set->crtc->fb = set->fb; +		set->crtc->enabled = (set->mode != NULL); +		if (set->mode != NULL) { +			DRM_DEBUG("attempting to set mode from userspace\n"); +			drm_mode_debug_printmodeline(set->mode); +			if (!drm_crtc_helper_set_mode(set->crtc, set->mode, set->x, +						      set->y)) { +				ret = -EINVAL; +				goto fail_set_mode; +			} +			/* TODO are these needed? */ +			set->crtc->desired_x = set->x; +			set->crtc->desired_y = set->y; +			set->crtc->desired_mode = set->mode; +		} +		drm_helper_disable_unused_functions(dev); +	} else if (flip_or_move) { +		if (set->crtc->fb != set->fb) +			set->crtc->fb = set->fb; +		crtc_funcs->mode_set_base(set->crtc, set->x, set->y); +	} + +	kfree(save_encoders); +	kfree(save_crtcs); +	return 0; + +fail_set_mode: +	set->crtc->enabled = save_enabled; +	count = 0; +	list_for_each_entry(connector, &dev->mode_config.connector_list, head) +		connector->encoder->crtc = save_crtcs[count++]; +fail_no_encoder: +	kfree(save_crtcs); +	count = 0; +	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { +		connector->encoder = save_encoders[count++]; +	} +	kfree(save_encoders); +	return ret; +		 +	 +} +EXPORT_SYMBOL(drm_crtc_helper_set_config); + +bool drm_helper_plugged_event(struct drm_device *dev) +{ +	DRM_DEBUG("\n"); + +	drm_helper_probe_connector_modes(dev, dev->mode_config.max_width, dev->mode_config.max_height); + +	drm_pick_crtcs(dev); + +	/* alert the driver fb layer */ +	dev->mode_config.funcs->fb_changed(dev); + +	drm_helper_disable_unused_functions(dev); + +	drm_sysfs_hotplug_event(dev); +	return true; +} +/** + * drm_initial_config - setup a sane initial connector configuration + * @dev: DRM device + * @can_grow: this configuration is growable + * + * LOCKING: + * Called at init time, must take mode config lock. + * + * Scan the CRTCs and connectors and try to put together an initial setup. + * At the moment, this is a cloned configuration across all heads with + * a new framebuffer object as the backing store. + * + * RETURNS: + * Zero if everything went ok, nonzero otherwise. + */ +bool drm_helper_initial_config(struct drm_device *dev, bool can_grow) +{ +	int ret = false; + +	drm_helper_plugged_event(dev); +	return ret; +} +EXPORT_SYMBOL(drm_helper_initial_config); + +/** + * drm_hotplug_stage_two + * @dev DRM device + * @connector hotpluged connector + * + * LOCKING. + * Caller must hold mode config lock, function might grab struct lock. + * + * Stage two of a hotplug. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int drm_helper_hotplug_stage_two(struct drm_device *dev) +{ +	dev->mode_config.hotplug_counter++; + +	drm_helper_plugged_event(dev); + +	return 0; +} +EXPORT_SYMBOL(drm_helper_hotplug_stage_two); + +int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, +				   struct drm_mode_fb_cmd *mode_cmd) +{ +	fb->width = mode_cmd->width; +	fb->height = mode_cmd->height; +	fb->pitch = mode_cmd->pitch; +	fb->bits_per_pixel = mode_cmd->bpp; +	fb->depth = mode_cmd->depth; +	fb->mm_handle = mode_cmd->handle; +	 +	return 0; +} +EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); + +/** + * drm_get_buffer_object - find the buffer object for a given handle + * @dev: DRM device + * @bo: pointer to caller's buffer_object pointer + * @handle: handle to lookup + * + * LOCKING: + * Must take @dev's struct_mutex to protect buffer object lookup. + * + * Given @handle, lookup the buffer object in @dev and put it in the caller's + * @bo pointer. + * + * RETURNS: + * Zero on success, -EINVAL if the handle couldn't be found. + */ +int drm_get_buffer_object(struct drm_device *dev, struct drm_buffer_object **bo, unsigned long handle) +{ +	struct drm_user_object *uo; +	struct drm_hash_item *hash; +	int ret; + +	*bo = NULL; + +	mutex_lock(&dev->struct_mutex); +	ret = drm_ht_find_item(&dev->object_hash, handle, &hash); +	if (ret) { +		DRM_ERROR("Couldn't find handle.\n"); +		ret = -EINVAL; +		goto out_err; +	} + +	uo = drm_hash_entry(hash, struct drm_user_object, hash); +	if (uo->type != drm_buffer_type) { +		ret = -EINVAL; +		goto out_err; +	} +	 +	*bo = drm_user_object_entry(uo, struct drm_buffer_object, base); +	ret = 0; +out_err: +	mutex_unlock(&dev->struct_mutex); +	return ret; +} +EXPORT_SYMBOL(drm_get_buffer_object); + diff --git a/linux-core/drm_crtc_helper.h b/linux-core/drm_crtc_helper.h new file mode 100644 index 00000000..dcb46f98 --- /dev/null +++ b/linux-core/drm_crtc_helper.h @@ -0,0 +1,97 @@ +/* + * Copyright © 2006 Keith Packard + * Copyright © 2007 Intel Corporation + *   Jesse Barnes <jesse.barnes@intel.com> + */ + +/* + * The DRM mode setting helper functions are common code for drivers to use if they wish. + * Drivers are not forced to use this code in their implementations but it would be useful + * if they code they do use at least provides a consistent interface and operation to userspace + */ + +#ifndef __DRM_CRTC_HELPER_H__ +#define __DRM_CRTC_HELPER_H__ + +#include <linux/i2c.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/idr.h> + +#include <linux/fb.h> + +struct drm_crtc_helper_funcs { +	/* +	 * Control power levels on the CRTC.  If the mode passed in is +	 * unsupported, the provider must use the next lowest power level. +	 */ +	void (*dpms)(struct drm_crtc *crtc, int mode); +  	void (*prepare)(struct drm_crtc *crtc); +	void (*commit)(struct drm_crtc *crtc); + +	/* Provider can fixup or change mode timings before modeset occurs */ +	bool (*mode_fixup)(struct drm_crtc *crtc, +			   struct drm_display_mode *mode, +			   struct drm_display_mode *adjusted_mode); +	/* Actually set the mode */ +	void (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode, +			 struct drm_display_mode *adjusted_mode, int x, int y); + +	/* Move the crtc on the current fb to the given position *optional* */ +	void (*mode_set_base)(struct drm_crtc *crtc, int x, int y); +}; + +struct drm_encoder_helper_funcs { +	void (*dpms)(struct drm_encoder *encoder, int mode); +	void (*save)(struct drm_encoder *encoder); +	void (*restore)(struct drm_encoder *encoder); + +	bool (*mode_fixup)(struct drm_encoder *encoder, +			   struct drm_display_mode *mode, +			   struct drm_display_mode *adjusted_mode); +	void (*prepare)(struct drm_encoder *encoder); +	void (*commit)(struct drm_encoder *encoder); +	void (*mode_set)(struct drm_encoder *encoder, +			 struct drm_display_mode *mode, +			 struct drm_display_mode *adjusted_mode); +	/* detect for DAC style encoders */ +	enum drm_connector_status (*detect)(struct drm_encoder *encoder, struct drm_connector *connector); +}; + +struct drm_connector_helper_funcs { +	int (*get_modes)(struct drm_connector *connector); +  	int (*mode_valid)(struct drm_connector *connector, +			  struct drm_display_mode *mode); +	struct drm_encoder *(*best_encoder)(struct drm_connector *connector); +}; +	 +extern void drm_helper_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, uint32_t maxY); +extern void drm_helper_disable_unused_functions(struct drm_device *dev); +extern int drm_helper_hotplug_stage_two(struct drm_device *dev); +extern bool drm_helper_initial_config(struct drm_device *dev, bool can_grow); +extern int drm_crtc_helper_set_config(struct drm_mode_set *set); +extern bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, +				     int x, int y); +extern bool drm_helper_crtc_in_use(struct drm_crtc *crtc); + +extern int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, +					  struct drm_mode_fb_cmd *mode_cmd); + +static inline void drm_crtc_helper_add(struct drm_crtc *crtc, const struct drm_crtc_helper_funcs *funcs) +{ +	crtc->helper_private = (void *)funcs; +} + +static inline void drm_encoder_helper_add(struct drm_encoder *encoder, const struct drm_encoder_helper_funcs *funcs) +{ +	encoder->helper_private = (void *)funcs; +} + +static inline void drm_connector_helper_add(struct drm_connector *connector, const struct drm_connector_helper_funcs *funcs) +{ +	connector->helper_private = (void *)funcs; +} + +extern int drm_get_buffer_object(struct drm_device *dev, struct drm_buffer_object **bo, unsigned long handle); + +#endif diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c index 6ac2adf3..36c0a14d 100644 --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -56,26 +56,29 @@ static int drm_version(struct drm_device *dev, void *data,  /** Ioctl table */  static struct drm_ioctl_desc drm_ioctls[] = { -	DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, 0), +	DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_CONTROL_ALLOW),  	DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0),  	DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, 0), -	DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY), +	DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER),  	DRM_IOCTL_DEF(DRM_IOCTL_GET_MAP, drm_getmap, 0),  	DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, 0),  	DRM_IOCTL_DEF(DRM_IOCTL_GET_STATS, drm_getstats, 0), -	DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER|DRM_ROOT_ONLY), +	DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER), -	DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_setunique, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), +	DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_setunique, DRM_AUTH|DRM_MASTER),  	DRM_IOCTL_DEF(DRM_IOCTL_BLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),  	DRM_IOCTL_DEF(DRM_IOCTL_UNBLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), -	DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), +	DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_MASTER), -	DRM_IOCTL_DEF(DRM_IOCTL_ADD_MAP, drm_addmap_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), +	DRM_IOCTL_DEF(DRM_IOCTL_ADD_MAP, drm_addmap_ioctl, DRM_AUTH|DRM_MASTER),  	DRM_IOCTL_DEF(DRM_IOCTL_RM_MAP, drm_rmmap_ioctl, DRM_AUTH),  	DRM_IOCTL_DEF(DRM_IOCTL_SET_SAREA_CTX, drm_setsareactx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),  	DRM_IOCTL_DEF(DRM_IOCTL_GET_SAREA_CTX, drm_getsareactx, DRM_AUTH), +	DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_ROOT_ONLY), +	DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_ROOT_ONLY), +  	DRM_IOCTL_DEF(DRM_IOCTL_ADD_CTX, drm_addctx, DRM_AUTH|DRM_ROOT_ONLY),  	DRM_IOCTL_DEF(DRM_IOCTL_RM_CTX, drm_rmctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),  	DRM_IOCTL_DEF(DRM_IOCTL_MOD_CTX, drm_modctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), @@ -100,7 +103,7 @@ static struct drm_ioctl_desc drm_ioctls[] = {  	/* The DRM_IOCTL_DMA ioctl should be defined by the driver. */  	DRM_IOCTL_DEF(DRM_IOCTL_DMA, NULL, DRM_AUTH), -	DRM_IOCTL_DEF(DRM_IOCTL_CONTROL, drm_control, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), +	DRM_IOCTL_DEF(DRM_IOCTL_CONTROL, drm_control, DRM_AUTH|DRM_MASTER),  #if __OS_HAS_AGP  	DRM_IOCTL_DEF(DRM_IOCTL_AGP_ACQUIRE, drm_agp_acquire_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), @@ -121,6 +124,28 @@ static struct drm_ioctl_desc drm_ioctls[] = {  	DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_update_drawable_info, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_MASTER|DRM_CONTROL_ALLOW), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_MASTER|DRM_CONTROL_ALLOW), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_MASTER|DRM_CONTROL_ALLOW), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_MASTER|DRM_CONTROL_ALLOW), + +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_mode_connector_property_set_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_mode_attachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_mode_detachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_MASTER | DRM_CONTROL_ALLOW), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_HOTPLUG, drm_mode_hotplug_ioctl, DRM_CONTROL_ALLOW), +	DRM_IOCTL_DEF(DRM_IOCTL_WAIT_HOTPLUG, drm_wait_hotplug, 0), + +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_REPLACEFB, drm_mode_replacefb, DRM_MASTER|DRM_ROOT_ONLY|DRM_CONTROL_ALLOW), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, DRM_MASTER|DRM_CONTROL_ALLOW), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_MASTER), +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETGAMMA, drm_mode_gamma_set_ioctl, DRM_MASTER), +  	DRM_IOCTL_DEF(DRM_IOCTL_MM_INIT, drm_mm_init_ioctl,  		      DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),  	DRM_IOCTL_DEF(DRM_IOCTL_MM_TAKEDOWN, drm_mm_takedown_ioctl, @@ -150,6 +175,10 @@ static struct drm_ioctl_desc drm_ioctls[] = {  	DRM_IOCTL_DEF(DRM_IOCTL_BO_VERSION, drm_bo_version_ioctl, 0),  	DRM_IOCTL_DEF(DRM_IOCTL_MM_INFO, drm_mm_info_ioctl, 0), + +	DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, 0), +	DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH), +	DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH),  };  #define DRM_CORE_IOCTL_COUNT	ARRAY_SIZE( drm_ioctls ) @@ -166,31 +195,25 @@ static struct drm_ioctl_desc drm_ioctls[] = {   */  int drm_lastclose(struct drm_device * dev)  { -	struct drm_magic_entry *pt, *next; -	struct drm_map_list *r_list, *list_t;  	struct drm_vma_entry *vma, *vma_temp;  	int i;  	DRM_DEBUG("\n"); +	if (!drm_core_check_feature(dev, DRIVER_MODESET)) +		drm_bo_driver_finish(dev); +  	/*  	 * We can't do much about this function failing.  	 */ -	drm_bo_driver_finish(dev); -  	if (dev->driver->lastclose)  		dev->driver->lastclose(dev);  	DRM_DEBUG("driver lastclose completed\n"); -	if (dev->unique) { -		drm_free(dev->unique, strlen(dev->unique) + 1, DRM_MEM_DRIVER); -		dev->unique = NULL; -		dev->unique_len = 0; -	} -	if (dev->irq_enabled) -		drm_irq_uninstall(dev); +/*	if (dev->irq_enabled) +		drm_irq_uninstall(dev); */  	/* Free drawable information memory */  	mutex_lock(&dev->struct_mutex); @@ -198,22 +221,11 @@ int drm_lastclose(struct drm_device * dev)  	drm_drawable_free_all(dev);  	del_timer(&dev->timer); -	if (dev->unique) { -		drm_free(dev->unique, strlen(dev->unique) + 1, DRM_MEM_DRIVER); -		dev->unique = NULL; -		dev->unique_len = 0; +	if (dev->primary->master) { +		drm_put_master(dev->primary->master); +		dev->primary->master = NULL;  	} - -	if (dev->magicfree.next) { -		list_for_each_entry_safe(pt, next, &dev->magicfree, head) { -			list_del(&pt->head); -			drm_ht_remove_item(&dev->magiclist, &pt->hash_item); -			drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); -		} -		drm_ht_remove(&dev->magiclist); -	} - - +	  	/* Clear AGP information */  	if (drm_core_has_AGP(dev) && dev->agp) {  		struct drm_agp_mem *entry, *tempe; @@ -234,7 +246,10 @@ int drm_lastclose(struct drm_device * dev)  		dev->agp->acquired = 0;  		dev->agp->enabled = 0;  	} -	if (drm_core_check_feature(dev, DRIVER_SG) && dev->sg) { + +	/* You're supposed to have a real memory manager for modesetting, but this'll suffice as a temporary workaround. */ +	/* This assumes sgdma is inited at load time. */ +	if (drm_core_check_feature(dev, DRIVER_SG) && !drm_core_check_feature(dev, DRIVER_MODESET) && dev->sg) {  		drm_sg_cleanup(dev->sg);  		dev->sg = NULL;  	} @@ -245,12 +260,13 @@ int drm_lastclose(struct drm_device * dev)  		drm_ctl_free(vma, sizeof(*vma), DRM_MEM_VMAS);  	} +	/*  	list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) {  		if (!(r_list->map->flags & _DRM_DRIVER)) {  			drm_rmmap_locked(dev, r_list->map);  			r_list = NULL;  		} -	} +	}*/  	if (drm_core_check_feature(dev, DRIVER_DMA_QUEUE) && dev->queuelist) {  		for (i = 0; i < dev->queue_count; i++) { @@ -272,11 +288,6 @@ int drm_lastclose(struct drm_device * dev)  	if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))  		drm_dma_takedown(dev); -	if (dev->lock.hw_lock) { -		dev->sigdata.lock = dev->lock.hw_lock = NULL;	/* SHM removed */ -		dev->lock.file_priv = NULL; -		wake_up_interruptible(&dev->lock.lock_queue); -	}  	dev->dev_mapping = NULL;  	mutex_unlock(&dev->struct_mutex); @@ -403,22 +414,27 @@ static void drm_cleanup(struct drm_device * dev)  		DRM_DEBUG("mtrr_del=%d\n", retval);  	} +	if (dev->driver->unload) +		dev->driver->unload(dev); +         +	drm_ht_remove(&dev->map_hash);  	if (drm_core_has_AGP(dev) && dev->agp) {  		drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS);  		dev->agp = NULL;  	} -	if (dev->driver->unload) -		dev->driver->unload(dev);  	if (!drm_fb_loaded)  		pci_disable_device(dev->pdev);  	drm_ctxbitmap_cleanup(dev);  	drm_ht_remove(&dev->map_hash); -	drm_mm_takedown(&dev->offset_manager); +	drm_memrange_takedown(&dev->offset_manager);  	drm_ht_remove(&dev->object_hash); -	drm_put_minor(&dev->primary); +	drm_put_minor(dev, &dev->primary); +	if (drm_core_check_feature(dev, DRIVER_MODESET)) +		drm_put_minor(dev, &dev->control); +  	if (drm_put_dev(dev))  		DRM_ERROR("Cannot unload module\n");  } @@ -433,8 +449,14 @@ int drm_minors_cleanup(int id, void *ptr, void *data)  	if (minor->dev->driver != driver)  		return 0; -	if (minor->type != DRM_MINOR_LEGACY) -		return 0; +	if (drm_core_check_feature(dev, DRIVER_MODESET)) { +		if (minor->type != DRM_MINOR_CONTROL) +			return 0; +	} else { +		if (minor->type != DRM_MINOR_LEGACY) +			return 0; +	} +  	if (dev)  		pci_dev_put(dev->pdev); @@ -620,6 +642,7 @@ long drm_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  		retcode = -EINVAL;  		goto err_i1;  	} +	    #if 0  	/*  	 * This check is disabled, because driver private ioctl->cmd @@ -641,7 +664,7 @@ long drm_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  	if (cmd & IOC_IN) {  		if (copy_from_user(kdata, (void __user *)arg,  				   _IOC_SIZE(cmd)) != 0) { -			retcode = -EACCES; +			retcode = -EFAULT;  			goto err_i1;  		}  	} @@ -651,7 +674,8 @@ long drm_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  		retcode = -EINVAL;  	} else if (((ioctl->flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN)) ||  		   ((ioctl->flags & DRM_AUTH) && !file_priv->authenticated) || -		   ((ioctl->flags & DRM_MASTER) && !file_priv->master)) { +		   ((ioctl->flags & DRM_MASTER) && !file_priv->master) || +		   ((!(ioctl->flags & DRM_CONTROL_ALLOW)) && (file_priv->minor->type == DRM_MINOR_CONTROL)) ) {  		retcode = -EACCES;  	} else {  		retcode = func(dev, kdata, file_priv); @@ -660,13 +684,13 @@ long drm_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  	if (cmd & IOC_OUT) {  		if (copy_to_user((void __user *)arg, kdata,  				 _IOC_SIZE(cmd)) != 0) -			retcode = -EACCES; +			retcode = -EFAULT;  	}  err_i1:  	atomic_dec(&dev->ioctl_count);  	if (retcode) -		DRM_DEBUG("ret = %d\n", retcode); +		DRM_ERROR("ret = %x %d\n", nr, retcode);  	return retcode;  }  EXPORT_SYMBOL(drm_unlocked_ioctl); diff --git a/linux-core/drm_edid.c b/linux-core/drm_edid.c new file mode 100644 index 00000000..07894720 --- /dev/null +++ b/linux-core/drm_edid.c @@ -0,0 +1,737 @@ +/* + * Copyright (c) 2006 Luc Verhaegen (quirks list) + * Copyright (c) 2007 Intel Corporation + *   Jesse Barnes <jesse.barnes@intel.com> + * + * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from + * FB layer. + *   Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, 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 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include <linux/kernel.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include "drmP.h" +#include "drm_edid.h" + +/* + * TODO: + *   - support EDID 1.4 + *   - port quirks from X server code + */ + +/* + * EDID blocks out in the wild have a variety of bugs, try to collect + * them here (note that userspace may work around broken monitors first, + * but fixes should make their way here so that the kernel "just works" + * on as many displays as possible). + */ + +/* First detailed mode wrong, use largest 60Hz mode */ +#define EDID_QUIRK_PREFER_LARGE_60		(1 << 0) +/* Reported 135MHz pixel clock is too high, needs adjustment */ +#define EDID_QUIRK_135_CLOCK_TOO_HIGH		(1 << 1) +/* Prefer the largest mode at 75 Hz */ +#define EDID_QUIRK_PREFER_LARGE_75		(1 << 2) +/* Detail timing is in cm not mm */ +#define EDID_QUIRK_DETAILED_IN_CM		(1 << 3) +/* Detailed timing descriptors have bogus size values, so just take the + * maximum size and use that. + */ +#define EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE	(1 << 4) +/* Monitor forgot to set the first detailed is preferred bit. */ +#define EDID_QUIRK_FIRST_DETAILED_PREFERRED	(1 << 5) +/* use +hsync +vsync for detailed mode */ +#define EDID_QUIRK_DETAILED_SYNC_PP		(1 << 6) + +static struct edid_quirk { +	char *vendor; +	int product_id; +	u32 quirks; +} edid_quirk_list[] = { +	/* Acer AL1706 */ +	{ "ACR", 44358, EDID_QUIRK_PREFER_LARGE_60 }, +	/* Acer F51 */ +	{ "API", 0x7602, EDID_QUIRK_PREFER_LARGE_60 }, +	/* Unknown Acer */ +	{ "ACR", 2423, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, + +	/* Belinea 10 15 55 */ +	{ "MAX", 1516, EDID_QUIRK_PREFER_LARGE_60 }, +	{ "MAX", 0x77e, EDID_QUIRK_PREFER_LARGE_60 }, + +	/* Envision Peripherals, Inc. EN-7100e */ +	{ "EPI", 59264, EDID_QUIRK_135_CLOCK_TOO_HIGH }, + +	/* Funai Electronics PM36B */ +	{ "FCM", 13600, EDID_QUIRK_PREFER_LARGE_75 | +	  EDID_QUIRK_DETAILED_IN_CM }, + +	/* LG Philips LCD LP154W01-A5 */ +	{ "LPL", 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, +	{ "LPL", 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, + +	/* Philips 107p5 CRT */ +	{ "PHL", 57364, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, + +	/* Proview AY765C */ +	{ "PTS", 765, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, + +	/* Samsung SyncMaster 205BW.  Note: irony */ +	{ "SAM", 541, EDID_QUIRK_DETAILED_SYNC_PP }, +	/* Samsung SyncMaster 22[5-6]BW */ +	{ "SAM", 596, EDID_QUIRK_PREFER_LARGE_60 }, +	{ "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 }, +}; + + +/* Valid EDID header has these bytes */ +static u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; + +/** + * edid_is_valid - sanity check EDID data + * @edid: EDID data + * + * Sanity check the EDID block by looking at the header, the version number + * and the checksum.  Return 0 if the EDID doesn't check out, or 1 if it's + * valid. + */ +static bool edid_is_valid(struct edid *edid) +{ +	int i; +	u8 csum = 0; +	u8 *raw_edid = (u8 *)edid; + +	if (memcmp(edid->header, edid_header, sizeof(edid_header))) +		goto bad; +	if (edid->version != 1) { +		DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version); +		goto bad; +	} +	if (edid->revision <= 0 || edid->revision > 3) { +		DRM_ERROR("EDID has minor version %d, which is not between 0-3\n", edid->revision); +		goto bad; +	} + +	for (i = 0; i < EDID_LENGTH; i++) +		csum += raw_edid[i]; +	if (csum) { +		DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum); +		goto bad; +	} + +	return 1; + +bad: +	if (raw_edid) { +		DRM_ERROR("Raw EDID:\n"); +		print_hex_dump_bytes(KERN_ERR, DUMP_PREFIX_NONE, raw_edid, EDID_LENGTH); +		printk("\n"); +	} +	return 0; +} + +/** + * edid_vendor - match a string against EDID's obfuscated vendor field + * @edid: EDID to match + * @vendor: vendor string + * + * Returns true if @vendor is in @edid, false otherwise + */ +static bool edid_vendor(struct edid *edid, char *vendor) +{ +	char edid_vendor[3]; + +	edid_vendor[0] = ((edid->mfg_id[0] & 0x7c) >> 2) + '@'; +	edid_vendor[1] = (((edid->mfg_id[0] & 0x3) << 3) | +			  ((edid->mfg_id[1] & 0xe0) >> 5)) + '@'; +	edid_vendor[2] = (edid->mfg_id[2] & 0x1f) + '@'; + +	return !strncmp(edid_vendor, vendor, 3); +} + +/** + * edid_get_quirks - return quirk flags for a given EDID + * @edid: EDID to process + * + * This tells subsequent routines what fixes they need to apply. + */ +static u32 edid_get_quirks(struct edid *edid) +{ +	struct edid_quirk *quirk; +	int i; + +	for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) { +		quirk = &edid_quirk_list[i]; + +		if (edid_vendor(edid, quirk->vendor) && +		    (EDID_PRODUCT_ID(edid) == quirk->product_id)) +			return quirk->quirks; +	} + +	return 0; +} + +#define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay) +#define MODE_REFRESH_DIFF(m,r) (abs((m)->vrefresh - target_refresh)) + + +/** + * edid_fixup_preferred - set preferred modes based on quirk list + * @connector: has mode list to fix up + * @quirks: quirks list + * + * Walk the mode list for @connector, clearing the preferred status + * on existing modes and setting it anew for the right mode ala @quirks. + */ +static void edid_fixup_preferred(struct drm_connector *connector, +				 u32 quirks) +{ +	struct drm_display_mode *t, *cur_mode, *preferred_mode; +	int target_refresh; + +	if (list_empty(&connector->probed_modes)) +		return; + +	if (quirks & EDID_QUIRK_PREFER_LARGE_60) +		target_refresh = 60; +	if (quirks & EDID_QUIRK_PREFER_LARGE_75) +		target_refresh = 75; + +	preferred_mode = list_first_entry(&connector->probed_modes, +					  struct drm_display_mode, head); + +	list_for_each_entry_safe(cur_mode, t, &connector->probed_modes, head) { +		cur_mode->type &= ~DRM_MODE_TYPE_PREFERRED; + +		if (cur_mode == preferred_mode) +			continue; + +		/* Largest mode is preferred */ +		if (MODE_SIZE(cur_mode) > MODE_SIZE(preferred_mode)) +			preferred_mode = cur_mode; + +		/* At a given size, try to get closest to target refresh */ +		if ((MODE_SIZE(cur_mode) == MODE_SIZE(preferred_mode)) && +		    MODE_REFRESH_DIFF(cur_mode, target_refresh) < +		    MODE_REFRESH_DIFF(preferred_mode, target_refresh)) { +			preferred_mode = cur_mode; +		} +	} + +	preferred_mode->type |= DRM_MODE_TYPE_PREFERRED; +} + +/** + * drm_mode_std - convert standard mode info (width, height, refresh) into mode + * @t: standard timing params + * + * Take the standard timing params (in this case width, aspect, and refresh) + * and convert them into a real mode using CVT. + * + * Punts for now, but should eventually use the FB layer's CVT based mode + * generation code. + */ +struct drm_display_mode *drm_mode_std(struct drm_device *dev, +				      struct std_timing *t) +{ +//	struct fb_videomode mode; + +//	fb_find_mode_cvt(&mode, 0, 0); +	/* JJJ:  convert to drm_display_mode */ +	struct drm_display_mode *mode; +	int hsize = t->hsize * 8 + 248, vsize; + +	mode = drm_mode_create(dev); +	if (!mode) +		return NULL; + +	if (t->aspect_ratio == 0) +		vsize = (hsize * 10) / 16; +	else if (t->aspect_ratio == 1) +		vsize = (hsize * 3) / 4; +	else if (t->aspect_ratio == 2) +		vsize = (hsize * 4) / 5; +	else +		vsize = (hsize * 9) / 16; + +	drm_mode_set_name(mode); + +	return mode; +} + +/** + * drm_mode_detailed - create a new mode from an EDID detailed timing section + * @dev: DRM device (needed to create new mode) + * @edid: EDID block + * @timing: EDID detailed timing info + * @quirks: quirks to apply + * + * An EDID detailed timing block contains enough info for us to create and + * return a new struct drm_display_mode. + */ +static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, +						  struct edid *edid, +						  struct detailed_timing *timing, +						  u32 quirks) +{ +	struct drm_display_mode *mode; +	struct detailed_pixel_timing *pt = &timing->data.pixel_data; + +	if (pt->stereo) { +		printk(KERN_WARNING "stereo mode not supported\n"); +		return NULL; +	} +	if (!pt->separate_sync) { +		printk(KERN_WARNING "integrated sync not supported\n"); +		return NULL; +	} + +	mode = drm_mode_create(dev); +	if (!mode) +		return NULL; + +	mode->type = DRM_MODE_TYPE_DRIVER; + +	if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH) +		timing->pixel_clock = 1088; + +	mode->clock = timing->pixel_clock * 10; + +	mode->hdisplay = (pt->hactive_hi << 8) | pt->hactive_lo; +	mode->hsync_start = mode->hdisplay + ((pt->hsync_offset_hi << 8) | +					      pt->hsync_offset_lo); +	mode->hsync_end = mode->hsync_start + +		((pt->hsync_pulse_width_hi << 8) | +		 pt->hsync_pulse_width_lo); +	mode->htotal = mode->hdisplay + ((pt->hblank_hi << 8) | pt->hblank_lo); + +	mode->vdisplay = (pt->vactive_hi << 8) | pt->vactive_lo; +	mode->vsync_start = mode->vdisplay + ((pt->vsync_offset_hi << 8) | +					      pt->vsync_offset_lo); +	mode->vsync_end = mode->vsync_start + +		((pt->vsync_pulse_width_hi << 8) | +		 pt->vsync_pulse_width_lo); +	mode->vtotal = mode->vdisplay + ((pt->vblank_hi << 8) | pt->vblank_lo); + +	drm_mode_set_name(mode); + +	if (pt->interlaced) +		mode->flags |= DRM_MODE_FLAG_INTERLACE; + +	if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) { +		pt->hsync_positive = 1; +		pt->vsync_positive = 1; +	} + +	mode->flags |= pt->hsync_positive ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; +	mode->flags |= pt->vsync_positive ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; + +	mode->width_mm = pt->width_mm_lo | (pt->width_mm_hi << 8); +	mode->height_mm = pt->height_mm_lo | (pt->height_mm_hi << 8); + +	if (quirks & EDID_QUIRK_DETAILED_IN_CM) { +		mode->width_mm *= 10; +		mode->height_mm *= 10; +	} + +	if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) { +		mode->width_mm = edid->width_cm * 10; +		mode->height_mm = edid->height_cm * 10; +	} + +	return mode; +} + +/* + * Detailed mode info for the EDID "established modes" data to use. + */ +static struct drm_display_mode edid_est_modes[] = { +	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, +		   968, 1056, 0, 600, 601, 605, 628, 0, +		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */ +	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824, +		   896, 1024, 0, 600, 601, 603,  625, 0, +		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@56Hz */ +	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656, +		   720, 840, 0, 480, 481, 484, 500, 0, +		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@75Hz */ +	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664, +		   704,  832, 0, 480, 489, 491, 520, 0, +		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@72Hz */ +	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704, +		   768,  864, 0, 480, 483, 486, 525, 0, +		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@67Hz */ +	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656, +		   752, 800, 0, 480, 490, 492, 525, 0, +		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */ +	{ DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738, +		   846, 900, 0, 400, 421, 423,  449, 0, +		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 720x400@88Hz */ +	{ DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738, +		   846,  900, 0, 400, 412, 414, 449, 0, +		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 720x400@70Hz */ +	{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296, +		   1440, 1688, 0, 1024, 1025, 1028, 1066, 0, +		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */ +	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040, +		   1136, 1312, 0,  768, 769, 772, 800, 0, +		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@75Hz */ +	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048, +		   1184, 1328, 0,  768, 771, 777, 806, 0, +		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@70Hz */ +	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, +		   1184, 1344, 0,  768, 771, 777, 806, 0, +		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */ +	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032, +		   1208, 1264, 0, 768, 768, 776, 817, 0, +		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */ +	{ DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864, +		   928, 1152, 0, 624, 625, 628, 667, 0, +		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 832x624@75Hz */ +	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816, +		   896, 1056, 0, 600, 601, 604,  625, 0, +		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@75Hz */ +	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856, +		   976, 1040, 0, 600, 637, 643, 666, 0, +		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@72Hz */ +	{ DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, +		   1344, 1600, 0,  864, 865, 868, 900, 0, +		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */ +}; + +#define EDID_EST_TIMINGS 16 +#define EDID_STD_TIMINGS 8 +#define EDID_DETAILED_TIMINGS 4 + +/** + * add_established_modes - get est. modes from EDID and add them + * @edid: EDID block to scan + * + * Each EDID block contains a bitmap of the supported "established modes" list + * (defined above).  Tease them out and add them to the global modes list. + */ +static int add_established_modes(struct drm_connector *connector, struct edid *edid) +{ +	struct drm_device *dev = connector->dev; +	unsigned long est_bits = edid->established_timings.t1 | +		(edid->established_timings.t2 << 8) | +		((edid->established_timings.mfg_rsvd & 0x80) << 9); +	int i, modes = 0; + +	for (i = 0; i <= EDID_EST_TIMINGS; i++) +		if (est_bits & (1<<i)) { +			struct drm_display_mode *newmode; +			newmode = drm_mode_duplicate(dev, &edid_est_modes[i]); +			if (newmode) { +				drm_mode_probed_add(connector, newmode); +				modes++; +			} +		} + +	return modes; +} + +/** + * add_standard_modes - get std. modes from EDID and add them + * @edid: EDID block to scan + * + * Standard modes can be calculated using the CVT standard.  Grab them from + * @edid, calculate them, and add them to the list. + */ +static int add_standard_modes(struct drm_connector *connector, struct edid *edid) +{ +	struct drm_device *dev = connector->dev; +	int i, modes = 0; + +	for (i = 0; i < EDID_STD_TIMINGS; i++) { +		struct std_timing *t = &edid->standard_timings[i]; +		struct drm_display_mode *newmode; + +		/* If std timings bytes are 1, 1 it's empty */ +		if (t->hsize == 1 && (t->aspect_ratio | t->vfreq) == 1) +			continue; + +		newmode = drm_mode_std(dev, &edid->standard_timings[i]); +		if (newmode) { +			drm_mode_probed_add(connector, newmode); +			modes++; +		} +	} + +	return modes; +} + +/** + * add_detailed_modes - get detailed mode info from EDID data + * @connector: attached connector + * @edid: EDID block to scan + * @quirks: quirks to apply + * + * Some of the detailed timing sections may contain mode information.  Grab + * it and add it to the list. + */ +static int add_detailed_info(struct drm_connector *connector, +			     struct edid *edid, u32 quirks) +{ +	struct drm_device *dev = connector->dev; +	int i, j, modes = 0; + +	for (i = 0; i < EDID_DETAILED_TIMINGS; i++) { +		struct detailed_timing *timing = &edid->detailed_timings[i]; +		struct detailed_non_pixel *data = &timing->data.other_data; +		struct drm_display_mode *newmode; + +		/* EDID up to and including 1.2 may put monitor info here */ +		if (edid->version == 1 && edid->revision < 3) +			continue; + +		/* Detailed mode timing */ +		if (timing->pixel_clock) { +			newmode = drm_mode_detailed(dev, edid, timing, quirks); +			if (!newmode) +				continue; + +			/* First detailed mode is preferred */ +			if (i == 0 && edid->preferred_timing) +				newmode->type |= DRM_MODE_TYPE_PREFERRED; +			drm_mode_probed_add(connector, newmode); + +			modes++; +			continue; +		} + +		/* Other timing or info */ +		switch (data->type) { +		case EDID_DETAIL_MONITOR_SERIAL: +			break; +		case EDID_DETAIL_MONITOR_STRING: +			break; +		case EDID_DETAIL_MONITOR_RANGE: +			/* Get monitor range data */ +			break; +		case EDID_DETAIL_MONITOR_NAME: +			break; +		case EDID_DETAIL_MONITOR_CPDATA: +			break; +		case EDID_DETAIL_STD_MODES: +			/* Five modes per detailed section */ +			for (j = 0; j < 5; i++) { +				struct std_timing *std; +				struct drm_display_mode *newmode; + +				std = &data->data.timings[j]; +				newmode = drm_mode_std(dev, std); +				if (newmode) { +					drm_mode_probed_add(connector, newmode); +					modes++; +				} +			} +			break; +		default: +			break; +		} +	} + +	return modes; +} + +#define DDC_ADDR 0x50 + +unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter) +{ +	unsigned char start = 0x0; +	unsigned char *buf = kmalloc(EDID_LENGTH, GFP_KERNEL); +	struct i2c_msg msgs[] = { +		{ +			.addr	= DDC_ADDR, +			.flags	= 0, +			.len	= 1, +			.buf	= &start, +		}, { +			.addr	= DDC_ADDR, +			.flags	= I2C_M_RD, +			.len	= EDID_LENGTH, +			.buf	= buf, +		} +	}; + +	if (!buf) { +		dev_warn(&adapter->dev, "unable to allocate memory for EDID " +			 "block.\n"); +		return NULL; +	} + +	if (i2c_transfer(adapter, msgs, 2) == 2) +		return buf; + +	dev_info(&adapter->dev, "unable to read EDID block.\n"); +	kfree(buf); +	return NULL; +} +EXPORT_SYMBOL(drm_do_probe_ddc_edid); + +static unsigned char *drm_ddc_read(struct i2c_adapter *adapter) +{ +	struct i2c_algo_bit_data *algo_data = adapter->algo_data; +	unsigned char *edid = NULL; +	int i, j; + +	algo_data->setscl(algo_data->data, 1); + +	for (i = 0; i < 3; i++) { +		/* For some old monitors we need the +		 * following process to initialize/stop DDC +		 */ +		algo_data->setsda(algo_data->data, 1); +		msleep(13); + +		algo_data->setscl(algo_data->data, 1); +		for (j = 0; j < 5; j++) { +			msleep(10); +			if (algo_data->getscl(algo_data->data)) +				break; +		} +		if (j == 5) +			continue; + +		algo_data->setsda(algo_data->data, 0); +		msleep(15); +		algo_data->setscl(algo_data->data, 0); +		msleep(15); +		algo_data->setsda(algo_data->data, 1); +		msleep(15); + +		/* Do the real work */ +		edid = drm_do_probe_ddc_edid(adapter); +		algo_data->setsda(algo_data->data, 0); +		algo_data->setscl(algo_data->data, 0); +		msleep(15); + +		algo_data->setscl(algo_data->data, 1); +		for (j = 0; j < 10; j++) { +			msleep(10); +			if (algo_data->getscl(algo_data->data)) +				break; +		} + +		algo_data->setsda(algo_data->data, 1); +		msleep(15); +		algo_data->setscl(algo_data->data, 0); +		algo_data->setsda(algo_data->data, 0); +		if (edid) +			break; +	} +	/* Release the DDC lines when done or the Apple Cinema HD display +	 * will switch off +	 */ +	algo_data->setsda(algo_data->data, 1); +	algo_data->setscl(algo_data->data, 1); +	 +	return edid; +} + +/** + * drm_get_edid - get EDID data, if available + * @connector: connector we're probing + * @adapter: i2c adapter to use for DDC + * + * Poke the given connector's i2c channel to grab EDID data if possible. + *  + * Return edid data or NULL if we couldn't find any. + */ +struct edid *drm_get_edid(struct drm_connector *connector, +			  struct i2c_adapter *adapter) +{ +	struct edid *edid; + +	edid = (struct edid *)drm_ddc_read(adapter); +	if (!edid) { +		dev_warn(&connector->dev->pdev->dev, "%s: no EDID data\n", +			 drm_get_connector_name(connector)); +		return NULL; +	} +	if (!edid_is_valid(edid)) { +		dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n", +			 drm_get_connector_name(connector)); +		kfree(edid); +		return NULL; +	} + +	connector->display_info.raw_edid = (char *)edid; + +	return edid; +} +EXPORT_SYMBOL(drm_get_edid); + +/** + * drm_add_edid_modes - add modes from EDID data, if available + * @connector: connector we're probing + * @edid: edid data + * + * Add the specified modes to the connector's mode list. + * + * Return number of modes added or 0 if we couldn't find any. + */ +int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) +{ +	int num_modes = 0; +	u32 quirks; + +	if (edid == NULL) { +		return 0; +	} +	if (!edid_is_valid(edid)) { +		dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n", +			 drm_get_connector_name(connector)); +		return 0; +	} + +	quirks = edid_get_quirks(edid); + +	num_modes += add_established_modes(connector, edid); +	num_modes += add_standard_modes(connector, edid); +	num_modes += add_detailed_info(connector, edid, quirks); + +	if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) +		edid_fixup_preferred(connector, quirks); + +	connector->display_info.serration_vsync = edid->serration_vsync; +	connector->display_info.sync_on_green = edid->sync_on_green; +	connector->display_info.composite_sync = edid->composite_sync; +	connector->display_info.separate_syncs = edid->separate_syncs; +	connector->display_info.blank_to_black = edid->blank_to_black; +	connector->display_info.video_level = edid->video_level; +	connector->display_info.digital = edid->digital; +	connector->display_info.width_mm = edid->width_cm * 10; +	connector->display_info.height_mm = edid->height_cm * 10; +	connector->display_info.gamma = edid->gamma; +	connector->display_info.gtf_supported = edid->default_gtf; +	connector->display_info.standard_color = edid->standard_color; +	connector->display_info.display_type = edid->display_type; +	connector->display_info.active_off_supported = edid->pm_active_off; +	connector->display_info.suspend_supported = edid->pm_suspend; +	connector->display_info.standby_supported = edid->pm_standby; +	connector->display_info.gamma = edid->gamma; + +	return num_modes; +} +EXPORT_SYMBOL(drm_add_edid_modes); diff --git a/linux-core/drm_edid.h b/linux-core/drm_edid.h new file mode 100644 index 00000000..dccfba91 --- /dev/null +++ b/linux-core/drm_edid.h @@ -0,0 +1,178 @@ +#ifndef __DRM_EDID_H__ +#define __DRM_EDID_H__ + +#include <linux/types.h> + +#define EDID_LENGTH 128 +#define DDC_ADDR 0x50 + +#ifdef BIG_ENDIAN +#error "EDID structure is little endian, need big endian versions" +#endif + +struct est_timings { +	u8 t1; +	u8 t2; +	u8 mfg_rsvd; +} __attribute__((packed)); + +struct std_timing { +	u8 hsize; /* need to multiply by 8 then add 248 */ +	u8 vfreq:6; /* need to add 60 */ +	u8 aspect_ratio:2; /* 00=16:10, 01=4:3, 10=5:4, 11=16:9 */ +} __attribute__((packed)); + +/* If detailed data is pixel timing */ +struct detailed_pixel_timing { +	u8 hactive_lo; +	u8 hblank_lo; +	u8 hblank_hi:4; +	u8 hactive_hi:4; +	u8 vactive_lo; +	u8 vblank_lo; +	u8 vblank_hi:4; +	u8 vactive_hi:4; +	u8 hsync_offset_lo; +	u8 hsync_pulse_width_lo; +	u8 vsync_pulse_width_lo:4; +	u8 vsync_offset_lo:4; +	u8 hsync_pulse_width_hi:2; +	u8 hsync_offset_hi:2; +	u8 vsync_pulse_width_hi:2; +	u8 vsync_offset_hi:2; +	u8 width_mm_lo; +	u8 height_mm_lo; +	u8 height_mm_hi:4; +	u8 width_mm_hi:4; +	u8 hborder; +	u8 vborder; +	u8 unknown0:1; +	u8 vsync_positive:1; +	u8 hsync_positive:1; +	u8 separate_sync:2; +	u8 stereo:1; +	u8 unknown6:1; +	u8 interlaced:1; +} __attribute__((packed)); + +/* If it's not pixel timing, it'll be one of the below */ +struct detailed_data_string { +	u8 str[13]; +} __attribute__((packed)); + +struct detailed_data_monitor_range { +	u8 min_vfreq; +	u8 max_vfreq; +	u8 min_hfreq_khz; +	u8 max_hfreq_khz; +	u8 pixel_clock_mhz; /* need to multiply by 10 */ +	u16 sec_gtf_toggle; /* A000=use above, 20=use below */ /* FIXME: byte order */ +	u8 hfreq_start_khz; /* need to multiply by 2 */ +	u8 c; /* need to divide by 2 */ +	u16 m; /* FIXME: byte order */ +	u8 k; +	u8 j; /* need to divide by 2 */ +} __attribute__((packed)); + +struct detailed_data_wpindex { +	u8 white_y_lo:2; +	u8 white_x_lo:2; +	u8 pad:4; +	u8 white_x_hi; +	u8 white_y_hi; +	u8 gamma; /* need to divide by 100 then add 1 */ +} __attribute__((packed)); + +struct detailed_data_color_point { +	u8 windex1; +	u8 wpindex1[3]; +	u8 windex2; +	u8 wpindex2[3]; +} __attribute__((packed)); + +struct detailed_non_pixel { +	u8 pad1; +	u8 type; /* ff=serial, fe=string, fd=monitor range, fc=monitor name +		    fb=color point data, fa=standard timing data, +		    f9=undefined, f8=mfg. reserved */ +	u8 pad2; +	union { +		struct detailed_data_string str; +		struct detailed_data_monitor_range range; +		struct detailed_data_wpindex color; +		struct std_timing timings[5]; +	} data; +} __attribute__((packed)); + +#define EDID_DETAIL_STD_MODES 0xfa +#define EDID_DETAIL_MONITOR_CPDATA 0xfb +#define EDID_DETAIL_MONITOR_NAME 0xfc +#define EDID_DETAIL_MONITOR_RANGE 0xfd +#define EDID_DETAIL_MONITOR_STRING 0xfe +#define EDID_DETAIL_MONITOR_SERIAL 0xff + +struct detailed_timing { +	u16 pixel_clock; /* need to multiply by 10 KHz */ /* FIXME: byte order */ +	union { +		struct detailed_pixel_timing pixel_data; +		struct detailed_non_pixel other_data; +	} data; +} __attribute__((packed)); + +struct edid { +	u8 header[8]; +	/* Vendor & product info */ +	u8 mfg_id[2]; +	u8 prod_code[2]; +	u32 serial; /* FIXME: byte order */ +	u8 mfg_week; +	u8 mfg_year; +	/* EDID version */ +	u8 version; +	u8 revision; +	/* Display info: */ +	/*   input definition */ +	u8 serration_vsync:1; +	u8 sync_on_green:1; +	u8 composite_sync:1; +	u8 separate_syncs:1; +	u8 blank_to_black:1; +	u8 video_level:2; +	u8 digital:1; /* bits below must be zero if set */ +	u8 width_cm; +	u8 height_cm; +	u8 gamma; +	/*   feature support */ +	u8 default_gtf:1; +	u8 preferred_timing:1; +	u8 standard_color:1; +	u8 display_type:2; /* 00=mono, 01=rgb, 10=non-rgb, 11=unknown */ +	u8 pm_active_off:1; +	u8 pm_suspend:1; +	u8 pm_standby:1; +	/* Color characteristics */ +	u8 red_green_lo; +	u8 black_white_lo; +	u8 red_x; +	u8 red_y; +	u8 green_x; +	u8 green_y; +	u8 blue_x; +	u8 blue_y; +	u8 white_x; +	u8 white_y; +	/* Est. timings and mfg rsvd timings*/ +	struct est_timings established_timings; +	/* Standard timings 1-8*/ +	struct std_timing standard_timings[8]; +	/* Detailing timings 1-4 */ +	struct detailed_timing detailed_timings[4]; +	/* Number of 128 byte ext. blocks */ +	u8 extensions; +	/* Checksum */ +	u8 checksum; +} __attribute__((packed)); + +#define EDID_PRODUCT_ID(e) ((e)->prod_code[0] | ((e)->prod_code[1] << 8)) + +#endif /* __DRM_EDID_H__ */ diff --git a/linux-core/drm_fb.c b/linux-core/drm_fb.c new file mode 100644 index 00000000..74a4394a --- /dev/null +++ b/linux-core/drm_fb.c @@ -0,0 +1,432 @@ +/* + * Copyright © 2007 David Airlie + * + * 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 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + *     David Airlie + */ +    /* +     *  Modularization +     */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/init.h> + +#include "drmP.h" +#include "drm_crtc.h" + +struct drmfb_par { +	struct drm_device *dev; +	struct drm_crtc *crtc; +}; + +static int drmfb_setcolreg(unsigned regno, unsigned red, unsigned green, +			   unsigned blue, unsigned transp, +			   struct fb_info *info) +{ +	struct drmfb_par *par = info->par; +	struct drm_framebuffer *fb = par->crtc->fb; +	struct drm_crtc *crtc = par->crtc; + +	if (regno > 255) +		return 1; + +	if (fb->depth == 8) { +		return 0; +	} +	 + 	if (regno < 16) { +		switch (fb->depth) { +		case 15: +			fb->pseudo_palette[regno] = ((red & 0xf800) >>  1) | +				((green & 0xf800) >>  6) | +				((blue & 0xf800) >> 11); +			break; +		case 16: +			fb->pseudo_palette[regno] = (red & 0xf800) | +				((green & 0xfc00) >>  5) | +				((blue  & 0xf800) >> 11); +			break; +		case 24: +		case 32: +			fb->pseudo_palette[regno] = ((red & 0xff00) << 8) | +				(green & 0xff00) | +				((blue  & 0xff00) >> 8); +			break; +		} +        } + +	return 0; +} + +static int drmfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ +	struct drmfb_par *par = info->par; +	struct drm_device *dev = par->dev; +	struct drm_framebuffer *fb = par->crtc->fb; +	struct drm_display_mode *drm_mode; +	struct drm_output *output; +	int depth; + +	if (!var->pixclock) +		return -EINVAL; + +	/* Need to resize the fb object !!! */ +	if (var->xres > fb->width || var->yres > fb->height) { +		DRM_ERROR("Requested width/height is greater than current fb object %dx%d > %dx%d\n",var->xres,var->yres,fb->width,fb->height); +		DRM_ERROR("Need resizing code.\n"); +		return -EINVAL; +	} + +	switch (var->bits_per_pixel) { +	case 16: +		depth = (var->green.length == 6) ? 16 : 15; +		break; +	case 32: +		depth = (var->transp.length > 0) ? 32 : 24; +		break; +	default: +		depth = var->bits_per_pixel; +		break; +	} +		 +	switch (depth) { +	case 8: +		var->red.offset = 0; +		var->green.offset = 0; +		var->blue.offset = 0; +		var->red.length = 8; +		var->green.length = 8; +		var->blue.length = 8; +		var->transp.length = 0; +		var->transp.offset = 0; +		break; +	case 15: +		var->red.offset = 10; +		var->green.offset = 5; +		var->blue.offset = 0; +		var->red.length = 5; +		var->green.length = 5; +		var->blue.length = 5; +		var->transp.length = 1; +		var->transp.offset = 15; +		break; +	case 16: +		var->red.offset = 11; +		var->green.offset = 6; +		var->blue.offset = 0; +		var->red.length = 5; +		var->green.length = 6; +		var->blue.length = 5; +		var->transp.length = 0; +		var->transp.offset = 0; +		break; +	case 24: +		var->red.offset = 16; +		var->green.offset = 8; +		var->blue.offset = 0; +		var->red.length = 8; +		var->green.length = 8; +		var->blue.length = 8; +		var->transp.length = 0; +		var->transp.offset = 0; +		break; +	case 32: +		var->red.offset = 16; +		var->green.offset = 8; +		var->blue.offset = 0; +		var->red.length = 8; +		var->green.length = 8; +		var->blue.length = 8; +		var->transp.length = 8; +		var->transp.offset = 24; +		break; +	default: +		return -EINVAL;	 +	} + +#if 0 +	/* Here we walk the output mode list and look for modes. If we haven't +	 * got it, then bail. Not very nice, so this is disabled. +	 * In the set_par code, we create our mode based on the incoming +	 * parameters. Nicer, but may not be desired by some. +	 */ +	list_for_each_entry(output, &dev->mode_config.output_list, head) { +		if (output->crtc == par->crtc) +			break; +	} +     +	list_for_each_entry(drm_mode, &output->modes, head) { +		if (drm_mode->hdisplay == var->xres && +		    drm_mode->vdisplay == var->yres && +		    drm_mode->clock != 0) +		    break; +	} + +	if (!drm_mode) +		return -EINVAL; +#endif + +	return 0; +} + +/* this will let fbcon do the mode init */ +static int drmfb_set_par(struct fb_info *info) +{ +	struct drmfb_par *par = info->par; +	struct drm_framebuffer *fb = par->crtc->fb; +	struct drm_device *dev = par->dev; +	struct drm_display_mode *drm_mode; +	struct fb_var_screeninfo *var = &info->var; +	struct drm_output *output; + +	switch (var->bits_per_pixel) { +	case 16: +		fb->depth = (var->green.length == 6) ? 16 : 15; +		break; +	case 32: +		fb->depth = (var->transp.length > 0) ? 32 : 24; +		break; +	default: +		fb->depth = var->bits_per_pixel; +		break; +	} + +	fb->bits_per_pixel = var->bits_per_pixel; + +	info->fix.line_length = fb->pitch; +	info->fix.smem_len = info->fix.line_length * fb->height; +	info->fix.visual = (fb->depth == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; + +	info->screen_size = info->fix.smem_len; /* ??? */ + +	/* Should we walk the output's modelist or just create our own ??? +	 * For now, we create and destroy a mode based on the incoming  +	 * parameters. But there's commented out code below which scans  +	 * the output list too. +	 */ +#if 0 +	list_for_each_entry(output, &dev->mode_config.output_list, head) { +		if (output->crtc == par->crtc) +			break; +	} +     +	list_for_each_entry(drm_mode, &output->modes, head) { +		if (drm_mode->hdisplay == var->xres && +		    drm_mode->vdisplay == var->yres && +		    drm_mode->clock != 0) +		    break; +	} +#else +	drm_mode = drm_mode_create(dev); +	drm_mode->hdisplay = var->xres; +	drm_mode->hsync_start = drm_mode->hdisplay + var->right_margin; +	drm_mode->hsync_end = drm_mode->hsync_start + var->hsync_len; +	drm_mode->htotal = drm_mode->hsync_end + var->left_margin; +	drm_mode->vdisplay = var->yres; +	drm_mode->vsync_start = drm_mode->vdisplay + var->lower_margin; +	drm_mode->vsync_end = drm_mode->vsync_start + var->vsync_len; +	drm_mode->vtotal = drm_mode->vsync_end + var->upper_margin; +	drm_mode->clock = PICOS2KHZ(var->pixclock); +	drm_mode->vrefresh = drm_mode_vrefresh(drm_mode); +	drm_mode_set_name(drm_mode); +	drm_mode_set_crtcinfo(drm_mode, CRTC_INTERLACE_HALVE_V); +#endif + +	if (!drm_crtc_set_mode(par->crtc, drm_mode, 0, 0)) +		return -EINVAL; + +	/* Have to destroy our created mode if we're not searching the mode +	 * list for it. +	 */ +#if 1  +	drm_mode_destroy(dev, drm_mode); +#endif + +	return 0; +} + +static struct fb_ops drmfb_ops = { +	.owner = THIS_MODULE, +	//	.fb_open = drmfb_open, +	//	.fb_read = drmfb_read, +	//	.fb_write = drmfb_write, +	//	.fb_release = drmfb_release, +	//	.fb_ioctl = drmfb_ioctl, +	.fb_check_var = drmfb_check_var, +	.fb_set_par = drmfb_set_par, +	.fb_setcolreg = drmfb_setcolreg, +	.fb_fillrect = cfb_fillrect, +	.fb_copyarea = cfb_copyarea, +	.fb_imageblit = cfb_imageblit, +}; + +int drmfb_probe(struct drm_device *dev, struct drm_crtc *crtc) +{ +	struct fb_info *info; +	struct drm_framebuffer *fb = crtc->fb; +	struct drmfb_par *par; +	struct device *device = &dev->pdev->dev;  +	struct drm_display_mode *mode = crtc->desired_mode; +	int ret; + +	info = framebuffer_alloc(sizeof(struct drmfb_par), device); +	if (!info) +		return -ENOMEM; + +	fb->fbdev = info; +		 +	par = info->par; + +	par->dev = dev; +	par->crtc = crtc; + +	info->fbops = &drmfb_ops; + +	strcpy(info->fix.id, "drmfb"); +	info->fix.type = FB_TYPE_PACKED_PIXELS; +	info->fix.visual = FB_VISUAL_TRUECOLOR; +	info->fix.accel = FB_ACCEL_NONE; +	info->fix.type_aux = 0; +	info->fix.mmio_start = 0; +	info->fix.mmio_len = 0; +	info->fix.line_length = fb->pitch; +	info->fix.smem_start = fb->offset + dev->mode_config.fb_base; +	info->fix.smem_len = info->fix.line_length * fb->height; + +	info->flags = FBINFO_DEFAULT; + +	ret = drm_mem_reg_ioremap(dev, &fb->bo->mem, &fb->virtual_base); +	if (ret) +		DRM_ERROR("error mapping fb: %d\n", ret); + +	info->screen_base = fb->virtual_base; +	info->screen_size = info->fix.smem_len; /* ??? */ +	info->pseudo_palette = fb->pseudo_palette; +	info->var.xres_virtual = fb->width; +	info->var.yres_virtual = fb->height; +	info->var.bits_per_pixel = fb->bits_per_pixel; +	info->var.xoffset = 0; +	info->var.yoffset = 0; +	info->var.activate = FB_ACTIVATE_NOW; +	info->var.height = -1; +	info->var.width = -1; +	info->var.vmode = FB_VMODE_NONINTERLACED; + +	info->var.xres = mode->hdisplay; +	info->var.right_margin = mode->hsync_start - mode->hdisplay; +	info->var.hsync_len = mode->hsync_end - mode->hsync_start; +	info->var.left_margin = mode->htotal - mode->hsync_end; +	info->var.yres = mode->vdisplay; +	info->var.lower_margin = mode->vsync_start - mode->vdisplay; +	info->var.vsync_len = mode->vsync_end - mode->vsync_start; +	info->var.upper_margin = mode->vtotal - mode->vsync_end; +	info->var.pixclock = 10000000 / mode->htotal * 1000 / +				mode->vtotal * 100; +	/* avoid overflow */ +	info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh; + +	DRM_DEBUG("fb depth is %d\n", fb->depth); +	switch(fb->depth) { +	case 8: +		info->var.red.offset = 0; +		info->var.green.offset = 0; +		info->var.blue.offset = 0; +		info->var.red.length = 8; /* 8bit DAC */ +		info->var.green.length = 8; +		info->var.blue.length = 8; +		info->var.transp.offset = 0; +		info->var.transp.length = 0; +		break; +	case 15: +		info->var.red.offset = 10; +		info->var.green.offset = 5; +		info->var.blue.offset = 0; +		info->var.red.length = info->var.green.length = +			info->var.blue.length = 5; +		info->var.transp.offset = 15; +		info->var.transp.length = 1; +		break; +	case 16: +		info->var.red.offset = 11; +		info->var.green.offset = 5; +		info->var.blue.offset = 0; +		info->var.red.length = 5; +		info->var.green.length = 6; +		info->var.blue.length = 5; +		info->var.transp.offset = 0; +		info->var.transp.length = 0; +		break; +	case 24: +		info->var.red.offset = 16; +		info->var.green.offset = 8; +		info->var.blue.offset = 0; +		info->var.red.length = info->var.green.length = +			info->var.blue.length = 8; +		info->var.transp.offset = 0; +		info->var.transp.length = 0; +		break; +	case 32: +		info->var.red.offset = 16; +		info->var.green.offset = 8; +		info->var.blue.offset = 0; +		info->var.red.length = info->var.green.length = +			info->var.blue.length = 8; +		info->var.transp.offset = 24; +		info->var.transp.length = 8; +		break; +	default: +		break; +	} + +	if (register_framebuffer(info) < 0) { +		unregister_framebuffer(info); +		return -EINVAL; +	} + +	printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, +	       info->fix.id); +	return 0; +} +EXPORT_SYMBOL(drmfb_probe); + +int drmfb_remove(struct drm_device *dev, struct drm_crtc *crtc) +{ +	struct fb_info *info = fb->fbdev; +	struct drm_framebuffer *fb = crtc->fb; +	 +	if (info) { +		drm_mem_reg_iounmap(dev, &fb->bo->mem, fb->virtual_base); +		unregister_framebuffer(info); +		framebuffer_release(info); +	} +	return 0; +} +EXPORT_SYMBOL(drmfb_remove); +MODULE_LICENSE("GPL"); diff --git a/linux-core/drm_fops.c b/linux-core/drm_fops.c index 3bc25f24..3b3a0a3c 100644 --- a/linux-core/drm_fops.c +++ b/linux-core/drm_fops.c @@ -43,10 +43,8 @@ static int drm_open_helper(struct inode *inode, struct file *filp,  static int drm_setup(struct drm_device * dev)  { -	drm_local_map_t *map;  	int i;  	int ret; -	int sareapage;  	if (dev->driver->firstopen) {  		ret = dev->driver->firstopen(dev); @@ -54,14 +52,6 @@ static int drm_setup(struct drm_device * dev)  			return ret;  	} -	dev->magicfree.next = NULL; - -	/* prebuild the SAREA */ -	sareapage = max(SAREA_MAX, PAGE_SIZE); -	i = drm_addmap(dev, 0, sareapage, _DRM_SHM, _DRM_CONTAINS_LOCK, &map); -	if (i != 0) -		return i; -  	atomic_set(&dev->ioctl_count, 0);  	atomic_set(&dev->vma_count, 0);  	dev->buf_use = 0; @@ -76,11 +66,8 @@ static int drm_setup(struct drm_device * dev)  	for (i = 0; i < ARRAY_SIZE(dev->counts); i++)  		atomic_set(&dev->counts[i], 0); -	drm_ht_create(&dev->magiclist, DRM_MAGIC_HASH_ORDER); -	INIT_LIST_HEAD(&dev->magicfree); -  	dev->sigdata.lock = NULL; -	init_waitqueue_head(&dev->lock.lock_queue); +  	dev->queue_count = 0;  	dev->queue_reserved = 0;  	dev->queue_slots = 0; @@ -260,6 +247,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,  	INIT_LIST_HEAD(&priv->lhead);  	INIT_LIST_HEAD(&priv->refd_objects); +	INIT_LIST_HEAD(&priv->fbs);  	for (i = 0; i < _DRM_NO_REF_TYPES; ++i) {  		ret = drm_ht_create(&priv->refd_object_hash[i], @@ -274,16 +262,45 @@ static int drm_open_helper(struct inode *inode, struct file *filp,  		goto out_free;  	} +	if (dev->driver->driver_features & DRIVER_GEM) +		drm_gem_open(dev, priv); +  	if (dev->driver->open) {  		ret = dev->driver->open(dev, priv);  		if (ret < 0)  			goto out_free;  	} + +	/* if there is no current master make this fd it */  	mutex_lock(&dev->struct_mutex); -	if (list_empty(&dev->filelist)) -		priv->master = 1; +	if (!priv->minor->master) { +		priv->minor->master = drm_get_master(priv->minor); +		if (!priv->minor->master) { +			ret = -ENOMEM; +			goto out_free; +		} +		priv->is_master = 1; +		priv->master = priv->minor->master; + +		priv->authenticated = 1; +		mutex_unlock(&dev->struct_mutex); +		if (dev->driver->master_create) { +			ret = dev->driver->master_create(dev, priv->master); +			if (ret) { +				drm_put_master(priv->minor->master); +				priv->minor->master = priv->master = NULL; +				mutex_unlock(&dev->struct_mutex); +				goto out_free; +			} +		} +	} else { +		priv->master = priv->minor->master; +		mutex_unlock(&dev->struct_mutex); +	} + +	mutex_lock(&dev->struct_mutex);  	list_add(&priv->lhead, &dev->filelist);  	mutex_unlock(&dev->struct_mutex); @@ -389,60 +406,67 @@ int drm_release(struct inode *inode, struct file *filp)  		  current->pid, (long)old_encode_dev(file_priv->minor->device),  		  dev->open_count); -	if (dev->driver->reclaim_buffers_locked && dev->lock.hw_lock) { -		if (drm_i_have_hw_lock(dev, file_priv)) { -			dev->driver->reclaim_buffers_locked(dev, file_priv); -		} else { -			unsigned long _end=jiffies + 3*DRM_HZ; -			int locked = 0; - -			drm_idlelock_take(&dev->lock); - -			/* -			 * Wait for a while. -			 */ - -			do{ -				spin_lock_bh(&dev->lock.spinlock); -				locked = dev->lock.idle_has_lock; -				spin_unlock_bh(&dev->lock.spinlock); -				if (locked) -					break; -				schedule(); -			} while (!time_after_eq(jiffies, _end)); - -			if (!locked) { -				DRM_ERROR("reclaim_buffers_locked() deadlock. Please rework this\n" -					  "\tdriver to use reclaim_buffers_idlelocked() instead.\n" -					  "\tI will go on reclaiming the buffers anyway.\n"); +	/* if the master has gone away we can't do anything with the lock */ +	if (file_priv->minor->master) { +		if (dev->driver->reclaim_buffers_locked && file_priv->master->lock.hw_lock) { +			if (drm_i_have_hw_lock(dev, file_priv)) { +				dev->driver->reclaim_buffers_locked(dev, file_priv); +			} else { +				unsigned long _end=jiffies + 3*DRM_HZ; +				int locked = 0; +				 +				drm_idlelock_take(&file_priv->master->lock); +				 +				/* +				 * Wait for a while. +				 */ +				 +				do{ +					spin_lock_bh(&file_priv->master->lock.spinlock); +					locked = file_priv->master->lock.idle_has_lock; +					spin_unlock_bh(&file_priv->master->lock.spinlock); +					if (locked) +						break; +					schedule(); +				} while (!time_after_eq(jiffies, _end)); +				 +				if (!locked) { +					DRM_ERROR("reclaim_buffers_locked() deadlock. Please rework this\n" +						  "\tdriver to use reclaim_buffers_idlelocked() instead.\n" +						  "\tI will go on reclaiming the buffers anyway.\n"); +				} +				 +				dev->driver->reclaim_buffers_locked(dev, file_priv); +				drm_idlelock_release(&file_priv->master->lock);  			} - -			dev->driver->reclaim_buffers_locked(dev, file_priv); -			drm_idlelock_release(&dev->lock);  		} -	} - -	if (dev->driver->reclaim_buffers_idlelocked && dev->lock.hw_lock) { -		drm_idlelock_take(&dev->lock); -		dev->driver->reclaim_buffers_idlelocked(dev, file_priv); -		drm_idlelock_release(&dev->lock); +		if (dev->driver->reclaim_buffers_idlelocked && file_priv->master->lock.hw_lock) { +			 +			drm_idlelock_take(&file_priv->master->lock); +			dev->driver->reclaim_buffers_idlelocked(dev, file_priv); +			drm_idlelock_release(&file_priv->master->lock); +			 +		} -	} -	if (drm_i_have_hw_lock(dev, file_priv)) { -		DRM_DEBUG("File %p released, freeing lock for context %d\n", -			  filp, _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); +		if (drm_i_have_hw_lock(dev, file_priv)) { +			DRM_DEBUG("File %p released, freeing lock for context %d\n", +				  filp, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock)); +			 +			drm_lock_free(&file_priv->master->lock, +				      _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock)); +		} +		 -		drm_lock_free(&dev->lock, -			      _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); +		if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) && +		    !dev->driver->reclaim_buffers_locked) { +			dev->driver->reclaim_buffers(dev, file_priv); +		}  	} - -	if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) && -	    !dev->driver->reclaim_buffers_locked) { -		dev->driver->reclaim_buffers(dev, file_priv); -	} +	if (dev->driver->driver_features & DRIVER_GEM) +		drm_gem_release(dev, file_priv);  	drm_fasync(-1, filp, 0); @@ -468,15 +492,30 @@ int drm_release(struct inode *inode, struct file *filp)  	}  	mutex_unlock(&dev->ctxlist_mutex); -	mutex_lock(&dev->struct_mutex); -	drm_object_release(filp); -	if (file_priv->remove_auth_on_close == 1) { +	if (drm_core_check_feature(dev, DRIVER_MODESET)) +		drm_fb_release(filp); + +	if (file_priv->is_master) {  		struct drm_file *temp; +		list_for_each_entry(temp, &dev->filelist, lhead) { +			if ((temp->master == file_priv->master) && +			    (temp != file_priv)) +				temp->authenticated = 0; +		} -		list_for_each_entry(temp, &dev->filelist, lhead) -			temp->authenticated = 0; +		if (file_priv->minor->master == file_priv->master) +			file_priv->minor->master = NULL; +		drm_put_master(file_priv->master);  	} + +	file_priv->master = NULL; +	file_priv->is_master = 0; + +	mutex_lock(&dev->struct_mutex); +	drm_object_release(filp); +  	list_del(&file_priv->lhead); +  	mutex_unlock(&dev->struct_mutex);  	if (dev->driver->postclose) @@ -490,9 +529,9 @@ int drm_release(struct inode *inode, struct file *filp)  	atomic_inc(&dev->counts[_DRM_STAT_CLOSES]);  	spin_lock(&dev->count_lock);  	if (!--dev->open_count) { -		if (atomic_read(&dev->ioctl_count) || dev->blocked) { -			DRM_ERROR("Device busy: %d %d\n", -				  atomic_read(&dev->ioctl_count), dev->blocked); +		if (atomic_read(&dev->ioctl_count)) { +			DRM_ERROR("Device busy: %d\n", +				  atomic_read(&dev->ioctl_count));  			spin_unlock(&dev->count_lock);  			unlock_kernel();  			return -EBUSY; diff --git a/linux-core/drm_gem.c b/linux-core/drm_gem.c new file mode 100644 index 00000000..05320376 --- /dev/null +++ b/linux-core/drm_gem.c @@ -0,0 +1,419 @@ +/* + * Copyright © 2008 Intel Corporation + * + * 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 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + *    Eric Anholt <eric@anholt.net> + * + */ + +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/uaccess.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/module.h> +#include <linux/mman.h> +#include <linux/pagemap.h> +#include "drmP.h" + +/** @file drm_gem.c + * + * This file provides some of the base ioctls and library routines for + * the graphics memory manager implemented by each device driver. + * + * Because various devices have different requirements in terms of + * synchronization and migration strategies, implementing that is left up to + * the driver, and all that the general API provides should be generic -- + * allocating objects, reading/writing data with the cpu, freeing objects. + * Even there, platform-dependent optimizations for reading/writing data with + * the CPU mean we'll likely hook those out to driver-specific calls.  However, + * the DRI2 implementation wants to have at least allocate/mmap be generic. + * + * The goal was to have swap-backed object allocation managed through + * struct file.  However, file descriptors as handles to a struct file have + * two major failings: + * - Process limits prevent more than 1024 or so being used at a time by + *   default. + * - Inability to allocate high fds will aggravate the X Server's select() + *   handling, and likely that of many GL client applications as well. + * + * This led to a plan of using our own integer IDs (called handles, following + * DRM terminology) to mimic fds, and implement the fd syscalls we need as + * ioctls.  The objects themselves will still include the struct file so + * that we can transition to fds if the required kernel infrastructure shows + * up at a later date, and as our interface with shmfs for memory allocation. + */ + +/** + * Initialize the GEM device fields + */ + +int +drm_gem_init(struct drm_device *dev) +{ +	spin_lock_init(&dev->object_name_lock); +	idr_init(&dev->object_name_idr); +	atomic_set(&dev->object_count, 0); +	atomic_set(&dev->object_memory, 0); +	atomic_set(&dev->pin_count, 0); +	atomic_set(&dev->pin_memory, 0); +	atomic_set(&dev->gtt_count, 0); +	atomic_set(&dev->gtt_memory, 0); +	return 0; +} + +/** + * Allocate a GEM object of the specified size with shmfs backing store + */ +struct drm_gem_object * +drm_gem_object_alloc(struct drm_device *dev, size_t size) +{ +	struct drm_gem_object *obj; + +	BUG_ON((size & (PAGE_SIZE - 1)) != 0); + +	obj = kcalloc(1, sizeof(*obj), GFP_KERNEL); + +	obj->dev = dev; +	obj->filp = shmem_file_setup("drm mm object", size, 0); +	if (IS_ERR(obj->filp)) { +		kfree(obj); +		return NULL; +	} +	kref_init(&obj->refcount); +	kref_init(&obj->handlecount); +	obj->size = size; +	if (dev->driver->gem_init_object != NULL && +	    dev->driver->gem_init_object(obj) != 0) { +		fput(obj->filp); +		kfree(obj); +		return NULL; +	} +	atomic_inc(&dev->object_count); +	atomic_add(obj->size, &dev->object_memory); +	return obj; +} +EXPORT_SYMBOL(drm_gem_object_alloc); + +/** + * Removes the mapping from handle to filp for this object. + */ +static int +drm_gem_handle_delete(struct drm_file *filp, int handle) +{ +	struct drm_device *dev; +	struct drm_gem_object *obj; + +	/* This is gross. The idr system doesn't let us try a delete and +	 * return an error code.  It just spews if you fail at deleting. +	 * So, we have to grab a lock around finding the object and then +	 * doing the delete on it and dropping the refcount, or the user +	 * could race us to double-decrement the refcount and cause a +	 * use-after-free later.  Given the frequency of our handle lookups, +	 * we may want to use ida for number allocation and a hash table +	 * for the pointers, anyway. +	 */ +	spin_lock(&filp->table_lock); + +	/* Check if we currently have a reference on the object */ +	obj = idr_find(&filp->object_idr, handle); +	if (obj == NULL) { +		spin_unlock(&filp->table_lock); +		return -EINVAL; +	} +	dev = obj->dev; + +	/* Release reference and decrement refcount. */ +	idr_remove(&filp->object_idr, handle); +	spin_unlock(&filp->table_lock); + +	mutex_lock(&dev->struct_mutex); +	drm_gem_object_handle_unreference(obj); +	mutex_unlock(&dev->struct_mutex); + +	return 0; +} + +/** + * Create a handle for this object. This adds a handle reference + * to the object, which includes a regular reference count. Callers + * will likely want to dereference the object afterwards. + */ +int +drm_gem_handle_create(struct drm_file *file_priv, +		       struct drm_gem_object *obj, +		       int *handlep) +{ +	int	ret; + +	/* +	 * Get the user-visible handle using idr. +	 */ +again: +	/* ensure there is space available to allocate a handle */ +	if (idr_pre_get(&file_priv->object_idr, GFP_KERNEL) == 0) +		return -ENOMEM; + +	/* do the allocation under our spinlock */ +	spin_lock(&file_priv->table_lock); +	ret = idr_get_new_above(&file_priv->object_idr, obj, 1, handlep); +	spin_unlock(&file_priv->table_lock); +	if (ret == -EAGAIN) +		goto again; + +	if (ret != 0) +		return ret; + +	drm_gem_object_handle_reference(obj); +	return 0; +} +EXPORT_SYMBOL(drm_gem_handle_create); + +/** Returns a reference to the object named by the handle. */ +struct drm_gem_object * +drm_gem_object_lookup(struct drm_device *dev, struct drm_file *filp, +		      int handle) +{ +	struct drm_gem_object *obj; + +	spin_lock(&filp->table_lock); + +	/* Check if we currently have a reference on the object */ +	obj = idr_find(&filp->object_idr, handle); +	if (obj == NULL) { +		spin_unlock(&filp->table_lock); +		return NULL; +	} + +	drm_gem_object_reference(obj); + +	spin_unlock(&filp->table_lock); + +	return obj; +} +EXPORT_SYMBOL(drm_gem_object_lookup); + +/** + * Releases the handle to an mm object. + */ +int +drm_gem_close_ioctl(struct drm_device *dev, void *data, +		    struct drm_file *file_priv) +{ +	struct drm_gem_close *args = data; +	int ret; + +	if (!(dev->driver->driver_features & DRIVER_GEM)) +		return -ENODEV; + +	ret = drm_gem_handle_delete(file_priv, args->handle); + +	return ret; +} + +/** + * Create a global name for an object, returning the name. + * + * Note that the name does not hold a reference; when the object + * is freed, the name goes away. + */ +int +drm_gem_flink_ioctl(struct drm_device *dev, void *data, +		    struct drm_file *file_priv) +{ +	struct drm_gem_flink *args = data; +	struct drm_gem_object *obj; +	int ret; + +	if (!(dev->driver->driver_features & DRIVER_GEM)) +		return -ENODEV; + +	obj = drm_gem_object_lookup(dev, file_priv, args->handle); +	if (obj == NULL) +		return -EINVAL; + +again: +	if (idr_pre_get(&dev->object_name_idr, GFP_KERNEL) == 0) +		return -ENOMEM; + +	spin_lock(&dev->object_name_lock); +	if (obj->name) { +		spin_unlock(&dev->object_name_lock); +		return -EEXIST; +	} +	ret = idr_get_new_above(&dev->object_name_idr, obj, 1, +				 &obj->name); +	spin_unlock(&dev->object_name_lock); +	if (ret == -EAGAIN) +		goto again; + +	if (ret != 0) { +		mutex_lock(&dev->struct_mutex); +		drm_gem_object_unreference(obj); +		mutex_unlock(&dev->struct_mutex); +		return ret; +	} + +	/* +	 * Leave the reference from the lookup around as the +	 * name table now holds one +	 */ +	args->name = (uint64_t) obj->name; + +	return 0; +} + +/** + * Open an object using the global name, returning a handle and the size. + * + * This handle (of course) holds a reference to the object, so the object + * will not go away until the handle is deleted. + */ +int +drm_gem_open_ioctl(struct drm_device *dev, void *data, +		   struct drm_file *file_priv) +{ +	struct drm_gem_open *args = data; +	struct drm_gem_object *obj; +	int ret; +	int handle; + +	if (!(dev->driver->driver_features & DRIVER_GEM)) +		return -ENODEV; + +	spin_lock(&dev->object_name_lock); +	obj = idr_find(&dev->object_name_idr, (int) args->name); +	if (obj) +		drm_gem_object_reference(obj); +	spin_unlock(&dev->object_name_lock); +	if (!obj) +		return -ENOENT; + +	ret = drm_gem_handle_create(file_priv, obj, &handle); +	mutex_lock(&dev->struct_mutex); +	drm_gem_object_unreference(obj); +	mutex_unlock(&dev->struct_mutex); +	if (ret) +		return ret; + +	args->handle = handle; +	args->size = obj->size; + +	return 0; +} + +/** + * Called at device open time, sets up the structure for handling refcounting + * of mm objects. + */ +void +drm_gem_open(struct drm_device *dev, struct drm_file *file_private) +{ +	idr_init(&file_private->object_idr); +	spin_lock_init(&file_private->table_lock); +} + +/** + * Called at device close to release the file's + * handle references on objects. + */ +static int +drm_gem_object_release_handle(int id, void *ptr, void *data) +{ +	struct drm_gem_object *obj = ptr; + +	drm_gem_object_handle_unreference(obj); + +	return 0; +} + +/** + * Called at close time when the filp is going away. + * + * Releases any remaining references on objects by this filp. + */ +void +drm_gem_release(struct drm_device *dev, struct drm_file *file_private) +{ +	mutex_lock(&dev->struct_mutex); +	idr_for_each(&file_private->object_idr, +		     &drm_gem_object_release_handle, NULL); + +	idr_destroy(&file_private->object_idr); +	mutex_unlock(&dev->struct_mutex); +} + +/** + * Called after the last reference to the object has been lost. + * + * Frees the object + */ +void +drm_gem_object_free(struct kref *kref) +{ +	struct drm_gem_object *obj = (struct drm_gem_object *) kref; +	struct drm_device *dev = obj->dev; + +	BUG_ON(!mutex_is_locked(&dev->struct_mutex)); + +	if (dev->driver->gem_free_object != NULL) +		dev->driver->gem_free_object(obj); + +	fput(obj->filp); +	atomic_dec(&dev->object_count); +	atomic_sub(obj->size, &dev->object_memory); +	kfree(obj); +} +EXPORT_SYMBOL(drm_gem_object_free); + +/** + * Called after the last handle to the object has been closed + * + * Removes any name for the object. Note that this must be + * called before drm_gem_object_free or we'll be touching + * freed memory + */ +void +drm_gem_object_handle_free(struct kref *kref) +{ +	struct drm_gem_object *obj = container_of(kref, +						  struct drm_gem_object, +						  handlecount); +	struct drm_device *dev = obj->dev; + +	/* Remove any name for this object */ +	spin_lock(&dev->object_name_lock); +	if (obj->name) { +		idr_remove(&dev->object_name_idr, obj->name); +		spin_unlock(&dev->object_name_lock); +		/* +		 * The object name held a reference to this object, drop +		 * that now. +		 */ +		drm_gem_object_unreference(obj); +	} else +		spin_unlock(&dev->object_name_lock); + +} +EXPORT_SYMBOL(drm_gem_object_handle_free); + diff --git a/linux-core/drm_ioctl.c b/linux-core/drm_ioctl.c index 3df163db..e35126a3 100644 --- a/linux-core/drm_ioctl.c +++ b/linux-core/drm_ioctl.c @@ -53,12 +53,13 @@ int drm_getunique(struct drm_device *dev, void *data,  		  struct drm_file *file_priv)  {  	struct drm_unique *u = data; +	struct drm_master *master = file_priv->master; -	if (u->unique_len >= dev->unique_len) { -		if (copy_to_user(u->unique, dev->unique, dev->unique_len)) +	if (u->unique_len >= master->unique_len) { +		if (copy_to_user(u->unique, master->unique, master->unique_len))  			return -EFAULT;  	} -	u->unique_len = dev->unique_len; +	u->unique_len = master->unique_len;  	return 0;  } @@ -81,36 +82,37 @@ int drm_setunique(struct drm_device *dev, void *data,  		  struct drm_file *file_priv)  {  	struct drm_unique *u = data; +	struct drm_master *master = file_priv->master;  	int domain, bus, slot, func, ret; -	if (dev->unique_len || dev->unique) +	if (master->unique_len || master->unique)  		return -EBUSY;  	if (!u->unique_len || u->unique_len > 1024)  		return -EINVAL; -	dev->unique_len = u->unique_len; -	dev->unique = drm_alloc(u->unique_len + 1, DRM_MEM_DRIVER); -	if (!dev->unique) +	master->unique_len = u->unique_len; +	master->unique = drm_alloc(u->unique_len + 1, DRM_MEM_DRIVER); +	if (!master->unique)  		return -ENOMEM; -	if (copy_from_user(dev->unique, u->unique, dev->unique_len)) +	if (copy_from_user(master->unique, u->unique, master->unique_len))  		return -EFAULT; -	dev->unique[dev->unique_len] = '\0'; +	master->unique[master->unique_len] = '\0';  	dev->devname =  	    drm_alloc(strlen(dev->driver->pci_driver.name) + -		      strlen(dev->unique) + 2, DRM_MEM_DRIVER); +		      strlen(master->unique) + 2, DRM_MEM_DRIVER);  	if (!dev->devname)  		return -ENOMEM;  	sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name, -		dev->unique); +		master->unique);  	/* Return error if the busid submitted doesn't match the device's actual  	 * busid.  	 */ -	ret = sscanf(dev->unique, "PCI:%d:%d:%d", &bus, &slot, &func); +	ret = sscanf(master->unique, "PCI:%d:%d:%d", &bus, &slot, &func);  	if (ret != 3)  		return -EINVAL;  	domain = bus >> 8; @@ -125,33 +127,35 @@ int drm_setunique(struct drm_device *dev, void *data,  	return 0;  } -static int drm_set_busid(struct drm_device * dev) +static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv)  { +	struct drm_master *master = file_priv->master;  	int len; -	if (dev->unique != NULL) + +	if (master->unique != NULL)  		return -EBUSY; -	dev->unique_len = 40; -	dev->unique = drm_alloc(dev->unique_len + 1, DRM_MEM_DRIVER); -	if (dev->unique == NULL) +	master->unique_len = 40; +	master->unique = drm_alloc(master->unique_len + 1, DRM_MEM_DRIVER); +	if (master->unique == NULL)  		return -ENOMEM; -	len = snprintf(dev->unique, dev->unique_len, "pci:%04x:%02x:%02x.%d", +	len = snprintf(master->unique, master->unique_len, "pci:%04x:%02x:%02x.%d",  		       drm_get_pci_domain(dev),  		       dev->pdev->bus->number,  		       PCI_SLOT(dev->pdev->devfn),  		       PCI_FUNC(dev->pdev->devfn)); -	if (len > dev->unique_len) +	if (len > master->unique_len)  		DRM_ERROR("buffer overflow");  	dev->devname = -	    drm_alloc(strlen(dev->driver->pci_driver.name) + dev->unique_len + +	    drm_alloc(strlen(dev->driver->pci_driver.name) + master->unique_len +  		      2, DRM_MEM_DRIVER);  	if (dev->devname == NULL)  		return -ENOMEM;  	sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name, -		dev->unique); +		master->unique);  	return 0;  } @@ -275,7 +279,7 @@ int drm_getstats(struct drm_device *dev, void *data,  	for (i = 0; i < dev->counters; i++) {  		if (dev->types[i] == _DRM_STAT_LOCK)  			stats->data[i].value = -			    (dev->lock.hw_lock ? dev->lock.hw_lock->lock : 0); +			    (file_priv->master->lock.hw_lock ? file_priv->master->lock.hw_lock->lock : 0);  		else  			stats->data[i].value = atomic_read(&dev->counts[i]);  		stats->data[i].type = dev->types[i]; @@ -317,7 +321,7 @@ int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_pri  			/*  			 * Version 1.1 includes tying of DRM to specific device  			 */ -			drm_set_busid(dev); +			drm_set_busid(dev, file_priv);  		}  	} diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index d0d6f987..800768ae 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -63,7 +63,7 @@ int drm_irq_by_busid(struct drm_device *dev, void *data,  	    p->devnum != PCI_SLOT(dev->pdev->devfn) || p->funcnum != PCI_FUNC(dev->pdev->devfn))  		return -EINVAL; -	p->irq = dev->irq; +	p->irq = dev->pdev->irq;  	DRM_DEBUG("%d:%d:%d => IRQ %d\n", p->busnum, p->devnum, p->funcnum,  		  p->irq); @@ -96,30 +96,37 @@ static void vblank_disable_fn(unsigned long arg)  static void drm_vblank_cleanup(struct drm_device *dev)  { -	/* Bail if the driver didn't call drm_vblank_init() */ -	if (dev->num_crtcs == 0) -		return; -  	del_timer(&dev->vblank_disable_timer);  	vblank_disable_fn((unsigned long)dev); -	drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs, +	if (dev->vbl_queue) +	    drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs,  		 DRM_MEM_DRIVER); -	drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs, + +	if (dev->vbl_sigs) +	    drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs,  		 DRM_MEM_DRIVER); -	drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) * + +	if (dev->_vblank_count) +	    drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) *  		 dev->num_crtcs, DRM_MEM_DRIVER); -	drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) * + +	if (dev->vblank_refcount) +	    drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) *  		 dev->num_crtcs, DRM_MEM_DRIVER); -	drm_free(dev->vblank_enabled, sizeof(*dev->vblank_enabled) * + +	if (dev->vblank_enabled) +	    drm_free(dev->vblank_enabled, sizeof(*dev->vblank_enabled) *  		 dev->num_crtcs, DRM_MEM_DRIVER); -	drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * dev->num_crtcs, + +	if (dev->last_vblank) +	    drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * dev->num_crtcs,  		 DRM_MEM_DRIVER); -	drm_free(dev->vblank_inmodeset, sizeof(*dev->vblank_inmodeset) * -		 dev->num_crtcs, DRM_MEM_DRIVER); -	dev->num_crtcs = 0; +	if (dev->vblank_inmodeset) +	    drm_free(dev->vblank_inmodeset, sizeof(*dev->vblank_inmodeset) * +		     dev->num_crtcs, DRM_MEM_DRIVER);  }  int drm_vblank_init(struct drm_device *dev, int num_crtcs) @@ -128,6 +135,7 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)  	setup_timer(&dev->vblank_disable_timer, vblank_disable_fn,  		    (unsigned long)dev); +	init_timer_deferrable(&dev->vblank_disable_timer);  	spin_lock_init(&dev->vbl_lock);  	atomic_set(&dev->vbl_signal_pending, 0);  	dev->num_crtcs = num_crtcs; @@ -184,6 +192,82 @@ err:  }  EXPORT_SYMBOL(drm_vblank_init); +int drm_wait_hotplug(struct drm_device *dev, void *data, +		    struct drm_file *file_priv) +{ +	union drm_wait_hotplug *hotplugwait = data; +	struct timeval now; +	int ret = 0; +	unsigned int flags; + +	if ((!dev->irq) || (!dev->irq_enabled)) +		return -EINVAL; + +	flags = hotplugwait->request.type; + +	if (flags & _DRM_HOTPLUG_SIGNAL) { +		unsigned long irqflags; +		struct list_head *hotplug_sigs = dev->hotplug_sigs; +		struct drm_hotplug_sig *hotplug_sig; + +		hotplug_sig = drm_calloc(1, sizeof(struct drm_hotplug_sig), +				     DRM_MEM_DRIVER); +		if (!hotplug_sig) +			return -ENOMEM; + +		atomic_inc(&dev->hotplug_signal_pending); + +		hotplug_sig->info.si_signo = hotplugwait->request.signal; +		hotplug_sig->task = current; +		hotplug_sig->counter =  +			hotplugwait->reply.counter =  +					dev->mode_config.hotplug_counter; + +		spin_lock_irqsave(&dev->hotplug_lock, irqflags); + +		list_add_tail(&hotplug_sig->head, hotplug_sigs); + +		spin_unlock_irqrestore(&dev->hotplug_lock, irqflags); +	} else { +		int cur_hotplug = dev->mode_config.hotplug_counter; + +		DRM_WAIT_ON(ret, dev->hotplug_queue, 3 * DRM_HZ, +				dev->mode_config.hotplug_counter > cur_hotplug); + +		do_gettimeofday(&now); + +		hotplugwait->reply.tval_sec = now.tv_sec; +		hotplugwait->reply.tval_usec = now.tv_usec; +		hotplugwait->reply.counter = dev->mode_config.hotplug_counter; +	} + +	return ret; +} + +static void drm_hotplug_cleanup(struct drm_device *dev) +{ +	if (dev->hotplug_sigs) +	    drm_free(dev->hotplug_sigs, sizeof(*dev->hotplug_sigs), +		 DRM_MEM_DRIVER); +} +EXPORT_SYMBOL(drm_hotplug_cleanup); + +int drm_hotplug_init(struct drm_device *dev) +{ +	spin_lock_init(&dev->hotplug_lock); +	atomic_set(&dev->hotplug_signal_pending, 0); + +	dev->hotplug_sigs = drm_alloc(sizeof(struct list_head), DRM_MEM_DRIVER); +	if (!dev->hotplug_sigs) +		return -ENOMEM; + +	INIT_LIST_HEAD(dev->hotplug_sigs); +	init_waitqueue_head(&dev->hotplug_queue); + +	return 0; +} +EXPORT_SYMBOL(drm_hotplug_init); +  /**   * Install IRQ handler.   * @@ -201,7 +285,7 @@ int drm_irq_install(struct drm_device * dev)  	if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))  		return -EINVAL; -	if (dev->irq == 0) +	if (dev->pdev->irq == 0)  		return -EINVAL;  	mutex_lock(&dev->struct_mutex); @@ -214,12 +298,12 @@ int drm_irq_install(struct drm_device * dev)  	if (dev->irq_enabled) {  		mutex_unlock(&dev->struct_mutex); -		return -EBUSY; +		return 0;  	}  	dev->irq_enabled = 1;  	mutex_unlock(&dev->struct_mutex); -	DRM_DEBUG("irq=%d\n", dev->irq); +	DRM_DEBUG("irq=%d\n", dev->pdev->irq);  	/* Before installing handler */  	dev->driver->irq_preinstall(dev); @@ -228,7 +312,7 @@ int drm_irq_install(struct drm_device * dev)  	if (drm_core_check_feature(dev, DRIVER_IRQ_SHARED))  		sh_flags = IRQF_SHARED; -	ret = request_irq(dev->irq, dev->driver->irq_handler, +	ret = request_irq(dev->pdev->irq, dev->driver->irq_handler,  			  sh_flags, dev->devname, dev);  	if (ret < 0) {  		mutex_lock(&dev->struct_mutex); @@ -236,6 +320,10 @@ int drm_irq_install(struct drm_device * dev)  		mutex_unlock(&dev->struct_mutex);  		return ret;  	} +	/* Expose the device irq to device drivers that want to export it for +	 * whatever reason. +	 */ +	dev->irq = dev->pdev->irq;  	/* After installing handler */  	ret = dev->driver->irq_postinstall(dev); @@ -271,14 +359,16 @@ int drm_irq_uninstall(struct drm_device * dev)  	if (!irq_enabled)  		return -EINVAL; -	DRM_DEBUG("irq=%d\n", dev->irq); +	DRM_DEBUG("irq=%d\n", dev->pdev->irq);  	dev->driver->irq_uninstall(dev); -	free_irq(dev->irq, dev); +	free_irq(dev->pdev->irq, dev);  	drm_vblank_cleanup(dev); +	drm_hotplug_cleanup(dev); +  	dev->locked_tasklet_func = NULL;  	return 0; @@ -308,13 +398,17 @@ int drm_control(struct drm_device *dev, void *data,  	case DRM_INST_HANDLER:  		if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))  			return 0; +		if (drm_core_check_feature(dev, DRIVER_MODESET)) +			return 0;  		if (dev->if_version < DRM_IF_VERSION(1, 2) && -		    ctl->irq != dev->irq) +		    ctl->irq != dev->pdev->irq)  			return -EINVAL;  		return drm_irq_install(dev);  	case DRM_UNINST_HANDLER:  		if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))  			return 0; +		if (drm_core_check_feature(dev, DRIVER_MODESET)) +			return 0;  		return drm_irq_uninstall(dev);  	default:  		return -EINVAL; @@ -514,7 +608,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,  	int ret = 0;  	unsigned int flags, seq, crtc; -	if ((!dev->irq) || (!dev->irq_enabled)) +	if ((!dev->pdev->irq) || (!dev->irq_enabled))  		return -EINVAL;  	if (vblwait->request.type & @@ -555,7 +649,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,  	if (flags & _DRM_VBLANK_SIGNAL) {  		unsigned long irqflags;  		struct list_head *vbl_sigs = &dev->vbl_sigs[crtc]; -		struct drm_vbl_sig *vbl_sig; +		struct drm_vbl_sig *vbl_sig, *tmp;  		spin_lock_irqsave(&dev->vbl_lock, irqflags); @@ -563,7 +657,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,  		 * for the same vblank sequence number; nothing to be done in  		 * that case  		 */ -		list_for_each_entry(vbl_sig, vbl_sigs, head) { +		list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) {  			if (vbl_sig->sequence == vblwait->request.sequence  			    && vbl_sig->info.si_signo ==  			    vblwait->request.signal @@ -688,6 +782,53 @@ void drm_handle_vblank(struct drm_device *dev, int crtc)  EXPORT_SYMBOL(drm_handle_vblank);  /** + * Send the HOTPLUG signals. + * + * \param dev DRM device. + * + * Sends a signal for each task in drm_device::hotplug_sigs and empties the list. + */ +static void drm_hotplug_send_signals(struct drm_device * dev) +{ +	struct drm_hotplug_sig *hotplug_sig, *tmp; +	struct list_head *hotplug_sigs; +	unsigned long flags; + +	spin_lock_irqsave(&dev->hotplug_lock, flags); + +	hotplug_sigs = dev->hotplug_sigs; + +	list_for_each_entry_safe(hotplug_sig, tmp, hotplug_sigs, head) { +	    hotplug_sig->info.si_code = hotplug_sig->counter; + +	    send_sig_info(hotplug_sig->info.si_signo, +			      &hotplug_sig->info, hotplug_sig->task); + +	    list_del(&hotplug_sig->head); + +	    drm_free(hotplug_sig, sizeof(*hotplug_sig), +			 DRM_MEM_DRIVER); +	    atomic_dec(&dev->hotplug_signal_pending); +	} + +	spin_unlock_irqrestore(&dev->hotplug_lock, flags); +} + +/** + * drm_handle_hotplug - handle a hotplug event + * @dev: DRM device + * @crtc: where this event occurred + * + * Drivers should call this routine in their hotplug interrupt handlers. + */ +void drm_handle_hotplug(struct drm_device *dev) +{ +	DRM_WAKEUP(&dev->hotplug_queue); +	drm_hotplug_send_signals(dev); +} +EXPORT_SYMBOL(drm_handle_hotplug); + +/**   * Tasklet wrapper function.   *   * \param data DRM device in disguise. @@ -704,18 +845,18 @@ static void drm_locked_tasklet_func(unsigned long data)  	spin_lock_irqsave(&dev->tasklet_lock, irqflags);  	if (!dev->locked_tasklet_func || -	    !drm_lock_take(&dev->lock, +	    !drm_lock_take(&dev->primary->master->lock,  			   DRM_KERNEL_CONTEXT)) {  		spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);  		return;  	} -	dev->lock.lock_time = jiffies; +	dev->primary->master->lock.lock_time = jiffies;  	atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);  	dev->locked_tasklet_func(dev); -	drm_lock_free(&dev->lock, +	drm_lock_free(&dev->primary->master->lock,  		      DRM_KERNEL_CONTEXT);  	dev->locked_tasklet_func = NULL; diff --git a/linux-core/drm_lock.c b/linux-core/drm_lock.c index 573213de..59f95e4e 100644 --- a/linux-core/drm_lock.c +++ b/linux-core/drm_lock.c @@ -52,6 +52,7 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)  {  	DECLARE_WAITQUEUE(entry, current);  	struct drm_lock *lock = data; +	struct drm_master *master = file_priv->master;  	int ret = 0;  	++file_priv->lock_count; @@ -64,26 +65,27 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)  	DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",  		  lock->context, current->pid, -		  dev->lock.hw_lock->lock, lock->flags); +		  master->lock.hw_lock->lock, lock->flags);  	if (drm_core_check_feature(dev, DRIVER_DMA_QUEUE))  		if (lock->context < 0)  			return -EINVAL; -	add_wait_queue(&dev->lock.lock_queue, &entry); -	spin_lock_bh(&dev->lock.spinlock); -	dev->lock.user_waiters++; -	spin_unlock_bh(&dev->lock.spinlock); +	add_wait_queue(&master->lock.lock_queue, &entry); +	spin_lock_bh(&master->lock.spinlock); +	master->lock.user_waiters++; +	spin_unlock_bh(&master->lock.spinlock); +  	for (;;) {  		__set_current_state(TASK_INTERRUPTIBLE); -		if (!dev->lock.hw_lock) { +		if (!master->lock.hw_lock) {  			/* Device has been unregistered */  			ret = -EINTR;  			break;  		} -		if (drm_lock_take(&dev->lock, lock->context)) { -			dev->lock.file_priv = file_priv; -			dev->lock.lock_time = jiffies; +		if (drm_lock_take(&master->lock, lock->context)) { +			master->lock.file_priv = file_priv; +			master->lock.lock_time = jiffies;  			atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);  			break;	/* Got lock */  		} @@ -95,11 +97,11 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)  			break;  		}  	} -	spin_lock_bh(&dev->lock.spinlock); -	dev->lock.user_waiters--; -	spin_unlock_bh(&dev->lock.spinlock); +	spin_lock_bh(&master->lock.spinlock); +	master->lock.user_waiters--; +	spin_unlock_bh(&master->lock.spinlock);  	__set_current_state(TASK_RUNNING); -	remove_wait_queue(&dev->lock.lock_queue, &entry); +	remove_wait_queue(&master->lock.lock_queue, &entry);  	DRM_DEBUG("%d %s\n", lock->context,  		  ret ? "interrupted" : "has lock"); @@ -108,14 +110,14 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)  	/* don't set the block all signals on the master process for now   	 * really probably not the correct answer but lets us debug xkb   	 * xserver for now */ -	if (!file_priv->master) { +	if (!file_priv->is_master) {  		sigemptyset(&dev->sigmask);  		sigaddset(&dev->sigmask, SIGSTOP);  		sigaddset(&dev->sigmask, SIGTSTP);  		sigaddset(&dev->sigmask, SIGTTIN);  		sigaddset(&dev->sigmask, SIGTTOU);  		dev->sigdata.context = lock->context; -		dev->sigdata.lock = dev->lock.hw_lock; +		dev->sigdata.lock = master->lock.hw_lock;  		block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask);  	} @@ -154,6 +156,7 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)  int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv)  {  	struct drm_lock *lock = data; +	struct drm_master *master = file_priv->master;  	unsigned long irqflags;  	if (lock->context == DRM_KERNEL_CONTEXT) { @@ -180,7 +183,7 @@ int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv)  	if (dev->driver->kernel_context_switch_unlock)  		dev->driver->kernel_context_switch_unlock(dev);  	else { -		if (drm_lock_free(&dev->lock,lock->context)) { +		if (drm_lock_free(&master->lock,lock->context)) {  			/* FIXME: Should really bail out here. */  		}  	} @@ -386,13 +389,12 @@ void drm_idlelock_release(struct drm_lock_data *lock_data)  }  EXPORT_SYMBOL(drm_idlelock_release); -  int drm_i_have_hw_lock(struct drm_device *dev, struct drm_file *file_priv)  { - -	return (file_priv->lock_count && dev->lock.hw_lock && -		_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) && -		dev->lock.file_priv == file_priv); +	struct drm_master *master = file_priv->master; +	return (file_priv->lock_count && master->lock.hw_lock && +		_DRM_LOCK_IS_HELD(master->lock.hw_lock->lock) && +		master->lock.file_priv == file_priv);  }  EXPORT_SYMBOL(drm_i_have_hw_lock); diff --git a/linux-core/drm_memory.c b/linux-core/drm_memory.c index e1df3dac..b90fc020 100644 --- a/linux-core/drm_memory.c +++ b/linux-core/drm_memory.c @@ -310,6 +310,7 @@ int drm_free_agp(DRM_AGP_MEM * handle, int pages)  {  	return drm_agp_free_memory(handle) ? 0 : -EINVAL;  } +EXPORT_SYMBOL(drm_free_agp);  /** Wrapper around agp_bind_memory() */  int drm_bind_agp(DRM_AGP_MEM * handle, unsigned int start) @@ -322,6 +323,7 @@ int drm_unbind_agp(DRM_AGP_MEM * handle)  {  	return drm_agp_unbind_memory(handle);  } +EXPORT_SYMBOL(drm_unbind_agp);  #else  /* __OS_HAS_AGP*/  static void *agp_remap(unsigned long offset, unsigned long size, diff --git a/linux-core/drm_mm.c b/linux-core/drm_memrange.c index 59110293..5921eff8 100644 --- a/linux-core/drm_mm.c +++ b/linux-core/drm_memrange.c @@ -44,26 +44,26 @@  #include "drmP.h"  #include <linux/slab.h> -unsigned long drm_mm_tail_space(struct drm_mm *mm) +unsigned long drm_memrange_tail_space(struct drm_memrange *mm)  {  	struct list_head *tail_node; -	struct drm_mm_node *entry; +	struct drm_memrange_node *entry;  	tail_node = mm->ml_entry.prev; -	entry = list_entry(tail_node, struct drm_mm_node, ml_entry); +	entry = list_entry(tail_node, struct drm_memrange_node, ml_entry);  	if (!entry->free)  		return 0;  	return entry->size;  } -int drm_mm_remove_space_from_tail(struct drm_mm *mm, unsigned long size) +int drm_memrange_remove_space_from_tail(struct drm_memrange *mm, unsigned long size)  {  	struct list_head *tail_node; -	struct drm_mm_node *entry; +	struct drm_memrange_node *entry;  	tail_node = mm->ml_entry.prev; -	entry = list_entry(tail_node, struct drm_mm_node, ml_entry); +	entry = list_entry(tail_node, struct drm_memrange_node, ml_entry);  	if (!entry->free)  		return -ENOMEM; @@ -75,13 +75,13 @@ int drm_mm_remove_space_from_tail(struct drm_mm *mm, unsigned long size)  } -static int drm_mm_create_tail_node(struct drm_mm *mm, +static int drm_memrange_create_tail_node(struct drm_memrange *mm,  			    unsigned long start,  			    unsigned long size)  { -	struct drm_mm_node *child; +	struct drm_memrange_node *child; -	child = (struct drm_mm_node *) +	child = (struct drm_memrange_node *)  		drm_ctl_alloc(sizeof(*child), DRM_MEM_MM);  	if (!child)  		return -ENOMEM; @@ -98,26 +98,26 @@ static int drm_mm_create_tail_node(struct drm_mm *mm,  } -int drm_mm_add_space_to_tail(struct drm_mm *mm, unsigned long size) +int drm_memrange_add_space_to_tail(struct drm_memrange *mm, unsigned long size)  {  	struct list_head *tail_node; -	struct drm_mm_node *entry; +	struct drm_memrange_node *entry;  	tail_node = mm->ml_entry.prev; -	entry = list_entry(tail_node, struct drm_mm_node, ml_entry); +	entry = list_entry(tail_node, struct drm_memrange_node, ml_entry);  	if (!entry->free) { -		return drm_mm_create_tail_node(mm, entry->start + entry->size, size); +		return drm_memrange_create_tail_node(mm, entry->start + entry->size, size);  	}  	entry->size += size;  	return 0;  } -static struct drm_mm_node *drm_mm_split_at_start(struct drm_mm_node *parent, +static struct drm_memrange_node *drm_memrange_split_at_start(struct drm_memrange_node *parent,  					    unsigned long size)  { -	struct drm_mm_node *child; +	struct drm_memrange_node *child; -	child = (struct drm_mm_node *) +	child = (struct drm_memrange_node *)  		drm_ctl_alloc(sizeof(*child), DRM_MEM_MM);  	if (!child)  		return NULL; @@ -137,19 +137,19 @@ static struct drm_mm_node *drm_mm_split_at_start(struct drm_mm_node *parent,  	return child;  } -struct drm_mm_node *drm_mm_get_block(struct drm_mm_node * parent, +struct drm_memrange_node *drm_memrange_get_block(struct drm_memrange_node * parent,  				unsigned long size, unsigned alignment)  { -	struct drm_mm_node *align_splitoff = NULL; -	struct drm_mm_node *child; +	struct drm_memrange_node *align_splitoff = NULL; +	struct drm_memrange_node *child;  	unsigned tmp = 0;  	if (alignment)  		tmp = parent->start % alignment;  	if (tmp) { -		align_splitoff = drm_mm_split_at_start(parent, alignment - tmp); +		align_splitoff = drm_memrange_split_at_start(parent, alignment - tmp);  		if (!align_splitoff)  			return NULL;  	} @@ -159,40 +159,41 @@ struct drm_mm_node *drm_mm_get_block(struct drm_mm_node * parent,  		parent->free = 0;  		return parent;  	} else { -		child = drm_mm_split_at_start(parent, size); +		child = drm_memrange_split_at_start(parent, size);  	}  	if (align_splitoff) -		drm_mm_put_block(align_splitoff); +		drm_memrange_put_block(align_splitoff);  	return child;  } +EXPORT_SYMBOL(drm_memrange_get_block);  /*   * Put a block. Merge with the previous and / or next block if they are free.   * Otherwise add to the free stack.   */ -void drm_mm_put_block(struct drm_mm_node * cur) +void drm_memrange_put_block(struct drm_memrange_node * cur)  { -	struct drm_mm *mm = cur->mm; +	struct drm_memrange *mm = cur->mm;  	struct list_head *cur_head = &cur->ml_entry;  	struct list_head *root_head = &mm->ml_entry; -	struct drm_mm_node *prev_node = NULL; -	struct drm_mm_node *next_node; +	struct drm_memrange_node *prev_node = NULL; +	struct drm_memrange_node *next_node;  	int merged = 0;  	if (cur_head->prev != root_head) { -		prev_node = list_entry(cur_head->prev, struct drm_mm_node, ml_entry); +		prev_node = list_entry(cur_head->prev, struct drm_memrange_node, ml_entry);  		if (prev_node->free) {  			prev_node->size += cur->size;  			merged = 1;  		}  	}  	if (cur_head->next != root_head) { -		next_node = list_entry(cur_head->next, struct drm_mm_node, ml_entry); +		next_node = list_entry(cur_head->next, struct drm_memrange_node, ml_entry);  		if (next_node->free) {  			if (merged) {  				prev_node->size += next_node->size; @@ -215,16 +216,16 @@ void drm_mm_put_block(struct drm_mm_node * cur)  		drm_ctl_free(cur, sizeof(*cur), DRM_MEM_MM);  	}  } -EXPORT_SYMBOL(drm_mm_put_block); +EXPORT_SYMBOL(drm_memrange_put_block); -struct drm_mm_node *drm_mm_search_free(const struct drm_mm * mm, +struct drm_memrange_node *drm_memrange_search_free(const struct drm_memrange * mm,  				  unsigned long size,  				  unsigned alignment, int best_match)  {  	struct list_head *list;  	const struct list_head *free_stack = &mm->fl_entry; -	struct drm_mm_node *entry; -	struct drm_mm_node *best; +	struct drm_memrange_node *entry; +	struct drm_memrange_node *best;  	unsigned long best_size;  	unsigned wasted; @@ -232,7 +233,7 @@ struct drm_mm_node *drm_mm_search_free(const struct drm_mm * mm,  	best_size = ~0UL;  	list_for_each(list, free_stack) { -		entry = list_entry(list, struct drm_mm_node, fl_entry); +		entry = list_entry(list, struct drm_memrange_node, fl_entry);  		wasted = 0;  		if (entry->size < size) @@ -257,30 +258,31 @@ struct drm_mm_node *drm_mm_search_free(const struct drm_mm * mm,  	return best;  } +EXPORT_SYMBOL(drm_memrange_search_free); -int drm_mm_clean(struct drm_mm * mm) +int drm_memrange_clean(struct drm_memrange * mm)  {  	struct list_head *head = &mm->ml_entry;  	return (head->next->next == head);  } -int drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size) +int drm_memrange_init(struct drm_memrange * mm, unsigned long start, unsigned long size)  {  	INIT_LIST_HEAD(&mm->ml_entry);  	INIT_LIST_HEAD(&mm->fl_entry); -	return drm_mm_create_tail_node(mm, start, size); +	return drm_memrange_create_tail_node(mm, start, size);  } -EXPORT_SYMBOL(drm_mm_init); +EXPORT_SYMBOL(drm_memrange_init); -void drm_mm_takedown(struct drm_mm * mm) +void drm_memrange_takedown(struct drm_memrange * mm)  {  	struct list_head *bnode = mm->fl_entry.next; -	struct drm_mm_node *entry; +	struct drm_memrange_node *entry; -	entry = list_entry(bnode, struct drm_mm_node, fl_entry); +	entry = list_entry(bnode, struct drm_memrange_node, fl_entry);  	if (entry->ml_entry.next != &mm->ml_entry ||  	    entry->fl_entry.next != &mm->fl_entry) { @@ -292,5 +294,4 @@ void drm_mm_takedown(struct drm_mm * mm)  	list_del(&entry->ml_entry);  	drm_ctl_free(entry, sizeof(*entry), DRM_MEM_MM);  } - -EXPORT_SYMBOL(drm_mm_takedown); +EXPORT_SYMBOL(drm_memrange_takedown); diff --git a/linux-core/drm_modes.c b/linux-core/drm_modes.c new file mode 100644 index 00000000..67f278eb --- /dev/null +++ b/linux-core/drm_modes.c @@ -0,0 +1,570 @@ +/* + * Copyright © 1997-2003 by The XFree86 Project, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Except as contained in this notice, the name of the copyright holder(s) + * and author(s) shall not be used in advertising or otherwise to promote + * the sale, use or other dealings in this Software without prior written + * authorization from the copyright holder(s) and author(s). + */ +/* + * Copyright © 2007 Dave Airlie + */ + +#include <linux/list.h> +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" + +/** + * drm_mode_debug_printmodeline - debug print a mode + * @dev: DRM device + * @mode: mode to print + * + * LOCKING: + * None. + * + * Describe @mode using DRM_DEBUG. + */ +void drm_mode_debug_printmodeline(struct drm_display_mode *mode) +{ +	DRM_DEBUG("Modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n", +		  mode->base.id, mode->name, mode->vrefresh, mode->clock, +		  mode->hdisplay, mode->hsync_start, +		  mode->hsync_end, mode->htotal, +		  mode->vdisplay, mode->vsync_start, +		  mode->vsync_end, mode->vtotal, mode->type, mode->flags); +} +EXPORT_SYMBOL(drm_mode_debug_printmodeline); + +/** + * drm_mode_set_name - set the name on a mode + * @mode: name will be set in this mode + * + * LOCKING: + * None. + * + * Set the name of @mode to a standard format. + */ +void drm_mode_set_name(struct drm_display_mode *mode) +{ +	snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d", mode->hdisplay, +		 mode->vdisplay); +} +EXPORT_SYMBOL(drm_mode_set_name); + +/** + * drm_mode_list_concat - move modes from one list to another + * @head: source list + * @new: dst list + * + * LOCKING: + * Caller must ensure both lists are locked. + * + * Move all the modes from @head to @new. + */ +void drm_mode_list_concat(struct list_head *head, struct list_head *new) +{ + +	struct list_head *entry, *tmp; + +	list_for_each_safe(entry, tmp, head) { +		list_move_tail(entry, new); +	} +} +EXPORT_SYMBOL(drm_mode_list_concat); + +/** + * drm_mode_width - get the width of a mode + * @mode: mode + * + * LOCKING: + * None. + * + * Return @mode's width (hdisplay) value. + * + * FIXME: is this needed? + * + * RETURNS: + * @mode->hdisplay + */ +int drm_mode_width(struct drm_display_mode *mode) +{ +	return mode->hdisplay; + +} +EXPORT_SYMBOL(drm_mode_width); + +/** + * drm_mode_height - get the height of a mode + * @mode: mode + * + * LOCKING: + * None. + * + * Return @mode's height (vdisplay) value. + * + * FIXME: is this needed? + * + * RETURNS: + * @mode->vdisplay + */ +int drm_mode_height(struct drm_display_mode *mode) +{ +	return mode->vdisplay; +} +EXPORT_SYMBOL(drm_mode_height); + +/** + * drm_mode_vrefresh - get the vrefresh of a mode + * @mode: mode + * + * LOCKING: + * None. + * + * Return @mode's vrefresh rate or calculate it if necessary. + * + * FIXME: why is this needed?  shouldn't vrefresh be set already? + * + * RETURNS: + * Vertical refresh rate of @mode x 1000. For precision reasons. + */ +int drm_mode_vrefresh(struct drm_display_mode *mode) +{ +	int refresh = 0; +	unsigned int calc_val; + +	if (mode->vrefresh > 0) +		refresh = mode->vrefresh; +	else if (mode->htotal > 0 && mode->vtotal > 0) { +		/* work out vrefresh the value will be x1000 */ +		calc_val = (mode->clock * 1000); + +		calc_val /= mode->htotal; +		calc_val *= 1000; +		calc_val /= mode->vtotal; + +		refresh = calc_val; +		if (mode->flags & DRM_MODE_FLAG_INTERLACE) +			refresh *= 2; +		if (mode->flags & DRM_MODE_FLAG_DBLSCAN) +			refresh /= 2; +		if (mode->vscan > 1) +			refresh /= mode->vscan; +	} +	return refresh; +} +EXPORT_SYMBOL(drm_mode_vrefresh); +	 +/** + * drm_mode_set_crtcinfo - set CRTC modesetting parameters + * @p: mode + * @adjust_flags: unused? (FIXME) + * + * LOCKING: + * None. + * + * Setup the CRTC modesetting parameters for @p, adjusting if necessary. + */ +void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags) +{ +	if ((p == NULL) || ((p->type & DRM_MODE_TYPE_CRTC_C) == DRM_MODE_TYPE_BUILTIN)) +		return; + +	p->crtc_hdisplay = p->hdisplay; +	p->crtc_hsync_start = p->hsync_start; +	p->crtc_hsync_end = p->hsync_end; +	p->crtc_htotal = p->htotal; +	p->crtc_hskew = p->hskew; +	p->crtc_vdisplay = p->vdisplay; +	p->crtc_vsync_start = p->vsync_start; +	p->crtc_vsync_end = p->vsync_end; +	p->crtc_vtotal = p->vtotal; + +	if (p->flags & DRM_MODE_FLAG_INTERLACE) { +		if (adjust_flags & CRTC_INTERLACE_HALVE_V) { +			p->crtc_vdisplay /= 2; +			p->crtc_vsync_start /= 2; +			p->crtc_vsync_end /= 2; +			p->crtc_vtotal /= 2; +		} + +		p->crtc_vtotal |= 1; +	} + +	if (p->flags & DRM_MODE_FLAG_DBLSCAN) { +		p->crtc_vdisplay *= 2; +		p->crtc_vsync_start *= 2; +		p->crtc_vsync_end *= 2; +		p->crtc_vtotal *= 2; +	} + +	if (p->vscan > 1) { +		p->crtc_vdisplay *= p->vscan; +		p->crtc_vsync_start *= p->vscan; +		p->crtc_vsync_end *= p->vscan; +		p->crtc_vtotal *= p->vscan; +	} + +	p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay); +	p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal); +	p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay); +	p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal); + +	p->crtc_hadjusted = false; +	p->crtc_vadjusted = false; +} +EXPORT_SYMBOL(drm_mode_set_crtcinfo); + + +/** + * drm_mode_duplicate - allocate and duplicate an existing mode + * @m: mode to duplicate + * + * LOCKING: + * None. + * + * Just allocate a new mode, copy the existing mode into it, and return + * a pointer to it.  Used to create new instances of established modes. + */ +struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, +					    struct drm_display_mode *mode) +{ +	struct drm_display_mode *nmode; +	int new_id; + +	nmode = drm_mode_create(dev); +	if (!nmode) +		return NULL; + +	new_id = nmode->base.id; +	*nmode = *mode; +	nmode->base.id = new_id; +	INIT_LIST_HEAD(&nmode->head); +	return nmode; +} +EXPORT_SYMBOL(drm_mode_duplicate); + +/** + * drm_mode_equal - test modes for equality + * @mode1: first mode + * @mode2: second mode + * + * LOCKING: + * None. + * + * Check to see if @mode1 and @mode2 are equivalent. + * + * RETURNS: + * True if the modes are equal, false otherwise. + */ +bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2) +{ +	/* do clock check convert to PICOS so fb modes get matched +	 * the same */ +	if (mode1->clock && mode2->clock) { +		if (KHZ2PICOS(mode1->clock) != KHZ2PICOS(mode2->clock)) +			return false; +	} else if (mode1->clock != mode2->clock) +		return false; + +	if (mode1->hdisplay == mode2->hdisplay && +	    mode1->hsync_start == mode2->hsync_start && +	    mode1->hsync_end == mode2->hsync_end && +	    mode1->htotal == mode2->htotal && +	    mode1->hskew == mode2->hskew && +	    mode1->vdisplay == mode2->vdisplay && +	    mode1->vsync_start == mode2->vsync_start && +	    mode1->vsync_end == mode2->vsync_end && +	    mode1->vtotal == mode2->vtotal && +	    mode1->vscan == mode2->vscan && +	    mode1->flags == mode2->flags) +		return true; +	 +	return false; +} +EXPORT_SYMBOL(drm_mode_equal); + +/** + * drm_mode_validate_size - make sure modes adhere to size constraints + * @dev: DRM device + * @mode_list: list of modes to check + * @maxX: maximum width + * @maxY: maximum height + * @maxPitch: max pitch + * + * LOCKING: + * Caller must hold a lock protecting @mode_list. + * + * The DRM device (@dev) has size and pitch limits.  Here we validate the + * modes we probed for @dev against those limits and set their status as + * necessary. + */ +void drm_mode_validate_size(struct drm_device *dev, +			    struct list_head *mode_list, +			    int maxX, int maxY, int maxPitch) +{ +	struct drm_display_mode *mode; + +	list_for_each_entry(mode, mode_list, head) { +		if (maxPitch > 0 && mode->hdisplay > maxPitch) +			mode->status = MODE_BAD_WIDTH; +		 +		if (maxX > 0 && mode->hdisplay > maxX) +			mode->status = MODE_VIRTUAL_X; + +		if (maxY > 0 && mode->vdisplay > maxY) +			mode->status = MODE_VIRTUAL_Y; +	} +} +EXPORT_SYMBOL(drm_mode_validate_size); + +/** + * drm_mode_validate_clocks - validate modes against clock limits + * @dev: DRM device + * @mode_list: list of modes to check + * @min: minimum clock rate array + * @max: maximum clock rate array + * @n_ranges: number of clock ranges (size of arrays) + * + * LOCKING: + * Caller must hold a lock protecting @mode_list. + * + * Some code may need to check a mode list against the clock limits of the + * device in question.  This function walks the mode list, testing to make + * sure each mode falls within a given range (defined by @min and @max + * arrays) and sets @mode->status as needed. + */ +void drm_mode_validate_clocks(struct drm_device *dev, +			      struct list_head *mode_list, +			      int *min, int *max, int n_ranges) +{ +	struct drm_display_mode *mode; +	int i; + +	list_for_each_entry(mode, mode_list, head) { +		bool good = false; +		for (i = 0; i < n_ranges; i++) { +			if (mode->clock >= min[i] && mode->clock <= max[i]) { +				good = true; +				break; +			} +		} +		if (!good) +			mode->status = MODE_CLOCK_RANGE; +	} +} +EXPORT_SYMBOL(drm_mode_validate_clocks); + +/** + * drm_mode_prune_invalid - remove invalid modes from mode list + * @dev: DRM device + * @mode_list: list of modes to check + * @verbose: be verbose about it + * + * LOCKING: + * Caller must hold a lock protecting @mode_list. + * + * Once mode list generation is complete, a caller can use this routine to + * remove invalid modes from a mode list.  If any of the modes have a + * status other than %MODE_OK, they are removed from @mode_list and freed. + */ +void drm_mode_prune_invalid(struct drm_device *dev, +			    struct list_head *mode_list, bool verbose) +{ +	struct drm_display_mode *mode, *t; + +	list_for_each_entry_safe(mode, t, mode_list, head) { +		if (mode->status != MODE_OK) { +			list_del(&mode->head); +			if (verbose) { +				drm_mode_debug_printmodeline(mode); +				DRM_DEBUG("Not using %s mode %d\n", mode->name, mode->status); +			} +			drm_mode_destroy(dev, mode); +		} +	} +} +EXPORT_SYMBOL(drm_mode_prune_invalid); + +/** + * drm_mode_compare - compare modes for favorability + * @lh_a: list_head for first mode + * @lh_b: list_head for second mode + * + * LOCKING: + * None. + * + * Compare two modes, given by @lh_a and @lh_b, returning a value indicating + * which is better. + * + * RETURNS: + * Negative if @lh_a is better than @lh_b, zero if they're equivalent, or + * positive if @lh_b is better than @lh_a. + */ +static int drm_mode_compare(struct list_head *lh_a, struct list_head *lh_b) +{ +	struct drm_display_mode *a = list_entry(lh_a, struct drm_display_mode, head); +	struct drm_display_mode *b = list_entry(lh_b, struct drm_display_mode, head); +	int diff; + +	diff = ((b->type & DRM_MODE_TYPE_PREFERRED) != 0) - +		((a->type & DRM_MODE_TYPE_PREFERRED) != 0); +	if (diff) +		return diff; +	diff = b->hdisplay * b->vdisplay - a->hdisplay * a->vdisplay; +	if (diff) +		return diff; +	diff = b->clock - a->clock; +	return diff; +} + +/* FIXME: what we don't have a list sort function? */ +/* list sort from Mark J Roberts (mjr@znex.org) */ +void list_sort(struct list_head *head, int (*cmp)(struct list_head *a, struct list_head *b)) +{ +	struct list_head *p, *q, *e, *list, *tail, *oldhead; +	int insize, nmerges, psize, qsize, i; +	 +	list = head->next; +	list_del(head); +	insize = 1; +	for (;;) { +		p = oldhead = list; +		list = tail = NULL; +		nmerges = 0; +		 +		while (p) { +			nmerges++; +			q = p; +			psize = 0; +			for (i = 0; i < insize; i++) { +				psize++; +				q = q->next == oldhead ? NULL : q->next; +				if (!q) +					break; +			} +			 +			qsize = insize; +			while (psize > 0 || (qsize > 0 && q)) { +				if (!psize) { +					e = q; +					q = q->next; +					qsize--; +					if (q == oldhead) +						q = NULL; +				} else if (!qsize || !q) { +					e = p; +					p = p->next; +					psize--; +					if (p == oldhead) +						p = NULL; +				} else if (cmp(p, q) <= 0) { +					e = p; +					p = p->next; +					psize--; +					if (p == oldhead) +						p = NULL; +				} else { +					e = q; +					q = q->next; +					qsize--; +					if (q == oldhead) +						q = NULL; +				} +				if (tail) +					tail->next = e; +				else +					list = e; +				e->prev = tail; +				tail = e; +			} +			p = q; +		} +		 +		tail->next = list; +		list->prev = tail; +		 +		if (nmerges <= 1) +			break; +		 +		insize *= 2; +	} +	 +	head->next = list; +	head->prev = list->prev; +	list->prev->next = head; +	list->prev = head; +} + +/** + * drm_mode_sort - sort mode list + * @mode_list: list to sort + * + * LOCKING: + * Caller must hold a lock protecting @mode_list. + * + * Sort @mode_list by favorability, putting good modes first. + */ +void drm_mode_sort(struct list_head *mode_list) +{ +	list_sort(mode_list, drm_mode_compare); +} +EXPORT_SYMBOL(drm_mode_sort); + +/** + * drm_mode_connector_list_update - update the mode list for the connector + * @connector: the connector to update + * + * LOCKING: + * Caller must hold a lock protecting @mode_list. + * + * This moves the modes from the @connector probed_modes list + * to the actual mode list. It compares the probed mode against the current + * list and only adds different modes. All modes unverified after this point + * will be removed by the prune invalid modes. + */ +void drm_mode_connector_list_update(struct drm_connector *connector) +{ +	struct drm_display_mode *mode; +	struct drm_display_mode *pmode, *pt; +	int found_it; + +	list_for_each_entry_safe(pmode, pt, &connector->probed_modes, +				 head) { +		found_it = 0; +		/* go through current modes checking for the new probed mode */ +		list_for_each_entry(mode, &connector->modes, head) { +			if (drm_mode_equal(pmode, mode)) { +				found_it = 1; +				/* if equal delete the probed mode */ +				mode->status = pmode->status; +				list_del(&pmode->head); +				drm_mode_destroy(connector->dev, pmode); +				break; +			} +		} + +		if (!found_it) { +			list_move_tail(&pmode->head, &connector->modes); +		} +	} +} +EXPORT_SYMBOL(drm_mode_connector_list_update); diff --git a/linux-core/drm_objects.h b/linux-core/drm_objects.h index 770fbc56..96cfc113 100644 --- a/linux-core/drm_objects.h +++ b/linux-core/drm_objects.h @@ -156,7 +156,6 @@ struct drm_fence_object {  };  #define _DRM_FENCE_CLASSES 8 -#define _DRM_FENCE_TYPE_EXE 0x00  struct drm_fence_class_manager {  	struct list_head ring; @@ -301,7 +300,12 @@ struct drm_ttm_backend_func {  	void (*destroy) (struct drm_ttm_backend *backend);  }; - +/** + * This structure associates a set of flags and methods with a drm_ttm + * object, and will also be subclassed by the particular backend. + * + * \sa #drm_agp_ttm_backend + */  struct drm_ttm_backend {  	struct drm_device *dev;  	uint32_t flags; @@ -413,7 +417,7 @@ extern int drm_ttm_destroy(struct drm_ttm *ttm);   */  struct drm_bo_mem_reg { -	struct drm_mm_node *mm_node; +	struct drm_memrange_node *mm_node;  	unsigned long size;  	unsigned long num_pages;  	uint32_t page_alignment; @@ -494,7 +498,7 @@ struct drm_buffer_object {  	unsigned long num_pages;  	/* For pinned buffers */ -	struct drm_mm_node *pinned_node; +	struct drm_memrange_node *pinned_node;  	uint32_t pinned_mem_type;  	struct list_head pinned_lru; @@ -529,7 +533,7 @@ struct drm_mem_type_manager {  	int has_type;  	int use_type;  	int kern_init_type; -	struct drm_mm manager; +	struct drm_memrange manager;  	struct list_head lru;  	struct list_head pinned;  	uint32_t flags; @@ -651,12 +655,12 @@ struct drm_bo_driver {  /*   * buffer objects (drm_bo.c)   */ -  extern int drm_bo_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);  extern int drm_bo_destroy_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);  extern int drm_bo_map_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);  extern int drm_bo_unmap_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);  extern int drm_bo_reference_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int drm_bo_set_pin(struct drm_device *dev, struct drm_buffer_object *bo, int pin);  extern int drm_bo_unreference_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);  extern int drm_bo_wait_idle_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);  extern int drm_bo_info_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); @@ -676,6 +680,8 @@ extern int drm_bo_pci_offset(struct drm_device *dev,  			     unsigned long *bus_size);  extern int drm_mem_reg_is_pci(struct drm_device *dev, struct drm_bo_mem_reg *mem); +extern int drm_bo_add_user_object(struct drm_file *file_priv, +				  struct drm_buffer_object *bo, int shareable);  extern void drm_bo_usage_deref_locked(struct drm_buffer_object **bo);  extern void drm_bo_usage_deref_unlocked(struct drm_buffer_object **bo);  extern void drm_putback_buffer_objects(struct drm_device *dev); @@ -714,6 +720,8 @@ extern int drm_bo_do_validate(struct drm_buffer_object *bo,  			      uint32_t fence_class,  			      struct drm_bo_info_rep *rep);  extern int drm_bo_evict_cached(struct drm_buffer_object *bo); + +extern void drm_bo_takedown_vm_locked(struct drm_buffer_object *bo);  /*   * Buffer object memory move- and map helpers.   * drm_bo_move.c @@ -800,6 +808,10 @@ extern void drm_regs_init(struct drm_reg_manager *manager,  					      const void *),  			  void (*reg_destroy)(struct drm_reg *)); +extern int drm_mem_reg_ioremap(struct drm_device *dev, struct drm_bo_mem_reg * mem, +			       void **virtual); +extern void drm_mem_reg_iounmap(struct drm_device *dev, struct drm_bo_mem_reg * mem, +				void *virtual);  /*   * drm_bo_lock.c   * Simple replacement for the hardware lock on buffer manager init and clean. diff --git a/linux-core/drm_proc.c b/linux-core/drm_proc.c index 42da5c69..127a7987 100644 --- a/linux-core/drm_proc.c +++ b/linux-core/drm_proc.c @@ -51,6 +51,10 @@ static int drm_bufs_info(char *buf, char **start, off_t offset,  			 int request, int *eof, void *data);  static int drm_objects_info(char *buf, char **start, off_t offset,  			 int request, int *eof, void *data); +static int drm_gem_name_info(char *buf, char **start, off_t offset, +			     int request, int *eof, void *data); +static int drm_gem_object_info(char *buf, char **start, off_t offset, +			       int request, int *eof, void *data);  #if DRM_DEBUG_CODE  static int drm_vma_info(char *buf, char **start, off_t offset,  			int request, int *eof, void *data); @@ -70,6 +74,8 @@ static struct drm_proc_list {  	{"queues", drm_queues_info},  	{"bufs", drm_bufs_info},  	{"objects", drm_objects_info}, +	{"gem_names", drm_gem_name_info}, +	{"gem_objects", drm_gem_object_info},  #if DRM_DEBUG_CODE  	{"vma", drm_vma_info},  #endif @@ -166,6 +172,7 @@ static int drm_name_info(char *buf, char **start, off_t offset, int request,  			 int *eof, void *data)  {  	struct drm_minor *minor = (struct drm_minor *) data;  +	struct drm_master *master = minor->master;  	struct drm_device *dev = minor->dev;  	int len = 0; @@ -174,13 +181,16 @@ static int drm_name_info(char *buf, char **start, off_t offset, int request,  		return 0;  	} +	if (!master) +		return 0; +  	*start = &buf[offset];  	*eof = 0; -	if (dev->unique) { +	if (master->unique) {  		DRM_PROC_PRINT("%s %s %s\n",  			       dev->driver->pci_driver.name, -			       pci_name(dev->pdev), dev->unique); +			       pci_name(dev->pdev), master->unique);  	} else {  		DRM_PROC_PRINT("%s %s\n", dev->driver->pci_driver.name,  			       pci_name(dev->pdev)); @@ -582,6 +592,84 @@ static int drm_clients_info(char *buf, char **start, off_t offset,  	return ret;  } +struct drm_gem_name_info_data { +	int			len; +	char			*buf; +	int			eof; +}; + +static int drm_gem_one_name_info (int id, void *ptr, void *data) +{ +	struct drm_gem_object *obj = ptr; +	struct drm_gem_name_info_data	*nid = data; + +	DRM_INFO ("name %d size %d\n", obj->name, obj->size); +	if (nid->eof) +		return 0; +	 +	nid->len += sprintf (&nid->buf[nid->len], +			     "%6d%9d%8d%9d\n", +			     obj->name, obj->size, +			     atomic_read(&obj->handlecount.refcount), +			     atomic_read(&obj->refcount.refcount)); +	if (nid->len > DRM_PROC_LIMIT) { +		nid->eof = 1; +		return 0; +	} +	return 0; +} + +static int drm_gem_name_info(char *buf, char **start, off_t offset, +			     int request, int *eof, void *data) +{ +	struct drm_minor *minor = (struct drm_minor *) data;  +	struct drm_device *dev = minor->dev; +	struct drm_gem_name_info_data nid; +	 +	if (offset > DRM_PROC_LIMIT) { +		*eof = 1; +		return 0; +	} + +	nid.len = sprintf (buf, "  name     size handles refcount\n"); +	nid.buf = buf; +	nid.eof = 0; +	idr_for_each (&dev->object_name_idr, drm_gem_one_name_info, &nid); +	 +	*start = &buf[offset]; +	*eof = 0; +	if (nid.len > request + offset) +		return request; +	*eof = 1; +	return nid.len - offset; +} + +static int drm_gem_object_info(char *buf, char **start, off_t offset, +			       int request, int *eof, void *data) +{ +	struct drm_minor *minor = (struct drm_minor *) data;  +	struct drm_device *dev = minor->dev; +	int len = 0; +	 +	if (offset > DRM_PROC_LIMIT) { +		*eof = 1; +		return 0; +	} + +	*start = &buf[offset]; +	*eof = 0; +	DRM_PROC_PRINT("%d objects\n", atomic_read (&dev->object_count)); +	DRM_PROC_PRINT("%d object bytes\n", atomic_read (&dev->object_memory)); +	DRM_PROC_PRINT("%d pinned\n", atomic_read (&dev->pin_count)); +	DRM_PROC_PRINT("%d pin bytes\n", atomic_read (&dev->pin_memory)); +	DRM_PROC_PRINT("%d gtt bytes\n", atomic_read (&dev->gtt_memory)); +	DRM_PROC_PRINT("%d gtt total\n", dev->gtt_total); +	if (len > request + offset) +		return request; +	*eof = 1; +	return len - offset; +} +  #if DRM_DEBUG_CODE  static int drm__vma_info(char *buf, char **start, off_t offset, int request, diff --git a/linux-core/drm_sman.c b/linux-core/drm_sman.c index 8421a939..7c16f685 100644 --- a/linux-core/drm_sman.c +++ b/linux-core/drm_sman.c @@ -88,34 +88,34 @@ EXPORT_SYMBOL(drm_sman_init);  static void *drm_sman_mm_allocate(void *private, unsigned long size,  				  unsigned alignment)  { -	struct drm_mm *mm = (struct drm_mm *) private; -	struct drm_mm_node *tmp; +	struct drm_memrange *mm = (struct drm_memrange *) private; +	struct drm_memrange_node *tmp; -	tmp = drm_mm_search_free(mm, size, alignment, 1); +	tmp = drm_memrange_search_free(mm, size, alignment, 1);  	if (!tmp) {  		return NULL;  	} -	tmp = drm_mm_get_block(tmp, size, alignment); +	tmp = drm_memrange_get_block(tmp, size, alignment);  	return tmp;  }  static void drm_sman_mm_free(void *private, void *ref)  { -	struct drm_mm_node *node = (struct drm_mm_node *) ref; +	struct drm_memrange_node *node = (struct drm_memrange_node *) ref; -	drm_mm_put_block(node); +	drm_memrange_put_block(node);  }  static void drm_sman_mm_destroy(void *private)  { -	struct drm_mm *mm = (struct drm_mm *) private; -	drm_mm_takedown(mm); +	struct drm_memrange *mm = (struct drm_memrange *) private; +	drm_memrange_takedown(mm);  	drm_free(mm, sizeof(*mm), DRM_MEM_MM);  }  static unsigned long drm_sman_mm_offset(void *private, void *ref)  { -	struct drm_mm_node *node = (struct drm_mm_node *) ref; +	struct drm_memrange_node *node = (struct drm_memrange_node *) ref;  	return node->start;  } @@ -124,7 +124,7 @@ drm_sman_set_range(struct drm_sman * sman, unsigned int manager,  		   unsigned long start, unsigned long size)  {  	struct drm_sman_mm *sman_mm; -	struct drm_mm *mm; +	struct drm_memrange *mm;  	int ret;  	BUG_ON(manager >= sman->num_managers); @@ -135,7 +135,7 @@ drm_sman_set_range(struct drm_sman * sman, unsigned int manager,  		return -ENOMEM;  	}  	sman_mm->private = mm; -	ret = drm_mm_init(mm, start, size); +	ret = drm_memrange_init(mm, start, size);  	if (ret) {  		drm_free(mm, sizeof(*mm), DRM_MEM_MM); diff --git a/linux-core/drm_sman.h b/linux-core/drm_sman.h index 39a39fef..0299776c 100644 --- a/linux-core/drm_sman.h +++ b/linux-core/drm_sman.h @@ -45,7 +45,7 @@  /*   * A class that is an abstration of a simple memory allocator.   * The sman implementation provides a default such allocator - * using the drm_mm.c implementation. But the user can replace it. + * using the drm_memrange.c implementation. But the user can replace it.   * See the SiS implementation, which may use the SiS FB kernel module   * for memory management.   */ @@ -116,7 +116,7 @@ extern int drm_sman_init(struct drm_sman * sman, unsigned int num_managers,  			 unsigned int user_order, unsigned int owner_order);  /* - * Initialize a drm_mm.c allocator. Should be called only once for each + * Initialize a drm_memrange.c allocator. Should be called only once for each   * manager unless a customized allogator is used.   */ diff --git a/linux-core/drm_stub.c b/linux-core/drm_stub.c index c68adbaf..c62b901d 100644 --- a/linux-core/drm_stub.c +++ b/linux-core/drm_stub.c @@ -58,6 +58,14 @@ static int drm_minor_get_id(struct drm_device *dev, int type)  	int ret;  	int base = 0, limit = 63; +	if (type == DRM_MINOR_CONTROL) { +		base += 64; +		limit = base + 127; +	} else if (type == DRM_MINOR_RENDER) { +		base += 128; +		limit = base + 255; +	}	 +  again:  	if (idr_pre_get(&drm_minors_idr, GFP_KERNEL) == 0) {  		DRM_ERROR("Out of memory expanding drawable idr\n"); @@ -80,21 +88,99 @@ again:  	return new_id;  } +int drm_setmaster_ioctl(struct drm_device *dev, void *data, +			struct drm_file *file_priv) +{ +	if (file_priv->minor->master && file_priv->minor->master != file_priv->master) +		return -EINVAL; + +	if (!file_priv->master) +		return -EINVAL; + +	if (!file_priv->minor->master && file_priv->minor->master != file_priv->master) +		file_priv->minor->master = file_priv->master; +	return 0; +} + +int drm_dropmaster_ioctl(struct drm_device *dev, void *data, +			 struct drm_file *file_priv) +{ +	if (!file_priv->master) +		return -EINVAL; +	file_priv->minor->master = NULL; +	return 0; +} + +struct drm_master *drm_get_master(struct drm_minor *minor) +{ +	struct drm_master *master; + +	master = drm_calloc(1, sizeof(*master), DRM_MEM_DRIVER); +	if (!master) +		return NULL; + +//	INIT_LIST_HEAD(&master->filelist); +	spin_lock_init(&master->lock.spinlock); +	init_waitqueue_head(&master->lock.lock_queue); +	drm_ht_create(&master->magiclist, DRM_MAGIC_HASH_ORDER); +	INIT_LIST_HEAD(&master->magicfree); +	master->minor = minor; + +	list_add_tail(&master->head, &minor->master_list); + +	return master; +} + +void drm_put_master(struct drm_master *master) +{ +	struct drm_magic_entry *pt, *next; +	struct drm_device *dev = master->minor->dev; + +	list_del(&master->head); + +	if (dev->driver->master_destroy) +		dev->driver->master_destroy(dev, master); + +	if (master->unique) { +		drm_free(master->unique, strlen(master->unique) + 1, DRM_MEM_DRIVER); +		master->unique = NULL; +		master->unique_len = 0; +	} + +	list_for_each_entry_safe(pt, next, &master->magicfree, head) { +		list_del(&pt->head); +		drm_ht_remove_item(&master->magiclist, &pt->hash_item); +		drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); +	} + +	drm_ht_remove(&master->magiclist); + +	if (master->lock.hw_lock) { +		if (dev->sigdata.lock == master->lock.hw_lock) +			dev->sigdata.lock = NULL; +		master->lock.hw_lock = NULL;	/* SHM removed */ +		master->lock.file_priv = NULL; +		wake_up_interruptible(&master->lock.lock_queue); +	} + +	drm_free(master, sizeof(*master), DRM_MEM_DRIVER); +} +  static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,  			   const struct pci_device_id *ent,  			   struct drm_driver *driver)  {  	int retcode; -	INIT_LIST_HEAD(&dev->filelist);  	INIT_LIST_HEAD(&dev->ctxlist);  	INIT_LIST_HEAD(&dev->vmalist);  	INIT_LIST_HEAD(&dev->maplist); +	INIT_LIST_HEAD(&dev->filelist);  	spin_lock_init(&dev->count_lock);  	spin_lock_init(&dev->drw_lock);  	spin_lock_init(&dev->tasklet_lock); -	spin_lock_init(&dev->lock.spinlock); +//	spin_lock_init(&dev->lock.spinlock);  	init_timer(&dev->timer);  	mutex_init(&dev->struct_mutex);  	mutex_init(&dev->ctxlist_mutex); @@ -112,18 +198,18 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,  	dev->irq = pdev->irq;  	dev->irq_enabled = 0; -	if (drm_ht_create(&dev->map_hash, DRM_MAP_HASH_ORDER)) { +	if (drm_ht_create(&dev->map_hash, DRM_MAP_HASH_ORDER))  		return -ENOMEM; -	} -	if (drm_mm_init(&dev->offset_manager, DRM_FILE_PAGE_OFFSET_START, -			DRM_FILE_PAGE_OFFSET_SIZE)) { + +	if (drm_memrange_init(&dev->offset_manager, DRM_FILE_PAGE_OFFSET_START, +			      DRM_FILE_PAGE_OFFSET_SIZE)) {  		drm_ht_remove(&dev->map_hash);  		return -ENOMEM;  	}  	if (drm_ht_create(&dev->object_hash, DRM_OBJECT_HASH_ORDER)) {  		drm_ht_remove(&dev->map_hash); -		drm_mm_takedown(&dev->offset_manager); +		drm_memrange_takedown(&dev->offset_manager);  		return -ENOMEM;  	} @@ -163,7 +249,16 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,  		goto error_out_unreg;  	} +	if (driver->driver_features & DRIVER_GEM) { +		retcode = drm_gem_init (dev); +		if (retcode) { +			DRM_ERROR("Cannot initialize graphics execution manager (GEM)\n"); +			goto error_out_unreg; +		} +	} +  	drm_fence_manager_init(dev); +  	return 0;  error_out_unreg: @@ -204,6 +299,7 @@ static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int t  	new_minor->device = MKDEV(DRM_MAJOR, minor_id);  	new_minor->dev = dev;  	new_minor->index = minor_id; +	INIT_LIST_HEAD(&new_minor->master_list);  	idr_replace(&drm_minors_idr, new_minor, minor_id); @@ -213,6 +309,13 @@ static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int t  			DRM_ERROR("DRM: Failed to initialize /proc/dri.\n");  			goto err_mem;  		} +		if (dev->driver->proc_init) { +			ret = dev->driver->proc_init(new_minor); +			if (ret) { +				DRM_ERROR("DRM: Driver failed to initialize /proc/dri.\n"); +				goto err_mem; +			} +		}  	} else  		new_minor->dev_root = NULL; @@ -229,8 +332,11 @@ static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int t  err_g2: -	if (new_minor->type == DRM_MINOR_LEGACY) +	if (new_minor->type == DRM_MINOR_LEGACY) { +		if (dev->driver->proc_cleanup) +			dev->driver->proc_cleanup(new_minor);  		drm_proc_cleanup(new_minor, drm_proc_root); +	}  err_mem:  	kfree(new_minor);  err_idr: @@ -280,20 +386,32 @@ int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent,  	}  	/* only add the control node on a modesetting platform */ +	if (drm_core_check_feature(dev, DRIVER_MODESET)) +		if ((ret = drm_get_minor(dev, &dev->control, DRM_MINOR_CONTROL))) +			goto err_g3; +  	if ((ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY))) -		goto err_g3; +		goto err_g4;  	if (dev->driver->load)  		if ((ret = dev->driver->load(dev, ent->driver_data))) -			goto err_g4; +			goto err_g5; + +	/* setup the grouping for the legacy output */ +	if (drm_core_check_feature(dev, DRIVER_MODESET)) +		if (drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group)) +		    goto err_g5;  	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",  		 driver->name, driver->major, driver->minor, driver->patchlevel,  		 driver->date, dev->primary->index);  	return 0; +err_g5: +	drm_put_minor(dev, &dev->primary);  err_g4: -	drm_put_minor(&dev->primary); +	if (drm_core_check_feature(dev, DRIVER_MODESET)) +		drm_put_minor(dev, &dev->control);  err_g3:  	if (!drm_fb_loaded)  		pci_disable_device(pdev); @@ -325,11 +443,6 @@ int drm_put_dev(struct drm_device * dev)  {  	DRM_DEBUG("release primary %s\n", dev->driver->pci_driver.name); -	if (dev->unique) { -		drm_free(dev->unique, strlen(dev->unique) + 1, DRM_MEM_DRIVER); -		dev->unique = NULL; -		dev->unique_len = 0; -	}  	if (dev->devname) {  		drm_free(dev->devname, strlen(dev->devname) + 1,  			 DRM_MEM_DRIVER); @@ -349,13 +462,16 @@ int drm_put_dev(struct drm_device * dev)   * last minor released.   *   */ -int drm_put_minor(struct drm_minor **minor_p) +int drm_put_minor(struct drm_device *dev, struct drm_minor **minor_p)  {  	struct drm_minor *minor = *minor_p;  	DRM_DEBUG("release secondary minor %d\n", minor->index); -	if (minor->type == DRM_MINOR_LEGACY) +	if (minor->type == DRM_MINOR_LEGACY) { +		if (dev->driver->proc_cleanup) +			dev->driver->proc_cleanup(minor);  		drm_proc_cleanup(minor, drm_proc_root); +	}  	drm_sysfs_device_remove(minor);  	idr_remove(&drm_minors_idr, minor->index); diff --git a/linux-core/drm_sysfs.c b/linux-core/drm_sysfs.c index 32759424..5c384a60 100644 --- a/linux-core/drm_sysfs.c +++ b/linux-core/drm_sysfs.c @@ -36,8 +36,9 @@ static int drm_sysfs_suspend(struct device *dev, pm_message_t state)  	printk(KERN_ERR "%s\n", __FUNCTION__); -	if (drm_dev->driver->suspend) -		return drm_dev->driver->suspend(drm_dev, state); +	if (drm_minor->type == DRM_MINOR_CONTROL) +		if (drm_dev->driver->suspend) +			return drm_dev->driver->suspend(drm_dev, state);  	return 0;  } @@ -54,8 +55,9 @@ static int drm_sysfs_resume(struct device *dev)  	struct drm_minor *drm_minor = to_drm_minor(dev);  	struct drm_device *drm_dev = drm_minor->dev; -	if (drm_dev->driver->resume) -		return drm_dev->driver->resume(drm_dev); +	if (drm_minor->type == DRM_MINOR_CONTROL) +		if (drm_dev->driver->resume) +			return drm_dev->driver->resume(drm_dev);  	return 0;  } @@ -131,10 +133,6 @@ static ssize_t show_dri(struct device *device, struct device_attribute *attr,  	return snprintf(buf, PAGE_SIZE, "%s\n", drm_dev->driver->pci_driver.name);  } -static struct device_attribute device_attrs[] = { -	__ATTR(dri_library_name, S_IRUGO, show_dri, NULL), -}; -  /**   * drm_sysfs_device_release - do nothing   * @dev: Linux device @@ -148,6 +146,315 @@ static void drm_sysfs_device_release(struct device *dev)  	return;  } +/* + * Connector properties + */ +static ssize_t status_show(struct device *device, +			   struct device_attribute *attr, +			   char *buf) +{ +	struct drm_connector *connector = container_of(device, struct drm_connector, kdev); +	return snprintf(buf, PAGE_SIZE, "%s", +			drm_get_connector_status_name(connector->funcs->detect(connector))); +} + +static ssize_t dpms_show(struct device *device, +			   struct device_attribute *attr, +			   char *buf) +{ +	struct drm_connector *connector = container_of(device, struct drm_connector, kdev); +	struct drm_device *dev = connector->dev; +	uint64_t dpms_status; +	int ret; + +	ret = drm_connector_property_get_value(connector, +					    dev->mode_config.dpms_property, +					    &dpms_status); +	if (ret) +		return 0; +	 +	return snprintf(buf, PAGE_SIZE, "%s", drm_get_dpms_name((int)dpms_status)); +} + +static ssize_t enabled_show(struct device *device, +			   struct device_attribute *attr, +			   char *buf) +{ +	struct drm_connector *connector = container_of(device, struct drm_connector, kdev); + +	if (connector->encoder) +		return snprintf(buf, PAGE_SIZE, "enabled"); +	else +		return snprintf(buf, PAGE_SIZE, "disabled"); +} + +static ssize_t edid_show(struct kobject *kobj, struct bin_attribute *attr, +			 char *buf, loff_t off, size_t count) +{ +	struct device *connector_dev = container_of(kobj, struct device, kobj); +	struct drm_connector *connector = container_of(connector_dev, struct drm_connector, +						 kdev); +	unsigned char *edid; +	size_t size; + +	if (!connector->edid_blob_ptr) +		return 0; + +	edid = connector->edid_blob_ptr->data; +	size = connector->edid_blob_ptr->length; +	if (!edid) +		return 0; +		 +	if (off >= size) +		return 0; + +	if (off + count > size) +		count = size - off; +	memcpy(buf, edid + off, count); + +	return count; +} + +static ssize_t modes_show(struct device *device, +			   struct device_attribute *attr, +			   char *buf) +{ +	struct drm_connector *connector = container_of(device, struct drm_connector, kdev); +	struct drm_display_mode *mode; +	int written = 0; + +	list_for_each_entry(mode, &connector->modes, head) { +		written += snprintf(buf + written, PAGE_SIZE - written, "%s\n", +				    mode->name); +	} + +	return written; +} + +static ssize_t subconnector_show(struct device *device, +			   struct device_attribute *attr, +			   char *buf) +{ +	struct drm_connector *connector = container_of(device, struct drm_connector, kdev); +	struct drm_device *dev = connector->dev; +	struct drm_property *prop = NULL; +	uint64_t subconnector; +	int ret; + +	switch (connector->connector_type) { +		case DRM_MODE_CONNECTOR_DVII: +			prop = dev->mode_config.dvi_i_subconnector_property; +			break; +		case DRM_MODE_CONNECTOR_Composite: +		case DRM_MODE_CONNECTOR_SVIDEO: +		case DRM_MODE_CONNECTOR_Component: +			prop = dev->mode_config.tv_subconnector_property; +			break; +		default: +			DRM_ERROR("Wrong connector type for this property\n"); +			return 0; +	} + +	if (!prop) { +		DRM_ERROR("Unable to find subconnector property\n"); +		return 0; +	} + +	ret = drm_connector_property_get_value(connector, prop, &subconnector); +	if (ret) +		return 0; + +	return snprintf(buf, PAGE_SIZE, "%s", drm_get_subconnector_name((int)subconnector)); +} + +static ssize_t select_subconnector_show(struct device *device, +			   struct device_attribute *attr, +			   char *buf) +{ +	struct drm_connector *connector = container_of(device, struct drm_connector, kdev); +	struct drm_device *dev = connector->dev; +	struct drm_property *prop = NULL; +	uint64_t subconnector; +	int ret; + +	switch (connector->connector_type) { +		case DRM_MODE_CONNECTOR_DVII: +			prop = dev->mode_config.dvi_i_select_subconnector_property; +			break; +		case DRM_MODE_CONNECTOR_Composite: +		case DRM_MODE_CONNECTOR_SVIDEO: +		case DRM_MODE_CONNECTOR_Component: +			prop = dev->mode_config.tv_select_subconnector_property; +			break; +		default: +			DRM_ERROR("Wrong connector type for this property\n"); +			return 0; +	} + +	if (!prop) { +		DRM_ERROR("Unable to find select subconnector property\n"); +		return 0; +	} + +	ret = drm_connector_property_get_value(connector, prop, &subconnector); +	if (ret) +		return 0; + +	return snprintf(buf, PAGE_SIZE, "%s", drm_get_select_subconnector_name((int)subconnector)); +} + +static struct device_attribute connector_attrs[] = { +	__ATTR_RO(status), +	__ATTR_RO(enabled), +	__ATTR_RO(dpms), +	__ATTR_RO(modes), +}; + +/* These attributes are for both DVI-I connectors and all types of tv-out. */ +static struct device_attribute connector_attrs_opt1[] = { +	__ATTR_RO(subconnector), +	__ATTR_RO(select_subconnector), +}; + +static struct bin_attribute edid_attr = { +	.attr.name = "edid", +	.size = 128, +	.read = edid_show, +}; + +/** + * drm_sysfs_connector_add - add an connector to sysfs + * @connector: connector to add + * + * Create an connector device in sysfs, along with its associated connector + * properties (so far, connection status, dpms, mode list & edid) and + * generate a hotplug event so userspace knows there's a new connector + * available. + * + * Note: + * This routine should only be called *once* for each DRM minor registered. + * A second call for an already registered device will trigger the BUG_ON + * below. + */ +int drm_sysfs_connector_add(struct drm_connector *connector) +{ +	struct drm_device *dev = connector->dev; +	int ret = 0, i, j; + +	/* We shouldn't get called more than once for the same connector */ +	BUG_ON(device_is_registered(&connector->kdev)); + +	connector->kdev.parent = &dev->primary->kdev; +	connector->kdev.class = drm_class; +	connector->kdev.release = drm_sysfs_device_release; + +	DRM_DEBUG("adding \"%s\" to sysfs\n", +		  drm_get_connector_name(connector)); + +	snprintf(connector->kdev.bus_id, BUS_ID_SIZE, "card%d-%s", +		 dev->primary->index, drm_get_connector_name(connector)); +	ret = device_register(&connector->kdev); + +	if (ret) { +		DRM_ERROR("failed to register connector device: %d\n", ret); +		goto out; +	} + +	/* Standard attributes */ + +	for (i = 0; i < ARRAY_SIZE(connector_attrs); i++) { +		ret = device_create_file(&connector->kdev, &connector_attrs[i]); +		if (ret) +			goto err_out_files; +	} + +	/* Optional attributes */ +	/* On the long run it maybe a good idea to make one set of optionals per connector type. */ + +	switch (connector->connector_type) { +		case DRM_MODE_CONNECTOR_DVII: +		case DRM_MODE_CONNECTOR_Composite: +		case DRM_MODE_CONNECTOR_SVIDEO: +		case DRM_MODE_CONNECTOR_Component: +			for (i = 0; i < ARRAY_SIZE(connector_attrs_opt1); i++) { +				ret = device_create_file(&connector->kdev, &connector_attrs_opt1[i]); +				if (ret) +					goto err_out_files; +			} +			break; +		default: +			break; +	} + +	ret = sysfs_create_bin_file(&connector->kdev.kobj, &edid_attr); +	if (ret) +		goto err_out_files; + +	/* Let userspace know we have a new connector */ +	drm_sysfs_hotplug_event(dev); + +	return 0; + +err_out_files: +	if (i > 0) +		for (j = 0; j < i; j++) +			device_remove_file(&connector->kdev, &connector_attrs[i]); +	device_unregister(&connector->kdev); + +out: +	return ret; +} +EXPORT_SYMBOL(drm_sysfs_connector_add); + +/** + * drm_sysfs_connector_remove - remove an connector device from sysfs + * @connector: connector to remove + * + * Remove @connector and its associated attributes from sysfs.  Note that + * the device model core will take care of sending the "remove" uevent + * at this time, so we don't need to do it. + * + * Note: + * This routine should only be called if the connector was previously + * successfully registered.  If @connector hasn't been registered yet, + * you'll likely see a panic somewhere deep in sysfs code when called. + */ +void drm_sysfs_connector_remove(struct drm_connector *connector) +{ +	int i; + +	DRM_DEBUG("removing \"%s\" from sysfs\n", +		  drm_get_connector_name(connector)); + +	for (i = 0; i < ARRAY_SIZE(connector_attrs); i++) +		device_remove_file(&connector->kdev, &connector_attrs[i]); +	sysfs_remove_bin_file(&connector->kdev.kobj, &edid_attr); +	device_unregister(&connector->kdev); +} +EXPORT_SYMBOL(drm_sysfs_connector_remove); + +/** + * drm_sysfs_hotplug_event - generate a DRM uevent + * @dev: DRM device + * + * Send a uevent for the DRM device specified by @dev.  Currently we only + * set HOTPLUG=1 in the uevent environment, but this could be expanded to + * deal with other types of events. + */ +void drm_sysfs_hotplug_event(struct drm_device *dev) +{ +	char *event_string = "HOTPLUG=1"; +	char *envp[] = { event_string, NULL }; + +	DRM_DEBUG("generating hotplug event\n"); + +	kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp); +} + +static struct device_attribute dri_attrs[] = { +	__ATTR(dri_library_name, S_IRUGO, show_dri, NULL), +}; +  /**   * drm_sysfs_device_add - adds a class device to sysfs for a character driver   * @dev: DRM device to be added @@ -156,6 +463,11 @@ static void drm_sysfs_device_release(struct device *dev)   * Add a DRM device to the DRM's device model class.  We use @dev's PCI device   * as the parent for the Linux device, and make sure it has a file containing   * the driver we're using (for userspace compatibility). + * + * Note: + * This routine should only be called *once* for each DRM minor registered. + * A second call for an already registered device will trigger the BUG_ON + * below.   */  int drm_sysfs_device_add(struct drm_minor *minor)  { @@ -167,18 +479,28 @@ int drm_sysfs_device_add(struct drm_minor *minor)  	minor->kdev.class = drm_class;  	minor->kdev.release = drm_sysfs_device_release;  	minor->kdev.devt = minor->device; -	minor_str = "card%d"; +	if (minor->type == DRM_MINOR_CONTROL) +		minor_str = "controlD%d"; +	else if (minor->type == DRM_MINOR_RENDER) +		minor_str = "renderD%d"; +	else +		minor_str = "card%d";  	snprintf(minor->kdev.bus_id, BUS_ID_SIZE, minor_str, minor->index); +	/* Shouldn't register more than once */ +	BUG_ON(device_is_registered(&minor->kdev)); + +	DRM_DEBUG("registering DRM device \"%s\"\n", minor->kdev.bus_id); +  	err = device_register(&minor->kdev);  	if (err) {  		DRM_ERROR("device add failed: %d\n", err);  		goto err_out;  	} -	for (i = 0; i < ARRAY_SIZE(device_attrs); i++) { -		err = device_create_file(&minor->kdev, &device_attrs[i]); +	for (i = 0; i < ARRAY_SIZE(dri_attrs); i++) { +		err = device_create_file(&minor->kdev, &dri_attrs[i]);  		if (err)  			goto err_out_files;  	} @@ -188,7 +510,7 @@ int drm_sysfs_device_add(struct drm_minor *minor)  err_out_files:  	if (i > 0)  		for (j = 0; j < i; j++) -			device_remove_file(&minor->kdev, &device_attrs[i]); +			device_remove_file(&minor->kdev, &dri_attrs[i]);  	device_unregister(&minor->kdev);  err_out: @@ -206,7 +528,7 @@ void drm_sysfs_device_remove(struct drm_minor *minor)  {  	int i; -	for (i = 0; i < ARRAY_SIZE(device_attrs); i++) -		device_remove_file(&minor->kdev, &device_attrs[i]); +	for (i = 0; i < ARRAY_SIZE(dri_attrs); i++) +		device_remove_file(&minor->kdev, &dri_attrs[i]);  	device_unregister(&minor->kdev);  } diff --git a/linux-core/drm_ttm.c b/linux-core/drm_ttm.c index 80a8ff5d..aa137dda 100644 --- a/linux-core/drm_ttm.c +++ b/linux-core/drm_ttm.c @@ -72,7 +72,7 @@ void drm_ttm_cache_flush(struct page *pages[], unsigned long num_pages)  		return;  	}  #endif -	if (on_each_cpu(drm_ttm_ipi_handler, NULL, 1, 1) != 0) +	if (drm_on_each_cpu(drm_ttm_ipi_handler, NULL, 1) != 0)  		DRM_ERROR("Timed out waiting for drm cache flush.\n");  }  EXPORT_SYMBOL(drm_ttm_cache_flush); diff --git a/linux-core/dvo.h b/linux-core/dvo.h new file mode 100644 index 00000000..b122ea1f --- /dev/null +++ b/linux-core/dvo.h @@ -0,0 +1,159 @@ +/* + * Copyright © 2006 Eric Anholt + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission.  The copyright holders make no representations + * about the suitability of this software for any purpose.  It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, 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. + */ + +#ifndef _INTEL_DVO_H  +#define _INTEL_DVO_H  + +#include <linux/i2c.h> +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "intel_drv.h" + +struct intel_dvo_device { +	char *name; +	int type; +	/* DVOA/B/C output register */ +	u32 dvo_reg; +	/* GPIO register used for i2c bus to control this device */ +	u32 gpio; +	int slave_addr; +	struct intel_i2c_chan *i2c_bus; + +	const struct intel_dvo_dev_ops *dev_ops; +	void *dev_priv; + +	struct drm_display_mode *panel_fixed_mode; +	bool panel_wants_dither; +}; + +struct intel_dvo_dev_ops { +	/* +	 * Initialize the device at startup time. +	 * Returns NULL if the device does not exist. +	 */ +	bool (*init)(struct intel_dvo_device *dvo, +		     struct intel_i2c_chan *i2cbus); + +	/* +	 * Called to allow the output a chance to create properties after the +	 * RandR objects have been created. +	 */ +	void (*create_resources)(struct intel_dvo_device *dvo); + +	/* +	 * Turn on/off output or set intermediate power levels if available. +	 * +	 * Unsupported intermediate modes drop to the lower power setting. +	 * If the  mode is DPMSModeOff, the output must be disabled, +	 * as the DPLL may be disabled afterwards. +	 */ +	void (*dpms)(struct intel_dvo_device *dvo, int mode); + +	/* +	 * Saves the output's state for restoration on VT switch. +	 */ +	void (*save)(struct intel_dvo_device *dvo); + +	/* +	 * Restore's the output's state at VT switch. +	 */ +	void (*restore)(struct intel_dvo_device *dvo); + +	/* +	 * Callback for testing a video mode for a given output. +	 * +	 * This function should only check for cases where a mode can't +	 * be supported on the output specifically, and not represent +	 * generic CRTC limitations. +	 * +	 * \return MODE_OK if the mode is valid, or another MODE_* otherwise. +	 */ +	int (*mode_valid)(struct intel_dvo_device *dvo, +			  struct drm_display_mode *mode); + +	/* +	 * Callback to adjust the mode to be set in the CRTC. +	 * +	 * This allows an output to adjust the clock or even the entire set of +	 * timings, which is used for panels with fixed timings or for +	 * buses with clock limitations. +	 */ +	bool (*mode_fixup)(struct intel_dvo_device *dvo, +			   struct drm_display_mode *mode, +			   struct drm_display_mode *adjusted_mode); + +	/* +	 * Callback for preparing mode changes on an output +	 */ +	void (*prepare)(struct intel_dvo_device *dvo); + +	/* +	 * Callback for committing mode changes on an output +	 */ +	void (*commit)(struct intel_dvo_device *dvo); + +	/* +	 * Callback for setting up a video mode after fixups have been made. +	 * +	 * This is only called while the output is disabled.  The dpms callback +	 * must be all that's necessary for the output, to turn the output on +	 * after this function is called. +	 */ +	void (*mode_set)(struct intel_dvo_device *dvo, +			 struct drm_display_mode *mode, +			 struct drm_display_mode *adjusted_mode); + +	/* +	 * Probe for a connected output, and return detect_status. +	 */ +	enum drm_connector_status (*detect)(struct intel_dvo_device *dvo); + +	/** +	 * Query the device for the modes it provides. +	 * +	 * This function may also update MonInfo, mm_width, and mm_height. +	 * +	 * \return singly-linked list of modes or NULL if no modes found. +	 */ +	struct drm_display_mode *(*get_modes)(struct intel_dvo_device *dvo); + +#ifdef RANDR_12_INTERFACE +	/** +	 * Callback when an output's property has changed. +	 */ +	bool (*set_property)(struct intel_dvo_device *dvo, +			     struct drm_property *property, uint64_t val); +#endif + +	/** +	 * Clean up driver-specific bits of the output +	 */ +	void (*destroy) (struct intel_dvo_device *dvo); + +	/** +	 * Debugging hook to dump device registers to log file +	 */ +	void (*dump_regs)(struct intel_dvo_device *dvo); +}; + +#endif /* _INTEL_DVO_H */ diff --git a/linux-core/dvo_ch7017.c b/linux-core/dvo_ch7017.c new file mode 100644 index 00000000..b10e0388 --- /dev/null +++ b/linux-core/dvo_ch7017.c @@ -0,0 +1,454 @@ +/* + * Copyright © 2006 Intel Corporation + * + * 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 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + *    Eric Anholt <eric@anholt.net> + * + */ + +#include "dvo.h" + +#define CH7017_TV_DISPLAY_MODE		0x00 +#define CH7017_FLICKER_FILTER		0x01 +#define CH7017_VIDEO_BANDWIDTH		0x02 +#define CH7017_TEXT_ENHANCEMENT		0x03 +#define CH7017_START_ACTIVE_VIDEO	0x04 +#define CH7017_HORIZONTAL_POSITION	0x05 +#define CH7017_VERTICAL_POSITION	0x06 +#define CH7017_BLACK_LEVEL		0x07 +#define CH7017_CONTRAST_ENHANCEMENT	0x08 +#define CH7017_TV_PLL			0x09 +#define CH7017_TV_PLL_M			0x0a +#define CH7017_TV_PLL_N			0x0b +#define CH7017_SUB_CARRIER_0		0x0c +#define CH7017_CIV_CONTROL		0x10 +#define CH7017_CIV_0			0x11 +#define CH7017_CHROMA_BOOST		0x14 +#define CH7017_CLOCK_MODE		0x1c +#define CH7017_INPUT_CLOCK		0x1d +#define CH7017_GPIO_CONTROL		0x1e +#define CH7017_INPUT_DATA_FORMAT	0x1f +#define CH7017_CONNECTION_DETECT	0x20 +#define CH7017_DAC_CONTROL		0x21 +#define CH7017_BUFFERED_CLOCK_OUTPUT	0x22 +#define CH7017_DEFEAT_VSYNC		0x47 +#define CH7017_TEST_PATTERN		0x48 + +#define CH7017_POWER_MANAGEMENT		0x49 +/** Enables the TV output path. */ +#define CH7017_TV_EN			(1 << 0) +#define CH7017_DAC0_POWER_DOWN		(1 << 1) +#define CH7017_DAC1_POWER_DOWN		(1 << 2) +#define CH7017_DAC2_POWER_DOWN		(1 << 3) +#define CH7017_DAC3_POWER_DOWN		(1 << 4) +/** Powers down the TV out block, and DAC0-3 */ +#define CH7017_TV_POWER_DOWN_EN		(1 << 5) + +#define CH7017_VERSION_ID		0x4a + +#define CH7017_DEVICE_ID		0x4b +#define CH7017_DEVICE_ID_VALUE		0x1b +#define CH7018_DEVICE_ID_VALUE		0x1a +#define CH7019_DEVICE_ID_VALUE		0x19 + +#define CH7017_XCLK_D2_ADJUST		0x53 +#define CH7017_UP_SCALER_COEFF_0	0x55 +#define CH7017_UP_SCALER_COEFF_1	0x56 +#define CH7017_UP_SCALER_COEFF_2	0x57 +#define CH7017_UP_SCALER_COEFF_3	0x58 +#define CH7017_UP_SCALER_COEFF_4	0x59 +#define CH7017_UP_SCALER_VERTICAL_INC_0	0x5a +#define CH7017_UP_SCALER_VERTICAL_INC_1	0x5b +#define CH7017_GPIO_INVERT		0x5c +#define CH7017_UP_SCALER_HORIZONTAL_INC_0	0x5d +#define CH7017_UP_SCALER_HORIZONTAL_INC_1	0x5e + +#define CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT	0x5f +/**< Low bits of horizontal active pixel input */ + +#define CH7017_ACTIVE_INPUT_LINE_OUTPUT	0x60 +/** High bits of horizontal active pixel input */ +#define CH7017_LVDS_HAP_INPUT_MASK	(0x7 << 0) +/** High bits of vertical active line output */ +#define CH7017_LVDS_VAL_HIGH_MASK	(0x7 << 3) + +#define CH7017_VERTICAL_ACTIVE_LINE_OUTPUT	0x61 +/**< Low bits of vertical active line output */ + +#define CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT	0x62 +/**< Low bits of horizontal active pixel output */ + +#define CH7017_LVDS_POWER_DOWN		0x63 +/** High bits of horizontal active pixel output */ +#define CH7017_LVDS_HAP_HIGH_MASK	(0x7 << 0) +/** Enables the LVDS power down state transition */ +#define CH7017_LVDS_POWER_DOWN_EN	(1 << 6) +/** Enables the LVDS upscaler */ +#define CH7017_LVDS_UPSCALER_EN		(1 << 7) +#define CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED 0x08 + +#define CH7017_LVDS_ENCODING		0x64 +#define CH7017_LVDS_DITHER_2D		(1 << 2) +#define CH7017_LVDS_DITHER_DIS		(1 << 3) +#define CH7017_LVDS_DUAL_CHANNEL_EN	(1 << 4) +#define CH7017_LVDS_24_BIT		(1 << 5) + +#define CH7017_LVDS_ENCODING_2		0x65 + +#define CH7017_LVDS_PLL_CONTROL		0x66 +/** Enables the LVDS panel output path */ +#define CH7017_LVDS_PANEN		(1 << 0) +/** Enables the LVDS panel backlight */ +#define CH7017_LVDS_BKLEN		(1 << 3) + +#define CH7017_POWER_SEQUENCING_T1	0x67 +#define CH7017_POWER_SEQUENCING_T2	0x68 +#define CH7017_POWER_SEQUENCING_T3	0x69 +#define CH7017_POWER_SEQUENCING_T4	0x6a +#define CH7017_POWER_SEQUENCING_T5	0x6b +#define CH7017_GPIO_DRIVER_TYPE		0x6c +#define CH7017_GPIO_DATA		0x6d +#define CH7017_GPIO_DIRECTION_CONTROL	0x6e + +#define CH7017_LVDS_PLL_FEEDBACK_DIV	0x71 +# define CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT 4 +# define CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT 0 +# define CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED 0x80 + +#define CH7017_LVDS_PLL_VCO_CONTROL	0x72 +# define CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED 0x80 +# define CH7017_LVDS_PLL_VCO_SHIFT	4 +# define CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT 0 + +#define CH7017_OUTPUTS_ENABLE		0x73 +# define CH7017_CHARGE_PUMP_LOW		0x0 +# define CH7017_CHARGE_PUMP_HIGH	0x3 +# define CH7017_LVDS_CHANNEL_A		(1 << 3) +# define CH7017_LVDS_CHANNEL_B		(1 << 4) +# define CH7017_TV_DAC_A		(1 << 5) +# define CH7017_TV_DAC_B		(1 << 6) +# define CH7017_DDC_SELECT_DC2		(1 << 7) + +#define CH7017_LVDS_OUTPUT_AMPLITUDE	0x74 +#define CH7017_LVDS_PLL_EMI_REDUCTION	0x75 +#define CH7017_LVDS_POWER_DOWN_FLICKER	0x76 + +#define CH7017_LVDS_CONTROL_2		0x78 +# define CH7017_LOOP_FILTER_SHIFT	5 +# define CH7017_PHASE_DETECTOR_SHIFT	0 + +#define CH7017_BANG_LIMIT_CONTROL	0x7f + +struct ch7017_priv { +	uint8_t save_hapi; +	uint8_t save_vali; +	uint8_t save_valo; +	uint8_t save_ailo; +	uint8_t save_lvds_pll_vco; +	uint8_t save_feedback_div; +	uint8_t save_lvds_control_2; +	uint8_t save_outputs_enable; +	uint8_t save_lvds_power_down; +	uint8_t save_power_management; +}; + +static void ch7017_dump_regs(struct intel_dvo_device *dvo); +static void ch7017_dpms(struct intel_dvo_device *dvo, int mode); + +static bool ch7017_read(struct intel_dvo_device *dvo, int addr, uint8_t *val) +{ +	struct intel_i2c_chan *i2cbus = dvo->i2c_bus; +	u8 out_buf[2]; +	u8 in_buf[2]; + +	struct i2c_msg msgs[] = { +		{ +			.addr = i2cbus->slave_addr, +			.flags = 0, +			.len = 1, +			.buf = out_buf, +		}, +		{ +			.addr = i2cbus->slave_addr, +			.flags = I2C_M_RD, +			.len = 1, +			.buf = in_buf, +		} +	}; + +	out_buf[0] = addr; +	out_buf[1] = 0; + +	if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) { +		*val= in_buf[0]; +		return true; +	}; + +	return false; +} + +static bool ch7017_write(struct intel_dvo_device *dvo, int addr, uint8_t val) +{ +	struct intel_i2c_chan *i2cbus = dvo->i2c_bus; +	uint8_t out_buf[2]; +	struct i2c_msg msg = { +		.addr = i2cbus->slave_addr, +		.flags = 0, +		.len = 2, +		.buf = out_buf, +	}; + +	out_buf[0] = addr; +	out_buf[1] = val; + +	if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1) +		return true; + +	return false; +} + +/** Probes for a CH7017 on the given bus and slave address. */ +static bool ch7017_init(struct intel_dvo_device *dvo, +			struct intel_i2c_chan *i2cbus) +{ +	struct ch7017_priv *priv; +	uint8_t val; + +	priv = kzalloc(sizeof(struct ch7017_priv), GFP_KERNEL); +	if (priv == NULL) +		return false; + +	dvo->i2c_bus = i2cbus; +	dvo->i2c_bus->slave_addr = dvo->slave_addr; +	dvo->dev_priv = priv; + +	if (!ch7017_read(dvo, CH7017_DEVICE_ID, &val)) +		goto fail; + +	if (val != CH7017_DEVICE_ID_VALUE && +	    val != CH7018_DEVICE_ID_VALUE && +	    val != CH7019_DEVICE_ID_VALUE) { +		DRM_DEBUG("ch701x not detected, got %d: from %s Slave %d.\n", +			  val, i2cbus->adapter.name,i2cbus->slave_addr);  +		goto fail; +	} + +	return true; +fail: +	kfree(priv); +	return false; +} + +static enum drm_connector_status ch7017_detect(struct intel_dvo_device *dvo) +{ +	return connector_status_unknown; +} + +static enum drm_mode_status ch7017_mode_valid(struct intel_dvo_device *dvo, +					      struct drm_display_mode *mode) +{ +	if (mode->clock > 160000) +		return MODE_CLOCK_HIGH; + +	return MODE_OK; +} + +static void ch7017_mode_set(struct intel_dvo_device *dvo, +			    struct drm_display_mode *mode, +			    struct drm_display_mode *adjusted_mode) +{ +	uint8_t lvds_pll_feedback_div, lvds_pll_vco_control; +	uint8_t outputs_enable, lvds_control_2, lvds_power_down; +	uint8_t horizontal_active_pixel_input; +	uint8_t horizontal_active_pixel_output, vertical_active_line_output; +	uint8_t active_input_line_output; + +	DRM_DEBUG("Registers before mode setting\n"); +	ch7017_dump_regs(dvo); + +	/* LVDS PLL settings from page 75 of 7017-7017ds.pdf*/ +	if (mode->clock < 100000) { +		outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_LOW; +		lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED | +			(2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) | +			(13 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT); +		lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED | +			(2 << CH7017_LVDS_PLL_VCO_SHIFT) | +			(3 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT); +		lvds_control_2 = (1 << CH7017_LOOP_FILTER_SHIFT) | +			(0 << CH7017_PHASE_DETECTOR_SHIFT); +	} else { +		outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_HIGH; +		lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED | +			(2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) | +			(3 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT); +		lvds_pll_feedback_div = 35; +		lvds_control_2 = (3 << CH7017_LOOP_FILTER_SHIFT) | +			(0 << CH7017_PHASE_DETECTOR_SHIFT); +		if (1) { /* XXX: dual channel panel detection.  Assume yes for now. */ +			outputs_enable |= CH7017_LVDS_CHANNEL_B; +			lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED | +				(2 << CH7017_LVDS_PLL_VCO_SHIFT) | +				(13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT); +		} else { +			lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED | +				(1 << CH7017_LVDS_PLL_VCO_SHIFT) | +				(13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT); +		} +	} + +	horizontal_active_pixel_input = mode->hdisplay & 0x00ff; + +	vertical_active_line_output = mode->vdisplay & 0x00ff; +	horizontal_active_pixel_output = mode->hdisplay & 0x00ff; + +	active_input_line_output = ((mode->hdisplay & 0x0700) >> 8) | +				   (((mode->vdisplay & 0x0700) >> 8) << 3); + +	lvds_power_down = CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED | +			  (mode->hdisplay & 0x0700) >> 8; + +	ch7017_dpms(dvo, DRM_MODE_DPMS_OFF); +	ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, +			horizontal_active_pixel_input); +	ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT, +			horizontal_active_pixel_output); +	ch7017_write(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, +			vertical_active_line_output); +	ch7017_write(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT, +			active_input_line_output); +	ch7017_write(dvo, CH7017_LVDS_PLL_VCO_CONTROL, lvds_pll_vco_control); +	ch7017_write(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, lvds_pll_feedback_div); +	ch7017_write(dvo, CH7017_LVDS_CONTROL_2, lvds_control_2); +	ch7017_write(dvo, CH7017_OUTPUTS_ENABLE, outputs_enable); + +	/* Turn the LVDS back on with new settings. */ +	ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, lvds_power_down); + +	DRM_DEBUG("Registers after mode setting\n"); +	ch7017_dump_regs(dvo); +} + +/* set the CH7017 power state */ +static void ch7017_dpms(struct intel_dvo_device *dvo, int mode) +{ +	uint8_t val; + +	ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &val); + +	/* Turn off TV/VGA, and never turn it on since we don't support it. */ +	ch7017_write(dvo, CH7017_POWER_MANAGEMENT, +			CH7017_DAC0_POWER_DOWN | +			CH7017_DAC1_POWER_DOWN | +			CH7017_DAC2_POWER_DOWN | +			CH7017_DAC3_POWER_DOWN | +			CH7017_TV_POWER_DOWN_EN); + +	if (mode == DRM_MODE_DPMS_ON) { +		/* Turn on the LVDS */ +		ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, +			     val & ~CH7017_LVDS_POWER_DOWN_EN); +	} else { +		/* Turn off the LVDS */ +		ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, +			     val | CH7017_LVDS_POWER_DOWN_EN); +	} + +	/* XXX: Should actually wait for update power status somehow */ +	udelay(20000); +} + +static void ch7017_dump_regs(struct intel_dvo_device *dvo) +{ +	uint8_t val; + +#define DUMP(reg)					\ +do {							\ +	ch7017_read(dvo, reg, &val);			\ +	DRM_DEBUG(#reg ": %02x\n", val);		\ +} while (0) + +	DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT); +	DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT); +	DUMP(CH7017_VERTICAL_ACTIVE_LINE_OUTPUT); +	DUMP(CH7017_ACTIVE_INPUT_LINE_OUTPUT); +	DUMP(CH7017_LVDS_PLL_VCO_CONTROL); +	DUMP(CH7017_LVDS_PLL_FEEDBACK_DIV); +	DUMP(CH7017_LVDS_CONTROL_2); +	DUMP(CH7017_OUTPUTS_ENABLE); +	DUMP(CH7017_LVDS_POWER_DOWN); +} + +static void ch7017_save(struct intel_dvo_device *dvo) +{ +	struct ch7017_priv *priv = dvo->dev_priv; + +	ch7017_read(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, &priv->save_hapi); +	ch7017_read(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, &priv->save_valo); +	ch7017_read(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT, &priv->save_ailo); +	ch7017_read(dvo, CH7017_LVDS_PLL_VCO_CONTROL, &priv->save_lvds_pll_vco); +	ch7017_read(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, &priv->save_feedback_div); +	ch7017_read(dvo, CH7017_LVDS_CONTROL_2, &priv->save_lvds_control_2); +	ch7017_read(dvo, CH7017_OUTPUTS_ENABLE, &priv->save_outputs_enable); +	ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &priv->save_lvds_power_down); +	ch7017_read(dvo, CH7017_POWER_MANAGEMENT, &priv->save_power_management); +} + +static void ch7017_restore(struct intel_dvo_device *dvo) +{ +	struct ch7017_priv *priv = dvo->dev_priv; + +	/* Power down before changing mode */ +	ch7017_dpms(dvo, DRM_MODE_DPMS_OFF); + +	ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, priv->save_hapi); +	ch7017_write(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, priv->save_valo); +	ch7017_write(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT, priv->save_ailo); +	ch7017_write(dvo, CH7017_LVDS_PLL_VCO_CONTROL, priv->save_lvds_pll_vco); +	ch7017_write(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, priv->save_feedback_div); +	ch7017_write(dvo, CH7017_LVDS_CONTROL_2, priv->save_lvds_control_2); +	ch7017_write(dvo, CH7017_OUTPUTS_ENABLE, priv->save_outputs_enable); +	ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, priv->save_lvds_power_down); +	ch7017_write(dvo, CH7017_POWER_MANAGEMENT, priv->save_power_management); +} + +static void ch7017_destroy(struct intel_dvo_device *dvo) +{ +	struct ch7017_priv *priv = dvo->dev_priv; + +	if (priv) { +		kfree(priv); +		dvo->dev_priv = NULL; +	} +} + +struct intel_dvo_dev_ops ch7017_ops = { +	.init = ch7017_init, +	.detect = ch7017_detect, +	.mode_valid = ch7017_mode_valid, +	.mode_set = ch7017_mode_set, +	.dpms = ch7017_dpms, +	.dump_regs = ch7017_dump_regs, +	.save = ch7017_save, +	.restore = ch7017_restore, +	.destroy = ch7017_destroy, +}; diff --git a/linux-core/dvo_ch7xxx.c b/linux-core/dvo_ch7xxx.c new file mode 100644 index 00000000..77c86395 --- /dev/null +++ b/linux-core/dvo_ch7xxx.c @@ -0,0 +1,368 @@ +/************************************************************************** + +Copyright © 2006 Dave Airlie + +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 THE AUTHOR 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 "dvo.h" + +#define CH7xxx_REG_VID		0x4a +#define CH7xxx_REG_DID		0x4b + +#define CH7011_VID		0x83 /* 7010 as well */ +#define CH7009A_VID		0x84 +#define CH7009B_VID		0x85 +#define CH7301_VID		0x95 + +#define CH7xxx_VID		0x84 +#define CH7xxx_DID		0x17 + +#define CH7xxx_NUM_REGS		0x4c + +#define CH7xxx_CM		0x1c +#define CH7xxx_CM_XCM		(1<<0) +#define CH7xxx_CM_MCP		(1<<2) +#define CH7xxx_INPUT_CLOCK	0x1d +#define CH7xxx_GPIO		0x1e +#define CH7xxx_GPIO_HPIR	(1<<3) +#define CH7xxx_IDF		0x1f + +#define CH7xxx_IDF_HSP		(1<<3) +#define CH7xxx_IDF_VSP		(1<<4) + +#define CH7xxx_CONNECTION_DETECT 0x20 +#define CH7xxx_CDET_DVI		(1<<5) + +#define CH7301_DAC_CNTL		0x21 +#define CH7301_HOTPLUG		0x23 +#define CH7xxx_TCTL		0x31 +#define CH7xxx_TVCO		0x32 +#define CH7xxx_TPCP		0x33 +#define CH7xxx_TPD		0x34 +#define CH7xxx_TPVT		0x35 +#define CH7xxx_TLPF		0x36 +#define CH7xxx_TCT		0x37 +#define CH7301_TEST_PATTERN	0x48 + +#define CH7xxx_PM		0x49 +#define CH7xxx_PM_FPD		(1<<0) +#define CH7301_PM_DACPD0	(1<<1) +#define CH7301_PM_DACPD1	(1<<2) +#define CH7301_PM_DACPD2	(1<<3) +#define CH7xxx_PM_DVIL		(1<<6) +#define CH7xxx_PM_DVIP		(1<<7) + +#define CH7301_SYNC_POLARITY	0x56 +#define CH7301_SYNC_RGB_YUV	(1<<0) +#define CH7301_SYNC_POL_DVI	(1<<5) + +/** @file + * driver for the Chrontel 7xxx DVI chip over DVO. + */ + +static struct ch7xxx_id_struct { +	uint8_t vid; +	char *name; +} ch7xxx_ids[] = {  +	{ CH7011_VID, "CH7011" }, +	{ CH7009A_VID, "CH7009A" }, +	{ CH7009B_VID, "CH7009B" }, +	{ CH7301_VID, "CH7301" }, +}; + +struct ch7xxx_reg_state { +    uint8_t regs[CH7xxx_NUM_REGS]; +}; + +struct ch7xxx_priv { +	bool quiet; + +	struct ch7xxx_reg_state save_reg; +	struct ch7xxx_reg_state mode_reg; +	uint8_t save_TCTL, save_TPCP, save_TPD, save_TPVT; +	uint8_t save_TLPF, save_TCT, save_PM, save_IDF; +}; + +static void ch7xxx_save(struct intel_dvo_device *dvo); + +static char *ch7xxx_get_id(uint8_t vid) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(ch7xxx_ids); i++) { +		if (ch7xxx_ids[i].vid == vid) +			return ch7xxx_ids[i].name; +	} + +	return NULL; +} + +/** Reads an 8 bit register */ +static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch) +{ +	struct ch7xxx_priv *ch7xxx= dvo->dev_priv; +	struct intel_i2c_chan *i2cbus = dvo->i2c_bus; +	u8 out_buf[2]; +	u8 in_buf[2]; + +	struct i2c_msg msgs[] = { +		{ +			.addr = i2cbus->slave_addr, +			.flags = 0, +			.len = 1, +			.buf = out_buf, +		}, +		{ +			.addr = i2cbus->slave_addr, +			.flags = I2C_M_RD, +			.len = 1, +			.buf = in_buf, +		} +	}; + +	out_buf[0] = addr; +	out_buf[1] = 0; + +	if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) { +		*ch = in_buf[0]; +		return true; +	}; + +	if (!ch7xxx->quiet) { +		DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n", +			  addr, i2cbus->adapter.name, i2cbus->slave_addr); +	} +	return false; +} + +/** Writes an 8 bit register */ +static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch) +{ +	struct ch7xxx_priv *ch7xxx = dvo->dev_priv; +	struct intel_i2c_chan *i2cbus = dvo->i2c_bus; +	uint8_t out_buf[2]; +	struct i2c_msg msg = { +		.addr = i2cbus->slave_addr, +		.flags = 0, +		.len = 2, +		.buf = out_buf, +	}; + +	out_buf[0] = addr; +	out_buf[1] = ch; + +	if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1) +		return true; + +	if (!ch7xxx->quiet) { +		DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n", +			  addr, i2cbus->adapter.name, i2cbus->slave_addr); +	} + +	return false; +} + +static bool ch7xxx_init(struct intel_dvo_device *dvo, +			struct intel_i2c_chan *i2cbus) +{ +	/* this will detect the CH7xxx chip on the specified i2c bus */ +	struct ch7xxx_priv *ch7xxx; +	uint8_t vendor, device; +	char *name; + +	ch7xxx = kzalloc(sizeof(struct ch7xxx_priv), GFP_KERNEL); +	if (ch7xxx == NULL) +		return false; + +	dvo->i2c_bus = i2cbus; +	dvo->i2c_bus->slave_addr = dvo->slave_addr; +	dvo->dev_priv = ch7xxx; +	ch7xxx->quiet = true; + +	if (!ch7xxx_readb(dvo, CH7xxx_REG_VID, &vendor)) +		goto out; + +	name = ch7xxx_get_id(vendor); +	if (!name) { +		DRM_DEBUG("ch7xxx not detected; got 0x%02x from %s slave %d.\n", +			  vendor, i2cbus->adapter.name, i2cbus->slave_addr); +		goto out; +	} + + +	if (!ch7xxx_readb(dvo, CH7xxx_REG_DID, &device)) +		goto out; + +	if (device != CH7xxx_DID) { +		DRM_DEBUG("ch7xxx not detected; got 0x%02x from %s slave %d.\n", +			  vendor, i2cbus->adapter.name, i2cbus->slave_addr); +		goto out; +	} + +	ch7xxx->quiet = false; +	DRM_DEBUG("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n", +		  name, vendor, device); +	return true; +out: +	kfree(ch7xxx); +	return false; +} + +static enum drm_connector_status ch7xxx_detect(struct intel_dvo_device *dvo) +{ +	uint8_t cdet, orig_pm, pm; + +	ch7xxx_readb(dvo, CH7xxx_PM, &orig_pm); + +	pm = orig_pm; +	pm &= ~CH7xxx_PM_FPD; +	pm |= CH7xxx_PM_DVIL | CH7xxx_PM_DVIP; + +	ch7xxx_writeb(dvo, CH7xxx_PM, pm); + +	ch7xxx_readb(dvo, CH7xxx_CONNECTION_DETECT, &cdet); + +	ch7xxx_writeb(dvo, CH7xxx_PM, orig_pm); + +	if (cdet & CH7xxx_CDET_DVI)  +		return connector_status_connected; +	return connector_status_disconnected; +} + +static enum drm_mode_status ch7xxx_mode_valid(struct intel_dvo_device *dvo, +					      struct drm_display_mode *mode) +{ +	if (mode->clock > 165000) +		return MODE_CLOCK_HIGH; + +	return MODE_OK; +} + +static void ch7xxx_mode_set(struct intel_dvo_device *dvo, +			    struct drm_display_mode *mode, +			    struct drm_display_mode *adjusted_mode) +{ +	uint8_t tvco, tpcp, tpd, tlpf, idf; + +	if (mode->clock <= 65000) { +		tvco = 0x23; +		tpcp = 0x08; +		tpd = 0x16; +		tlpf = 0x60; +	} else { +		tvco = 0x2d; +		tpcp = 0x06; +		tpd = 0x26; +		tlpf = 0xa0; +	} + +	ch7xxx_writeb(dvo, CH7xxx_TCTL, 0x00); +	ch7xxx_writeb(dvo, CH7xxx_TVCO, tvco); +	ch7xxx_writeb(dvo, CH7xxx_TPCP, tpcp); +	ch7xxx_writeb(dvo, CH7xxx_TPD, tpd); +	ch7xxx_writeb(dvo, CH7xxx_TPVT, 0x30); +	ch7xxx_writeb(dvo, CH7xxx_TLPF, tlpf); +	ch7xxx_writeb(dvo, CH7xxx_TCT, 0x00); + +	ch7xxx_readb(dvo, CH7xxx_IDF, &idf); + +	idf &= ~(CH7xxx_IDF_HSP | CH7xxx_IDF_VSP); +	if (mode->flags & DRM_MODE_FLAG_PHSYNC) +		idf |= CH7xxx_IDF_HSP; + +	if (mode->flags & DRM_MODE_FLAG_PVSYNC) +		idf |= CH7xxx_IDF_HSP; + +	ch7xxx_writeb(dvo, CH7xxx_IDF, idf); +} + +/* set the CH7xxx power state */ +static void ch7xxx_dpms(struct intel_dvo_device *dvo, int mode) +{ +	if (mode == DRM_MODE_DPMS_ON) +		ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP); +	else +		ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD); +} + +static void ch7xxx_dump_regs(struct intel_dvo_device *dvo) +{ +	struct ch7xxx_priv *ch7xxx = dvo->dev_priv; +	int i; + +	for (i = 0; i < CH7xxx_NUM_REGS; i++) { +		if ((i % 8) == 0 ) +			DRM_DEBUG("\n %02X: ", i); +		DRM_DEBUG("%02X ", ch7xxx->mode_reg.regs[i]); +	} +} + +static void ch7xxx_save(struct intel_dvo_device *dvo) +{ +	struct ch7xxx_priv *ch7xxx= dvo->dev_priv; + +	ch7xxx_readb(dvo, CH7xxx_TCTL, &ch7xxx->save_TCTL); +	ch7xxx_readb(dvo, CH7xxx_TPCP, &ch7xxx->save_TPCP); +	ch7xxx_readb(dvo, CH7xxx_TPD, &ch7xxx->save_TPD); +	ch7xxx_readb(dvo, CH7xxx_TPVT, &ch7xxx->save_TPVT); +	ch7xxx_readb(dvo, CH7xxx_TLPF, &ch7xxx->save_TLPF); +	ch7xxx_readb(dvo, CH7xxx_PM, &ch7xxx->save_PM); +	ch7xxx_readb(dvo, CH7xxx_IDF, &ch7xxx->save_IDF); +} + +static void ch7xxx_restore(struct intel_dvo_device *dvo) +{ +	struct ch7xxx_priv *ch7xxx = dvo->dev_priv; + +	ch7xxx_writeb(dvo, CH7xxx_TCTL, ch7xxx->save_TCTL); +	ch7xxx_writeb(dvo, CH7xxx_TPCP, ch7xxx->save_TPCP); +	ch7xxx_writeb(dvo, CH7xxx_TPD, ch7xxx->save_TPD); +	ch7xxx_writeb(dvo, CH7xxx_TPVT, ch7xxx->save_TPVT); +	ch7xxx_writeb(dvo, CH7xxx_TLPF, ch7xxx->save_TLPF); +	ch7xxx_writeb(dvo, CH7xxx_IDF, ch7xxx->save_IDF); +	ch7xxx_writeb(dvo, CH7xxx_PM, ch7xxx->save_PM); +} + +static void ch7xxx_destroy(struct intel_dvo_device *dvo) +{ +	struct ch7xxx_priv *ch7xxx = dvo->dev_priv; + +	if (ch7xxx) { +		kfree(ch7xxx); +		dvo->dev_priv = NULL; +	} +} + +struct intel_dvo_dev_ops ch7xxx_ops = { +	.init = ch7xxx_init, +	.detect = ch7xxx_detect, +	.mode_valid = ch7xxx_mode_valid, +	.mode_set = ch7xxx_mode_set, +	.dpms = ch7xxx_dpms, +	.dump_regs = ch7xxx_dump_regs, +	.save = ch7xxx_save, +	.restore = ch7xxx_restore, +	.destroy = ch7xxx_destroy, +}; diff --git a/linux-core/dvo_ivch.c b/linux-core/dvo_ivch.c new file mode 100644 index 00000000..788b0721 --- /dev/null +++ b/linux-core/dvo_ivch.c @@ -0,0 +1,463 @@ +/* + * Copyright © 2006 Intel Corporation + * + * 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 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + *    Eric Anholt <eric@anholt.net> + * + */ + +#include "dvo.h" + +/*  + * register definitions for the i82807aa. + * + * Documentation on this chipset can be found in datasheet #29069001 at + * intel.com. + */ + +/*  + * VCH Revision & GMBus Base Addr + */ +#define VR00		0x00 +# define VR00_BASE_ADDRESS_MASK		0x007f + +/*  + * Functionality Enable + */ +#define VR01		0x01 + +/* + * Enable the panel fitter + */ +# define VR01_PANEL_FIT_ENABLE		(1 << 3) +/* + * Enables the LCD display. + * + * This must not be set while VR01_DVO_BYPASS_ENABLE is set. + */ +# define VR01_LCD_ENABLE		(1 << 2) +/** Enables the DVO repeater. */ +# define VR01_DVO_BYPASS_ENABLE		(1 << 1) +/** Enables the DVO clock */ +# define VR01_DVO_ENABLE		(1 << 0) + +/*  + * LCD Interface Format + */ +#define VR10		0x10 +/** Enables LVDS output instead of CMOS */ +# define VR10_LVDS_ENABLE		(1 << 4) +/** Enables 18-bit LVDS output. */ +# define VR10_INTERFACE_1X18		(0 << 2) +/** Enables 24-bit LVDS or CMOS output */ +# define VR10_INTERFACE_1X24		(1 << 2) +/** Enables 2x18-bit LVDS or CMOS output. */ +# define VR10_INTERFACE_2X18		(2 << 2) +/** Enables 2x24-bit LVDS output */ +# define VR10_INTERFACE_2X24		(3 << 2) + +/* + * VR20 LCD Horizontal Display Size + */ +#define VR20	0x20 + +/*  + * LCD Vertical Display Size + */ +#define VR21	0x20 + +/*  + * Panel power down status + */ +#define VR30		0x30 +/** Read only bit indicating that the panel is not in a safe poweroff state. */ +# define VR30_PANEL_ON			(1 << 15) + +#define VR40		0x40 +# define VR40_STALL_ENABLE		(1 << 13) +# define VR40_VERTICAL_INTERP_ENABLE	(1 << 12) +# define VR40_ENHANCED_PANEL_FITTING	(1 << 11) +# define VR40_HORIZONTAL_INTERP_ENABLE	(1 << 10) +# define VR40_AUTO_RATIO_ENABLE		(1 << 9) +# define VR40_CLOCK_GATING_ENABLE	(1 << 8) + +/* + * Panel Fitting Vertical Ratio + * (((image_height - 1) << 16) / ((panel_height - 1))) >> 2 + */ +#define VR41		0x41 + +/*  + * Panel Fitting Horizontal Ratio + * (((image_width - 1) << 16) / ((panel_width - 1))) >> 2 + */ +#define VR42		0x42 + +/* + * Horizontal Image Size + */ +#define VR43		0x43 + +/* VR80 GPIO 0 + */ +#define VR80	    0x80 +#define VR81	    0x81 +#define VR82	    0x82 +#define VR83	    0x83 +#define VR84	    0x84 +#define VR85	    0x85 +#define VR86	    0x86 +#define VR87	    0x87 +     +/* VR88 GPIO 8 + */ +#define VR88	    0x88 + +/* Graphics BIOS scratch 0 + */ +#define VR8E	    0x8E +# define VR8E_PANEL_TYPE_MASK		(0xf << 0) +# define VR8E_PANEL_INTERFACE_CMOS	(0 << 4) +# define VR8E_PANEL_INTERFACE_LVDS	(1 << 4) +# define VR8E_FORCE_DEFAULT_PANEL	(1 << 5) + +/* Graphics BIOS scratch 1 + */ +#define VR8F	    0x8F +# define VR8F_VCH_PRESENT		(1 << 0) +# define VR8F_DISPLAY_CONN		(1 << 1) +# define VR8F_POWER_MASK		(0x3c) +# define VR8F_POWER_POS			(2) + + +struct ivch_priv { +	bool quiet; + +	uint16_t width, height; + +	uint16_t save_VR01; +	uint16_t save_VR40; +}; + +#if 0 +struct vch_capabilities { +    struct aimdb_block	aimdb_block; +    uint8_t		panel_type; +    uint8_t		set_panel_type; +    uint8_t		slave_address; +    uint8_t		capabilities; +#define VCH_PANEL_FITTING_SUPPORT	(0x3 << 0) +#define VCH_PANEL_FITTING_TEXT		(1 << 2) +#define VCH_PANEL_FITTING_GRAPHICS	(1 << 3) +#define VCH_PANEL_FITTING_RATIO		(1 << 4) +#define VCH_DITHERING			(1 << 5) +    uint8_t		backlight_gpio; +    uint8_t		set_panel_type_us_gpios; +} __attribute__ ((packed)); +#endif + +static void ivch_dump_regs(struct intel_dvo_device *dvo); + +/** + * Reads a register on the ivch. + * + * Each of the 256 registers are 16 bits long. + */ +static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data) +{ +	struct ivch_priv *priv = dvo->dev_priv; +	struct intel_i2c_chan *i2cbus = dvo->i2c_bus; +	u8 out_buf[1]; +	u8 in_buf[2]; + +	struct i2c_msg msgs[] = { +		{ +			.addr = i2cbus->slave_addr, +			.flags = I2C_M_RD, +			.len = 0, +		}, +		{ +			.addr = 0, +			.flags = I2C_M_NOSTART, +			.len = 1, +			.buf = out_buf, +		}, +		{ +			.addr = i2cbus->slave_addr, +			.flags = I2C_M_RD | I2C_M_NOSTART, +			.len = 2, +			.buf = in_buf, +		} +	}; + +	out_buf[0] = addr; + +	if (i2c_transfer(&i2cbus->adapter, msgs, 3) == 3) { +		*data = (in_buf[1] << 8) | in_buf[0]; +		return true; +	}; + +	if (!priv->quiet) { +		DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n", +			  addr, i2cbus->adapter.name, i2cbus->slave_addr); +	} +	return false; +} +  +/** Writes a 16-bit register on the ivch */ +static bool ivch_write(struct intel_dvo_device *dvo, int addr, uint16_t data) +{ +	struct ivch_priv *priv = dvo->dev_priv; +	struct intel_i2c_chan *i2cbus = dvo->i2c_bus; +	u8 out_buf[3]; +	struct i2c_msg msg = { +		.addr = i2cbus->slave_addr, +		.flags = 0, +		.len = 3, +		.buf = out_buf, +	}; + +	out_buf[0] = addr; +	out_buf[1] = data & 0xff; +	out_buf[2] = data >> 8; + +	if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1) +		return true; + +	if (!priv->quiet) { +		DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n", +			  addr, i2cbus->adapter.name, i2cbus->slave_addr); +	} + +	return false; +} + +/** Probes the given bus and slave address for an ivch */ +static bool ivch_init(struct intel_dvo_device *dvo, +		      struct intel_i2c_chan *i2cbus) +{ +	struct ivch_priv *priv; +	uint16_t temp; + +	priv = kzalloc(sizeof(struct ivch_priv), GFP_KERNEL); +	if (priv == NULL) +		return false; + +	dvo->i2c_bus = i2cbus; +	dvo->i2c_bus->slave_addr = dvo->slave_addr; +	dvo->dev_priv = priv; +	priv->quiet = true; + +	if (!ivch_read(dvo, VR00, &temp)) +		goto out; +	priv->quiet = false; + +	/* Since the identification bits are probably zeroes, which doesn't seem +	 * very unique, check that the value in the base address field matches +	 * the address it's responding on. +	 */ +	if ((temp & VR00_BASE_ADDRESS_MASK) != dvo->slave_addr) { +		DRM_DEBUG("ivch detect failed due to address mismatch " +			  "(%d vs %d)\n", +			  (temp & VR00_BASE_ADDRESS_MASK), dvo->slave_addr); +		goto out; +	} + +#if 0 +	if (!xf86I2CDevInit(&priv->d)) { +		goto out; +	} +#endif +	ivch_read(dvo, VR20, &priv->width); +	ivch_read(dvo, VR21, &priv->height); + +	return true; + +out: +	kfree(priv); +	return false; +} + +static enum drm_connector_status ivch_detect(struct intel_dvo_device *dvo) +{ +	return connector_status_connected; +} + +static enum drm_mode_status ivch_mode_valid(struct intel_dvo_device *dvo, +					    struct drm_display_mode *mode) +{ +	if (mode->clock > 112000) +		return MODE_CLOCK_HIGH; + +	return MODE_OK; +} + +/** Sets the power state of the panel connected to the ivch */ +static void ivch_dpms(struct intel_dvo_device *dvo, int mode) +{ +	int i; +	uint16_t vr01, vr30, backlight; + +	/* Set the new power state of the panel. */ +	if (!ivch_read(dvo, VR01, &vr01)) +		return; + +	if (mode == DRM_MODE_DPMS_ON) +		backlight = 1; +	else +		backlight = 0; +	ivch_write(dvo, VR80, backlight); + +	if (mode == DRM_MODE_DPMS_ON) +		vr01 |= VR01_LCD_ENABLE | VR01_DVO_ENABLE; +	else +		vr01 &= ~(VR01_LCD_ENABLE | VR01_DVO_ENABLE); + +	ivch_write(dvo, VR01, vr01); + +	/* Wait for the panel to make its state transition */ +	for (i = 0; i < 100; i++) { +		if (!ivch_read(dvo, VR30, &vr30)) +			break; + +		if (((vr30 & VR30_PANEL_ON) != 0) == (mode == DRM_MODE_DPMS_ON)) +			break; +		udelay(1000); +	} +	/* wait some more; vch may fail to resync sometimes without this */ +	udelay(16 * 1000); +} + +static void ivch_mode_set(struct intel_dvo_device *dvo, +			  struct drm_display_mode *mode, +			  struct drm_display_mode *adjusted_mode) +{ +	uint16_t vr40 = 0; +	uint16_t vr01; + +	vr01 = 0; +	vr40 = (VR40_STALL_ENABLE | VR40_VERTICAL_INTERP_ENABLE | +		VR40_HORIZONTAL_INTERP_ENABLE); + +	if (mode->hdisplay != adjusted_mode->hdisplay ||  +	    mode->vdisplay != adjusted_mode->vdisplay) { +		uint16_t x_ratio, y_ratio; + +		vr01 |= VR01_PANEL_FIT_ENABLE; +		vr40 |= VR40_CLOCK_GATING_ENABLE; +		x_ratio = (((mode->hdisplay - 1) << 16) / +			   (adjusted_mode->hdisplay - 1)) >> 2; +		y_ratio = (((mode->vdisplay - 1) << 16) / +			   (adjusted_mode->vdisplay - 1)) >> 2; +		ivch_write (dvo, VR42, x_ratio); +		ivch_write (dvo, VR41, y_ratio); +	} else { +		vr01 &= ~VR01_PANEL_FIT_ENABLE; +		vr40 &= ~VR40_CLOCK_GATING_ENABLE; +	} +	vr40 &= ~VR40_AUTO_RATIO_ENABLE; + +	ivch_write(dvo, VR01, vr01); +	ivch_write(dvo, VR40, vr40); + +	ivch_dump_regs(dvo); +} + +static void ivch_dump_regs(struct intel_dvo_device *dvo) +{ +	uint16_t val; + +	ivch_read(dvo, VR00, &val); +	DRM_DEBUG("VR00: 0x%04x\n", val); +	ivch_read(dvo, VR01, &val); +	DRM_DEBUG("VR01: 0x%04x\n", val); +	ivch_read(dvo, VR30, &val); +	DRM_DEBUG("VR30: 0x%04x\n", val); +	ivch_read(dvo, VR40, &val); +	DRM_DEBUG("VR40: 0x%04x\n", val); + +	/* GPIO registers */ +	ivch_read(dvo, VR80, &val); +	DRM_DEBUG("VR80: 0x%04x\n", val); +	ivch_read(dvo, VR81, &val); +	DRM_DEBUG("VR81: 0x%04x\n", val); +	ivch_read(dvo, VR82, &val); +	DRM_DEBUG("VR82: 0x%04x\n", val); +	ivch_read(dvo, VR83, &val); +	DRM_DEBUG("VR83: 0x%04x\n", val); +	ivch_read(dvo, VR84, &val); +	DRM_DEBUG("VR84: 0x%04x\n", val); +	ivch_read(dvo, VR85, &val); +	DRM_DEBUG("VR85: 0x%04x\n", val); +	ivch_read(dvo, VR86, &val); +	DRM_DEBUG("VR86: 0x%04x\n", val); +	ivch_read(dvo, VR87, &val); +	DRM_DEBUG("VR87: 0x%04x\n", val); +	ivch_read(dvo, VR88, &val); +	DRM_DEBUG("VR88: 0x%04x\n", val); + +	/* Scratch register 0 - AIM Panel type */ +	ivch_read(dvo, VR8E, &val); +	DRM_DEBUG("VR8E: 0x%04x\n", val); + +	/* Scratch register 1 - Status register */ +	ivch_read(dvo, VR8F, &val); +	DRM_DEBUG("VR8F: 0x%04x\n", val); +} + +static void ivch_save(struct intel_dvo_device *dvo) +{ +	struct ivch_priv *priv = dvo->dev_priv; + +	ivch_read(dvo, VR01, &priv->save_VR01); +	ivch_read(dvo, VR40, &priv->save_VR40); +} + +static void ivch_restore(struct intel_dvo_device *dvo) +{ +	struct ivch_priv *priv = dvo->dev_priv; + +	ivch_write(dvo, VR01, priv->save_VR01); +	ivch_write(dvo, VR40, priv->save_VR40); +} + +static void ivch_destroy(struct intel_dvo_device *dvo) +{ +	struct ivch_priv *priv = dvo->dev_priv; + +	if (priv) { +		kfree(priv); +		dvo->dev_priv = NULL; +	} +} + +struct intel_dvo_dev_ops ivch_ops= { +	.init = ivch_init, +	.dpms = ivch_dpms, +	.save = ivch_save, +	.restore = ivch_restore, +	.mode_valid = ivch_mode_valid, +	.mode_set = ivch_mode_set, +	.detect = ivch_detect, +	.dump_regs = ivch_dump_regs, +	.destroy = ivch_destroy, +}; diff --git a/linux-core/dvo_sil164.c b/linux-core/dvo_sil164.c new file mode 100644 index 00000000..033a4bb0 --- /dev/null +++ b/linux-core/dvo_sil164.c @@ -0,0 +1,302 @@ +/************************************************************************** + +Copyright © 2006 Dave Airlie + +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 THE AUTHOR 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 "dvo.h" + +#define SIL164_VID 0x0001 +#define SIL164_DID 0x0006 + +#define SIL164_VID_LO 0x00 +#define SIL164_VID_HI 0x01 +#define SIL164_DID_LO 0x02 +#define SIL164_DID_HI 0x03 +#define SIL164_REV    0x04 +#define SIL164_RSVD   0x05 +#define SIL164_FREQ_LO 0x06 +#define SIL164_FREQ_HI 0x07 + +#define SIL164_REG8 0x08 +#define SIL164_8_VEN (1<<5) +#define SIL164_8_HEN (1<<4) +#define SIL164_8_DSEL (1<<3) +#define SIL164_8_BSEL (1<<2) +#define SIL164_8_EDGE (1<<1) +#define SIL164_8_PD   (1<<0) + +#define SIL164_REG9 0x09 +#define SIL164_9_VLOW (1<<7) +#define SIL164_9_MSEL_MASK (0x7<<4) +#define SIL164_9_TSEL (1<<3) +#define SIL164_9_RSEN (1<<2) +#define SIL164_9_HTPLG (1<<1) +#define SIL164_9_MDI (1<<0) + +#define SIL164_REGC 0x0c + +struct sil164_save_rec { +	uint8_t reg8; +	uint8_t reg9; +	uint8_t regc; +}; + +struct sil164_priv { +	//I2CDevRec d; +	bool quiet; +	struct sil164_save_rec save_regs; +	struct sil164_save_rec mode_regs; +}; + +#define SILPTR(d) ((SIL164Ptr)(d->DriverPrivate.ptr)) + +static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch) +{ +	struct sil164_priv *sil = dvo->dev_priv; +	struct intel_i2c_chan *i2cbus = dvo->i2c_bus; +	u8 out_buf[2]; +	u8 in_buf[2]; + +	struct i2c_msg msgs[] = { +		{ +			.addr = i2cbus->slave_addr, +			.flags = 0, +			.len = 1, +			.buf = out_buf, +		}, +		{ +			.addr = i2cbus->slave_addr, +			.flags = I2C_M_RD, +			.len = 1, +			.buf = in_buf, +		} +	}; + +	out_buf[0] = addr; +	out_buf[1] = 0; + +	if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) { +		*ch = in_buf[0]; +		return true; +	}; + +	if (!sil->quiet) { +		DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n", +			  addr, i2cbus->adapter.name, i2cbus->slave_addr); +	} +	return false; +} + +static bool sil164_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch) +{ +	struct sil164_priv *sil= dvo->dev_priv; +	struct intel_i2c_chan *i2cbus = dvo->i2c_bus; +	uint8_t out_buf[2]; +	struct i2c_msg msg = { +		.addr = i2cbus->slave_addr, +		.flags = 0, +		.len = 2, +		.buf = out_buf, +	}; + +	out_buf[0] = addr; +	out_buf[1] = ch; + +	if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1) +		return true; + +	if (!sil->quiet) { +		DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n", +			  addr, i2cbus->adapter.name, i2cbus->slave_addr); +	} + +	return false; +} + +/* Silicon Image 164 driver for chip on i2c bus */ +static bool sil164_init(struct intel_dvo_device *dvo, +			struct intel_i2c_chan *i2cbus) +{ +	/* this will detect the SIL164 chip on the specified i2c bus */ +	struct sil164_priv *sil; +	unsigned char ch; + +	sil = kzalloc(sizeof(struct sil164_priv), GFP_KERNEL); +	if (sil == NULL) +		return false; + +	dvo->i2c_bus = i2cbus; +	dvo->i2c_bus->slave_addr = dvo->slave_addr; +	dvo->dev_priv = sil; +	sil->quiet = true; + +	if (!sil164_readb(dvo, SIL164_VID_LO, &ch)) +		goto out; + +	if (ch != (SIL164_VID & 0xff)) { +		DRM_DEBUG("sil164 not detected got %d: from %s Slave %d.\n", +			  ch, i2cbus->adapter.name, i2cbus->slave_addr); +		goto out; +	} + +	if (!sil164_readb(dvo, SIL164_DID_LO, &ch)) +		goto out; + +	if (ch != (SIL164_DID & 0xff)) { +		DRM_DEBUG("sil164 not detected got %d: from %s Slave %d.\n", +			  ch, i2cbus->adapter.name, i2cbus->slave_addr); +		goto out; +	} +	sil->quiet = false; + +	DRM_DEBUG("init sil164 dvo controller successfully!\n"); +	return true; + +out: +	kfree(sil); +	return false; +} + +static enum drm_connector_status sil164_detect(struct intel_dvo_device *dvo) +{ +	uint8_t reg9; + +	sil164_readb(dvo, SIL164_REG9, ®9); + +	if (reg9 & SIL164_9_HTPLG) +		return connector_status_connected; +	else +		return connector_status_disconnected; +} + +static enum drm_mode_status sil164_mode_valid(struct intel_dvo_device *dvo, +					      struct drm_display_mode *mode) +{ +	return MODE_OK; +} + +static void sil164_mode_set(struct intel_dvo_device *dvo, +			    struct drm_display_mode *mode, +			    struct drm_display_mode *adjusted_mode) +{ +	/* As long as the basics are set up, since we don't have clock +	 * dependencies in the mode setup, we can just leave the +	 * registers alone and everything will work fine. +	 */ +	/* recommended programming sequence from doc */ +	/*sil164_writeb(sil, 0x08, 0x30); +	  sil164_writeb(sil, 0x09, 0x00); +	  sil164_writeb(sil, 0x0a, 0x90); +	  sil164_writeb(sil, 0x0c, 0x89); +	  sil164_writeb(sil, 0x08, 0x31);*/ +	/* don't do much */ +	return; +} + +/* set the SIL164 power state */ +static void sil164_dpms(struct intel_dvo_device *dvo, int mode) +{ +	int ret; +	unsigned char ch; + +	ret = sil164_readb(dvo, SIL164_REG8, &ch); +	if (ret == false) +		return; + +	if (mode == DRM_MODE_DPMS_ON) +		ch |= SIL164_8_PD; +	else +		ch &= ~SIL164_8_PD; + +	sil164_writeb(dvo, SIL164_REG8, ch); +	return; +} + +static void sil164_dump_regs(struct intel_dvo_device *dvo) +{ +	uint8_t val; + +	sil164_readb(dvo, SIL164_FREQ_LO, &val); +	DRM_DEBUG("SIL164_FREQ_LO: 0x%02x\n", val); +	sil164_readb(dvo, SIL164_FREQ_HI, &val); +	DRM_DEBUG("SIL164_FREQ_HI: 0x%02x\n", val); +	sil164_readb(dvo, SIL164_REG8, &val); +	DRM_DEBUG("SIL164_REG8: 0x%02x\n", val); +	sil164_readb(dvo, SIL164_REG9, &val); +	DRM_DEBUG("SIL164_REG9: 0x%02x\n", val); +	sil164_readb(dvo, SIL164_REGC, &val); +	DRM_DEBUG("SIL164_REGC: 0x%02x\n", val); +} + +static void sil164_save(struct intel_dvo_device *dvo) +{ +	struct sil164_priv *sil= dvo->dev_priv; + +	if (!sil164_readb(dvo, SIL164_REG8, &sil->save_regs.reg8)) +		return; + +	if (!sil164_readb(dvo, SIL164_REG9, &sil->save_regs.reg9)) +		return; + +	if (!sil164_readb(dvo, SIL164_REGC, &sil->save_regs.regc)) +		return; + +	return; +} + +static void sil164_restore(struct intel_dvo_device *dvo) +{ +	struct sil164_priv *sil = dvo->dev_priv; + +	/* Restore it powered down initially */ +	sil164_writeb(dvo, SIL164_REG8, sil->save_regs.reg8 & ~0x1); + +	sil164_writeb(dvo, SIL164_REG9, sil->save_regs.reg9); +	sil164_writeb(dvo, SIL164_REGC, sil->save_regs.regc); +	sil164_writeb(dvo, SIL164_REG8, sil->save_regs.reg8); +} + +static void sil164_destroy(struct intel_dvo_device *dvo) +{ +	struct sil164_priv *sil = dvo->dev_priv; + +	if (sil) { +		kfree(sil); +		dvo->dev_priv = NULL; +	} +} + +struct intel_dvo_dev_ops sil164_ops = { +	.init = sil164_init, +	.detect = sil164_detect, +	.mode_valid = sil164_mode_valid, +	.mode_set = sil164_mode_set, +	.dpms = sil164_dpms, +	.dump_regs = sil164_dump_regs, +	.save = sil164_save, +	.restore = sil164_restore, +	.destroy = sil164_destroy, +}; diff --git a/linux-core/dvo_tfp410.c b/linux-core/dvo_tfp410.c new file mode 100644 index 00000000..207fda80 --- /dev/null +++ b/linux-core/dvo_tfp410.c @@ -0,0 +1,335 @@ +/* + * Copyright © 2007 Dave Mueller + * + * 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 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + *    Dave Mueller <dave.mueller@gmx.ch> + * + */ + +#include "dvo.h" + +/* register definitions according to the TFP410 data sheet */ +#define TFP410_VID		0x014C +#define TFP410_DID		0x0410 + +#define TFP410_VID_LO		0x00 +#define TFP410_VID_HI		0x01 +#define TFP410_DID_LO		0x02 +#define TFP410_DID_HI		0x03 +#define TFP410_REV		0x04 + +#define TFP410_CTL_1		0x08 +#define TFP410_CTL_1_TDIS	(1<<6) +#define TFP410_CTL_1_VEN	(1<<5) +#define TFP410_CTL_1_HEN	(1<<4) +#define TFP410_CTL_1_DSEL	(1<<3) +#define TFP410_CTL_1_BSEL	(1<<2) +#define TFP410_CTL_1_EDGE	(1<<1) +#define TFP410_CTL_1_PD		(1<<0) + +#define TFP410_CTL_2		0x09 +#define TFP410_CTL_2_VLOW	(1<<7) +#define TFP410_CTL_2_MSEL_MASK	(0x7<<4) +#define TFP410_CTL_2_MSEL	(1<<4) +#define TFP410_CTL_2_TSEL	(1<<3) +#define TFP410_CTL_2_RSEN	(1<<2) +#define TFP410_CTL_2_HTPLG	(1<<1) +#define TFP410_CTL_2_MDI	(1<<0) + +#define TFP410_CTL_3		0x0A +#define TFP410_CTL_3_DK_MASK 	(0x7<<5) +#define TFP410_CTL_3_DK		(1<<5) +#define TFP410_CTL_3_DKEN	(1<<4) +#define TFP410_CTL_3_CTL_MASK	(0x7<<1) +#define TFP410_CTL_3_CTL	(1<<1) + +#define TFP410_USERCFG		0x0B + +#define TFP410_DE_DLY		0x32 + +#define TFP410_DE_CTL		0x33 +#define TFP410_DE_CTL_DEGEN	(1<<6) +#define TFP410_DE_CTL_VSPOL	(1<<5) +#define TFP410_DE_CTL_HSPOL	(1<<4) +#define TFP410_DE_CTL_DEDLY8	(1<<0) + +#define TFP410_DE_TOP		0x34 + +#define TFP410_DE_CNT_LO	0x36 +#define TFP410_DE_CNT_HI	0x37 + +#define TFP410_DE_LIN_LO	0x38 +#define TFP410_DE_LIN_HI	0x39 + +#define TFP410_H_RES_LO		0x3A +#define TFP410_H_RES_HI		0x3B + +#define TFP410_V_RES_LO		0x3C +#define TFP410_V_RES_HI		0x3D + +struct tfp410_save_rec { +	uint8_t ctl1; +	uint8_t ctl2; +}; + +struct tfp410_priv { +	bool quiet; + +	struct tfp410_save_rec saved_reg; +	struct tfp410_save_rec mode_reg; +}; + +static bool tfp410_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch) +{ +	struct tfp410_priv *tfp = dvo->dev_priv; +	struct intel_i2c_chan *i2cbus = dvo->i2c_bus; +	u8 out_buf[2]; +	u8 in_buf[2]; + +	struct i2c_msg msgs[] = { +		{ +			.addr = i2cbus->slave_addr, +			.flags = 0, +			.len = 1, +			.buf = out_buf, +		}, +		{ +			.addr = i2cbus->slave_addr, +			.flags = I2C_M_RD, +			.len = 1, +			.buf = in_buf, +		} +	}; + +	out_buf[0] = addr; +	out_buf[1] = 0; + +	if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) { +		*ch = in_buf[0]; +		return true; +	}; + +	if (!tfp->quiet) { +		DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n", +			  addr, i2cbus->adapter.name, i2cbus->slave_addr); +	} +	return false; +} + +static bool tfp410_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch) +{ +	struct tfp410_priv *tfp = dvo->dev_priv; +	struct intel_i2c_chan *i2cbus = dvo->i2c_bus; +	uint8_t out_buf[2]; +	struct i2c_msg msg = { +		.addr = i2cbus->slave_addr, +		.flags = 0, +		.len = 2, +		.buf = out_buf, +	}; + +	out_buf[0] = addr; +	out_buf[1] = ch; + +	if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1) +		return true; + +	if (!tfp->quiet) { +		DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n", +			  addr, i2cbus->adapter.name, i2cbus->slave_addr); +	} + +	return false; +} + +static int tfp410_getid(struct intel_dvo_device *dvo, int addr) +{ +	uint8_t ch1, ch2; + +	if (tfp410_readb(dvo, addr+0, &ch1) && +	    tfp410_readb(dvo, addr+1, &ch2)) +		return ((ch2 << 8) & 0xFF00) | (ch1 & 0x00FF); + +	return -1; +} + +/* Ti TFP410 driver for chip on i2c bus */ +static bool tfp410_init(struct intel_dvo_device *dvo, +			struct intel_i2c_chan *i2cbus) +{ +	/* this will detect the tfp410 chip on the specified i2c bus */ +	struct tfp410_priv *tfp; +	int id; + +	tfp = kzalloc(sizeof(struct tfp410_priv), GFP_KERNEL); +	if (tfp == NULL) +		return false; + +	dvo->i2c_bus = i2cbus; +	dvo->i2c_bus->slave_addr = dvo->slave_addr; +	dvo->dev_priv = tfp; +	tfp->quiet = true; + +	if ((id = tfp410_getid(dvo, TFP410_VID_LO)) != TFP410_VID) { +		DRM_DEBUG("tfp410 not detected got VID %X: from %s Slave %d.\n", +			  id, i2cbus->adapter.name, i2cbus->slave_addr); +		goto out; +	} + +	if ((id = tfp410_getid(dvo, TFP410_DID_LO)) != TFP410_DID) { +		DRM_DEBUG("tfp410 not detected got DID %X: from %s Slave %d.\n", +			  id, i2cbus->adapter.name, i2cbus->slave_addr); +		goto out; +	} +	tfp->quiet = false; +	return true; +out: +	kfree(tfp); +	return false; +} + +static enum drm_connector_status tfp410_detect(struct intel_dvo_device *dvo) +{ +	enum drm_connector_status ret = connector_status_disconnected; +	uint8_t ctl2; + +	if (tfp410_readb(dvo, TFP410_CTL_2, &ctl2)) { +		if (ctl2 & TFP410_CTL_2_HTPLG) +			ret = connector_status_connected; +		else +			ret = connector_status_disconnected; +	} + +	return ret; +} + +static enum drm_mode_status tfp410_mode_valid(struct intel_dvo_device *dvo, +					      struct drm_display_mode *mode) +{ +	return MODE_OK; +} + +static void tfp410_mode_set(struct intel_dvo_device *dvo, +			    struct drm_display_mode *mode, +			    struct drm_display_mode *adjusted_mode) +{ +    /* As long as the basics are set up, since we don't have clock dependencies +     * in the mode setup, we can just leave the registers alone and everything +     * will work fine. +     */ +    /* don't do much */ +    return; +} + +/* set the tfp410 power state */ +static void tfp410_dpms(struct intel_dvo_device *dvo, int mode) +{ +	uint8_t ctl1; + +	if (!tfp410_readb(dvo, TFP410_CTL_1, &ctl1)) +		return; + +	if (mode == DRM_MODE_DPMS_ON) +		ctl1 |= TFP410_CTL_1_PD; +	else +		ctl1 &= ~TFP410_CTL_1_PD; + +	tfp410_writeb(dvo, TFP410_CTL_1, ctl1); +} + +static void tfp410_dump_regs(struct intel_dvo_device *dvo) +{ +	uint8_t val, val2; + +	tfp410_readb(dvo, TFP410_REV, &val); +	DRM_DEBUG("TFP410_REV: 0x%02X\n", val); +	tfp410_readb(dvo, TFP410_CTL_1, &val); +	DRM_DEBUG("TFP410_CTL1: 0x%02X\n", val); +	tfp410_readb(dvo, TFP410_CTL_2, &val); +	DRM_DEBUG("TFP410_CTL2: 0x%02X\n", val); +	tfp410_readb(dvo, TFP410_CTL_3, &val); +	DRM_DEBUG("TFP410_CTL3: 0x%02X\n", val); +	tfp410_readb(dvo, TFP410_USERCFG, &val); +	DRM_DEBUG("TFP410_USERCFG: 0x%02X\n", val); +	tfp410_readb(dvo, TFP410_DE_DLY, &val); +	DRM_DEBUG("TFP410_DE_DLY: 0x%02X\n", val); +	tfp410_readb(dvo, TFP410_DE_CTL, &val); +	DRM_DEBUG("TFP410_DE_CTL: 0x%02X\n", val); +	tfp410_readb(dvo, TFP410_DE_TOP, &val); +	DRM_DEBUG("TFP410_DE_TOP: 0x%02X\n", val); +	tfp410_readb(dvo, TFP410_DE_CNT_LO, &val); +	tfp410_readb(dvo, TFP410_DE_CNT_HI, &val2); +	DRM_DEBUG("TFP410_DE_CNT: 0x%02X%02X\n", val2, val); +	tfp410_readb(dvo, TFP410_DE_LIN_LO, &val); +	tfp410_readb(dvo, TFP410_DE_LIN_HI, &val2); +	DRM_DEBUG("TFP410_DE_LIN: 0x%02X%02X\n", val2, val); +	tfp410_readb(dvo, TFP410_H_RES_LO, &val); +	tfp410_readb(dvo, TFP410_H_RES_HI, &val2); +	DRM_DEBUG("TFP410_H_RES: 0x%02X%02X\n", val2, val); +	tfp410_readb(dvo, TFP410_V_RES_LO, &val); +	tfp410_readb(dvo, TFP410_V_RES_HI, &val2); +	DRM_DEBUG("TFP410_V_RES: 0x%02X%02X\n", val2, val); +} + +static void tfp410_save(struct intel_dvo_device *dvo) +{ +	struct tfp410_priv *tfp = dvo->dev_priv; + +	if (!tfp410_readb(dvo, TFP410_CTL_1, &tfp->saved_reg.ctl1)) +		return; + +	if (!tfp410_readb(dvo, TFP410_CTL_2, &tfp->saved_reg.ctl2)) +		return; +} + +static void tfp410_restore(struct intel_dvo_device *dvo) +{ +	struct tfp410_priv *tfp = dvo->dev_priv; + +	/* Restore it powered down initially */ +	tfp410_writeb(dvo, TFP410_CTL_1, tfp->saved_reg.ctl1 & ~0x1); + +	tfp410_writeb(dvo, TFP410_CTL_2, tfp->saved_reg.ctl2); +	tfp410_writeb(dvo, TFP410_CTL_1, tfp->saved_reg.ctl1); +} + +static void tfp410_destroy(struct intel_dvo_device *dvo) +{ +	struct tfp410_priv *tfp = dvo->dev_priv; + +	if (tfp) { +		kfree(tfp); +		dvo->dev_priv = NULL; +	} +} + +struct intel_dvo_dev_ops tfp410_ops = { +	.init = tfp410_init, +	.detect = tfp410_detect, +	.mode_valid = tfp410_mode_valid, +	.mode_set = tfp410_mode_set, +	.dpms = tfp410_dpms, +	.dump_regs = tfp410_dump_regs, +	.save = tfp410_save, +	.restore = tfp410_restore, +	.destroy = tfp410_destroy, +}; diff --git a/linux-core/i915_buffer.c b/linux-core/i915_buffer.c index 8d991c42..4224b737 100644 --- a/linux-core/i915_buffer.c +++ b/linux-core/i915_buffer.c @@ -89,7 +89,7 @@ int i915_init_mem_type(struct drm_device *dev, uint32_t type,  		man->drm_bus_maptype = _DRM_AGP;  		man->gpu_offset = 0;  		break; -	case DRM_BO_MEM_PRIV0: +	case DRM_BO_MEM_VRAM:  		if (!(drm_core_has_AGP(dev) && dev->agp)) {  			DRM_ERROR("AGP is not enabled for memory type %u\n",  				  (unsigned)type); @@ -103,6 +103,9 @@ int i915_init_mem_type(struct drm_device *dev, uint32_t type,  		man->drm_bus_maptype = _DRM_AGP;  		man->gpu_offset = 0;  		break; +	case DRM_BO_MEM_PRIV0: /* for OS preallocated space */ +		DRM_ERROR("PRIV0 not used yet.\n"); +		break;  	default:  		DRM_ERROR("Unsupported memory type %u\n", (unsigned)type);  		return -EINVAL; @@ -139,7 +142,7 @@ static void i915_emit_copy_blit(struct drm_device * dev,  {  	uint32_t cur_pages;  	uint32_t stride = PAGE_SIZE; -	drm_i915_private_t *dev_priv = dev->dev_private; +	struct drm_i915_private *dev_priv = dev->dev_private;  	RING_LOCALS;  	if (!dev_priv) @@ -267,10 +270,12 @@ static inline void clflush(volatile void *__p)  static inline void drm_cache_flush_addr(void *virt)  { +#ifdef cpu_has_clflush  	int i;  	for (i = 0; i < PAGE_SIZE; i += boot_cpu_data.x86_clflush_size)  		clflush(virt+i); +#endif  }  static inline void drm_cache_flush_page(struct page *p) @@ -288,6 +293,9 @@ void i915_flush_ttm(struct drm_ttm *ttm)  	DRM_MEMORYBARRIER();  #ifdef CONFIG_X86_32 +#ifndef cpu_has_clflush +#define cpu_has_clflush 0 +#endif  	/* Hopefully nobody has built an x86-64 processor without clflush */  	if (!cpu_has_clflush) {  		wbinvd(); diff --git a/linux-core/i915_drv.c b/linux-core/i915_drv.c index cf8016c0..a3b35358 100644 --- a/linux-core/i915_drv.c +++ b/linux-core/i915_drv.c @@ -30,6 +30,7 @@  #include "drmP.h"  #include "drm.h"  #include "i915_drm.h" +#include "intel_drv.h"  #include "i915_drv.h"  #include "drm_pciids.h" @@ -38,14 +39,23 @@ static struct pci_device_id pciidlist[] = {  	i915_PCI_IDS  }; -#ifdef I915_HAVE_FENCE +unsigned int i915_modeset = 0; +module_param_named(modeset, i915_modeset, int, 0400); + +unsigned int i915_fbpercrtc = 0; +module_param_named(fbpercrtc, i915_fbpercrtc, int, 0400); + +unsigned int i915_rightof = 1; +module_param_named(i915_rightof, i915_rightof, int, 0400); + +#if defined(I915_HAVE_FENCE) && defined(I915_TTM)  extern struct drm_fence_driver i915_fence_driver;  #endif -#ifdef I915_HAVE_BUFFER +#if defined(I915_HAVE_BUFFER) && defined(I915_TTM) -static uint32_t i915_mem_prios[] = {DRM_BO_MEM_PRIV0, DRM_BO_MEM_TT, DRM_BO_MEM_LOCAL}; -static uint32_t i915_busy_prios[] = {DRM_BO_MEM_TT, DRM_BO_MEM_PRIV0, DRM_BO_MEM_LOCAL}; +static uint32_t i915_mem_prios[] = {DRM_BO_MEM_VRAM, DRM_BO_MEM_TT, DRM_BO_MEM_LOCAL}; +static uint32_t i915_busy_prios[] = {DRM_BO_MEM_TT, DRM_BO_MEM_VRAM, DRM_BO_MEM_LOCAL};  static struct drm_bo_driver i915_bo_driver = {  	.mem_type_prio = i915_mem_prios, @@ -61,7 +71,203 @@ static struct drm_bo_driver i915_bo_driver = {  	.ttm_cache_flush = i915_flush_ttm,  	.command_stream_barrier = NULL,  }; -#endif +#endif /* ttm */ + +static bool i915_pipe_enabled(struct drm_device *dev, enum pipe pipe) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; + +	if (pipe == PIPE_A) +		return (I915_READ(DPLL_A) & DPLL_VCO_ENABLE); +	else +		return (I915_READ(DPLL_B) & DPLL_VCO_ENABLE); +} + +static void i915_save_palette(struct drm_device *dev, enum pipe pipe) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	unsigned long reg = (pipe == PIPE_A ? PALETTE_A : PALETTE_B); +	u32 *array; +	int i; + +	if (!i915_pipe_enabled(dev, pipe)) +		return; + +	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)); +} + +static void i915_restore_palette(struct drm_device *dev, enum pipe pipe) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	unsigned long reg = (pipe == PIPE_A ? PALETTE_A : PALETTE_B); +	u32 *array; +	int i; + +	if (!i915_pipe_enabled(dev, pipe)) +		return; + +	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]); +} + +static u8 i915_read_indexed(u16 index_port, u16 data_port, u8 reg) +{ +	outb(reg, index_port); +	return inb(data_port); +} + +static u8 i915_read_ar(u16 st01, u8 reg, u16 palette_enable) +{ +	inb(st01); +	outb(palette_enable | reg, VGA_AR_INDEX); +	return inb(VGA_AR_DATA_READ); +} + +static void i915_write_ar(u8 st01, u8 reg, u8 val, u16 palette_enable) +{ +	inb(st01); +	outb(palette_enable | reg, VGA_AR_INDEX); +	outb(val, VGA_AR_DATA_WRITE); +} + +static void i915_write_indexed(u16 index_port, u16 data_port, u8 reg, u8 val) +{ +	outb(reg, index_port); +	outb(val, data_port); +} + +static void i915_save_vga(struct drm_device *dev) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	int i; +	u16 cr_index, cr_data, st01; + +	/* VGA color palette registers */ +	dev_priv->saveDACMASK = inb(VGA_DACMASK); +	/* DACCRX automatically increments during read */ +	outb(0, VGA_DACRX); +	/* Read 3 bytes of color data from each index */ +	for (i = 0; i < 256 * 3; i++) +		dev_priv->saveDACDATA[i] = inb(VGA_DACDATA); + +	/* MSR bits */ +	dev_priv->saveMSR = inb(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(cr_index, cr_data, 0x11, +			   i915_read_indexed(cr_index, cr_data, 0x11) & +			   (~0x80)); +	for (i = 0; i <= 0x24; i++) +		dev_priv->saveCR[i] = +			i915_read_indexed(cr_index, cr_data, i); +	/* Make sure we don't turn off CR group 0 writes */ +	dev_priv->saveCR[0x11] &= ~0x80; + +	/* Attribute controller registers */ +	inb(st01); +	dev_priv->saveAR_INDEX = inb(VGA_AR_INDEX); +	for (i = 0; i <= 0x14; i++) +		dev_priv->saveAR[i] = i915_read_ar(st01, i, 0); +	inb(st01); +	outb(dev_priv->saveAR_INDEX, VGA_AR_INDEX); +	inb(st01); + +	/* Graphics controller registers */ +	for (i = 0; i < 9; i++) +		dev_priv->saveGR[i] = +			i915_read_indexed(VGA_GR_INDEX, VGA_GR_DATA, i); + +	dev_priv->saveGR[0x10] = +		i915_read_indexed(VGA_GR_INDEX, VGA_GR_DATA, 0x10); +	dev_priv->saveGR[0x11] = +		i915_read_indexed(VGA_GR_INDEX, VGA_GR_DATA, 0x11); +	dev_priv->saveGR[0x18] = +		i915_read_indexed(VGA_GR_INDEX, VGA_GR_DATA, 0x18); + +	/* Sequencer registers */ +	for (i = 0; i < 8; i++) +		dev_priv->saveSR[i] = +			i915_read_indexed(VGA_SR_INDEX, VGA_SR_DATA, i); +} + +static void i915_restore_vga(struct drm_device *dev) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	int i; +	u16 cr_index, cr_data, st01; + +	/* MSR bits */ +	outb(dev_priv->saveMSR, VGA_MSR_WRITE); +	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(VGA_SR_INDEX, VGA_SR_DATA, i, +				   dev_priv->saveSR[i]); + +	/* CRT controller regs */ +	/* Enable CR group 0 writes */ +	i915_write_indexed(cr_index, cr_data, 0x11, dev_priv->saveCR[0x11]); +	for (i = 0; i <= 0x24; i++) +		i915_write_indexed(cr_index, cr_data, i, dev_priv->saveCR[i]); + +	/* Graphics controller regs */ +	for (i = 0; i < 9; i++) +		i915_write_indexed(VGA_GR_INDEX, VGA_GR_DATA, i, +				   dev_priv->saveGR[i]); + +	i915_write_indexed(VGA_GR_INDEX, VGA_GR_DATA, 0x10, +			   dev_priv->saveGR[0x10]); +	i915_write_indexed(VGA_GR_INDEX, VGA_GR_DATA, 0x11, +			   dev_priv->saveGR[0x11]); +	i915_write_indexed(VGA_GR_INDEX, VGA_GR_DATA, 0x18, +			   dev_priv->saveGR[0x18]); + +	/* Attribute controller registers */ +	inb(st01); /* switch back to index mode */ +	for (i = 0; i <= 0x14; i++) +		i915_write_ar(st01, i, dev_priv->saveAR[i], 0); +	inb(st01); /* switch back to index mode */ +	outb(dev_priv->saveAR_INDEX | 0x20, VGA_AR_INDEX); +	inb(st01); + +	/* VGA color palette registers */ +	outb(dev_priv->saveDACMASK, VGA_DACMASK); +	/* DACCRX automatically increments during read */ +	outb(0, VGA_DACWX); +	/* Read 3 bytes of color data from each index */ +	for (i = 0; i < 256 * 3; i++) +		outb(dev_priv->saveDACDATA[i], VGA_DACDATA); + +}  static int i915_suspend(struct drm_device *dev, pm_message_t state)  { @@ -111,18 +317,22 @@ static int i915_resume(struct drm_device *dev)  }  static int probe(struct pci_dev *pdev, const struct pci_device_id *ent); +static void remove(struct pci_dev *pdev); +  static struct drm_driver driver = {  	/* don't use mtrr's here, the Xserver or user space app should  	 * deal with them for intel hardware.  	 */  	.driver_features =  	    DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | /* DRIVER_USE_MTRR | */ -	    DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, +	    DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM,  	.load = i915_driver_load,  	.unload = i915_driver_unload,  	.firstopen = i915_driver_firstopen, +	.open = i915_driver_open,  	.lastclose = i915_driver_lastclose,  	.preclose = i915_driver_preclose, +	.postclose = i915_driver_postclose,  	.suspend = i915_suspend,  	.resume = i915_resume,  	.device_is_agp = i915_driver_device_is_agp, @@ -136,7 +346,13 @@ static struct drm_driver driver = {  	.reclaim_buffers = drm_core_reclaim_buffers,  	.get_map_ofs = drm_core_get_map_ofs,  	.get_reg_ofs = drm_core_get_reg_ofs, +	.master_create = i915_master_create, +	.master_destroy = i915_master_destroy, +	.proc_init = i915_gem_proc_init, +	.proc_cleanup = i915_gem_proc_cleanup,  	.ioctls = i915_ioctls, +	.gem_init_object = i915_gem_init_object, +	.gem_free_object = i915_gem_free_object,  	.fops = {  		.owner = THIS_MODULE,  		.open = drm_open, @@ -153,12 +369,12 @@ static struct drm_driver driver = {  		.name = DRIVER_NAME,  		.id_table = pciidlist,  		.probe = probe, -		.remove = __devexit_p(drm_cleanup_pci), +		.remove = remove,  		}, -#ifdef I915_HAVE_FENCE +#if defined(I915_HAVE_FENCE) && defined(I915_TTM)  	.fence_driver = &i915_fence_driver,  #endif -#ifdef I915_HAVE_BUFFER +#if defined(I915_HAVE_BUFFER) && defined(I915_TTM)  	.bo_driver = &i915_bo_driver,  #endif  	.name = DRIVER_NAME, @@ -171,12 +387,36 @@ static struct drm_driver driver = {  static int probe(struct pci_dev *pdev, const struct pci_device_id *ent)  { -	return drm_get_dev(pdev, ent, &driver); +	int ret; + +	/* On the 945G/GM, the chipset reports the MSI capability on the +	 * integrated graphics even though the support isn't actually there +	 * according to the published specs.  It doesn't appear to function +	 * correctly in testing on 945G. +	 * This may be a side effect of MSI having been made available for PEG +	 * and the registers being closely associated. +	 */ +	if (pdev->device != 0x2772 && pdev->device != 0x27A2) +		(void )pci_enable_msi(pdev); + +	ret = drm_get_dev(pdev, ent, &driver); +	if (ret && pdev->msi_enabled) +		pci_disable_msi(pdev); +	return ret; +} +static void remove(struct pci_dev *pdev) +{ +	drm_cleanup_pci(pdev); +	if (pdev->msi_enabled) +		pci_disable_msi(pdev);  }  static int __init i915_init(void)  {  	driver.num_ioctls = i915_max_ioctl; +	if (i915_modeset == 1) +		driver.driver_features |= DRIVER_MODESET; +  	return drm_init(&driver, pciidlist);  } diff --git a/linux-core/i915_execbuf.c b/linux-core/i915_execbuf.c index 804f3ac1..932882dd 100644 --- a/linux-core/i915_execbuf.c +++ b/linux-core/i915_execbuf.c @@ -819,9 +819,13 @@ void i915_fence_or_sync(struct drm_file *file_priv,  int i915_execbuffer(struct drm_device *dev, void *data,  		    struct drm_file *file_priv)  { -	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; -	drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) -	    dev_priv->sarea_priv; +	struct drm_i915_private *dev_priv = (struct drm_i915_private *)  +		dev->dev_private; +	struct drm_i915_master_private *master_priv =  +		(struct drm_i915_master_private *) +		dev->primary->master->driver_priv; +	struct drm_i915_sarea *sarea_priv = (struct drm_i915_sarea *) +		master_priv->sarea_priv;  	struct drm_i915_execbuffer *exec_buf = data;  	struct drm_i915_batchbuffer *batch = &exec_buf->batch;  	struct drm_fence_arg *fence_arg = &exec_buf->fence_arg; diff --git a/linux-core/i915_fence.c b/linux-core/i915_fence.c index 45613c3a..436b7e1f 100644 --- a/linux-core/i915_fence.c +++ b/linux-core/i915_fence.c @@ -94,7 +94,7 @@ static void i915_fence_flush(struct drm_device *dev,  static void i915_fence_poll(struct drm_device *dev, uint32_t fence_class,  			    uint32_t waiting_types)  { -	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; +	struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;  	struct drm_fence_manager *fm = &dev->fm;  	struct drm_fence_class_manager *fc = &fm->fence_class[0];  	uint32_t sequence; @@ -120,11 +120,11 @@ static void i915_fence_poll(struct drm_device *dev, uint32_t fence_class,  		if (dev_priv->fence_irq_on &&  		    !(fc->waiting_types & DRM_FENCE_TYPE_EXE)) { -			i915_user_irq_off(dev_priv); +			i915_user_irq_off(dev);  			dev_priv->fence_irq_on = 0;  		} else if (!dev_priv->fence_irq_on &&  			   (fc->waiting_types & DRM_FENCE_TYPE_EXE)) { -			i915_user_irq_on(dev_priv); +			i915_user_irq_on(dev);  			dev_priv->fence_irq_on = 1;  		}  	} @@ -147,7 +147,7 @@ static int i915_fence_emit_sequence(struct drm_device *dev, uint32_t class,  			     uint32_t flags, uint32_t *sequence,  			     uint32_t *native_type)  { -	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; +	struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;  	if (unlikely(!dev_priv))  		return -EINVAL; @@ -181,7 +181,7 @@ static int i915_fence_wait(struct drm_fence_object *fence,  			   int lazy, int interruptible, uint32_t mask)  {  	struct drm_device *dev = fence->dev; -	drm_i915_private_t *dev_priv = (struct drm_i915_private *) dev->dev_private; +	struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;  	struct drm_fence_manager *fm = &dev->fm;  	struct drm_fence_class_manager *fc = &fm->fence_class[0];  	int ret; diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c new file mode 100644 index 00000000..63f4b91d --- /dev/null +++ b/linux-core/i915_gem.c @@ -0,0 +1,2710 @@ +/* + * Copyright © 2008 Intel Corporation + * + * 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 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + *    Eric Anholt <eric@anholt.net> + * + */ + +#include "drmP.h" +#include "drm.h" +#include "drm_compat.h" +#include "i915_drm.h" +#include "i915_drv.h" + +#define WATCH_COHERENCY	0 +#define WATCH_BUF	0 +#define WATCH_EXEC	0 +#define WATCH_LRU	0 +#define WATCH_RELOC	0 +#define WATCH_INACTIVE	0 +#define WATCH_PWRITE	0 + +#if WATCH_BUF | WATCH_EXEC | WATCH_PWRITE +static void +i915_gem_dump_object(struct drm_gem_object *obj, int len, +		     const char *where, uint32_t mark); +#endif +	 +static int +i915_gem_object_set_domain(struct drm_gem_object *obj, +			    uint32_t read_domains, +			    uint32_t write_domain); +int +i915_gem_set_domain(struct drm_gem_object *obj, +		    struct drm_file *file_priv, +		    uint32_t read_domains, +		    uint32_t write_domain); + +static void +i915_gem_clflush_object(struct drm_gem_object *obj); + +int i915_gem_do_init(struct drm_device *dev, unsigned long start, +		     unsigned long end) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; + +	if (start >= end || +	    (start & (PAGE_SIZE - 1)) != 0 || +	    (end & (PAGE_SIZE - 1)) != 0) { +		return -EINVAL; +	} + +	drm_memrange_init(&dev_priv->mm.gtt_space, start, +			  end - start); + +	dev->gtt_total = (uint32_t) (end - start); + +	return 0; +} + +int +i915_gem_init_ioctl(struct drm_device *dev, void *data, +		    struct drm_file *file_priv) +{ +	struct drm_i915_gem_init *args = data; +	int ret; + +	mutex_lock(&dev->struct_mutex); +	ret = i915_gem_do_init(dev, args->gtt_start, args->gtt_end); +	mutex_unlock(&dev->struct_mutex); + +	return ret; +} + + +/** + * 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 drm_i915_gem_create *args = data; +	struct drm_gem_object *obj; +	int handle, ret; + +	args->size = roundup(args->size, PAGE_SIZE); + +	/* Allocate the new object */ +	obj = drm_gem_object_alloc(dev, args->size); +	if (obj == NULL) +		return -ENOMEM; + +	ret = drm_gem_handle_create(file_priv, obj, &handle); +	mutex_lock(&dev->struct_mutex); +	drm_gem_object_handle_unreference(obj); +	mutex_unlock(&dev->struct_mutex); + +	if (ret) +		return ret; + +	args->handle = handle; + +	return 0; +} + +/** + * 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 drm_i915_gem_pread *args = data; +	struct drm_gem_object *obj; +	ssize_t read; +	loff_t offset; +	int ret; + +	obj = drm_gem_object_lookup(dev, file_priv, args->handle); +	if (obj == NULL) +		return -EINVAL; + +	mutex_lock(&dev->struct_mutex); +	ret = i915_gem_set_domain(obj, file_priv, +				  I915_GEM_DOMAIN_CPU, 0); +	if (ret) { +		drm_gem_object_unreference(obj); +		mutex_unlock(&dev->struct_mutex); +		return ret; +	} +	offset = args->offset; + +	read = vfs_read(obj->filp, (char __user *)(uintptr_t)args->data_ptr, +			args->size, &offset); +	if (read != args->size) { +		drm_gem_object_unreference(obj); +		mutex_unlock(&dev->struct_mutex); +		if (read < 0) +			return read; +		else +			return -EINVAL; +	} + +	drm_gem_object_unreference(obj); +	mutex_unlock(&dev->struct_mutex); + +	return 0; +} + +#include "drm_compat.h" + +/** + * 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 drm_i915_gem_pwrite *args = data; +	struct drm_gem_object *obj; +	struct drm_i915_gem_object *obj_priv; +	ssize_t remain; +	loff_t offset; +	char __user *user_data; +	char *vaddr; +	int i, o, l; +	int ret = 0; +	unsigned long pfn; +	unsigned long unwritten; + +	obj = drm_gem_object_lookup(dev, file_priv, args->handle); +	if (obj == NULL) +		return -EINVAL; + +	/** Bounds check destination. +	 * +	 * XXX: This could use review for overflow issues... +	 */ +	if (args->offset > obj->size || args->size > obj->size ||  +	    args->offset + args->size > obj->size) +		return -EFAULT; + +	user_data = (char __user *) (uintptr_t) args->data_ptr; +	remain = args->size; +	if (!access_ok(VERIFY_READ, user_data, remain)) +		return -EFAULT; + + +	mutex_lock(&dev->struct_mutex); +	ret = i915_gem_object_pin(obj, 0); +	if (ret) { +		drm_gem_object_unreference(obj); +		mutex_unlock(&dev->struct_mutex); +		return ret; +	} +	ret = i915_gem_set_domain(obj, file_priv, +				  I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT); +	if (ret) +		goto fail; +	 +	obj_priv = obj->driver_private; +	offset = obj_priv->gtt_offset + args->offset; +	obj_priv->dirty = 1; +	 +	while (remain > 0) { +		 +		/** Operation in this page +		 * +		 * i = page number +		 * o = offset within page +		 * l = bytes to copy +		 */ +		i = offset >> PAGE_SHIFT; +		o = offset & (PAGE_SIZE-1); +		l = remain; +		if ((o + l) > PAGE_SIZE) +			l = PAGE_SIZE - o; + +		pfn = (dev->agp->base >> PAGE_SHIFT) + i; +		 +#ifdef DRM_KMAP_ATOMIC_PROT_PFN +		/* kmap_atomic can't map IO pages on non-HIGHMEM kernels +		 */ +		vaddr = kmap_atomic_prot_pfn(pfn, KM_USER0, +					     __pgprot(__PAGE_KERNEL)); +#if WATCH_PWRITE +		DRM_INFO("pwrite i %d o %d l %d pfn %ld vaddr %p\n", +			 i, o, l, pfn, vaddr); +#endif +		unwritten = __copy_from_user_inatomic_nocache(vaddr + o, user_data, l); +		kunmap_atomic(vaddr, KM_USER0); + +		if (unwritten) +#endif +		{ +			vaddr = ioremap(pfn << PAGE_SHIFT, PAGE_SIZE); +#if WATCH_PWRITE +			DRM_INFO("pwrite slow i %d o %d l %d pfn %ld vaddr %p\n", +				 i, o, l, pfn, vaddr); +#endif +			if (vaddr == NULL) { +				ret = -EFAULT; +				goto fail; +			} +			unwritten = __copy_from_user(vaddr + o, user_data, l); +#if WATCH_PWRITE +			DRM_INFO("unwritten %ld\n", unwritten); +#endif +			iounmap(vaddr); +			if (unwritten) { +				ret = -EFAULT; +				goto fail; +			} +		} + +		remain -= l; +		user_data += l; +		offset += l; +	} +#if WATCH_PWRITE && 1 +	i915_gem_clflush_object(obj); +	i915_gem_dump_object(obj, args->offset + args->size, __func__, ~0); +	i915_gem_clflush_object(obj); +#endif + +fail: +	i915_gem_object_unpin (obj); +	drm_gem_object_unreference(obj); +	mutex_unlock(&dev->struct_mutex); + +#if WATCH_PWRITE +	if (ret) +		DRM_INFO("pwrite failed %d\n", ret); +#endif +	return ret; +} + +/** + * Called when user space prepares to use an object + */ +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_gem_object *obj; +	int ret; + +	if (!(dev->driver->driver_features & DRIVER_GEM)) +		return -ENODEV; + +	obj = drm_gem_object_lookup(dev, file_priv, args->handle); +	if (obj == NULL) +		return -EINVAL; + +	mutex_lock(&dev->struct_mutex); +	ret = i915_gem_set_domain(obj, file_priv, +				  args->read_domains, args->write_domain); +	drm_gem_object_unreference(obj); +	mutex_unlock(&dev->struct_mutex); +	return ret; +} + +/** + * Called when user space has done writes to this buffer + */ +int +i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data, +		      struct drm_file *file_priv) +{ +	struct drm_i915_gem_sw_finish *args = data; +	struct drm_gem_object *obj; +	struct drm_i915_gem_object *obj_priv; +	int ret = 0; + +	if (!(dev->driver->driver_features & DRIVER_GEM)) +		return -ENODEV; + +	mutex_lock(&dev->struct_mutex); +	obj = drm_gem_object_lookup(dev, file_priv, args->handle); +	if (obj == NULL) { +		mutex_unlock(&dev->struct_mutex); +		return -EINVAL; +	} + +#if WATCH_BUF +	DRM_INFO("%s: sw_finish %d (%p)\n", +		 __func__, args->handle, obj); +#endif +    	obj_priv = obj->driver_private; +		 +    	/** Pinned buffers may be scanout, so flush the cache +    	 */ +    	if ((obj->write_domain & I915_GEM_DOMAIN_CPU) && obj_priv->pin_count) { +    		i915_gem_clflush_object(obj); +    		drm_agp_chipset_flush(dev); +    	} +	drm_gem_object_unreference(obj); +	mutex_unlock(&dev->struct_mutex); +	return ret; +} + +/** + * Maps the contents of an object, returning the address it is mapped + * into. + * + * While the mapping holds a reference on the contents of the object, it doesn't + * imply a ref on the object itself. + */ +int +i915_gem_mmap_ioctl(struct drm_device *dev, void *data, +		   struct drm_file *file_priv) +{ +	struct drm_i915_gem_mmap *args = data; +	struct drm_gem_object *obj; +	loff_t offset; +	unsigned long addr; + +	if (!(dev->driver->driver_features & DRIVER_GEM)) +		return -ENODEV; + +	obj = drm_gem_object_lookup(dev, file_priv, args->handle); +	if (obj == NULL) +		return -EINVAL; + +	offset = args->offset; + +	down_write(¤t->mm->mmap_sem); +	addr = do_mmap(obj->filp, 0, args->size, +		       PROT_READ | PROT_WRITE, MAP_SHARED, +		       args->offset); +	up_write(¤t->mm->mmap_sem); +	mutex_lock(&dev->struct_mutex); +	drm_gem_object_unreference(obj); +	mutex_unlock(&dev->struct_mutex); +	if (IS_ERR((void *)addr)) +		return addr; + +	args->addr_ptr = (uint64_t) addr; + +	return 0; +} + +static void +i915_gem_object_free_page_list(struct drm_gem_object *obj) +{ +	struct drm_i915_gem_object *obj_priv = obj->driver_private; +	int page_count = obj->size / PAGE_SIZE; +	int i; + +	if (obj_priv->page_list == NULL) +		return; + + +	for (i = 0; i < page_count; i++) +		if (obj_priv->page_list[i] != NULL) { +			if (obj_priv->dirty) +				set_page_dirty(obj_priv->page_list[i]); +			mark_page_accessed(obj_priv->page_list[i]); +			page_cache_release(obj_priv->page_list[i]); +		} +	obj_priv->dirty = 0; + +	drm_free(obj_priv->page_list, +		 page_count * sizeof(struct page *), +		 DRM_MEM_DRIVER); +	obj_priv->page_list = NULL; +} + +static void +i915_gem_object_move_to_active(struct drm_gem_object *obj) +{ +	struct drm_device *dev = obj->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_gem_object *obj_priv = obj->driver_private; + +	/* Add a reference if we're newly entering the active list. */ +	if (!obj_priv->active) { +		drm_gem_object_reference(obj); +		obj_priv->active = 1; +	} +	/* Move from whatever list we were on to the tail of execution. */ +	list_move_tail(&obj_priv->list, +		       &dev_priv->mm.active_list); +} + +#if WATCH_INACTIVE +static void +i915_verify_inactive(struct drm_device *dev, char *file, int line) +{ +	drm_i915_private_t *dev_priv = dev->dev_private; +	struct drm_gem_object *obj; +	struct drm_i915_gem_object *obj_priv; + +	list_for_each_entry(obj_priv, &dev_priv->mm.inactive_list, list) { +		obj = obj_priv->obj; +		if (obj_priv->pin_count || obj_priv->active || (obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) +			DRM_ERROR("inactive %p (p %d a %d w %x)  %s:%d\n", +				  obj, +				  obj_priv->pin_count, obj_priv->active, obj->write_domain, file, line); +	} +} +#else +#define i915_verify_inactive(dev,file,line) +#endif + +static void +i915_gem_object_move_to_inactive(struct drm_gem_object *obj) +{ +	struct drm_device *dev = obj->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_gem_object *obj_priv = obj->driver_private; + +	i915_verify_inactive(dev, __FILE__, __LINE__); +	if (obj_priv->pin_count != 0) +		list_del_init(&obj_priv->list); +	else +		list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list); + +	if (obj_priv->active) { +		obj_priv->active = 0; +		drm_gem_object_unreference(obj); +	} +	i915_verify_inactive(dev, __FILE__, __LINE__); +} + +/** + * Creates a new sequence number, emitting a write of it to the status page + * plus an interrupt, which will trigger i915_user_interrupt_handler. + * + * Must be called with struct_lock held. + * + * Returned sequence numbers are nonzero on success. + */ +static uint32_t +i915_add_request(struct drm_device *dev, uint32_t flush_domains) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_gem_request *request; +	uint32_t seqno; +	int was_empty; +	RING_LOCALS; + +	request = drm_calloc(1, sizeof(*request), DRM_MEM_DRIVER); +	if (request == NULL) +		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(GFX_OP_USER_INTERRUPT); +	ADVANCE_LP_RING(); + +	DRM_DEBUG("%d\n", seqno); + +	request->seqno = seqno; +	request->emitted_jiffies = jiffies; +	request->flush_domains = flush_domains; +	was_empty = list_empty(&dev_priv->mm.request_list); +	list_add_tail(&request->list, &dev_priv->mm.request_list); + +	if (was_empty) +		schedule_delayed_work (&dev_priv->mm.retire_work, HZ); +	return seqno; +} + +/** + * Command execution barrier + * + * Ensures that all commands in the ring are finished + * before signalling the CPU + */ + +uint32_t +i915_retire_commands(struct drm_device *dev) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	uint32_t cmd = MI_FLUSH | MI_NO_WRITE_FLUSH; +	uint32_t flush_domains = 0; +	RING_LOCALS; + +	/* The sampler always gets flushed on i965 (sigh) */ +	if (IS_I965G(dev)) +		flush_domains |= I915_GEM_DOMAIN_SAMPLER; +	BEGIN_LP_RING(2); +	OUT_RING(cmd); +	OUT_RING(0); /* noop */ +	ADVANCE_LP_RING(); +	return flush_domains; +} + +/** + * Moves buffers associated only with the given active seqno from the active + * to inactive list, potentially freeing them. + */ +static void +i915_gem_retire_request(struct drm_device *dev, +			struct drm_i915_gem_request *request) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; + +	if (request->flush_domains != 0) { +		struct drm_i915_gem_object *obj_priv, *next; + +		/* First clear any buffers that were only waiting for a flush +		 * matching the one just retired. +		 */ + +		list_for_each_entry_safe(obj_priv, next, +					 &dev_priv->mm.flushing_list, list) { +			struct drm_gem_object *obj = obj_priv->obj; + +			if (obj->write_domain & request->flush_domains) { +				obj->write_domain = 0; +				i915_gem_object_move_to_inactive(obj); +			} +		} + +	} + +	/* Move any buffers on the active list that are no longer referenced +	 * by the ringbuffer to the flushing/inactive lists as appropriate. +	 */ +	while (!list_empty(&dev_priv->mm.active_list)) { +		struct drm_gem_object *obj; +		struct drm_i915_gem_object *obj_priv; + +		obj_priv = list_first_entry(&dev_priv->mm.active_list, +					    struct drm_i915_gem_object, +					    list); +		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) +			return; +#if WATCH_LRU +		DRM_INFO("%s: retire %d moves to inactive list %p\n", +			 __func__, request->seqno, obj); +#endif + +		if (obj->write_domain != 0) { +			list_move_tail(&obj_priv->list, +				       &dev_priv->mm.flushing_list); +		} else { +			i915_gem_object_move_to_inactive(obj); +		} +	} +} + +/** + * Returns true if seq1 is later than seq2. + */ +static int +i915_seqno_passed(uint32_t seq1, uint32_t seq2) +{ +	return (int32_t)(seq1 - seq2) >= 0; +} + +uint32_t +i915_get_gem_seqno(struct drm_device *dev) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; + +	return READ_HWSP(dev_priv, I915_GEM_HWS_INDEX); +} + +/** + * This function clears the request list as sequence numbers are passed. + */ +void +i915_gem_retire_requests(struct drm_device *dev) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	uint32_t seqno; + +	seqno = i915_get_gem_seqno(dev); + +	while (!list_empty(&dev_priv->mm.request_list)) { +		struct drm_i915_gem_request *request; +		uint32_t retiring_seqno; + +		request = list_first_entry(&dev_priv->mm.request_list, +					   struct drm_i915_gem_request, +					   list); +		retiring_seqno = request->seqno; + +		if (i915_seqno_passed(seqno, retiring_seqno) || dev_priv->mm.wedged) { +			i915_gem_retire_request(dev, request); + +			list_del(&request->list); +			drm_free(request, sizeof(*request), DRM_MEM_DRIVER); +		} else +			break; +	} +} + +void +i915_gem_retire_work_handler(struct work_struct *work) +{ +	struct drm_i915_private *dev_priv; +	struct drm_device *dev; + +	dev_priv = container_of(work, struct drm_i915_private, +				mm.retire_work.work); +	dev = dev_priv->dev; + +	mutex_lock(&dev->struct_mutex); +	i915_gem_retire_requests(dev); +	if (!list_empty(&dev_priv->mm.request_list)) +		schedule_delayed_work (&dev_priv->mm.retire_work, HZ); +	mutex_unlock(&dev->struct_mutex); +} + +/** + * Waits for a sequence number to be signaled, and cleans up the + * request and object lists appropriately for that event. + */ +int +i915_wait_request(struct drm_device *dev, uint32_t seqno) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	int ret = 0; + +	BUG_ON(seqno == 0); + +	if (!i915_seqno_passed(i915_get_gem_seqno(dev), seqno)) { +		dev_priv->mm.waiting_gem_seqno = seqno; +		i915_user_irq_on(dev); +		ret = wait_event_interruptible(dev_priv->irq_queue, +					       i915_seqno_passed(i915_get_gem_seqno(dev), +								 seqno) || dev_priv->mm.wedged); +		i915_user_irq_off(dev); +		dev_priv->mm.waiting_gem_seqno = 0; +	} +	if (dev_priv->mm.wedged) +		ret = -EIO; + +	if (ret) +		DRM_ERROR("%s returns %d (awaiting %d at %d)\n", +			  __func__, ret, seqno, i915_get_gem_seqno(dev)); + +	/* 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); + +	return ret; +} + +static void +i915_gem_flush(struct drm_device *dev, +	       uint32_t invalidate_domains, +	       uint32_t flush_domains) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	uint32_t cmd; +	RING_LOCALS; + +#if WATCH_EXEC +	DRM_INFO("%s: invalidate %08x flush %08x\n", __func__, +		  invalidate_domains, flush_domains); +#endif + +	if (flush_domains & I915_GEM_DOMAIN_CPU) +		drm_agp_chipset_flush(dev); + +	if ((invalidate_domains|flush_domains) & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) { +		/* +		 * 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; +		if (!IS_I965G(dev)) { +			/* +			 * On the 965, the sampler cache always gets flushed +			 * and this bit is reserved. +			 */ +			if (invalidate_domains & I915_GEM_DOMAIN_SAMPLER) +				cmd |= MI_READ_FLUSH; +		} +		if (invalidate_domains & I915_GEM_DOMAIN_INSTRUCTION) +			cmd |= MI_EXE_FLUSH; + +#if WATCH_EXEC +		DRM_INFO("%s: queue flush %08x to ring\n", __func__, cmd); +#endif +		BEGIN_LP_RING(2); +		OUT_RING(cmd); +		OUT_RING(0); /* noop */ +		ADVANCE_LP_RING(); +	} +} + +/** + * Ensures that all rendering to the object has completed and the object is + * safe to unbind from the GTT or access from the CPU. + */ +static int +i915_gem_object_wait_rendering(struct drm_gem_object *obj) +{ +	struct drm_device *dev = obj->dev; +	struct drm_i915_gem_object *obj_priv = obj->driver_private; +	int ret; + +	/* If there are writes queued to the buffer, flush and +	 * create a new seqno to wait for. +	 */ +	if (obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) { +		uint32_t write_domain = obj->write_domain; +#if WATCH_BUF +		DRM_INFO("%s: flushing object %p from write domain %08x\n", +			  __func__, obj, write_domain); +#endif +		i915_gem_flush(dev, 0, write_domain); +		obj->write_domain = 0; + +		i915_gem_object_move_to_active(obj); +		obj_priv->last_rendering_seqno = i915_add_request(dev, +								  write_domain); +		BUG_ON(obj_priv->last_rendering_seqno == 0); +#if WATCH_LRU +		DRM_INFO("%s: flush moves to exec list %p\n", __func__, obj); +#endif +	} +	/* If there is rendering queued on the buffer being evicted, wait for +	 * it. +	 */ +	if (obj_priv->active) { +#if WATCH_BUF +		DRM_INFO("%s: object %p wait for seqno %08x\n", +			  __func__, obj, obj_priv->last_rendering_seqno); +#endif +		ret = i915_wait_request(dev, obj_priv->last_rendering_seqno); +		if (ret != 0) +			return ret; +	} + +	return 0; +} + +/** + * Unbinds an object from the GTT aperture. + */ +static int +i915_gem_object_unbind(struct drm_gem_object *obj) +{ +	struct drm_device *dev = obj->dev; +	struct drm_i915_gem_object *obj_priv = obj->driver_private; +	int ret = 0; + +#if WATCH_BUF +	DRM_INFO("%s:%d %p\n", __func__, __LINE__, obj); +	DRM_INFO("gtt_space %p\n", obj_priv->gtt_space); +#endif +	if (obj_priv->gtt_space == NULL) +		return 0; + +	if (obj_priv->pin_count != 0) { +		DRM_ERROR("Attempting to unbind pinned buffer\n"); +		return -EINVAL; +	} + +	/* Wait for any rendering to complete +	 */ +	ret = i915_gem_object_wait_rendering(obj); +	if (ret) { +		DRM_ERROR ("wait_rendering failed: %d\n", ret); +		return ret; +	} + +	/* 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_domain(obj, I915_GEM_DOMAIN_CPU, +					 I915_GEM_DOMAIN_CPU); +	if (ret) { +		DRM_ERROR("set_domain failed: %d\n", ret); +		return ret; +	} + +	if (obj_priv->agp_mem != NULL) { +		drm_unbind_agp(obj_priv->agp_mem); +		drm_free_agp(obj_priv->agp_mem, obj->size / PAGE_SIZE); +		obj_priv->agp_mem = NULL; +	} + +	BUG_ON(obj_priv->active); + +	i915_gem_object_free_page_list(obj); + +	if (obj_priv->gtt_space) { +		atomic_dec(&dev->gtt_count); +		atomic_sub(obj->size, &dev->gtt_memory); +	 +		drm_memrange_put_block(obj_priv->gtt_space); +		obj_priv->gtt_space = NULL; +	} + +	/* Remove ourselves from the LRU list if present. */ +	if (!list_empty(&obj_priv->list)) +		list_del_init(&obj_priv->list); + +	return 0; +} + +#if WATCH_BUF | WATCH_EXEC | WATCH_PWRITE +static void +i915_gem_dump_page(struct page *page, uint32_t start, uint32_t end, +		   uint32_t bias, uint32_t mark) +{ +	uint32_t *mem = kmap_atomic(page, KM_USER0); +	int i; +	for (i = start; i < end; i += 4) +		DRM_INFO("%08x: %08x%s\n", +			  (int) (bias + i), mem[i / 4], +			  (bias + i == mark) ? " ********" : ""); +	kunmap_atomic(mem, KM_USER0); +	/* give syslog time to catch up */ +	msleep(1); +} + +static void +i915_gem_dump_object(struct drm_gem_object *obj, int len, +		     const char *where, uint32_t mark) +{ +	struct drm_i915_gem_object *obj_priv = obj->driver_private; +	int page; + +	DRM_INFO("%s: object at offset %08x\n", where, obj_priv->gtt_offset); +	for (page = 0; page < (len + PAGE_SIZE-1) / PAGE_SIZE; page++) { +		int page_len, chunk, chunk_len; + +		page_len = len - page * PAGE_SIZE; +		if (page_len > PAGE_SIZE) +			page_len = PAGE_SIZE; + +		for (chunk = 0; chunk < page_len; chunk += 128) { +			chunk_len = page_len - chunk; +			if (chunk_len > 128) +				chunk_len = 128; +			i915_gem_dump_page(obj_priv->page_list[page], +					   chunk, chunk + chunk_len, +					   obj_priv->gtt_offset + +					   page * PAGE_SIZE, +					   mark); +		} +	} +} +#endif + +#if WATCH_LRU +static void +i915_dump_lru(struct drm_device *dev, const char *where) +{ +	struct drm_i915_private		*dev_priv = dev->dev_private; +	struct drm_i915_gem_object	*obj_priv; + +	DRM_INFO("active list %s {\n", where); +	list_for_each_entry(obj_priv, &dev_priv->mm.active_list, +			    list) +	{ +		DRM_INFO("    %p: %08x\n", obj_priv, +			 obj_priv->last_rendering_seqno); +	} +	DRM_INFO("}\n"); +	DRM_INFO("flushing list %s {\n", where); +	list_for_each_entry(obj_priv, &dev_priv->mm.flushing_list, +			    list) +	{ +		DRM_INFO("    %p: %08x\n", obj_priv, +			 obj_priv->last_rendering_seqno); +	} +	DRM_INFO("}\n"); +	DRM_INFO("inactive %s {\n", where); +	list_for_each_entry(obj_priv, &dev_priv->mm.inactive_list, list) { +		DRM_INFO("    %p: %08x\n", obj_priv, +			 obj_priv->last_rendering_seqno); +	} +	DRM_INFO("}\n"); +} +#endif + +static int +i915_gem_evict_something(struct drm_device *dev) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_gem_object *obj; +	struct drm_i915_gem_object *obj_priv; +	int ret = 0; + +	for (;;) { +		/* If there's an inactive buffer available now, grab it +		 * and be done. +		 */ +		if (!list_empty(&dev_priv->mm.inactive_list)) { +			obj_priv = list_first_entry(&dev_priv->mm.inactive_list, +						    struct drm_i915_gem_object, +						    list); +			obj = obj_priv->obj; +			BUG_ON(obj_priv->pin_count != 0); +#if WATCH_LRU +			DRM_INFO("%s: evicting %p\n", __func__, obj); +#endif +			BUG_ON(obj_priv->active); + +			/* Wait on the rendering and unbind the buffer. */ +			ret = i915_gem_object_unbind(obj); +			break; +		} + +		/* 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. +		 */ +		if (!list_empty(&dev_priv->mm.request_list)) { +			struct drm_i915_gem_request *request; + +			request = list_first_entry(&dev_priv->mm.request_list, +						   struct drm_i915_gem_request, +						   list); + +			ret = i915_wait_request(dev, request->seqno); +			if (ret) +				break; + +			/* if waiting caused an object to become inactive, +			 * then loop around and wait for it. Otherwise, we +			 * assume that waiting freed and unbound something, +			 * so there should now be some space in the GTT +			 */ +			if (!list_empty(&dev_priv->mm.inactive_list)) +				continue; +			break; +		} + +		/* 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. +		 */ +		if (!list_empty(&dev_priv->mm.flushing_list)) { +			obj_priv = list_first_entry(&dev_priv->mm.flushing_list, +						    struct drm_i915_gem_object, +						    list); +			obj = obj_priv->obj; + +			i915_gem_flush(dev, +				       obj->write_domain, +				       obj->write_domain); +			i915_add_request(dev, obj->write_domain); + +			obj = NULL; +			continue; +		} + +		DRM_ERROR("inactive empty %d request empty %d flushing empty %d\n", +			  list_empty(&dev_priv->mm.inactive_list), +			  list_empty(&dev_priv->mm.request_list), +			  list_empty(&dev_priv->mm.flushing_list)); +		/* If we didn't do any of the above, there's nothing to be done +		 * and we just can't fit it in. +		 */ +		return -ENOMEM; +	} +	return ret; +} + +static int +i915_gem_object_get_page_list(struct drm_gem_object *obj) +{ +	struct drm_i915_gem_object *obj_priv = obj->driver_private; +	int page_count, i; +	struct address_space *mapping; +	struct inode *inode; +	struct page *page; +	int ret; +	 +	if (obj_priv->page_list) +		return 0; + +	/* Get the list of pages out of our struct file.  They'll be pinned +	 * at this point until we release them. +	 */ +	page_count = obj->size / PAGE_SIZE; +	BUG_ON(obj_priv->page_list != NULL); +	obj_priv->page_list = drm_calloc(page_count, sizeof(struct page *), +					 DRM_MEM_DRIVER); +	if (obj_priv->page_list == NULL) { +		DRM_ERROR("Faled to allocate page list\n"); +		return -ENOMEM; +	} + +	inode = obj->filp->f_path.dentry->d_inode; +	mapping = inode->i_mapping; +	for (i = 0; i < page_count; i++) { +		page = find_get_page(mapping, i); +		if (page == NULL || !PageUptodate(page)) { +			if (page) { +				page_cache_release(page); +				page = NULL; +			} +			ret = shmem_getpage(inode, i, &page, SGP_DIRTY, NULL); +	 +			if (ret) { +				DRM_ERROR("shmem_getpage failed: %d\n", ret); +				i915_gem_object_free_page_list(obj); +				return ret; +			} +			unlock_page(page); +		} +		obj_priv->page_list[i] = page; +	} +	return 0; +} + +/** + * Finds free space in the GTT aperture and binds the object there. + */ +static int +i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment) +{ +	struct drm_device *dev = obj->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_gem_object *obj_priv = obj->driver_private; +	struct drm_memrange_node *free_space; +	int page_count, ret; + +	if (alignment == 0) +		alignment = PAGE_SIZE; +	if (alignment & (PAGE_SIZE - 1)) { +		DRM_ERROR("Invalid object alignment requested %u\n", alignment); +		return -EINVAL; +	} + + search_free: +	free_space = drm_memrange_search_free(&dev_priv->mm.gtt_space, +					      obj->size, +					      alignment, 0); +	if (free_space != NULL) { +		obj_priv->gtt_space = +			drm_memrange_get_block(free_space, obj->size, +					       alignment); +		if (obj_priv->gtt_space != NULL) { +			obj_priv->gtt_space->private = obj; +			obj_priv->gtt_offset = obj_priv->gtt_space->start; +		} +	} +	if (obj_priv->gtt_space == NULL) { +		/* If the gtt is empty and we're still having trouble +		 * fitting our object in, we're out of memory. +		 */ +#if WATCH_LRU +		DRM_INFO("%s: GTT full, evicting something\n", __func__); +#endif +		if (list_empty(&dev_priv->mm.inactive_list) && +		    list_empty(&dev_priv->mm.flushing_list) && +		    list_empty(&dev_priv->mm.active_list)) { +			DRM_ERROR("GTT full, but LRU list empty\n"); +			return -ENOMEM; +		} + +		ret = i915_gem_evict_something(dev); +		if (ret != 0) { +			DRM_ERROR("Failed to evict a buffer %d\n", ret); +			return ret; +		} +		goto search_free; +	} + +#if WATCH_BUF +	DRM_INFO("Binding object of size %d at 0x%08x\n", +		 obj->size, obj_priv->gtt_offset); +#endif +	ret = i915_gem_object_get_page_list(obj); +	if (ret) { +		drm_memrange_put_block(obj_priv->gtt_space); +		obj_priv->gtt_space = NULL; +		return ret; +	} + +	page_count = obj->size / PAGE_SIZE; +	/* Create an AGP memory structure pointing at our pages, and bind it +	 * into the GTT. +	 */ +	obj_priv->agp_mem = drm_agp_bind_pages(dev, +					       obj_priv->page_list, +					       page_count, +					       obj_priv->gtt_offset); +	if (obj_priv->agp_mem == NULL) { +		i915_gem_object_free_page_list(obj); +		drm_memrange_put_block(obj_priv->gtt_space); +		obj_priv->gtt_space = NULL; +		return -ENOMEM; +	} +	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 +	 */ +	BUG_ON(obj->read_domains & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)); +	BUG_ON(obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)); + +	return 0; +} + +static void +i915_gem_clflush_object(struct drm_gem_object *obj) +{ +	struct drm_i915_gem_object	*obj_priv = obj->driver_private; + +	/* If we don't have a page list set up, then we're not pinned +	 * to GPU, and we can ignore the cache flush because it'll happen +	 * again at bind time. +	 */ +	if (obj_priv->page_list == NULL) +		return; + +	drm_ttm_cache_flush(obj_priv->page_list, obj->size / PAGE_SIZE); +} + +/* + * 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 + *		wait_rendering (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 + */ +static int +i915_gem_object_set_domain(struct drm_gem_object *obj, +			    uint32_t read_domains, +			    uint32_t write_domain) +{ +	struct drm_device		*dev = obj->dev; +	struct drm_i915_gem_object	*obj_priv = obj->driver_private; +	uint32_t			invalidate_domains = 0; +	uint32_t			flush_domains = 0; +	int				ret; + +#if WATCH_BUF +	DRM_INFO("%s: object %p read %08x -> %08x write %08x -> %08x\n", +		 __func__, obj,  +		 obj->read_domains, read_domains,  +		 obj->write_domain, write_domain); +#endif +	/* +	 * If the object isn't moving to a new write domain, +	 * let the object stay in multiple read domains +	 */ +	if (write_domain == 0) +		read_domains |= obj->read_domains; +	else +		obj_priv->dirty = 1; + +	/* +	 * 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 != read_domains) { +		flush_domains |= obj->write_domain; +		invalidate_domains |= read_domains & ~obj->write_domain; +	} +	/* +	 * Invalidate any read caches which may have +	 * stale data. That is, any new read domains. +	 */ +	invalidate_domains |= read_domains & ~obj->read_domains; +	if ((flush_domains | invalidate_domains) & I915_GEM_DOMAIN_CPU) { +#if WATCH_BUF +		DRM_INFO("%s: CPU domain flush %08x invalidate %08x\n", +			 __func__, flush_domains, invalidate_domains); +#endif +		/* +		 * If we're invaliding the CPU cache and flushing a GPU cache, +		 * then pause for rendering so that the GPU caches will be +		 * flushed before the cpu cache is invalidated +		 */ +		if ((invalidate_domains & I915_GEM_DOMAIN_CPU) && +		    (flush_domains & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT))) { +			ret = i915_gem_object_wait_rendering(obj); +			if (ret) +				return ret; +		} +		i915_gem_clflush_object(obj); +	} + +	if ((write_domain | flush_domains) != 0) +		obj->write_domain = write_domain; +	obj->read_domains = read_domains; +	dev->invalidate_domains |= invalidate_domains; +	dev->flush_domains |= flush_domains; +#if WATCH_BUF +	DRM_INFO("%s: read %08x write %08x invalidate %08x flush %08x\n", +		 __func__, +		 obj->read_domains, obj->write_domain, +		 dev->invalidate_domains, dev->flush_domains); +#endif +	return 0; +} + +/** + * Once all of the objects have been set in the proper domain, + * perform the necessary flush and invalidate operations. + * + * Returns the write domains flushed, for use in flush tracking. + */ +static uint32_t +i915_gem_dev_set_domain(struct drm_device *dev) +{ +	uint32_t flush_domains = dev->flush_domains; + +	/* +	 * Now that all the buffers are synced to the proper domains, +	 * flush and invalidate the collected domains +	 */ +	if (dev->invalidate_domains | dev->flush_domains) { +#if WATCH_EXEC +		DRM_INFO("%s: invalidate_domains %08x flush_domains %08x\n", +			  __func__, +			 dev->invalidate_domains, +			 dev->flush_domains); +#endif +		i915_gem_flush(dev, +			       dev->invalidate_domains, +			       dev->flush_domains); +		dev->invalidate_domains = 0; +		dev->flush_domains = 0; +	} + +	return flush_domains; +} + +#if WATCH_COHERENCY +static void +i915_gem_object_check_coherency(struct drm_gem_object *obj, int handle) +{ +	struct drm_device *dev = obj->dev; +	struct drm_i915_gem_object *obj_priv = obj->driver_private; +	int page; +	uint32_t *gtt_mapping; +	uint32_t *backing_map = NULL; +	int bad_count = 0; + +	DRM_INFO("%s: checking coherency of object %p@0x%08x (%d, %dkb):\n", +		 __func__, obj, obj_priv->gtt_offset, handle, +		 obj->size / 1024); + +	gtt_mapping = ioremap(dev->agp->base + obj_priv->gtt_offset, +			      obj->size); +	if (gtt_mapping == NULL) { +		DRM_ERROR("failed to map GTT space\n"); +		return; +	} + +	for (page = 0; page < obj->size / PAGE_SIZE; page++) { +		int i; + +		backing_map = kmap_atomic(obj_priv->page_list[page], KM_USER0); + +		if (backing_map == NULL) { +			DRM_ERROR("failed to map backing page\n"); +			goto out; +		} + +		for (i = 0; i < PAGE_SIZE / 4; i++) { +			uint32_t cpuval = backing_map[i]; +			uint32_t gttval = readl(gtt_mapping + +						page * 1024 + i); + +			if (cpuval != gttval) { +				DRM_INFO("incoherent CPU vs GPU at 0x%08x: " +					 "0x%08x vs 0x%08x\n", +					 (int)(obj_priv->gtt_offset + +					       page * PAGE_SIZE + i * 4), +					 cpuval, gttval); +				if (bad_count++ >= 8) { +					DRM_INFO("...\n"); +					goto out; +				} +			} +		} +		kunmap_atomic(backing_map, KM_USER0); +		backing_map = NULL; +	} + + out: +	if (backing_map != NULL) +		kunmap_atomic(backing_map, KM_USER0); +	iounmap(gtt_mapping); + +	/* give syslog time to catch up */ +	msleep(1); + +	/* Directly flush the object, since we just loaded values with the CPU +	 * from thebacking pages and we don't want to disturb the cache +	 * management that we're trying to observe. +	 */ + +	i915_gem_clflush_object(obj); +} +#endif + +/** + * Pin an object to the GTT and evaluate the relocations landing in it. + */ +static int +i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, +				 struct drm_file *file_priv, +				 struct drm_i915_gem_exec_object *entry) +{ +	struct drm_device *dev = obj->dev; +	struct drm_i915_gem_relocation_entry reloc; +	struct drm_i915_gem_relocation_entry __user *relocs; +	struct drm_i915_gem_object *obj_priv = obj->driver_private; +	int i, ret; +	uint32_t last_reloc_offset = -1; +	void *reloc_page = NULL; + +	/* Choose the GTT offset for our buffer and put it there. */ +	ret = i915_gem_object_pin(obj, (uint32_t) entry->alignment); +	if (ret) +		return ret; + +	entry->offset = obj_priv->gtt_offset; + +	relocs = (struct drm_i915_gem_relocation_entry __user *) +		 (uintptr_t) entry->relocs_ptr; +	/* Apply the relocations, using the GTT aperture to avoid cache +	 * flushing requirements. +	 */ +	for (i = 0; i < entry->relocation_count; i++) { +		struct drm_gem_object *target_obj; +		struct drm_i915_gem_object *target_obj_priv; +		uint32_t reloc_val, reloc_offset, *reloc_entry; +		int ret; + +		ret = copy_from_user(&reloc, relocs + i, sizeof(reloc)); +		if (ret != 0) { +			i915_gem_object_unpin(obj); +			return ret; +		} + +		target_obj = drm_gem_object_lookup(obj->dev, file_priv, +						   reloc.target_handle); +		if (target_obj == NULL) { +			i915_gem_object_unpin(obj); +			return -EINVAL; +		} +		target_obj_priv = target_obj->driver_private; + +		/* 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->gtt_space == NULL) { +			DRM_ERROR("No GTT space found for object %d\n", +				  reloc.target_handle); +			drm_gem_object_unreference(target_obj); +			i915_gem_object_unpin(obj); +			return -EINVAL; +		} + +		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); +			drm_gem_object_unreference(target_obj); +			i915_gem_object_unpin(obj); +			return -EINVAL; +		} +		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); +			drm_gem_object_unreference(target_obj); +			i915_gem_object_unpin(obj); +			return -EINVAL; +		} + +		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); +			drm_gem_object_unreference(target_obj); +			i915_gem_object_unpin(obj); +			return -EINVAL; +		} + +#if WATCH_RELOC +		DRM_INFO("%s: obj %p offset %08x target %d " +			 "read %08x write %08x gtt %08x " +			 "presumed %08x delta %08x\n", +			 __func__, +			 obj, +			 (int) reloc.offset, +			 (int) reloc.target_handle, +			 (int) reloc.read_domains, +			 (int) reloc.write_domain, +			 (int) target_obj_priv->gtt_offset, +			 (int) reloc.presumed_offset, +			 reloc.delta); +#endif + +		target_obj->pending_read_domains |= reloc.read_domains; +		target_obj->pending_write_domain |= reloc.write_domain; + +		/* If the relocation already has the right value in it, no +		 * more work needs to be done. +		 */ +		if (target_obj_priv->gtt_offset == reloc.presumed_offset) { +			drm_gem_object_unreference(target_obj); +			continue; +		} + +		/* Now that we're going to actually write some data in, +		 * make sure that any rendering using this buffer's contents +		 * is completed. +		 */ +		i915_gem_object_wait_rendering(obj); + +		/* As we're writing through the gtt, flush +		 * any CPU writes before we write the relocations +		 */ +		if (obj->write_domain & I915_GEM_DOMAIN_CPU) { +			i915_gem_clflush_object(obj); +			drm_agp_chipset_flush(dev); +			obj->write_domain = 0; +		} + +		/* Map the page containing the relocation we're going to +		 * perform. +		 */ +		reloc_offset = obj_priv->gtt_offset + reloc.offset; +		if (reloc_page == NULL || +		    (last_reloc_offset & ~(PAGE_SIZE - 1)) != +		    (reloc_offset & ~(PAGE_SIZE - 1))) { +			if (reloc_page != NULL) +				iounmap(reloc_page); + +			reloc_page = ioremap(dev->agp->base + +					     (reloc_offset & ~(PAGE_SIZE - 1)), +					     PAGE_SIZE); +			last_reloc_offset = reloc_offset; +			if (reloc_page == NULL) { +				drm_gem_object_unreference(target_obj); +				i915_gem_object_unpin(obj); +				return -ENOMEM; +			} +		} + +		reloc_entry = (uint32_t *)((char *)reloc_page + +					   (reloc_offset & (PAGE_SIZE - 1))); +		reloc_val = target_obj_priv->gtt_offset + reloc.delta; + +#if WATCH_BUF +		DRM_INFO("Applied relocation: %p@0x%08x %08x -> %08x\n", +			  obj, (unsigned int) reloc.offset, +			  readl(reloc_entry), reloc_val); +#endif +		writel(reloc_val, reloc_entry); + +		/* Write the updated presumed offset for this entry back out +		 * to the user. +		 */ +		reloc.presumed_offset = target_obj_priv->gtt_offset; +		ret = copy_to_user(relocs + i, &reloc, sizeof(reloc)); +		if (ret != 0) { +			drm_gem_object_unreference(target_obj); +			i915_gem_object_unpin(obj); +			return ret; +		} + +		drm_gem_object_unreference(target_obj); +	} + +	if (reloc_page != NULL) +		iounmap(reloc_page); + +#if WATCH_BUF +	if (0) +		i915_gem_dump_object(obj, 128, __func__, ~0); +#endif +	return 0; +} + +/** Dispatch a batchbuffer to the ring + */ +static int +i915_dispatch_gem_execbuffer(struct drm_device *dev, +			      struct drm_i915_gem_execbuffer *exec, +			      uint64_t exec_offset) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_clip_rect __user *boxes = (struct drm_clip_rect __user *) +					     (uintptr_t) exec->cliprects_ptr; +	int nbox = exec->num_cliprects; +	int i = 0, count; +	uint32_t	exec_start, exec_len; +	RING_LOCALS; + +	exec_start = (uint32_t) exec_offset + exec->batch_start_offset; +	exec_len = (uint32_t) exec->batch_len; + +	if ((exec_start | exec_len) & 0x7) { +		DRM_ERROR("alignment\n"); +		return -EINVAL; +	} + +	if (!exec_start) +		return -EINVAL; + +	count = nbox ? nbox : 1; + +	for (i = 0; i < count; i++) { +		if (i < nbox) { +			int ret = i915_emit_box(dev, boxes, i, +						exec->DR1, exec->DR4); +			if (ret) +				return ret; +		} + +		if (IS_I830(dev) || IS_845G(dev)) { +			BEGIN_LP_RING(4); +			OUT_RING(MI_BATCH_BUFFER); +			OUT_RING(exec_start | MI_BATCH_NON_SECURE); +			OUT_RING(exec_start + exec_len - 4); +			OUT_RING(0); +			ADVANCE_LP_RING(); +		} else { +			BEGIN_LP_RING(2); +			if (IS_I965G(dev)) { +				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); +			} +			ADVANCE_LP_RING(); +		} +	} + +	/* XXX breadcrumb */ +	return 0; +} + +/* 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. + */ +static int +i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file_priv) +{ +	struct drm_i915_file_private *i915_file_priv = file_priv->driver_priv; +	int ret = 0; +	uint32_t seqno; + +	mutex_lock(&dev->struct_mutex); +	seqno = i915_file_priv->mm.last_gem_throttle_seqno; +	i915_file_priv->mm.last_gem_throttle_seqno = i915_file_priv->mm.last_gem_seqno; +	if (seqno) +		ret = i915_wait_request(dev, seqno); +	mutex_unlock(&dev->struct_mutex); +	return ret; +} + +int +i915_gem_execbuffer(struct drm_device *dev, void *data, +		    struct drm_file *file_priv) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_file_private *i915_file_priv = file_priv->driver_priv; +	struct drm_i915_gem_execbuffer *args = data; +	struct drm_i915_gem_exec_object *exec_list = NULL; +	struct drm_gem_object **object_list = NULL; +	struct drm_gem_object *batch_obj; +	int ret, i, pinned = 0; +	uint64_t exec_offset; +	uint32_t seqno, flush_domains; + +#if WATCH_EXEC +	DRM_INFO("buffers_ptr %d buffer_count %d len %08x\n", +		  (int) args->buffers_ptr, args->buffer_count, args->batch_len); +#endif + +	/* Copy in the exec list from userland */ +	exec_list = drm_calloc(sizeof(*exec_list), args->buffer_count, +			       DRM_MEM_DRIVER); +	object_list = drm_calloc(sizeof(*object_list), args->buffer_count, +				 DRM_MEM_DRIVER); +	if (exec_list == NULL || object_list == NULL) { +		DRM_ERROR("Failed to allocate exec or object list " +			  "for %d buffers\n", +			  args->buffer_count); +		ret = -ENOMEM; +		goto pre_mutex_err; +	} +	ret = copy_from_user(exec_list, +			     (struct drm_i915_relocation_entry __user *) +			     (uintptr_t) args->buffers_ptr, +			     sizeof(*exec_list) * args->buffer_count); +	if (ret != 0) { +		DRM_ERROR("copy %d exec entries failed %d\n", +			  args->buffer_count, ret); +		goto pre_mutex_err; +	} + +	mutex_lock(&dev->struct_mutex); + +	i915_verify_inactive(dev, __FILE__, __LINE__); + +	if (dev_priv->mm.wedged) { +		DRM_ERROR("Execbuf while wedged\n"); +		mutex_unlock(&dev->struct_mutex); +		return -EIO; +	} +		 +	if (dev_priv->mm.suspended) { +		DRM_ERROR("Execbuf while VT-switched.\n"); +		mutex_unlock(&dev->struct_mutex); +		return -EBUSY; +	} + +	/* Zero the gloabl flush/invalidate flags. These +	 * will be modified as each object is bound to the +	 * gtt +	 */ +	dev->invalidate_domains = 0; +	dev->flush_domains = 0; + +	/* Look up object handles and perform the relocations */ +	for (i = 0; i < args->buffer_count; i++) { +		object_list[i] = drm_gem_object_lookup(dev, file_priv, +						       exec_list[i].handle); +		if (object_list[i] == NULL) { +			DRM_ERROR("Invalid object handle %d at index %d\n", +				   exec_list[i].handle, i); +			ret = -EINVAL; +			goto err; +		} + +		object_list[i]->pending_read_domains = 0; +		object_list[i]->pending_write_domain = 0; +		ret = i915_gem_object_pin_and_relocate(object_list[i], +						       file_priv, +						       &exec_list[i]); +		if (ret) { +			DRM_ERROR("object bind and relocate failed %d\n", ret); +			goto err; +		} +		pinned = i + 1; +	} + +	/* Set the pending read domains for the batch buffer to COMMAND */ +	batch_obj = object_list[args->buffer_count-1]; +	batch_obj->pending_read_domains = I915_GEM_DOMAIN_COMMAND; +	batch_obj->pending_write_domain = 0; + +	i915_verify_inactive(dev, __FILE__, __LINE__); + +	for (i = 0; i < args->buffer_count; i++) { +		struct drm_gem_object *obj = object_list[i]; +		struct drm_i915_gem_object *obj_priv = obj->driver_private; + +		if (obj_priv->gtt_space == NULL) { +			/* We evicted the buffer in the process of validating +			 * our set of buffers in.  We could try to recover by +			 * kicking them everything out and trying again from +			 * the start. +			 */ +			ret = -ENOMEM; +			goto err; +		} + +		/* make sure all previous memory operations have passed */ +		ret = i915_gem_object_set_domain(obj, +						 obj->pending_read_domains, +						 obj->pending_write_domain); +		if (ret) +			goto err; +	} + +	i915_verify_inactive(dev, __FILE__, __LINE__); + +	/* Flush/invalidate caches and chipset buffer */ +	flush_domains = i915_gem_dev_set_domain(dev); + +	i915_verify_inactive(dev, __FILE__, __LINE__); + +#if WATCH_COHERENCY +	for (i = 0; i < args->buffer_count; i++) { +		i915_gem_object_check_coherency(object_list[i], +						exec_list[i].handle); +	} +#endif + +	exec_offset = exec_list[args->buffer_count - 1].offset; + +#if WATCH_EXEC +	i915_gem_dump_object(object_list[args->buffer_count - 1], +			      args->batch_len, +			      __func__, +			      ~0); +#endif + +	/* Exec the batchbuffer */ +	ret = i915_dispatch_gem_execbuffer(dev, args, exec_offset); +	if (ret) { +		DRM_ERROR("dispatch failed %d\n", ret); +		goto err; +	} + +	/* +	 * Ensure that the commands in the batch buffer are +	 * finished before the interrupt fires +	 */ +	flush_domains |= i915_retire_commands(dev); + +	i915_verify_inactive(dev, __FILE__, __LINE__); + +	/* +	 * Get 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). +	 */ +	seqno = i915_add_request(dev, flush_domains); +	BUG_ON(seqno == 0); +	i915_file_priv->mm.last_gem_seqno = seqno; +	for (i = 0; i < args->buffer_count; i++) { +		struct drm_gem_object *obj = object_list[i]; +		struct drm_i915_gem_object *obj_priv = obj->driver_private; + +		i915_gem_object_move_to_active(obj); +		obj_priv->last_rendering_seqno = seqno; +#if WATCH_LRU +		DRM_INFO("%s: move to exec list %p\n", __func__, obj); +#endif +	} +#if WATCH_LRU +	i915_dump_lru(dev, __func__); +#endif + +	i915_verify_inactive(dev, __FILE__, __LINE__); + +	/* Copy the new buffer offsets back to the user's exec list. */ +	ret = copy_to_user((struct drm_i915_relocation_entry __user *) +			   (uintptr_t) args->buffers_ptr, +			   exec_list, +			   sizeof(*exec_list) * args->buffer_count); +	if (ret) +		DRM_ERROR("failed to copy %d exec entries " +			  "back to user (%d)\n", +			   args->buffer_count, ret); +err: +	if (object_list != NULL) { +		for (i = 0; i < pinned; i++) +			i915_gem_object_unpin(object_list[i]); + +		for (i = 0; i < args->buffer_count; i++) +			drm_gem_object_unreference(object_list[i]); +	} +	mutex_unlock(&dev->struct_mutex); + +pre_mutex_err: +	drm_free(object_list, sizeof(*object_list) * args->buffer_count, +		 DRM_MEM_DRIVER); +	drm_free(exec_list, sizeof(*exec_list) * args->buffer_count, +		 DRM_MEM_DRIVER); + +	return ret; +} + +int +i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment) +{ +	struct drm_device *dev = obj->dev; +	struct drm_i915_gem_object *obj_priv = obj->driver_private; +	int ret; + +	i915_verify_inactive(dev, __FILE__, __LINE__); +	if (obj_priv->gtt_space == NULL) { +		ret = i915_gem_object_bind_to_gtt(obj, alignment); +		if (ret != 0) { +			DRM_ERROR("Failure to bind: %d", ret); +			return ret; +		} +	} +	obj_priv->pin_count++; + +	/* 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 (!obj_priv->active && (obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) == 0 && +		    !list_empty(&obj_priv->list)) +			list_del_init(&obj_priv->list); +	} +	i915_verify_inactive(dev, __FILE__, __LINE__); + +	return 0; +} + +void +i915_gem_object_unpin(struct drm_gem_object *obj) +{ +	struct drm_device *dev = obj->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_gem_object *obj_priv = obj->driver_private; + +	i915_verify_inactive(dev, __FILE__, __LINE__); +	obj_priv->pin_count--; +	BUG_ON(obj_priv->pin_count < 0); +	BUG_ON(obj_priv->gtt_space == NULL); + +	/* 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 (!obj_priv->active && (obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) == 0) +			list_move_tail(&obj_priv->list, +				       &dev_priv->mm.inactive_list); +		atomic_dec(&dev->pin_count); +		atomic_sub(obj->size, &dev->pin_memory); +	} +	i915_verify_inactive(dev, __FILE__, __LINE__); +} + +int +i915_gem_pin_ioctl(struct drm_device *dev, void *data, +		   struct drm_file *file_priv) +{ +	struct drm_i915_gem_pin *args = data; +	struct drm_gem_object *obj; +	struct drm_i915_gem_object *obj_priv; +	int ret; + +	mutex_lock(&dev->struct_mutex); + +	obj = drm_gem_object_lookup(dev, file_priv, args->handle); +	if (obj == NULL) { +		DRM_ERROR("Bad handle in i915_gem_pin_ioctl(): %d\n", +			  args->handle); +		mutex_unlock(&dev->struct_mutex); +		return -EINVAL; +	} +	obj_priv = obj->driver_private; + +	ret = i915_gem_object_pin(obj, args->alignment); +	if (ret != 0) { +		drm_gem_object_unreference(obj); +		mutex_unlock(&dev->struct_mutex); +		return ret; +	} + +	/** XXX - flush the CPU caches for pinned objects +	 * as the X server doesn't manage domains yet +	 */ +	if (obj->write_domain & I915_GEM_DOMAIN_CPU) { +		i915_gem_clflush_object(obj); +		drm_agp_chipset_flush(dev); +		obj->write_domain = 0; +	} +	args->offset = obj_priv->gtt_offset; +	drm_gem_object_unreference(obj); +	mutex_unlock(&dev->struct_mutex); + +	return 0; +} + +int +i915_gem_unpin_ioctl(struct drm_device *dev, void *data, +		     struct drm_file *file_priv) +{ +	struct drm_i915_gem_pin *args = data; +	struct drm_gem_object *obj; + +	mutex_lock(&dev->struct_mutex); + +	obj = drm_gem_object_lookup(dev, file_priv, args->handle); +	if (obj == NULL) { +		DRM_ERROR("Bad handle in i915_gem_unpin_ioctl(): %d\n", +			  args->handle); +		mutex_unlock(&dev->struct_mutex); +		return -EINVAL; +	} + +	i915_gem_object_unpin(obj); + +	drm_gem_object_unreference(obj); +	mutex_unlock(&dev->struct_mutex); +	return 0; +} + +int +i915_gem_busy_ioctl(struct drm_device *dev, void *data, +		    struct drm_file *file_priv) +{ +	struct drm_i915_gem_busy *args = data; +	struct drm_gem_object *obj; +	struct drm_i915_gem_object *obj_priv; + +	mutex_lock(&dev->struct_mutex); +	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); +		mutex_unlock(&dev->struct_mutex); +		return -EINVAL; +	} + +	obj_priv = obj->driver_private; +	args->busy = obj_priv->active; + +	drm_gem_object_unreference(obj); +	mutex_unlock(&dev->struct_mutex); +	return 0; +} + +int +i915_gem_throttle_ioctl(struct drm_device *dev, void *data, +			struct drm_file *file_priv) +{ +    return i915_gem_ring_throttle(dev, file_priv); +} + +int i915_gem_init_object(struct drm_gem_object *obj) +{ +	struct drm_i915_gem_object *obj_priv; + +	obj_priv = drm_calloc(1, sizeof(*obj_priv), DRM_MEM_DRIVER); +	if (obj_priv == NULL) +		return -ENOMEM; + +	/* +	 * We've just allocated pages from the kernel, +	 * so they've just been written by the CPU with +	 * zeros. They'll need to be clflushed before we +	 * use them with the GPU. +	 */ +	obj->write_domain = I915_GEM_DOMAIN_CPU; +	obj->read_domains = I915_GEM_DOMAIN_CPU; + +	obj->driver_private = obj_priv; +	obj_priv->obj = obj; +	INIT_LIST_HEAD(&obj_priv->list); +	return 0; +} + +void i915_gem_free_object(struct drm_gem_object *obj) +{ +	struct drm_i915_gem_object *obj_priv = obj->driver_private; + +	while (obj_priv->pin_count > 0) +		i915_gem_object_unpin(obj); + +	i915_gem_object_unbind(obj); + +	drm_free(obj->driver_private, 1, DRM_MEM_DRIVER); +} + +int +i915_gem_set_domain(struct drm_gem_object *obj, +		    struct drm_file *file_priv, +		    uint32_t read_domains, +		    uint32_t write_domain) +{ +	struct drm_device *dev = obj->dev; +	int ret; +	uint32_t flush_domains; + +	BUG_ON(!mutex_is_locked(&dev->struct_mutex)); + +	ret = i915_gem_object_set_domain(obj, read_domains, write_domain); +	if (ret) +		return ret; +	flush_domains = i915_gem_dev_set_domain(obj->dev); +	 +	if (flush_domains & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) +		(void) i915_add_request(dev, flush_domains); + +	return 0; +} + +/** Unbinds all objects that are on the given buffer list. */ +static int +i915_gem_evict_from_list(struct drm_device *dev, struct list_head *head) +{ +	struct drm_gem_object *obj; +	struct drm_i915_gem_object *obj_priv; +	int ret; + +	while (!list_empty(head)) { +		obj_priv = list_first_entry(head, +					    struct drm_i915_gem_object, +					    list); +		obj = obj_priv->obj; + +		if (obj_priv->pin_count != 0) { +			DRM_ERROR("Pinned object in unbind list\n"); +			mutex_unlock(&dev->struct_mutex); +			return -EINVAL; +		} + +		ret = i915_gem_object_unbind(obj); +		if (ret != 0) { +			DRM_ERROR("Error unbinding object in LeaveVT: %d\n", +				  ret); +			mutex_unlock(&dev->struct_mutex); +			return ret; +		} +	} + + +	return 0; +} + +static int +i915_gem_idle(struct drm_device *dev) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	uint32_t seqno, cur_seqno, last_seqno; +	int stuck; + +	if (dev_priv->mm.suspended) +		return 0; + +	/* 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; + +	i915_kernel_lost_context(dev); + +	/* Flush the GPU along with all non-CPU write domains +	 */ +	i915_gem_flush(dev, ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT), +		       ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)); +	seqno = i915_add_request(dev, ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)); + +	if (seqno == 0) { +		mutex_unlock(&dev->struct_mutex); +		return -ENOMEM; +	} + +	dev_priv->mm.waiting_gem_seqno = seqno; +	last_seqno = 0; +	stuck = 0; +	for (;;) { +		cur_seqno = i915_get_gem_seqno(dev); +		if (i915_seqno_passed(cur_seqno, seqno)) +			break; +		if (last_seqno == cur_seqno) { +			if (stuck++ > 100) { +				DRM_ERROR("hardware wedged\n"); +				dev_priv->mm.wedged = 1; +				DRM_WAKEUP(&dev_priv->irq_queue); +				break; +			} +		} +		msleep(10); +		last_seqno = cur_seqno; +	} +	dev_priv->mm.waiting_gem_seqno = 0; + +	i915_gem_retire_requests(dev); + +	/* Active and flushing should now be empty as we've +	 * waited for a sequence higher than any pending execbuffer +	 */ +	BUG_ON(!list_empty(&dev_priv->mm.active_list)); +	BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); + +	/* Request should now be empty as we've also waited +	 * for the last request in the list +	 */ +	BUG_ON(!list_empty(&dev_priv->mm.request_list)); + +	/* Move all buffers out of the GTT. */ +	i915_gem_evict_from_list(dev, &dev_priv->mm.inactive_list); + +	BUG_ON(!list_empty(&dev_priv->mm.active_list)); +	BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); +	BUG_ON(!list_empty(&dev_priv->mm.inactive_list)); +	BUG_ON(!list_empty(&dev_priv->mm.request_list)); +	return 0; +} + +int +i915_gem_init_ringbuffer(struct drm_device *dev) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_gem_object *obj; +	struct drm_i915_gem_object *obj_priv; +	int ret; + +	obj = drm_gem_object_alloc(dev, 128 * 1024); +	if (obj == NULL) { +		DRM_ERROR("Failed to allocate ringbuffer\n"); +		return -ENOMEM; +	} +	obj_priv = obj->driver_private; + +	ret = i915_gem_object_pin(obj, 4096); +	if (ret != 0) { +		drm_gem_object_unreference(obj); +		return ret; +	} + +	/* Set up the kernel mapping for the ring. */ +	dev_priv->ring.Size = obj->size; +	dev_priv->ring.tail_mask = obj->size - 1; + +	dev_priv->ring.map.offset = dev->agp->base + obj_priv->gtt_offset; +	dev_priv->ring.map.size = obj->size; +	dev_priv->ring.map.type = 0; +	dev_priv->ring.map.flags = 0; +	dev_priv->ring.map.mtrr = 0; + +	drm_core_ioremap(&dev_priv->ring.map, dev); +	if (dev_priv->ring.map.handle == NULL) { +		DRM_ERROR("Failed to map ringbuffer.\n"); +		memset(&dev_priv->ring, 0, sizeof(dev_priv->ring)); +		drm_gem_object_unreference(obj); +		return -EINVAL; +	} +	dev_priv->ring.ring_obj = obj; +	dev_priv->ring.virtual_start = dev_priv->ring.map.handle; + +	/* Stop the ring if it's running. */ +	I915_WRITE(PRB0_CTL, 0); +        I915_WRITE(PRB0_HEAD, 0); +        I915_WRITE(PRB0_TAIL, 0); +	I915_WRITE(PRB0_START, 0); + +	/* Initialize the ring. */ +	I915_WRITE(PRB0_START, obj_priv->gtt_offset); +	I915_WRITE(PRB0_CTL, (((obj->size - 4096) & RING_NR_PAGES) | +			      RING_NO_REPORT | +			      RING_VALID)); + +	/* Update our cache of the ring state */ +	i915_kernel_lost_context(dev); + +	return 0; +} + +void +i915_gem_cleanup_ringbuffer(struct drm_device *dev) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; + +	if (dev_priv->ring.ring_obj == NULL) +		return; + +	drm_core_ioremapfree(&dev_priv->ring.map, dev); + +	i915_gem_object_unpin(dev_priv->ring.ring_obj); +	drm_gem_object_unreference(dev_priv->ring.ring_obj); + +	memset(&dev_priv->ring, 0, sizeof(dev_priv->ring)); +} + +int +i915_gem_entervt_ioctl(struct drm_device *dev, void *data, +		       struct drm_file *file_priv) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	int ret; + +	if (dev_priv->mm.wedged) { +		DRM_ERROR("Renabling wedged hardware, good luck\n"); +		dev_priv->mm.wedged = 0; +	} + +	ret = i915_gem_init_ringbuffer(dev); +	if (ret != 0) +		return ret; + +	mutex_lock(&dev->struct_mutex); +	BUG_ON(!list_empty(&dev_priv->mm.active_list)); +	BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); +	BUG_ON(!list_empty(&dev_priv->mm.inactive_list)); +	BUG_ON(!list_empty(&dev_priv->mm.request_list)); +	dev_priv->mm.suspended = 0; +	mutex_unlock(&dev->struct_mutex); +	return 0; +} + +int +i915_gem_leavevt_ioctl(struct drm_device *dev, void *data, +		       struct drm_file *file_priv) +{ +	int ret; + +	mutex_lock(&dev->struct_mutex); +	ret = i915_gem_idle(dev); +	if (ret == 0) +		i915_gem_cleanup_ringbuffer(dev); +	mutex_unlock(&dev->struct_mutex); + +	return 0; +} + +static int i915_gem_active_info(char *buf, char **start, off_t offset, +				int request, int *eof, void *data) +{ +	struct drm_minor *minor = (struct drm_minor *) data;  +	struct drm_device *dev = minor->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_gem_object *obj_priv; +	int len = 0; + +	if (offset > DRM_PROC_LIMIT) { +		*eof = 1; +		return 0; +	} + +	*start = &buf[offset]; +	*eof = 0; +	DRM_PROC_PRINT("Active:\n"); +	list_for_each_entry(obj_priv, &dev_priv->mm.active_list, +			    list) +	{ +		struct drm_gem_object *obj = obj_priv->obj; +		if (obj->name) { +			DRM_PROC_PRINT("    %p(%d): %08x %08x %d\n", +				       obj, obj->name, +				       obj->read_domains, obj->write_domain, +				       obj_priv->last_rendering_seqno); +		} else { +			DRM_PROC_PRINT("       %p: %08x %08x %d\n", +				       obj, +				       obj->read_domains, obj->write_domain, +				       obj_priv->last_rendering_seqno); +		} +	} +	if (len > request + offset) +		return request; +	*eof = 1; +	return len - offset; +} + +static int i915_gem_flushing_info(char *buf, char **start, off_t offset, +				  int request, int *eof, void *data) +{ +	struct drm_minor *minor = (struct drm_minor *) data;  +	struct drm_device *dev = minor->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_gem_object *obj_priv; +	int len = 0; + +	if (offset > DRM_PROC_LIMIT) { +		*eof = 1; +		return 0; +	} + +	*start = &buf[offset]; +	*eof = 0; +	DRM_PROC_PRINT("Flushing:\n"); +	list_for_each_entry(obj_priv, &dev_priv->mm.flushing_list, +			    list) +	{ +		struct drm_gem_object *obj = obj_priv->obj; +		if (obj->name) { +			DRM_PROC_PRINT("    %p(%d): %08x %08x %d\n", +				       obj, obj->name, +				       obj->read_domains, obj->write_domain, +				       obj_priv->last_rendering_seqno); +		} else { +			DRM_PROC_PRINT("       %p: %08x %08x %d\n", obj, +				       obj->read_domains, obj->write_domain, +				       obj_priv->last_rendering_seqno); +		} +	} +	if (len > request + offset) +		return request; +	*eof = 1; +	return len - offset; +} + +static int i915_gem_inactive_info(char *buf, char **start, off_t offset, +				  int request, int *eof, void *data) +{ +	struct drm_minor *minor = (struct drm_minor *) data;  +	struct drm_device *dev = minor->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_gem_object *obj_priv; +	int len = 0; + +	if (offset > DRM_PROC_LIMIT) { +		*eof = 1; +		return 0; +	} + +	*start = &buf[offset]; +	*eof = 0; +	DRM_PROC_PRINT("Inactive:\n"); +	list_for_each_entry(obj_priv, &dev_priv->mm.inactive_list, +			    list) +	{ +		struct drm_gem_object *obj = obj_priv->obj; +		if (obj->name) { +			DRM_PROC_PRINT("    %p(%d): %08x %08x %d\n", +				       obj, obj->name, +				       obj->read_domains, obj->write_domain, +				       obj_priv->last_rendering_seqno); +		} else { +			DRM_PROC_PRINT("       %p: %08x %08x %d\n", obj, +				       obj->read_domains, obj->write_domain, +				       obj_priv->last_rendering_seqno); +		} +	} +	if (len > request + offset) +		return request; +	*eof = 1; +	return len - offset; +} + +static int i915_gem_request_info(char *buf, char **start, off_t offset, +				 int request, int *eof, void *data) +{ +	struct drm_minor *minor = (struct drm_minor *) data;  +	struct drm_device *dev = minor->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_gem_request *gem_request; +	int len = 0; + +	if (offset > DRM_PROC_LIMIT) { +		*eof = 1; +		return 0; +	} + +	*start = &buf[offset]; +	*eof = 0; +	DRM_PROC_PRINT("Request:\n"); +	list_for_each_entry(gem_request, &dev_priv->mm.request_list, +			    list) +	{ +		DRM_PROC_PRINT ("    %d @ %d %08x\n", +				gem_request->seqno, +				(int) (jiffies - gem_request->emitted_jiffies), +				gem_request->flush_domains); +	} +	if (len > request + offset) +		return request; +	*eof = 1; +	return len - offset; +} + +static int i915_gem_seqno_info(char *buf, char **start, off_t offset, +			       int request, int *eof, void *data) +{ +	struct drm_minor *minor = (struct drm_minor *) data;  +	struct drm_device *dev = minor->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	int len = 0; + +	if (offset > DRM_PROC_LIMIT) { +		*eof = 1; +		return 0; +	} + +	*start = &buf[offset]; +	*eof = 0; +	DRM_PROC_PRINT("Current sequence: %d\n", i915_get_gem_seqno(dev)); +	DRM_PROC_PRINT("Waiter sequence:  %d\n", dev_priv->mm.waiting_gem_seqno); +	DRM_PROC_PRINT("IRQ sequence:     %d\n", dev_priv->mm.irq_gem_seqno); +	if (len > request + offset) +		return request; +	*eof = 1; +	return len - offset; +} + + +static int i915_interrupt_info(char *buf, char **start, off_t offset, +			       int request, int *eof, void *data) +{ +	struct drm_minor *minor = (struct drm_minor *) data;  +	struct drm_device *dev = minor->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	int len = 0; + +	if (offset > DRM_PROC_LIMIT) { +		*eof = 1; +		return 0; +	} + +	*start = &buf[offset]; +	*eof = 0; +	DRM_PROC_PRINT("Interrupt enable:    %08x\n", +		       I915_READ(IER)); +	DRM_PROC_PRINT("Interrupt identity:  %08x\n", +		       I915_READ(IIR)); +	DRM_PROC_PRINT("Interrupt mask:      %08x\n", +		       I915_READ(IMR)); +	DRM_PROC_PRINT("Pipe A stat:         %08x\n", +		       I915_READ(PIPEASTAT)); +	DRM_PROC_PRINT("Pipe B stat:         %08x\n", +		       I915_READ(PIPEBSTAT)); +	DRM_PROC_PRINT("Interrupts received: %d\n", +		       atomic_read(&dev_priv->irq_received)); +	DRM_PROC_PRINT("Current sequence:    %d\n", +		       i915_get_gem_seqno(dev)); +	DRM_PROC_PRINT("Waiter sequence:     %d\n", +		       dev_priv->mm.waiting_gem_seqno); +	DRM_PROC_PRINT("IRQ sequence:        %d\n", +		       dev_priv->mm.irq_gem_seqno); +	if (len > request + offset) +		return request; +	*eof = 1; +	return len - offset; +} + +static struct drm_proc_list { +	const char *name;	/**< file name */ +	int (*f) (char *, char **, off_t, int, int *, void *);		/**< proc callback*/ +} i915_gem_proc_list[] = { +	{"i915_gem_active", i915_gem_active_info}, +	{"i915_gem_flushing", i915_gem_flushing_info}, +	{"i915_gem_inactive", i915_gem_inactive_info}, +	{"i915_gem_request", i915_gem_request_info}, +	{"i915_gem_seqno", i915_gem_seqno_info}, +	{"i915_gem_interrupt", i915_interrupt_info}, +}; + +#define I915_GEM_PROC_ENTRIES ARRAY_SIZE(i915_gem_proc_list) + +int i915_gem_proc_init(struct drm_minor *minor) +{ +	struct proc_dir_entry *ent; +	int i, j; + +	for (i = 0; i < I915_GEM_PROC_ENTRIES; i++) { +		ent = create_proc_entry(i915_gem_proc_list[i].name, +					S_IFREG | S_IRUGO, minor->dev_root); +		if (!ent) { +			DRM_ERROR("Cannot create /proc/dri/.../%s\n", +				  i915_gem_proc_list[i].name); +			for (j = 0; j < i; j++) +				remove_proc_entry(i915_gem_proc_list[i].name, +						  minor->dev_root); +			return -1; +		} +		ent->read_proc = i915_gem_proc_list[i].f; +		ent->data = minor; +	} +	return 0; +} + +void i915_gem_proc_cleanup(struct drm_minor *minor) +{ +	int i; + +	if (!minor->dev_root) +		return; + +	for (i = 0; i < I915_GEM_PROC_ENTRIES; i++) +		remove_proc_entry(i915_gem_proc_list[i].name, minor->dev_root); +} + +void +i915_gem_lastclose(struct drm_device *dev) +{ +	int ret; +	struct drm_i915_private *dev_priv = dev->dev_private; + +	mutex_lock(&dev->struct_mutex); + +	if (dev_priv->ring.ring_obj != NULL) { +		ret = i915_gem_idle(dev); +		if (ret) +			DRM_ERROR("failed to idle hardware: %d\n", ret); +	 +		i915_gem_cleanup_ringbuffer(dev); +	} +	 +	mutex_unlock(&dev->struct_mutex); +} diff --git a/linux-core/i915_init.c b/linux-core/i915_init.c new file mode 120000 index 00000000..473ddf7b --- /dev/null +++ b/linux-core/i915_init.c @@ -0,0 +1 @@ +../shared-core/i915_init.c
\ No newline at end of file diff --git a/linux-core/i915_opregion.c b/linux-core/i915_opregion.c index fdd4987e..d015db28 100644 --- a/linux-core/i915_opregion.c +++ b/linux-core/i915_opregion.c @@ -248,6 +248,7 @@ void opregion_enable_asle(struct drm_device *dev)  {  	struct drm_i915_private *dev_priv = dev->dev_private;  	struct opregion_asle *asle = dev_priv->opregion.asle; +	u32 mask = 0;  	if (asle) {  		u32 pipeb_stats = I915_READ(PIPEBSTAT); @@ -257,12 +258,13 @@ void opregion_enable_asle(struct drm_device *dev)  			   to generate an IRQ on writes */  			I915_WRITE(PIPEBSTAT, pipeb_stats |=   				   I915_LEGACY_BLC_EVENT_ENABLE); -			dev_priv->irq_enable_reg |=  -				(I915_ASLE_INTERRUPT  -				 | I915_DISPLAY_PIPE_B_EVENT_INTERRUPT); +			mask = I915_ASLE_INTERRUPT | +				I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;  		} else -			dev_priv->irq_enable_reg |= I915_ASLE_INTERRUPT; +			mask = I915_ASLE_INTERRUPT; +		dev_priv->irq_mask_reg &= ~mask; +  		asle->tche = ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN |   			ASLE_PFMB_EN;  		asle->ardy = 1; diff --git a/linux-core/intel_bios.c b/linux-core/intel_bios.c new file mode 100644 index 00000000..0cdc915c --- /dev/null +++ b/linux-core/intel_bios.c @@ -0,0 +1,242 @@ +/* + * Copyright © 2006 Intel Corporation + * + * 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 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + *    Eric Anholt <eric@anholt.net> + * + */ +#include "drmP.h" +#include "drm.h" +#include "i915_drm.h" +#include "i915_drv.h" +#include "intel_bios.h" + +#if 0 +unsigned char * +i830_bios_get_aim_data_block (ScrnInfoPtr pScrn, int aim, int data_block) +{ +    unsigned char   *bios; +    int		    bdb_off; +    int		    vbt_off; +    int		    aim_off; +    struct vbt_header *vbt; +    struct aimdb_header *aimdb; +    struct aimdb_block *aimdb_block; + +    bios = i830_bios_get (pScrn); +    if (!bios) +	return NULL; + +    vbt_off = INTEL_BIOS_16(0x1a); +    vbt = (struct vbt_header *)(bios + vbt_off); + +    aim_off = vbt->aim_offset[aim]; +    if (!aim_off) +    { +	free (bios); +	return NULL; +    } +    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "aim_off %d\n", aim_off); +    aimdb = (struct aimdb_header *) (bios + vbt_off + aim_off); +    bdb_off = aimdb->aimdb_header_size; +    while (bdb_off < aimdb->aimdb_size) +    { +	aimdb_block = (struct aimdb_block *) (bios + vbt_off + aim_off + bdb_off); +	if (aimdb_block->aimdb_id == data_block) +	{ +	    unsigned char   *aim = malloc (aimdb_block->aimdb_size + sizeof (struct aimdb_block)); +	    if (!aim) +	    { +		free (bios); +		return NULL; +	    } +	    memcpy (aim, aimdb_block, aimdb_block->aimdb_size + sizeof (struct aimdb_block)); +	    free (bios); +	    return aim; +	} +	bdb_off += aimdb_block->aimdb_size + sizeof (struct aimdb_block); +    } +    free (bios); +    return NULL; +} +#endif + +static void * +find_section(struct bdb_header *bdb, int section_id) +{ +	u8 *base = (u8 *)bdb; +	int index = 0; +	u16 total, current_size; +	u8 current_id; + +	/* skip to first section */ +	index += bdb->header_size; +	total = bdb->bdb_size; + +	/* walk the sections looking for section_id */ +	while (index < total) { +		current_id = *(base + index); +		index++; +		current_size = *((u16 *)(base + index)); +		index += 2; +		if (current_id == section_id) +			return base + index; +		index += current_size; +	} + +	return NULL; +} + +/* Try to find panel data */ +static void +parse_panel_data(struct drm_i915_private *dev_priv, struct bdb_header *bdb) +{ +	struct bdb_lvds_options *lvds_options; +	struct bdb_lvds_lfp_data *lvds_lfp_data; +	struct bdb_lvds_lfp_data_entry *entry; +	struct lvds_dvo_timing *dvo_timing; +	struct drm_display_mode *panel_fixed_mode; + +	/* Defaults if we can't find VBT info */ +	dev_priv->lvds_dither = 0; +	dev_priv->lvds_vbt = 0; + +	lvds_options = find_section(bdb, BDB_LVDS_OPTIONS); +	if (!lvds_options) +		return; + +	dev_priv->lvds_dither = lvds_options->pixel_dither; +	if (lvds_options->panel_type == 0xff) +		return; + +	lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA); +	if (!lvds_lfp_data) +		return; + +	dev_priv->lvds_vbt = 1; + +	entry = &lvds_lfp_data->data[lvds_options->panel_type]; +	dvo_timing = &entry->dvo_timing; + +	panel_fixed_mode = drm_calloc(1, sizeof(*panel_fixed_mode), +				      DRM_MEM_DRIVER); + +	panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) | +		dvo_timing->hactive_lo; +	panel_fixed_mode->hsync_start = panel_fixed_mode->hdisplay + +		((dvo_timing->hsync_off_hi << 8) | dvo_timing->hsync_off_lo); +	panel_fixed_mode->hsync_end = panel_fixed_mode->hsync_start + +		dvo_timing->hsync_pulse_width; +	panel_fixed_mode->htotal = panel_fixed_mode->hdisplay + +		((dvo_timing->hblank_hi << 8) | dvo_timing->hblank_lo); + +	panel_fixed_mode->vdisplay = (dvo_timing->vactive_hi << 8) | +		dvo_timing->vactive_lo; +	panel_fixed_mode->vsync_start = panel_fixed_mode->vdisplay + +		dvo_timing->vsync_off; +	panel_fixed_mode->vsync_end = panel_fixed_mode->vsync_start + +		dvo_timing->vsync_pulse_width; +	panel_fixed_mode->vtotal = panel_fixed_mode->vdisplay + +		((dvo_timing->vblank_hi << 8) | dvo_timing->vblank_lo); +	panel_fixed_mode->clock = dvo_timing->clock * 10; +	panel_fixed_mode->type = DRM_MODE_TYPE_PREFERRED; + +	drm_mode_set_name(panel_fixed_mode); + +	dev_priv->vbt_mode = panel_fixed_mode; + +	DRM_DEBUG("Found panel mode in BIOS VBT tables:\n"); +	drm_mode_debug_printmodeline(panel_fixed_mode); + +	return; +} + +static void +parse_general_features(struct drm_i915_private *dev_priv, +		       struct bdb_header *bdb) +{ +	struct bdb_general_features *general; + +	/* Set sensible defaults in case we can't find the general block */ +	dev_priv->int_tv_support = 1; +	dev_priv->int_crt_support = 1; + +	general = find_section(bdb, BDB_GENERAL_FEATURES); +	if (general) { +		dev_priv->int_tv_support = general->int_tv_support; +		dev_priv->int_crt_support = general->int_crt_support; +	} +} + +/** + * intel_init_bios - initialize VBIOS settings & find VBT + * @dev: DRM device + * + * Loads the Video BIOS and checks that the VBT exists.  Sets scratch registers + * to appropriate values. + * + * VBT existence is a sanity check that is relied on by other i830_bios.c code. + * Note that it would be better to use a BIOS call to get the VBT, as BIOSes may + * feed an updated VBT back through that, compared to what we'll fetch using + * this method of groping around in the BIOS data. + * + * Returns 0 on success, nonzero on failure. + */ +bool +intel_init_bios(struct drm_device *dev) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct pci_dev *pdev = dev->pdev; +	struct vbt_header *vbt = NULL; +	struct bdb_header *bdb; +	u8 __iomem *bios; +	size_t size; +	int i; + +	bios = pci_map_rom(pdev, &size); +	if (!bios) +		return -1; + +	/* Scour memory looking for the VBT signature */ +	for (i = 0; i + 4 < size; i++) { +		if (!memcmp(bios + i, "$VBT", 4)) { +			vbt = (struct vbt_header *)(bios + i); +			break; +		} +	} + +	if (!vbt) { +		DRM_ERROR("VBT signature missing\n"); +		pci_unmap_rom(pdev, bios); +		return -1; +	} + +	bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset); + +	/* Grab useful general definitions */ +	parse_general_features(dev_priv, bdb); +	parse_panel_data(dev_priv, bdb); + +	pci_unmap_rom(pdev, bios); + +	return 0; +} diff --git a/linux-core/intel_bios.h b/linux-core/intel_bios.h new file mode 100644 index 00000000..5ea715ac --- /dev/null +++ b/linux-core/intel_bios.h @@ -0,0 +1,405 @@ +/* + * Copyright © 2006 Intel Corporation + * + * 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 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + *    Eric Anholt <eric@anholt.net> + * + */ + +#ifndef _I830_BIOS_H_ +#define _I830_BIOS_H_ + +#include "drmP.h" + +struct vbt_header { +	u8 signature[20];		/**< Always starts with 'VBT$' */ +	u16 version;			/**< decimal */ +	u16 header_size;		/**< in bytes */ +	u16 vbt_size;			/**< in bytes */ +	u8 vbt_checksum; +	u8 reserved0; +	u32 bdb_offset;			/**< from beginning of VBT */ +	u32 aim_offset[4];		/**< from beginning of VBT */ +} __attribute__((packed)); + +struct bdb_header { +	u8 signature[16];		/**< Always 'BIOS_DATA_BLOCK' */ +	u16 version;			/**< decimal */ +	u16 header_size;		/**< in bytes */ +	u16 bdb_size;			/**< in bytes */ +}; + +/* strictly speaking, this is a "skip" block, but it has interesting info */ +struct vbios_data { +	u8 type; /* 0 == desktop, 1 == mobile */ +	u8 relstage; +	u8 chipset; +	u8 lvds_present:1; +	u8 tv_present:1; +	u8 rsvd2:6; /* finish byte */ +	u8 rsvd3[4]; +	u8 signon[155]; +	u8 copyright[61]; +	u16 code_segment; +	u8 dos_boot_mode; +	u8 bandwidth_percent; +	u8 rsvd4; /* popup memory size */ +	u8 resize_pci_bios; +	u8 rsvd5; /* is crt already on ddc2 */ +} __attribute__((packed)); + +/* + * There are several types of BIOS data blocks (BDBs), each block has + * an ID and size in the first 3 bytes (ID in first, size in next 2). + * Known types are listed below. + */ +#define BDB_GENERAL_FEATURES	  1 +#define BDB_GENERAL_DEFINITIONS	  2 +#define BDB_OLD_TOGGLE_LIST	  3 +#define BDB_MODE_SUPPORT_LIST	  4 +#define BDB_GENERIC_MODE_TABLE	  5 +#define BDB_EXT_MMIO_REGS	  6 +#define BDB_SWF_IO		  7 +#define BDB_SWF_MMIO		  8 +#define BDB_DOT_CLOCK_TABLE	  9 +#define BDB_MODE_REMOVAL_TABLE	 10 +#define BDB_CHILD_DEVICE_TABLE	 11 +#define BDB_DRIVER_FEATURES	 12 +#define BDB_DRIVER_PERSISTENCE	 13 +#define BDB_EXT_TABLE_PTRS	 14 +#define BDB_DOT_CLOCK_OVERRIDE	 15 +#define BDB_DISPLAY_SELECT	 16 +/* 17 rsvd */ +#define BDB_DRIVER_ROTATION	 18 +#define BDB_DISPLAY_REMOVE	 19 +#define BDB_OEM_CUSTOM		 20 +#define BDB_EFP_LIST		 21 /* workarounds for VGA hsync/vsync */ +#define BDB_SDVO_LVDS_OPTIONS	 22 +#define BDB_SDVO_PANEL_DTDS	 23 +#define BDB_SDVO_LVDS_PNP_IDS	 24 +#define BDB_SDVO_LVDS_POWER_SEQ	 25 +#define BDB_TV_OPTIONS		 26 +#define BDB_LVDS_OPTIONS	 40 +#define BDB_LVDS_LFP_DATA_PTRS	 41 +#define BDB_LVDS_LFP_DATA	 42 +#define BDB_LVDS_BACKLIGHT	 43 +#define BDB_LVDS_POWER		 44 +#define BDB_SKIP		254 /* VBIOS private block, ignore */ + +struct bdb_general_features { +        /* bits 1 */ +	u8 panel_fitting:2; +	u8 flexaim:1; +	u8 msg_enable:1; +	u8 clear_screen:3; +	u8 color_flip:1; + +        /* bits 2 */ +	u8 download_ext_vbt:1; +	u8 enable_ssc:1; +	u8 ssc_freq:1; +	u8 enable_lfp_on_override:1; +	u8 disable_ssc_ddt:1; +	u8 rsvd8:3; /* finish byte */ + +        /* bits 3 */ +	u8 disable_smooth_vision:1; +	u8 single_dvi:1; +	u8 rsvd9:6; /* finish byte */ + +        /* bits 4 */ +	u8 legacy_monitor_detect; + +        /* bits 5 */ +	u8 int_crt_support:1; +	u8 int_tv_support:1; +	u8 rsvd11:6; /* finish byte */ +} __attribute__((packed)); + +struct bdb_general_definitions { +	/* DDC GPIO */ +	u8 crt_ddc_gmbus_pin; + +	/* DPMS bits */ +	u8 dpms_acpi:1; +	u8 skip_boot_crt_detect:1; +	u8 dpms_aim:1; +	u8 rsvd1:5; /* finish byte */ + +	/* boot device bits */ +	u8 boot_display[2]; +	u8 child_dev_size; + +	/* device info */ +	u8 tv_or_lvds_info[33]; +	u8 dev1[33]; +	u8 dev2[33]; +	u8 dev3[33]; +	u8 dev4[33]; +	/* may be another device block here on some platforms */ +}; + +struct bdb_lvds_options { +	u8 panel_type; +	u8 rsvd1; +	/* LVDS capabilities, stored in a dword */ +	u8 rsvd2:1; +	u8 lvds_edid:1; +	u8 pixel_dither:1; +	u8 pfit_ratio_auto:1; +	u8 pfit_gfx_mode_enhanced:1; +	u8 pfit_text_mode_enhanced:1; +	u8 pfit_mode:2; +	u8 rsvd4; +} __attribute__((packed)); + +/* LFP pointer table contains entries to the struct below */ +struct bdb_lvds_lfp_data_ptr { +	u16 fp_timing_offset; /* offsets are from start of bdb */ +	u8 fp_table_size; +	u16 dvo_timing_offset; +	u8 dvo_table_size; +	u16 panel_pnp_id_offset; +	u8 pnp_table_size; +} __attribute__((packed)); + +struct bdb_lvds_lfp_data_ptrs { +	u8 lvds_entries; /* followed by one or more lvds_data_ptr structs */ +	struct bdb_lvds_lfp_data_ptr ptr[16]; +} __attribute__((packed)); + +/* LFP data has 3 blocks per entry */ +struct lvds_fp_timing { +	u16 x_res; +	u16 y_res; +	u32 lvds_reg; +	u32 lvds_reg_val; +	u32 pp_on_reg; +	u32 pp_on_reg_val; +	u32 pp_off_reg; +	u32 pp_off_reg_val; +	u32 pp_cycle_reg; +	u32 pp_cycle_reg_val; +	u32 pfit_reg; +	u32 pfit_reg_val; +	u16 terminator; +} __attribute__((packed)); + +struct lvds_dvo_timing { +	u16 clock;		/**< In 10khz */ +	u8 hactive_lo; +	u8 hblank_lo; +	u8 hblank_hi:4; +	u8 hactive_hi:4; +	u8 vactive_lo; +	u8 vblank_lo; +	u8 vblank_hi:4; +	u8 vactive_hi:4; +	u8 hsync_off_lo; +	u8 hsync_pulse_width; +	u8 vsync_pulse_width:4; +	u8 vsync_off:4; +	u8 rsvd0:6; +	u8 hsync_off_hi:2; +	u8 h_image; +	u8 v_image; +	u8 max_hv; +	u8 h_border; +	u8 v_border; +	u8 rsvd1:3; +	u8 digital:2; +	u8 vsync_positive:1; +	u8 hsync_positive:1; +	u8 rsvd2:1; +} __attribute__((packed)); + +struct lvds_pnp_id { +	u16 mfg_name; +	u16 product_code; +	u32 serial; +	u8 mfg_week; +	u8 mfg_year; +} __attribute__((packed)); + +struct bdb_lvds_lfp_data_entry { +	struct lvds_fp_timing fp_timing; +	struct lvds_dvo_timing dvo_timing; +	struct lvds_pnp_id pnp_id; +} __attribute__((packed)); + +struct bdb_lvds_lfp_data { +	struct bdb_lvds_lfp_data_entry data[16]; +} __attribute__((packed)); + +struct aimdb_header { +	char signature[16]; +	char oem_device[20]; +	u16 aimdb_version; +	u16 aimdb_header_size; +	u16 aimdb_size; +} __attribute__((packed)); + +struct aimdb_block { +	u8 aimdb_id; +	u16 aimdb_size; +} __attribute__((packed)); + +struct vch_panel_data { +	u16 fp_timing_offset; +	u8 fp_timing_size; +	u16 dvo_timing_offset; +	u8 dvo_timing_size; +	u16 text_fitting_offset; +	u8 text_fitting_size; +	u16 graphics_fitting_offset; +	u8 graphics_fitting_size; +} __attribute__((packed)); + +struct vch_bdb_22 { +	struct aimdb_block aimdb_block; +	struct vch_panel_data panels[16]; +} __attribute__((packed)); + +bool intel_init_bios(struct drm_device *dev); + +/* + * Driver<->VBIOS interaction occurs through scratch bits in + * GR18 & SWF*. + */ + +/* GR18 bits are set on display switch and hotkey events */ +#define GR18_DRIVER_SWITCH_EN	(1<<7) /* 0: VBIOS control, 1: driver control */ +#define GR18_HOTKEY_MASK	0x78 /* See also SWF4 15:0 */ +#define   GR18_HK_NONE		(0x0<<3) +#define   GR18_HK_LFP_STRETCH	(0x1<<3) +#define   GR18_HK_TOGGLE_DISP	(0x2<<3) +#define   GR18_HK_DISP_SWITCH	(0x4<<3) /* see SWF14 15:0 for what to enable */ +#define   GR18_HK_POPUP_DISABLED (0x6<<3) +#define   GR18_HK_POPUP_ENABLED	(0x7<<3) +#define   GR18_HK_PFIT		(0x8<<3) +#define   GR18_HK_APM_CHANGE	(0xa<<3) +#define   GR18_HK_MULTIPLE	(0xc<<3) +#define GR18_USER_INT_EN	(1<<2) +#define GR18_A0000_FLUSH_EN	(1<<1) +#define GR18_SMM_EN		(1<<0) + +/* Set by driver, cleared by VBIOS */ +#define SWF00_YRES_SHIFT	16 +#define SWF00_XRES_SHIFT	0 +#define SWF00_RES_MASK		0xffff + +/* Set by VBIOS at boot time and driver at runtime */ +#define SWF01_TV2_FORMAT_SHIFT	8 +#define SWF01_TV1_FORMAT_SHIFT	0 +#define SWF01_TV_FORMAT_MASK	0xffff + +#define SWF10_VBIOS_BLC_I2C_EN	(1<<29) +#define SWF10_GTT_OVERRIDE_EN	(1<<28) +#define SWF10_LFP_DPMS_OVR	(1<<27) /* override DPMS on display switch */ +#define SWF10_ACTIVE_TOGGLE_LIST_MASK (7<<24) +#define   SWF10_OLD_TOGGLE	0x0 +#define   SWF10_TOGGLE_LIST_1	0x1 +#define   SWF10_TOGGLE_LIST_2	0x2 +#define   SWF10_TOGGLE_LIST_3	0x3 +#define   SWF10_TOGGLE_LIST_4	0x4 +#define SWF10_PANNING_EN	(1<<23) +#define SWF10_DRIVER_LOADED	(1<<22) +#define SWF10_EXTENDED_DESKTOP	(1<<21) +#define SWF10_EXCLUSIVE_MODE	(1<<20) +#define SWF10_OVERLAY_EN	(1<<19) +#define SWF10_PLANEB_HOLDOFF	(1<<18) +#define SWF10_PLANEA_HOLDOFF	(1<<17) +#define SWF10_VGA_HOLDOFF	(1<<16) +#define SWF10_ACTIVE_DISP_MASK	0xffff +#define   SWF10_PIPEB_LFP2	(1<<15) +#define   SWF10_PIPEB_EFP2	(1<<14) +#define   SWF10_PIPEB_TV2	(1<<13) +#define   SWF10_PIPEB_CRT2	(1<<12) +#define   SWF10_PIPEB_LFP	(1<<11) +#define   SWF10_PIPEB_EFP	(1<<10) +#define   SWF10_PIPEB_TV	(1<<9) +#define   SWF10_PIPEB_CRT	(1<<8) +#define   SWF10_PIPEA_LFP2	(1<<7) +#define   SWF10_PIPEA_EFP2	(1<<6) +#define   SWF10_PIPEA_TV2	(1<<5) +#define   SWF10_PIPEA_CRT2	(1<<4) +#define   SWF10_PIPEA_LFP	(1<<3) +#define   SWF10_PIPEA_EFP	(1<<2) +#define   SWF10_PIPEA_TV	(1<<1) +#define   SWF10_PIPEA_CRT	(1<<0) + +#define SWF11_MEMORY_SIZE_SHIFT	16 +#define SWF11_SV_TEST_EN	(1<<15) +#define SWF11_IS_AGP		(1<<14) +#define SWF11_DISPLAY_HOLDOFF	(1<<13) +#define SWF11_DPMS_REDUCED	(1<<12) +#define SWF11_IS_VBE_MODE	(1<<11) +#define SWF11_PIPEB_ACCESS	(1<<10) /* 0 here means pipe a */ +#define SWF11_DPMS_MASK		0x07 +#define   SWF11_DPMS_OFF	(1<<2) +#define   SWF11_DPMS_SUSPEND	(1<<1) +#define   SWF11_DPMS_STANDBY	(1<<0) +#define   SWF11_DPMS_ON		0 + +#define SWF14_GFX_PFIT_EN	(1<<31) +#define SWF14_TEXT_PFIT_EN	(1<<30) +#define SWF14_LID_STATUS_CLOSED	(1<<29) /* 0 here means open */ +#define SWF14_POPUP_EN		(1<<28) +#define SWF14_DISPLAY_HOLDOFF	(1<<27) +#define SWF14_DISP_DETECT_EN	(1<<26) +#define SWF14_DOCKING_STATUS_DOCKED (1<<25) /* 0 here means undocked */ +#define SWF14_DRIVER_STATUS	(1<<24) +#define SWF14_OS_TYPE_WIN9X	(1<<23) +#define SWF14_OS_TYPE_WINNT	(1<<22) +/* 21:19 rsvd */ +#define SWF14_PM_TYPE_MASK	0x00070000 +#define   SWF14_PM_ACPI_VIDEO	(0x4 << 16) +#define   SWF14_PM_ACPI		(0x3 << 16) +#define   SWF14_PM_APM_12	(0x2 << 16) +#define   SWF14_PM_APM_11	(0x1 << 16) +#define SWF14_HK_REQUEST_MASK	0x0000ffff /* see GR18 6:3 for event type */ +          /* if GR18 indicates a display switch */ +#define   SWF14_DS_PIPEB_LFP2_EN (1<<15) +#define   SWF14_DS_PIPEB_EFP2_EN (1<<14) +#define   SWF14_DS_PIPEB_TV2_EN  (1<<13) +#define   SWF14_DS_PIPEB_CRT2_EN (1<<12) +#define   SWF14_DS_PIPEB_LFP_EN  (1<<11) +#define   SWF14_DS_PIPEB_EFP_EN  (1<<10) +#define   SWF14_DS_PIPEB_TV_EN   (1<<9) +#define   SWF14_DS_PIPEB_CRT_EN  (1<<8) +#define   SWF14_DS_PIPEA_LFP2_EN (1<<7) +#define   SWF14_DS_PIPEA_EFP2_EN (1<<6) +#define   SWF14_DS_PIPEA_TV2_EN  (1<<5) +#define   SWF14_DS_PIPEA_CRT2_EN (1<<4) +#define   SWF14_DS_PIPEA_LFP_EN  (1<<3) +#define   SWF14_DS_PIPEA_EFP_EN  (1<<2) +#define   SWF14_DS_PIPEA_TV_EN   (1<<1) +#define   SWF14_DS_PIPEA_CRT_EN  (1<<0) +          /* if GR18 indicates a panel fitting request */ +#define   SWF14_PFIT_EN		(1<<0) /* 0 means disable */ +          /* if GR18 indicates an APM change request */ +#define   SWF14_APM_HIBERNATE	0x4 +#define   SWF14_APM_SUSPEND	0x3 +#define   SWF14_APM_STANDBY	0x1 +#define   SWF14_APM_RESTORE	0x0 + +#endif /* _I830_BIOS_H_ */ diff --git a/linux-core/intel_crt.c b/linux-core/intel_crt.c new file mode 100644 index 00000000..2505d983 --- /dev/null +++ b/linux-core/intel_crt.c @@ -0,0 +1,296 @@ +/* + * Copyright © 2006-2007 Intel Corporation + * + * 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 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + *	Eric Anholt <eric@anholt.net> + */ + +#include <linux/i2c.h> +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "drm_crtc_helper.h" +#include "intel_drv.h" +#include "i915_drm.h" +#include "i915_drv.h" + +static void intel_crt_dpms(struct drm_encoder *encoder, int mode) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	u32 temp; +	 +	temp = I915_READ(ADPA); +	temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); +	temp &= ~ADPA_DAC_ENABLE; +	 +	switch(mode) { +	case DRM_MODE_DPMS_ON: +		temp |= ADPA_DAC_ENABLE; +		break; +	case DRM_MODE_DPMS_STANDBY: +		temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE; +		break; +	case DRM_MODE_DPMS_SUSPEND: +		temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE; +		break; +	case DRM_MODE_DPMS_OFF: +		temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE; +		break; +	} +	 +	I915_WRITE(ADPA, temp); +} + +static void intel_crt_save(struct drm_connector *connector) +{ +	 +} + +static void intel_crt_restore(struct drm_connector *connector) +{ + +} + +static int intel_crt_mode_valid(struct drm_connector *connector, +				struct drm_display_mode *mode) +{ +	if (mode->flags & DRM_MODE_FLAG_DBLSCAN) +		return MODE_NO_DBLESCAN; + +	if (mode->clock > 400000 || mode->clock < 25000) +		return MODE_CLOCK_RANGE; + +	return MODE_OK; +} + +static bool intel_crt_mode_fixup(struct drm_encoder *encoder, +				 struct drm_display_mode *mode, +				 struct drm_display_mode *adjusted_mode) +{ +	return true; +} + +static void intel_crt_mode_set(struct drm_encoder *encoder, +			       struct drm_display_mode *mode, +			       struct drm_display_mode *adjusted_mode) +{ +	 +	struct drm_device *dev = encoder->dev; +	struct drm_crtc *crtc = encoder->crtc; +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +	struct drm_i915_private *dev_priv = dev->dev_private; +	int dpll_md_reg; +	u32 adpa, dpll_md; + +	if (intel_crtc->pipe == 0)  +		dpll_md_reg = DPLL_A_MD; +	else +		dpll_md_reg = DPLL_B_MD; + +	/* +	 * Disable separate mode multiplier used when cloning SDVO to CRT +	 * XXX this needs to be adjusted when we really are cloning +	 */ +	if (IS_I965G(dev)) { +		dpll_md = I915_READ(dpll_md_reg); +		I915_WRITE(dpll_md_reg, +			   dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK); +	} +	 +	adpa = 0; +	if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) +		adpa |= ADPA_HSYNC_ACTIVE_HIGH; +	if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) +		adpa |= ADPA_VSYNC_ACTIVE_HIGH; +	 +	if (intel_crtc->pipe == 0) +		adpa |= ADPA_PIPE_A_SELECT; +	else +		adpa |= ADPA_PIPE_B_SELECT; +	 +	I915_WRITE(ADPA, adpa); +} + +/** + * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence. + * + * Not for i915G/i915GM + * + * \return true if CRT is connected. + * \return false if CRT is disconnected. + */ +static bool intel_crt_detect_hotplug(struct drm_connector *connector) +{ +	struct drm_device *dev = connector->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	u32 temp; + +	unsigned long timeout = jiffies + msecs_to_jiffies(1000); + +	temp = I915_READ(PORT_HOTPLUG_EN); + +	I915_WRITE(PORT_HOTPLUG_EN, +		   temp | CRT_HOTPLUG_FORCE_DETECT | (1 << 5)); + +	do { +		if (!(I915_READ(PORT_HOTPLUG_EN) & CRT_HOTPLUG_FORCE_DETECT)) +			break; +		msleep(1); +	} while (time_after(timeout, jiffies)); + +	if ((I915_READ(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) == +	    CRT_HOTPLUG_MONITOR_COLOR) +		return true; + +	return false; +} + +static bool intel_crt_detect_ddc(struct drm_connector *connector) +{ +	struct intel_output *intel_output = to_intel_output(connector); + +	/* CRT should always be at 0, but check anyway */ +	if (intel_output->type != INTEL_OUTPUT_ANALOG) +		return false; +	 +	return intel_ddc_probe(intel_output); +} + +static enum drm_connector_status intel_crt_detect(struct drm_connector *connector) +{ +	struct drm_device *dev = connector->dev; +	 +	if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) { +		if (intel_crt_detect_hotplug(connector)) +			return connector_status_connected; +		else +			return connector_status_disconnected; +	} + +	if (intel_crt_detect_ddc(connector)) +		return connector_status_connected; + +	/* TODO use load detect */ +	return connector_status_unknown; +} + +static void intel_crt_destroy(struct drm_connector *connector) +{ +	struct intel_output *intel_output = to_intel_output(connector); + +	intel_i2c_destroy(intel_output->ddc_bus); +	drm_sysfs_connector_remove(connector); +	drm_connector_cleanup(connector); +	kfree(connector); +} + +static int intel_crt_get_modes(struct drm_connector *connector) +{ +	struct intel_output *intel_output = to_intel_output(connector); +	return intel_ddc_get_modes(intel_output); +} + +static int intel_crt_set_property(struct drm_connector *connector, +				  struct drm_property *property, +				  uint64_t value) +{ +	struct drm_device *dev = connector->dev; + +	if (property == dev->mode_config.dpms_property && connector->encoder) +		intel_crt_dpms(connector->encoder, (uint32_t)(value & 0xf)); + +	return 0; +} + +/* + * Routines for controlling stuff on the analog port + */ + +static const struct drm_encoder_helper_funcs intel_crt_helper_funcs = { +	.dpms = intel_crt_dpms, +	.mode_fixup = intel_crt_mode_fixup, +	.prepare = intel_encoder_prepare, +	.commit = intel_encoder_commit, +	.mode_set = intel_crt_mode_set, +}; + +static const struct drm_connector_funcs intel_crt_connector_funcs = { +	.save = intel_crt_save, +	.restore = intel_crt_restore, +	.detect = intel_crt_detect, +	.fill_modes = drm_helper_probe_single_connector_modes, +	.destroy = intel_crt_destroy, +	.set_property = intel_crt_set_property, +}; + +static const struct drm_connector_helper_funcs intel_crt_connector_helper_funcs = { +	.mode_valid = intel_crt_mode_valid, +	.get_modes = intel_crt_get_modes, +	.best_encoder = intel_best_encoder, +};	 + +void intel_crt_enc_destroy(struct drm_encoder *encoder) +{ +	drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs intel_crt_enc_funcs = { +	.destroy = intel_crt_enc_destroy, +}; + +void intel_crt_init(struct drm_device *dev) +{ +	struct drm_connector *connector; +	struct intel_output *intel_output; + +	intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL); +	if (!intel_output) +		return; + +	connector = &intel_output->base; +	drm_connector_init(dev, &intel_output->base, +			   &intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA); + +	drm_encoder_init(dev, &intel_output->enc, &intel_crt_enc_funcs, +			 DRM_MODE_ENCODER_DAC); + +	drm_mode_connector_attach_encoder(&intel_output->base, +					  &intel_output->enc); + +	/* Set up the DDC bus. */ +	intel_output->ddc_bus = intel_i2c_create(dev, GPIOA, "CRTDDC_A"); +	if (!intel_output->ddc_bus) { +		dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " +			   "failed.\n"); +		return; +	} + +	intel_output->type = INTEL_OUTPUT_ANALOG; +	connector->interlace_allowed = 0; +	connector->doublescan_allowed = 0; + +	drm_encoder_helper_add(&intel_output->enc, &intel_crt_helper_funcs); +	drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs); + +	drm_sysfs_connector_add(connector); +} diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c new file mode 100644 index 00000000..0236bbc9 --- /dev/null +++ b/linux-core/intel_display.c @@ -0,0 +1,1620 @@ +/* + * Copyright © 2006-2007 Intel Corporation + * + * 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 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + *	Eric Anholt <eric@anholt.net> + */ + +#include <linux/i2c.h> +#include "drmP.h" +#include "intel_drv.h" +#include "i915_drm.h" +#include "i915_drv.h" + +#include "drm_crtc_helper.h" + +bool intel_pipe_has_type (struct drm_crtc *crtc, int type); + +typedef struct { +    /* given values */     +    int n; +    int m1, m2; +    int p1, p2; +    /* derived values */ +    int	dot; +    int	vco; +    int	m; +    int	p; +} intel_clock_t; + +typedef struct { +    int	min, max; +} intel_range_t; + +typedef struct { +    int	dot_limit; +    int	p2_slow, p2_fast; +} intel_p2_t; + +#define INTEL_P2_NUM		      2 + +typedef struct { +    intel_range_t   dot, vco, n, m, m1, m2, p, p1; +    intel_p2_t	    p2; +} intel_limit_t; + +#define I8XX_DOT_MIN		  25000 +#define I8XX_DOT_MAX		 350000 +#define I8XX_VCO_MIN		 930000 +#define I8XX_VCO_MAX		1400000 +#define I8XX_N_MIN		      3 +#define I8XX_N_MAX		     16 +#define I8XX_M_MIN		     96 +#define I8XX_M_MAX		    140 +#define I8XX_M1_MIN		     18 +#define I8XX_M1_MAX		     26 +#define I8XX_M2_MIN		      6 +#define I8XX_M2_MAX		     16 +#define I8XX_P_MIN		      4 +#define I8XX_P_MAX		    128 +#define I8XX_P1_MIN		      2 +#define I8XX_P1_MAX		     33 +#define I8XX_P1_LVDS_MIN	      1 +#define I8XX_P1_LVDS_MAX	      6 +#define I8XX_P2_SLOW		      4 +#define I8XX_P2_FAST		      2 +#define I8XX_P2_LVDS_SLOW	      14 +#define I8XX_P2_LVDS_FAST	      14 /* No fast option */ +#define I8XX_P2_SLOW_LIMIT	 165000 + +#define I9XX_DOT_MIN		  20000 +#define I9XX_DOT_MAX		 400000 +#define I9XX_VCO_MIN		1400000 +#define I9XX_VCO_MAX		2800000 +#define I9XX_N_MIN		      3 +#define I9XX_N_MAX		      8 +#define I9XX_M_MIN		     70 +#define I9XX_M_MAX		    120 +#define I9XX_M1_MIN		     10 +#define I9XX_M1_MAX		     20 +#define I9XX_M2_MIN		      5 +#define I9XX_M2_MAX		      9 +#define I9XX_P_SDVO_DAC_MIN	      5 +#define I9XX_P_SDVO_DAC_MAX	     80 +#define I9XX_P_LVDS_MIN		      7 +#define I9XX_P_LVDS_MAX		     98 +#define I9XX_P1_MIN		      1 +#define I9XX_P1_MAX		      8 +#define I9XX_P2_SDVO_DAC_SLOW		     10 +#define I9XX_P2_SDVO_DAC_FAST		      5 +#define I9XX_P2_SDVO_DAC_SLOW_LIMIT	 200000 +#define I9XX_P2_LVDS_SLOW		     14 +#define I9XX_P2_LVDS_FAST		      7 +#define I9XX_P2_LVDS_SLOW_LIMIT		 112000 + +#define INTEL_LIMIT_I8XX_DVO_DAC    0 +#define INTEL_LIMIT_I8XX_LVDS	    1 +#define INTEL_LIMIT_I9XX_SDVO_DAC   2 +#define INTEL_LIMIT_I9XX_LVDS	    3 + +static const intel_limit_t intel_limits[] = { +    { /* INTEL_LIMIT_I8XX_DVO_DAC */ +        .dot = { .min = I8XX_DOT_MIN,		.max = I8XX_DOT_MAX }, +        .vco = { .min = I8XX_VCO_MIN,		.max = I8XX_VCO_MAX }, +        .n   = { .min = I8XX_N_MIN,		.max = I8XX_N_MAX }, +        .m   = { .min = I8XX_M_MIN,		.max = I8XX_M_MAX }, +        .m1  = { .min = I8XX_M1_MIN,		.max = I8XX_M1_MAX }, +        .m2  = { .min = I8XX_M2_MIN,		.max = I8XX_M2_MAX }, +        .p   = { .min = I8XX_P_MIN,		.max = I8XX_P_MAX }, +        .p1  = { .min = I8XX_P1_MIN,		.max = I8XX_P1_MAX }, +	.p2  = { .dot_limit = I8XX_P2_SLOW_LIMIT, +		 .p2_slow = I8XX_P2_SLOW,	.p2_fast = I8XX_P2_FAST }, +    }, +    { /* INTEL_LIMIT_I8XX_LVDS */ +        .dot = { .min = I8XX_DOT_MIN,		.max = I8XX_DOT_MAX }, +        .vco = { .min = I8XX_VCO_MIN,		.max = I8XX_VCO_MAX }, +        .n   = { .min = I8XX_N_MIN,		.max = I8XX_N_MAX }, +        .m   = { .min = I8XX_M_MIN,		.max = I8XX_M_MAX }, +        .m1  = { .min = I8XX_M1_MIN,		.max = I8XX_M1_MAX }, +        .m2  = { .min = I8XX_M2_MIN,		.max = I8XX_M2_MAX }, +        .p   = { .min = I8XX_P_MIN,		.max = I8XX_P_MAX }, +        .p1  = { .min = I8XX_P1_LVDS_MIN,	.max = I8XX_P1_LVDS_MAX }, +	.p2  = { .dot_limit = I8XX_P2_SLOW_LIMIT, +		 .p2_slow = I8XX_P2_LVDS_SLOW,	.p2_fast = I8XX_P2_LVDS_FAST }, +    }, +    { /* INTEL_LIMIT_I9XX_SDVO_DAC */ +        .dot = { .min = I9XX_DOT_MIN,		.max = I9XX_DOT_MAX }, +        .vco = { .min = I9XX_VCO_MIN,		.max = I9XX_VCO_MAX }, +        .n   = { .min = I9XX_N_MIN,		.max = I9XX_N_MAX }, +        .m   = { .min = I9XX_M_MIN,		.max = I9XX_M_MAX }, +        .m1  = { .min = I9XX_M1_MIN,		.max = I9XX_M1_MAX }, +        .m2  = { .min = I9XX_M2_MIN,		.max = I9XX_M2_MAX }, +        .p   = { .min = I9XX_P_SDVO_DAC_MIN,	.max = I9XX_P_SDVO_DAC_MAX }, +        .p1  = { .min = I9XX_P1_MIN,		.max = I9XX_P1_MAX }, +	.p2  = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT, +		 .p2_slow = I9XX_P2_SDVO_DAC_SLOW,	.p2_fast = I9XX_P2_SDVO_DAC_FAST }, +    }, +    { /* INTEL_LIMIT_I9XX_LVDS */ +        .dot = { .min = I9XX_DOT_MIN,		.max = I9XX_DOT_MAX }, +        .vco = { .min = I9XX_VCO_MIN,		.max = I9XX_VCO_MAX }, +        .n   = { .min = I9XX_N_MIN,		.max = I9XX_N_MAX }, +        .m   = { .min = I9XX_M_MIN,		.max = I9XX_M_MAX }, +        .m1  = { .min = I9XX_M1_MIN,		.max = I9XX_M1_MAX }, +        .m2  = { .min = I9XX_M2_MIN,		.max = I9XX_M2_MAX }, +        .p   = { .min = I9XX_P_LVDS_MIN,	.max = I9XX_P_LVDS_MAX }, +        .p1  = { .min = I9XX_P1_MIN,		.max = I9XX_P1_MAX }, +	/* The single-channel range is 25-112Mhz, and dual-channel +	 * is 80-224Mhz.  Prefer single channel as much as possible. +	 */ +	.p2  = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT, +		 .p2_slow = I9XX_P2_LVDS_SLOW,	.p2_fast = I9XX_P2_LVDS_FAST }, +    }, +}; + +static const intel_limit_t *intel_limit(struct drm_crtc *crtc) +{ +	struct drm_device *dev = crtc->dev; +	const intel_limit_t *limit; +	 +	if (IS_I9XX(dev)) { +		if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) +			limit = &intel_limits[INTEL_LIMIT_I9XX_LVDS]; +		else +			limit = &intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC]; +	} else { +		if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) +			limit = &intel_limits[INTEL_LIMIT_I8XX_LVDS]; +		else +			limit = &intel_limits[INTEL_LIMIT_I8XX_DVO_DAC]; +	} +	return limit; +} + +/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */ + +static void i8xx_clock(int refclk, intel_clock_t *clock) +{ +	clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); +	clock->p = clock->p1 * clock->p2; +	clock->vco = refclk * clock->m / (clock->n + 2); +	clock->dot = clock->vco / clock->p; +} + +/** Derive the pixel clock for the given refclk and divisors for 9xx chips. */ + +static void i9xx_clock(int refclk, intel_clock_t *clock) +{ +	clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); +	clock->p = clock->p1 * clock->p2; +	clock->vco = refclk * clock->m / (clock->n + 2); +	clock->dot = clock->vco / clock->p; +} + +static void intel_clock(struct drm_device *dev, int refclk, +			intel_clock_t *clock) +{ +	if (IS_I9XX(dev)) +		return i9xx_clock (refclk, clock); +	else +		return i8xx_clock (refclk, clock); +} + +/** + * Returns whether any output on the specified pipe is of the specified type + */ +bool intel_pipe_has_type (struct drm_crtc *crtc, int type) +{ +    struct drm_device *dev = crtc->dev; +    struct drm_mode_config *mode_config = &dev->mode_config; +    struct drm_connector *l_entry; + +    list_for_each_entry(l_entry, &mode_config->connector_list, head) { +	    if (l_entry->encoder && +	        l_entry->encoder->crtc == crtc) { +		    struct intel_output *intel_output = to_intel_output(l_entry); +		    if (intel_output->type == type) +			    return true; +	    } +    } +    return false; +} + +#define INTELPllInvalid(s)   { /* ErrorF (s) */; return false; } +/** + * Returns whether the given set of divisors are valid for a given refclk with + * the given connectors. + */ + +static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock) +{ +	const intel_limit_t *limit = intel_limit (crtc); +	 +	if (clock->p1  < limit->p1.min  || limit->p1.max  < clock->p1) +		INTELPllInvalid ("p1 out of range\n"); +	if (clock->p   < limit->p.min   || limit->p.max   < clock->p) +		INTELPllInvalid ("p out of range\n"); +	if (clock->m2  < limit->m2.min  || limit->m2.max  < clock->m2) +		INTELPllInvalid ("m2 out of range\n"); +	if (clock->m1  < limit->m1.min  || limit->m1.max  < clock->m1) +		INTELPllInvalid ("m1 out of range\n"); +	if (clock->m1 <= clock->m2) +		INTELPllInvalid ("m1 <= m2\n"); +	if (clock->m   < limit->m.min   || limit->m.max   < clock->m) +		INTELPllInvalid ("m out of range\n"); +	if (clock->n   < limit->n.min   || limit->n.max   < clock->n) +		INTELPllInvalid ("n out of range\n"); +	if (clock->vco < limit->vco.min || limit->vco.max < clock->vco) +		INTELPllInvalid ("vco out of range\n"); +	/* XXX: We may need to be checking "Dot clock" depending on the multiplier, +	 * connector, etc., rather than just a single range. +	 */ +	if (clock->dot < limit->dot.min || limit->dot.max < clock->dot) +		INTELPllInvalid ("dot out of range\n"); +	 +	return true; +} + +/** + * Returns a set of divisors for the desired target clock with the given + * refclk, or FALSE.  The returned values represent the clock equation: + * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. + */ +static bool intel_find_best_PLL(struct drm_crtc *crtc, int target, +				int refclk, intel_clock_t *best_clock) +{ +	struct drm_device *dev = crtc->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	intel_clock_t clock; +	const intel_limit_t *limit = intel_limit(crtc); +	int err = target; + +	if (IS_I9XX(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && +	    (I915_READ(LVDS) & LVDS_PORT_EN) != 0) { +		/* +		 * For LVDS, if the panel is on, just rely on its current +		 * settings for dual-channel.  We haven't figured out how to +		 * reliably set up different single/dual channel state, if we +		 * even can. +		 */ +		if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) == +		    LVDS_CLKB_POWER_UP) +			clock.p2 = limit->p2.p2_fast; +		else +			clock.p2 = limit->p2.p2_slow; +	} else { +		if (target < limit->p2.dot_limit) +			clock.p2 = limit->p2.p2_slow; +		else +			clock.p2 = limit->p2.p2_fast; +	} +	 +	memset (best_clock, 0, sizeof (*best_clock)); +	 +	for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) { +		for (clock.m2 = limit->m2.min; clock.m2 < clock.m1 && +			     clock.m2 <= limit->m2.max; clock.m2++) { +			for (clock.n = limit->n.min; clock.n <= limit->n.max; +			     clock.n++) { +				for (clock.p1 = limit->p1.min; +				     clock.p1 <= limit->p1.max; clock.p1++) { +					int this_err; +					 +					intel_clock(dev, refclk, &clock); +					 +					if (!intel_PLL_is_valid(crtc, &clock)) +						continue; +					 +					this_err = abs(clock.dot - target); +					if (this_err < err) { +						*best_clock = clock; +						err = this_err; +					} +				} +			} +		} +	} + +	return (err != target); +} + +void +intel_set_vblank(struct drm_device *dev) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_crtc *crtc; +	struct intel_crtc *intel_crtc; +	int vbl_pipe = 0; + +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		intel_crtc = to_intel_crtc(crtc); + +		if (crtc->enabled) +			vbl_pipe |= (1<<intel_crtc->pipe); +	} + +	dev_priv->vblank_pipe = vbl_pipe; +	i915_enable_interrupt(dev); +} +void +intel_wait_for_vblank(struct drm_device *dev) +{ +	/* Wait for 20ms, i.e. one cycle at 50hz. */ +	udelay(20000); +} + +void +intel_pipe_set_base(struct drm_crtc *crtc, int x, int y) +{ +	struct drm_device *dev = crtc->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_i915_master_private *master_priv; +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +	struct intel_framebuffer *intel_fb; +	struct drm_i915_gem_object *obj_priv; +	int pipe = intel_crtc->pipe; +	unsigned long Start, Offset; +	int dspbase = (pipe == 0 ? DSPAADDR : DSPBADDR); +	int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF); +	int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE; +	int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; +	u32 dspcntr; + +	/* no fb bound */ +	if (!crtc->fb) { +		DRM_DEBUG("No FB bound\n"); +		return; +	} + +	intel_fb = to_intel_framebuffer(crtc->fb); + +	obj_priv = intel_fb->obj->driver_private; + +	Start = obj_priv->gtt_offset; +	Offset = y * crtc->fb->pitch + x * (crtc->fb->bits_per_pixel / 8); + +	I915_WRITE(dspstride, crtc->fb->pitch); + +	dspcntr = I915_READ(dspcntr_reg); +	switch (crtc->fb->bits_per_pixel) { +	case 8: +		dspcntr |= DISPPLANE_8BPP; +		break; +	case 16: +		if (crtc->fb->depth == 15) +			dspcntr |= DISPPLANE_15_16BPP; +		else +			dspcntr |= DISPPLANE_16BPP; +		break; +	case 24: +	case 32: +		dspcntr |= DISPPLANE_32BPP_NO_ALPHA; +		break; +	default: +		DRM_ERROR("Unknown color depth\n"); +		return; +	} +	I915_WRITE(dspcntr_reg, dspcntr); + +	DRM_DEBUG("Writing base %08lX %08lX %d %d\n", Start, Offset, x, y); +	if (IS_I965G(dev)) { +		I915_WRITE(dspbase, Offset); +		I915_READ(dspbase); +		I915_WRITE(dspsurf, Start); +		I915_READ(dspsurf); +	} else { +		I915_WRITE(dspbase, Start + Offset); +		I915_READ(dspbase); +	} +	 + +	if (!dev->primary->master) +		return; + +	master_priv = dev->primary->master->driver_priv; +	if (!master_priv->sarea_priv)  +		return; +		 +	switch (pipe) { +	case 0: +		master_priv->sarea_priv->planeA_x = x; +		master_priv->sarea_priv->planeA_y = y; +		break; +	case 1: +		master_priv->sarea_priv->planeB_x = x; +		master_priv->sarea_priv->planeB_y = y; +		break; +	default: +		DRM_ERROR("Can't update pipe %d in SAREA\n", pipe); +		break; +	} +} + + + +/** + * Sets the power management mode of the pipe and plane. + * + * This code should probably grow support for turning the cursor off and back + * on appropriately at the same time as we're turning the pipe off/on. + */ +static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) +{ +	struct drm_device *dev = crtc->dev; +	struct drm_i915_master_private *master_priv; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +	int pipe = intel_crtc->pipe; +	int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; +	int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; +	int dspbase_reg = (pipe == 0) ? DSPAADDR : DSPBADDR; +	int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; +	u32 temp; +	bool enabled; + +	/* XXX: When our outputs are all unaware of DPMS modes other than off +	 * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. +	 */ +	switch (mode) { +	case DRM_MODE_DPMS_ON: +	case DRM_MODE_DPMS_STANDBY: +	case DRM_MODE_DPMS_SUSPEND: +		/* Enable the DPLL */ +		temp = I915_READ(dpll_reg); +		if ((temp & DPLL_VCO_ENABLE) == 0) { +			I915_WRITE(dpll_reg, temp); +			I915_READ(dpll_reg); +			/* Wait for the clocks to stabilize. */ +			udelay(150); +			I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); +			I915_READ(dpll_reg); +			/* Wait for the clocks to stabilize. */ +			udelay(150); +			I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); +			I915_READ(dpll_reg); +			/* Wait for the clocks to stabilize. */ +			udelay(150); +		} +		 +		/* Enable the pipe */ +		temp = I915_READ(pipeconf_reg); +		if ((temp & PIPEACONF_ENABLE) == 0) +			I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE); +		 +		/* Enable the plane */ +		temp = I915_READ(dspcntr_reg); +		if ((temp & DISPLAY_PLANE_ENABLE) == 0) { +			I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE); +			/* Flush the plane changes */ +			I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); +		} +		 +		intel_crtc_load_lut(crtc); +		 +		/* Give the overlay scaler a chance to enable if it's on this pipe */ +		//intel_crtc_dpms_video(crtc, true); TODO +	break; +	case DRM_MODE_DPMS_OFF: +		/* Give the overlay scaler a chance to disable if it's on this pipe */ +		//intel_crtc_dpms_video(crtc, FALSE); TODO +		 +		/* Disable the VGA plane that we never use */ +		I915_WRITE(VGACNTRL, VGA_DISP_DISABLE); +		 +		/* Disable display plane */ +		temp = I915_READ(dspcntr_reg); +		if ((temp & DISPLAY_PLANE_ENABLE) != 0) { +			I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE); +			/* Flush the plane changes */ +			I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); +			I915_READ(dspbase_reg); +		} +		 +		if (!IS_I9XX(dev)) { +			/* Wait for vblank for the disable to take effect */ +			intel_wait_for_vblank(dev); +		} +		 +		/* Next, disable display pipes */ +		temp = I915_READ(pipeconf_reg); +		if ((temp & PIPEACONF_ENABLE) != 0) { +			I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE); +			I915_READ(pipeconf_reg); +		} +		 +		/* Wait for vblank for the disable to take effect. */ +		intel_wait_for_vblank(dev); +		 +		temp = I915_READ(dpll_reg); +		if ((temp & DPLL_VCO_ENABLE) != 0) { +			I915_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE); +			I915_READ(dpll_reg); +		} +		 +		/* Wait for the clocks to turn off. */ +		udelay(150); +		break; +	} + +	if (!dev->primary->master) +		return;	 + +	master_priv = dev->primary->master->driver_priv; +	if (!master_priv->sarea_priv) +		return; + +	enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF; +	 +	switch (pipe) { +	case 0: +		master_priv->sarea_priv->planeA_w = enabled ? crtc->mode.hdisplay : 0; +		master_priv->sarea_priv->planeA_h = enabled ? crtc->mode.vdisplay : 0; +		break; +	case 1: +		master_priv->sarea_priv->planeB_w = enabled ? crtc->mode.hdisplay : 0; +		master_priv->sarea_priv->planeB_h = enabled ? crtc->mode.vdisplay : 0; +		break; +	default: +		DRM_ERROR("Can't update pipe %d in SAREA\n", pipe); +		break; +	} + +	intel_crtc->dpms_mode = mode; +} + +static void intel_crtc_prepare (struct drm_crtc *crtc) +{ +	struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; +	crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); +} + +static void intel_crtc_commit (struct drm_crtc *crtc) +{ +	struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; +	crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); +} + +void intel_encoder_prepare (struct drm_encoder *encoder) +{ +	struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; +	/* lvds has its own version of prepare see intel_lvds_prepare */ +	encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF); +} + +void intel_encoder_commit (struct drm_encoder *encoder) +{ +	struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; +	/* lvds has its own version of commit see intel_lvds_commit */ +	encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); +} + +static bool intel_crtc_mode_fixup(struct drm_crtc *crtc, +				  struct drm_display_mode *mode, +				  struct drm_display_mode *adjusted_mode) +{ +	return true; +} + + +/** Returns the core display clock speed for i830 - i945 */ +static int intel_get_core_clock_speed(struct drm_device *dev) +{ + +	/* Core clock values taken from the published datasheets. +	 * The 830 may go up to 166 Mhz, which we should check. +	 */ +	if (IS_I945G(dev)) +		return 400000; +	else if (IS_I915G(dev)) +		return 333000; +	else if (IS_I945GM(dev) || IS_845G(dev)) +		return 200000; +	else if (IS_I915GM(dev)) { +		u16 gcfgc = 0; + +		pci_read_config_word(dev->pdev, GCFGC, &gcfgc); +		 +		if (gcfgc & GC_LOW_FREQUENCY_ENABLE) +			return 133000; +		else { +			switch (gcfgc & GC_DISPLAY_CLOCK_MASK) { +			case GC_DISPLAY_CLOCK_333_MHZ: +				return 333000; +			default: +			case GC_DISPLAY_CLOCK_190_200_MHZ: +				return 190000; +			} +		} +	} else if (IS_I865G(dev)) +		return 266000; +	else if (IS_I855(dev)) { +#if 0 +		PCITAG bridge = pciTag(0, 0, 0); /* This is always the host bridge */ +		u16 hpllcc = pciReadWord(bridge, HPLLCC); +		 +#endif +		u16 hpllcc = 0; +		/* Assume that the hardware is in the high speed state.  This +		 * should be the default. +		 */ +		switch (hpllcc & GC_CLOCK_CONTROL_MASK) { +		case GC_CLOCK_133_200: +		case GC_CLOCK_100_200: +			return 200000; +		case GC_CLOCK_166_250: +			return 250000; +		case GC_CLOCK_100_133: +			return 133000; +		} +	} else /* 852, 830 */ +		return 133000; +	 +	return 0; /* Silence gcc warning */ +} + + +/** + * Return the pipe currently connected to the panel fitter, + * or -1 if the panel fitter is not present or not in use + */ +static int intel_panel_fitter_pipe (struct drm_device *dev) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	u32  pfit_control; +     +	/* i830 doesn't have a panel fitter */ +	if (IS_I830(dev)) +		return -1; +     +	pfit_control = I915_READ(PFIT_CONTROL); +     +	/* See if the panel fitter is in use */ +	if ((pfit_control & PFIT_ENABLE) == 0) +		return -1; +	 +	/* 965 can place panel fitter on either pipe */ +	if (IS_I965G(dev)) +		return (pfit_control >> 29) & 0x3; +	 +	/* older chips can only use pipe 1 */ +	return 1; +} + +static void intel_crtc_mode_set(struct drm_crtc *crtc, +				struct drm_display_mode *mode, +				struct drm_display_mode *adjusted_mode, +				int x, int y) +{ +	struct drm_device *dev = crtc->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +	int pipe = intel_crtc->pipe; +	int fp_reg = (pipe == 0) ? FPA0 : FPB0; +	int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; +	int dpll_md_reg = (intel_crtc->pipe == 0) ? DPLL_A_MD : DPLL_B_MD; +	int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; +	int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; +	int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; +	int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; +	int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; +	int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B; +	int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B; +	int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B; +	int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE; +	int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS; +	int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; +	int refclk; +	intel_clock_t clock; +	u32 dpll = 0, fp = 0, dspcntr, pipeconf; +	bool ok, is_sdvo = false, is_dvo = false; +	bool is_crt = false, is_lvds = false, is_tv = false; +	struct drm_mode_config *mode_config = &dev->mode_config; +	struct drm_connector *connector; + +	list_for_each_entry(connector, &mode_config->connector_list, head) { +		struct intel_output *intel_output = to_intel_output(connector); + +		if (!connector->encoder || connector->encoder->crtc != crtc) +			continue; + +		switch (intel_output->type) { +		case INTEL_OUTPUT_LVDS: +			is_lvds = true; +			break; +		case INTEL_OUTPUT_SDVO: +			is_sdvo = true; +			break; +		case INTEL_OUTPUT_DVO: +			is_dvo = true; +			break; +		case INTEL_OUTPUT_TVOUT: +			is_tv = true; +			break; +		case INTEL_OUTPUT_ANALOG: +			is_crt = true; +			break; +		} +	} +	 +	if (IS_I9XX(dev)) { +		refclk = 96000; +	} else { +		refclk = 48000; +	} + +	ok = intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, &clock); +	if (!ok) { +		DRM_ERROR("Couldn't find PLL settings for mode!\n"); +		return; +	} + +	fp = clock.n << 16 | clock.m1 << 8 | clock.m2; +	 +	dpll = DPLL_VGA_MODE_DIS; +	if (IS_I9XX(dev)) { +		if (is_lvds) +			dpll |= DPLLB_MODE_LVDS; +		else +			dpll |= DPLLB_MODE_DAC_SERIAL; +		if (is_sdvo) { +			dpll |= DPLL_DVO_HIGH_SPEED; +			if (IS_I945G(dev) || IS_I945GM(dev)) { +				int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; +				dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES; +			} +		} +		 +		/* compute bitmask from p1 value */ +		dpll |= (1 << (clock.p1 - 1)) << 16; +		switch (clock.p2) { +		case 5: +			dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5; +			break; +		case 7: +			dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7; +			break; +		case 10: +			dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10; +			break; +		case 14: +			dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14; +			break; +		} +		if (IS_I965G(dev)) +			dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT); +	} else { +		if (is_lvds) { +			dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; +		} else { +			if (clock.p1 == 2) +				dpll |= PLL_P1_DIVIDE_BY_TWO; +			else +				dpll |= (clock.p1 - 2) << DPLL_FPA01_P1_POST_DIV_SHIFT; +			if (clock.p2 == 4) +				dpll |= PLL_P2_DIVIDE_BY_4; +		} +	} +	 +	if (is_tv) { +		/* XXX: just matching BIOS for now */ +/*	dpll |= PLL_REF_INPUT_TVCLKINBC; */ +		dpll |= 3; +	} +#if 0 +	else if (is_lvds) +		dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; +#endif +	else +		dpll |= PLL_REF_INPUT_DREFCLK; +	 +	/* setup pipeconf */ +	pipeconf = I915_READ(pipeconf_reg); + +	/* Set up the display plane register */ +	dspcntr = DISPPLANE_GAMMA_ENABLE; + +	if (pipe == 0) +		dspcntr |= DISPPLANE_SEL_PIPE_A; +	else +		dspcntr |= DISPPLANE_SEL_PIPE_B; +	 +	if (pipe == 0 && !IS_I965G(dev)) { +		/* Enable pixel doubling when the dot clock is > 90% of the (display) +		 * core speed. +		 * +		 * XXX: No double-wide on 915GM pipe B. Is that the only reason for the +		 * pipe == 0 check? +		 */ +		if (mode->clock > intel_get_core_clock_speed(dev) * 9 / 10) +			pipeconf |= PIPEACONF_DOUBLE_WIDE; +		else +			pipeconf &= ~PIPEACONF_DOUBLE_WIDE; +	} + +	dspcntr |= DISPLAY_PLANE_ENABLE; +	pipeconf |= PIPEACONF_ENABLE; +	dpll |= DPLL_VCO_ENABLE; + +	 +	/* Disable the panel fitter if it was on our pipe */ +	if (intel_panel_fitter_pipe(dev) == pipe) +		I915_WRITE(PFIT_CONTROL, 0); + +	DRM_DEBUG("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); +	drm_mode_debug_printmodeline(mode); +	 +#if 0 +	if (!xf86ModesEqual(mode, adjusted_mode)) { +		xf86DrvMsg(pScrn->scrnIndex, X_INFO, +			   "Adjusted mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); +		xf86PrintModeline(pScrn->scrnIndex, mode); +	} +	i830PrintPll("chosen", &clock); +#endif + +	if (dpll & DPLL_VCO_ENABLE) { +		I915_WRITE(fp_reg, fp); +		I915_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE); +		I915_READ(dpll_reg); +		udelay(150); +	} +	 +	/* The LVDS pin pair needs to be on before the DPLLs are enabled. +	 * This is an exception to the general rule that mode_set doesn't turn +	 * things on. +	 */ +	if (is_lvds) { +		u32 lvds = I915_READ(LVDS); +		 +		lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT; +		/* Set the B0-B3 data pairs corresponding to whether we're going to +		 * set the DPLLs for dual-channel mode or not. +		 */ +		if (clock.p2 == 7) +			lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; +		else +			lvds &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); +		 +		/* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) +		 * appropriately here, but we need to look more thoroughly into how +		 * panels behave in the two modes. +		 */ +		 +		I915_WRITE(LVDS, lvds); +		I915_READ(LVDS); +	} +	 +	I915_WRITE(fp_reg, fp); +	I915_WRITE(dpll_reg, dpll); +	I915_READ(dpll_reg); +	/* Wait for the clocks to stabilize. */ +	udelay(150); +	 +	if (IS_I965G(dev)) { +		int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; +		I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) | +			   ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT)); +	} else { +		/* write it again -- the BIOS does, after all */ +		I915_WRITE(dpll_reg, dpll); +	} +	I915_READ(dpll_reg); +	/* Wait for the clocks to stabilize. */ +	udelay(150); +	 +	I915_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) | +		   ((adjusted_mode->crtc_htotal - 1) << 16)); +	I915_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | +		   ((adjusted_mode->crtc_hblank_end - 1) << 16)); +	I915_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | +		   ((adjusted_mode->crtc_hsync_end - 1) << 16)); +	I915_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | +		   ((adjusted_mode->crtc_vtotal - 1) << 16)); +	I915_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | +		   ((adjusted_mode->crtc_vblank_end - 1) << 16)); +	I915_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | +		   ((adjusted_mode->crtc_vsync_end - 1) << 16)); +	/* pipesrc and dspsize control the size that is scaled from, which should +	 * always be the user's requested size. +	 */ +	I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); +	I915_WRITE(dsppos_reg, 0); +	I915_WRITE(pipesrc_reg, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); +	I915_WRITE(pipeconf_reg, pipeconf); +	I915_READ(pipeconf_reg); +	 +	intel_wait_for_vblank(dev); +	 +	I915_WRITE(dspcntr_reg, dspcntr); +	 +	/* Flush the plane changes */ +	intel_pipe_set_base(crtc, x, y); +	 +	intel_set_vblank(dev); + +	intel_wait_for_vblank(dev);     +} + +/** Loads the palette/gamma unit for the CRTC with the prepared values */ +void intel_crtc_load_lut(struct drm_crtc *crtc) +{ +	struct drm_device *dev = crtc->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +	int palreg = (intel_crtc->pipe == 0) ? PALETTE_A : PALETTE_B; +	int i; + +	/* The clocks have to be on to load the palette. */ +	if (!crtc->enabled) +		return; + +	for (i = 0; i < 256; i++) { +		I915_WRITE(palreg + 4 * i, +			   (intel_crtc->lut_r[i] << 16) | +			   (intel_crtc->lut_g[i] << 8) | +			   intel_crtc->lut_b[i]); +	} +} + +static int intel_crtc_cursor_set(struct drm_crtc *crtc, +				 struct drm_file *file_priv, +				 uint32_t handle, +				 uint32_t width, uint32_t height) +{ +	struct drm_device *dev = crtc->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +	struct drm_buffer_object *bo; +	int pipe = intel_crtc->pipe; +	uint32_t control = (pipe == 0) ? CURACNTR : CURBCNTR; +	uint32_t base = (pipe == 0) ? CURABASE : CURBBASE; +	uint32_t temp; +	int ret; +	size_t addr; + +	DRM_DEBUG("\n"); + +	/* if we want to turn of the cursor ignore width and height */ +	if (!handle) { +		DRM_DEBUG("cursor off\n"); +		/* turn of the cursor */ +		temp = 0; +		temp |= CURSOR_MODE_DISABLE; + +		I915_WRITE(control, temp); +		I915_WRITE(base, 0); +		return 0; +	} + +	/* Currently we only support 64x64 cursors */ +	if (width != 64 || height != 64) { +		DRM_ERROR("we currently only support 64x64 cursors\n"); +		return -EINVAL; +	} + +	ret = drm_get_buffer_object(dev, &bo, handle); +	if (ret) { +		return -EINVAL; +	} + +	if ((bo->mem.flags & DRM_BO_MASK_MEM) != DRM_BO_FLAG_MEM_VRAM) { +		DRM_ERROR("buffer needs to be in VRAM\n"); +		return -ENOMEM; +	} + +	if (bo->mem.size < width * height * 4) { +		DRM_ERROR("buffer is to small\n"); +		return -ENOMEM; +	} + +	if (dev_priv->cursor_needs_physical) +		addr = dev_priv->stolen_base + bo->offset; +	else +		addr = bo->offset; + +	intel_crtc->cursor_addr = addr; +	temp = 0; +	/* set the pipe for the cursor */ +	temp |= (pipe << 28); +	temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE; + +	I915_WRITE(control, temp); +	I915_WRITE(base, addr); + +	return 0; +} + +static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) +{ +	struct drm_device *dev = crtc->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +	int pipe = intel_crtc->pipe; +	uint32_t temp = 0; +	uint32_t adder; + +	if (x < 0) { +		temp |= (CURSOR_POS_SIGN << CURSOR_X_SHIFT); +		x = -x; +	} +	if (y < 0) { +		temp |= (CURSOR_POS_SIGN << CURSOR_Y_SHIFT); +		y = -y; +	} + +	temp |= ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT); +	temp |= ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT); + +	adder = intel_crtc->cursor_addr; +	I915_WRITE((pipe == 0) ? CURAPOS : CURBPOS, temp); +	I915_WRITE((pipe == 0) ? CURABASE : CURBBASE, adder); + +	return 0; +} + +/** Sets the color ramps on behalf of RandR */ +void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, +				 u16 blue, int regno) +{ +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + +	intel_crtc->lut_r[regno] = red >> 8; +	intel_crtc->lut_g[regno] = green >> 8; +	intel_crtc->lut_b[regno] = blue >> 8; +} + +static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, +				 u16 *blue, uint32_t size) +{ +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +	int i; + +	if (size != 256) +		return; + +	for (i = 0; i < 256; i++) { +		intel_crtc->lut_r[i] = red[i] >> 8; +		intel_crtc->lut_g[i] = green[i] >> 8; +		intel_crtc->lut_b[i] = blue[i] >> 8; +	} + +	intel_crtc_load_lut(crtc); +} + +/** + * Get a pipe with a simple mode set on it for doing load-based monitor + * detection. + * + * It will be up to the load-detect code to adjust the pipe as appropriate for + * its requirements.  The pipe will be connected to no other outputs. + * + * Currently this code will only succeed if there is a pipe with no outputs + * configured for it.  In the future, it could choose to temporarily disable + * some outputs to free up a pipe for its use. + * + * \return crtc, or NULL if no pipes are available. + */ +     +/* VESA 640x480x72Hz mode to set on the pipe */ +static struct drm_display_mode load_detect_mode = { +	DRM_MODE("640x480", DRM_MODE_TYPE_DEFAULT, 31500, 640, 664, +		 704, 832, 0, 480, 489, 491, 520, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), +}; + +struct drm_crtc *intel_get_load_detect_pipe(struct intel_output *intel_output, +					    struct drm_display_mode *mode, +					    int *dpms_mode) +{ +	struct intel_crtc *intel_crtc; +	struct drm_crtc *possible_crtc; +	struct drm_crtc *supported_crtc =NULL; +	struct drm_encoder *encoder = &intel_output->enc; +	struct drm_crtc *crtc = NULL; +	struct drm_device *dev = encoder->dev; +	struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; +	struct drm_crtc_helper_funcs *crtc_funcs; +	int i = -1; + +	/* +	 * Algorithm gets a little messy: +	 *   - if the connector already has an assigned crtc, use it (but make +	 *     sure it's on first) +	 *   - try to find the first unused crtc that can drive this connector, +	 *     and use that if we find one +	 *   - if there are no unused crtcs available, try to use the first +	 *     one we found that supports the connector +	 */ + +	/* See if we already have a CRTC for this connector */ +	if (encoder->crtc) { +		crtc = encoder->crtc; +		/* Make sure the crtc and connector are running */ +		intel_crtc = to_intel_crtc(crtc); +		*dpms_mode = intel_crtc->dpms_mode; +		if (intel_crtc->dpms_mode != DRM_MODE_DPMS_ON) { +			crtc_funcs = crtc->helper_private; +			crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); +			encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); +		} +		return crtc; +	} + +	/* Find an unused one (if possible) */ +	list_for_each_entry(possible_crtc, &dev->mode_config.crtc_list, head) { +		i++; +		if (!(encoder->possible_crtcs & (1 << i))) +			continue; +		if (!possible_crtc->enabled) { +			crtc = possible_crtc; +			break; +		} +		if (!supported_crtc) +			supported_crtc = possible_crtc; +	} + +	/* +	 * If we didn't find an unused CRTC, don't use any. +	 */ +	if (!crtc) { +		return NULL; +	} + +	encoder->crtc = crtc; +	intel_output->load_detect_temp = true; +     +	intel_crtc = to_intel_crtc(crtc); +	*dpms_mode = intel_crtc->dpms_mode; + +	if (!crtc->enabled) { +		if (!mode) +			mode = &load_detect_mode; +		drm_crtc_helper_set_mode(crtc, mode, 0, 0); +	} else { +		if (intel_crtc->dpms_mode != DRM_MODE_DPMS_ON) { +			crtc_funcs = crtc->helper_private; +			crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); +		} + +		/* Add this connector to the crtc */ +		encoder_funcs->mode_set(encoder, &crtc->mode, &crtc->mode); +		encoder_funcs->commit(encoder); +	} +	/* let the connector get through one full cycle before testing */ +	intel_wait_for_vblank(dev); + +	return crtc; +} + +void intel_release_load_detect_pipe(struct intel_output *intel_output, int dpms_mode) +{ +	struct drm_encoder *encoder = &intel_output->enc; +	struct drm_device *dev = encoder->dev; +	struct drm_crtc *crtc = encoder->crtc; +	struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; +	struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; +     +	if (intel_output->load_detect_temp) { +		encoder->crtc = NULL; +		intel_output->load_detect_temp = false; +		crtc->enabled = drm_helper_crtc_in_use(crtc); +		drm_helper_disable_unused_functions(dev); +	} + +	/* Switch crtc and output back off if necessary */ +	if (crtc->enabled && dpms_mode != DRM_MODE_DPMS_ON) { +		if (encoder->crtc == crtc) +			encoder_funcs->dpms(encoder, dpms_mode); +		crtc_funcs->dpms(crtc, dpms_mode); +	} +} + +/* Returns the clock of the currently programmed mode of the given pipe. */ +static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +	int pipe = intel_crtc->pipe; +	u32 dpll = I915_READ((pipe == 0) ? DPLL_A : DPLL_B); +	u32 fp; +	intel_clock_t clock; + +	if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) +		fp = I915_READ((pipe == 0) ? FPA0 : FPB0); +	else +		fp = I915_READ((pipe == 0) ? FPA1 : FPB1); + +	clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT; +	clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT; +	clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT; +	if (IS_I9XX(dev)) { +		clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK) >> +			       DPLL_FPA01_P1_POST_DIV_SHIFT); + +		switch (dpll & DPLL_MODE_MASK) { +		case DPLLB_MODE_DAC_SERIAL: +			clock.p2 = dpll & DPLL_DAC_SERIAL_P2_CLOCK_DIV_5 ? +				5 : 10; +			break; +		case DPLLB_MODE_LVDS: +			clock.p2 = dpll & DPLLB_LVDS_P2_CLOCK_DIV_7 ? +				7 : 14; +			break; +		default: +			DRM_DEBUG("Unknown DPLL mode %08x in programmed " +				  "mode\n", (int)(dpll & DPLL_MODE_MASK)); +			return 0; +		} + +		/* XXX: Handle the 100Mhz refclk */ +		i9xx_clock(96000, &clock); +	} else { +		bool is_lvds = (pipe == 1) && (I915_READ(LVDS) & LVDS_PORT_EN); + +		if (is_lvds) { +			clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >> +				       DPLL_FPA01_P1_POST_DIV_SHIFT); +			clock.p2 = 14; + +			if ((dpll & PLL_REF_INPUT_MASK) == +			    PLLB_REF_INPUT_SPREADSPECTRUMIN) { +				/* XXX: might not be 66MHz */ +				i8xx_clock(66000, &clock); +			} else +				i8xx_clock(48000, &clock);		 +		} else { +			if (dpll & PLL_P1_DIVIDE_BY_TWO) +				clock.p1 = 2; +			else { +				clock.p1 = ((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830) >> +					    DPLL_FPA01_P1_POST_DIV_SHIFT) + 2; +			} +			if (dpll & PLL_P2_DIVIDE_BY_4) +				clock.p2 = 4; +			else +				clock.p2 = 2; + +			i8xx_clock(48000, &clock); +		} +	} + +	/* XXX: It would be nice to validate the clocks, but we can't reuse +	 * i830PllIsValid() because it relies on the xf86_config connector +	 * configuration being accurate, which it isn't necessarily. +	 */ + +	return clock.dot; +} + +/** Returns the currently programmed mode of the given pipe. */ +struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev, +					     struct drm_crtc *crtc) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +	int pipe = intel_crtc->pipe; +	struct drm_display_mode *mode; +	int htot = I915_READ((pipe == 0) ? HTOTAL_A : HTOTAL_B); +	int hsync = I915_READ((pipe == 0) ? HSYNC_A : HSYNC_B); +	int vtot = I915_READ((pipe == 0) ? VTOTAL_A : VTOTAL_B); +	int vsync = I915_READ((pipe == 0) ? VSYNC_A : VSYNC_B); + +	mode = kzalloc(sizeof(*mode), GFP_KERNEL); +	if (!mode) +		return NULL; + +	mode->clock = intel_crtc_clock_get(dev, crtc); +	mode->hdisplay = (htot & 0xffff) + 1; +	mode->htotal = ((htot & 0xffff0000) >> 16) + 1; +	mode->hsync_start = (hsync & 0xffff) + 1; +	mode->hsync_end = ((hsync & 0xffff0000) >> 16) + 1; +	mode->vdisplay = (vtot & 0xffff) + 1; +	mode->vtotal = ((vtot & 0xffff0000) >> 16) + 1; +	mode->vsync_start = (vsync & 0xffff) + 1; +	mode->vsync_end = ((vsync & 0xffff0000) >> 16) + 1; + +	drm_mode_set_name(mode); +	drm_mode_set_crtcinfo(mode, 0); + +	return mode; +} + +static void intel_crtc_destroy(struct drm_crtc *crtc) +{ +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + +	drm_crtc_cleanup(crtc); +	kfree(intel_crtc); +} + +static const struct drm_crtc_helper_funcs intel_helper_funcs = { +	.dpms = intel_crtc_dpms, +	.mode_fixup = intel_crtc_mode_fixup, +	.mode_set = intel_crtc_mode_set, +	.mode_set_base = intel_pipe_set_base, +	.prepare = intel_crtc_prepare, +	.commit = intel_crtc_commit, +}; + +static const struct drm_crtc_funcs intel_crtc_funcs = { +	.cursor_set = intel_crtc_cursor_set, +	.cursor_move = intel_crtc_cursor_move, +	.gamma_set = intel_crtc_gamma_set, +	.set_config = drm_crtc_helper_set_config, +	.destroy = intel_crtc_destroy, +}; + + +void intel_crtc_init(struct drm_device *dev, int pipe) +{ +	struct intel_crtc *intel_crtc; +	int i; + +	intel_crtc = kzalloc(sizeof(struct intel_crtc) + (INTELFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL); +	if (intel_crtc == NULL) +		return; + +	drm_crtc_init(dev, &intel_crtc->base, &intel_crtc_funcs); + +	drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256); +	intel_crtc->pipe = pipe; +	for (i = 0; i < 256; i++) { +		intel_crtc->lut_r[i] = i; +		intel_crtc->lut_g[i] = i; +		intel_crtc->lut_b[i] = i; +	} + +	intel_crtc->cursor_addr = 0; +	intel_crtc->dpms_mode = DRM_MODE_DPMS_OFF; +	drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs); + +	intel_crtc->mode_set.crtc = &intel_crtc->base; +	intel_crtc->mode_set.connectors = (struct drm_connector **)(intel_crtc + 1); +	intel_crtc->mode_set.num_connectors = 0; + +	if (i915_fbpercrtc) { +		 + + +	} +} + +struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe) +{ +	struct drm_crtc *crtc = NULL; + +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +		if (intel_crtc->pipe == pipe) +			break; +	} +	return crtc; +} + +int intel_connector_clones(struct drm_device *dev, int type_mask) +{ +	int index_mask = 0; +	struct drm_connector *connector; +	int entry = 0; + +        list_for_each_entry(connector, &dev->mode_config.connector_list, head) { +		struct intel_output *intel_output = to_intel_output(connector); +		if (type_mask & (1 << intel_output->type)) +			index_mask |= (1 << entry); +		entry++; +	} +	return index_mask; +} + + +static void intel_setup_outputs(struct drm_device *dev) +{ +	struct drm_connector *connector; + +	intel_crt_init(dev); + +	/* Set up integrated LVDS */ +	if (IS_MOBILE(dev) && !IS_I830(dev)) +		intel_lvds_init(dev); + +	if (IS_I9XX(dev)) { +		intel_sdvo_init(dev, SDVOB); +		intel_sdvo_init(dev, SDVOC); +	} else +		intel_dvo_init(dev); + +	if (IS_I9XX(dev) && !IS_I915G(dev)) +		intel_tv_init(dev); + +	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { +		struct intel_output *intel_output = to_intel_output(connector); +		struct drm_encoder *encoder = &intel_output->enc; +		int crtc_mask = 0, clone_mask = 0; +		 +		/* valid crtcs */ +		switch(intel_output->type) { +		case INTEL_OUTPUT_DVO: +		case INTEL_OUTPUT_SDVO: +			crtc_mask = ((1 << 0)| +				     (1 << 1)); +			clone_mask = ((1 << INTEL_OUTPUT_ANALOG) | +				      (1 << INTEL_OUTPUT_DVO) | +				      (1 << INTEL_OUTPUT_SDVO)); +			break; +		case INTEL_OUTPUT_ANALOG: +			crtc_mask = ((1 << 0)| +				     (1 << 1)); +			clone_mask = ((1 << INTEL_OUTPUT_ANALOG) | +				      (1 << INTEL_OUTPUT_DVO) | +				      (1 << INTEL_OUTPUT_SDVO)); +			break; +		case INTEL_OUTPUT_LVDS: +			crtc_mask = (1 << 1); +			clone_mask = (1 << INTEL_OUTPUT_LVDS); +			break; +		case INTEL_OUTPUT_TVOUT: +			crtc_mask = ((1 << 0) | +				     (1 << 1)); +			clone_mask = (1 << INTEL_OUTPUT_TVOUT); +			break; +		} +		encoder->possible_crtcs = crtc_mask; +		encoder->possible_clones = intel_connector_clones(dev, clone_mask); +	} +} + +static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb) +{ +	struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); +	struct drm_device *dev = fb->dev; +	if (fb->fbdev) +		intelfb_remove(dev, fb); + +	drm_framebuffer_cleanup(fb); + +	kfree(intel_fb); +} +       +static const struct drm_framebuffer_funcs intel_fb_funcs = { +	.destroy = intel_user_framebuffer_destroy, +}; + +struct drm_framebuffer *intel_user_framebuffer_create(struct drm_device *dev, +						      struct drm_file *filp, +						      struct drm_mode_fb_cmd *mode_cmd) +{ +	struct intel_framebuffer *intel_fb; + +	intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL); +	if (!intel_fb) +		return NULL; + +	drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs); +	drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd); + +	if (filp) { +		intel_fb->obj = drm_gem_object_lookup(dev, filp, +						      mode_cmd->handle); +		if (!intel_fb->obj) { +			kfree(intel_fb); +			return NULL; +		} +	} +	drm_gem_object_unreference(intel_fb->obj); +	return &intel_fb->base; +} + +static int intel_insert_new_fb(struct drm_device *dev, struct drm_file *file_priv, +				struct drm_framebuffer *fb, struct drm_mode_fb_cmd *mode_cmd) +{ +	struct intel_framebuffer *intel_fb; +	struct drm_gem_object *obj; +	struct drm_crtc *crtc; + +	intel_fb = to_intel_framebuffer(fb); + +	mutex_lock(&dev->struct_mutex); +	obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle); +	 +	if (!obj) { +		mutex_unlock(&dev->struct_mutex); +		return -EINVAL; +	} +	drm_helper_mode_fill_fb_struct(fb, mode_cmd); + +	drm_gem_object_unreference(intel_fb->obj); +	drm_gem_object_unreference(obj); +	mutex_unlock(&dev->struct_mutex); + +	intel_fb->obj = obj; + +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		if (crtc->fb == fb) { +			struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; +			crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y); +		} +	} +	return 0; +} + +static const struct drm_mode_config_funcs intel_mode_funcs = { +	.resize_fb = intel_insert_new_fb, +	.fb_create = intel_user_framebuffer_create, +	.fb_changed = intelfb_probe, +}; + +void intel_modeset_init(struct drm_device *dev) +{ +	int num_pipe; +	int i; + +	drm_mode_config_init(dev); + +	dev->mode_config.min_width = 0; +	dev->mode_config.min_height = 0; + +	dev->mode_config.funcs = (void *)&intel_mode_funcs; + +	if (IS_I965G(dev)) { +		dev->mode_config.max_width = 8192; +		dev->mode_config.max_height = 8192; +	} else { +		dev->mode_config.max_width = 2048; +		dev->mode_config.max_height = 2048; +	} + +	/* set memory base */ +	if (IS_I9XX(dev)) +		dev->mode_config.fb_base = pci_resource_start(dev->pdev, 2); +	else +		dev->mode_config.fb_base = pci_resource_start(dev->pdev, 0); + +	if (IS_MOBILE(dev) || IS_I9XX(dev)) +		num_pipe = 2; +	else +		num_pipe = 1; +	DRM_DEBUG("%d display pipe%s available.\n", +		  num_pipe, num_pipe > 1 ? "s" : ""); + +	for (i = 0; i < num_pipe; i++) { +		intel_crtc_init(dev, i); +	} + +	intel_setup_outputs(dev); + +	/* setup fbs */ +	//drm_initial_config(dev, false); +} + +void intel_modeset_cleanup(struct drm_device *dev) +{ +	drm_mode_config_cleanup(dev); +} + + +/* current intel driver doesn't take advantage of encoders +   always give back the encoder for the connector +*/ +struct drm_encoder *intel_best_encoder(struct drm_connector *connector) +{ +	struct intel_output *intel_output = to_intel_output(connector); + +	return &intel_output->enc; +} diff --git a/linux-core/intel_drv.h b/linux-core/intel_drv.h new file mode 100644 index 00000000..bffbeef0 --- /dev/null +++ b/linux-core/intel_drv.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2006 Dave Airlie <airlied@linux.ie> + * Copyright (c) 2007 Intel Corporation + *   Jesse Barnes <jesse.barnes@intel.com> + */ +#ifndef __INTEL_DRV_H__ +#define __INTEL_DRV_H__ + +#include <linux/i2c.h> +#include <linux/i2c-id.h> +#include <linux/i2c-algo-bit.h> +#include "drm_crtc.h" + +#include "drm_crtc_helper.h" +/* + * Display related stuff + */ + +/* store information about an Ixxx DVO */ +/* The i830->i865 use multiple DVOs with multiple i2cs */ +/* the i915, i945 have a single sDVO i2c bus - which is different */ +#define MAX_OUTPUTS 6 +/* maximum connectors per crtcs in the mode set */ +#define INTELFB_CONN_LIMIT 4 + +#define INTEL_I2C_BUS_DVO 1 +#define INTEL_I2C_BUS_SDVO 2 + +/* these are outputs from the chip - integrated only  +   external chips are via DVO or SDVO output */ +#define INTEL_OUTPUT_UNUSED 0 +#define INTEL_OUTPUT_ANALOG 1 +#define INTEL_OUTPUT_DVO 2 +#define INTEL_OUTPUT_SDVO 3 +#define INTEL_OUTPUT_LVDS 4 +#define INTEL_OUTPUT_TVOUT 5 + +#define INTEL_DVO_CHIP_NONE 0 +#define INTEL_DVO_CHIP_LVDS 1 +#define INTEL_DVO_CHIP_TMDS 2 +#define INTEL_DVO_CHIP_TVOUT 4 + +struct intel_i2c_chan { +	struct drm_device *drm_dev; /* for getting at dev. private (mmio etc.) */ +	u32 reg; /* GPIO reg */ +	struct i2c_adapter adapter; +	struct i2c_algo_bit_data algo; +        u8 slave_addr; +}; + +struct intel_framebuffer { +	struct drm_framebuffer base; +	struct drm_gem_object *obj; +}; + + +struct intel_output { +	struct drm_connector base; + +	struct drm_encoder enc; +	int type; +	struct intel_i2c_chan *i2c_bus; /* for control functions */ +	struct intel_i2c_chan *ddc_bus; /* for DDC only stuff */ +	bool load_detect_temp; +	void *dev_priv; +}; + +struct intel_crtc { +	struct drm_crtc base; +	int pipe; +	int plane; +	uint32_t cursor_addr; +	u8 lut_r[256], lut_g[256], lut_b[256]; +	int dpms_mode; +	struct intel_framebuffer *fbdev_fb; +	/* a mode_set for fbdev users on this crtc */ +	struct drm_mode_set mode_set; +}; + +#define to_intel_crtc(x) container_of(x, struct intel_crtc, base) +#define to_intel_output(x) container_of(x, struct intel_output, base) +#define enc_to_intel_output(x) container_of(x, struct intel_output, enc) +#define to_intel_framebuffer(x) container_of(x, struct intel_framebuffer, base) + +struct intel_i2c_chan *intel_i2c_create(struct drm_device *dev, const u32 reg, +					const char *name); +void intel_i2c_destroy(struct intel_i2c_chan *chan); +int intel_ddc_get_modes(struct intel_output *intel_output); +extern bool intel_ddc_probe(struct intel_output *intel_output); + +extern void intel_crt_init(struct drm_device *dev); +extern void intel_sdvo_init(struct drm_device *dev, int output_device); +extern void intel_dvo_init(struct drm_device *dev); +extern void intel_tv_init(struct drm_device *dev); +extern void intel_lvds_init(struct drm_device *dev); + +extern void intel_crtc_load_lut(struct drm_crtc *crtc); +extern void intel_encoder_prepare (struct drm_encoder *encoder); +extern void intel_encoder_commit (struct drm_encoder *encoder); + +extern struct drm_encoder *intel_best_encoder(struct drm_connector *connector); + +extern struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev, + 						    struct drm_crtc *crtc); +extern void intel_wait_for_vblank(struct drm_device *dev); +extern struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe); +extern struct drm_crtc *intel_get_load_detect_pipe(struct intel_output *intel_output, +						   struct drm_display_mode *mode, +						   int *dpms_mode); +extern void intel_release_load_detect_pipe(struct intel_output *intel_output, +					   int dpms_mode); + +extern struct drm_connector* intel_sdvo_find(struct drm_device *dev, int sdvoB); +extern int intel_sdvo_supports_hotplug(struct drm_connector *connector); +extern void intel_sdvo_set_hotplug(struct drm_connector *connector, int enable); +extern int intelfb_probe(struct drm_device *dev); +extern int intelfb_remove(struct drm_device *dev, struct drm_framebuffer *fb); +extern int intelfb_resize(struct drm_device *dev, struct drm_crtc *crtc); +extern void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, +				    u16 blue, int regno); + +extern struct drm_framebuffer *intel_user_framebuffer_create(struct drm_device *dev, +							     struct drm_file *file_priv, +							     struct drm_mode_fb_cmd *mode_cmd); +#endif /* __INTEL_DRV_H__ */ diff --git a/linux-core/intel_dvo.c b/linux-core/intel_dvo.c new file mode 100644 index 00000000..39ec65d2 --- /dev/null +++ b/linux-core/intel_dvo.c @@ -0,0 +1,507 @@ +/* + * Copyright © 2006-2007 Intel Corporation + * + * 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 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + *	Eric Anholt <eric@anholt.net> + */ +/* + * Copyright 2006 Dave Airlie <airlied@linux.ie> + */ + +#include <linux/i2c.h> +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "intel_drv.h" +#include "i915_drm.h" +#include "i915_drv.h" +#include "dvo.h" + +#define SIL164_ADDR	0x38 +#define CH7xxx_ADDR	0x76 +#define TFP410_ADDR	0x38 + +extern struct intel_dvo_dev_ops sil164_ops; +extern struct intel_dvo_dev_ops ch7xxx_ops; +extern struct intel_dvo_dev_ops ivch_ops; +extern struct intel_dvo_dev_ops tfp410_ops; +extern struct intel_dvo_dev_ops ch7017_ops; + +struct intel_dvo_device intel_dvo_devices[] = { +	{ +		.type = INTEL_DVO_CHIP_TMDS, +		.name = "sil164", +		.dvo_reg = DVOC, +		.slave_addr = SIL164_ADDR, +		.dev_ops = &sil164_ops, +	}, +	{ +		.type = INTEL_DVO_CHIP_TMDS, +		.name = "ch7xxx", +		.dvo_reg = DVOC, +		.slave_addr = CH7xxx_ADDR, +		.dev_ops = &ch7xxx_ops, +	}, +	{ +		.type = INTEL_DVO_CHIP_LVDS, +		.name = "ivch", +		.dvo_reg = DVOA, +		.slave_addr = 0x02, /* Might also be 0x44, 0x84, 0xc4 */ +		.dev_ops = &ivch_ops, +	}, +	{ +		.type = INTEL_DVO_CHIP_TMDS, +		.name = "tfp410", +		.dvo_reg = DVOC, +		.slave_addr = TFP410_ADDR, +		.dev_ops = &tfp410_ops, +	}, +	{ +		.type = INTEL_DVO_CHIP_LVDS, +		.name = "ch7017", +		.dvo_reg = DVOC, +		.slave_addr = 0x75, +		.gpio = GPIOE, +		.dev_ops = &ch7017_ops, +	} +}; + +static void intel_dvo_dpms(struct drm_encoder *encoder, int mode) +{ +	struct drm_i915_private *dev_priv = encoder->dev->dev_private; +	struct intel_output *intel_output = enc_to_intel_output(encoder); +	struct intel_dvo_device *dvo = intel_output->dev_priv; +	u32 dvo_reg = dvo->dvo_reg; +	u32 temp = I915_READ(dvo_reg); + +	if (mode == DRM_MODE_DPMS_ON) { +		I915_WRITE(dvo_reg, temp | DVO_ENABLE); +		I915_READ(dvo_reg); +		dvo->dev_ops->dpms(dvo, mode); +	} else { +		dvo->dev_ops->dpms(dvo, mode); +		I915_WRITE(dvo_reg, temp & ~DVO_ENABLE); +		I915_READ(dvo_reg); +	} +} + +static void intel_dvo_save(struct drm_connector *connector) +{ +	struct drm_i915_private *dev_priv = connector->dev->dev_private; +	struct intel_output *intel_output = to_intel_output(connector); +	struct intel_dvo_device *dvo = intel_output->dev_priv; + +	/* Each output should probably just save the registers it touches, +	 * but for now, use more overkill. +	 */ +	dev_priv->saveDVOA = I915_READ(DVOA); +	dev_priv->saveDVOB = I915_READ(DVOB); +	dev_priv->saveDVOC = I915_READ(DVOC); + +	dvo->dev_ops->save(dvo); +} + +static void intel_dvo_restore(struct drm_connector *connector) +{ +	struct drm_i915_private *dev_priv = connector->dev->dev_private; +	struct intel_output *intel_output = to_intel_output(connector); +	struct intel_dvo_device *dvo = intel_output->dev_priv; + +	dvo->dev_ops->restore(dvo); + +	I915_WRITE(DVOA, dev_priv->saveDVOA); +	I915_WRITE(DVOB, dev_priv->saveDVOB); +	I915_WRITE(DVOC, dev_priv->saveDVOC); +} + +static int intel_dvo_mode_valid(struct drm_connector *connector, +				struct drm_display_mode *mode) +{ +	struct intel_output *intel_output = to_intel_output(connector); +	struct intel_dvo_device *dvo = intel_output->dev_priv; + +	if (mode->flags & DRM_MODE_FLAG_DBLSCAN) +		return MODE_NO_DBLESCAN; + +	/* XXX: Validate clock range */ + +	if (dvo->panel_fixed_mode) { +		if (mode->hdisplay > dvo->panel_fixed_mode->hdisplay) +			return MODE_PANEL; +		if (mode->vdisplay > dvo->panel_fixed_mode->vdisplay) +			return MODE_PANEL; +	} + +	return dvo->dev_ops->mode_valid(dvo, mode); +} + +static bool intel_dvo_mode_fixup(struct drm_encoder *encoder, +				 struct drm_display_mode *mode, +				 struct drm_display_mode *adjusted_mode) +{ +	struct intel_output *intel_output = enc_to_intel_output(encoder); +	struct intel_dvo_device *dvo = intel_output->dev_priv; + +	/* If we have timings from the BIOS for the panel, put them in +	 * to the adjusted mode.  The CRTC will be set up for this mode, +	 * with the panel scaling set up to source from the H/VDisplay +	 * of the original mode. +	 */ +	if (dvo->panel_fixed_mode != NULL) { +#define C(x) adjusted_mode->x = dvo->panel_fixed_mode->x +		C(hdisplay); +		C(hsync_start); +		C(hsync_end); +		C(htotal); +		C(vdisplay); +		C(vsync_start); +		C(vsync_end); +		C(vtotal); +		C(clock); +		drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); +#undef C +	} + +	if (dvo->dev_ops->mode_fixup) +		return dvo->dev_ops->mode_fixup(dvo, mode, adjusted_mode); + +	return true; +} + +static void intel_dvo_mode_set(struct drm_encoder *encoder, +			       struct drm_display_mode *mode, +			       struct drm_display_mode *adjusted_mode) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); +	struct intel_output *intel_output = enc_to_intel_output(encoder); +	struct intel_dvo_device *dvo = intel_output->dev_priv; +	int pipe = intel_crtc->pipe; +	u32 dvo_val; +	u32 dvo_reg = dvo->dvo_reg, dvo_srcdim_reg; +	int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; + +	switch (dvo_reg) { +	case DVOA: +	default: +		dvo_srcdim_reg = DVOA_SRCDIM; +		break; +	case DVOB: +		dvo_srcdim_reg = DVOB_SRCDIM; +		break; +	case DVOC: +		dvo_srcdim_reg = DVOC_SRCDIM; +		break; +	} + +	dvo->dev_ops->mode_set(dvo, mode, adjusted_mode); + +	/* Save the data order, since I don't know what it should be set to. */ +	dvo_val = I915_READ(dvo_reg) & +		  (DVO_PRESERVE_MASK | DVO_DATA_ORDER_GBRG); +	dvo_val |= DVO_DATA_ORDER_FP | DVO_BORDER_ENABLE | +		   DVO_BLANK_ACTIVE_HIGH; + +	if (pipe == 1) +		dvo_val |= DVO_PIPE_B_SELECT; +	dvo_val |= DVO_PIPE_STALL; +	if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) +		dvo_val |= DVO_HSYNC_ACTIVE_HIGH; +	if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) +		dvo_val |= DVO_VSYNC_ACTIVE_HIGH; + +	I915_WRITE(dpll_reg, I915_READ(dpll_reg) | DPLL_DVO_HIGH_SPEED); + +	/*I915_WRITE(DVOB_SRCDIM, +	  (adjusted_mode->hdisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) | +	  (adjusted_mode->VDisplay << DVO_SRCDIM_VERTICAL_SHIFT));*/ +	I915_WRITE(dvo_srcdim_reg, +		   (adjusted_mode->hdisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) | +		   (adjusted_mode->vdisplay << DVO_SRCDIM_VERTICAL_SHIFT)); +	/*I915_WRITE(DVOB, dvo_val);*/ +	I915_WRITE(dvo_reg, dvo_val); +} + +/** + * Detect the output connection on our DVO device. + * + * Unimplemented. + */ +static enum drm_connector_status intel_dvo_detect(struct drm_connector *connector) +{ +	struct intel_output *intel_output = to_intel_output(connector); +	struct intel_dvo_device *dvo = intel_output->dev_priv; + +	return dvo->dev_ops->detect(dvo); +} + +static int intel_dvo_get_modes(struct drm_connector *connector) +{ +	struct intel_output *intel_output = to_intel_output(connector); +	struct intel_dvo_device *dvo = intel_output->dev_priv; + +	/* We should probably have an i2c driver get_modes function for those +	 * devices which will have a fixed set of modes determined by the chip +	 * (TV-out, for example), but for now with just TMDS and LVDS, +	 * that's not the case. +	 */ +	intel_ddc_get_modes(intel_output); +	if (!list_empty(&connector->probed_modes)) +		return 1; + +#if 0 +	if (intel_output->i2c_drv->vid_rec->get_modes) +	{ +		modes = intel_output->i2c_drv->vid_rec->get_modes (intel_output->i2c_drv->dev_priv); +		if (modes != NULL) +			return modes; +	} +#endif + +	if (dvo->panel_fixed_mode != NULL) { +		struct drm_display_mode *mode; +		mode = drm_mode_duplicate(connector->dev, dvo->panel_fixed_mode); +		if (mode) { +			drm_mode_probed_add(connector, mode); +			return 1; +		} +	} +	return 0; +} + +static void intel_dvo_destroy (struct drm_connector *connector) +{ +	struct intel_output *intel_output = to_intel_output(connector); +	struct intel_dvo_device *dvo = intel_output->dev_priv; + +	if (dvo) { +		if (dvo->dev_ops->destroy) +			dvo->dev_ops->destroy(dvo); +		if (dvo->panel_fixed_mode) +			kfree(dvo->panel_fixed_mode); +		/* no need, in i830_dvoices[] now */ +		//kfree(dvo); +	} +	if (intel_output->i2c_bus) +		intel_i2c_destroy(intel_output->i2c_bus); +	if (intel_output->ddc_bus) +		intel_i2c_destroy(intel_output->ddc_bus); +	drm_sysfs_connector_remove(connector); +	drm_connector_cleanup(connector); +	kfree(intel_output); +} + +#ifdef RANDR_GET_CRTC_INTERFACE +static struct drm_crtc *intel_dvo_get_crtc(struct drm_connector *connector) +{ +	struct drm_device *dev = connector->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_output *intel_output = to_intel_output(connector); +	struct intel_dvo_device *dvo = intel_output->dev_priv; +	int pipe = !!(I915_READ(dvo->dvo_reg) & SDVO_PIPE_B_SELECT); + +	return intel_pipe_to_crtc(pScrn, pipe); +} +#endif + +static const struct drm_encoder_helper_funcs intel_dvo_helper_funcs = { +	.dpms = intel_dvo_dpms, +	.mode_fixup = intel_dvo_mode_fixup, +	.prepare = intel_encoder_prepare, +	.mode_set = intel_dvo_mode_set, +	.commit = intel_encoder_commit, +}; + +static const struct drm_connector_funcs intel_dvo_connector_funcs = { +	.save = intel_dvo_save, +	.restore = intel_dvo_restore, +	.detect = intel_dvo_detect, +	.destroy = intel_dvo_destroy, +	.fill_modes = drm_helper_probe_single_connector_modes, +}; + +static const struct drm_connector_helper_funcs intel_dvo_connector_helper_funcs = { +	.mode_valid = intel_dvo_mode_valid, +	.get_modes = intel_dvo_get_modes, +	.best_encoder = intel_best_encoder, +}; + +void intel_dvo_enc_destroy(struct drm_encoder *encoder) +{ +	drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs intel_dvo_enc_funcs = { +	.destroy = intel_dvo_enc_destroy, +}; + + +/** + * Attempts to get a fixed panel timing for LVDS (currently only the i830). + * + * Other chips with DVO LVDS will need to extend this to deal with the LVDS + * chip being on DVOB/C and having multiple pipes. + */ +static struct drm_display_mode * +intel_dvo_get_current_mode (struct drm_connector *connector) +{ +	struct drm_device *dev = connector->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_output *intel_output = to_intel_output(connector); +	struct intel_dvo_device *dvo = intel_output->dev_priv; +	uint32_t dvo_reg = dvo->dvo_reg; +	uint32_t dvo_val = I915_READ(dvo_reg); +	struct drm_display_mode *mode = NULL; + +	/* If the DVO port is active, that'll be the LVDS, so we can pull out +	 * its timings to get how the BIOS set up the panel. +	 */ +	if (dvo_val & DVO_ENABLE) { +		struct drm_crtc *crtc; +		int pipe = (dvo_val & DVO_PIPE_B_SELECT) ? 1 : 0; + +		crtc = intel_get_crtc_from_pipe(dev, pipe); +		if (crtc) { +			mode = intel_crtc_mode_get(dev, crtc); + +			if (mode) { +				mode->type |= DRM_MODE_TYPE_PREFERRED; +				if (dvo_val & DVO_HSYNC_ACTIVE_HIGH) +					mode->flags |= DRM_MODE_FLAG_PHSYNC; +				if (dvo_val & DVO_VSYNC_ACTIVE_HIGH) +					mode->flags |= DRM_MODE_FLAG_PVSYNC; +			} +		} +	} +	return mode; +} + +void intel_dvo_init(struct drm_device *dev) +{ +	struct intel_output *intel_output; +	struct intel_dvo_device *dvo; +	struct intel_i2c_chan *i2cbus = NULL; +	int ret = 0; +	int i; +	int gpio_inited = 0; +	int encoder_type = DRM_MODE_ENCODER_NONE; +	intel_output = kzalloc (sizeof(struct intel_output), GFP_KERNEL); +	if (!intel_output) +		return; + +	/* Set up the DDC bus */ +	intel_output->ddc_bus = intel_i2c_create(dev, GPIOD, "DVODDC_D"); +	if (!intel_output->ddc_bus) +		goto free_intel; + +	/* Now, try to find a controller */ +	for (i = 0; i < ARRAY_SIZE(intel_dvo_devices); i++) { +		struct drm_connector *connector = &intel_output->base; +		int gpio; + +		dvo = &intel_dvo_devices[i]; + +		/* Allow the I2C driver info to specify the GPIO to be used in +		 * special cases, but otherwise default to what's defined +		 * in the spec. +		 */ +		if (dvo->gpio != 0) +			gpio = dvo->gpio; +		else if (dvo->type == INTEL_DVO_CHIP_LVDS) +			gpio = GPIOB; +		else +			gpio = GPIOE; + +		/* Set up the I2C bus necessary for the chip we're probing. +		 * It appears that everything is on GPIOE except for panels +		 * on i830 laptops, which are on GPIOB (DVOA). +		 */ +		if (gpio_inited != gpio) { +			if (i2cbus != NULL) +				intel_i2c_destroy(i2cbus); +			if (!(i2cbus = intel_i2c_create(dev, gpio, +				gpio == GPIOB ? "DVOI2C_B" : "DVOI2C_E"))) { +				continue; +			} +			gpio_inited = gpio; +		} + +		if (dvo->dev_ops!= NULL) +			ret = dvo->dev_ops->init(dvo, i2cbus); +		else +			ret = false; + +		if (!ret) +			continue; + +		intel_output->type = INTEL_OUTPUT_DVO; +		switch (dvo->type) { +		case INTEL_DVO_CHIP_TMDS: +		  //			connector = DRM_MODE_CONNECTOR_DVID; +			drm_connector_init(dev, connector, &intel_dvo_connector_funcs, +					   DRM_MODE_CONNECTOR_DVII); +			encoder_type = DRM_MODE_ENCODER_TMDS; +			break; +		case INTEL_DVO_CHIP_LVDS: +		  //			connector = DRM_MODE_CONNECTOR_LVDS; +			drm_connector_init(dev, connector, &intel_dvo_connector_funcs, +					   DRM_MODE_CONNECTOR_LVDS); +			encoder_type = DRM_MODE_ENCODER_LVDS; +			break; +		} + +		drm_connector_helper_add(connector, &intel_dvo_connector_helper_funcs); +		connector->display_info.subpixel_order = SubPixelHorizontalRGB; +		connector->interlace_allowed = false; +		connector->doublescan_allowed = false; + +		intel_output->dev_priv = dvo; +		intel_output->i2c_bus = i2cbus; + +		drm_encoder_init(dev, &intel_output->enc, &intel_dvo_enc_funcs, encoder_type); +		drm_encoder_helper_add(&intel_output->enc, &intel_dvo_helper_funcs); + +		drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc); +		if (dvo->type == INTEL_DVO_CHIP_LVDS) { +			/* For our LVDS chipsets, we should hopefully be able +			 * to dig the fixed panel mode out of the BIOS data. +			 * However, it's in a different format from the BIOS +			 * data on chipsets with integrated LVDS (stored in AIM +			 * headers, likely), so for now, just get the current +			 * mode being output through DVO. +			 */ +			dvo->panel_fixed_mode = intel_dvo_get_current_mode(connector); +			dvo->panel_wants_dither = true; +		} + +		drm_sysfs_connector_add(connector); +		return; +	} + +	intel_i2c_destroy(intel_output->ddc_bus); +	/* Didn't find a chip, so tear down. */ +	if (i2cbus != NULL) +		intel_i2c_destroy(i2cbus); +free_intel: +	kfree(intel_output); +} diff --git a/linux-core/intel_fb.c b/linux-core/intel_fb.c new file mode 100644 index 00000000..ce8ac3d9 --- /dev/null +++ b/linux-core/intel_fb.c @@ -0,0 +1,1144 @@ +/* + * Copyright © 2007 David Airlie + * + * 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 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + *     David Airlie + */ +    /* +     *  Modularization +     */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/init.h> + +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "intel_drv.h" +#include "i915_drm.h" +#include "i915_drv.h" + +struct intelfb_par { +	struct drm_device *dev; +	struct drm_display_mode *our_mode; +	struct intel_framebuffer *intel_fb; +	int crtc_count; +	/* crtc currently bound to this */ +	uint32_t crtc_ids[2]; +}; +/* +static int +var_to_refresh(const struct fb_var_screeninfo *var) +{ +	int xtot = var->xres + var->left_margin + var->right_margin + +		var->hsync_len; +	int ytot = var->yres + var->upper_margin + var->lower_margin + +		var->vsync_len; + +	return (1000000000 / var->pixclock * 1000 + 500) / xtot / ytot; +}*/ + +static int intelfb_setcolreg(unsigned regno, unsigned red, unsigned green, +			unsigned blue, unsigned transp, +			struct fb_info *info) +{ +	struct intelfb_par *par = info->par; +	struct drm_device *dev = par->dev; +	struct drm_crtc *crtc; +	int i; + +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +		struct drm_mode_set *modeset = &intel_crtc->mode_set; +		struct drm_framebuffer *fb = modeset->fb; + +		for (i = 0; i < par->crtc_count; i++) +			if (crtc->base.id == par->crtc_ids[i]) +				break; + +		if (i == par->crtc_count) +			continue; +		 + +		if (regno > 255) +			return 1; + +		if (fb->depth == 8) { +			intel_crtc_fb_gamma_set(crtc, red, green, blue, regno); +			return 0; +		} + +		if (regno < 16) { +			switch (fb->depth) { +			case 15: +				fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) | +					((green & 0xf800) >>  6) | +					((blue & 0xf800) >> 11); +				break; +			case 16: +				fb->pseudo_palette[regno] = (red & 0xf800) | +					((green & 0xfc00) >>  5) | +					((blue  & 0xf800) >> 11); +				break; +			case 24: +			case 32: +				fb->pseudo_palette[regno] = ((red & 0xff00) << 8) | +					(green & 0xff00) | +					((blue  & 0xff00) >> 8); +				break; +			} +		} +	} +	return 0; +} + +static int intelfb_check_var(struct fb_var_screeninfo *var, +			struct fb_info *info) +{ +	struct intelfb_par *par = info->par; +	struct intel_framebuffer *intel_fb = par->intel_fb; +	struct drm_framebuffer *fb = &intel_fb->base; +	int depth; + +	if (var->pixclock == -1 || !var->pixclock) +		return -EINVAL; + +	/* Need to resize the fb object !!! */ +	if (var->xres > fb->width || var->yres > fb->height) { +		DRM_ERROR("Requested width/height is greater than current fb object %dx%d > %dx%d\n",var->xres,var->yres,fb->width,fb->height); +		DRM_ERROR("Need resizing code.\n"); +		return -EINVAL; +	} + +	switch (var->bits_per_pixel) { +	case 16: +		depth = (var->green.length == 6) ? 16 : 15; +		break; +	case 32: +		depth = (var->transp.length > 0) ? 32 : 24; +		break; +	default: +		depth = var->bits_per_pixel; +		break; +	} +		 +	switch (depth) { +	case 8: +		var->red.offset = 0; +		var->green.offset = 0; +		var->blue.offset = 0; +		var->red.length = 8; +		var->green.length = 8; +		var->blue.length = 8; +		var->transp.length = 0; +		var->transp.offset = 0; +		break; +	case 15: +		var->red.offset = 10; +		var->green.offset = 5; +		var->blue.offset = 0; +		var->red.length = 5; +		var->green.length = 5; +		var->blue.length = 5; +		var->transp.length = 1; +		var->transp.offset = 15; +		break; +	case 16: +		var->red.offset = 11; +		var->green.offset = 5; +		var->blue.offset = 0; +		var->red.length = 5; +		var->green.length = 6; +		var->blue.length = 5; +		var->transp.length = 0; +		var->transp.offset = 0; +		break; +	case 24: +		var->red.offset = 16; +		var->green.offset = 8; +		var->blue.offset = 0; +		var->red.length = 8; +		var->green.length = 8; +		var->blue.length = 8; +		var->transp.length = 0; +		var->transp.offset = 0; +		break; +	case 32: +		var->red.offset = 16; +		var->green.offset = 8; +		var->blue.offset = 0; +		var->red.length = 8; +		var->green.length = 8; +		var->blue.length = 8; +		var->transp.length = 8; +		var->transp.offset = 24; +		break; +	default: +		return -EINVAL;  +	} + +	return 0; +} + +/* this will let fbcon do the mode init */ +/* FIXME: take mode config lock? */ +static int intelfb_set_par(struct fb_info *info) +{ +	struct intelfb_par *par = info->par; +	struct drm_device *dev = par->dev; +	struct fb_var_screeninfo *var = &info->var; +	int i; + +	DRM_DEBUG("%d %d\n", var->xres, var->pixclock); + +	if (var->pixclock != -1) { + +		DRM_ERROR("PIXEL CLCOK SET\n"); +#if 0 +		struct intel_framebuffer *intel_fb = par->intel_fb; +		struct drm_framebuffer *fb = &intel_fb->base; +		struct drm_display_mode *drm_mode, *search_mode; +		struct drm_connector *connector = NULL; +		struct drm_device *dev = par->dev; + +		int found = 0; + +		switch (var->bits_per_pixel) { +		case 16: +			fb->depth = (var->green.length == 6) ? 16 : 15; +			break; +		case 32: +			fb->depth = (var->transp.length > 0) ? 32 : 24; +			break; +		default: +			fb->depth = var->bits_per_pixel; +			break; +		} +		 +		fb->bits_per_pixel = var->bits_per_pixel; +		 +		info->fix.line_length = fb->pitch; +		info->fix.smem_len = info->fix.line_length * fb->height; +		info->fix.visual = (fb->depth == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; +		 +		info->screen_size = info->fix.smem_len; /* ??? */ +		/* reuse desired mode if possible */ +		/* create a drm mode */ +		drm_mode = drm_mode_create(dev); +		drm_mode->hdisplay = var->xres; +		drm_mode->hsync_start = drm_mode->hdisplay + var->right_margin; +		drm_mode->hsync_end = drm_mode->hsync_start + var->hsync_len; +		drm_mode->htotal = drm_mode->hsync_end + var->left_margin; +		drm_mode->vdisplay = var->yres; +		drm_mode->vsync_start = drm_mode->vdisplay + var->lower_margin; +		drm_mode->vsync_end = drm_mode->vsync_start + var->vsync_len; +		drm_mode->vtotal = drm_mode->vsync_end + var->upper_margin; +		drm_mode->clock = PICOS2KHZ(var->pixclock); +		drm_mode->vrefresh = drm_mode_vrefresh(drm_mode); +		drm_mode->flags = 0; +		drm_mode->flags |= var->sync & FB_SYNC_HOR_HIGH_ACT ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; +		drm_mode->flags |= var->sync & FB_SYNC_VERT_HIGH_ACT ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; +		 +		drm_mode_set_name(drm_mode); +		drm_mode_set_crtcinfo(drm_mode, CRTC_INTERLACE_HALVE_V); +		 +		found = 0; +		list_for_each_entry(connector, &dev->mode_config.connector_list, head) { +			if (connector->encoder && +			    connector->encoder->crtc == par->set.crtc){ +				found = 1; +				break; +			} +		} +		 +		/* no connector bound, bail */ +		if (!found) +			return -EINVAL; +		 +		found = 0; +		drm_mode_debug_printmodeline(drm_mode); +		list_for_each_entry(search_mode, &connector->modes, head) { +			drm_mode_debug_printmodeline(search_mode); +			if (drm_mode_equal(drm_mode, search_mode)) { +				drm_mode_destroy(dev, drm_mode); +				drm_mode = search_mode; +				found = 1; +				break; +			} +		} +		 +		/* If we didn't find a matching mode that exists on our connector, +		 * create a new attachment for the incoming user specified mode +		 */ +		if (!found) { +			if (par->our_mode) { +				/* this also destroys the mode */ +				drm_mode_detachmode_crtc(dev, par->our_mode); +			} +			 +			par->set.mode = drm_mode; +			par->our_mode = drm_mode; +			drm_mode_debug_printmodeline(drm_mode); +			/* attach mode */ +			drm_mode_attachmode_crtc(dev, par->set.crtc, par->set.mode); +		} else { +			par->set.mode = drm_mode; +			if (par->our_mode) +				drm_mode_detachmode_crtc(dev, par->our_mode); +			par->our_mode = NULL; +		} +		return par->set.crtc->funcs->set_config(&par->set); +#endif +		return -EINVAL; +	} else { +		struct drm_crtc *crtc; +		int ret; + +		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +			struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + +			for (i = 0; i < par->crtc_count; i++) +				if (crtc->base.id == par->crtc_ids[i]) +					break; + +			if (i == par->crtc_count) +				continue; + +			if (crtc->fb == intel_crtc->mode_set.fb) { +				ret = crtc->funcs->set_config(&intel_crtc->mode_set); +				if (ret) +					return ret; +			} +		} +		return 0; +	} +} + +#if 0 +static void intelfb_copyarea(struct fb_info *info, +			const struct fb_copyarea *region) +{ +	struct intelfb_par *par = info->par; +	struct drm_device *dev = par->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	u32 src_x1, src_y1, dst_x1, dst_y1, dst_x2, dst_y2, offset; +	u32 cmd, rop_depth_pitch, src_pitch; +	RING_LOCALS; + +	cmd = XY_SRC_COPY_BLT_CMD; +	src_x1 = region->sx; +	src_y1 = region->sy; +	dst_x1 = region->dx; +	dst_y1 = region->dy; +	dst_x2 = region->dx + region->width; +	dst_y2 = region->dy + region->height; +	offset = par->fb->offset; +	rop_depth_pitch = BLT_ROP_GXCOPY | par->fb->pitch; +	src_pitch = par->fb->pitch; + +	switch (par->fb->bits_per_pixel) { +	case 16: +		rop_depth_pitch |= BLT_DEPTH_16_565; +		break; +	case 32: +		rop_depth_pitch |= BLT_DEPTH_32; +		cmd |= XY_SRC_COPY_BLT_WRITE_ALPHA | XY_SRC_COPY_BLT_WRITE_RGB; +		break; +	} + +	BEGIN_LP_RING(8); +	OUT_RING(cmd); +	OUT_RING(rop_depth_pitch); +	OUT_RING((dst_y1 << 16) | (dst_x1 & 0xffff)); +	OUT_RING((dst_y2 << 16) | (dst_x2 & 0xffff)); +	OUT_RING(offset); +	OUT_RING((src_y1 << 16) | (src_x1 & 0xffff)); +	OUT_RING(src_pitch); +	OUT_RING(offset); +	ADVANCE_LP_RING(); +} + +#define ROUND_UP_TO(x, y)	(((x) + (y) - 1) / (y) * (y)) +#define ROUND_DOWN_TO(x, y)	((x) / (y) * (y)) + +void intelfb_imageblit(struct fb_info *info, const struct fb_image *image) +{ +	struct intelfb_par *par = info->par; +	struct drm_device *dev = par->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	u32 cmd, rop_pitch_depth, tmp; +	int nbytes, ndwords, pad; +	u32 dst_x1, dst_y1, dst_x2, dst_y2, offset, bg, fg; +	int dat, ix, iy, iw; +	int i, j; +	RING_LOCALS; + +	/* size in bytes of a padded scanline */ +	nbytes = ROUND_UP_TO(image->width, 16) / 8; + +	/* Total bytes of padded scanline data to write out. */ +	nbytes *= image->height; + +	/* +	* Check if the glyph data exceeds the immediate mode limit. +	* It would take a large font (1K pixels) to hit this limit. +	*/ +	if (nbytes > 128 || image->depth != 1) +		return cfb_imageblit(info, image); + +	/* Src data is packaged a dword (32-bit) at a time. */ +	ndwords = ROUND_UP_TO(nbytes, 4) / 4; + +	/* +	* Ring has to be padded to a quad word. But because the command starts +	with 7 bytes, pad only if there is an even number of ndwords +	*/ +	pad = !(ndwords % 2); + +	DRM_DEBUG("imageblit %dx%dx%d to (%d,%d)\n", image->width, +		image->height, image->depth, image->dx, image->dy); +	DRM_DEBUG("nbytes: %d, ndwords: %d, pad: %d\n", nbytes, ndwords, pad); + +	tmp = (XY_MONO_SRC_COPY_IMM_BLT & 0xff) + ndwords; +	cmd = (XY_MONO_SRC_COPY_IMM_BLT & ~0xff) | tmp; +	offset = par->fb->offset; +	dst_x1 = image->dx; +	dst_y1 = image->dy; +	dst_x2 = image->dx + image->width; +	dst_y2 = image->dy + image->height; +	rop_pitch_depth = BLT_ROP_GXCOPY | par->fb->pitch; + +	switch (par->fb->bits_per_pixel) { +	case 8: +		rop_pitch_depth |= BLT_DEPTH_8; +		fg = image->fg_color; +		bg = image->bg_color; +		break; +	case 16: +		rop_pitch_depth |= BLT_DEPTH_16_565; +		fg = par->fb->pseudo_palette[image->fg_color]; +		bg = par->fb->pseudo_palette[image->bg_color]; +		break; +	case 32: +		rop_pitch_depth |= BLT_DEPTH_32; +		cmd |= XY_SRC_COPY_BLT_WRITE_ALPHA | XY_SRC_COPY_BLT_WRITE_RGB; +		fg = par->fb->pseudo_palette[image->fg_color]; +		bg = par->fb->pseudo_palette[image->bg_color]; +		break; +	default: +		DRM_ERROR("unknown depth %d\n", par->fb->bits_per_pixel); +		break; +	} +	 +	BEGIN_LP_RING(8 + ndwords); +	OUT_RING(cmd); +	OUT_RING(rop_pitch_depth); +	OUT_RING((dst_y1 << 16) | (dst_x1 & 0xffff)); +	OUT_RING((dst_y2 << 16) | (dst_x2 & 0xffff)); +	OUT_RING(offset); +	OUT_RING(bg); +	OUT_RING(fg); +	ix = iy = 0; +	iw = ROUND_UP_TO(image->width, 8) / 8; +	while (ndwords--) { +		dat = 0; +		for (j = 0; j < 2; ++j) { +			for (i = 0; i < 2; ++i) { +				if (ix != iw || i == 0) +					dat |= image->data[iy*iw + ix++] << (i+j*2)*8; +			} +			if (ix == iw && iy != (image->height - 1)) { +				ix = 0; +				++iy; +			} +		} +		OUT_RING(dat); +	} +	if (pad) +		OUT_RING(MI_NOOP); +	ADVANCE_LP_RING(); +} +#endif +static int intelfb_pan_display(struct fb_var_screeninfo *var, +				struct fb_info *info) +{ +	struct intelfb_par *par = info->par; +	struct drm_device *dev = par->dev; +	struct drm_mode_set *modeset; +	struct drm_crtc *crtc; +	struct intel_crtc *intel_crtc; +	int ret = 0; +	int i; + +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		 +		for (i = 0; i < par->crtc_count; i++) +			if (crtc->base.id == par->crtc_ids[i]) +				break; + +		if (i == par->crtc_count) +			continue; + +		intel_crtc = to_intel_crtc(crtc); +		modeset = &intel_crtc->mode_set; + +		modeset->x = var->xoffset; +		modeset->y = var->yoffset; + +		if (modeset->num_connectors) { +			ret = crtc->funcs->set_config(modeset); +		   +			if (!ret) { +				info->var.xoffset = var->xoffset; +				info->var.yoffset = var->yoffset; +			} +		} +	} + +	return ret; +} + +static void intelfb_on(struct fb_info *info) +{ +	struct intelfb_par *par = info->par; +	struct drm_device *dev = par->dev; +	struct drm_crtc *crtc; +	struct drm_encoder *encoder; +	int i; + +	/* +	 * For each CRTC in this fb, find all associated encoders +	 * and turn them off, then turn off the CRTC. +	 */ +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + +		for (i = 0; i < par->crtc_count; i++) +			if (crtc->base.id == par->crtc_ids[i]) +				break; + +		crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); + +		/* Found a CRTC on this fb, now find encoders */ +		list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { +			if (encoder->crtc == crtc) { +				struct drm_encoder_helper_funcs *encoder_funcs; +				encoder_funcs = encoder->helper_private; +				encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); +			} +		} +	} +} + +static void intelfb_off(struct fb_info *info, int dpms_mode) +{ +	struct intelfb_par *par = info->par; +	struct drm_device *dev = par->dev; +	struct drm_crtc *crtc; +	struct drm_encoder *encoder; +	int i; + +	/* +	 * For each CRTC in this fb, find all associated encoders +	 * and turn them off, then turn off the CRTC. +	 */ +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + +		for (i = 0; i < par->crtc_count; i++) +			if (crtc->base.id == par->crtc_ids[i]) +				break; + +		/* Found a CRTC on this fb, now find encoders */ +		list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { +			if (encoder->crtc == crtc) { +				struct drm_encoder_helper_funcs *encoder_funcs; +				encoder_funcs = encoder->helper_private; +				encoder_funcs->dpms(encoder, dpms_mode); +			} +		} +		if (dpms_mode == DRM_MODE_DPMS_OFF) +			crtc_funcs->dpms(crtc, dpms_mode); +	} +} + +int intelfb_blank(int blank, struct fb_info *info) +{ +	switch (blank) { +	case FB_BLANK_UNBLANK: +		intelfb_on(info); +		break; +	case FB_BLANK_NORMAL: +		intelfb_off(info, DRM_MODE_DPMS_STANDBY); +		break; +	case FB_BLANK_HSYNC_SUSPEND: +		intelfb_off(info, DRM_MODE_DPMS_STANDBY); +		break; +	case FB_BLANK_VSYNC_SUSPEND: +		intelfb_off(info, DRM_MODE_DPMS_SUSPEND); +		break; +	case FB_BLANK_POWERDOWN: +		intelfb_off(info, DRM_MODE_DPMS_OFF); +		break; +	} +	return 0; +} + +static struct fb_ops intelfb_ops = { +	.owner = THIS_MODULE, +	//.fb_open = intelfb_open, +	//.fb_read = intelfb_read, +	//.fb_write = intelfb_write, +	//.fb_release = intelfb_release, +	//.fb_ioctl = intelfb_ioctl, +	.fb_check_var = intelfb_check_var, +	.fb_set_par = intelfb_set_par, +	.fb_setcolreg = intelfb_setcolreg, +	.fb_fillrect = cfb_fillrect, +	.fb_copyarea = cfb_copyarea, //intelfb_copyarea, +	.fb_imageblit = cfb_imageblit, //intelfb_imageblit, +	.fb_pan_display = intelfb_pan_display, +	.fb_blank = intelfb_blank, +}; + +/** + * Curretly it is assumed that the old framebuffer is reused. + * + * LOCKING + * caller should hold the mode config lock. + * + */ +int intelfb_resize(struct drm_device *dev, struct drm_crtc *crtc) +{ +	struct fb_info *info; +	struct drm_framebuffer *fb; +	struct drm_display_mode *mode = crtc->desired_mode; + +	fb = crtc->fb; +	if (!fb) +		return 1; + +	info = fb->fbdev; +	if (!info) +		return 1; + +	if (!mode) +		return 1; + +	info->var.xres = mode->hdisplay; +	info->var.right_margin = mode->hsync_start - mode->hdisplay; +	info->var.hsync_len = mode->hsync_end - mode->hsync_start; +	info->var.left_margin = mode->htotal - mode->hsync_end; +	info->var.yres = mode->vdisplay; +	info->var.lower_margin = mode->vsync_start - mode->vdisplay; +	info->var.vsync_len = mode->vsync_end - mode->vsync_start; +	info->var.upper_margin = mode->vtotal - mode->vsync_end; +	info->var.pixclock = 10000000 / mode->htotal * 1000 / mode->vtotal * 100; +	/* avoid overflow */ +	info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh; + +	return 0; +} +EXPORT_SYMBOL(intelfb_resize); + +static struct drm_mode_set panic_mode; + +int intelfb_panic(struct notifier_block *n, unsigned long ununsed, +		  void *panic_str) +{ +	DRM_ERROR("panic occurred, switching back to text console\n"); +	drm_crtc_helper_set_config(&panic_mode); + +	return 0; +} +EXPORT_SYMBOL(intelfb_panic); +  +static struct notifier_block paniced = { +	.notifier_call = intelfb_panic, +}; + +int intelfb_create(struct drm_device *dev, uint32_t fb_width, uint32_t fb_height,  +		   uint32_t surface_width, uint32_t surface_height, +		   struct intel_framebuffer **intel_fb_p) +{ +	struct fb_info *info; +	struct intelfb_par *par; +	struct drm_framebuffer *fb; +	struct intel_framebuffer *intel_fb; +	struct drm_mode_fb_cmd mode_cmd; +	struct drm_gem_object *fbo = NULL; +	struct drm_i915_gem_object *obj_priv; +	struct device *device = &dev->pdev->dev;  +	int size, aligned_size, ret; + +	mode_cmd.width = surface_width;/* crtc->desired_mode->hdisplay; */ +	mode_cmd.height = surface_height;/* crtc->desired_mode->vdisplay; */ +	 +	mode_cmd.bpp = 32; +	mode_cmd.pitch = mode_cmd.width * ((mode_cmd.bpp + 1) / 8); +	mode_cmd.depth = 24; + +	size = mode_cmd.pitch * mode_cmd.height; +	aligned_size = ALIGN(size, PAGE_SIZE); +	fbo = drm_gem_object_alloc(dev, aligned_size); +	if (!fbo) { +		printk(KERN_ERR "failed to allocate framebuffer\n"); +		ret = -ENOMEM; +		goto out; +	} +	obj_priv = fbo->driver_private; + +	mutex_lock(&dev->struct_mutex); +	ret = i915_gem_object_pin(fbo, PAGE_SIZE); +	if (ret) { +		DRM_ERROR("failed to pin fb: %d\n", ret); +		goto out_unref; +	} + +	fb = intel_user_framebuffer_create(dev, NULL, &mode_cmd); +	if (!fb) { +		DRM_ERROR("failed to allocate fb.\n"); +		ret = -ENOMEM; +		goto out_unref; +	} + +	list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list); + +	intel_fb = to_intel_framebuffer(fb); +	*intel_fb_p = intel_fb; + +	intel_fb->obj = fbo; + +	info = framebuffer_alloc(sizeof(struct intelfb_par), device); +	if (!info) { +		ret = -ENOMEM; +		goto out_unref; +	} + +	par = info->par; + +	strcpy(info->fix.id, "inteldrmfb"); +	info->fix.type = FB_TYPE_PACKED_PIXELS; +	info->fix.visual = FB_VISUAL_TRUECOLOR; +	info->fix.type_aux = 0; +	info->fix.xpanstep = 1; /* doing it in hw */ +	info->fix.ypanstep = 1; /* doing it in hw */ +	info->fix.ywrapstep = 0; +	info->fix.accel = FB_ACCEL_I830; +	info->fix.type_aux = 0; + +	info->flags = FBINFO_DEFAULT; + +	info->fbops = &intelfb_ops; + +	info->fix.line_length = fb->pitch; +	info->fix.smem_start = dev->mode_config.fb_base + obj_priv->gtt_offset; +	info->fix.smem_len = size; + +	info->flags = FBINFO_DEFAULT; + + 	info->screen_base = ioremap_wc(dev->agp->base + obj_priv->gtt_offset, +				       size); +	if (!info->screen_base) { +		ret = -ENOSPC; +		goto out_unref; +	} +	info->screen_size = size; + +	memset(info->screen_base, 0, size); + +	info->pseudo_palette = fb->pseudo_palette; +	info->var.xres_virtual = fb->width; +	info->var.yres_virtual = fb->height; +	info->var.bits_per_pixel = fb->bits_per_pixel; +	info->var.xoffset = 0; +	info->var.yoffset = 0; +	info->var.activate = FB_ACTIVATE_NOW; +	info->var.height = -1; +	info->var.width = -1; + +	info->var.xres = fb_width; +	info->var.yres = fb_height; + + 	if (IS_I9XX(dev)) { + 		info->fix.mmio_start = pci_resource_start(dev->pdev, 0); + 		info->fix.mmio_len = pci_resource_len(dev->pdev, 0); + 	} else { + 		info->fix.mmio_start = pci_resource_start(dev->pdev, 1); + 		info->fix.mmio_len = pci_resource_len(dev->pdev, 1); + 	} + +	info->pixmap.size = 64*1024; +	info->pixmap.buf_align = 8; +	info->pixmap.access_align = 32; +	info->pixmap.flags = FB_PIXMAP_SYSTEM; +	info->pixmap.scan_align = 1; + +	DRM_DEBUG("fb depth is %d\n", fb->depth); +	DRM_DEBUG("   pitch is %d\n", fb->pitch); +	switch(fb->depth) { +	case 8: +		info->var.red.offset = 0; +		info->var.green.offset = 0; +		info->var.blue.offset = 0; +		info->var.red.length = 8; /* 8bit DAC */ +		info->var.green.length = 8; +		info->var.blue.length = 8; +		info->var.transp.offset = 0; +		info->var.transp.length = 0; +		break; + 	case 15: +		info->var.red.offset = 10; +		info->var.green.offset = 5; +		info->var.blue.offset = 0; +		info->var.red.length = 5; +		info->var.green.length = 5; +		info->var.blue.length = 5; +		info->var.transp.offset = 15; +		info->var.transp.length = 1; +		break; +	case 16: +		info->var.red.offset = 11; +		info->var.green.offset = 5; +		info->var.blue.offset = 0; +		info->var.red.length = 5; +		info->var.green.length = 6; +		info->var.blue.length = 5; +		info->var.transp.offset = 0; + 		break; +	case 24: +		info->var.red.offset = 16; +		info->var.green.offset = 8; +		info->var.blue.offset = 0; +		info->var.red.length = 8; +		info->var.green.length = 8; +		info->var.blue.length = 8; +		info->var.transp.offset = 0; +		info->var.transp.length = 0; +		break; +	case 32: +		info->var.red.offset = 16; +		info->var.green.offset = 8; +		info->var.blue.offset = 0; +		info->var.red.length = 8; +		info->var.green.length = 8; +		info->var.blue.length = 8; +		info->var.transp.offset = 24; +		info->var.transp.length = 8; +		break; +	default: +		break; +	} + +	fb->fbdev = info; + +	par->intel_fb = intel_fb; +	par->dev = dev; + +	/* To allow resizeing without swapping buffers */ +	printk("allocated %dx%d fb: 0x%08x, bo %p\n", intel_fb->base.width, +	       intel_fb->base.height, obj_priv->gtt_offset, fbo); + +	mutex_unlock(&dev->struct_mutex); +	return 0; + +out_unref: +	drm_gem_object_unreference(fbo); +	mutex_unlock(&dev->struct_mutex); +out: +	return ret; +} + +static int intelfb_multi_fb_probe_crtc(struct drm_device *dev, struct drm_crtc *crtc) +{ +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +	struct intel_framebuffer *intel_fb; +	struct drm_framebuffer *fb; +	struct drm_connector *connector; +	struct fb_info *info; +	struct intelfb_par *par; +	struct drm_mode_set *modeset; +	unsigned int width, height; +	int new_fb = 0; +	int ret, i, conn_count; + +	if (!drm_helper_crtc_in_use(crtc)) +		return 0; + +	if (!crtc->desired_mode) +		return 0; + +	width = crtc->desired_mode->hdisplay; +	height = crtc->desired_mode->vdisplay; + +	/* is there an fb bound to this crtc already */ +	if (!intel_crtc->mode_set.fb) { +		ret = intelfb_create(dev, width, height, width, height, &intel_fb); +		if (ret) +			return -EINVAL; +		new_fb = 1; +	} else { +		fb = intel_crtc->mode_set.fb; +		intel_fb = to_intel_framebuffer(fb); +		if ((intel_fb->base.width < width) || (intel_fb->base.height < height)) +			return -EINVAL; +	} +	 +	info = intel_fb->base.fbdev; +	par = info->par; + +	modeset = &intel_crtc->mode_set; +	modeset->fb = &intel_fb->base; +	conn_count = 0; +	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { +		if (connector->encoder) +			if (connector->encoder->crtc == modeset->crtc) { +				modeset->connectors[conn_count] = connector; +				conn_count++; +				if (conn_count > INTELFB_CONN_LIMIT) +					BUG(); +			} +	} +	 +	for (i = conn_count; i < INTELFB_CONN_LIMIT; i++) +		modeset->connectors[i] = NULL; + +	par->crtc_ids[0] = crtc->base.id; + +	modeset->num_connectors = conn_count; +	if (modeset->mode != modeset->crtc->desired_mode) +		modeset->mode = modeset->crtc->desired_mode; + +	par->crtc_count = 1; + +	if (new_fb) { +		info->var.pixclock = -1; +		if (register_framebuffer(info) < 0) +			return -EINVAL; +	} else +		intelfb_set_par(info); + +	printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, +	       info->fix.id); + +	/* Switch back to kernel console on panic */ +	panic_mode = *modeset; +	atomic_notifier_chain_register(&panic_notifier_list, &paniced); +	printk(KERN_INFO "registered panic notifier\n"); + +	return 0; +} + +static int intelfb_multi_fb_probe(struct drm_device *dev) +{ + +	struct drm_crtc *crtc; +	int ret = 0; + +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		ret = intelfb_multi_fb_probe_crtc(dev, crtc); +		if (ret) +			return ret; +	} +	return ret; +} + +static int intelfb_single_fb_probe(struct drm_device *dev) +{ +	struct drm_crtc *crtc; +	struct drm_connector *connector; +	unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1; +	unsigned int surface_width = 0, surface_height = 0; +	int new_fb = 0; +	int crtc_count = 0; +	int ret, i, conn_count = 0; +	struct intel_framebuffer *intel_fb; +	struct fb_info *info; +	struct intelfb_par *par; +	struct drm_mode_set *modeset = NULL; + +	DRM_DEBUG("\n"); +	/* first up get a count of crtcs now in use and new min/maxes width/heights */ +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		if (drm_helper_crtc_in_use(crtc)) { +			if (crtc->desired_mode) { +				if (crtc->desired_mode->hdisplay < fb_width) +					fb_width = crtc->desired_mode->hdisplay; +				 +				if (crtc->desired_mode->vdisplay < fb_height) +					fb_height = crtc->desired_mode->vdisplay; +				 +				if (crtc->desired_mode->hdisplay > surface_width) +					surface_width = crtc->desired_mode->hdisplay; +				 +				if (crtc->desired_mode->vdisplay > surface_height) +					surface_height = crtc->desired_mode->vdisplay; + +			} +		crtc_count++; +		} +	} + +	if (crtc_count == 0 || fb_width == -1 || fb_height == -1) { +		/* hmm everyone went away - assume VGA cable just fell out +		   and will come back later. */ +		return 0; +	} + +	/* do we have an fb already? */ +	if (list_empty(&dev->mode_config.fb_kernel_list)) { +		/* create an fb if we don't have one */ +		ret = intelfb_create(dev, fb_width, fb_height, surface_width, surface_height, &intel_fb); +		if (ret) +			return -EINVAL; +		new_fb = 1; +	} else { +		struct drm_framebuffer *fb; +		fb = list_first_entry(&dev->mode_config.fb_kernel_list, struct drm_framebuffer, filp_head); +		intel_fb = to_intel_framebuffer(fb); + +		/* if someone hotplugs something bigger than we have already allocated, we are pwned. +		   As really we can't resize an fbdev that is in the wild currently due to fbdev +		   not really being designed for the lower layers moving stuff around under it. +		   - so in the grand style of things - punt. */ +		if ((fb->width < surface_width) || (fb->height < surface_height)) { +			DRM_ERROR("Framebuffer not large enough to scale console onto.\n"); +			return -EINVAL; +		} +	} + +	info = intel_fb->base.fbdev; +	par = info->par; + +	crtc_count = 0; +	/* okay we need to setup new connector sets in the crtcs */ +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +		modeset = &intel_crtc->mode_set; +		modeset->fb = &intel_fb->base; +		conn_count = 0; +		list_for_each_entry(connector, &dev->mode_config.connector_list, head) { +			if (connector->encoder) +				if(connector->encoder->crtc == modeset->crtc) { +					modeset->connectors[conn_count] = connector; +					conn_count++; +					if (conn_count > INTELFB_CONN_LIMIT) +						BUG(); +				} +		} + +		for (i = conn_count; i < INTELFB_CONN_LIMIT; i++) +			modeset->connectors[i] = NULL; + +		par->crtc_ids[crtc_count++] = crtc->base.id; + +		modeset->num_connectors = conn_count; +		if (modeset->mode != modeset->crtc->desired_mode) +			modeset->mode = modeset->crtc->desired_mode; +	} +	par->crtc_count = crtc_count; + +	if (new_fb) { +		info->var.pixclock = -1; +		if (register_framebuffer(info) < 0) +			return -EINVAL; +	} else +		intelfb_set_par(info); +		 +	printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, +	       info->fix.id); + +	/* Switch back to kernel console on panic */ +	panic_mode = *modeset; +	atomic_notifier_chain_register(&panic_notifier_list, &paniced); +	printk(KERN_INFO "registered panic notifier\n"); + +	return 0; +} + +int intelfb_probe(struct drm_device *dev) +{ +	int ret; + +	DRM_DEBUG("\n"); + +	/* something has changed in the lower levels of hell - deal with it  +	   here */ + +	/* two modes : a) 1 fb to rule all crtcs. +	               b) one fb per crtc. +	   two actions 1) new connected device + 	               2) device removed. +	   case a/1 : if the fb surface isn't big enough - resize the surface fb. +	              if the fb size isn't big enough - resize fb into surface. +		      if everything big enough configure the new crtc/etc. +	   case a/2 : undo the configuration +	              possibly resize down the fb to fit the new configuration. +           case b/1 : see if it is on a new crtc - setup a new fb and add it. +	   case b/2 : teardown the new fb. +	*/ + +	/* mode a first */ +	/* search for an fb */ +	if (i915_fbpercrtc == 1) { +		ret = intelfb_multi_fb_probe(dev); +	} else { +		ret = intelfb_single_fb_probe(dev); +	} + +	return ret; +} +EXPORT_SYMBOL(intelfb_probe); + +int intelfb_remove(struct drm_device *dev, struct drm_framebuffer *fb) +{ +	struct fb_info *info; +	struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); + +	if (!fb) +		return -EINVAL; + +	info = fb->fbdev; +	 +	if (info) { +		unregister_framebuffer(info); +		iounmap(info->screen_base); +		mutex_lock(&dev->struct_mutex); +		drm_gem_object_unreference(intel_fb->obj); +		mutex_unlock(&dev->struct_mutex); +		framebuffer_release(info); +	} + +	atomic_notifier_chain_unregister(&panic_notifier_list, &paniced); +	memset(&panic_mode, 0, sizeof(struct drm_mode_set)); +	return 0; +} +EXPORT_SYMBOL(intelfb_remove); +MODULE_LICENSE("GPL"); diff --git a/linux-core/intel_i2c.c b/linux-core/intel_i2c.c new file mode 100644 index 00000000..efcbf656 --- /dev/null +++ b/linux-core/intel_i2c.c @@ -0,0 +1,190 @@ +/* + * Copyright © 2006-2007 Intel Corporation + * + * 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 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + *	Eric Anholt <eric@anholt.net> + */ +/* + * Copyright (c) 2006 Dave Airlie <airlied@linux.ie> + *   Jesse Barnes <jesse.barnes@intel.com> + */ + +#include <linux/i2c.h> +#include <linux/i2c-id.h> +#include <linux/i2c-algo-bit.h> +#include "drmP.h" +#include "drm.h" +#include "intel_drv.h" +#include "i915_drm.h" +#include "i915_drv.h" + +/* + * Intel GPIO access functions + */ + +#define I2C_RISEFALL_TIME 20 + +static int get_clock(void *data) +{ +	struct intel_i2c_chan *chan = data; +	struct drm_i915_private *dev_priv = chan->drm_dev->dev_private; +	u32 val; + +	val = I915_READ(chan->reg); +	return ((val & GPIO_CLOCK_VAL_IN) != 0); +} + +static int get_data(void *data) +{ +	struct intel_i2c_chan *chan = data; +	struct drm_i915_private *dev_priv = chan->drm_dev->dev_private; +	u32 val; + +	val = I915_READ(chan->reg); +	return ((val & GPIO_DATA_VAL_IN) != 0); +} + +static void set_clock(void *data, int state_high) +{ +	struct intel_i2c_chan *chan = data; +	struct drm_device *dev = chan->drm_dev; +	struct drm_i915_private *dev_priv = chan->drm_dev->dev_private; +	u32 reserved = 0, clock_bits; + +	/* On most chips, these bits must be preserved in software. */ +	if (!IS_I830(dev) && !IS_845G(dev)) +		reserved = I915_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE | +						   GPIO_CLOCK_PULLUP_DISABLE); + +	if (state_high) +		clock_bits = GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK; +	else +		clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK | +			GPIO_CLOCK_VAL_MASK; +	I915_WRITE(chan->reg, reserved | clock_bits); +	udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */ +} + +static void set_data(void *data, int state_high) +{ +	struct intel_i2c_chan *chan = data; +	struct drm_device *dev = chan->drm_dev; +	struct drm_i915_private *dev_priv = chan->drm_dev->dev_private; +	u32 reserved = 0, data_bits; + +	/* On most chips, these bits must be preserved in software. */ +	if (!IS_I830(dev) && !IS_845G(dev)) +		reserved = I915_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE | +						   GPIO_CLOCK_PULLUP_DISABLE); + +	if (state_high) +		data_bits = GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK; +	else +		data_bits = GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK | +			GPIO_DATA_VAL_MASK; + +	I915_WRITE(chan->reg, reserved | data_bits); +	udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */ +} + +/** + * intel_i2c_create - instantiate an Intel i2c bus using the specified GPIO reg + * @dev: DRM device + * @output: driver specific output device + * @reg: GPIO reg to use + * @name: name for this bus + * + * Creates and registers a new i2c bus with the Linux i2c layer, for use + * in output probing and control (e.g. DDC or SDVO control functions). + * + * Possible values for @reg include: + *   %GPIOA + *   %GPIOB + *   %GPIOC + *   %GPIOD + *   %GPIOE + *   %GPIOF + *   %GPIOG + *   %GPIOH + * see PRM for details on how these different busses are used. + */ +struct intel_i2c_chan *intel_i2c_create(struct drm_device *dev, const u32 reg, +					const char *name) +{ +	struct intel_i2c_chan *chan; + +	chan = kzalloc(sizeof(struct intel_i2c_chan), GFP_KERNEL); +	if (!chan) +		goto out_free; + +	chan->drm_dev = dev; +	chan->reg = reg; +	snprintf(chan->adapter.name, I2C_NAME_SIZE, "intel drm %s", name); +	chan->adapter.owner = THIS_MODULE; +#ifndef I2C_HW_B_INTELFB +#define I2C_HW_B_INTELFB I2C_HW_B_I810 +#endif +	chan->adapter.id = I2C_HW_B_INTELFB; +	chan->adapter.algo_data	= &chan->algo; +	chan->adapter.dev.parent = &dev->pdev->dev; +	chan->algo.setsda = set_data; +	chan->algo.setscl = set_clock; +	chan->algo.getsda = get_data; +	chan->algo.getscl = get_clock; +	chan->algo.udelay = 20; +	chan->algo.timeout = usecs_to_jiffies(2200); +	chan->algo.data = chan; + +	i2c_set_adapdata(&chan->adapter, chan); + +	if(i2c_bit_add_bus(&chan->adapter)) +		goto out_free; + +	/* JJJ:  raise SCL and SDA? */ +	set_data(chan, 1); +	set_clock(chan, 1); +	udelay(20); + +	return chan; + +out_free: +	kfree(chan); +	return NULL; +} + +/** + * intel_i2c_destroy - unregister and free i2c bus resources + * @output: channel to free + * + * Unregister the adapter from the i2c layer, then free the structure. + */ +void intel_i2c_destroy(struct intel_i2c_chan *chan) +{ +	if (!chan) +		return; + +	i2c_del_adapter(&chan->adapter); +	kfree(chan); +} + +	 +	 diff --git a/linux-core/intel_lvds.c b/linux-core/intel_lvds.c new file mode 100644 index 00000000..fa8209c7 --- /dev/null +++ b/linux-core/intel_lvds.c @@ -0,0 +1,515 @@ +/* + * Copyright © 2006-2007 Intel Corporation + * Copyright (c) 2006 Dave Airlie <airlied@linux.ie> + * + * 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 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + *	Eric Anholt <eric@anholt.net> + *      Dave Airlie <airlied@linux.ie> + *      Jesse Barnes <jesse.barnes@intel.com> + */ + +#include <linux/i2c.h> +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "drm_edid.h" +#include "intel_drv.h" +#include "i915_drm.h" +#include "i915_drv.h" + +/** + * Sets the backlight level. + * + * \param level backlight level, from 0 to intel_lvds_get_max_backlight(). + */ +static void intel_lvds_set_backlight(struct drm_device *dev, int level) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	u32 blc_pwm_ctl; + +	blc_pwm_ctl = I915_READ(BLC_PWM_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK; +	I915_WRITE(BLC_PWM_CTL, (blc_pwm_ctl | +				 (level << BACKLIGHT_DUTY_CYCLE_SHIFT))); +} + +/** + * Returns the maximum level of the backlight duty cycle field. + */ +static u32 intel_lvds_get_max_backlight(struct drm_device *dev) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +     +	return ((I915_READ(BLC_PWM_CTL) & BACKLIGHT_MODULATION_FREQ_MASK) >> +		BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; +} + +/** + * Sets the power state for the panel. + */ +static void intel_lvds_set_power(struct drm_device *dev, bool on) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	u32 pp_status; + +	if (on) { +		I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) | +			   POWER_TARGET_ON); +		do { +			pp_status = I915_READ(PP_STATUS); +		} while ((pp_status & PP_ON) == 0); + +		intel_lvds_set_backlight(dev, dev_priv->backlight_duty_cycle); +	} else { +		intel_lvds_set_backlight(dev, 0); + +		I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) & +			   ~POWER_TARGET_ON); +		do { +			pp_status = I915_READ(PP_STATUS); +		} while (pp_status & PP_ON); +	} +} + +static void intel_lvds_dpms(struct drm_encoder *encoder, int mode) +{ +	struct drm_device *dev = encoder->dev; + +	if (mode == DRM_MODE_DPMS_ON) +		intel_lvds_set_power(dev, true); +	else +		intel_lvds_set_power(dev, false); + +	/* XXX: We never power down the LVDS pairs. */ +} + +static void intel_lvds_save(struct drm_connector *connector) +{ +	struct drm_device *dev = connector->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; + +	dev_priv->savePP_ON = I915_READ(PP_ON_DELAYS); +	dev_priv->savePP_OFF = I915_READ(PP_OFF_DELAYS); +	dev_priv->savePP_CONTROL = I915_READ(PP_CONTROL); +	dev_priv->savePP_DIVISOR = I915_READ(PP_DIVISOR); +	dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL); +	dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL & +				       BACKLIGHT_DUTY_CYCLE_MASK); + +	/* +	 * If the light is off at server startup, just make it full brightness +	 */ +	if (dev_priv->backlight_duty_cycle == 0) +		dev_priv->backlight_duty_cycle = +			intel_lvds_get_max_backlight(dev); +} + +static void intel_lvds_restore(struct drm_connector *connector) +{ +	struct drm_device *dev = connector->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; + +	I915_WRITE(BLC_PWM_CTL, dev_priv->saveBLC_PWM_CTL); +	I915_WRITE(PP_ON_DELAYS, dev_priv->savePP_ON); +	I915_WRITE(PP_OFF_DELAYS, dev_priv->savePP_OFF); +	I915_WRITE(PP_DIVISOR, dev_priv->savePP_DIVISOR); +	I915_WRITE(PP_CONTROL, dev_priv->savePP_CONTROL); +	if (dev_priv->savePP_CONTROL & POWER_TARGET_ON) +		intel_lvds_set_power(dev, true); +	else +		intel_lvds_set_power(dev, false); +} + +static int intel_lvds_mode_valid(struct drm_connector *connector, +				 struct drm_display_mode *mode) +{ +	struct drm_device *dev = connector->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_display_mode *fixed_mode = dev_priv->panel_fixed_mode; + +	if (fixed_mode)	{ +		if (mode->hdisplay > fixed_mode->hdisplay) +			return MODE_PANEL; +		if (mode->vdisplay > fixed_mode->vdisplay) +			return MODE_PANEL; +	} + +	return MODE_OK; +} + +static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, +				  struct drm_display_mode *mode, +				  struct drm_display_mode *adjusted_mode) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); +	struct drm_encoder *tmp_encoder; + +	/* Should never happen!! */ +	if (!IS_I965G(dev) && intel_crtc->pipe == 0) { +		printk(KERN_ERR "Can't support LVDS on pipe A\n"); +		return false; +	} + +	/* Should never happen!! */ +	list_for_each_entry(tmp_encoder, &dev->mode_config.encoder_list, head) { +		if (tmp_encoder != encoder && tmp_encoder->crtc == encoder->crtc) { +			printk(KERN_ERR "Can't enable LVDS and another " +			       "encoder on the same pipe\n"); +			return false; +		} +	} + +	/* +	 * If we have timings from the BIOS for the panel, put them in +	 * to the adjusted mode.  The CRTC will be set up for this mode, +	 * with the panel scaling set up to source from the H/VDisplay +	 * of the original mode. +	 */ +	if (dev_priv->panel_fixed_mode != NULL) { +		adjusted_mode->hdisplay = dev_priv->panel_fixed_mode->hdisplay; +		adjusted_mode->hsync_start = +			dev_priv->panel_fixed_mode->hsync_start; +		adjusted_mode->hsync_end = +			dev_priv->panel_fixed_mode->hsync_end; +		adjusted_mode->htotal = dev_priv->panel_fixed_mode->htotal; +		adjusted_mode->vdisplay = dev_priv->panel_fixed_mode->vdisplay; +		adjusted_mode->vsync_start = +			dev_priv->panel_fixed_mode->vsync_start; +		adjusted_mode->vsync_end = +			dev_priv->panel_fixed_mode->vsync_end; +		adjusted_mode->vtotal = dev_priv->panel_fixed_mode->vtotal; +		adjusted_mode->clock = dev_priv->panel_fixed_mode->clock; +		drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); +	} + +	/* +	 * XXX: It would be nice to support lower refresh rates on the +	 * panels to reduce power consumption, and perhaps match the +	 * user's requested refresh rate. +	 */ + +	return true; +} + +static void intel_lvds_prepare(struct drm_encoder *encoder) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; + +	dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL); +	dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL & +				       BACKLIGHT_DUTY_CYCLE_MASK); + +	intel_lvds_set_power(dev, false); +} + +static void intel_lvds_commit( struct drm_encoder *encoder) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; + +	if (dev_priv->backlight_duty_cycle == 0) +		dev_priv->backlight_duty_cycle = +			intel_lvds_get_max_backlight(dev); + +	intel_lvds_set_power(dev, true); +} + +static void intel_lvds_mode_set(struct drm_encoder *encoder, +				struct drm_display_mode *mode, +				struct drm_display_mode *adjusted_mode) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); +	u32 pfit_control; + +	/* +	 * The LVDS pin pair will already have been turned on in the +	 * intel_crtc_mode_set since it has a large impact on the DPLL +	 * settings. +	 */ + +	/* +	 * Enable automatic panel scaling so that non-native modes fill the +	 * screen.  Should be enabled before the pipe is enabled, according to +	 * register description and PRM. +	 */ +	if (mode->hdisplay != adjusted_mode->hdisplay || +	    mode->vdisplay != adjusted_mode->vdisplay) +		pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE | +				HORIZ_AUTO_SCALE | VERT_INTERP_BILINEAR | +				HORIZ_INTERP_BILINEAR); +	else +		pfit_control = 0; + +	if (!IS_I965G(dev)) { +		if (dev_priv->panel_wants_dither) +			pfit_control |= PANEL_8TO6_DITHER_ENABLE; +	} +	else +		pfit_control |= intel_crtc->pipe << PFIT_PIPE_SHIFT; + +	I915_WRITE(PFIT_CONTROL, pfit_control); +} + +/** + * Detect the LVDS connection. + * + * This always returns CONNECTOR_STATUS_CONNECTED.  This connector should only have + * been set up if the LVDS was actually connected anyway. + */ +static enum drm_connector_status intel_lvds_detect(struct drm_connector *connector) +{ +	return connector_status_connected; +} + +/** + * Return the list of DDC modes if available, or the BIOS fixed mode otherwise. + */ +static int intel_lvds_get_modes(struct drm_connector *connector) +{ +	struct drm_device *dev = connector->dev; +	struct intel_output *intel_output = to_intel_output(connector); +	struct drm_i915_private *dev_priv = dev->dev_private; +	int ret = 0; + +	ret = intel_ddc_get_modes(intel_output); + +	if (ret) +		return ret; + +	/* Didn't get an EDID, so +	 * Set wide sync ranges so we get all modes +	 * handed to valid_mode for checking +	 */ +	connector->display_info.min_vfreq = 0; +	connector->display_info.max_vfreq = 200; +	connector->display_info.min_hfreq = 0; +	connector->display_info.max_hfreq = 200; + +	if (dev_priv->panel_fixed_mode != NULL) { +		struct drm_display_mode *mode = +			drm_mode_duplicate(dev, dev_priv->panel_fixed_mode); +		drm_mode_probed_add(connector, mode); +		return 1; +	} + +	return 0; +} + +/** + * intel_lvds_destroy - unregister and free LVDS structures + * @connector: connector to free + * + * Unregister the DDC bus for this connector then free the driver private + * structure. + */ +static void intel_lvds_destroy(struct drm_connector *connector) +{ +	struct intel_output *intel_output = to_intel_output(connector); + +	if (intel_output->ddc_bus) +		intel_i2c_destroy(intel_output->ddc_bus); +	drm_sysfs_connector_remove(connector); +	drm_connector_cleanup(connector); +	kfree(connector); +} + +static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = { +	.dpms = intel_lvds_dpms, +	.mode_fixup = intel_lvds_mode_fixup, +	.prepare = intel_lvds_prepare, +	.mode_set = intel_lvds_mode_set, +	.commit = intel_lvds_commit, +}; + +static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = { +	.get_modes = intel_lvds_get_modes, +	.mode_valid = intel_lvds_mode_valid, +	.best_encoder = intel_best_encoder, +};	 + +static const struct drm_connector_funcs intel_lvds_connector_funcs = { +	.save = intel_lvds_save, +	.restore = intel_lvds_restore, +	.detect = intel_lvds_detect, +	.fill_modes = drm_helper_probe_single_connector_modes, +	.destroy = intel_lvds_destroy, +}; + + +static void intel_lvds_enc_destroy(struct drm_encoder *encoder) +{ +	drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs intel_lvds_enc_funcs = { +	.destroy = intel_lvds_enc_destroy, +}; + + + +/** + * intel_lvds_init - setup LVDS connectors on this device + * @dev: drm device + * + * Create the connector, register the LVDS DDC bus, and try to figure out what + * modes we can display on the LVDS panel (if present). + */ +void intel_lvds_init(struct drm_device *dev) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_output *intel_output; +	struct drm_connector *connector; +	struct drm_encoder *encoder; +	struct drm_display_mode *scan; /* *modes, *bios_mode; */ +	struct drm_crtc *crtc; +	u32 lvds; +	int pipe; + +	intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL); +	if (!intel_output) { +		return; +	} + +	connector = &intel_output->base; +	encoder = &intel_output->enc; +	drm_connector_init(dev, &intel_output->base, &intel_lvds_connector_funcs, +			   DRM_MODE_CONNECTOR_LVDS); + +	drm_encoder_init(dev, &intel_output->enc, &intel_lvds_enc_funcs, +			 DRM_MODE_ENCODER_LVDS); + +	drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc); +	intel_output->type = INTEL_OUTPUT_LVDS; + +	drm_encoder_helper_add(encoder, &intel_lvds_helper_funcs); +	drm_connector_helper_add(connector, &intel_lvds_connector_helper_funcs); +	connector->display_info.subpixel_order = SubPixelHorizontalRGB; +	connector->interlace_allowed = false; +	connector->doublescan_allowed = false; + + +	/* +	 * LVDS discovery: +	 * 1) check for EDID on DDC +	 * 2) check for VBT data +	 * 3) check to see if LVDS is already on +	 *    if none of the above, no panel +	 * 4) make sure lid is open +	 *    if closed, act like it's not there for now +	 */ + +	/* Set up the DDC bus. */ +	intel_output->ddc_bus = intel_i2c_create(dev, GPIOC, "LVDSDDC_C"); +	if (!intel_output->ddc_bus) { +		dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " +			   "failed.\n"); +		goto failed; +	} + +	/* +	 * Attempt to get the fixed panel mode from DDC.  Assume that the +	 * preferred mode is the right one. +	 */ +	intel_ddc_get_modes(intel_output); + +	list_for_each_entry(scan, &connector->probed_modes, head) { +		if (scan->type & DRM_MODE_TYPE_PREFERRED) { +			dev_priv->panel_fixed_mode =  +				drm_mode_duplicate(dev, scan); +			goto out; /* FIXME: check for quirks */ +		} +	} + +	/* Failed to get EDID, what about VBT? */ +	if (dev_priv->vbt_mode) +		dev_priv->panel_fixed_mode = +			drm_mode_duplicate(dev, dev_priv->vbt_mode); + +	/* +	 * If we didn't get EDID, try checking if the panel is already turned +	 * on.  If so, assume that whatever is currently programmed is the +	 * correct mode. +	 */ +	lvds = I915_READ(LVDS); +	pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0; +	crtc = intel_get_crtc_from_pipe(dev, pipe); +		 +	if (crtc && (lvds & LVDS_PORT_EN)) { +		dev_priv->panel_fixed_mode = intel_crtc_mode_get(dev, crtc); +		if (dev_priv->panel_fixed_mode) { +			dev_priv->panel_fixed_mode->type |= +				DRM_MODE_TYPE_PREFERRED; +			goto out; /* FIXME: check for quirks */ +		} +	} + +	/* If we still don't have a mode after all that, give up. */ +	if (!dev_priv->panel_fixed_mode) +		goto failed; + +	/* FIXME: detect aopen & mac mini type stuff automatically? */ +	/* +	 * Blacklist machines with BIOSes that list an LVDS panel without +	 * actually having one. +	 */ +	if (IS_I945GM(dev)) { +		/* aopen mini pc */ +		if (dev->pdev->subsystem_vendor == 0xa0a0) +			goto failed; + +		if ((dev->pdev->subsystem_vendor == 0x8086) && +		    (dev->pdev->subsystem_device == 0x7270)) { +			/* It's a Mac Mini or Macbook Pro. +			 * +			 * Apple hardware is out to get us.  The macbook pro +			 * has a real LVDS panel, but the mac mini does not, +			 * and they have the same device IDs.  We'll +			 * distinguish by panel size, on the assumption +			 * that Apple isn't about to make any machines with an +			 * 800x600 display. +			 */ + +			if (dev_priv->panel_fixed_mode != NULL && +			    dev_priv->panel_fixed_mode->hdisplay == 800 && +			    dev_priv->panel_fixed_mode->vdisplay == 600) { +				DRM_DEBUG("Suspected Mac Mini, ignoring the LVDS\n"); +				goto failed; +			} +		} +	} + + +out: +	drm_sysfs_connector_add(connector); +	return; + +failed: +	DRM_DEBUG("No LVDS modes found, disabling.\n"); + 	if (intel_output->ddc_bus) +		intel_i2c_destroy(intel_output->ddc_bus); +	drm_connector_cleanup(connector); +	kfree(connector); +} diff --git a/linux-core/intel_modes.c b/linux-core/intel_modes.c new file mode 100644 index 00000000..79be3575 --- /dev/null +++ b/linux-core/intel_modes.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> + * Copyright (c) 2007 Intel Corporation + *   Jesse Barnes <jesse.barnes@intel.com> + */ + +#include <linux/i2c.h> +#include <linux/fb.h> +#include "drmP.h" +#include "intel_drv.h" + +/** + * intel_ddc_probe + * + */ +bool intel_ddc_probe(struct intel_output *intel_output) +{ +	u8 out_buf[] = { 0x0, 0x0}; +	u8 buf[2]; +	int ret; +	struct i2c_msg msgs[] = { +		{ +			.addr = 0x50, +			.flags = 0, +			.len = 1, +			.buf = out_buf, +		}, +		{ +			.addr = 0x50, +			.flags = I2C_M_RD, +			.len = 1, +			.buf = buf, +		} +	}; + +	ret = i2c_transfer(&intel_output->ddc_bus->adapter, msgs, 2); +	if (ret == 2) +		return true; + +	return false; +} + +/** + * intel_ddc_get_modes - get modelist from monitor + * @connector: DRM connector device to use + * + * Fetch the EDID information from @connector using the DDC bus. + */ +int intel_ddc_get_modes(struct intel_output *intel_output) +{ +	struct edid *edid; +	int ret = 0; + +	edid = drm_get_edid(&intel_output->base, &intel_output->ddc_bus->adapter); +	if (edid) { +		drm_mode_connector_update_edid_property(&intel_output->base, edid); +		ret = drm_add_edid_modes(&intel_output->base, edid); +		kfree(edid); +	} +	return ret; +} diff --git a/linux-core/intel_sdvo.c b/linux-core/intel_sdvo.c new file mode 100644 index 00000000..6624bde2 --- /dev/null +++ b/linux-core/intel_sdvo.c @@ -0,0 +1,1153 @@ +/* + * Copyright © 2006-2007 Intel Corporation + * + * 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 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + *	Eric Anholt <eric@anholt.net> + */ +/* + * Copyright 2006 Dave Airlie <airlied@linux.ie> + *   Jesse Barnes <jesse.barnes@intel.com> + */ + +#include <linux/i2c.h> +#include <linux/delay.h> +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "intel_drv.h" +#include "i915_drm.h" +#include "i915_drv.h" +#include "intel_sdvo_regs.h" + +struct intel_sdvo_priv { +	struct intel_i2c_chan *i2c_bus; +	int slaveaddr; +	int output_device; + +	u16 active_outputs; + +	struct intel_sdvo_caps caps; +	int pixel_clock_min, pixel_clock_max; + +	int save_sdvo_mult; +	u16 save_active_outputs; +	struct intel_sdvo_dtd save_input_dtd_1, save_input_dtd_2; +	struct intel_sdvo_dtd save_output_dtd[16]; +	u32 save_SDVOX; +}; + +/** + * Writes the SDVOB or SDVOC with the given value, but always writes both + * SDVOB and SDVOC to work around apparent hardware issues (according to + * comments in the BIOS). + */ +void intel_sdvo_write_sdvox(struct intel_output *intel_output, u32 val) +{ +	struct drm_device *dev = intel_output->base.dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_sdvo_priv   *sdvo_priv = intel_output->dev_priv; +	u32 bval = val, cval = val; +	int i; + +	if (sdvo_priv->output_device == SDVOB) { +		cval = I915_READ(SDVOC); +	} else { +		bval = I915_READ(SDVOB); +	} +	/* +	 * Write the registers twice for luck. Sometimes, +	 * writing them only once doesn't appear to 'stick'. +	 * The BIOS does this too. Yay, magic +	 */ +	for (i = 0; i < 2; i++) +	{ +		I915_WRITE(SDVOB, bval); +		I915_READ(SDVOB); +		I915_WRITE(SDVOC, cval); +		I915_READ(SDVOC); +	} +} + +static bool intel_sdvo_read_byte(struct intel_output *intel_output, u8 addr, +				 u8 *ch) +{ +	struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; +	u8 out_buf[2]; +	u8 buf[2]; +	int ret; + +	struct i2c_msg msgs[] = { +		{  +			.addr = sdvo_priv->i2c_bus->slave_addr, +			.flags = 0, +			.len = 1, +			.buf = out_buf, +		},  +		{ +			.addr = sdvo_priv->i2c_bus->slave_addr, +			.flags = I2C_M_RD, +			.len = 1, +			.buf = buf, +		} +	}; + +	out_buf[0] = addr; +	out_buf[1] = 0; + +	if ((ret = i2c_transfer(&sdvo_priv->i2c_bus->adapter, msgs, 2)) == 2) +	{ +//		DRM_DEBUG("got back from addr %02X = %02x\n", out_buf[0], buf[0]);  +		*ch = buf[0]; +		return true; +	} + +	DRM_DEBUG("i2c transfer returned %d\n", ret); +	return false; +} + +static bool intel_sdvo_write_byte(struct intel_output *intel_output, int addr, +				  u8 ch) +{ +	u8 out_buf[2]; +	struct i2c_msg msgs[] = { +		{  +			.addr = intel_output->i2c_bus->slave_addr, +			.flags = 0, +			.len = 2, +			.buf = out_buf, +		} +	}; + +	out_buf[0] = addr; +	out_buf[1] = ch; + +	if (i2c_transfer(&intel_output->i2c_bus->adapter, msgs, 1) == 1) +	{ +		return true; +	} +	return false; +} + +#define SDVO_CMD_NAME_ENTRY(cmd) {cmd, #cmd} +/** Mapping of command numbers to names, for debug output */ +const static struct _sdvo_cmd_name { +    u8 cmd; +    char *name; +} sdvo_cmd_names[] = { +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FIRMWARE_REV), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TRAINED_INPUTS), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_OUTPUTS), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_OUTPUTS), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_IN_OUT_MAP), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_IN_OUT_MAP), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ATTACHED_DISPLAYS), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HOT_PLUG_SUPPORT), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_HOT_PLUG), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_HOT_PLUG), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_INPUT), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_OUTPUT), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART1), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART2), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART2), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART1), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART2), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART1), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART2), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CLOCK_RATE_MULT), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CLOCK_RATE_MULT), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_RESOLUTION_SUPPORT), +    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH), +}; + +#define SDVO_NAME(dev_priv) ((dev_priv)->output_device == SDVOB ? "SDVOB" : "SDVOC") +#define SDVO_PRIV(output)   ((struct intel_sdvo_priv *) (output)->dev_priv) + +static void intel_sdvo_write_cmd(struct intel_output *intel_output, u8 cmd, +				 void *args, int args_len) +{ +	struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; +	int i; + +        if (1) { +                DRM_DEBUG("%s: W: %02X ", SDVO_NAME(sdvo_priv), cmd); +                for (i = 0; i < args_len; i++) +                        printk("%02X ", ((u8 *)args)[i]); +                for (; i < 8; i++) +                        printk("   "); +                for (i = 0; i < sizeof(sdvo_cmd_names) / sizeof(sdvo_cmd_names[0]); i++) { +                        if (cmd == sdvo_cmd_names[i].cmd) { +                                printk("(%s)", sdvo_cmd_names[i].name); +                                break; +                        } +                } +                if (i == sizeof(sdvo_cmd_names)/ sizeof(sdvo_cmd_names[0])) +                        printk("(%02X)",cmd); +                printk("\n"); +        } +                         +	for (i = 0; i < args_len; i++) { +		intel_sdvo_write_byte(intel_output, SDVO_I2C_ARG_0 - i, ((u8*)args)[i]); +	} + +	intel_sdvo_write_byte(intel_output, SDVO_I2C_OPCODE, cmd); +} + +static const char *cmd_status_names[] = { +	"Power on", +	"Success", +	"Not supported", +	"Invalid arg", +	"Pending", +	"Target not specified", +	"Scaling not supported" +}; + +static u8 intel_sdvo_read_response(struct intel_output *intel_output, void *response, +				   int response_len) +{ +	struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; +	int i; +	u8 status; +	u8 retry = 50; + +	while (retry--) { +		/* Read the command response */ +		for (i = 0; i < response_len; i++) { +			intel_sdvo_read_byte(intel_output, SDVO_I2C_RETURN_0 + i, +				     &((u8 *)response)[i]); +		} + +		/* read the return status */ +		intel_sdvo_read_byte(intel_output, SDVO_I2C_CMD_STATUS, &status); + +	        if (1) { +			DRM_DEBUG("%s: R: ", SDVO_NAME(sdvo_priv)); +       			for (i = 0; i < response_len; i++) +                        	printk("%02X ", ((u8 *)response)[i]); +                	for (; i < 8; i++) +                        	printk("   "); +                	if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP) +                        	printk("(%s)", cmd_status_names[status]); +                	else +                        	printk("(??? %d)", status); +                	printk("\n"); +        	} + +		if (status != SDVO_CMD_STATUS_PENDING) +			return status; + +		mdelay(50); +	} + +	return status; +} + +int intel_sdvo_get_pixel_multiplier(struct drm_display_mode *mode) +{ +	if (mode->clock >= 100000) +		return 1; +	else if (mode->clock >= 50000) +		return 2; +	else +		return 4; +} + +/** + * Don't check status code from this as it switches the bus back to the + * SDVO chips which defeats the purpose of doing a bus switch in the first + * place. + */ +void intel_sdvo_set_control_bus_switch(struct intel_output *intel_output, u8 target) +{ +	intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_CONTROL_BUS_SWITCH, &target, 1); +} + +static bool intel_sdvo_set_target_input(struct intel_output *intel_output, bool target_0, bool target_1) +{ +	struct intel_sdvo_set_target_input_args targets = {0}; +	u8 status; + +	if (target_0 && target_1) +		return SDVO_CMD_STATUS_NOTSUPP; + +	if (target_1) +		targets.target_1 = 1; + +	intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_TARGET_INPUT, &targets, +			     sizeof(targets)); + +	status = intel_sdvo_read_response(intel_output, NULL, 0); + +	return (status == SDVO_CMD_STATUS_SUCCESS); +} + +/** + * Return whether each input is trained. + * + * This function is making an assumption about the layout of the response, + * which should be checked against the docs. + */ +static bool intel_sdvo_get_trained_inputs(struct intel_output *intel_output, bool *input_1, bool *input_2) +{ +	struct intel_sdvo_get_trained_inputs_response response; +	u8 status; + +	intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_TRAINED_INPUTS, NULL, 0); +	status = intel_sdvo_read_response(intel_output, &response, sizeof(response)); +	if (status != SDVO_CMD_STATUS_SUCCESS) +		return false; + +	*input_1 = response.input0_trained; +	*input_2 = response.input1_trained; +	return true; +} + +static bool intel_sdvo_get_active_outputs(struct intel_output *intel_output, +					  u16 *outputs) +{ +	u8 status; + +	intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ACTIVE_OUTPUTS, NULL, 0); +	status = intel_sdvo_read_response(intel_output, outputs, sizeof(*outputs)); + +	return (status == SDVO_CMD_STATUS_SUCCESS); +} + +static bool intel_sdvo_set_active_outputs(struct intel_output *intel_output, +					  u16 outputs) +{ +	u8 status; + +	intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_ACTIVE_OUTPUTS, &outputs, +			     sizeof(outputs)); +	status = intel_sdvo_read_response(intel_output, NULL, 0); +	return (status == SDVO_CMD_STATUS_SUCCESS); +} + +static bool intel_sdvo_set_encoder_power_state(struct intel_output *intel_output, +					       int mode) +{ +	u8 status, state = SDVO_ENCODER_STATE_ON; + +	switch (mode) { +	case DRM_MODE_DPMS_ON: +		state = SDVO_ENCODER_STATE_ON; +		break; +	case DRM_MODE_DPMS_STANDBY: +		state = SDVO_ENCODER_STATE_STANDBY; +		break; +	case DRM_MODE_DPMS_SUSPEND: +		state = SDVO_ENCODER_STATE_SUSPEND; +		break; +	case DRM_MODE_DPMS_OFF: +		state = SDVO_ENCODER_STATE_OFF; +		break; +	} +	 +	intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_ENCODER_POWER_STATE, &state, +			     sizeof(state)); +	status = intel_sdvo_read_response(intel_output, NULL, 0); + +	return (status == SDVO_CMD_STATUS_SUCCESS); +} + +static bool intel_sdvo_get_input_pixel_clock_range(struct intel_output *intel_output, +						   int *clock_min, +						   int *clock_max) +{ +	struct intel_sdvo_pixel_clock_range clocks; +	u8 status; + +	intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE, +			     NULL, 0); + +	status = intel_sdvo_read_response(intel_output, &clocks, sizeof(clocks)); + +	if (status != SDVO_CMD_STATUS_SUCCESS) +		return false; + +	/* Convert the values from units of 10 kHz to kHz. */ +	*clock_min = clocks.min * 10; +	*clock_max = clocks.max * 10; + +	return true; +} + +static bool intel_sdvo_set_target_output(struct intel_output *intel_output, +					 u16 outputs) +{ +	u8 status; + +	intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_TARGET_OUTPUT, &outputs, +			     sizeof(outputs)); + +	status = intel_sdvo_read_response(intel_output, NULL, 0); +	return (status == SDVO_CMD_STATUS_SUCCESS); +} + +static bool intel_sdvo_get_timing(struct intel_output *intel_output, u8 cmd, +				  struct intel_sdvo_dtd *dtd) +{ +	u8 status; + +	intel_sdvo_write_cmd(intel_output, cmd, NULL, 0); +	status = intel_sdvo_read_response(intel_output, &dtd->part1, +					  sizeof(dtd->part1)); +	if (status != SDVO_CMD_STATUS_SUCCESS) +		return false; + +	intel_sdvo_write_cmd(intel_output, cmd + 1, NULL, 0); +	status = intel_sdvo_read_response(intel_output, &dtd->part2, +					  sizeof(dtd->part2)); +	if (status != SDVO_CMD_STATUS_SUCCESS) +		return false; + +	return true; +} + +static bool intel_sdvo_get_input_timing(struct intel_output *intel_output, +					 struct intel_sdvo_dtd *dtd) +{ +	return intel_sdvo_get_timing(intel_output, +				     SDVO_CMD_GET_INPUT_TIMINGS_PART1, dtd); +} + +static bool intel_sdvo_get_output_timing(struct intel_output *intel_output, +					 struct intel_sdvo_dtd *dtd) +{ +	return intel_sdvo_get_timing(intel_output, +				     SDVO_CMD_GET_OUTPUT_TIMINGS_PART1, dtd); +} + +static bool intel_sdvo_set_timing(struct intel_output *intel_output, u8 cmd, +				  struct intel_sdvo_dtd *dtd) +{ +	u8 status; + +	intel_sdvo_write_cmd(intel_output, cmd, &dtd->part1, sizeof(dtd->part1)); +	status = intel_sdvo_read_response(intel_output, NULL, 0); +	if (status != SDVO_CMD_STATUS_SUCCESS) +		return false; + +	intel_sdvo_write_cmd(intel_output, cmd + 1, &dtd->part2, sizeof(dtd->part2)); +	status = intel_sdvo_read_response(intel_output, NULL, 0); +	if (status != SDVO_CMD_STATUS_SUCCESS) +		return false; + +	return true; +} + +static bool intel_sdvo_set_input_timing(struct intel_output *intel_output, +					 struct intel_sdvo_dtd *dtd) +{ +	return intel_sdvo_set_timing(intel_output, +				     SDVO_CMD_SET_INPUT_TIMINGS_PART1, dtd); +} + +static bool intel_sdvo_set_output_timing(struct intel_output *intel_output, +					 struct intel_sdvo_dtd *dtd) +{ +	return intel_sdvo_set_timing(intel_output, +				     SDVO_CMD_SET_OUTPUT_TIMINGS_PART1, dtd); +} + +#if 0 +static bool intel_sdvo_get_preferred_input_timing(struct intel_output *intel_output, +						  struct intel_sdvo_dtd *dtd) +{ +	struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; +	u8 status; + +	intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1, +			     NULL, 0); + +	status = intel_sdvo_read_response(intel_output, &dtd->part1, +					  sizeof(dtd->part1)); +	if (status != SDVO_CMD_STATUS_SUCCESS) +		return false; + +	intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2, +			     NULL, 0); +	status = intel_sdvo_read_response(intel_output, &dtd->part2, +					  sizeof(dtd->part2)); +	if (status != SDVO_CMD_STATUS_SUCCESS) +		return false; + +	return true; +} +#endif + +static int intel_sdvo_get_clock_rate_mult(struct intel_output *intel_output) +{ +	u8 response, status; + +	intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_CLOCK_RATE_MULT, NULL, 0); +	status = intel_sdvo_read_response(intel_output, &response, 1); + +	if (status != SDVO_CMD_STATUS_SUCCESS) { +		DRM_DEBUG("Couldn't get SDVO clock rate multiplier\n"); +		return SDVO_CLOCK_RATE_MULT_1X; +	} else { +		DRM_DEBUG("Current clock rate multiplier: %d\n", response); +	} + +	return response; +} + +static bool intel_sdvo_set_clock_rate_mult(struct intel_output *intel_output, u8 val) +{ +	u8 status; + +	intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_CLOCK_RATE_MULT, &val, 1); +	status = intel_sdvo_read_response(intel_output, NULL, 0); +	if (status != SDVO_CMD_STATUS_SUCCESS) +		return false; + +	return true; +} + +static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder, +				  struct drm_display_mode *mode, +				  struct drm_display_mode *adjusted_mode) +{ +	/* Make the CRTC code factor in the SDVO pixel multiplier.  The SDVO +	 * device will be told of the multiplier during mode_set. +	 */ +	adjusted_mode->clock *= intel_sdvo_get_pixel_multiplier(mode); +	return true; +} + +static void intel_sdvo_mode_set(struct drm_encoder *encoder, +				struct drm_display_mode *mode, +				struct drm_display_mode *adjusted_mode) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_crtc *crtc = encoder->crtc; +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +	struct intel_output *intel_output = enc_to_intel_output(encoder); +	struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; +	u16 width, height; +	u16 h_blank_len, h_sync_len, v_blank_len, v_sync_len; +	u16 h_sync_offset, v_sync_offset; +	u32 sdvox; +	struct intel_sdvo_dtd output_dtd; +	int sdvo_pixel_multiply; + +	if (!mode) +		return; + +	width = mode->crtc_hdisplay; +	height = mode->crtc_vdisplay; + +	/* do some mode translations */ +	h_blank_len = mode->crtc_hblank_end - mode->crtc_hblank_start; +	h_sync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; + +	v_blank_len = mode->crtc_vblank_end - mode->crtc_vblank_start; +	v_sync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; + +	h_sync_offset = mode->crtc_hsync_start - mode->crtc_hblank_start; +	v_sync_offset = mode->crtc_vsync_start - mode->crtc_vblank_start; + +	output_dtd.part1.clock = mode->clock / 10; +	output_dtd.part1.h_active = width & 0xff; +	output_dtd.part1.h_blank = h_blank_len & 0xff; +	output_dtd.part1.h_high = (((width >> 8) & 0xf) << 4) | +		((h_blank_len >> 8) & 0xf); +	output_dtd.part1.v_active = height & 0xff; +	output_dtd.part1.v_blank = v_blank_len & 0xff; +	output_dtd.part1.v_high = (((height >> 8) & 0xf) << 4) | +		((v_blank_len >> 8) & 0xf); +	 +	output_dtd.part2.h_sync_off = h_sync_offset; +	output_dtd.part2.h_sync_width = h_sync_len & 0xff; +	output_dtd.part2.v_sync_off_width = (v_sync_offset & 0xf) << 4 | +		(v_sync_len & 0xf); +	output_dtd.part2.sync_off_width_high = ((h_sync_offset & 0x300) >> 2) | +		((h_sync_len & 0x300) >> 4) | ((v_sync_offset & 0x30) >> 2) | +		((v_sync_len & 0x30) >> 4); +	 +	output_dtd.part2.dtd_flags = 0x18; +	if (mode->flags & DRM_MODE_FLAG_PHSYNC) +		output_dtd.part2.dtd_flags |= 0x2; +	if (mode->flags & DRM_MODE_FLAG_PVSYNC) +		output_dtd.part2.dtd_flags |= 0x4; + +	output_dtd.part2.sdvo_flags = 0; +	output_dtd.part2.v_sync_off_high = v_sync_offset & 0xc0; +	output_dtd.part2.reserved = 0; + +	/* Set the output timing to the screen */ +	intel_sdvo_set_target_output(intel_output, sdvo_priv->active_outputs); +	intel_sdvo_set_output_timing(intel_output, &output_dtd); + +	/* Set the input timing to the screen. Assume always input 0. */ +	intel_sdvo_set_target_input(intel_output, true, false); + +	/* We would like to use i830_sdvo_create_preferred_input_timing() to +	 * provide the device with a timing it can support, if it supports that +	 * feature.  However, presumably we would need to adjust the CRTC to +	 * output the preferred timing, and we don't support that currently. +	 */ +#if 0 +	success = intel_sdvo_create_preferred_input_timing(intel_output, clock, +							   width, height); +	if (success) { +		struct intel_sdvo_dtd *input_dtd; +		 +		intel_sdvo_get_preferred_input_timing(intel_output, &input_dtd); +		intel_sdvo_set_input_timing(intel_output, &input_dtd); +	} +#else +	intel_sdvo_set_input_timing(intel_output, &output_dtd); +#endif	 + +	switch (intel_sdvo_get_pixel_multiplier(mode)) { +	case 1: +		intel_sdvo_set_clock_rate_mult(intel_output, +					       SDVO_CLOCK_RATE_MULT_1X); +		break; +	case 2: +		intel_sdvo_set_clock_rate_mult(intel_output, +					       SDVO_CLOCK_RATE_MULT_2X); +		break; +	case 4: +		intel_sdvo_set_clock_rate_mult(intel_output, +					       SDVO_CLOCK_RATE_MULT_4X); +		break; +	}	 + +	/* Set the SDVO control regs. */ +        if (0/*IS_I965GM(dev)*/) { +                sdvox = SDVO_BORDER_ENABLE; +        } else { +                sdvox = I915_READ(sdvo_priv->output_device); +                switch (sdvo_priv->output_device) { +                case SDVOB: +                        sdvox &= SDVOB_PRESERVE_MASK; +                        break; +                case SDVOC: +                        sdvox &= SDVOC_PRESERVE_MASK; +                        break; +                } +                sdvox |= (9 << 19) | SDVO_BORDER_ENABLE; +        } +	if (intel_crtc->pipe == 1) +		sdvox |= SDVO_PIPE_B_SELECT; + +	sdvo_pixel_multiply = intel_sdvo_get_pixel_multiplier(mode); +	if (IS_I965G(dev)) { +		/* done in crtc_mode_set as the dpll_md reg must be written  +		   early */ +	} else if (IS_I945G(dev) || IS_I945GM(dev)) { +		/* done in crtc_mode_set as it lives inside the  +		   dpll register */ +	} else { +		sdvox |= (sdvo_pixel_multiply - 1) << SDVO_PORT_MULTIPLY_SHIFT; +	} + +	intel_sdvo_write_sdvox(intel_output, sdvox); +} + +static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_output *intel_output = enc_to_intel_output(encoder); +	struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; +	u32 temp; + +	if (mode != DRM_MODE_DPMS_ON) { +		intel_sdvo_set_active_outputs(intel_output, 0); +		if (0) +			intel_sdvo_set_encoder_power_state(intel_output, mode); + +		if (mode == DRM_MODE_DPMS_OFF) { +			temp = I915_READ(sdvo_priv->output_device); +			if ((temp & SDVO_ENABLE) != 0) { +				intel_sdvo_write_sdvox(intel_output, temp & ~SDVO_ENABLE); +			} +		} +	} else { +		bool input1, input2; +		int i; +		u8 status; +		 +		temp = I915_READ(sdvo_priv->output_device); +		if ((temp & SDVO_ENABLE) == 0) +			intel_sdvo_write_sdvox(intel_output, temp | SDVO_ENABLE); +		for (i = 0; i < 2; i++) +		  intel_wait_for_vblank(dev); +		 +		status = intel_sdvo_get_trained_inputs(intel_output, &input1, +						       &input2); + +		 +		/* Warn if the device reported failure to sync.  +		 * A lot of SDVO devices fail to notify of sync, but it's +		 * a given it the status is a success, we succeeded. +		 */ +		if (status == SDVO_CMD_STATUS_SUCCESS && !input1) { +			DRM_DEBUG("First %s output reported failure to sync\n", +				   SDVO_NAME(sdvo_priv)); +		} +		 +		if (0) +			intel_sdvo_set_encoder_power_state(intel_output, mode); +		intel_sdvo_set_active_outputs(intel_output, sdvo_priv->active_outputs); +	}	 +	return; +} + +static void intel_sdvo_save(struct drm_connector *connector) +{ +	struct drm_device *dev = connector->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_output *intel_output = to_intel_output(connector); +	struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; +	int o; + +	sdvo_priv->save_sdvo_mult = intel_sdvo_get_clock_rate_mult(intel_output); +	intel_sdvo_get_active_outputs(intel_output, &sdvo_priv->save_active_outputs); + +	if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) { +		intel_sdvo_set_target_input(intel_output, true, false); +		intel_sdvo_get_input_timing(intel_output, +					    &sdvo_priv->save_input_dtd_1); +	} + +	if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) { +		intel_sdvo_set_target_input(intel_output, false, true); +		intel_sdvo_get_input_timing(intel_output, +					    &sdvo_priv->save_input_dtd_2); +	} + +	for (o = SDVO_OUTPUT_FIRST; o <= SDVO_OUTPUT_LAST; o++) +	{ +	        u16  this_output = (1 << o); +		if (sdvo_priv->caps.output_flags & this_output) +		{ +			intel_sdvo_set_target_output(intel_output, this_output); +			intel_sdvo_get_output_timing(intel_output, +						     &sdvo_priv->save_output_dtd[o]); +		} +	} + +	sdvo_priv->save_SDVOX = I915_READ(sdvo_priv->output_device); +} + +static void intel_sdvo_restore(struct drm_connector *connector) +{ +	struct drm_device *dev = connector->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_output *intel_output = to_intel_output(connector); +	struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; +	int o; +	int i; +	bool input1, input2; +	u8 status; + +	intel_sdvo_set_active_outputs(intel_output, 0); + +	for (o = SDVO_OUTPUT_FIRST; o <= SDVO_OUTPUT_LAST; o++) +	{ +		u16  this_output = (1 << o); +		if (sdvo_priv->caps.output_flags & this_output) { +			intel_sdvo_set_target_output(intel_output, this_output); +			intel_sdvo_set_output_timing(intel_output, &sdvo_priv->save_output_dtd[o]); +		} +	} + +	if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) { +		intel_sdvo_set_target_input(intel_output, true, false); +		intel_sdvo_set_input_timing(intel_output, &sdvo_priv->save_input_dtd_1); +	} + +	if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) { +		intel_sdvo_set_target_input(intel_output, false, true); +		intel_sdvo_set_input_timing(intel_output, &sdvo_priv->save_input_dtd_2); +	} +	 +	intel_sdvo_set_clock_rate_mult(intel_output, sdvo_priv->save_sdvo_mult); +	 +	I915_WRITE(sdvo_priv->output_device, sdvo_priv->save_SDVOX); +	 +	if (sdvo_priv->save_SDVOX & SDVO_ENABLE) +	{ +		for (i = 0; i < 2; i++) +			intel_wait_for_vblank(dev); +		status = intel_sdvo_get_trained_inputs(intel_output, &input1, &input2); +		if (status == SDVO_CMD_STATUS_SUCCESS && !input1) +			DRM_DEBUG("First %s output reported failure to sync\n", +				   SDVO_NAME(sdvo_priv)); +	} +	 +	intel_sdvo_set_active_outputs(intel_output, sdvo_priv->save_active_outputs); +} + +static int intel_sdvo_mode_valid(struct drm_connector *connector, +				 struct drm_display_mode *mode) +{ +	struct intel_output *intel_output = to_intel_output(connector); +	struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + +	if (mode->flags & DRM_MODE_FLAG_DBLSCAN) +		return MODE_NO_DBLESCAN; + +	if (sdvo_priv->pixel_clock_min > mode->clock) +		return MODE_CLOCK_LOW; + +	if (sdvo_priv->pixel_clock_max < mode->clock) +		return MODE_CLOCK_HIGH; + +	return MODE_OK; +} + +static bool intel_sdvo_get_capabilities(struct intel_output *intel_output, struct intel_sdvo_caps *caps) +{ +	u8 status; + +	intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_DEVICE_CAPS, NULL, 0); +	status = intel_sdvo_read_response(intel_output, caps, sizeof(*caps)); +	if (status != SDVO_CMD_STATUS_SUCCESS) +		return false; + +	return true; +} + +struct drm_connector* intel_sdvo_find(struct drm_device *dev, int sdvoB) +{ +	struct drm_connector *connector = NULL; +	struct intel_output *iout = NULL; +	struct intel_sdvo_priv *sdvo; + +	/* find the sdvo connector */ +	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { +		iout = to_intel_output(connector); + +		if (iout->type != INTEL_OUTPUT_SDVO) +			continue; + +		sdvo = iout->dev_priv; + +		if (sdvo->output_device == SDVOB && sdvoB) +			return connector; + +		if (sdvo->output_device == SDVOC && !sdvoB) +			return connector; + +	} +	 +	return NULL; +} + +int intel_sdvo_supports_hotplug(struct drm_connector *connector) +{ +	u8 response[2]; +	u8 status; +	struct intel_output *intel_output; +	DRM_DEBUG("\n"); + +	if (!connector) +		return 0; + +	intel_output = to_intel_output(connector); + +	intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_HOT_PLUG_SUPPORT, NULL, 0); +	status = intel_sdvo_read_response(intel_output, &response, 2); + +	if (response[0] !=0) +		return 1; + +	return 0; +} + +void intel_sdvo_set_hotplug(struct drm_connector *connector, int on) +{ +	u8 response[2]; +	u8 status; +	struct intel_output *intel_output = to_intel_output(connector); + +	intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ACTIVE_HOT_PLUG, NULL, 0); +	intel_sdvo_read_response(intel_output, &response, 2); + +	if (on) { +		intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_HOT_PLUG_SUPPORT, NULL, 0); +		status = intel_sdvo_read_response(intel_output, &response, 2); + +		intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &response, 2); +	} else { +		response[0] = 0; +		response[1] = 0; +		intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &response, 2); +	} + +	intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ACTIVE_HOT_PLUG, NULL, 0); +	intel_sdvo_read_response(intel_output, &response, 2); +} + +static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connector) +{ +	u8 response[2]; +	u8 status; +	struct intel_output *intel_output = to_intel_output(connector); + +	intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0); +	status = intel_sdvo_read_response(intel_output, &response, 2); + +	DRM_DEBUG("SDVO response %d %d\n", response[0], response[1]); +	if ((response[0] != 0) || (response[1] != 0)) +		return connector_status_connected; +	else +		return connector_status_disconnected; +} + +static int intel_sdvo_get_modes(struct drm_connector *connector) +{ +	struct intel_output *intel_output = to_intel_output(connector); + +	/* set the bus switch and get the modes */ +	intel_sdvo_set_control_bus_switch(intel_output, SDVO_CONTROL_BUS_DDC2); +	intel_ddc_get_modes(intel_output); + +	if (list_empty(&connector->probed_modes)) +		return 0; +	return 1; +#if 0 +	/* Mac mini hack.  On this device, I get DDC through the analog, which +	 * load-detects as disconnected.  I fail to DDC through the SDVO DDC, +	 * but it does load-detect as connected.  So, just steal the DDC bits  +	 * from analog when we fail at finding it the right way. +	 */ +	/* TODO */ +	return NULL; + +	return NULL; +#endif +} + +static void intel_sdvo_destroy(struct drm_connector *connector) +{ +	struct intel_output *intel_output = to_intel_output(connector); + +	if (intel_output->i2c_bus) +		intel_i2c_destroy(intel_output->i2c_bus); +	drm_sysfs_connector_remove(connector); +	drm_connector_cleanup(connector); +	kfree(intel_output); +} + +static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = { +	.dpms = intel_sdvo_dpms, +	.mode_fixup = intel_sdvo_mode_fixup, +	.prepare = intel_encoder_prepare, +	.mode_set = intel_sdvo_mode_set, +	.commit = intel_encoder_commit, +}; + +static const struct drm_connector_funcs intel_sdvo_connector_funcs = { +	.save = intel_sdvo_save, +	.restore = intel_sdvo_restore, +	.detect = intel_sdvo_detect, +	.fill_modes = drm_helper_probe_single_connector_modes, +	.destroy = intel_sdvo_destroy, +}; + +static const struct drm_connector_helper_funcs intel_sdvo_connector_helper_funcs = { +	.get_modes = intel_sdvo_get_modes, +	.mode_valid = intel_sdvo_mode_valid, +	.best_encoder = intel_best_encoder, +}; + +void intel_sdvo_enc_destroy(struct drm_encoder *encoder) +{ +	drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs intel_sdvo_enc_funcs = { +	.destroy = intel_sdvo_enc_destroy, +}; + + +void intel_sdvo_init(struct drm_device *dev, int output_device) +{ +	struct drm_connector *connector; +	struct intel_output *intel_output; +	struct intel_sdvo_priv *sdvo_priv; +	struct intel_i2c_chan *i2cbus = NULL; +	int connector_type; +	u8 ch[0x40]; +	int i; +	int encoder_type, output_id; + +	intel_output = kcalloc(sizeof(struct intel_output)+sizeof(struct intel_sdvo_priv), 1, GFP_KERNEL); +	if (!intel_output) { +		return; +	} + +	connector = &intel_output->base; + +	drm_connector_init(dev, connector, &intel_sdvo_connector_funcs, +			   DRM_MODE_CONNECTOR_Unknown); +	drm_connector_helper_add(connector, &intel_sdvo_connector_helper_funcs); +	sdvo_priv = (struct intel_sdvo_priv *)(intel_output + 1); +	intel_output->type = INTEL_OUTPUT_SDVO; + +	connector->interlace_allowed = 0; +	connector->doublescan_allowed = 0; + +	/* setup the DDC bus. */ +	if (output_device == SDVOB) +		i2cbus = intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOB"); +	else +		i2cbus = intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOC"); + +	if (!i2cbus) +		goto err_connector; + +	sdvo_priv->i2c_bus = i2cbus; + +	if (output_device == SDVOB) { +		output_id = 1; +		sdvo_priv->i2c_bus->slave_addr = 0x38; +	} else { +		output_id = 2; +		sdvo_priv->i2c_bus->slave_addr = 0x39; +	} + +	sdvo_priv->output_device = output_device; +	intel_output->i2c_bus = i2cbus; +	intel_output->dev_priv = sdvo_priv; + + +	/* Read the regs to test if we can talk to the device */ +	for (i = 0; i < 0x40; i++) { +		if (!intel_sdvo_read_byte(intel_output, i, &ch[i])) { +			DRM_DEBUG("No SDVO device found on SDVO%c\n", +				  output_device == SDVOB ? 'B' : 'C'); +			goto err_i2c; +		} +	} + +	intel_sdvo_get_capabilities(intel_output, &sdvo_priv->caps); + +	memset(&sdvo_priv->active_outputs, 0, sizeof(sdvo_priv->active_outputs)); + +	/* TODO, CVBS, SVID, YPRPB & SCART outputs. */ +	if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB0) +	{ +		sdvo_priv->active_outputs = SDVO_OUTPUT_RGB0; +		connector->display_info.subpixel_order = SubPixelHorizontalRGB; +		encoder_type = DRM_MODE_ENCODER_DAC; +		connector_type = DRM_MODE_CONNECTOR_VGA; +	} +	else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB1) +	{ +		sdvo_priv->active_outputs = SDVO_OUTPUT_RGB1; +		connector->display_info.subpixel_order = SubPixelHorizontalRGB; +		encoder_type = DRM_MODE_ENCODER_DAC; +		connector_type = DRM_MODE_CONNECTOR_VGA; +	} +	else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0) +	{ +		sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS0; +		connector->display_info.subpixel_order = SubPixelHorizontalRGB; +		encoder_type = DRM_MODE_ENCODER_TMDS; +		connector_type = DRM_MODE_CONNECTOR_DVID; +	} +	else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS1) +	{ +		sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS1; +		connector->display_info.subpixel_order = SubPixelHorizontalRGB; +		encoder_type = DRM_MODE_ENCODER_TMDS; +		connector_type = DRM_MODE_CONNECTOR_DVID; +	} +	else +	{ +		unsigned char bytes[2]; +		 +		memcpy (bytes, &sdvo_priv->caps.output_flags, 2); +		DRM_DEBUG("%s: No active RGB or TMDS outputs (0x%02x%02x)\n", +			  SDVO_NAME(sdvo_priv), +			  bytes[0], bytes[1]); +		goto err_i2c; +	} +	 +	drm_encoder_init(dev, &intel_output->enc, &intel_sdvo_enc_funcs, encoder_type); +	drm_encoder_helper_add(&intel_output->enc, &intel_sdvo_helper_funcs); +	connector->connector_type = connector_type; + +	drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc); +	drm_sysfs_connector_add(connector); + +	/* Set the input timing to the screen. Assume always input 0. */ +	intel_sdvo_set_target_input(intel_output, true, false); +	 +	intel_sdvo_get_input_pixel_clock_range(intel_output, +					       &sdvo_priv->pixel_clock_min, +					       &sdvo_priv->pixel_clock_max); + + +	DRM_DEBUG("%s device VID/DID: %02X:%02X.%02X, " +		  "clock range %dMHz - %dMHz, " +		  "input 1: %c, input 2: %c, " +		  "output 1: %c, output 2: %c\n", +		  SDVO_NAME(sdvo_priv), +		  sdvo_priv->caps.vendor_id, sdvo_priv->caps.device_id, +		  sdvo_priv->caps.device_rev_id, +		  sdvo_priv->pixel_clock_min / 1000, +		  sdvo_priv->pixel_clock_max / 1000, +		  (sdvo_priv->caps.sdvo_inputs_mask & 0x1) ? 'Y' : 'N', +		  (sdvo_priv->caps.sdvo_inputs_mask & 0x2) ? 'Y' : 'N', +		  /* check currently supported outputs */ +		  sdvo_priv->caps.output_flags &  +		  	(SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_RGB0) ? 'Y' : 'N', +		  sdvo_priv->caps.output_flags &  +		  	(SDVO_OUTPUT_TMDS1 | SDVO_OUTPUT_RGB1) ? 'Y' : 'N'); + +	intel_output->ddc_bus = i2cbus; + +	return; + +err_i2c: +	intel_i2c_destroy(intel_output->i2c_bus); +err_connector: +	drm_connector_cleanup(connector); +	kfree(intel_output); + +	return; +} diff --git a/linux-core/intel_sdvo_regs.h b/linux-core/intel_sdvo_regs.h new file mode 100644 index 00000000..a9d16711 --- /dev/null +++ b/linux-core/intel_sdvo_regs.h @@ -0,0 +1,328 @@ +/* + * Copyright © 2006-2007 Intel Corporation + * + * 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 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + *	Eric Anholt <eric@anholt.net> + */ + +/** + * @file SDVO command definitions and structures. + */ + +#define SDVO_OUTPUT_FIRST   (0) +#define SDVO_OUTPUT_TMDS0   (1 << 0) +#define SDVO_OUTPUT_RGB0    (1 << 1) +#define SDVO_OUTPUT_CVBS0   (1 << 2) +#define SDVO_OUTPUT_SVID0   (1 << 3) +#define SDVO_OUTPUT_YPRPB0  (1 << 4) +#define SDVO_OUTPUT_SCART0  (1 << 5) +#define SDVO_OUTPUT_LVDS0   (1 << 6) +#define SDVO_OUTPUT_TMDS1   (1 << 8) +#define SDVO_OUTPUT_RGB1    (1 << 9) +#define SDVO_OUTPUT_CVBS1   (1 << 10) +#define SDVO_OUTPUT_SVID1   (1 << 11) +#define SDVO_OUTPUT_YPRPB1  (1 << 12) +#define SDVO_OUTPUT_SCART1  (1 << 13) +#define SDVO_OUTPUT_LVDS1   (1 << 14) +#define SDVO_OUTPUT_LAST    (14) + +struct intel_sdvo_caps { +    u8 vendor_id; +    u8 device_id; +    u8 device_rev_id; +    u8 sdvo_version_major; +    u8 sdvo_version_minor; +    unsigned int sdvo_inputs_mask:2; +    unsigned int smooth_scaling:1; +    unsigned int sharp_scaling:1; +    unsigned int up_scaling:1; +    unsigned int down_scaling:1; +    unsigned int stall_support:1; +    unsigned int pad:1; +    u16 output_flags; +} __attribute__((packed)); + +/** This matches the EDID DTD structure, more or less */ +struct intel_sdvo_dtd { +    struct { +	u16 clock;		/**< pixel clock, in 10kHz units */ +	u8 h_active;		/**< lower 8 bits (pixels) */ +	u8 h_blank;		/**< lower 8 bits (pixels) */ +	u8 h_high;		/**< upper 4 bits each h_active, h_blank */ +	u8 v_active;		/**< lower 8 bits (lines) */ +	u8 v_blank;		/**< lower 8 bits (lines) */ +	u8 v_high;		/**< upper 4 bits each v_active, v_blank */ +    } part1; + +    struct { +	u8 h_sync_off;	/**< lower 8 bits, from hblank start */ +	u8 h_sync_width;	/**< lower 8 bits (pixels) */ +	/** lower 4 bits each vsync offset, vsync width */ +	u8 v_sync_off_width; +	/** +	 * 2 high bits of hsync offset, 2 high bits of hsync width, +	 * bits 4-5 of vsync offset, and 2 high bits of vsync width. +	 */ +	u8 sync_off_width_high; +	u8 dtd_flags; +	u8 sdvo_flags; +	/** bits 6-7 of vsync offset at bits 6-7 */ +	u8 v_sync_off_high; +	u8 reserved; +    } part2; +} __attribute__((packed)); + +struct intel_sdvo_pixel_clock_range { +    u16 min;			/**< pixel clock, in 10kHz units */ +    u16 max;			/**< pixel clock, in 10kHz units */ +} __attribute__((packed)); + +struct intel_sdvo_preferred_input_timing_args { +    u16 clock; +    u16 width; +    u16 height; +} __attribute__((packed)); + +/* I2C registers for SDVO */ +#define SDVO_I2C_ARG_0				0x07 +#define SDVO_I2C_ARG_1				0x06 +#define SDVO_I2C_ARG_2				0x05 +#define SDVO_I2C_ARG_3				0x04 +#define SDVO_I2C_ARG_4				0x03 +#define SDVO_I2C_ARG_5				0x02 +#define SDVO_I2C_ARG_6				0x01 +#define SDVO_I2C_ARG_7				0x00 +#define SDVO_I2C_OPCODE				0x08 +#define SDVO_I2C_CMD_STATUS			0x09 +#define SDVO_I2C_RETURN_0			0x0a +#define SDVO_I2C_RETURN_1			0x0b +#define SDVO_I2C_RETURN_2			0x0c +#define SDVO_I2C_RETURN_3			0x0d +#define SDVO_I2C_RETURN_4			0x0e +#define SDVO_I2C_RETURN_5			0x0f +#define SDVO_I2C_RETURN_6			0x10 +#define SDVO_I2C_RETURN_7			0x11 +#define SDVO_I2C_VENDOR_BEGIN			0x20 + +/* Status results */ +#define SDVO_CMD_STATUS_POWER_ON		0x0 +#define SDVO_CMD_STATUS_SUCCESS			0x1 +#define SDVO_CMD_STATUS_NOTSUPP			0x2 +#define SDVO_CMD_STATUS_INVALID_ARG		0x3 +#define SDVO_CMD_STATUS_PENDING			0x4 +#define SDVO_CMD_STATUS_TARGET_NOT_SPECIFIED	0x5 +#define SDVO_CMD_STATUS_SCALING_NOT_SUPP	0x6 + +/* SDVO commands, argument/result registers */ + +#define SDVO_CMD_RESET					0x01 + +/** Returns a struct intel_sdvo_caps */ +#define SDVO_CMD_GET_DEVICE_CAPS			0x02 + +#define SDVO_CMD_GET_FIRMWARE_REV			0x86 +# define SDVO_DEVICE_FIRMWARE_MINOR			SDVO_I2C_RETURN_0 +# define SDVO_DEVICE_FIRMWARE_MAJOR			SDVO_I2C_RETURN_1 +# define SDVO_DEVICE_FIRMWARE_PATCH			SDVO_I2C_RETURN_2 + +/** + * Reports which inputs are trained (managed to sync). + * + * Devices must have trained within 2 vsyncs of a mode change. + */ +#define SDVO_CMD_GET_TRAINED_INPUTS			0x03 +struct intel_sdvo_get_trained_inputs_response { +    unsigned int input0_trained:1; +    unsigned int input1_trained:1; +    unsigned int pad:6; +} __attribute__((packed)); + +/** Returns a struct intel_sdvo_output_flags of active outputs. */ +#define SDVO_CMD_GET_ACTIVE_OUTPUTS			0x04 + +/** + * Sets the current set of active outputs. + * + * Takes a struct intel_sdvo_output_flags.  Must be preceded by a SET_IN_OUT_MAP + * on multi-output devices. + */ +#define SDVO_CMD_SET_ACTIVE_OUTPUTS			0x05 + +/** + * Returns the current mapping of SDVO inputs to outputs on the device. + * + * Returns two struct intel_sdvo_output_flags structures. + */ +#define SDVO_CMD_GET_IN_OUT_MAP				0x06 + +/** + * Sets the current mapping of SDVO inputs to outputs on the device. + * + * Takes two struct i380_sdvo_output_flags structures. + */ +#define SDVO_CMD_SET_IN_OUT_MAP				0x07 + +/** + * Returns a struct intel_sdvo_output_flags of attached displays. + */ +#define SDVO_CMD_GET_ATTACHED_DISPLAYS			0x0b + +/** + * Returns a struct intel_sdvo_ouptut_flags of displays supporting hot plugging. + */ +#define SDVO_CMD_GET_HOT_PLUG_SUPPORT			0x0c + +/** + * Takes a struct intel_sdvo_output_flags. + */ +#define SDVO_CMD_SET_ACTIVE_HOT_PLUG			0x0d + +/** + * Returns a struct intel_sdvo_output_flags of displays with hot plug + * interrupts enabled. + */ +#define SDVO_CMD_GET_ACTIVE_HOT_PLUG			0x0e + +#define SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE		0x0f +struct intel_sdvo_get_interrupt_event_source_response { +    u16 interrupt_status; +    unsigned int ambient_light_interrupt:1; +    unsigned int pad:7; +} __attribute__((packed)); + +/** + * Selects which input is affected by future input commands. + * + * Commands affected include SET_INPUT_TIMINGS_PART[12], + * GET_INPUT_TIMINGS_PART[12], GET_PREFERRED_INPUT_TIMINGS_PART[12], + * GET_INPUT_PIXEL_CLOCK_RANGE, and CREATE_PREFERRED_INPUT_TIMINGS. + */ +#define SDVO_CMD_SET_TARGET_INPUT			0x10 +struct intel_sdvo_set_target_input_args { +    unsigned int target_1:1; +    unsigned int pad:7; +} __attribute__((packed)); + +/** + * Takes a struct intel_sdvo_output_flags of which outputs are targetted by + * future output commands. + * + * Affected commands inclue SET_OUTPUT_TIMINGS_PART[12], + * GET_OUTPUT_TIMINGS_PART[12], and GET_OUTPUT_PIXEL_CLOCK_RANGE. + */ +#define SDVO_CMD_SET_TARGET_OUTPUT			0x11 + +#define SDVO_CMD_GET_INPUT_TIMINGS_PART1		0x12 +#define SDVO_CMD_GET_INPUT_TIMINGS_PART2		0x13 +#define SDVO_CMD_SET_INPUT_TIMINGS_PART1		0x14 +#define SDVO_CMD_SET_INPUT_TIMINGS_PART2		0x15 +#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART1		0x16 +#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART2		0x17 +#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART1		0x18 +#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART2		0x19 +/* Part 1 */ +# define SDVO_DTD_CLOCK_LOW				SDVO_I2C_ARG_0 +# define SDVO_DTD_CLOCK_HIGH				SDVO_I2C_ARG_1 +# define SDVO_DTD_H_ACTIVE				SDVO_I2C_ARG_2 +# define SDVO_DTD_H_BLANK				SDVO_I2C_ARG_3 +# define SDVO_DTD_H_HIGH				SDVO_I2C_ARG_4 +# define SDVO_DTD_V_ACTIVE				SDVO_I2C_ARG_5 +# define SDVO_DTD_V_BLANK				SDVO_I2C_ARG_6 +# define SDVO_DTD_V_HIGH				SDVO_I2C_ARG_7 +/* Part 2 */ +# define SDVO_DTD_HSYNC_OFF				SDVO_I2C_ARG_0 +# define SDVO_DTD_HSYNC_WIDTH				SDVO_I2C_ARG_1 +# define SDVO_DTD_VSYNC_OFF_WIDTH			SDVO_I2C_ARG_2 +# define SDVO_DTD_SYNC_OFF_WIDTH_HIGH			SDVO_I2C_ARG_3 +# define SDVO_DTD_DTD_FLAGS				SDVO_I2C_ARG_4 +# define SDVO_DTD_DTD_FLAG_INTERLACED				(1 << 7) +# define SDVO_DTD_DTD_FLAG_STEREO_MASK				(3 << 5) +# define SDVO_DTD_DTD_FLAG_INPUT_MASK				(3 << 3) +# define SDVO_DTD_DTD_FLAG_SYNC_MASK				(3 << 1) +# define SDVO_DTD_SDVO_FLAS				SDVO_I2C_ARG_5 +# define SDVO_DTD_SDVO_FLAG_STALL				(1 << 7) +# define SDVO_DTD_SDVO_FLAG_CENTERED				(0 << 6) +# define SDVO_DTD_SDVO_FLAG_UPPER_LEFT				(1 << 6) +# define SDVO_DTD_SDVO_FLAG_SCALING_MASK			(3 << 4) +# define SDVO_DTD_SDVO_FLAG_SCALING_NONE			(0 << 4) +# define SDVO_DTD_SDVO_FLAG_SCALING_SHARP			(1 << 4) +# define SDVO_DTD_SDVO_FLAG_SCALING_SMOOTH			(2 << 4) +# define SDVO_DTD_VSYNC_OFF_HIGH			SDVO_I2C_ARG_6 + +/** + * Generates a DTD based on the given width, height, and flags. + * + * This will be supported by any device supporting scaling or interlaced + * modes. + */ +#define SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING		0x1a +# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_LOW		SDVO_I2C_ARG_0 +# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_HIGH		SDVO_I2C_ARG_1 +# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_LOW		SDVO_I2C_ARG_2 +# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_HIGH		SDVO_I2C_ARG_3 +# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_LOW		SDVO_I2C_ARG_4 +# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_HIGH	SDVO_I2C_ARG_5 +# define SDVO_PREFERRED_INPUT_TIMING_FLAGS		SDVO_I2C_ARG_6 +# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_INTERLACED		(1 << 0) +# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_SCALED		(1 << 1) + +#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1	0x1b +#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2	0x1c + +/** Returns a struct intel_sdvo_pixel_clock_range */ +#define SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE		0x1d +/** Returns a struct intel_sdvo_pixel_clock_range */ +#define SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE		0x1e + +/** Returns a byte bitfield containing SDVO_CLOCK_RATE_MULT_* flags */ +#define SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS		0x1f + +/** Returns a byte containing a SDVO_CLOCK_RATE_MULT_* flag */ +#define SDVO_CMD_GET_CLOCK_RATE_MULT			0x20 +/** Takes a byte containing a SDVO_CLOCK_RATE_MULT_* flag */ +#define SDVO_CMD_SET_CLOCK_RATE_MULT			0x21 +# define SDVO_CLOCK_RATE_MULT_1X				(1 << 0) +# define SDVO_CLOCK_RATE_MULT_2X				(1 << 1) +# define SDVO_CLOCK_RATE_MULT_4X				(1 << 3) + +#define SDVO_CMD_GET_SUPPORTED_TV_FORMATS		0x27 + +#define SDVO_CMD_GET_TV_FORMAT				0x28 + +#define SDVO_CMD_SET_TV_FORMAT				0x29 + +#define SDVO_CMD_GET_SUPPORTED_POWER_STATES		0x2a +#define SDVO_CMD_GET_ENCODER_POWER_STATE		0x2b +#define SDVO_CMD_SET_ENCODER_POWER_STATE		0x2c +# define SDVO_ENCODER_STATE_ON					(1 << 0) +# define SDVO_ENCODER_STATE_STANDBY				(1 << 1) +# define SDVO_ENCODER_STATE_SUSPEND				(1 << 2) +# define SDVO_ENCODER_STATE_OFF					(1 << 3) + +#define SDVO_CMD_SET_TV_RESOLUTION_SUPPORT		0x93 + +#define SDVO_CMD_SET_CONTROL_BUS_SWITCH			0x7a +# define SDVO_CONTROL_BUS_PROM				0x0 +# define SDVO_CONTROL_BUS_DDC1				0x1 +# define SDVO_CONTROL_BUS_DDC2				0x2 +# define SDVO_CONTROL_BUS_DDC3				0x3 + diff --git a/linux-core/intel_tv.c b/linux-core/intel_tv.c new file mode 100644 index 00000000..3efa9d2d --- /dev/null +++ b/linux-core/intel_tv.c @@ -0,0 +1,1734 @@ +/* + * Copyright © 2006 Intel Corporation + * + * 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 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + *    Eric Anholt <eric@anholt.net> + * + */ + +/** @file + * Integrated TV-out support for the 915GM and 945GM. + */ + +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "drm_edid.h" +#include "intel_drv.h" +#include "i915_drm.h" +#include "i915_drv.h" + +enum tv_margin { +	TV_MARGIN_LEFT, TV_MARGIN_TOP, +	TV_MARGIN_RIGHT, TV_MARGIN_BOTTOM +}; + +/** Private structure for the integrated TV support */ +struct intel_tv_priv { +	int type; +	char *tv_format; +	int margin[4]; +	u32 save_TV_H_CTL_1; +	u32 save_TV_H_CTL_2; +	u32 save_TV_H_CTL_3; +	u32 save_TV_V_CTL_1; +	u32 save_TV_V_CTL_2; +	u32 save_TV_V_CTL_3; +	u32 save_TV_V_CTL_4; +	u32 save_TV_V_CTL_5; +	u32 save_TV_V_CTL_6; +	u32 save_TV_V_CTL_7; +	u32 save_TV_SC_CTL_1, save_TV_SC_CTL_2, save_TV_SC_CTL_3; + +	u32 save_TV_CSC_Y; +	u32 save_TV_CSC_Y2; +	u32 save_TV_CSC_U; +	u32 save_TV_CSC_U2; +	u32 save_TV_CSC_V; +	u32 save_TV_CSC_V2; +	u32 save_TV_CLR_KNOBS; +	u32 save_TV_CLR_LEVEL; +	u32 save_TV_WIN_POS; +	u32 save_TV_WIN_SIZE; +	u32 save_TV_FILTER_CTL_1; +	u32 save_TV_FILTER_CTL_2; +	u32 save_TV_FILTER_CTL_3; + +	u32 save_TV_H_LUMA[60]; +	u32 save_TV_H_CHROMA[60]; +	u32 save_TV_V_LUMA[43]; +	u32 save_TV_V_CHROMA[43]; + +	u32 save_TV_DAC; +	u32 save_TV_CTL; +}; + +struct video_levels { +	int blank, black, burst; +}; + +struct color_conversion { +	u16 ry, gy, by, ay; +	u16 ru, gu, bu, au; +	u16 rv, gv, bv, av; +}; + +static const u32 filter_table[] = { +	0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140, +	0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000, +	0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160, +	0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780, +	0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50, +	0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20, +	0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0, +	0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0, +	0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020, +	0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140, +	0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20, +	0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848, +	0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900, +	0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080, +	0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060, +	0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140, +	0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000, +	0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160, +	0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780, +	0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50, +	0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20, +	0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0, +	0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0, +	0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020, +	0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140, +	0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20, +	0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848, +	0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900, +	0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080, +	0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060, +	0x36403000, 0x2D002CC0, 0x30003640, 0x2D0036C0, +	0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540, +	0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00, +	0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000, +	0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00, +	0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40, +	0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240, +	0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00, +	0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0, +	0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840, +	0x28003100, 0x28002F00, 0x00003100, 0x36403000,  +	0x2D002CC0, 0x30003640, 0x2D0036C0, +	0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540, +	0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00, +	0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000, +	0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00, +	0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40, +	0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240, +	0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00, +	0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0, +	0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840, +	0x28003100, 0x28002F00, 0x00003100, +}; + +/* + * Color conversion values have 3 separate fixed point formats: + * + * 10 bit fields (ay, au) + *   1.9 fixed point (b.bbbbbbbbb) + * 11 bit fields (ry, by, ru, gu, gv) + *   exp.mantissa (ee.mmmmmmmmm) + *   ee = 00 = 10^-1 (0.mmmmmmmmm) + *   ee = 01 = 10^-2 (0.0mmmmmmmmm) + *   ee = 10 = 10^-3 (0.00mmmmmmmmm) + *   ee = 11 = 10^-4 (0.000mmmmmmmmm) + * 12 bit fields (gy, rv, bu) + *   exp.mantissa (eee.mmmmmmmmm) + *   eee = 000 = 10^-1 (0.mmmmmmmmm) + *   eee = 001 = 10^-2 (0.0mmmmmmmmm) + *   eee = 010 = 10^-3 (0.00mmmmmmmmm) + *   eee = 011 = 10^-4 (0.000mmmmmmmmm) + *   eee = 100 = reserved + *   eee = 101 = reserved + *   eee = 110 = reserved + *   eee = 111 = 10^0 (m.mmmmmmmm) (only usable for 1.0 representation) + * + * Saturation and contrast are 8 bits, with their own representation: + * 8 bit field (saturation, contrast) + *   exp.mantissa (ee.mmmmmm) + *   ee = 00 = 10^-1 (0.mmmmmm) + *   ee = 01 = 10^0 (m.mmmmm) + *   ee = 10 = 10^1 (mm.mmmm) + *   ee = 11 = 10^2 (mmm.mmm) + * + * Simple conversion function: + * + * static u32 + * float_to_csc_11(float f) + * { + *     u32 exp; + *     u32 mant; + *     u32 ret; + *  + *     if (f < 0) + *         f = -f; + *  + *     if (f >= 1) { + *         exp = 0x7; + * 	   mant = 1 << 8; + *     } else { + *         for (exp = 0; exp < 3 && f < 0.5; exp++) + * 	       f *= 2.0; + *         mant = (f * (1 << 9) + 0.5); + *         if (mant >= (1 << 9)) + *             mant = (1 << 9) - 1; + *     } + *     ret = (exp << 9) | mant; + *     return ret; + * } + */ + +/* + * Behold, magic numbers!  If we plant them they might grow a big + * s-video cable to the sky... or something. + * + * Pre-converted to appropriate hex value. + */ + +/* + * PAL & NTSC values for composite & s-video connections + */ +static const struct color_conversion ntsc_m_csc_composite = { +	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104, +	.ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0f00, +	.rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0f00, +}; + +static const struct video_levels ntsc_m_levels_composite = { +	.blank = 225, .black = 267, .burst = 113, +}; + +static const struct color_conversion ntsc_m_csc_svideo = { +	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0134, +	.ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0f00, +	.rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0f00, +}; + +static const struct video_levels ntsc_m_levels_svideo = { +	.blank = 266, .black = 316, .burst = 133, +}; + +static const struct color_conversion ntsc_j_csc_composite = { +	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0119, +	.ru = 0x074c, .gu = 0x0546, .bu = 0x05ec, .au = 0x0f00, +	.rv = 0x035a, .gv = 0x0322, .bv = 0x06e1, .av = 0x0f00, +}; + +static const struct video_levels ntsc_j_levels_composite = { +	.blank = 225, .black = 225, .burst = 113, +}; + +static const struct color_conversion ntsc_j_csc_svideo = { +	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x014c, +	.ru = 0x0788, .gu = 0x0581, .bu = 0x0322, .au = 0x0f00, +	.rv = 0x0399, .gv = 0x0356, .bv = 0x070a, .av = 0x0f00, +}; + +static const struct video_levels ntsc_j_levels_svideo = { +	.blank = 266, .black = 266, .burst = 133, +}; + +static const struct color_conversion pal_csc_composite = { +	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0113, +	.ru = 0x0745, .gu = 0x053f, .bu = 0x05e1, .au = 0x0f00, +	.rv = 0x0353, .gv = 0x031c, .bv = 0x06dc, .av = 0x0f00, +}; +	 +static const struct video_levels pal_levels_composite = { +	.blank = 237, .black = 237, .burst = 118, +}; + +static const struct color_conversion pal_csc_svideo = { +	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145, +	.ru = 0x0780, .gu = 0x0579, .bu = 0x031c, .au = 0x0f00, +	.rv = 0x0390, .gv = 0x034f, .bv = 0x0705, .av = 0x0f00, +}; + +static const struct video_levels pal_levels_svideo = { +	.blank = 280, .black = 280, .burst = 139, +}; + +static const struct color_conversion pal_m_csc_composite = { +	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104, +	.ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0f00, +	.rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0f00, +}; + +static const struct video_levels pal_m_levels_composite = { +	.blank = 225, .black = 267, .burst = 113, +}; + +static const struct color_conversion pal_m_csc_svideo = { +	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0134, +	.ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0f00, +	.rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0f00, +}; + +static const struct video_levels pal_m_levels_svideo = { +	.blank = 266, .black = 316, .burst = 133, +}; + +static const struct color_conversion pal_n_csc_composite = { +	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104, +	.ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0f00, +	.rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0f00, +}; + +static const struct video_levels pal_n_levels_composite = { +	.blank = 225, .black = 267, .burst = 118, +}; + +static const struct color_conversion pal_n_csc_svideo = { +	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0134, +	.ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0f00, +	.rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0f00, +}; + +static const struct video_levels pal_n_levels_svideo = { +	.blank = 266, .black = 316, .burst = 139, +}; + +/* + * Component connections + */ +static const struct color_conversion sdtv_csc_yprpb = { +	.ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0146, +	.ru = 0x0559, .gu = 0x0353, .bu = 0x0100, .au = 0x0f00, +	.rv = 0x0100, .gv = 0x03ad, .bv = 0x074d, .av = 0x0f00, +}; + +static const struct color_conversion sdtv_csc_rgb = { +	.ry = 0x0000, .gy = 0x0f00, .by = 0x0000, .ay = 0x0166, +	.ru = 0x0000, .gu = 0x0000, .bu = 0x0f00, .au = 0x0166, +	.rv = 0x0f00, .gv = 0x0000, .bv = 0x0000, .av = 0x0166, +}; + +static const struct color_conversion hdtv_csc_yprpb = { +	.ry = 0x05b3, .gy = 0x016e, .by = 0x0728, .ay = 0x0146, +	.ru = 0x07d5, .gu = 0x038b, .bu = 0x0100, .au = 0x0f00, +	.rv = 0x0100, .gv = 0x03d1, .bv = 0x06bc, .av = 0x0f00, +}; + +static const struct color_conversion hdtv_csc_rgb = { +	.ry = 0x0000, .gy = 0x0f00, .by = 0x0000, .ay = 0x0166, +	.ru = 0x0000, .gu = 0x0000, .bu = 0x0f00, .au = 0x0166, +	.rv = 0x0f00, .gv = 0x0000, .bv = 0x0000, .av = 0x0166, +}; + +static const struct video_levels component_levels = { +	.blank = 279, .black = 279, .burst = 0, +}; + + +struct tv_mode { +	char *name; +	int clock; +	int refresh; /* in millihertz (for precision) */ +	u32 oversample; +	int hsync_end, hblank_start, hblank_end, htotal; +	bool progressive, trilevel_sync, component_only; +	int vsync_start_f1, vsync_start_f2, vsync_len; +	bool veq_ena; +	int veq_start_f1, veq_start_f2, veq_len; +	int vi_end_f1, vi_end_f2, nbr_end; +	bool burst_ena; +	int hburst_start, hburst_len; +	int vburst_start_f1, vburst_end_f1; +	int vburst_start_f2, vburst_end_f2; +	int vburst_start_f3, vburst_end_f3; +	int vburst_start_f4, vburst_end_f4; +	/* +	 * subcarrier programming +	 */ +	int dda2_size, dda3_size, dda1_inc, dda2_inc, dda3_inc; +	u32 sc_reset; +	bool pal_burst; +	/* +	 * blank/black levels +	 */ +	const struct video_levels *composite_levels, *svideo_levels; +	const struct color_conversion *composite_color, *svideo_color; +	const u32 *filter_table; +	int max_srcw; +}; + + +/* + * Sub carrier DDA + * + *  I think this works as follows: + * + *  subcarrier freq = pixel_clock * (dda1_inc + dda2_inc / dda2_size) / 4096 + * + * Presumably, when dda3 is added in, it gets to adjust the dda2_inc value + * + * So, + *  dda1_ideal = subcarrier/pixel * 4096 + *  dda1_inc = floor (dda1_ideal) + *  dda2 = dda1_ideal - dda1_inc + * + *  then pick a ratio for dda2 that gives the closest approximation. If + *  you can't get close enough, you can play with dda3 as well. This + *  seems likely to happen when dda2 is small as the jumps would be larger + * + * To invert this, + * + *  pixel_clock = subcarrier * 4096 / (dda1_inc + dda2_inc / dda2_size) + * + * The constants below were all computed using a 107.520MHz clock + */ +  +/** + * Register programming values for TV modes. + * + * These values account for -1s required. + */ + +const static struct tv_mode tv_modes[] = { +	{ +		.name		= "NTSC-M", +		.clock		= 107520,	 +		.refresh	= 29970, +		.oversample	= TV_OVERSAMPLE_8X, +		.component_only = 0, +		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ + +		.hsync_end	= 64,		    .hblank_end		= 124, +		.hblank_start	= 836,		    .htotal		= 857, + +		.progressive	= false,	    .trilevel_sync = false, + +		.vsync_start_f1	= 6,		    .vsync_start_f2	= 7, +		.vsync_len	= 6, + +		.veq_ena	= true,		    .veq_start_f1    	= 0, +		.veq_start_f2	= 1,		    .veq_len		= 18, + +		.vi_end_f1	= 20,		    .vi_end_f2		= 21, +		.nbr_end	= 240, + +		.burst_ena	= true, +		.hburst_start	= 72,		    .hburst_len		= 34, +		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240, +		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240, +		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,  +		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240, + +		/* desired 3.5800000 actual 3.5800000 clock 107.52 */ +		.dda1_inc	=    136, +		.dda2_inc	=   7624,	    .dda2_size		=  20013, +		.dda3_inc	=      0,	    .dda3_size		=      0, +		.sc_reset	= TV_SC_RESET_EVERY_4, +		.pal_burst	= false, + +		.composite_levels = &ntsc_m_levels_composite, +		.composite_color = &ntsc_m_csc_composite, +		.svideo_levels  = &ntsc_m_levels_svideo, +		.svideo_color = &ntsc_m_csc_svideo, + +		.filter_table = filter_table, +	}, +	{ +		.name		= "NTSC-443", +		.clock		= 107520,	 +		.refresh	= 29970, +		.oversample	= TV_OVERSAMPLE_8X, +		.component_only = 0, +		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */ +		.hsync_end	= 64,		    .hblank_end		= 124, +		.hblank_start	= 836,		    .htotal		= 857, + +		.progressive	= false,	    .trilevel_sync = false, + +		.vsync_start_f1 = 6,		    .vsync_start_f2	= 7, +		.vsync_len	= 6, + +		.veq_ena	= true,		    .veq_start_f1    	= 0, +		.veq_start_f2	= 1,		    .veq_len		= 18, + +		.vi_end_f1	= 20,		    .vi_end_f2		= 21, +		.nbr_end	= 240, + +		.burst_ena	= 8, +		.hburst_start	= 72,		    .hburst_len		= 34, +		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240, +		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240, +		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,  +		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240, + +		/* desired 4.4336180 actual 4.4336180 clock 107.52 */ +		.dda1_inc       =    168, +		.dda2_inc       =  18557,       .dda2_size      =  20625, +		.dda3_inc       =      0,       .dda3_size      =      0, +		.sc_reset   = TV_SC_RESET_EVERY_8, +		.pal_burst  = true, + +		.composite_levels = &ntsc_m_levels_composite, +		.composite_color = &ntsc_m_csc_composite, +		.svideo_levels  = &ntsc_m_levels_svideo, +		.svideo_color = &ntsc_m_csc_svideo, + +		.filter_table = filter_table, +	}, +	{ +		.name		= "NTSC-J", +		.clock		= 107520,	 +		.refresh	= 29970, +		.oversample	= TV_OVERSAMPLE_8X, +		.component_only = 0, + +		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ +		.hsync_end	= 64,		    .hblank_end		= 124, +		.hblank_start = 836,	    .htotal		= 857, + +		.progressive	= false,    .trilevel_sync = false, + +		.vsync_start_f1	= 6,	    .vsync_start_f2	= 7, +		.vsync_len	= 6, + +		.veq_ena	= true,		    .veq_start_f1    	= 0, +		.veq_start_f2 = 1,	    .veq_len		= 18, + +		.vi_end_f1	= 20,		    .vi_end_f2		= 21, +		.nbr_end	= 240, + +		.burst_ena	= true, +		.hburst_start	= 72,		    .hburst_len		= 34, +		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240, +		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240, +		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,  +		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240, + +		/* desired 3.5800000 actual 3.5800000 clock 107.52 */ +		.dda1_inc	=    136, +		.dda2_inc	=   7624,	    .dda2_size		=  20013, +		.dda3_inc	=      0,	    .dda3_size		=      0, +		.sc_reset	= TV_SC_RESET_EVERY_4, +		.pal_burst	= false, + +		.composite_levels = &ntsc_j_levels_composite, +		.composite_color = &ntsc_j_csc_composite, +		.svideo_levels  = &ntsc_j_levels_svideo, +		.svideo_color = &ntsc_j_csc_svideo, + +		.filter_table = filter_table, +	}, +	{ +		.name		= "PAL-M", +		.clock		= 107520,	 +		.refresh	= 29970, +		.oversample	= TV_OVERSAMPLE_8X, +		.component_only = 0, + +		/* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ +		.hsync_end	= 64,		  .hblank_end		= 124, +		.hblank_start = 836,	  .htotal		= 857, + +		.progressive	= false,	    .trilevel_sync = false, + +		.vsync_start_f1	= 6,		    .vsync_start_f2	= 7, +		.vsync_len	= 6, + +		.veq_ena	= true,		    .veq_start_f1    	= 0, +		.veq_start_f2	= 1,		    .veq_len		= 18, + +		.vi_end_f1	= 20,		    .vi_end_f2		= 21, +		.nbr_end	= 240, + +		.burst_ena	= true, +		.hburst_start	= 72,		    .hburst_len		= 34, +		.vburst_start_f1 = 9,		    .vburst_end_f1	= 240, +		.vburst_start_f2 = 10,		    .vburst_end_f2	= 240, +		.vburst_start_f3 = 9,		    .vburst_end_f3	= 240,  +		.vburst_start_f4 = 10,		    .vburst_end_f4	= 240, + +		/* desired 3.5800000 actual 3.5800000 clock 107.52 */ +		.dda1_inc	=    136, +		.dda2_inc	=    7624,	    .dda2_size		=  20013, +		.dda3_inc	=      0,	    .dda3_size		=      0, +		.sc_reset	= TV_SC_RESET_EVERY_4, +		.pal_burst  = false, + +		.composite_levels = &pal_m_levels_composite, +		.composite_color = &pal_m_csc_composite, +		.svideo_levels  = &pal_m_levels_svideo, +		.svideo_color = &pal_m_csc_svideo, + +		.filter_table = filter_table, +	}, +	{ +		/* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */ +		.name	    = "PAL-N", +		.clock		= 107520,	 +		.refresh	= 25000, +		.oversample	= TV_OVERSAMPLE_8X, +		.component_only = 0, + +		.hsync_end	= 64,		    .hblank_end		= 128, +		.hblank_start = 844,	    .htotal		= 863, + +		.progressive  = false,    .trilevel_sync = false, + + +		.vsync_start_f1	= 6,	   .vsync_start_f2	= 7, +		.vsync_len	= 6, + +		.veq_ena	= true,		    .veq_start_f1    	= 0, +		.veq_start_f2	= 1,		    .veq_len		= 18, + +		.vi_end_f1	= 24,		    .vi_end_f2		= 25, +		.nbr_end	= 286, + +		.burst_ena	= true, +		.hburst_start = 73,	    	    .hburst_len		= 34, +		.vburst_start_f1 = 8,	    .vburst_end_f1	= 285, +		.vburst_start_f2 = 8,	    .vburst_end_f2	= 286, +		.vburst_start_f3 = 9,	    .vburst_end_f3	= 286,  +		.vburst_start_f4 = 9,	    .vburst_end_f4	= 285, + + +		/* desired 4.4336180 actual 4.4336180 clock 107.52 */ +		.dda1_inc       =    168, +		.dda2_inc       =  18557,       .dda2_size      =  20625, +		.dda3_inc       =      0,       .dda3_size      =      0, +		.sc_reset   = TV_SC_RESET_EVERY_8, +		.pal_burst  = true, + +		.composite_levels = &pal_n_levels_composite, +		.composite_color = &pal_n_csc_composite, +		.svideo_levels  = &pal_n_levels_svideo, +		.svideo_color = &pal_n_csc_svideo, + +		.filter_table = filter_table, +	}, +	{ +		/* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */ +		.name	    = "PAL", +		.clock		= 107520,	 +		.refresh	= 25000, +		.oversample	= TV_OVERSAMPLE_8X, +		.component_only = 0, + +		.hsync_end	= 64,		    .hblank_end		= 128, +		.hblank_start	= 844,	    .htotal		= 863, + +		.progressive	= false,    .trilevel_sync = false, + +		.vsync_start_f1	= 5,	    .vsync_start_f2	= 6, +		.vsync_len	= 5, + +		.veq_ena	= true,		    .veq_start_f1    	= 0, +		.veq_start_f2	= 1,	    .veq_len		= 15, + +		.vi_end_f1	= 24,		    .vi_end_f2		= 25, +		.nbr_end	= 286, + +		.burst_ena	= true, +		.hburst_start	= 73,		    .hburst_len		= 32, +		.vburst_start_f1 = 8,		    .vburst_end_f1	= 285, +		.vburst_start_f2 = 8,		    .vburst_end_f2	= 286, +		.vburst_start_f3 = 9,		    .vburst_end_f3	= 286,  +		.vburst_start_f4 = 9,		    .vburst_end_f4	= 285, + +		/* desired 4.4336180 actual 4.4336180 clock 107.52 */ +		.dda1_inc       =    168, +		.dda2_inc       =  18557,       .dda2_size      =  20625, +		.dda3_inc       =      0,       .dda3_size      =      0, +		.sc_reset   = TV_SC_RESET_EVERY_8, +		.pal_burst  = true, + +		.composite_levels = &pal_levels_composite, +		.composite_color = &pal_csc_composite, +		.svideo_levels  = &pal_levels_svideo, +		.svideo_color = &pal_csc_svideo, + +		.filter_table = filter_table, +	}, +	{ +		.name       = "480p@59.94Hz", +		.clock 	= 107520,	 +		.refresh	= 59940, +		.oversample     = TV_OVERSAMPLE_4X, +		.component_only = 1, + +		.hsync_end      = 64,               .hblank_end         = 122, +		.hblank_start   = 842,              .htotal             = 857, + +		.progressive    = true,.trilevel_sync = false, + +		.vsync_start_f1 = 12,               .vsync_start_f2     = 12, +		.vsync_len      = 12, + +		.veq_ena        = false, + +		.vi_end_f1      = 44,               .vi_end_f2          = 44, +		.nbr_end        = 496, + +		.burst_ena      = false, + +		.filter_table = filter_table, +	}, +	{ +		.name       = "480p@60Hz", +		.clock 	= 107520,	 +		.refresh	= 60000, +		.oversample     = TV_OVERSAMPLE_4X, +		.component_only = 1, + +		.hsync_end      = 64,               .hblank_end         = 122, +		.hblank_start   = 842,              .htotal             = 856, + +		.progressive    = true,.trilevel_sync = false, + +		.vsync_start_f1 = 12,               .vsync_start_f2     = 12, +		.vsync_len      = 12, + +		.veq_ena        = false, + +		.vi_end_f1      = 44,               .vi_end_f2          = 44, +		.nbr_end        = 496, + +		.burst_ena      = false, + +		.filter_table = filter_table, +	}, +	{ +		.name       = "576p", +		.clock 	= 107520,	 +		.refresh	= 50000, +		.oversample     = TV_OVERSAMPLE_4X, +		.component_only = 1, + +		.hsync_end      = 64,               .hblank_end         = 139, +		.hblank_start   = 859,              .htotal             = 863, + +		.progressive    = true,		.trilevel_sync = false, + +		.vsync_start_f1 = 10,               .vsync_start_f2     = 10, +		.vsync_len      = 10, + +		.veq_ena        = false, + +		.vi_end_f1      = 48,               .vi_end_f2          = 48, +		.nbr_end        = 575, + +		.burst_ena      = false, + +		.filter_table = filter_table, +	}, +	{ +		.name       = "720p@60Hz", +		.clock		= 148800,	 +		.refresh	= 60000, +		.oversample     = TV_OVERSAMPLE_2X, +		.component_only = 1, + +		.hsync_end      = 80,               .hblank_end         = 300, +		.hblank_start   = 1580,             .htotal             = 1649, + +		.progressive    = true, 	    .trilevel_sync = true, + +		.vsync_start_f1 = 10,               .vsync_start_f2     = 10, +		.vsync_len      = 10, + +		.veq_ena        = false, + +		.vi_end_f1      = 29,               .vi_end_f2          = 29, +		.nbr_end        = 719, + +		.burst_ena      = false, + +		.filter_table = filter_table, +	}, +	{ +		.name       = "720p@59.94Hz", +		.clock		= 148800,	 +		.refresh	= 59940, +		.oversample     = TV_OVERSAMPLE_2X, +		.component_only = 1, + +		.hsync_end      = 80,               .hblank_end         = 300, +		.hblank_start   = 1580,             .htotal             = 1651, + +		.progressive    = true, 	    .trilevel_sync = true, + +		.vsync_start_f1 = 10,               .vsync_start_f2     = 10, +		.vsync_len      = 10, + +		.veq_ena        = false, + +		.vi_end_f1      = 29,               .vi_end_f2          = 29, +		.nbr_end        = 719, + +		.burst_ena      = false, + +		.filter_table = filter_table, +	}, +	{ +		.name       = "720p@50Hz", +		.clock		= 148800,	 +		.refresh	= 50000, +		.oversample     = TV_OVERSAMPLE_2X, +		.component_only = 1, + +		.hsync_end      = 80,               .hblank_end         = 300, +		.hblank_start   = 1580,             .htotal             = 1979, + +		.progressive    = true, 	        .trilevel_sync = true, + +		.vsync_start_f1 = 10,               .vsync_start_f2     = 10, +		.vsync_len      = 10, + +		.veq_ena        = false, + +		.vi_end_f1      = 29,               .vi_end_f2          = 29, +		.nbr_end        = 719, + +		.burst_ena      = false, + +		.filter_table = filter_table, +		.max_srcw = 800 +	}, +	{ +		.name       = "1080i@50Hz", +		.clock		= 148800,	 +		.refresh	= 25000, +		.oversample     = TV_OVERSAMPLE_2X, +		.component_only = 1, + +		.hsync_end      = 88,               .hblank_end         = 235, +		.hblank_start   = 2155,             .htotal             = 2639, + +		.progressive    = false, 	    .trilevel_sync = true, + +		.vsync_start_f1 = 4,              .vsync_start_f2     = 5, +		.vsync_len      = 10, + +		.veq_ena	= true,		    .veq_start_f1    	= 4, +		.veq_start_f2   = 4,	    .veq_len		= 10, + + +		.vi_end_f1      = 21,           .vi_end_f2          = 22, +		.nbr_end        = 539, + +		.burst_ena      = false, + +		.filter_table = filter_table, +	}, +	{ +		.name       = "1080i@60Hz", +		.clock		= 148800,	 +		.refresh	= 30000, +		.oversample     = TV_OVERSAMPLE_2X, +		.component_only = 1, + +		.hsync_end      = 88,               .hblank_end         = 235, +		.hblank_start   = 2155,             .htotal             = 2199, + +		.progressive    = false, 	    .trilevel_sync = true, + +		.vsync_start_f1 = 4,               .vsync_start_f2     = 5, +		.vsync_len      = 10, + +		.veq_ena	= true,		    .veq_start_f1    	= 4, +		.veq_start_f2	= 4,		    .veq_len		= 10, + + +		.vi_end_f1      = 21,               .vi_end_f2          = 22, +		.nbr_end        = 539, + +		.burst_ena      = false, + +		.filter_table = filter_table, +	}, +	{ +		.name       = "1080i@59.94Hz", +		.clock		= 148800,	 +		.refresh	= 29970, +		.oversample     = TV_OVERSAMPLE_2X, +		.component_only = 1, + +		.hsync_end      = 88,               .hblank_end         = 235, +		.hblank_start   = 2155,             .htotal             = 2200, + +		.progressive    = false, 	    .trilevel_sync = true, + +		.vsync_start_f1 = 4,            .vsync_start_f2    = 5, +		.vsync_len      = 10, + +		.veq_ena	= true,		    .veq_start_f1	= 4, +		.veq_start_f2 = 4,	    	    .veq_len = 10, + + +		.vi_end_f1      = 21,           .vi_end_f2         	= 22, +		.nbr_end        = 539, + +		.burst_ena      = false, + +		.filter_table = filter_table, +	}, +}; + +#define NUM_TV_MODES sizeof(tv_modes) / sizeof (tv_modes[0]) + +static void +intel_tv_dpms(struct drm_encoder *encoder, int mode) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; + +	switch(mode) { +	case DRM_MODE_DPMS_ON: +		I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE); +		break; +	case DRM_MODE_DPMS_STANDBY: +	case DRM_MODE_DPMS_SUSPEND: +	case DRM_MODE_DPMS_OFF: +		I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE); +		break; +	} +} + +static void +intel_tv_save(struct drm_connector *connector) +{ +	struct drm_device *dev = connector->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_output *intel_output = to_intel_output(connector); +	struct intel_tv_priv *tv_priv = intel_output->dev_priv; +	int i; + +	tv_priv->save_TV_H_CTL_1 = I915_READ(TV_H_CTL_1); +	tv_priv->save_TV_H_CTL_2 = I915_READ(TV_H_CTL_2); +	tv_priv->save_TV_H_CTL_3 = I915_READ(TV_H_CTL_3); +	tv_priv->save_TV_V_CTL_1 = I915_READ(TV_V_CTL_1); +	tv_priv->save_TV_V_CTL_2 = I915_READ(TV_V_CTL_2); +	tv_priv->save_TV_V_CTL_3 = I915_READ(TV_V_CTL_3); +	tv_priv->save_TV_V_CTL_4 = I915_READ(TV_V_CTL_4); +	tv_priv->save_TV_V_CTL_5 = I915_READ(TV_V_CTL_5); +	tv_priv->save_TV_V_CTL_6 = I915_READ(TV_V_CTL_6); +	tv_priv->save_TV_V_CTL_7 = I915_READ(TV_V_CTL_7); +	tv_priv->save_TV_SC_CTL_1 = I915_READ(TV_SC_CTL_1); +	tv_priv->save_TV_SC_CTL_2 = I915_READ(TV_SC_CTL_2); +	tv_priv->save_TV_SC_CTL_3 = I915_READ(TV_SC_CTL_3); + +	tv_priv->save_TV_CSC_Y = I915_READ(TV_CSC_Y); +	tv_priv->save_TV_CSC_Y2 = I915_READ(TV_CSC_Y2); +	tv_priv->save_TV_CSC_U = I915_READ(TV_CSC_U); +	tv_priv->save_TV_CSC_U2 = I915_READ(TV_CSC_U2); +	tv_priv->save_TV_CSC_V = I915_READ(TV_CSC_V); +	tv_priv->save_TV_CSC_V2 = I915_READ(TV_CSC_V2); +	tv_priv->save_TV_CLR_KNOBS = I915_READ(TV_CLR_KNOBS); +	tv_priv->save_TV_CLR_LEVEL = I915_READ(TV_CLR_LEVEL); +	tv_priv->save_TV_WIN_POS = I915_READ(TV_WIN_POS); +	tv_priv->save_TV_WIN_SIZE = I915_READ(TV_WIN_SIZE); +	tv_priv->save_TV_FILTER_CTL_1 = I915_READ(TV_FILTER_CTL_1); +	tv_priv->save_TV_FILTER_CTL_2 = I915_READ(TV_FILTER_CTL_2); +	tv_priv->save_TV_FILTER_CTL_3 = I915_READ(TV_FILTER_CTL_3); + +	for (i = 0; i < 60; i++) +		tv_priv->save_TV_H_LUMA[i] = I915_READ(TV_H_LUMA_0 + (i <<2)); +	for (i = 0; i < 60; i++) +		tv_priv->save_TV_H_CHROMA[i] = I915_READ(TV_H_CHROMA_0 + (i <<2)); +	for (i = 0; i < 43; i++) +		tv_priv->save_TV_V_LUMA[i] = I915_READ(TV_V_LUMA_0 + (i <<2)); +	for (i = 0; i < 43; i++) +		tv_priv->save_TV_V_CHROMA[i] = I915_READ(TV_V_CHROMA_0 + (i <<2)); + +	tv_priv->save_TV_DAC = I915_READ(TV_DAC); +	tv_priv->save_TV_CTL = I915_READ(TV_CTL); +} + +static void +intel_tv_restore(struct drm_connector *connector) +{ +	struct drm_device *dev = connector->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_output *intel_output = to_intel_output(connector); +	struct intel_tv_priv *tv_priv = intel_output->dev_priv; +	struct drm_crtc *crtc = connector->encoder->crtc; +	struct intel_crtc *intel_crtc; +	int i; + +	/* FIXME: No CRTC? */ +	if (!crtc) +		return; + +	intel_crtc = to_intel_crtc(crtc); +	I915_WRITE(TV_H_CTL_1, tv_priv->save_TV_H_CTL_1); +	I915_WRITE(TV_H_CTL_2, tv_priv->save_TV_H_CTL_2); +	I915_WRITE(TV_H_CTL_3, tv_priv->save_TV_H_CTL_3); +	I915_WRITE(TV_V_CTL_1, tv_priv->save_TV_V_CTL_1); +	I915_WRITE(TV_V_CTL_2, tv_priv->save_TV_V_CTL_2); +	I915_WRITE(TV_V_CTL_3, tv_priv->save_TV_V_CTL_3); +	I915_WRITE(TV_V_CTL_4, tv_priv->save_TV_V_CTL_4); +	I915_WRITE(TV_V_CTL_5, tv_priv->save_TV_V_CTL_5); +	I915_WRITE(TV_V_CTL_6, tv_priv->save_TV_V_CTL_6); +	I915_WRITE(TV_V_CTL_7, tv_priv->save_TV_V_CTL_7); +	I915_WRITE(TV_SC_CTL_1, tv_priv->save_TV_SC_CTL_1); +	I915_WRITE(TV_SC_CTL_2, tv_priv->save_TV_SC_CTL_2); +	I915_WRITE(TV_SC_CTL_3, tv_priv->save_TV_SC_CTL_3); + +	I915_WRITE(TV_CSC_Y, tv_priv->save_TV_CSC_Y); +	I915_WRITE(TV_CSC_Y2, tv_priv->save_TV_CSC_Y2); +	I915_WRITE(TV_CSC_U, tv_priv->save_TV_CSC_U); +	I915_WRITE(TV_CSC_U2, tv_priv->save_TV_CSC_U2); +	I915_WRITE(TV_CSC_V, tv_priv->save_TV_CSC_V); +	I915_WRITE(TV_CSC_V2, tv_priv->save_TV_CSC_V2); +	I915_WRITE(TV_CLR_KNOBS, tv_priv->save_TV_CLR_KNOBS); +	I915_WRITE(TV_CLR_LEVEL, tv_priv->save_TV_CLR_LEVEL); + +	{ +		int pipeconf_reg = (intel_crtc->pipe == 0) ? +			PIPEACONF : PIPEBCONF; +		int dspcntr_reg = (intel_crtc->plane == 0) ? +			DSPACNTR : DSPBCNTR; +		int pipeconf = I915_READ(pipeconf_reg); +		int dspcntr = I915_READ(dspcntr_reg); +		int dspbase_reg = (intel_crtc->plane == 0) ? +			DSPAADDR : DSPBADDR; +		/* Pipe must be off here */ +		I915_WRITE(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE); +		/* Flush the plane changes */ +		I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); + +		if (!IS_I9XX(dev)) { +			/* Wait for vblank for the disable to take effect */ +			intel_wait_for_vblank(dev); +		} + +		I915_WRITE(pipeconf_reg, pipeconf & ~PIPEACONF_ENABLE); +		/* Wait for vblank for the disable to take effect. */ +		intel_wait_for_vblank(dev); + +		/* Filter ctl must be set before TV_WIN_SIZE */ +		I915_WRITE(TV_FILTER_CTL_1, tv_priv->save_TV_FILTER_CTL_1); +		I915_WRITE(TV_FILTER_CTL_2, tv_priv->save_TV_FILTER_CTL_2); +		I915_WRITE(TV_FILTER_CTL_3, tv_priv->save_TV_FILTER_CTL_3); +		I915_WRITE(TV_WIN_POS, tv_priv->save_TV_WIN_POS); +		I915_WRITE(TV_WIN_SIZE, tv_priv->save_TV_WIN_SIZE); +		I915_WRITE(pipeconf_reg, pipeconf); +		I915_WRITE(dspcntr_reg, dspcntr); +		/* Flush the plane changes */ +		I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); +	} + +	for (i = 0; i < 60; i++) +		I915_WRITE(TV_H_LUMA_0 + (i <<2), tv_priv->save_TV_H_LUMA[i]); +	for (i = 0; i < 60; i++) +		I915_WRITE(TV_H_CHROMA_0 + (i <<2), tv_priv->save_TV_H_CHROMA[i]); +	for (i = 0; i < 43; i++) +		I915_WRITE(TV_V_LUMA_0 + (i <<2), tv_priv->save_TV_V_LUMA[i]); +	for (i = 0; i < 43; i++) +		I915_WRITE(TV_V_CHROMA_0 + (i <<2), tv_priv->save_TV_V_CHROMA[i]); + +	I915_WRITE(TV_DAC, tv_priv->save_TV_DAC); +	I915_WRITE(TV_CTL, tv_priv->save_TV_CTL); +} + +static const struct tv_mode * +intel_tv_mode_lookup (char *tv_format) +{ +	int i; +     +	for (i = 0; i < sizeof(tv_modes) / sizeof (tv_modes[0]); i++) { +		const struct tv_mode *tv_mode = &tv_modes[i]; + +		if (!strcmp(tv_format, tv_mode->name)) +			return tv_mode; +	} +	return NULL; +} + +static const struct tv_mode * +intel_tv_mode_find (struct intel_output *intel_output) +{ +	struct intel_tv_priv *tv_priv = intel_output->dev_priv; + +	return intel_tv_mode_lookup(tv_priv->tv_format); +} + +static enum drm_mode_status +intel_tv_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) +{ +	struct intel_output *intel_output = to_intel_output(connector); +	const struct tv_mode *tv_mode = intel_tv_mode_find(intel_output); + +	/* Ensure TV refresh is close to desired refresh */ +	if (tv_mode && abs(tv_mode->refresh - drm_mode_vrefresh(mode)) < 1) +		return MODE_OK; +	return MODE_CLOCK_RANGE; +} + + +static bool +intel_tv_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, +		    struct drm_display_mode *adjusted_mode) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_mode_config *drm_config = &dev->mode_config; +	struct intel_output *intel_output = enc_to_intel_output(encoder); +	const struct tv_mode *tv_mode = intel_tv_mode_find (intel_output); +	struct drm_encoder *other_encoder; + +	if (!tv_mode) +		return false; +     +	/* FIXME: lock encoder list */ +	list_for_each_entry(other_encoder, &drm_config->encoder_list, head) { +		if (other_encoder != encoder && +		    other_encoder->crtc == encoder->crtc) +			return false; +	} + +	adjusted_mode->clock = tv_mode->clock; +	return true; +} + +static void +intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, +		  struct drm_display_mode *adjusted_mode) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_crtc *crtc = encoder->crtc; +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +	struct intel_output *intel_output = enc_to_intel_output(encoder); +	struct intel_tv_priv *tv_priv = intel_output->dev_priv; +	const struct tv_mode *tv_mode = intel_tv_mode_find(intel_output); +	u32 tv_ctl; +	u32 hctl1, hctl2, hctl3; +	u32 vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7; +	u32 scctl1, scctl2, scctl3; +	int i, j; +	const struct video_levels *video_levels; +	const struct color_conversion *color_conversion; +	bool burst_ena; +     +	if (!tv_mode) +		return;	/* can't happen (mode_prepare prevents this) */ +     +	tv_ctl = 0; + +	switch (tv_priv->type) { +	default: +	case DRM_MODE_CONNECTOR_Unknown: +	case DRM_MODE_CONNECTOR_Composite: +		tv_ctl |= TV_ENC_OUTPUT_COMPOSITE; +		video_levels = tv_mode->composite_levels; +		color_conversion = tv_mode->composite_color; +		burst_ena = tv_mode->burst_ena; +		break; +	case DRM_MODE_CONNECTOR_Component: +		tv_ctl |= TV_ENC_OUTPUT_COMPONENT; +		video_levels = &component_levels; +		if (tv_mode->burst_ena) +			color_conversion = &sdtv_csc_yprpb; +		else +			color_conversion = &hdtv_csc_yprpb; +		burst_ena = false; +		break; +	case DRM_MODE_CONNECTOR_SVIDEO: +		tv_ctl |= TV_ENC_OUTPUT_SVIDEO; +		video_levels = tv_mode->svideo_levels; +		color_conversion = tv_mode->svideo_color; +		burst_ena = tv_mode->burst_ena; +		break; +	} +	hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) | +		(tv_mode->htotal << TV_HTOTAL_SHIFT); + +	hctl2 = (tv_mode->hburst_start << 16) | +		(tv_mode->hburst_len << TV_HBURST_LEN_SHIFT); + +	if (burst_ena) +		hctl2 |= TV_BURST_ENA; + +	hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) | +		(tv_mode->hblank_end << TV_HBLANK_END_SHIFT); + +	vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) | +		(tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) | +		(tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT); + +	vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) | +		(tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) | +		(tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT); + +	vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) | +		(tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) | +		(tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT); + +	if (tv_mode->veq_ena) +		vctl3 |= TV_EQUAL_ENA; + +	vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) | +		(tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT); + +	vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) | +		(tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT); + +	vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) | +		(tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT); + +	vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) | +		(tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT); + +	if (intel_crtc->pipe == 1) +		tv_ctl |= TV_ENC_PIPEB_SELECT; +	tv_ctl |= tv_mode->oversample; + +	if (tv_mode->progressive) +		tv_ctl |= TV_PROGRESSIVE; +	if (tv_mode->trilevel_sync) +		tv_ctl |= TV_TRILEVEL_SYNC; +	if (tv_mode->pal_burst) +		tv_ctl |= TV_PAL_BURST; +	scctl1 = 0; +	/* dda1 implies valid video levels */ +	if (tv_mode->dda1_inc) { +		scctl1 |= TV_SC_DDA1_EN; +		scctl1 |= video_levels->burst << TV_BURST_LEVEL_SHIFT; +	} + +	if (tv_mode->dda2_inc) +		scctl1 |= TV_SC_DDA2_EN; + +	if (tv_mode->dda3_inc) +		scctl1 |= TV_SC_DDA3_EN; + +	scctl1 |= tv_mode->sc_reset; +	scctl1 |= tv_mode->dda1_inc << TV_SCDDA1_INC_SHIFT; + +	scctl2 = tv_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT | +		tv_mode->dda2_inc << TV_SCDDA2_INC_SHIFT; + +	scctl3 = tv_mode->dda3_size << TV_SCDDA3_SIZE_SHIFT | +		tv_mode->dda3_inc << TV_SCDDA3_INC_SHIFT; + +	/* Enable two fixes for the chips that need them. */ +	if (dev->pci_device < 0x2772) +		tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX; + +	I915_WRITE(TV_H_CTL_1, hctl1); +	I915_WRITE(TV_H_CTL_2, hctl2); +	I915_WRITE(TV_H_CTL_3, hctl3); +	I915_WRITE(TV_V_CTL_1, vctl1); +	I915_WRITE(TV_V_CTL_2, vctl2); +	I915_WRITE(TV_V_CTL_3, vctl3); +	I915_WRITE(TV_V_CTL_4, vctl4); +	I915_WRITE(TV_V_CTL_5, vctl5); +	I915_WRITE(TV_V_CTL_6, vctl6); +	I915_WRITE(TV_V_CTL_7, vctl7); +	I915_WRITE(TV_SC_CTL_1, scctl1); +	I915_WRITE(TV_SC_CTL_2, scctl2); +	I915_WRITE(TV_SC_CTL_3, scctl3); + +	if (color_conversion) { +		I915_WRITE(TV_CSC_Y, (color_conversion->ry << 16) | +			   color_conversion->gy); +		I915_WRITE(TV_CSC_Y2,(color_conversion->by << 16) | +			   color_conversion->ay); +		I915_WRITE(TV_CSC_U, (color_conversion->ru << 16) | +			   color_conversion->gu); +		I915_WRITE(TV_CSC_U2, (color_conversion->bu << 16) | +			   color_conversion->au); +		I915_WRITE(TV_CSC_V, (color_conversion->rv << 16) | +			   color_conversion->gv); +		I915_WRITE(TV_CSC_V2, (color_conversion->bv << 16) | +			   color_conversion->av); +	} + +	I915_WRITE(TV_CLR_KNOBS, 0x00606000); +	if (video_levels) +		I915_WRITE(TV_CLR_LEVEL, +			   ((video_levels->black << TV_BLACK_LEVEL_SHIFT) | +			    (video_levels->blank << TV_BLANK_LEVEL_SHIFT))); +	{ +		int pipeconf_reg = (intel_crtc->pipe == 0) ? +			PIPEACONF : PIPEBCONF; +		int dspcntr_reg = (intel_crtc->plane == 0) ? +			DSPACNTR : DSPBCNTR; +		int pipeconf = I915_READ(pipeconf_reg); +		int dspcntr = I915_READ(dspcntr_reg); +		int dspbase_reg = (intel_crtc->plane == 0) ? +			DSPAADDR : DSPBADDR; +		int xpos = 0x0, ypos = 0x0; +		unsigned int xsize, ysize; +		/* Pipe must be off here */ +		I915_WRITE(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE); +		/* Flush the plane changes */ +		I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); + +		/* Wait for vblank for the disable to take effect */ +		if (!IS_I9XX(dev)) +			intel_wait_for_vblank(dev); + +		I915_WRITE(pipeconf_reg, pipeconf & ~PIPEACONF_ENABLE); +		/* Wait for vblank for the disable to take effect. */ +		intel_wait_for_vblank(dev); + +		/* Filter ctl must be set before TV_WIN_SIZE */ +		I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE);  +		xsize = tv_mode->hblank_start - tv_mode->hblank_end; +		if (tv_mode->progressive) +			ysize = tv_mode->nbr_end + 1; +		else +			ysize = 2*tv_mode->nbr_end + 1; + +		xpos += tv_priv->margin[TV_MARGIN_LEFT]; +		ypos += tv_priv->margin[TV_MARGIN_TOP]; +		xsize -= (tv_priv->margin[TV_MARGIN_LEFT] +  +			  tv_priv->margin[TV_MARGIN_RIGHT]); +		ysize -= (tv_priv->margin[TV_MARGIN_TOP] +  +			  tv_priv->margin[TV_MARGIN_BOTTOM]); +		I915_WRITE(TV_WIN_POS, (xpos<<16)|ypos); +		I915_WRITE(TV_WIN_SIZE, (xsize<<16)|ysize); + +		I915_WRITE(pipeconf_reg, pipeconf); +		I915_WRITE(dspcntr_reg, dspcntr); +		/* Flush the plane changes */ +		I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); +	} 	 + +	j = 0; +	for (i = 0; i < 60; i++) +		I915_WRITE(TV_H_LUMA_0 + (i<<2), tv_mode->filter_table[j++]); +	for (i = 0; i < 60; i++) +		I915_WRITE(TV_H_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]); +	for (i = 0; i < 43; i++) +		I915_WRITE(TV_V_LUMA_0 + (i<<2), tv_mode->filter_table[j++]); +	for (i = 0; i < 43; i++) +		I915_WRITE(TV_V_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]); +	I915_WRITE(TV_DAC, 0); +	I915_WRITE(TV_CTL, tv_ctl); +} + +static const struct drm_display_mode reported_modes[] = { +	{ +		.name = "NTSC 480i", +		.clock = 107520, +		.hdisplay = 1280, +		.hsync_start = 1368, +		.hsync_end = 1496, +		.htotal = 1712, + +		.vdisplay = 1024, +		.vsync_start = 1027, +		.vsync_end = 1034, +		.vtotal = 1104, +		.type = DRM_MODE_TYPE_DRIVER, +	}, +}; + +/** + * Detects TV presence by checking for load. + * + * Requires that the current pipe's DPLL is active. + + * \return true if TV is connected. + * \return false if TV is disconnected. + */ +static int +intel_tv_detect_type (struct drm_crtc *crtc, struct intel_output *intel_output) +{ +	struct drm_encoder *encoder = &intel_output->enc; +	struct drm_device *dev = encoder->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	u32 pipeastat, pipeastat_save; +	u32 tv_ctl, save_tv_ctl; +	u32 tv_dac, save_tv_dac; +	int type = DRM_MODE_CONNECTOR_Unknown; + +	tv_dac = I915_READ(TV_DAC); + +	/* Disable TV interrupts around load detect or we'll recurse */ +	pipeastat = I915_READ(PIPEASTAT); +	pipeastat_save = pipeastat; +	pipeastat &= ~PIPE_HOTPLUG_INTERRUPT_ENABLE; +	pipeastat &= ~PIPE_HOTPLUG_TV_INTERRUPT_ENABLE; +	I915_WRITE(PIPEASTAT, pipeastat | PIPE_HOTPLUG_TV_INTERRUPT_STATUS | +		   PIPE_HOTPLUG_INTERRUPT_STATUS); + +	/* +	 * Detect TV by polling) +	 */ +	if (intel_output->load_detect_temp) { +		/* TV not currently running, prod it with destructive detect */ +		save_tv_dac = tv_dac; +		tv_ctl = I915_READ(TV_CTL); +		save_tv_ctl = tv_ctl; +		tv_ctl &= ~TV_ENC_ENABLE; +		tv_ctl &= ~TV_TEST_MODE_MASK; +		tv_ctl |= TV_TEST_MODE_MONITOR_DETECT; +		tv_dac &= ~TVDAC_SENSE_MASK; +		tv_dac |= (TVDAC_STATE_CHG_EN | +			   TVDAC_A_SENSE_CTL | +			   TVDAC_B_SENSE_CTL | +			   TVDAC_C_SENSE_CTL | +			   DAC_CTL_OVERRIDE | +			   DAC_A_0_7_V | +			   DAC_B_0_7_V | +			   DAC_C_0_7_V); +		I915_WRITE(TV_CTL, tv_ctl); +		I915_WRITE(TV_DAC, tv_dac); +		intel_wait_for_vblank(dev); +		tv_dac = I915_READ(TV_DAC); +		I915_WRITE(TV_DAC, save_tv_dac); +		I915_WRITE(TV_CTL, save_tv_ctl); +	} +	/* +	 *  A B C +	 *  0 1 1 Composite +	 *  1 0 X svideo +	 *  0 0 0 Component +	 */ +	if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) { +		DRM_DEBUG("Detected Composite TV connection\n"); +		type = DRM_MODE_CONNECTOR_Composite; +	} else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) { +		DRM_DEBUG("Detected S-Video TV connection\n"); +		type = DRM_MODE_CONNECTOR_SVIDEO; +	} else if ((tv_dac & TVDAC_SENSE_MASK) == 0) { +		DRM_DEBUG("Detected Component TV connection\n"); +		type = DRM_MODE_CONNECTOR_Component; +	} else { +		DRM_DEBUG("No TV connection detected\n"); +		type = -1; +	} + +	/* Restore interrupt config */ +	I915_WRITE(PIPEASTAT, pipeastat_save | PIPE_HOTPLUG_TV_INTERRUPT_STATUS | +		   PIPE_HOTPLUG_INTERRUPT_STATUS); + +	return type; +} + +/** + * Detect the TV connection. + * + * Currently this always returns CONNECTOR_STATUS_UNKNOWN, as we need to be sure + * we have a pipe programmed in order to probe the TV. + */ +static enum drm_connector_status +intel_tv_detect(struct drm_connector *connector) +{ +	struct drm_crtc *crtc; +	struct drm_display_mode mode; +	struct intel_output *intel_output = to_intel_output(connector); +	struct intel_tv_priv *tv_priv = intel_output->dev_priv; +	struct drm_encoder *encoder = &intel_output->enc; +	int dpms_mode; +	int type = tv_priv->type; + +	mode = reported_modes[0]; +	drm_mode_set_crtcinfo(&mode, CRTC_INTERLACE_HALVE_V); + +	if (encoder->crtc) { +		type = intel_tv_detect_type(encoder->crtc, intel_output); +	} else { +		crtc = intel_get_load_detect_pipe(intel_output, &mode, &dpms_mode); +		if (crtc) { +			type = intel_tv_detect_type(crtc, intel_output); +			intel_release_load_detect_pipe(intel_output, dpms_mode); +		} else +			type = -1; +	} + +#if 0 +	if (type != tv_priv->type) { +		struct drm_property *connector_property = +			connector->dev->mode_config.connector_type_property; + +		tv_priv->type = type; +		drm_connector_property_set_value(connector, connector_property, +					      type); +	} +#endif +	if (type < 0) +		return connector_status_disconnected; + +	return connector_status_connected; +} + +static struct input_res { +	char *name; +	int w, h;	 +} input_res_table[] =  +{ +	{"640x480", 640, 480}, +	{"800x600", 800, 600}, +	{"1024x768", 1024, 768}, +	{"1280x1024", 1280, 1024}, +	{"848x480", 848, 480}, +	{"1280x720", 1280, 720}, +	{"1920x1080", 1920, 1080}, +}; + +/** + * Stub get_modes function. + * + * This should probably return a set of fixed modes, unless we can figure out + * how to probe modes off of TV connections. + */ + +static int +intel_tv_get_modes(struct drm_connector *connector) +{ +	struct drm_display_mode *mode_ptr; +	struct intel_output *intel_output = to_intel_output(connector); +	const struct tv_mode *tv_mode = intel_tv_mode_find(intel_output); +	int j; + +	for (j = 0; j < sizeof(input_res_table) / sizeof(input_res_table[0]); +	     j++) { +		struct input_res *input = &input_res_table[j]; +		unsigned int hactive_s = input->w; +		unsigned int vactive_s = input->h; +	 +		if (tv_mode->max_srcw && input->w > tv_mode->max_srcw) +			continue; + +		if (input->w > 1024 && (!tv_mode->progressive  +					&& !tv_mode->component_only)) +			continue; + +		mode_ptr = drm_calloc(1, sizeof(struct drm_display_mode), +				      DRM_MEM_DRIVER); +		strncpy(mode_ptr->name, input->name, DRM_DISPLAY_MODE_LEN); + +		mode_ptr->hdisplay = hactive_s; +		mode_ptr->hsync_start = hactive_s + 1; +		mode_ptr->hsync_end = hactive_s + 64; +		if (mode_ptr->hsync_end <= mode_ptr->hsync_start) +			mode_ptr->hsync_end = mode_ptr->hsync_start + 1; +		mode_ptr->htotal = hactive_s + 96; + +		mode_ptr->vdisplay = vactive_s; +		mode_ptr->vsync_start = vactive_s + 1; +		mode_ptr->vsync_end = vactive_s + 32; +		if (mode_ptr->vsync_end <= mode_ptr->vsync_start) +			mode_ptr->vsync_end = mode_ptr->vsync_start  + 1; +		mode_ptr->vtotal = vactive_s + 33; + +		mode_ptr->clock = (int) (tv_mode->refresh *  +					 mode_ptr->vtotal *  +					 mode_ptr->htotal / 1000) / 1000; +	 +		mode_ptr->type = DRM_MODE_TYPE_DRIVER; +		drm_mode_probed_add(connector, mode_ptr); +	}  + +	return 0; +} + +static void +intel_tv_destroy (struct drm_connector *connector) +{ +	struct intel_output *intel_output = to_intel_output(connector); + +	drm_sysfs_connector_remove(connector); +	drm_connector_cleanup(connector); +	drm_free(intel_output, sizeof(struct intel_output) + sizeof(struct intel_tv_priv), +		 DRM_MEM_DRIVER); +} + + +static int +intel_tv_set_property(struct drm_connector *connector, struct drm_property *property, +		      uint64_t val) +{ +	struct drm_device *dev = connector->dev; +	struct intel_output *intel_output = to_intel_output(connector); +	struct intel_tv_priv *tv_priv = intel_output->dev_priv; +	int ret = 0; + +	ret = drm_connector_property_set_value(connector, property, val); +	if (ret < 0) +		goto out; + +	if (property == dev->mode_config.tv_left_margin_property) +		tv_priv->margin[TV_MARGIN_LEFT] = val; +	else if (property == dev->mode_config.tv_right_margin_property) +		tv_priv->margin[TV_MARGIN_RIGHT] = val; +	else if (property == dev->mode_config.tv_top_margin_property) +		tv_priv->margin[TV_MARGIN_TOP] = val; +	else if (property == dev->mode_config.tv_bottom_margin_property) +		tv_priv->margin[TV_MARGIN_BOTTOM] = val; +	else if (property == dev->mode_config.tv_mode_property) { +		if (val >= NUM_TV_MODES) { +			ret = -EINVAL; +			goto out; +		} +		tv_priv->tv_format = tv_modes[val].name; +		intel_tv_mode_set(&intel_output->enc, NULL, NULL); +	} else { +		ret = -EINVAL; +		goto out; +	} + +	intel_tv_mode_set(&intel_output->enc, NULL, NULL); +out: +	return ret; +} + +static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = { +	.dpms = intel_tv_dpms, +	.mode_fixup = intel_tv_mode_fixup, +	.prepare = intel_encoder_prepare, +	.mode_set = intel_tv_mode_set, +	.commit = intel_encoder_commit, +}; + +static const struct drm_connector_funcs intel_tv_connector_funcs = { +	.save = intel_tv_save, +	.restore = intel_tv_restore, +	.detect = intel_tv_detect, +	.destroy = intel_tv_destroy, +	.set_property = intel_tv_set_property, +	.fill_modes = drm_helper_probe_single_connector_modes, +}; + +static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = { +	.mode_valid = intel_tv_mode_valid, +	.get_modes = intel_tv_get_modes, +	.best_encoder = intel_best_encoder, +}; + +void intel_tv_enc_destroy(struct drm_encoder *encoder) +{ +	drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs intel_tv_enc_funcs = { +	.destroy = intel_tv_enc_destroy, +}; + + +void +intel_tv_init(struct drm_device *dev) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct drm_connector *connector; +	struct intel_output *intel_output; +	struct intel_tv_priv *tv_priv; +	u32 tv_dac_on, tv_dac_off, save_tv_dac; +	char **tv_format_names; +	int i, initial_mode = 0; + +	if ((I915_READ(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED) +		return; + +	/* Even if we have an encoder we may not have a connector */ +	if (!dev_priv->int_tv_support) +		return; + +	/* +	 * Sanity check the TV output by checking to see if the +	 * DAC register holds a value +	 */ +	save_tv_dac = I915_READ(TV_DAC); + +	I915_WRITE(TV_DAC, save_tv_dac | TVDAC_STATE_CHG_EN); +	tv_dac_on = I915_READ(TV_DAC); + +	I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN); +	tv_dac_off = I915_READ(TV_DAC); + +	I915_WRITE(TV_DAC, save_tv_dac); + +	/* +	 * If the register does not hold the state change enable +	 * bit, (either as a 0 or a 1), assume it doesn't really +	 * exist +	 */ +	if ((tv_dac_on & TVDAC_STATE_CHG_EN) == 0 ||  +	    (tv_dac_off & TVDAC_STATE_CHG_EN) != 0) +		return; + +	intel_output = drm_calloc(1, sizeof(struct intel_output) + +				  sizeof(struct intel_tv_priv), DRM_MEM_DRIVER); +	if (!intel_output) { +		return; +	} +	connector = &intel_output->base; + +	drm_connector_init(dev, connector, &intel_tv_connector_funcs, +			   DRM_MODE_CONNECTOR_SVIDEO); + +	drm_encoder_init(dev, &intel_output->enc, &intel_tv_enc_funcs, +			 DRM_MODE_ENCODER_TVDAC); + +	drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc); +	tv_priv = (struct intel_tv_priv *)(intel_output + 1); +	intel_output->type = INTEL_OUTPUT_TVOUT; +	intel_output->enc.possible_crtcs = ((1 << 0) | (1 << 1)); +	intel_output->enc.possible_clones = (1 << INTEL_OUTPUT_TVOUT); +	intel_output->dev_priv = tv_priv; +	tv_priv->type = DRM_MODE_CONNECTOR_Unknown; + +	/* BIOS margin values */ +	tv_priv->margin[TV_MARGIN_LEFT] = 54; +	tv_priv->margin[TV_MARGIN_TOP] = 36; +	tv_priv->margin[TV_MARGIN_RIGHT] = 46; +	tv_priv->margin[TV_MARGIN_BOTTOM] = 37; +     +	tv_priv->tv_format = kstrdup(tv_modes[initial_mode].name, GFP_KERNEL); +     +	drm_encoder_helper_add(&intel_output->enc, &intel_tv_helper_funcs); +	drm_connector_helper_add(connector, &intel_tv_connector_helper_funcs); +	connector->interlace_allowed = false; +	connector->doublescan_allowed = false; + +	/* Create TV properties then attach current values */ +	tv_format_names = drm_alloc(sizeof(char *) * NUM_TV_MODES, +				    DRM_MEM_DRIVER); +	if (!tv_format_names) +		goto out; +	for (i = 0; i < NUM_TV_MODES; i++) +		tv_format_names[i] = tv_modes[i].name; +	drm_mode_create_tv_properties(dev, NUM_TV_MODES, tv_format_names); + +	drm_connector_attach_property(connector, dev->mode_config.tv_mode_property, +				   initial_mode); +	drm_connector_attach_property(connector, +				   dev->mode_config.tv_left_margin_property, +				   tv_priv->margin[TV_MARGIN_LEFT]); +	drm_connector_attach_property(connector, +				   dev->mode_config.tv_top_margin_property, +				   tv_priv->margin[TV_MARGIN_TOP]); +	drm_connector_attach_property(connector, +				   dev->mode_config.tv_right_margin_property, +				   tv_priv->margin[TV_MARGIN_RIGHT]); +	drm_connector_attach_property(connector, +				   dev->mode_config.tv_bottom_margin_property, +				   tv_priv->margin[TV_MARGIN_BOTTOM]); +out: +	drm_sysfs_connector_add(connector); +} diff --git a/linux-core/nouveau_bios.c b/linux-core/nouveau_bios.c new file mode 100644 index 00000000..faa2b2b0 --- /dev/null +++ b/linux-core/nouveau_bios.c @@ -0,0 +1,854 @@ +/* + * Copyright (C) 2005-2006 Erik Waling + * Copyright (C) 2006 Stephane Marchesin + * Copyright (C) 2007-2008 Stuart Bennett + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 <asm/byteorder.h> +#include "nouveau_bios.h" +#include "nouveau_drv.h" + +/* returns true if it mismatches */ +static bool nv_checksum(const uint8_t *data, unsigned int length) +{ +	/* there's a few checksums in the BIOS, so here's a generic checking function */ +	int i; +	uint8_t sum = 0; + +	for (i = 0; i < length; i++) +		sum += data[i]; + +	if (sum) +		return true; + +	return false; +} + +static int nv_valid_bios(struct drm_device *dev, uint8_t *data) +{ +	/* check for BIOS signature */ +	if (!(data[0] == 0x55 && data[1] == 0xAA)) { +		DRM_ERROR("BIOS signature not found.\n"); +		return 0; +	} + +	if (nv_checksum(data, data[2] * 512)) { +		DRM_ERROR("BIOS checksum invalid.\n"); +		return 1; +	} + +	return 2; +} + +static void nv_shadow_bios_rom(struct drm_device *dev, uint8_t *data) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	int i; + +	/* enable access to rom */ +	NV_WRITE(NV04_PBUS_PCI_NV_20, NV04_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED); + +	/* This is also valid for pre-NV50, it just happened to be the only define already present. */ +	for (i=0; i < NV50_PROM__ESIZE; i++) { +		/* Appearantly needed for a 6600GT/6800LE bug. */ +		data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i); +		data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i); +		data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i); +		data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i); +		data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i); +	} + +	/* disable access to rom */ +	NV_WRITE(NV04_PBUS_PCI_NV_20, NV04_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED); +} + +static void nv_shadow_bios_ramin(struct drm_device *dev, uint8_t *data) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	uint32_t old_bar0_pramin = 0; +	int i; + +	/* Move the bios copy to the start of ramin? */ +	if (dev_priv->card_type >= NV_50) { +		uint32_t vbios_vram = (NV_READ(0x619f04) & ~0xff) << 8; + +		if (!vbios_vram) +			vbios_vram = (NV_READ(0x1700) << 16) + 0xf0000; + +		old_bar0_pramin = NV_READ(0x1700); +		NV_WRITE(0x1700, vbios_vram >> 16); +	} + +	for (i=0; i < NV50_PROM__ESIZE; i++) +		data[i] = DRM_READ8(dev_priv->mmio, NV04_PRAMIN + i); + +	if (dev_priv->card_type >= NV_50) +		NV_WRITE(0x1700, old_bar0_pramin); +} + +static bool nv_shadow_bios(struct drm_device *dev, uint8_t *data) +{ +	nv_shadow_bios_rom(dev, data); +	if (nv_valid_bios(dev, data) == 2) +		return true; + +	nv_shadow_bios_ramin(dev, data); +	if (nv_valid_bios(dev, data)) +		return true; + +	return false; +} + +struct bit_entry { +	uint8_t id[2]; +	uint16_t length; +	uint16_t offset; +}; + +static int parse_bit_A_tbl_entry(struct drm_device *dev, struct bios *bios, struct bit_entry *bitentry) +{ +	/* Parses the load detect value table. +	 * +	 * Starting at bitentry->offset: +	 * +	 * offset + 0 (16 bits): table pointer +	 */ + +	uint16_t load_table_pointer; + +	if (bitentry->length != 3) { +		DRM_ERROR("Do not understand BIT loadval table\n"); +		return 0; +	} + +	load_table_pointer = le16_to_cpu(*((uint16_t *)(&bios->data[bitentry->offset]))); + +	if (load_table_pointer == 0x0) { +		DRM_ERROR("Pointer to loadval table invalid\n"); +		return 0; +	} + +	/* Some kind of signature */ +	if (bios->data[load_table_pointer] != 16 || bios->data[load_table_pointer + 1] != 4 ||  +		bios->data[load_table_pointer + 2] != 4 || bios->data[load_table_pointer + 3] != 2) +		return 0; + +	bios->dactestval = le32_to_cpu(*((uint32_t *)&bios->data[load_table_pointer + 4])) & 0x3FF; + +	return 1; +} + +static int parse_bit_C_tbl_entry(struct drm_device *dev, struct bios *bios, struct bit_entry *bitentry) +{ +	/* offset + 8  (16 bits): PLL limits table pointer +	 * +	 * There's more in here, but that's unknown. +	 */ + +	if (bitentry->length < 10) { +		DRM_ERROR("Do not understand BIT C table\n"); +		return 0; +	} + +	bios->pll_limit_tbl_ptr = le16_to_cpu(*((uint16_t *)(&bios->data[bitentry->offset + 8]))); + +	return 1; +} + +static void parse_bit_structure(struct drm_device *dev, struct bios *bios, const uint16_t bitoffset) +{ +	int entries = bios->data[bitoffset + 4]; +	/* parse i first, I next (which needs C & M before it), and L before D */ +	char parseorder[] = "iCMILDTA"; +	struct bit_entry bitentry; +	int i, j, offset; + +	for (i = 0; i < sizeof(parseorder); i++) { +		for (j = 0, offset = bitoffset + 6; j < entries; j++, offset += 6) { +			bitentry.id[0] = bios->data[offset]; +			bitentry.id[1] = bios->data[offset + 1]; +			bitentry.length = le16_to_cpu(*((uint16_t *)&bios->data[offset + 2])); +			bitentry.offset = le16_to_cpu(*((uint16_t *)&bios->data[offset + 4])); + +			if (bitentry.id[0] != parseorder[i]) +				continue; + +			switch (bitentry.id[0]) { +			case 'A': +				parse_bit_A_tbl_entry(dev, bios, &bitentry); +				break; +			case 'C': +				parse_bit_C_tbl_entry(dev, bios, &bitentry); +				break; +			} +		} +	} +} + +static uint16_t findstr(uint8_t *data, int n, const uint8_t *str, int len) +{ +	int i, j; + +	for (i = 0; i <= (n - len); i++) { +		for (j = 0; j < len; j++) +			if (data[i + j] != str[j]) +				break; +		if (j == len) +			return i; +	} + +	return 0; +} + +static void +read_dcb_i2c_entry(struct drm_device *dev, uint8_t dcb_version, uint16_t i2ctabptr, int index) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct bios *bios = &dev_priv->bios; +	uint8_t *i2ctable = &bios->data[i2ctabptr]; +	uint8_t headerlen = 0; +	int i2c_entries = MAX_NUM_DCB_ENTRIES; +	int recordoffset = 0, rdofs = 1, wrofs = 0; + +	if (!i2ctabptr) +		return; + +	if (dcb_version >= 0x30) { +		if (i2ctable[0] != dcb_version) /* necessary? */ +			DRM_ERROR( +				   "DCB I2C table version mismatch (%02X vs %02X)\n", +				   i2ctable[0], dcb_version); +		headerlen = i2ctable[1]; +		i2c_entries = i2ctable[2]; + +		/* same address offset used for read and write for C51 and G80 */ +		if (bios->chip_version == 0x51) +			rdofs = wrofs = 1; +		if (i2ctable[0] >= 0x40) +			rdofs = wrofs = 0; +	} +	/* it's your own fault if you call this function on a DCB 1.1 BIOS -- +	 * the test below is for DCB 1.2 +	 */ +	if (dcb_version < 0x14) { +		recordoffset = 2; +		rdofs = 0; +		wrofs = 1; +	} + +	if (index == 0xf) +		return; +	if (index > i2c_entries) { +		DRM_ERROR( +			   "DCB I2C index too big (%d > %d)\n", +			   index, i2ctable[2]); +		return; +	} +	if (i2ctable[headerlen + 4 * index + 3] == 0xff) { +		DRM_ERROR( +			   "DCB I2C entry invalid\n"); +		return; +	} + +	if (bios->chip_version == 0x51) { +		int port_type = i2ctable[headerlen + 4 * index + 3]; + +		if (port_type != 4) +			DRM_ERROR( +				   "DCB I2C table has port type %d\n", port_type); +	} +	if (i2ctable[0] >= 0x40) { +		int port_type = i2ctable[headerlen + 4 * index + 3]; + +		if (port_type != 5) +			DRM_ERROR( +				   "DCB I2C table has port type %d\n", port_type); +	} + +	dev_priv->dcb_table.i2c_read[index] = i2ctable[headerlen + recordoffset + rdofs + 4 * index]; +	dev_priv->dcb_table.i2c_write[index] = i2ctable[headerlen + recordoffset + wrofs + 4 * index]; +} + +static bool +parse_dcb_entry(struct drm_device *dev, int index, uint8_t dcb_version, uint16_t i2ctabptr, uint32_t conn, uint32_t conf) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct dcb_entry *entry = &dev_priv->dcb_table.entry[index]; + +	memset(entry, 0, sizeof (struct dcb_entry)); + +	entry->index = index; +	/* safe defaults for a crt */ +	entry->type = 0; +	entry->i2c_index = 0; +	entry->heads = 1; +	entry->bus = 0; +	entry->location = LOC_ON_CHIP; +	entry->or = 1; +	entry->duallink_possible = false; + +	if (dcb_version >= 0x20) { +		entry->type = conn & 0xf; +		entry->i2c_index = (conn >> 4) & 0xf; +		entry->heads = (conn >> 8) & 0xf; +		entry->bus = (conn >> 16) & 0xf; +		entry->location = (conn >> 20) & 0xf; +		entry->or = (conn >> 24) & 0xf; +		/* Normal entries consist of a single bit, but dual link has the +		 * adjacent more significant bit set too +		 */ +		if ((1 << (ffs(entry->or) - 1)) * 3 == entry->or) +			entry->duallink_possible = true; + +		switch (entry->type) { +		case DCB_OUTPUT_LVDS: +			{ +			uint32_t mask; +			if (conf & 0x1) +				entry->lvdsconf.use_straps_for_mode = true; +			if (dcb_version < 0x22) { +				mask = ~0xd; +				/* both 0x4 and 0x8 show up in v2.0 tables; assume they mean +				 * the same thing, which is probably wrong, but might work */ +				if (conf & 0x4 || conf & 0x8) +					entry->lvdsconf.use_power_scripts = true; +			} else { +				mask = ~0x5; +				if (conf & 0x4) +					entry->lvdsconf.use_power_scripts = true; +			} +			if (conf & mask) { +				if (dcb_version < 0x40) { /* we know g80 cards have unknown bits */ +					DRM_ERROR("Unknown LVDS configuration bits, please report\n"); +					/* cause output setting to fail, so message is seen */ +					dev_priv->dcb_table.entries = 0; +					return false; +				} +			} +			break; +			} +		case 0xe: +			/* weird type that appears on g80 mobile bios; nv driver treats it as a terminator */ +			return false; +		} +		read_dcb_i2c_entry(dev, dcb_version, i2ctabptr, entry->i2c_index); +	} else if (dcb_version >= 0x14 ) { +		if (conn != 0xf0003f00 && conn != 0xf2247f10 && conn != 0xf2204001 && conn != 0xf2204301 && conn != 0xf2244311 && conn != 0xf2045f14 && conn != 0xf2205004 && conn != 0xf2208001 && conn != 0xf4204011 && conn != 0xf4208011 && conn != 0xf4248011) { +			DRM_ERROR( +				   "Unknown DCB 1.4 / 1.5 entry, please report\n"); +			/* cause output setting to fail, so message is seen */ +			dev_priv->dcb_table.entries = 0; +			return false; +		} +		/* most of the below is a "best guess" atm */ +		entry->type = conn & 0xf; +		if (entry->type == 4) { /* digital */ +			if (conn & 0x10) +				entry->type = DCB_OUTPUT_LVDS; +			else +				entry->type = DCB_OUTPUT_TMDS; +		} +		/* what's in bits 5-13? could be some brooktree/chrontel/philips thing, in tv case */ +		entry->i2c_index = (conn >> 14) & 0xf; +		/* raw heads field is in range 0-1, so move to 1-2 */ +		entry->heads = ((conn >> 18) & 0x7) + 1; +		entry->location = (conn >> 21) & 0xf; +		entry->bus = (conn >> 25) & 0x7; +		/* set or to be same as heads -- hopefully safe enough */ +		entry->or = entry->heads; + +		switch (entry->type) { +		case DCB_OUTPUT_LVDS: +			/* this is probably buried in conn's unknown bits */ +			entry->lvdsconf.use_power_scripts = true; +			break; +		case DCB_OUTPUT_TMDS: +			/* invent a DVI-A output, by copying the fields of the DVI-D output +			 * reported to work by math_b on an NV20(!) */ +			memcpy(&entry[1], &entry[0], sizeof(struct dcb_entry)); +			entry[1].type = DCB_OUTPUT_ANALOG; +			dev_priv->dcb_table.entries++; +		} +		read_dcb_i2c_entry(dev, dcb_version, i2ctabptr, entry->i2c_index); +	} else if (dcb_version >= 0x12) { +		/* v1.2 tables normally have the same 5 entries, which are not +		 * specific to the card, so use the defaults for a crt */ +		/* DCB v1.2 does have an I2C table that read_dcb_i2c_table can handle, but cards +		 * exist (seen on nv11) where the pointer to the table points to the wrong +		 * place, so for now, we rely on the indices parsed in parse_bmp_structure +		 */ +		entry->i2c_index = dev_priv->bios.legacy.i2c_indices.crt; +	} else { /* pre DCB / v1.1 - use the safe defaults for a crt */ +		DRM_ERROR( +			   "No information in BIOS output table; assuming a CRT output exists\n"); +		entry->i2c_index = dev_priv->bios.legacy.i2c_indices.crt; +	} + +	if (entry->type == DCB_OUTPUT_LVDS && dev_priv->bios.fp.strapping != 0xff) +		entry->lvdsconf.use_straps_for_mode = true; + +	dev_priv->dcb_table.entries++; + +	return true; +} + +static void merge_like_dcb_entries(struct drm_device *dev) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; + +	/* DCB v2.0 lists each output combination separately. +	 * Here we merge compatible entries to have fewer outputs, with more options +	 */ +	int i, newentries = 0; + +	for (i = 0; i < dev_priv->dcb_table.entries; i++) { +		struct dcb_entry *ient = &dev_priv->dcb_table.entry[i]; +		int j; + +		for (j = i + 1; j < dev_priv->dcb_table.entries; j++) { +			struct dcb_entry *jent = &dev_priv->dcb_table.entry[j]; + +			if (jent->type == 100) /* already merged entry */ +				continue; + +			/* merge heads field when all other fields the same */ +			if (jent->i2c_index == ient->i2c_index && jent->type == ient->type && jent->location == ient->location && jent->or == ient->or) { +				DRM_INFO( +					   "Merging DCB entries %d and %d\n", i, j); +				ient->heads |= jent->heads; +				jent->type = 100; /* dummy value */ +			} +		} +	} + +	/* Compact entries merged into others out of dcb_table */ +	for (i = 0; i < dev_priv->dcb_table.entries; i++) { +		if ( dev_priv->dcb_table.entry[i].type == 100 ) +			continue; + +		if (newentries != i) +			memcpy(&dev_priv->dcb_table.entry[newentries], &dev_priv->dcb_table.entry[i], sizeof(struct dcb_entry)); +		newentries++; +	} + +	dev_priv->dcb_table.entries = newentries; +} + +static unsigned int parse_dcb_table(struct drm_device *dev, struct bios *bios) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	uint16_t dcbptr, i2ctabptr = 0; +	uint8_t *dcbtable; +	uint8_t dcb_version, headerlen = 0x4, entries = MAX_NUM_DCB_ENTRIES; +	bool configblock = true; +	int recordlength = 8, confofs = 4; +	int i; + +	dev_priv->dcb_table.entries = 0; + +	/* get the offset from 0x36 */ +	dcbptr = le16_to_cpu(*(uint16_t *)&bios->data[0x36]); + +	if (dcbptr == 0x0) { +		DRM_ERROR( +			   "No Display Configuration Block pointer found\n"); +		/* this situation likely means a really old card, pre DCB, so we'll add the safe CRT entry */ +		parse_dcb_entry(dev, 0, 0, 0, 0, 0); +		return 1; +	} + +	dcbtable = &bios->data[dcbptr]; + +	/* get DCB version */ +	dcb_version = dcbtable[0]; +	DRM_INFO( +		   "Display Configuration Block version %d.%d found\n", +		   dcb_version >> 4, dcb_version & 0xf); + +	if (dcb_version >= 0x20) { /* NV17+ */ +		uint32_t sig; + +		if (dcb_version >= 0x30) { /* NV40+ */ +			headerlen = dcbtable[1]; +			entries = dcbtable[2]; +			recordlength = dcbtable[3]; +			i2ctabptr = le16_to_cpu(*(uint16_t *)&dcbtable[4]); +			sig = le32_to_cpu(*(uint32_t *)&dcbtable[6]); + +			DRM_INFO( +				   "DCB header length %d, with %d possible entries\n", +				   headerlen, entries); +		} else { +			i2ctabptr = le16_to_cpu(*(uint16_t *)&dcbtable[2]); +			sig = le32_to_cpu(*(uint32_t *)&dcbtable[4]); +			headerlen = 8; +		} + +		if (sig != 0x4edcbdcb) { +			DRM_ERROR( +				   "Bad Display Configuration Block signature (%08X)\n", sig); +			return 0; +		} +	} else if (dcb_version >= 0x14) { /* some NV15/16, and NV11+ */ +		char sig[8]; + +		memset(sig, 0, 8); +		strncpy(sig, (char *)&dcbtable[-7], 7); +		i2ctabptr = le16_to_cpu(*(uint16_t *)&dcbtable[2]); +		recordlength = 10; +		confofs = 6; + +		if (strcmp(sig, "DEV_REC")) { +			DRM_ERROR( +				   "Bad Display Configuration Block signature (%s)\n", sig); +			return 0; +		} +	} else if (dcb_version >= 0x12) { /* some NV6/10, and NV15+ */ +		i2ctabptr = le16_to_cpu(*(uint16_t *)&dcbtable[2]); +		configblock = false; +	} else {	/* NV5+, maybe NV4 */ +		/* DCB 1.1 seems to be quite unhelpful - we'll just add the safe CRT entry */ +		parse_dcb_entry(dev, 0, dcb_version, 0, 0, 0); +		return 1; +	} + +	if (entries >= MAX_NUM_DCB_ENTRIES) +		entries = MAX_NUM_DCB_ENTRIES; + +	for (i = 0; i < entries; i++) { +		uint32_t connection, config = 0; + +		connection = le32_to_cpu(*(uint32_t *)&dcbtable[headerlen + recordlength * i]); +		if (configblock) +			config = le32_to_cpu(*(uint32_t *)&dcbtable[headerlen + confofs + recordlength * i]); + +		/* Should we allow discontinuous DCBs? Certainly DCB I2C tables can be discontinuous */ +		if ((connection & 0x0000000f) == 0x0000000f) /* end of records */ +			break; +		if (connection == 0x00000000) /* seen on an NV11 with DCB v1.5 */ +			break; + +		DRM_INFO("Raw DCB entry %d: %08x %08x\n", i, connection, config); +		if (!parse_dcb_entry(dev, dev_priv->dcb_table.entries, dcb_version, i2ctabptr, connection, config)) +			break; +	} + +	merge_like_dcb_entries(dev); + +	return dev_priv->dcb_table.entries; +} + +int nouveau_parse_bios(struct drm_device *dev) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; + +	const uint8_t bit_signature[] = { 'B', 'I', 'T' }; +	int offset; + +	dev_priv->bios.data = kzalloc(NV50_PROM__ESIZE, GFP_KERNEL); +	if (!dev_priv->bios.data) +		return -ENOMEM; + +	if (!nv_shadow_bios(dev, dev_priv->bios.data)) +		return -EINVAL; + +	dev_priv->bios.length = dev_priv->bios.data[2] * 512; +	if (dev_priv->bios.length > NV50_PROM__ESIZE) +		dev_priv->bios.length = NV50_PROM__ESIZE; + +	if ((offset = findstr(dev_priv->bios.data, dev_priv->bios.length, bit_signature, sizeof(bit_signature)))) { +		DRM_INFO("BIT BIOS found\n"); +		parse_bit_structure(dev, &dev_priv->bios, offset + 4); +	} else { +		DRM_ERROR("BIT BIOS not found\n"); +		return -EINVAL; +	} + +	if (parse_dcb_table(dev, &dev_priv->bios)) +		DRM_INFO("Found %d entries in DCB\n", dev_priv->dcb_table.entries); + +	return 0; +} + +/* temporary */ +#define NV_RAMDAC_NVPLL			0x00680500 +#define NV_RAMDAC_MPLL			0x00680504 +#define NV_RAMDAC_VPLL			0x00680508 +#	define NV_RAMDAC_PLL_COEFF_MDIV			0x000000FF +#	define NV_RAMDAC_PLL_COEFF_NDIV			0x0000FF00 +#	define NV_RAMDAC_PLL_COEFF_PDIV			0x00070000 +#	define NV30_RAMDAC_ENABLE_VCO2			(1 << 7) +#define NV_RAMDAC_VPLL2			0x00680520 + +bool get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims *pll_lim) +{ +	/* PLL limits table +	 * +	 * Version 0x10: NV31 +	 * One byte header (version), one record of 24 bytes +	 * Version 0x11: NV36 - Not implemented +	 * Seems to have same record style as 0x10, but 3 records rather than 1 +	 * Version 0x20: Found on Geforce 6 cards +	 * Trivial 4 byte BIT header. 31 (0x1f) byte record length +	 * Version 0x21: Found on Geforce 7, 8 and some Geforce 6 cards +	 * 5 byte header, fifth byte of unknown purpose. 35 (0x23) byte record +	 * length in general, some (integrated) have an extra configuration byte +	 */ + +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct bios *bios = &dev_priv->bios; +	uint8_t pll_lim_ver = 0, headerlen = 0, recordlen = 0, entries = 0; +	int pllindex = 0; +	uint32_t crystal_strap_mask, crystal_straps; + +	if (!bios->pll_limit_tbl_ptr) { +		if (bios->chip_version >= 0x40 || bios->chip_version == 0x31 || bios->chip_version == 0x36) { +			DRM_ERROR("Pointer to PLL limits table invalid\n"); +			return false; +		} +	} else { +		pll_lim_ver = bios->data[bios->pll_limit_tbl_ptr]; + +		DRM_INFO("Found PLL limits table version 0x%X\n", pll_lim_ver); +	} + +	crystal_strap_mask = 1 << 6; +	/* open coded pNv->twoHeads test */ +	if (bios->chip_version > 0x10 && bios->chip_version != 0x15 && +		bios->chip_version != 0x1a && bios->chip_version != 0x20) +		crystal_strap_mask |= 1 << 22; +	crystal_straps = NV_READ(NV50_PEXTDEV + 0x0) & crystal_strap_mask; + +	switch (pll_lim_ver) { +	/* we use version 0 to indicate a pre limit table bios (single stage pll) +	 * and load the hard coded limits instead */ +	case 0: +		break; +	case 0x10: +	case 0x11: /* strictly v0x11 has 3 entries, but the last two don't seem to get used */ +		headerlen = 1; +		recordlen = 0x18; +		entries = 1; +		pllindex = 0; +		break; +	case 0x20: +	case 0x21: +		headerlen = bios->data[bios->pll_limit_tbl_ptr + 1]; +		recordlen = bios->data[bios->pll_limit_tbl_ptr + 2]; +		entries = bios->data[bios->pll_limit_tbl_ptr + 3]; +		break; +	default: +		DRM_ERROR("PLL limits table revision not currently supported\n"); +		return false; +	} + +	/* initialize all members to zero */ +	memset(pll_lim, 0, sizeof(struct pll_lims)); + +	if (pll_lim_ver == 0x10 || pll_lim_ver == 0x11) { +		uint16_t plloffs = bios->pll_limit_tbl_ptr + headerlen + recordlen * pllindex; + +		pll_lim->vco1.minfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs]))); +		pll_lim->vco1.maxfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + 4]))); +		pll_lim->vco2.minfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + 8]))); +		pll_lim->vco2.maxfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + 12]))); +		pll_lim->vco1.min_inputfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + 16]))); +		pll_lim->vco2.min_inputfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + 20]))); +		pll_lim->vco1.max_inputfreq = pll_lim->vco2.max_inputfreq = INT_MAX; + +		/* these values taken from nv30/31/36 */ +		pll_lim->vco1.min_n = 0x1; +		if (bios->chip_version == 0x36) +			pll_lim->vco1.min_n = 0x5; +		pll_lim->vco1.max_n = 0xff; +		pll_lim->vco1.min_m = 0x1; +		pll_lim->vco1.max_m = 0xd; +		pll_lim->vco2.min_n = 0x4; +		/* on nv30, 31, 36 (i.e. all cards with two stage PLLs with this +		 * table version (apart from nv35)), N2 is compared to +		 * maxN2 (0x46) and 10 * maxM2 (0x4), so set maxN2 to 0x28 and +		 * save a comparison +		 */ +		pll_lim->vco2.max_n = 0x28; +		if (bios->chip_version == 0x30 || bios->chip_version == 0x35) +		       /* only 5 bits available for N2 on nv30/35 */ +			pll_lim->vco2.max_n = 0x1f; +		pll_lim->vco2.min_m = 0x1; +		pll_lim->vco2.max_m = 0x4; +	} else if (pll_lim_ver) {	/* ver 0x20, 0x21 */ +		uint16_t plloffs = bios->pll_limit_tbl_ptr + headerlen; +		uint32_t reg = 0; /* default match */ +		int i; + +		/* first entry is default match, if nothing better. warn if reg field nonzero */ +		if (le32_to_cpu(*((uint32_t *)&bios->data[plloffs]))) +			DRM_ERROR("Default PLL limit entry has non-zero register field\n"); + +		if (limit_match > MAX_PLL_TYPES) +			/* we've been passed a reg as the match */ +			reg = limit_match; +		else /* limit match is a pll type */ +			for (i = 1; i < entries && !reg; i++) { +				uint32_t cmpreg = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + recordlen * i]))); + +				if (limit_match == NVPLL && (cmpreg == NV_RAMDAC_NVPLL || cmpreg == 0x4000)) +					reg = cmpreg; +				if (limit_match == MPLL && (cmpreg == NV_RAMDAC_MPLL || cmpreg == 0x4020)) +					reg = cmpreg; +				if (limit_match == VPLL1 && (cmpreg == NV_RAMDAC_VPLL || cmpreg == 0x4010)) +					reg = cmpreg; +				if (limit_match == VPLL2 && (cmpreg == NV_RAMDAC_VPLL2 || cmpreg == 0x4018)) +					reg = cmpreg; +			} + +		for (i = 1; i < entries; i++) +			if (le32_to_cpu(*((uint32_t *)&bios->data[plloffs + recordlen * i])) == reg) { +				pllindex = i; +				break; +			} + +		plloffs += recordlen * pllindex; + +		DRM_INFO("Loading PLL limits for reg 0x%08x\n", pllindex ? reg : 0); + +		/* frequencies are stored in tables in MHz, kHz are more useful, so we convert */ + +		/* What output frequencies can each VCO generate? */ +		pll_lim->vco1.minfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 4]))) * 1000; +		pll_lim->vco1.maxfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 6]))) * 1000; +		pll_lim->vco2.minfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 8]))) * 1000; +		pll_lim->vco2.maxfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 10]))) * 1000; + +		/* What input frequencies do they accept (past the m-divider)? */ +		pll_lim->vco1.min_inputfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 12]))) * 1000; +		pll_lim->vco2.min_inputfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 14]))) * 1000; +		pll_lim->vco1.max_inputfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 16]))) * 1000; +		pll_lim->vco2.max_inputfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 18]))) * 1000; + +		/* What values are accepted as multiplier and divider? */ +		pll_lim->vco1.min_n = bios->data[plloffs + 20]; +		pll_lim->vco1.max_n = bios->data[plloffs + 21]; +		pll_lim->vco1.min_m = bios->data[plloffs + 22]; +		pll_lim->vco1.max_m = bios->data[plloffs + 23]; +		pll_lim->vco2.min_n = bios->data[plloffs + 24]; +		pll_lim->vco2.max_n = bios->data[plloffs + 25]; +		pll_lim->vco2.min_m = bios->data[plloffs + 26]; +		pll_lim->vco2.max_m = bios->data[plloffs + 27]; + +		pll_lim->unk1c = bios->data[plloffs + 28]; +		pll_lim->max_log2p_bias = bios->data[plloffs + 29]; +		pll_lim->log2p_bias = bios->data[plloffs + 30]; + +		if (recordlen > 0x22) +			pll_lim->refclk = le32_to_cpu(*((uint32_t *)&bios->data[plloffs + 31])); + +		if (recordlen > 0x23) +			if (bios->data[plloffs + 35]) +				DRM_ERROR("Bits set in PLL configuration byte (%x)\n", bios->data[plloffs + 35]); + +		/* C51 special not seen elsewhere */ +		/*if (bios->chip_version == 0x51 && !pll_lim->refclk) { +			uint32_t sel_clk = nv32_rd(pScrn, NV_RAMDAC_SEL_CLK); + +			if (((limit_match == NV_RAMDAC_VPLL || limit_match == VPLL1) && sel_clk & 0x20) || +			    ((limit_match == NV_RAMDAC_VPLL2 || limit_match == VPLL2) && sel_clk & 0x80)) { +				if (nv_idx_port_rd(pScrn, CRTC_INDEX_COLOR, NV_VGA_CRTCX_REVISION) < 0xa3) +					pll_lim->refclk = 200000; +				else +					pll_lim->refclk = 25000; +			} +		}*/ +	} + +	/* By now any valid limit table ought to have set a max frequency for +	 * vco1, so if it's zero it's either a pre limit table bios, or one +	 * with an empty limit table (seen on nv18) +	 */ +	if (!pll_lim->vco1.maxfreq) { +		pll_lim->vco1.minfreq = bios->fminvco; +		pll_lim->vco1.maxfreq = bios->fmaxvco; +		pll_lim->vco1.min_inputfreq = 0; +		pll_lim->vco1.max_inputfreq = INT_MAX; +		pll_lim->vco1.min_n = 0x1; +		pll_lim->vco1.max_n = 0xff; +		pll_lim->vco1.min_m = 0x1; +		if (crystal_straps == 0) { +			/* nv05 does this, nv11 doesn't, nv10 unknown */ +			if (bios->chip_version < 0x11) +				pll_lim->vco1.min_m = 0x7; +			pll_lim->vco1.max_m = 0xd; +		} else { +			if (bios->chip_version < 0x11) +				pll_lim->vco1.min_m = 0x8; +			pll_lim->vco1.max_m = 0xe; +		} +	} + +	if (!pll_lim->refclk) +		switch (crystal_straps) { +		case 0: +			pll_lim->refclk = 13500; +			break; +		case (1 << 6): +			pll_lim->refclk = 14318; +			break; +		case (1 << 22): +			pll_lim->refclk = 27000; +			break; +		case (1 << 22 | 1 << 6): +			pll_lim->refclk = 25000; +			break; +		} + +#if 1 /* for easy debugging */ +	DRM_INFO("pll.vco1.minfreq: %d\n", pll_lim->vco1.minfreq); +	DRM_INFO("pll.vco1.maxfreq: %d\n", pll_lim->vco1.maxfreq); +	DRM_INFO("pll.vco2.minfreq: %d\n", pll_lim->vco2.minfreq); +	DRM_INFO("pll.vco2.maxfreq: %d\n", pll_lim->vco2.maxfreq); + +	DRM_INFO("pll.vco1.min_inputfreq: %d\n", pll_lim->vco1.min_inputfreq); +	DRM_INFO("pll.vco1.max_inputfreq: %d\n", pll_lim->vco1.max_inputfreq); +	DRM_INFO("pll.vco2.min_inputfreq: %d\n", pll_lim->vco2.min_inputfreq); +	DRM_INFO("pll.vco2.max_inputfreq: %d\n", pll_lim->vco2.max_inputfreq); + +	DRM_INFO("pll.vco1.min_n: %d\n", pll_lim->vco1.min_n); +	DRM_INFO("pll.vco1.max_n: %d\n", pll_lim->vco1.max_n); +	DRM_INFO("pll.vco1.min_m: %d\n", pll_lim->vco1.min_m); +	DRM_INFO("pll.vco1.max_m: %d\n", pll_lim->vco1.max_m); +	DRM_INFO("pll.vco2.min_n: %d\n", pll_lim->vco2.min_n); +	DRM_INFO("pll.vco2.max_n: %d\n", pll_lim->vco2.max_n); +	DRM_INFO("pll.vco2.min_m: %d\n", pll_lim->vco2.min_m); +	DRM_INFO("pll.vco2.max_m: %d\n", pll_lim->vco2.max_m); + +	DRM_INFO("pll.unk1c: %d\n", pll_lim->unk1c); +	DRM_INFO("pll.max_log2p_bias: %d\n", pll_lim->max_log2p_bias); +	DRM_INFO("pll.log2p_bias: %d\n", pll_lim->log2p_bias); + +	DRM_INFO("pll.refclk: %d\n", pll_lim->refclk); +#endif + +	return true; +} diff --git a/linux-core/nouveau_bios.h b/linux-core/nouveau_bios.h new file mode 100644 index 00000000..e33ecd0f --- /dev/null +++ b/linux-core/nouveau_bios.h @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2005-2006 Erik Waling + * Copyright (C) 2006 Stephane Marchesin + * Copyright (C) 2007-2008 Stuart Bennett + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 __NOUVEAU_BIOS_H__ +#define __NOUVEAU_BIOS_H__ + +#include "drmP.h" +#include "drm.h" + +#define LOC_ON_CHIP 0 + +enum dcb_output_type {/* matches DCB types */ +	DCB_OUTPUT_NONE = 4, +	DCB_OUTPUT_ANALOG = 0, +	DCB_OUTPUT_TMDS = 2, +	DCB_OUTPUT_LVDS = 3, +	DCB_OUTPUT_TV = 1, +}; + +struct bios { +	uint8_t *data; +	unsigned int length; +	bool execute; + +	uint8_t major_version, chip_version; +	uint8_t feature_byte; + +	uint32_t fmaxvco, fminvco; + +	uint32_t dactestval; + +	uint16_t init_script_tbls_ptr; +	uint16_t extra_init_script_tbl_ptr; +	uint16_t macro_index_tbl_ptr; +	uint16_t macro_tbl_ptr; +	uint16_t condition_tbl_ptr; +	uint16_t io_condition_tbl_ptr; +	uint16_t io_flag_condition_tbl_ptr; +	uint16_t init_function_tbl_ptr; + +	uint16_t pll_limit_tbl_ptr; +	uint16_t ram_restrict_tbl_ptr; + +	struct { +		struct nouveau_hw_mode *native_mode; +		uint8_t *edid; +		uint16_t lvdsmanufacturerpointer; +		uint16_t xlated_entry; +		bool power_off_for_reset; +		bool reset_after_pclk_change; +		bool dual_link; +		bool link_c_increment; +		bool if_is_24bit; +		bool BITbit1; +		int duallink_transition_clk; +		/* lower nibble stores PEXTDEV_BOOT_0 strap +		 * upper nibble stores xlated display strap */ +		uint8_t strapping; +	} fp; + +	struct { +		uint16_t output0_script_ptr; +		uint16_t output1_script_ptr; +	} tmds; + +	struct { +		uint16_t mem_init_tbl_ptr; +		uint16_t sdr_seq_tbl_ptr; +		uint16_t ddr_seq_tbl_ptr; + +		struct { +			uint8_t crt, tv, panel; +		} i2c_indices; +	} legacy; +}; + +struct dcb_entry { +	int index; +	uint8_t type; +	uint8_t i2c_index; +	uint8_t heads; +	uint8_t bus; +	uint8_t location; +	uint8_t or; +	bool duallink_possible; +	union { +		struct { +			bool use_straps_for_mode; +			bool use_power_scripts; +		} lvdsconf; +	}; +}; + +/* changing these requires matching changes to reg tables in nv_get_clock */ +#define MAX_PLL_TYPES	4 +enum pll_types { +	NVPLL, +	MPLL, +	VPLL1, +	VPLL2 +}; + +struct pll_lims { +	struct { +		int minfreq; +		int maxfreq; +		int min_inputfreq; +		int max_inputfreq; + +		uint8_t min_m; +		uint8_t max_m; +		uint8_t min_n; +		uint8_t max_n; +	} vco1, vco2; + +	uint8_t unk1c; +	uint8_t max_log2p_bias; +	uint8_t log2p_bias; +	int refclk; +}; + +bool get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims *pll_lim); +int nouveau_parse_bios(struct drm_device *dev); + +#endif /* __NOUVEAU_BIOS_H__ */ diff --git a/linux-core/nouveau_bo.c b/linux-core/nouveau_bo.c index ab3b23a4..86347e03 100644 --- a/linux-core/nouveau_bo.c +++ b/linux-core/nouveau_bo.c @@ -229,7 +229,7 @@ out_cleanup:          if (tmp_mem.mm_node) {                  mutex_lock(&dev->struct_mutex);                  if (tmp_mem.mm_node != bo->pinned_node) -                        drm_mm_put_block(tmp_mem.mm_node); +                        drm_memrange_put_block(tmp_mem.mm_node);                  tmp_mem.mm_node = NULL;                  mutex_unlock(&dev->struct_mutex);          } diff --git a/linux-core/nouveau_drv.c b/linux-core/nouveau_drv.c index c8f57dff..04f002f2 100644 --- a/linux-core/nouveau_drv.c +++ b/linux-core/nouveau_drv.c @@ -28,6 +28,9 @@  #include "drm_pciids.h" +unsigned int nouveau_modeset = 0; /* kms */ +module_param_named(modeset, nouveau_modeset, int, 0400); +  static struct pci_device_id pciidlist[] = {  	{  		PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID), @@ -104,6 +107,10 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *ent)  static int __init nouveau_init(void)  {  	driver.num_ioctls = nouveau_max_ioctl; + +	if (nouveau_modeset == 1) +		driver.driver_features |= DRIVER_MODESET; +  	return drm_init(&driver, pciidlist);  } diff --git a/linux-core/nouveau_sgdma.c b/linux-core/nouveau_sgdma.c index cc4d5a92..81704ea1 100644 --- a/linux-core/nouveau_sgdma.c +++ b/linux-core/nouveau_sgdma.c @@ -280,7 +280,7 @@ nouveau_sgdma_nottm_hack_init(struct drm_device *dev)  	struct drm_nouveau_private *dev_priv = dev->dev_private;  	struct drm_ttm_backend *be;  	struct drm_scatter_gather sgreq; -	struct drm_mm_node mm_node; +	struct drm_memrange_node mm_node;  	struct drm_bo_mem_reg mem;  	int ret; diff --git a/linux-core/nv50_connector.c b/linux-core/nv50_connector.c new file mode 100644 index 00000000..34706bae --- /dev/null +++ b/linux-core/nv50_connector.c @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 "nv50_connector.h" + +static struct nv50_output *nv50_connector_to_output(struct nv50_connector *connector, bool digital) +{ +	struct nv50_display *display = nv50_get_display(connector->dev); +	struct nv50_output *output = NULL; +	bool digital_possible = false; +	bool analog_possible = false; + +	switch (connector->type) { +		case CONNECTOR_VGA: +		case CONNECTOR_TV: +			analog_possible = true; +			break; +		case CONNECTOR_DVI_I: +			analog_possible = true; +			digital_possible = true; +			break; +		case CONNECTOR_DVI_D: +		case CONNECTOR_LVDS: +			digital_possible = true; +			break; +		default: +			break; +	} + +	/* Return early on bad situations. */ +	if (!analog_possible && !digital_possible) +		return NULL; + +	if (!analog_possible && !digital) +		return NULL; + +	if (!digital_possible && digital) +		return NULL; + +	list_for_each_entry(output, &display->outputs, item) { +		if (connector->bus != output->bus) +			continue; +		if (digital && output->type == OUTPUT_TMDS) +			return output; +		if (digital && output->type == OUTPUT_LVDS) +			return output; +		if (!digital && output->type == OUTPUT_DAC) +			return output; +		if (!digital && output->type == OUTPUT_TV) +			return output; +	} + +	return NULL; +} + +static int nv50_connector_hpd_detect(struct nv50_connector *connector) +{ +	struct drm_nouveau_private *dev_priv = connector->dev->dev_private; +	bool present = 0; +	uint32_t reg = 0; + +	/* Assume connected for the moment. */ +	if (connector->type == CONNECTOR_LVDS) { +		NV50_DEBUG("LVDS is defaulting to connected for the moment.\n"); +		return 1; +	} + +	/* No i2c port, no idea what to do for hotplug. */ +	if (connector->i2c_chan->index == 15) { +		DRM_ERROR("You have a non-LVDS SOR with no i2c port, please report\n"); +		return -EINVAL; +	} + +	if (connector->i2c_chan->index > 3) { +		DRM_ERROR("You have an unusual configuration, index is %d\n", connector->i2c_chan->index); +		DRM_ERROR("Please report.\n"); +		return -EINVAL; +	} + +	/* Check hotplug pins. */ +	reg = NV_READ(NV50_PCONNECTOR_HOTPLUG_STATE); +	if (reg & (NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C0 << (4 * connector->i2c_chan->index))) +		present = 1; + +	if (present) +		NV50_DEBUG("Hotplug detect returned positive for bus %d\n", connector->bus); +	else +		NV50_DEBUG("Hotplug detect returned negative for bus %d\n", connector->bus); + +	return present; +} + +static int nv50_connector_i2c_detect(struct nv50_connector *connector) +{ +	/* kindly borrrowed from the intel driver, hope it works. */ +	uint8_t out_buf[] = { 0x0, 0x0}; +	uint8_t buf[2]; +	int ret; +	struct i2c_msg msgs[] = { +		{ +			.addr = 0x50, +			.flags = 0, +			.len = 1, +			.buf = out_buf, +		}, +		{ +			.addr = 0x50, +			.flags = I2C_M_RD, +			.len = 1, +			.buf = buf, +		} +	}; + +	if (!connector->i2c_chan) +		return -EINVAL; + +	ret = i2c_transfer(&connector->i2c_chan->adapter, msgs, 2); +	NV50_DEBUG("I2C detect returned %d\n", ret); + +	if (ret == 2) +		return true; + +	return false; +} + +static int nv50_connector_destroy(struct nv50_connector *connector) +{ +	struct drm_device *dev = connector->dev; +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_display *display = nv50_get_display(dev); + +	NV50_DEBUG("\n"); + +	if (!display || !connector) +		return -EINVAL; + +	list_del(&connector->item); + +	if (connector->i2c_chan) +		nv50_i2c_channel_destroy(connector->i2c_chan); + +	if (dev_priv->free_connector) +		dev_priv->free_connector(connector); + +	return 0; +} + +int nv50_connector_create(struct drm_device *dev, int bus, int i2c_index, int type) +{ +	struct nv50_connector *connector = NULL; +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_display *display = NULL; + +	NV50_DEBUG("\n"); + +	/* This allows the public layer to do it's thing. */ +	if (dev_priv->alloc_connector) +		connector = dev_priv->alloc_connector(dev); + +	if (!connector) +		return -ENOMEM; + +	connector->dev = dev; + +	display = nv50_get_display(dev); +	if (!display) +		goto out; + +	if (type == CONNECTOR_UNKNOWN) +		goto out; + +	list_add_tail(&connector->item, &display->connectors); + +	connector->bus = bus; +	connector->type = type; + +	switch (type) { +		case CONNECTOR_VGA: +			DRM_INFO("Detected a VGA connector\n"); +			break; +		case CONNECTOR_DVI_D: +			DRM_INFO("Detected a DVI-D connector\n"); +			break; +		case CONNECTOR_DVI_I: +			DRM_INFO("Detected a DVI-I connector\n"); +			break; +		case CONNECTOR_LVDS: +			DRM_INFO("Detected a LVDS connector\n"); +			break; +		case CONNECTOR_TV: +			DRM_INFO("Detected a TV connector\n"); +			break; +		default: +			DRM_ERROR("Unknown connector, this is not good.\n"); +			break; +	} + +	/* some reasonable defaults */ +	if (type == CONNECTOR_DVI_D || type == CONNECTOR_DVI_I || type == CONNECTOR_LVDS) +		connector->requested_scaling_mode = SCALE_FULLSCREEN; +	else +		connector->requested_scaling_mode = SCALE_NON_GPU; + +	connector->use_dithering = false; + +	if (i2c_index < 0xf) +		connector->i2c_chan = nv50_i2c_channel_create(dev, i2c_index); + +	/* set function pointers */ +	connector->hpd_detect = nv50_connector_hpd_detect; +	connector->i2c_detect = nv50_connector_i2c_detect; +	connector->destroy = nv50_connector_destroy; +	connector->to_output = nv50_connector_to_output; + +	return 0; + +out: +	if (dev_priv->free_connector) +		dev_priv->free_connector(connector); + +	return -EINVAL; +} diff --git a/linux-core/nv50_connector.h b/linux-core/nv50_connector.h new file mode 100644 index 00000000..fa7316e2 --- /dev/null +++ b/linux-core/nv50_connector.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 __NV50_CONNECTOR_H__ +#define __NV50_CONNECTOR_H__ + +#include "nv50_output.h" +#include "nv50_i2c.h" + +#define CONNECTOR_UNKNOWN 0 +#define CONNECTOR_VGA 1 +#define CONNECTOR_DVI_D 2 +#define CONNECTOR_DVI_I 3 +#define CONNECTOR_LVDS 4 +#define CONNECTOR_TV 5 + +struct nv50_connector { +	struct list_head item; + +	struct drm_device *dev; +	int type; + +	int bus; +	struct nv50_i2c_channel *i2c_chan; +	struct nv50_output *output; + +	int requested_scaling_mode; + +	bool use_dithering; + +	int (*hpd_detect) (struct nv50_connector *connector); +	int (*i2c_detect) (struct nv50_connector *connector); +	int (*destroy) (struct nv50_connector *connector); +	struct nv50_output *(*to_output) (struct nv50_connector *connector, bool digital); +}; + +int nv50_connector_create(struct drm_device *dev, int bus, int i2c_index, int type); + +#endif /* __NV50_CONNECTOR_H__ */ diff --git a/linux-core/nv50_crtc.c b/linux-core/nv50_crtc.c new file mode 100644 index 00000000..f56aa339 --- /dev/null +++ b/linux-core/nv50_crtc.c @@ -0,0 +1,533 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 "nv50_crtc.h" +#include "nv50_cursor.h" +#include "nv50_lut.h" +#include "nv50_fb.h" + +static int nv50_crtc_validate_mode(struct nv50_crtc *crtc, struct nouveau_hw_mode *mode) +{ +	NV50_DEBUG("\n"); + +	if (mode->clock > 400000) +		return MODE_CLOCK_HIGH; + +	if (mode->clock < 25000) +		return MODE_CLOCK_LOW; + +	return MODE_OK; +} + +static int nv50_crtc_set_mode(struct nv50_crtc *crtc, struct nouveau_hw_mode *mode) +{ +	struct nouveau_hw_mode *hw_mode = crtc->mode; +	uint8_t rval; + +	NV50_DEBUG("index %d\n", crtc->index); + +	if (!mode) { +		DRM_ERROR("No mode\n"); +		return MODE_NOMODE; +	} + +	if ((rval = crtc->validate_mode(crtc, mode))) { +		DRM_ERROR("Mode invalid\n"); +		return rval; +	} + +	/* copy values to mode */ +	*hw_mode = *mode; + +	return 0; +} + +static int nv50_crtc_execute_mode(struct nv50_crtc *crtc) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; +	struct nouveau_hw_mode *hw_mode; +	uint32_t hsync_dur,  vsync_dur, hsync_start_to_end, vsync_start_to_end; +	uint32_t hunk1, vunk1, vunk2a, vunk2b; +	uint32_t offset = crtc->index * 0x400; + +	NV50_DEBUG("index %d\n", crtc->index); +	NV50_DEBUG("%s native mode\n", crtc->use_native_mode ? "using" : "not using"); + +	if (crtc->use_native_mode) +		hw_mode = crtc->native_mode; +	else +		hw_mode = crtc->mode; + +	hsync_dur = hw_mode->hsync_end - hw_mode->hsync_start; +	vsync_dur = hw_mode->vsync_end - hw_mode->vsync_start; +	hsync_start_to_end = hw_mode->hblank_end - hw_mode->hsync_start; +	vsync_start_to_end = hw_mode->vblank_end - hw_mode->vsync_start; +	/* I can't give this a proper name, anyone else can? */ +	hunk1 = hw_mode->htotal - hw_mode->hsync_start + hw_mode->hblank_start; +	vunk1 = hw_mode->vtotal - hw_mode->vsync_start + hw_mode->vblank_start; +	/* Another strange value, this time only for interlaced modes. */ +	vunk2a = 2*hw_mode->vtotal - hw_mode->vsync_start + hw_mode->vblank_start; +	vunk2b = hw_mode->vtotal - hw_mode->vsync_start + hw_mode->vblank_end; + +	if (hw_mode->flags & DRM_MODE_FLAG_INTERLACE) { +		vsync_dur /= 2; +		vsync_start_to_end  /= 2; +		vunk1 /= 2; +		vunk2a /= 2; +		vunk2b /= 2; +		/* magic */ +		if (hw_mode->flags & DRM_MODE_FLAG_DBLSCAN) { +			vsync_start_to_end -= 1; +			vunk1 -= 1; +			vunk2a -= 1; +			vunk2b -= 1; +		} +	} + +	OUT_MODE(NV50_CRTC0_CLOCK + offset, hw_mode->clock | 0x800000); +	OUT_MODE(NV50_CRTC0_INTERLACE + offset, (hw_mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 0); +	OUT_MODE(NV50_CRTC0_DISPLAY_START + offset, 0); +	OUT_MODE(NV50_CRTC0_UNK82C + offset, 0); +	OUT_MODE(NV50_CRTC0_DISPLAY_TOTAL + offset, hw_mode->vtotal << 16 | hw_mode->htotal); +	OUT_MODE(NV50_CRTC0_SYNC_DURATION + offset, (vsync_dur - 1) << 16 | (hsync_dur - 1)); +	OUT_MODE(NV50_CRTC0_SYNC_START_TO_BLANK_END + offset, (vsync_start_to_end - 1) << 16 | (hsync_start_to_end - 1)); +	OUT_MODE(NV50_CRTC0_MODE_UNK1 + offset, (vunk1 - 1) << 16 | (hunk1 - 1)); +	if (hw_mode->flags & DRM_MODE_FLAG_INTERLACE) { +		OUT_MODE(NV50_CRTC0_MODE_UNK2 + offset, (vunk2b - 1) << 16 | (vunk2a - 1)); +	} + +	crtc->set_fb(crtc); +	crtc->set_dither(crtc); + +	/* This is the actual resolution of the mode. */ +	OUT_MODE(NV50_CRTC0_REAL_RES + offset, (crtc->mode->vdisplay << 16) | crtc->mode->hdisplay); +	OUT_MODE(NV50_CRTC0_SCALE_CENTER_OFFSET + offset, NV50_CRTC_SCALE_CENTER_OFFSET_VAL(0,0)); + +	/* Maybe move this as well? */ +	crtc->blank(crtc, false); + +	return 0; +} + +static int nv50_crtc_set_fb(struct nv50_crtc *crtc) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; +	uint32_t offset = crtc->index * 0x400; + +	NV50_DEBUG("\n"); + +	OUT_MODE(NV50_CRTC0_FB_SIZE + offset, crtc->fb->height << 16 | crtc->fb->width); + +	/* I suspect this flag indicates a linear fb. */ +	OUT_MODE(NV50_CRTC0_FB_PITCH + offset, crtc->fb->pitch | 0x100000); + +	switch (crtc->fb->depth) { +		case 8: +			OUT_MODE(NV50_CRTC0_DEPTH + offset, NV50_CRTC0_DEPTH_8BPP);  +			break; +		case 15: +			OUT_MODE(NV50_CRTC0_DEPTH + offset, NV50_CRTC0_DEPTH_15BPP); +			break; +		case 16: +			OUT_MODE(NV50_CRTC0_DEPTH + offset, NV50_CRTC0_DEPTH_16BPP); +			break; +		case 24: +			OUT_MODE(NV50_CRTC0_DEPTH + offset, NV50_CRTC0_DEPTH_24BPP);  +			break; +	} + +	OUT_MODE(NV50_CRTC0_COLOR_CTRL + offset, NV50_CRTC_COLOR_CTRL_MODE_COLOR); +	OUT_MODE(NV50_CRTC0_FB_POS + offset, (crtc->fb->y << 16) | (crtc->fb->x)); + +	return 0; +} + +static int nv50_crtc_blank(struct nv50_crtc *crtc, bool blanked) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; +	uint32_t offset = crtc->index * 0x400; + +	NV50_DEBUG("index %d\n", crtc->index); +	NV50_DEBUG("%s\n", blanked ? "blanked" : "unblanked"); + +	/* We really need a framebuffer. */ +	if (!crtc->fb->block && !blanked) { +		DRM_ERROR("No framebuffer available on crtc %d\n", crtc->index); +		return -EINVAL; +	} + +	if (blanked) { +		crtc->cursor->hide(crtc); + +		OUT_MODE(NV50_CRTC0_CLUT_MODE + offset, NV50_CRTC0_CLUT_MODE_BLANK); +		OUT_MODE(NV50_CRTC0_CLUT_OFFSET + offset, 0); +		if (dev_priv->chipset != 0x50) +			OUT_MODE(NV84_CRTC0_BLANK_UNK1 + offset, NV84_CRTC0_BLANK_UNK1_BLANK); +		OUT_MODE(NV50_CRTC0_BLANK_CTRL + offset, NV50_CRTC0_BLANK_CTRL_BLANK); +		if (dev_priv->chipset != 0x50) +			OUT_MODE(NV84_CRTC0_BLANK_UNK2 + offset, NV84_CRTC0_BLANK_UNK2_BLANK); +	} else { +		OUT_MODE(NV50_CRTC0_FB_OFFSET + offset, crtc->fb->block->start >> 8); +		OUT_MODE(0x864 + offset, 0); + +		crtc->cursor->set_offset(crtc); + +		if (dev_priv->chipset != 0x50) +			OUT_MODE(NV84_CRTC0_BLANK_UNK2 + offset, NV84_CRTC0_BLANK_UNK2_UNBLANK); + +		if (crtc->cursor->visible) +			crtc->cursor->show(crtc); +		else +			crtc->cursor->hide(crtc); + +		OUT_MODE(NV50_CRTC0_CLUT_MODE + offset,  +			crtc->fb->depth == 8 ? NV50_CRTC0_CLUT_MODE_OFF : NV50_CRTC0_CLUT_MODE_ON); +		OUT_MODE(NV50_CRTC0_CLUT_OFFSET + offset, crtc->lut->block->start >> 8); +		if (dev_priv->chipset != 0x50) +			OUT_MODE(NV84_CRTC0_BLANK_UNK1 + offset, NV84_CRTC0_BLANK_UNK1_UNBLANK); +		OUT_MODE(NV50_CRTC0_BLANK_CTRL + offset, NV50_CRTC0_BLANK_CTRL_UNBLANK); +	} + +	/* sometimes you need to know if a screen is already blanked. */ +	crtc->blanked = blanked; + +	return 0; +} + +static int nv50_crtc_set_dither(struct nv50_crtc *crtc) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; +	uint32_t offset = crtc->index * 0x400; + +	NV50_DEBUG("\n"); + +	OUT_MODE(NV50_CRTC0_DITHERING_CTRL + offset, crtc->use_dithering ?  +			NV50_CRTC0_DITHERING_CTRL_ON : NV50_CRTC0_DITHERING_CTRL_OFF); + +	return 0; +} + +static void nv50_crtc_calc_scale(struct nv50_crtc *crtc, uint32_t *outX, uint32_t *outY) +{ +	uint32_t hor_scale, ver_scale; + +	/* max res is 8192, which is 2^13, which leaves 19 bits */ +	hor_scale = (crtc->native_mode->hdisplay << 19)/crtc->mode->hdisplay; +	ver_scale = (crtc->native_mode->vdisplay << 19)/crtc->mode->vdisplay; + +	if (ver_scale > hor_scale) { +		*outX = (crtc->mode->hdisplay * hor_scale) >> 19; +		*outY = (crtc->mode->vdisplay * hor_scale) >> 19; +	} else { +		*outX = (crtc->mode->hdisplay * ver_scale) >> 19; +		*outY = (crtc->mode->vdisplay * ver_scale) >> 19; +	} +} + +static int nv50_crtc_set_scale(struct nv50_crtc *crtc) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; +	uint32_t offset = crtc->index * 0x400; +	uint32_t outX, outY; + +	NV50_DEBUG("\n"); + +	switch (crtc->requested_scaling_mode) { +		case SCALE_ASPECT: +			nv50_crtc_calc_scale(crtc, &outX, &outY); +			break; +		case SCALE_FULLSCREEN: +			outX = crtc->native_mode->hdisplay; +			outY = crtc->native_mode->vdisplay; +			break; +		case SCALE_NOSCALE: +		case SCALE_NON_GPU: +		default: +			outX = crtc->mode->hdisplay; +			outY = crtc->mode->vdisplay; +			break; +	} + +	/* Got a better name for SCALER_ACTIVE? */ +	/* One day i've got to really figure out why this is needed. */ +	if ((crtc->mode->flags & DRM_MODE_FLAG_DBLSCAN) || (crtc->mode->flags & DRM_MODE_FLAG_INTERLACE) || +		crtc->mode->hdisplay != outX || crtc->mode->vdisplay != outY) { +		OUT_MODE(NV50_CRTC0_SCALE_CTRL + offset, NV50_CRTC0_SCALE_CTRL_SCALER_ACTIVE); +	} else { +		OUT_MODE(NV50_CRTC0_SCALE_CTRL + offset, NV50_CRTC0_SCALE_CTRL_SCALER_INACTIVE); +	} + +	OUT_MODE(NV50_CRTC0_SCALE_RES1 + offset, outY << 16 | outX); +	OUT_MODE(NV50_CRTC0_SCALE_RES2 + offset, outY << 16 | outX); + +	/* processed */ +	crtc->scaling_mode = crtc->requested_scaling_mode; + +	return 0; +} + +static int nv50_crtc_calc_clock(struct nv50_crtc *crtc,  +	uint32_t *bestN1, uint32_t *bestN2, uint32_t *bestM1, uint32_t *bestM2, uint32_t *bestlog2P) +{ +	struct nouveau_hw_mode *hw_mode; +	struct pll_lims limits; +	int clk, vco2, crystal; +	int minvco1, minvco2, minU1, maxU1, minU2, maxU2, minM1, maxM1; +	int maxvco1, maxvco2, minN1, maxN1, minM2, maxM2, minN2, maxN2; +	bool fixedgain2; +	int M1, N1, M2, N2, log2P; +	int clkP, calcclk1, calcclk2, calcclkout; +	int delta, bestdelta = INT_MAX; +	int bestclk = 0; + +	NV50_DEBUG("\n"); + +	if (crtc->use_native_mode) +		hw_mode = crtc->native_mode; +	else +		hw_mode = crtc->mode; + +	clk = hw_mode->clock; + +	/* These are in the g80 bios tables, at least in mine. */ +	if (!get_pll_limits(crtc->dev, NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1(crtc->index), &limits)) +		return -EINVAL; + +	minvco1 = limits.vco1.minfreq, maxvco1 = limits.vco1.maxfreq; +	minvco2 = limits.vco2.minfreq, maxvco2 = limits.vco2.maxfreq; +	minU1 = limits.vco1.min_inputfreq, minU2 = limits.vco2.min_inputfreq; +	maxU1 = limits.vco1.max_inputfreq, maxU2 = limits.vco2.max_inputfreq; +	minM1 = limits.vco1.min_m, maxM1 = limits.vco1.max_m; +	minN1 = limits.vco1.min_n, maxN1 = limits.vco1.max_n; +	minM2 = limits.vco2.min_m, maxM2 = limits.vco2.max_m; +	minN2 = limits.vco2.min_n, maxN2 = limits.vco2.max_n; +	crystal = limits.refclk; +	fixedgain2 = (minM2 == maxM2 && minN2 == maxN2); + +	vco2 = (maxvco2 - maxvco2/200) / 2; +	for (log2P = 0; clk && log2P < 6 && clk <= (vco2 >> log2P); log2P++) /* log2P is maximum of 6 */ +		; +	clkP = clk << log2P; + +	if (maxvco2 < clk + clk/200)	/* +0.5% */ +		maxvco2 = clk + clk/200; + +	for (M1 = minM1; M1 <= maxM1; M1++) { +		if (crystal/M1 < minU1) +			return bestclk; +		if (crystal/M1 > maxU1) +			continue; + +		for (N1 = minN1; N1 <= maxN1; N1++) { +			calcclk1 = crystal * N1 / M1; +			if (calcclk1 < minvco1) +				continue; +			if (calcclk1 > maxvco1) +				break; + +			for (M2 = minM2; M2 <= maxM2; M2++) { +				if (calcclk1/M2 < minU2) +					break; +				if (calcclk1/M2 > maxU2) +					continue; + +				/* add calcclk1/2 to round better */ +				N2 = (clkP * M2 + calcclk1/2) / calcclk1; +				if (N2 < minN2) +					continue; +				if (N2 > maxN2) +					break; + +				if (!fixedgain2) { +					calcclk2 = calcclk1 * N2 / M2; +					if (calcclk2 < minvco2) +						break; +					if (calcclk2 > maxvco2) +						continue; +				} else +					calcclk2 = calcclk1; + +				calcclkout = calcclk2 >> log2P; +				delta = abs(calcclkout - clk); +				/* we do an exhaustive search rather than terminating +				 * on an optimality condition... +				 */ +				if (delta < bestdelta) { +					bestdelta = delta; +					bestclk = calcclkout; +					*bestN1 = N1; +					*bestN2 = N2; +					*bestM1 = M1; +					*bestM2 = M2; +					*bestlog2P = log2P; +					if (delta == 0)	/* except this one */ +						return bestclk; +				} +			} +		} +	} + +	return bestclk; +} + +static int nv50_crtc_set_clock(struct nv50_crtc *crtc) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + +	uint32_t pll_reg = NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1(crtc->index); + +	uint32_t N1 = 0, N2 = 0, M1 = 0, M2 = 0, log2P = 0; + +	uint32_t reg1 = NV_READ(pll_reg + 4); +	uint32_t reg2 = NV_READ(pll_reg + 8); + +	NV50_DEBUG("\n"); + +	NV_WRITE(pll_reg, NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1_CONNECTED | 0x10000011); + +	/* The other bits are typically empty, but let's be on the safe side. */ +	reg1 &= 0xff00ff00; +	reg2 &= 0x8000ff00; + +	if (!nv50_crtc_calc_clock(crtc, &N1, &N2, &M1, &M2, &log2P)) +		return -EINVAL; + +	NV50_DEBUG("N1 %d N2 %d M1 %d M2 %d log2P %d\n", N1, N2, M1, M2, log2P); + +	reg1 |= (M1 << 16) | N1; +	reg2 |= (log2P << 28) | (M2 << 16) | N2; + +	NV_WRITE(pll_reg + 4, reg1); +	NV_WRITE(pll_reg + 8, reg2); + +	return 0; +} + +static int nv50_crtc_set_clock_mode(struct nv50_crtc *crtc) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + +	NV50_DEBUG("\n"); + +	/* This acknowledges a clock request. */ +	NV_WRITE(NV50_PDISPLAY_CRTC_CLK_CLK_CTRL2(crtc->index), 0); + +	return 0; +} + +static int nv50_crtc_destroy(struct nv50_crtc *crtc) +{ +	struct drm_device *dev = crtc->dev; +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_display *display = nv50_get_display(dev); + +	NV50_DEBUG("\n"); + +	if (!display || !crtc) +		return -EINVAL; + +	list_del(&crtc->item); + +	nv50_fb_destroy(crtc); +	nv50_lut_destroy(crtc); +	nv50_cursor_destroy(crtc); + +	kfree(crtc->mode); +	kfree(crtc->native_mode); + +	if (dev_priv->free_crtc) +		dev_priv->free_crtc(crtc); + +	return 0; +} + +int nv50_crtc_create(struct drm_device *dev, int index) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_crtc *crtc = NULL; +	struct nv50_display *display = NULL; +	int rval = 0; + +	NV50_DEBUG("\n"); + +	/* This allows the public layer to do it's thing. */ +	if (dev_priv->alloc_crtc) +		crtc = dev_priv->alloc_crtc(dev); + +	if (!crtc) +		return -ENOMEM; + +	crtc->dev = dev; + +	display = nv50_get_display(dev); +	if (!display) { +		rval = -EINVAL; +		goto out; +	} + +	list_add_tail(&crtc->item, &display->crtcs); + +	crtc->index = index; + +	crtc->mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL); +	crtc->native_mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL); + +	crtc->requested_scaling_mode = SCALE_INVALID; +	crtc->scaling_mode = SCALE_INVALID; + +	if (!crtc->mode || !crtc->native_mode) { +		rval = -ENOMEM; +		goto out; +	} + +	nv50_fb_create(crtc); +	nv50_lut_create(crtc); +	nv50_cursor_create(crtc); + +	/* set function pointers */ +	crtc->validate_mode = nv50_crtc_validate_mode; +	crtc->set_mode = nv50_crtc_set_mode; +	crtc->execute_mode = nv50_crtc_execute_mode; +	crtc->set_fb = nv50_crtc_set_fb; +	crtc->blank = nv50_crtc_blank; +	crtc->set_dither = nv50_crtc_set_dither; +	crtc->set_scale = nv50_crtc_set_scale; +	crtc->set_clock = nv50_crtc_set_clock; +	crtc->set_clock_mode = nv50_crtc_set_clock_mode; +	crtc->destroy = nv50_crtc_destroy; + +	return 0; + +out: +	if (crtc->mode) +		kfree(crtc->mode); +	if (crtc->native_mode) +		kfree(crtc->native_mode); +	if (dev_priv->free_crtc) +		dev_priv->free_crtc(crtc); + +	return rval; +} diff --git a/linux-core/nv50_crtc.h b/linux-core/nv50_crtc.h new file mode 100644 index 00000000..b4b83584 --- /dev/null +++ b/linux-core/nv50_crtc.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 __NV50_CRTC_H__ +#define __NV50_CRTC_H__ + +#include "nv50_display.h" + +struct nv50_cursor; +struct nv50_lut; +struct nv50_fb; + +struct nv50_crtc { +	struct list_head item; + +	struct drm_device *dev; +	int index; +	bool enabled; +	bool blanked; + +	struct nouveau_hw_mode *mode; +	struct nouveau_hw_mode *native_mode; + +	bool use_native_mode; +	bool use_dithering; + +	/* Changing scaling modes requires a modeset sometimes. */ +	/* We need to know the currently active hw mode, as well as the requested one by the user. */ +	int requested_scaling_mode; +	int scaling_mode; + +	struct nv50_cursor *cursor; +	struct nv50_lut *lut; +	struct nv50_fb *fb; + +	int (*validate_mode) (struct nv50_crtc *crtc, struct nouveau_hw_mode *mode); +	int (*set_mode) (struct nv50_crtc *crtc, struct nouveau_hw_mode *mode); +	int (*execute_mode) (struct nv50_crtc *crtc); +	int (*set_fb) (struct nv50_crtc *crtc); +	int (*blank) (struct nv50_crtc *crtc, bool blanked); +	int (*set_dither) (struct nv50_crtc *crtc); +	int (*set_scale) (struct nv50_crtc *crtc); +	int (*set_clock) (struct nv50_crtc *crtc); +	int (*set_clock_mode) (struct nv50_crtc *crtc); +	int (*destroy) (struct nv50_crtc *crtc); +}; + +int nv50_crtc_create(struct drm_device *dev, int index); + +#endif /* __NV50_CRTC_H__ */ diff --git a/linux-core/nv50_cursor.c b/linux-core/nv50_cursor.c new file mode 100644 index 00000000..091c94fe --- /dev/null +++ b/linux-core/nv50_cursor.c @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 "nv50_cursor.h" +#include "nv50_crtc.h" +#include "nv50_display.h" + +static int nv50_cursor_enable(struct nv50_crtc *crtc) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + +	NV50_DEBUG("\n"); + +	NV_WRITE(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index), 0x2000); +	while(NV_READ(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index)) & NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_MASK); + +	NV_WRITE(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index), NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON); +	while((NV_READ(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index)) & NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_MASK) +		!= NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE); + +	crtc->cursor->enabled = true; + +	return 0; +} + +static int nv50_cursor_disable(struct nv50_crtc *crtc) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + +	NV50_DEBUG("\n"); + +	NV_WRITE(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index), 0); +	while(NV_READ(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index)) & NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_MASK); + +	crtc->cursor->enabled = false; + +	return 0; +} + +/* Calling update or changing the stored cursor state is left to the higher level ioctl's. */ +static int nv50_cursor_show(struct nv50_crtc *crtc) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; +	uint32_t offset = crtc->index * 0x400; + +	NV50_DEBUG("\n"); + +	/* Better not show the cursor when we have none. */ +	/* TODO: is cursor offset actually set? */ +	if (!crtc->cursor->block) { +		DRM_ERROR("No cursor available on crtc %d\n", crtc->index); +		return -EINVAL; +	} + +	OUT_MODE(NV50_CRTC0_CURSOR_CTRL + offset, NV50_CRTC0_CURSOR_CTRL_SHOW); + +	return 0; +} + +static int nv50_cursor_hide(struct nv50_crtc *crtc) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; +	uint32_t offset = crtc->index * 0x400; + +	NV50_DEBUG("\n"); + +	OUT_MODE(NV50_CRTC0_CURSOR_CTRL + offset, NV50_CRTC0_CURSOR_CTRL_HIDE); + +	return 0; +} + +static int nv50_cursor_set_pos(struct nv50_crtc *crtc, int x, int y) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + +	NV_WRITE(NV50_HW_CURSOR_POS(crtc->index), ((y & 0xFFFF) << 16) | (x & 0xFFFF)); +	/* Needed to make the cursor move. */ +	NV_WRITE(NV50_HW_CURSOR_POS_CTRL(crtc->index), 0); + +	return 0; +} + +static int nv50_cursor_set_offset(struct nv50_crtc *crtc) +{ +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + +	NV50_DEBUG("\n"); + +	if (crtc->cursor->block) { +		OUT_MODE(NV50_CRTC0_CURSOR_OFFSET + crtc->index * 0x400, crtc->cursor->block->start >> 8); +	} else { +		OUT_MODE(NV50_CRTC0_CURSOR_OFFSET + crtc->index * 0x400, 0); +	} + +	return 0; +} + +static int nv50_cursor_set_bo(struct nv50_crtc *crtc, drm_handle_t handle) +{ +	struct mem_block *block = NULL; +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; + +	NV50_DEBUG("\n"); + +	block = find_block_by_handle(dev_priv->fb_heap, handle); + +	if (block) { +		bool first_time = false; +		if (!crtc->cursor->block) +			first_time = true; + +		crtc->cursor->block = block; + +		/* set the cursor offset cursor */ +		if (first_time) { +			crtc->cursor->set_offset(crtc); +			if (crtc->cursor->visible) +				crtc->cursor->show(crtc); +		} +	} else { +		DRM_ERROR("Unable to find cursor bo with handle 0x%X\n", handle); +		return -EINVAL; +	} + +	return 0; +} + +int nv50_cursor_create(struct nv50_crtc *crtc) +{ +	NV50_DEBUG("\n"); + +	if (!crtc) +		return -EINVAL; + +	crtc->cursor = kzalloc(sizeof(struct nv50_cursor), GFP_KERNEL); +	if (!crtc->cursor) +		return -ENOMEM; + +	/* function pointers */ +	crtc->cursor->show = nv50_cursor_show; +	crtc->cursor->hide = nv50_cursor_hide; +	crtc->cursor->set_pos = nv50_cursor_set_pos; +	crtc->cursor->set_offset = nv50_cursor_set_offset; +	crtc->cursor->set_bo = nv50_cursor_set_bo; +	crtc->cursor->enable = nv50_cursor_enable; +	crtc->cursor->disable = nv50_cursor_disable; + +	return 0; +} + +int nv50_cursor_destroy(struct nv50_crtc *crtc) +{ +	int rval = 0; + +	NV50_DEBUG("\n"); + +	if (!crtc) +		return -EINVAL; + +	if (crtc->cursor->enabled) { +		rval = crtc->cursor->disable(crtc); +		if (rval != 0) +			return rval; +	} + +	kfree(crtc->cursor); +	crtc->cursor = NULL; + +	return 0; +} diff --git a/linux-core/nv50_cursor.h b/linux-core/nv50_cursor.h new file mode 100644 index 00000000..4fd0d39a --- /dev/null +++ b/linux-core/nv50_cursor.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 __NV50_CURSOR_H__ +#define __NV50_CURSOR_H__ + +#include "nv50_display.h" + +struct nv50_crtc; + +struct nv50_cursor { +	struct mem_block *block; +	int x, y; +	bool visible; +	bool enabled; + +	int (*show) (struct nv50_crtc *crtc); +	int (*hide) (struct nv50_crtc *crtc); +	int (*set_pos) (struct nv50_crtc *crtc, int x, int y); +	int (*set_offset) (struct nv50_crtc *crtc); +	int (*set_bo) (struct nv50_crtc *crtc, drm_handle_t handle); +	int (*enable) (struct nv50_crtc *crtc); +	int (*disable) (struct nv50_crtc *crtc); +}; + +int nv50_cursor_create(struct nv50_crtc *crtc); +int nv50_cursor_destroy(struct nv50_crtc *crtc); + +#endif /* __NV50_CURSOR_H__ */ diff --git a/linux-core/nv50_dac.c b/linux-core/nv50_dac.c new file mode 100644 index 00000000..5dddb469 --- /dev/null +++ b/linux-core/nv50_dac.c @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 "nv50_output.h" + +static int nv50_dac_validate_mode(struct nv50_output *output, struct nouveau_hw_mode *mode) +{ +	NV50_DEBUG("\n"); + +	if (mode->clock > 400000)  +		return MODE_CLOCK_HIGH; + +	if (mode->clock < 25000) +		return MODE_CLOCK_LOW; + +	return MODE_OK; +} + +static int nv50_dac_execute_mode(struct nv50_output *output, bool disconnect) +{ +	struct drm_nouveau_private *dev_priv = output->dev->dev_private; +	struct nv50_crtc *crtc = output->crtc; +	struct nouveau_hw_mode *desired_mode = NULL; + +	uint32_t offset = nv50_output_or_offset(output) * 0x80; + +	uint32_t mode_ctl = NV50_DAC_MODE_CTRL_OFF; +	uint32_t mode_ctl2 = 0; + +	NV50_DEBUG("or %d\n", nv50_output_or_offset(output)); + +	if (disconnect) { +		NV50_DEBUG("Disconnecting DAC\n"); +		OUT_MODE(NV50_DAC0_MODE_CTRL + offset, mode_ctl); +		return 0; +	} + +	desired_mode = (crtc->use_native_mode ? crtc->native_mode : +									crtc->mode); + +	if (crtc->index == 1) +		mode_ctl |= NV50_DAC_MODE_CTRL_CRTC1; +	else +		mode_ctl |= NV50_DAC_MODE_CTRL_CRTC0; + +	/* Lacking a working tv-out, this is not a 100% sure. */ +	if (output->type == OUTPUT_DAC) { +		mode_ctl |= 0x40; +	} else if (output->type == OUTPUT_TV) { +		mode_ctl |= 0x100; +	} + +	if (desired_mode->flags & DRM_MODE_FLAG_NHSYNC) +		mode_ctl2 |= NV50_DAC_MODE_CTRL2_NHSYNC; + +	if (desired_mode->flags & DRM_MODE_FLAG_NVSYNC) +		mode_ctl2 |= NV50_DAC_MODE_CTRL2_NVSYNC; + +	OUT_MODE(NV50_DAC0_MODE_CTRL + offset, mode_ctl); +	OUT_MODE(NV50_DAC0_MODE_CTRL2 + offset, mode_ctl2); + +	return 0; +} + +static int nv50_dac_set_clock_mode(struct nv50_output *output) +{ +	struct drm_nouveau_private *dev_priv = output->dev->dev_private; + +	NV50_DEBUG("or %d\n", nv50_output_or_offset(output)); + +	NV_WRITE(NV50_PDISPLAY_DAC_CLK_CLK_CTRL2(nv50_output_or_offset(output)),  0); + +	return 0; +} + +static int nv50_dac_set_power_mode(struct nv50_output *output, int mode) +{ +	struct drm_nouveau_private *dev_priv = output->dev->dev_private; +	uint32_t val; +	int or = nv50_output_or_offset(output); + +	NV50_DEBUG("or %d\n", or); + +	/* wait for it to be done */ +	while (NV_READ(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(or)) & NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_PENDING); + +	val = NV_READ(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(or)) & ~0x7F; + +	if (mode != DRM_MODE_DPMS_ON) +		val |= NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_BLANKED; + +	switch (mode) { +		case DRM_MODE_DPMS_STANDBY: +			val |= NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_HSYNC_OFF; +			break; +		case DRM_MODE_DPMS_SUSPEND: +			val |= NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_VSYNC_OFF; +			break; +		case DRM_MODE_DPMS_OFF: +			val |= NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_OFF; +			val |= NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_HSYNC_OFF; +			val |= NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_VSYNC_OFF; +			break; +		default: +			break; +	} + +	NV_WRITE(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(or), val | NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_PENDING); + +	return 0; +} + +static int nv50_dac_detect(struct nv50_output *output) +{ +	struct drm_nouveau_private *dev_priv = output->dev->dev_private; +	int or = nv50_output_or_offset(output); +	bool present = 0; +	uint32_t dpms_state, load_pattern, load_state; + +	NV_WRITE(NV50_PDISPLAY_DAC_REGS_CLK_CTRL1(or), 0x00000001); +	dpms_state = NV_READ(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(or)); + +	NV_WRITE(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(or), 0x00150000 | NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_PENDING); +	while (NV_READ(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(or)) & NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_PENDING); + +	/* Use bios provided value if possible. */ +	if (dev_priv->bios.dactestval) { +		load_pattern = dev_priv->bios.dactestval; +		NV50_DEBUG("Using bios provided load_pattern of %d\n", load_pattern); +	} else { +		load_pattern = 340; +		NV50_DEBUG("Using default load_pattern of %d\n", load_pattern); +	} + +	NV_WRITE(NV50_PDISPLAY_DAC_REGS_LOAD_CTRL(or), NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_ACTIVE | load_pattern); +	udelay(10000); /* give it some time to process */ +	load_state = NV_READ(NV50_PDISPLAY_DAC_REGS_LOAD_CTRL(or)); + +	NV_WRITE(NV50_PDISPLAY_DAC_REGS_LOAD_CTRL(or), 0); +	NV_WRITE(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(or), dpms_state); + +	if ((load_state & NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_PRESENT) == NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_PRESENT) +		present = 1; + +	if (present) +		NV50_DEBUG("Load was detected on output with or %d\n", or); +	else +		NV50_DEBUG("Load was not detected on output with or %d\n", or); + +	return present; +} + +static int nv50_dac_destroy(struct nv50_output *output) +{ +	struct drm_device *dev = output->dev; +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_display *display = nv50_get_display(dev); + +	NV50_DEBUG("\n"); + +	if (!display || !output) +		return -EINVAL; + +	list_del(&output->item); + +	kfree(output->native_mode); +	if (dev_priv->free_output) +		dev_priv->free_output(output); + +	return 0; +} + +int nv50_dac_create(struct drm_device *dev, int dcb_entry) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_output *output = NULL; +	struct nv50_display *display = NULL; +	struct dcb_entry *entry = NULL; +	int rval = 0; + +	NV50_DEBUG("\n"); + +	/* This allows the public layer to do it's thing. */ +	if (dev_priv->alloc_output) +		output = dev_priv->alloc_output(dev); + +	if (!output) +		return -ENOMEM; + +	output->dev = dev; + +	display = nv50_get_display(dev); +	if (!display) { +		rval = -EINVAL; +		goto out; +	} + +	entry = &dev_priv->dcb_table.entry[dcb_entry]; +	if (!entry) { +		rval = -EINVAL; +		goto out; +	} + +	switch (entry->type) { +		case DCB_OUTPUT_ANALOG: +			output->type = OUTPUT_DAC; +			DRM_INFO("Detected a DAC output\n"); +			break; +		default: +			rval = -EINVAL; +			goto out; +	} + +	output->dcb_entry = dcb_entry; +	output->bus = entry->bus; + +	list_add_tail(&output->item, &display->outputs); + +	output->native_mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL); +	if (!output->native_mode) { +		rval = -ENOMEM; +		goto out; +	} + +	/* Set function pointers. */ +	output->validate_mode = nv50_dac_validate_mode; +	output->execute_mode = nv50_dac_execute_mode; +	output->set_clock_mode = nv50_dac_set_clock_mode; +	output->set_power_mode = nv50_dac_set_power_mode; +	output->detect = nv50_dac_detect; +	output->destroy = nv50_dac_destroy; + +	return 0; + +out: +	if (output->native_mode) +		kfree(output->native_mode); +	if (dev_priv->free_output) +		dev_priv->free_output(output); +	return rval; +} + diff --git a/linux-core/nv50_display.c b/linux-core/nv50_display.c new file mode 100644 index 00000000..eeaa0e68 --- /dev/null +++ b/linux-core/nv50_display.c @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 "nv50_display.h" +#include "nv50_crtc.h" +#include "nv50_output.h" +#include "nv50_connector.h" + +static int nv50_display_pre_init(struct nv50_display *display) +{ +	struct drm_device *dev = display->dev; +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	int i; +	uint32_t ram_amount; + +	NV50_DEBUG("\n"); + +	NV_WRITE(0x00610184, NV_READ(0x00614004)); +	/* +	 * I think the 0x006101XX range is some kind of main control area that enables things. +	 */ +	/* CRTC? */ +	NV_WRITE(0x00610190 + 0 * 0x10, NV_READ(0x00616100 + 0 * 0x800)); +	NV_WRITE(0x00610190 + 1 * 0x10, NV_READ(0x00616100 + 1 * 0x800)); +	NV_WRITE(0x00610194 + 0 * 0x10, NV_READ(0x00616104 + 0 * 0x800)); +	NV_WRITE(0x00610194 + 1 * 0x10, NV_READ(0x00616104 + 1 * 0x800)); +	NV_WRITE(0x00610198 + 0 * 0x10, NV_READ(0x00616108 + 0 * 0x800)); +	NV_WRITE(0x00610198 + 1 * 0x10, NV_READ(0x00616108 + 1 * 0x800)); +	NV_WRITE(0x0061019c + 0 * 0x10, NV_READ(0x0061610c + 0 * 0x800)); +	NV_WRITE(0x0061019c + 1 * 0x10, NV_READ(0x0061610c + 1 * 0x800)); +	/* DAC */ +	NV_WRITE(0x006101d0 + 0 * 0x4, NV_READ(0x0061a000 + 0 * 0x800)); +	NV_WRITE(0x006101d0 + 1 * 0x4, NV_READ(0x0061a000 + 1 * 0x800)); +	NV_WRITE(0x006101d0 + 2 * 0x4, NV_READ(0x0061a000 + 2 * 0x800)); +	/* SOR */ +	NV_WRITE(0x006101e0 + 0 * 0x4, NV_READ(0x0061c000 + 0 * 0x800)); +	NV_WRITE(0x006101e0 + 1 * 0x4, NV_READ(0x0061c000 + 1 * 0x800)); +	/* Something not yet in use, tv-out maybe. */ +	NV_WRITE(0x006101f0 + 0 * 0x4, NV_READ(0x0061e000 + 0 * 0x800)); +	NV_WRITE(0x006101f0 + 1 * 0x4, NV_READ(0x0061e000 + 1 * 0x800)); +	NV_WRITE(0x006101f0 + 2 * 0x4, NV_READ(0x0061e000 + 2 * 0x800)); + +	for (i = 0; i < 3; i++) { +		NV_WRITE(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(i), 0x00550000 | NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_PENDING); +		NV_WRITE(NV50_PDISPLAY_DAC_REGS_CLK_CTRL1(i), 0x00000001); +	} + +	/* This used to be in crtc unblank, but seems out of place there. */ +	NV_WRITE(NV50_PDISPLAY_UNK_380, 0); +	/* RAM is clamped to 256 MiB. */ +	ram_amount = nouveau_mem_fb_amount(display->dev); +	NV50_DEBUG("ram_amount %d\n", ram_amount); +	if (ram_amount > 256*1024*1024) +		ram_amount = 256*1024*1024; +	NV_WRITE(NV50_PDISPLAY_RAM_AMOUNT, ram_amount - 1); +	NV_WRITE(NV50_PDISPLAY_UNK_388, 0x150000); +	NV_WRITE(NV50_PDISPLAY_UNK_38C, 0); + +	display->preinit_done = true; + +	return 0; +} + +static int nv50_display_init(struct nv50_display *display) +{ +	struct drm_device *dev = display->dev; +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	uint32_t val; + +	NV50_DEBUG("\n"); + +	/* The precise purpose is unknown, i suspect it has something to do with text mode. */ +	if (NV_READ(NV50_PDISPLAY_SUPERVISOR) & 0x100) { +		NV_WRITE(NV50_PDISPLAY_SUPERVISOR, 0x100); +		NV_WRITE(0x006194e8, NV_READ(0x006194e8) & ~1); +		while (NV_READ(0x006194e8) & 2); +	} + +	/* taken from nv bug #12637 */ +	NV_WRITE(NV50_PDISPLAY_UNK200_CTRL, 0x2b00); +	do { +		val = NV_READ(NV50_PDISPLAY_UNK200_CTRL); +		if ((val & 0x9f0000) == 0x20000) +			NV_WRITE(NV50_PDISPLAY_UNK200_CTRL, val | 0x800000); + +		if ((val & 0x3f0000) == 0x30000) +			NV_WRITE(NV50_PDISPLAY_UNK200_CTRL, val | 0x200000); +	} while (val & 0x1e0000); + +	NV_WRITE(NV50_PDISPLAY_CTRL_STATE, NV50_PDISPLAY_CTRL_STATE_ENABLE); +	NV_WRITE(NV50_PDISPLAY_UNK200_CTRL, 0x1000b03); +	while (!(NV_READ(NV50_PDISPLAY_UNK200_CTRL) & 0x40000000)); + +	/* For the moment this is just a wrapper, which should be replaced with a real fifo at some point. */ +	OUT_MODE(NV50_UNK84, 0); +	OUT_MODE(NV50_UNK88, 0); +	OUT_MODE(NV50_CRTC0_BLANK_CTRL, NV50_CRTC0_BLANK_CTRL_BLANK); +	OUT_MODE(NV50_CRTC0_UNK800, 0); +	OUT_MODE(NV50_CRTC0_DISPLAY_START, 0); +	OUT_MODE(NV50_CRTC0_UNK82C, 0); + +	/* enable clock change interrupts. */ +	NV_WRITE(NV50_PDISPLAY_SUPERVISOR_INTR, NV_READ(NV50_PDISPLAY_SUPERVISOR_INTR) | 0x70); + +	display->init_done = true; + +	return 0; +} + +static int nv50_display_disable(struct nv50_display *display) +{ +	struct drm_device *dev = display->dev; +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_crtc *crtc = NULL; +	int i; + +	NV50_DEBUG("\n"); + +	list_for_each_entry(crtc, &display->crtcs, item) { +		crtc->blank(crtc, true); +	} + +	display->update(display); + +	/* Almost like ack'ing a vblank interrupt, maybe in the spirit of cleaning up? */ +	list_for_each_entry(crtc, &display->crtcs, item) { +		if (crtc->enabled) { +			uint32_t mask; + +			if (crtc->index == 1) +				mask = NV50_PDISPLAY_SUPERVISOR_CRTC1; +			else +				mask = NV50_PDISPLAY_SUPERVISOR_CRTC0; + +			NV_WRITE(NV50_PDISPLAY_SUPERVISOR, mask); +			while (!(NV_READ(NV50_PDISPLAY_SUPERVISOR) & mask)); +		} +	} + +	NV_WRITE(NV50_PDISPLAY_UNK200_CTRL, 0); +	NV_WRITE(NV50_PDISPLAY_CTRL_STATE, 0); +	while ((NV_READ(NV50_PDISPLAY_UNK200_CTRL) & 0x1e0000) != 0); + +	for (i = 0; i < 2; i++) { +		while (NV_READ(NV50_PDISPLAY_SOR_REGS_DPMS_STATE(i)) & NV50_PDISPLAY_SOR_REGS_DPMS_STATE_WAIT); +	} + +	/* disable clock change interrupts. */ +	NV_WRITE(NV50_PDISPLAY_SUPERVISOR_INTR, NV_READ(NV50_PDISPLAY_SUPERVISOR_INTR) & ~0x70); + +	display->init_done = false; + +	return 0; +} + +static int nv50_display_update(struct nv50_display *display) +{ +	struct drm_device *dev = display->dev; +	struct drm_nouveau_private *dev_priv = dev->dev_private; + +	NV50_DEBUG("\n"); + +	OUT_MODE(NV50_UPDATE_DISPLAY, 0); + +	return 0; +} + +int nv50_display_create(struct drm_device *dev) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_display *display = kzalloc(sizeof(struct nv50_display), GFP_KERNEL); +	int i, type, output_index, bus; +	/* DAC0, DAC1, DAC2, SOR0, SOR1*/ +	int or_counter[5] = {0, 0, 0, 0, 0}; +	int i2c_index[5] = {0, 0, 0, 0, 0}; +	uint32_t bus_mask = 0; +	uint32_t bus_digital = 0, bus_analog = 0; + +	NV50_DEBUG("\n"); + +	if (!display) +		return -ENOMEM; + +	INIT_LIST_HEAD(&display->crtcs); +	INIT_LIST_HEAD(&display->outputs); +	INIT_LIST_HEAD(&display->connectors); + +	dev_priv->display_priv = display; + +	for (i = 0; i < 2; i++) { +		nv50_crtc_create(dev, i); +	} + +	/* we setup the outputs up from the BIOS table */ +	for (i = 0 ; i < dev_priv->dcb_table.entries; i++) { +		type = dev_priv->dcb_table.entry[i].type; +		output_index = ffs(dev_priv->dcb_table.entry[i].or) - 1; +		bus = dev_priv->dcb_table.entry[i].bus; + +		switch (type) { +			case DCB_OUTPUT_TMDS: +			case DCB_OUTPUT_LVDS: +				or_counter[output_index + 3] += 1; +				i2c_index[output_index + 3] = dev_priv->dcb_table.entry[i].i2c_index; +				bus_digital |= (1 << bus); +				nv50_sor_create(dev, i); +				break; +			case DCB_OUTPUT_ANALOG: +				or_counter[output_index] += 1; +				i2c_index[output_index] = dev_priv->dcb_table.entry[i].i2c_index; +				bus_analog |= (1 << bus); +				nv50_dac_create(dev, i); +				break; +			default: +				break; +		} + +	} + +	/* setup the connectors based on the output tables. */ +	for (i = 0 ; i < dev_priv->dcb_table.entries; i++) { +		int connector_type = 0; +		type = dev_priv->dcb_table.entry[i].type; +		bus = dev_priv->dcb_table.entry[i].bus; + +		/* already done? */ +		if (bus_mask & (1 << bus)) +			continue; + +		/* only do it for supported outputs */ +		if (type != DCB_OUTPUT_ANALOG && type != DCB_OUTPUT_TMDS +			&& type != DCB_OUTPUT_LVDS) +			continue; + +		switch (type) { +			case DCB_OUTPUT_TMDS: +			case DCB_OUTPUT_ANALOG: +				if ((bus_digital & (1 << bus)) && (bus_analog & (1 << bus))) +					connector_type = CONNECTOR_DVI_I; +				else if (bus_digital & (1 << bus)) +					connector_type = CONNECTOR_DVI_D; +				else if (bus_analog & (1 << bus)) +					connector_type = CONNECTOR_VGA; +				break; +			case DCB_OUTPUT_LVDS: +				connector_type = CONNECTOR_LVDS; +				break; +			default: +				connector_type = CONNECTOR_UNKNOWN; +				break; +		} + +		if (connector_type == CONNECTOR_UNKNOWN) +			continue; + +		nv50_connector_create(dev, bus, dev_priv->dcb_table.entry[i].i2c_index, connector_type); + +		bus_mask |= (1 << bus); +	} + +	display->dev = dev; + +	/* function pointers */ +	display->init = nv50_display_init; +	display->pre_init = nv50_display_pre_init; +	display->disable = nv50_display_disable; +	display->update = nv50_display_update; + +	return 0; +} + +int nv50_display_destroy(struct drm_device *dev) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_display *display = nv50_get_display(dev); +	struct nv50_crtc *crtc = NULL; +	struct nv50_output *output = NULL; +	struct nv50_connector *connector = NULL; + +	NV50_DEBUG("\n"); + +	if (display->init_done) +		display->disable(display); + +	list_for_each_entry(connector, &display->connectors, item) { +		connector->destroy(connector); +	} + +	list_for_each_entry(output, &display->outputs, item) { +		output->destroy(output); +	} + +	list_for_each_entry(crtc, &display->crtcs, item) { +		crtc->destroy(crtc); +	} + +	kfree(display); +	dev_priv->display_priv = NULL; + +	return 0; +} + +/* This can be replaced with a real fifo in the future. */ +void nv50_display_command(struct drm_nouveau_private *dev_priv, uint32_t mthd, uint32_t val) +{ +	uint32_t counter = 0; + +#if 1 +	DRM_INFO("mthd 0x%03X val 0x%08X\n", mthd, val); +#endif + +	NV_WRITE(NV50_PDISPLAY_CTRL_VAL, val); +	NV_WRITE(NV50_PDISPLAY_CTRL_STATE, NV50_PDISPLAY_CTRL_STATE_PENDING | 0x10000 | mthd | NV50_PDISPLAY_CTRL_STATE_ENABLE); + +	while (NV_READ(NV50_PDISPLAY_CTRL_STATE) & NV50_PDISPLAY_CTRL_STATE_PENDING) { +		counter++; +		if (counter > 1000000) { +			DRM_ERROR("You probably need a reboot now\n"); +			break; +		} +		udelay(1); +	} +} + +struct nv50_display *nv50_get_display(struct drm_device *dev) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; + +	return (struct nv50_display *) dev_priv->display_priv; +} diff --git a/linux-core/nv50_display.h b/linux-core/nv50_display.h new file mode 100644 index 00000000..3c2ee1c9 --- /dev/null +++ b/linux-core/nv50_display.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 __NV50_DISPLAY_H__ +#define __NV50_DISPLAY_H__ + +#include "drmP.h" +#include "drm.h" +#include "nouveau_dma.h" +#include "nouveau_drv.h" +#include "nouveau_reg.h" +#include "nv50_display_commands.h" + +/* for convience, so you can see through the trees. */ +#define NV50_DEBUG DRM_ERROR + +struct nouveau_hw_mode { +	unsigned int clock; +	unsigned short hdisplay, hblank_start, hsync_start, hsync_end, hblank_end, htotal; +	unsigned short vdisplay, vblank_start, vsync_start, vsync_end, vblank_end, vtotal; + +	unsigned int flags; +}; + +struct nv50_crtc; +struct nv50_output; +struct nv50_connector; + +struct nv50_display { +	struct drm_device *dev; + +	bool preinit_done; +	bool init_done; + +	int last_crtc; /* crtc used for last mode set */ + +	int (*pre_init) (struct nv50_display *display); +	int (*init) (struct nv50_display *display); +	int (*disable) (struct nv50_display *display); +	int (*update) (struct nv50_display *display); + +	struct list_head crtcs; +	struct list_head outputs; +	struct list_head connectors; +}; + +enum scaling_modes { +	SCALE_NON_GPU, +	SCALE_FULLSCREEN, +	SCALE_ASPECT, +	SCALE_NOSCALE, +	SCALE_INVALID +}; + +void nv50_display_command(struct drm_nouveau_private *dev_priv, uint32_t mthd, uint32_t val); +struct nv50_display *nv50_get_display(struct drm_device *dev); +int nv50_display_create(struct drm_device *dev); +int nv50_display_destroy(struct drm_device *dev); + +#endif /* __NV50_DISPLAY_H__ */ diff --git a/linux-core/nv50_display_commands.h b/linux-core/nv50_display_commands.h new file mode 100644 index 00000000..97b3d3c1 --- /dev/null +++ b/linux-core/nv50_display_commands.h @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +/* copied from ddx definitions, until rules-ng can handle this */ + +#define NV50_UPDATE_DISPLAY		0x80 +#define NV50_UNK84				0x84 +#define NV50_UNK88				0x88 + +#define NV50_DAC0_MODE_CTRL		0x400 +	#define NV50_DAC_MODE_CTRL_OFF			(0 << 0) +	#define NV50_DAC_MODE_CTRL_CRTC0			(1 << 0) +	#define NV50_DAC_MODE_CTRL_CRTC1			(1 << 1) +#define NV50_DAC1_MODE_CTRL		0x480 +#define NV50_DAC2_MODE_CTRL		0x500 + +#define NV50_DAC0_MODE_CTRL2		0x404 +	#define NV50_DAC_MODE_CTRL2_NHSYNC			(1 << 0) +	#define NV50_DAC_MODE_CTRL2_NVSYNC			(2 << 0) +#define NV50_DAC1_MODE_CTRL2		0x484 +#define NV50_DAC2_MODE_CTRL2		0x504 + +#define NV50_SOR0_MODE_CTRL		0x600 +	#define NV50_SOR_MODE_CTRL_OFF			(0 << 0) +	#define NV50_SOR_MODE_CTRL_CRTC0			(1 << 0) +	#define NV50_SOR_MODE_CTRL_CRTC1			(1 << 1) +	#define NV50_SOR_MODE_CTRL_LVDS			(0 << 8) +	#define NV50_SOR_MODE_CTRL_TMDS			(1 << 8) +	#define NV50_SOR_MODE_CTRL_TMDS_DUAL_LINK	(4 << 8) +	#define NV50_SOR_MODE_CTRL_NHSYNC			(1 << 12) +	#define NV50_SOR_MODE_CTRL_NVSYNC			(2 << 12) +#define NV50_SOR1_MODE_CTRL		0x640 + +#define NV50_CRTC0_UNK800			0x800 +#define NV50_CRTC0_CLOCK			0x804 +#define NV50_CRTC0_INTERLACE		0x808 + +/* 0x810 is a reasonable guess, nothing more. */ +#define NV50_CRTC0_DISPLAY_START				0x810 +#define NV50_CRTC0_DISPLAY_TOTAL				0x814 +#define NV50_CRTC0_SYNC_DURATION				0x818 +#define NV50_CRTC0_SYNC_START_TO_BLANK_END		0x81C +#define NV50_CRTC0_MODE_UNK1					0x820 +#define NV50_CRTC0_MODE_UNK2					0x824 + +#define NV50_CRTC0_UNK82C						0x82C + +/* You can't have a palette in 8 bit mode (=OFF) */ +#define NV50_CRTC0_CLUT_MODE		0x840 +	#define NV50_CRTC0_CLUT_MODE_BLANK		0x00000000 +	#define NV50_CRTC0_CLUT_MODE_OFF		0x80000000 +	#define NV50_CRTC0_CLUT_MODE_ON		0xC0000000 +#define NV50_CRTC0_CLUT_OFFSET		0x844 + +/* Anyone know what part of the chip is triggered here precisely? */ +#define NV84_CRTC0_BLANK_UNK1		0x85C +	#define NV84_CRTC0_BLANK_UNK1_BLANK	0x0 +	#define NV84_CRTC0_BLANK_UNK1_UNBLANK	0x1 + +#define NV50_CRTC0_FB_OFFSET		0x860 + +#define NV50_CRTC0_FB_SIZE			0x868 +#define NV50_CRTC0_FB_PITCH		0x86C + +#define NV50_CRTC0_DEPTH			0x870 +	#define NV50_CRTC0_DEPTH_8BPP		0x1E00 +	#define NV50_CRTC0_DEPTH_15BPP	0xE900 +	#define NV50_CRTC0_DEPTH_16BPP	0xE800 +	#define NV50_CRTC0_DEPTH_24BPP	0xCF00 + +/* I'm openminded to better interpretations. */ +/* This is an educated guess. */ +/* NV50 has RAMDAC and TMDS offchip, so it's unlikely to be that. */ +#define NV50_CRTC0_BLANK_CTRL		0x874 +	#define NV50_CRTC0_BLANK_CTRL_BLANK	0x0 +	#define NV50_CRTC0_BLANK_CTRL_UNBLANK	0x1 + +#define NV50_CRTC0_CURSOR_CTRL	0x880 +	#define NV50_CRTC0_CURSOR_CTRL_SHOW	0x85000000 +	#define NV50_CRTC0_CURSOR_CTRL_HIDE	0x05000000 + +#define NV50_CRTC0_CURSOR_OFFSET	0x884 + +/* Anyone know what part of the chip is triggered here precisely? */ +#define NV84_CRTC0_BLANK_UNK2		0x89C +	#define NV84_CRTC0_BLANK_UNK2_BLANK	0x0 +	#define NV84_CRTC0_BLANK_UNK2_UNBLANK	0x1 + +#define NV50_CRTC0_DITHERING_CTRL	0x8A0 +	#define NV50_CRTC0_DITHERING_CTRL_ON	0x11 +	#define NV50_CRTC0_DITHERING_CTRL_OFF	0x0 + +#define NV50_CRTC0_SCALE_CTRL		0x8A4 +	#define	NV50_CRTC0_SCALE_CTRL_SCALER_INACTIVE	(0 << 0) +	/* It doesn't seem to be needed, hence i wonder what it does precisely. */ +	#define	NV50_CRTC0_SCALE_CTRL_SCALER_ACTIVE	(9 << 0) +#define NV50_CRTC0_COLOR_CTRL		0x8A8 +	#define NV50_CRTC_COLOR_CTRL_MODE_COLOR		(4 << 16) + +#define NV50_CRTC0_FB_POS			0x8C0 +#define NV50_CRTC0_REAL_RES		0x8C8 + +/* Added a macro, because the signed stuff can cause you problems very quickly. */ +#define NV50_CRTC0_SCALE_CENTER_OFFSET		0x8D4 +	#define NV50_CRTC_SCALE_CENTER_OFFSET_VAL(x, y) ((((unsigned)y << 16) & 0xFFFF0000) | (((unsigned)x) & 0x0000FFFF)) +/* Both of these are needed, otherwise nothing happens. */ +#define NV50_CRTC0_SCALE_RES1		0x8D8 +#define NV50_CRTC0_SCALE_RES2		0x8DC + +#define NV50_CRTC1_UNK800			0xC00 +#define NV50_CRTC1_CLOCK			0xC04 +#define NV50_CRTC1_INTERLACE		0xC08 + +/* 0xC10 is a reasonable guess, nothing more. */ +#define NV50_CRTC1_DISPLAY_START				0xC10 +#define NV50_CRTC1_DISPLAY_TOTAL				0xC14 +#define NV50_CRTC1_SYNC_DURATION				0xC18 +#define NV50_CRTC1_SYNC_START_TO_BLANK_END		0xC1C +#define NV50_CRTC1_MODE_UNK1					0xC20 +#define NV50_CRTC1_MODE_UNK2					0xC24 + +#define NV50_CRTC1_CLUT_MODE		0xC40 +	#define NV50_CRTC1_CLUT_MODE_BLANK		0x00000000 +	#define NV50_CRTC1_CLUT_MODE_OFF		0x80000000 +	#define NV50_CRTC1_CLUT_MODE_ON		0xC0000000 +#define NV50_CRTC1_CLUT_OFFSET		0xC44 + +/* Anyone know what part of the chip is triggered here precisely? */ +#define NV84_CRTC1_BLANK_UNK1		0xC5C +	#define NV84_CRTC1_BLANK_UNK1_BLANK	0x0 +	#define NV84_CRTC1_BLANK_UNK1_UNBLANK	0x1 + +#define NV50_CRTC1_FB_OFFSET		0xC60 + +#define NV50_CRTC1_FB_SIZE			0xC68 +#define NV50_CRTC1_FB_PITCH		0xC6C + +#define NV50_CRTC1_DEPTH			0xC70 +	#define NV50_CRTC1_DEPTH_8BPP		0x1E00 +	#define NV50_CRTC1_DEPTH_15BPP	0xE900 +	#define NV50_CRTC1_DEPTH_16BPP	0xE800 +	#define NV50_CRTC1_DEPTH_24BPP	0xCF00 + +/* I'm openminded to better interpretations. */ +#define NV50_CRTC1_BLANK_CTRL		0xC74 +	#define NV50_CRTC1_BLANK_CTRL_BLANK	0x0 +	#define NV50_CRTC1_BLANK_CTRL_UNBLANK	0x1 + +#define NV50_CRTC1_CURSOR_CTRL		0xC80 +	#define NV50_CRTC1_CURSOR_CTRL_SHOW	0x85000000 +	#define NV50_CRTC1_CURSOR_CTRL_HIDE	0x05000000 + +#define NV50_CRTC1_CURSOR_OFFSET	0xC84 + +/* Anyone know what part of the chip is triggered here precisely? */ +#define NV84_CRTC1_BLANK_UNK2		0xC9C +	#define NV84_CRTC1_BLANK_UNK2_BLANK	0x0 +	#define NV84_CRTC1_BLANK_UNK2_UNBLANK	0x1 + +#define NV50_CRTC1_DITHERING_CTRL	0xCA0 +	#define NV50_CRTC1_DITHERING_CTRL_ON	0x11 +	#define NV50_CRTC1_DITHERING_CTRL_OFF	0x0 + +#define NV50_CRTC1_SCALE_CTRL		0xCA4 +#define NV50_CRTC1_COLOR_CTRL		0xCA8 + +#define NV50_CRTC1_FB_POS			0xCC0 +#define NV50_CRTC1_REAL_RES		0xCC8 + +#define NV50_CRTC1_SCALE_CENTER_OFFSET		0xCD4 +/* Both of these are needed, otherwise nothing happens. */ +#define NV50_CRTC1_SCALE_RES1		0xCD8 +#define NV50_CRTC1_SCALE_RES2		0xCDC diff --git a/linux-core/nv50_fb.c b/linux-core/nv50_fb.c new file mode 100644 index 00000000..153899da --- /dev/null +++ b/linux-core/nv50_fb.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 "nv50_fb.h" +#include "nv50_lut.h" +#include "nv50_crtc.h" +#include "nv50_display.h" + +static int nv50_fb_bind(struct nv50_crtc *crtc, struct nv50_fb_info *info) +{ +	int rval = 0; + +	NV50_DEBUG("\n"); + +	if (!crtc || !info) { +		DRM_ERROR("crtc %p info %p\n",crtc, info); +		return -EINVAL; +	} + +	if (!info->block || !info->width || !info->height || !info->depth || !info->bpp || !info->pitch) { +		DRM_ERROR("block %p width %d height %d depth %d bpp %d pitch %d\n", info->block, info->width,  +			info->height, info->depth, info->bpp, info->pitch); +		return -EINVAL; +	} + +	crtc->fb->block = info->block; +	crtc->fb->width = info->width; +	crtc->fb->height = info->height; + +	crtc->fb->y = info->x; +	crtc->fb->x = info->y; + +	crtc->fb->depth = info->depth; +	crtc->fb->bpp = info->bpp; + +	crtc->fb->pitch = info->pitch; + +	/* update lut if needed */ +	if (crtc->fb->depth != crtc->lut->depth) { +		int r_size = 0, g_size = 0, b_size = 0; +		uint16_t *r_val, *g_val, *b_val; +		int i; + +		switch (crtc->fb->depth) { +			case 15: +				r_size = 32; +				g_size = 32; +				b_size = 32; +				break; +			case 16: +				r_size = 32; +				g_size = 64; +				b_size = 32; +				break; +			case 24: +			default: +				r_size = 256; +				g_size = 256; +				b_size = 256; +				break; +		} + +		r_val = kmalloc(r_size * sizeof(uint16_t), GFP_KERNEL); +		g_val = kmalloc(g_size * sizeof(uint16_t), GFP_KERNEL); +		b_val = kmalloc(b_size * sizeof(uint16_t), GFP_KERNEL); + +		if (!r_val || !g_val || !b_val) +			return -ENOMEM; + +		/* Set the color indices. */ +		for (i = 0; i < r_size; i++) { +			r_val[i] = i << 8; +		} +		for (i = 0; i < g_size; i++) { +			g_val[i] = i << 8; +		} +		for (i = 0; i < b_size; i++) { +			b_val[i] = i << 8; +		} + +		rval = crtc->lut->set(crtc, r_val, g_val, b_val); + +		/* free before returning */ +		kfree(r_val); +		kfree(g_val); +		kfree(b_val); + +		if (rval != 0) +			return rval; +	} + +	return 0; +} + +int nv50_fb_create(struct nv50_crtc *crtc) +{ +	if (!crtc) +		return -EINVAL; + +	crtc->fb = kzalloc(sizeof(struct nv50_fb), GFP_KERNEL); + +	if (!crtc->fb) +		return -ENOMEM; + +	crtc->fb->bind = nv50_fb_bind; + +	return 0; +} + +int nv50_fb_destroy(struct nv50_crtc *crtc) +{ +	if (!crtc) +		return -EINVAL; + +	kfree(crtc->fb); +	crtc->fb = NULL; + +	return 0; +} diff --git a/linux-core/nv50_fb.h b/linux-core/nv50_fb.h new file mode 100644 index 00000000..3051dc5c --- /dev/null +++ b/linux-core/nv50_fb.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 __NV50_FB_H__ +#define __NV50_FB_H__ + +#include "nv50_display.h" + +struct nv50_crtc; + +struct nv50_fb_info { +	struct mem_block *block; +	int width, height; +	int bpp, depth; +	int pitch; +	int x,y; +}; + +struct nv50_fb { +	struct mem_block *block; +	int width, height; +	int bpp, depth; +	int pitch; + +	int x,y; + +	/* function points */ +	int (*bind) (struct nv50_crtc *crtc, struct nv50_fb_info *info); +}; + +int nv50_fb_create(struct nv50_crtc *crtc); +int nv50_fb_destroy(struct nv50_crtc *crtc); + +#endif /* __NV50_FB_H__ */ diff --git a/linux-core/nv50_fbcon.c b/linux-core/nv50_fbcon.c new file mode 100644 index 00000000..3dd73062 --- /dev/null +++ b/linux-core/nv50_fbcon.c @@ -0,0 +1,627 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/init.h> + +#include "nv50_fbcon.h" + +static int nv50_fbcon_setcolreg(unsigned regno, unsigned red, unsigned green, +			unsigned blue, unsigned transp, +			struct fb_info *info) +{ +	struct nv50_fbcon_par *par = info->par; +	struct drm_device *dev = par->dev; +	struct drm_framebuffer *drm_fb;  + +	list_for_each_entry(drm_fb, &dev->mode_config.fb_kernel_list, filp_head) { +		if (regno > 255) +			return 1; + +		/* TODO: 8 bit support */ +		if (regno < 16) { +			switch (drm_fb->depth) { +			case 15: +				drm_fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) | +					((green & 0xf800) >>  6) | +					((blue & 0xf800) >> 11); +				break; +			case 16: +				drm_fb->pseudo_palette[regno] = (red & 0xf800) | +					((green & 0xfc00) >>  5) | +					((blue  & 0xf800) >> 11); +				break; +			case 24: +			case 32: +				drm_fb->pseudo_palette[regno] = ((red & 0xff00) << 8) | +					(green & 0xff00) | +					((blue  & 0xff00) >> 8); +				break; +			} +		} +	} +	return 0; +} + +static int nv50_fbcon_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ +	struct nv50_fbcon_par *par = info->par; +	struct drm_framebuffer *drm_fb = par->fb; +	int depth; + +	NV50_DEBUG("\n"); + +	if (!var || !drm_fb || !info) { +		DRM_ERROR("No var, drm_fb or info\n"); +	} + +	par->use_preferred_mode = false; + +	if (var->pixclock == -1 || !var->pixclock) { +		DRM_INFO("Using preferred mode.\n"); +		par->use_preferred_mode = true; +	} + +	/* Need to resize the fb object !!! */ +	if (var->xres > drm_fb->width || var->yres > drm_fb->height) { +		DRM_ERROR("Requested width/height is greater than current fb object %dx%d > %dx%d\n", var->xres,var->yres, drm_fb->width, drm_fb->height); +		DRM_ERROR("Need resizing code.\n"); +		return -EINVAL; +	} + +	switch (var->bits_per_pixel) { +	case 16: +		depth = (var->green.length == 6) ? 16 : 15; +		break; +	case 32: +		depth = (var->transp.length > 0) ? 32 : 24; +		break; +	default: +		depth = var->bits_per_pixel; +		break; +	} + +	switch (depth) { +	case 15: +		var->red.offset = 10; +		var->green.offset = 5; +		var->blue.offset = 0; +		var->red.length = 5; +		var->green.length = 5; +		var->blue.length = 5; +		var->transp.length = 1; +		var->transp.offset = 15; +		break; +	case 16: +		var->red.offset = 11; +		var->green.offset = 5; +		var->blue.offset = 0; +		var->red.length = 5; +		var->green.length = 6; +		var->blue.length = 5; +		var->transp.length = 0; +		var->transp.offset = 0; +		break; +	case 24: +		var->red.offset = 16; +		var->green.offset = 8; +		var->blue.offset = 0; +		var->red.length = 8; +		var->green.length = 8; +		var->blue.length = 8; +		var->transp.length = 0; +		var->transp.offset = 0; +		break; +	case 32: +		var->red.offset = 16; +		var->green.offset = 8; +		var->blue.offset = 0; +		var->red.length = 8; +		var->green.length = 8; +		var->blue.length = 8; +		var->transp.length = 8; +		var->transp.offset = 24; +		break; +	default: +		DRM_ERROR("Invalid depth %d\n", depth); +		return -EINVAL;  +	} + +	return 0; +} + +static int nv50_fbcon_set_par(struct fb_info *info) +{ +	struct nv50_fbcon_par *par; +	struct drm_framebuffer *drm_fb; +	struct drm_connector *drm_connector; +	struct drm_crtc *drm_crtc; +	struct fb_var_screeninfo *var; +	struct drm_display_mode *drm_mode = NULL, *t; +	struct drm_device *dev; +	int rval; +	bool crtc_used[2] = {false, false}; + +	NV50_DEBUG("\n"); + +	if (!info) { +		DRM_ERROR("No fb_info\n"); +		return -EINVAL; +	} + +	par = info->par; + +	if (!par) { +		DRM_ERROR("No nv50_fbcon_par\n"); +		return -EINVAL; +	} + +	drm_fb = par->fb; +	var = &info->var; +	dev = par->dev; + +	if (!drm_fb || !var || !dev) { +		DRM_ERROR("No drm_fb, var or dev\n"); +		return -EINVAL; +	} + +	par->use_preferred_mode = false; + +	if (var->pixclock == -1 || !var->pixclock) { +		DRM_INFO("Using preferred mode.\n"); +		par->use_preferred_mode = true; +	} + +	/* FB setup */ +	switch (var->bits_per_pixel) { +	case 16: +		drm_fb->depth = (var->green.length == 6) ? 16 : 15; +		break; +	case 32: +		drm_fb->depth = (var->transp.length > 0) ? 32 : 24; +		break; +	default: +		drm_fb->depth = var->bits_per_pixel; +		break; +	} + +	drm_fb->bits_per_pixel = var->bits_per_pixel; + +	info->fix.line_length = drm_fb->pitch; +	info->fix.smem_len = info->fix.line_length * drm_fb->height; +	/* ignoring 8bpp for the moment */ +	info->fix.visual = FB_VISUAL_TRUECOLOR; + +	info->screen_size = info->fix.smem_len; /* ??? */ + +	/* create a drm mode */ +	if (!par->use_preferred_mode) { +		drm_mode = drm_mode_create(dev); +		drm_mode->hdisplay = var->xres; +		drm_mode->hsync_start = drm_mode->hdisplay + var->right_margin; +		drm_mode->hsync_end = drm_mode->hsync_start + var->hsync_len; +		drm_mode->htotal = drm_mode->hsync_end + var->left_margin; +		drm_mode->vdisplay = var->yres; +		drm_mode->vsync_start = drm_mode->vdisplay + var->lower_margin; +		drm_mode->vsync_end = drm_mode->vsync_start + var->vsync_len; +		drm_mode->vtotal = drm_mode->vsync_end + var->upper_margin; +		drm_mode->clock = PICOS2KHZ(var->pixclock); +		drm_mode->vrefresh = drm_mode_vrefresh(drm_mode); +		drm_mode->flags = 0; +		drm_mode->flags |= var->sync & FB_SYNC_HOR_HIGH_ACT ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; +		drm_mode->flags |= var->sync & FB_SYNC_VERT_HIGH_ACT ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; + +		drm_mode_set_name(drm_mode); +		drm_mode_set_crtcinfo(drm_mode, CRTC_INTERLACE_HALVE_V); +	} + +	/* hook up crtc's */ +	list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) { +		enum drm_connector_status status; +		struct drm_mode_set mode_set; +		int crtc_count = 0; + +		status = drm_connector->funcs->detect(drm_connector); + +		if (status != connector_status_connected) +			continue; + +		memset(&mode_set, 0, sizeof(struct drm_mode_set)); + +		/* set connector */ +		mode_set.num_connectors = 1; +		mode_set.connectors = kzalloc(sizeof(struct drm_connector *), GFP_KERNEL); +		if (!mode_set.connectors) { +			rval = -ENOMEM; +			goto out; +		} +		mode_set.connectors[0] = drm_connector; + +		/* set fb */ +		list_for_each_entry(drm_fb, &dev->mode_config.fb_kernel_list, filp_head) { +			break; /* first entry is the only entry */ +		} +		mode_set.fb = drm_fb; + +		/* set mode */ +		if (par->use_preferred_mode) { +			/* find preferred mode */ +			list_for_each_entry_safe(drm_mode, t, &drm_connector->modes, head) { +				if (drm_mode->type & DRM_MODE_TYPE_PREFERRED) +					break; +			} +		} +		mode_set.mode = drm_mode; + +		/* choose crtc it already has, if possible */ +		if (drm_connector->encoder) { +			struct drm_encoder *drm_encoder = drm_connector->encoder; + +			if (drm_encoder->crtc) { +				list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) { +					if (drm_crtc == drm_encoder->crtc) { +						if (!crtc_used[crtc_count]) /* still available? */ +							mode_set.crtc = drm_crtc; +						break; +					} + +					crtc_count++; +				} +			} +		} + +		/* proceed as planned */ +		if (mode_set.crtc) { +			mode_set.crtc->funcs->set_config(&mode_set); +			crtc_used[crtc_count] = true; +		} + +		if (!mode_set.crtc) { +			crtc_count = 0; /* reset */ + +			/* choose a "random" crtc */ +			list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) { +				if (crtc_used[crtc_count]) { +					crtc_count++; +					continue; +				} + +				/* found a crtc */ +				mode_set.crtc = drm_crtc; + +				break; +			} + +			/* proceed as planned */ +			if (mode_set.crtc) { +				mode_set.crtc->funcs->set_config(&mode_set); +				crtc_used[crtc_count] = true; +			} +		} + +		kfree(mode_set.connectors); +	} + +	return 0; + +out: +	return rval; +} + +static struct fb_ops nv50_fb_ops = { +	.owner = THIS_MODULE, +	//.fb_open = nv50_fb_open, +	//.fb_read = nv50_fb_read, +	//.fb_write = nv50_fb_write, +	//.fb_release = nv50_fb_release, +	//.fb_ioctl = nv50_fb_ioctl, +	.fb_check_var = nv50_fbcon_check_var, +	.fb_set_par = nv50_fbcon_set_par, +	.fb_setcolreg = nv50_fbcon_setcolreg, +	.fb_fillrect = cfb_fillrect, +	.fb_copyarea = cfb_copyarea, +	.fb_imageblit = cfb_imageblit, +	//.fb_pan_display = nv50_fb_pan_display, +}; + +static int nv50_fbcon_initial_config(struct drm_device *dev) +{ +	struct drm_connector *drm_connector; + +	struct drm_framebuffer *drm_fb = NULL; +	struct drm_mode_fb_cmd drm_fb_cmd; +	enum drm_connector_status status; +	uint32_t max_width = 0, max_height = 0, pitch = 0; +	struct mem_block *block; +	struct drm_file *file_priv; +	uint32_t flags; +	int rval = 0; + +	list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) { +		status = drm_connector->funcs->detect(drm_connector); + +		/* find the framebuffer size */ +		if (status == connector_status_connected) { +			struct drm_display_mode *mode, *t; +			list_for_each_entry_safe(mode, t, &drm_connector->modes, head) { +				if (mode->type & DRM_MODE_TYPE_PREFERRED) { +					if (mode->hdisplay > max_width) +						max_width = mode->hdisplay; +					if (mode->vdisplay > max_height) +						max_height = mode->vdisplay; +				} +			} +		} +	} + +	/* allocate framebuffer */ +	file_priv = kzalloc(sizeof(struct drm_file), GFP_KERNEL); +	if (!file_priv) { +		rval = -ENOMEM; +		goto out; +	} + +	pitch = (max_width + 63) & ~63; +	pitch *= 4; /* TODO */ + +	flags = NOUVEAU_MEM_FB | NOUVEAU_MEM_MAPPED; + +	/* Any file_priv should do as it's pointer is used as identification. */ +	block = nouveau_mem_alloc(dev, 0, pitch * max_height, flags, file_priv); +	if (!block) { +		rval = -ENOMEM; +		goto out; +	} + +	memset(&drm_fb_cmd, 0, sizeof(struct drm_mode_fb_cmd)); + +	drm_fb_cmd.width = max_width; +	drm_fb_cmd.height = max_height; +	drm_fb_cmd.pitch = pitch; +	drm_fb_cmd.bpp = 32; /* TODO */ +	drm_fb_cmd.handle = block->map_handle; +	drm_fb_cmd.depth = 24; /* TODO */ + +	drm_fb = dev->mode_config.funcs->fb_create(dev, file_priv, &drm_fb_cmd); +	if (!drm_fb) { +		rval = -EINVAL; +		goto out; +	} + +	list_add(&drm_fb->filp_head, &dev->mode_config.fb_kernel_list); + +	return 0; + +out: +	if (file_priv) +		kfree(file_priv); +	if (drm_fb) +		drm_fb->funcs->destroy(drm_fb); + +	return rval; +} + +/* + * Single framebuffer, ideally operating in clone mode across various connectors. + */ +int nv50_fbcon_init(struct drm_device *dev) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct fb_info *info; +	struct nv50_fbcon_par *par; +	struct device *device = &dev->pdev->dev; +	struct drm_framebuffer *drm_fb; +	struct mem_block *block; +	void __iomem *fb = NULL; +	int rval; + +	rval = nv50_fbcon_initial_config(dev); +	if (rval != 0) { +		DRM_ERROR("nv50_fbcon_initial_config failed\n"); +		return rval; +	} + +	list_for_each_entry(drm_fb, &dev->mode_config.fb_kernel_list, filp_head) { +		break; /* first entry is the only entry */ +	} + +	if (!drm_fb) { +		DRM_ERROR("no drm_fb found\n"); +		return -EINVAL; +	} + +	block = find_block_by_handle(dev_priv->fb_heap, drm_fb->mm_handle); +	if (!block) { +		DRM_ERROR("can't find mem_block\n"); +		return -EINVAL; +	} + +	info = framebuffer_alloc(sizeof(struct nv50_fbcon_par), device); +	if (!info) { +		DRM_ERROR("framebuffer_alloc failed\n"); +		return -EINVAL; +	} + +	par = info->par; + +	strcpy(info->fix.id, "nv50drmfb"); +	info->fix.type = FB_TYPE_PACKED_PIXELS; +	info->fix.visual = FB_VISUAL_TRUECOLOR; +	info->fix.type_aux = 0; +	info->fix.xpanstep = 0; /* 1 is doing it in hw */ +	info->fix.ypanstep = 0; +	info->fix.ywrapstep = 0; +	info->fix.accel = FB_ACCEL_NONE; +	info->fix.type_aux = 0; + +	info->flags = FBINFO_DEFAULT; + +	info->fbops = &nv50_fb_ops; + +	info->fix.line_length = drm_fb->pitch; +	info->fix.smem_start = dev_priv->fb_phys + block->start; +	info->fix.smem_len = info->fix.line_length * drm_fb->height; + +	info->flags = FBINFO_DEFAULT; + +	fb = ioremap(dev_priv->fb_phys + block->start, block->size); +	if (!fb) { +		DRM_ERROR("Unable to ioremap framebuffer\n"); +		return -EINVAL; +	} + + 	info->screen_base = fb; +	info->screen_size = info->fix.smem_len; /* FIXME */ + +	info->pseudo_palette = drm_fb->pseudo_palette; +	info->var.xres_virtual = drm_fb->width; +	info->var.yres_virtual = drm_fb->height; +	info->var.bits_per_pixel = drm_fb->bits_per_pixel; +	info->var.xoffset = 0; +	info->var.yoffset = 0; +	info->var.activate = FB_ACTIVATE_NOW; +	info->var.height = -1; +	info->var.width = -1; + +	/* TODO: improve this */ +	info->var.xres = drm_fb->width; +	info->var.yres = drm_fb->height; + +	info->fix.mmio_start = drm_get_resource_start(dev, 0); +	info->fix.mmio_len = drm_get_resource_len(dev, 0); + +	DRM_DEBUG("fb depth is %d\n", drm_fb->depth); +	DRM_DEBUG("   pitch is %d\n", drm_fb->pitch); + +	switch(drm_fb->depth) { + 	case 15: +		info->var.red.offset = 10; +		info->var.green.offset = 5; +		info->var.blue.offset = 0; +		info->var.red.length = 5; +		info->var.green.length = 5; +		info->var.blue.length = 5; +		info->var.transp.offset = 15; +		info->var.transp.length = 1; +		break; +	case 16: +		info->var.red.offset = 11; +		info->var.green.offset = 5; +		info->var.blue.offset = 0; +		info->var.red.length = 5; +		info->var.green.length = 6; +		info->var.blue.length = 5; +		info->var.transp.offset = 0; + 		break; +	case 24: +		info->var.red.offset = 16; +		info->var.green.offset = 8; +		info->var.blue.offset = 0; +		info->var.red.length = 8; +		info->var.green.length = 8; +		info->var.blue.length = 8; +		info->var.transp.offset = 0; +		info->var.transp.length = 0; +		break; +	case 32: +		info->var.red.offset = 16; +		info->var.green.offset = 8; +		info->var.blue.offset = 0; +		info->var.red.length = 8; +		info->var.green.length = 8; +		info->var.blue.length = 8; +		info->var.transp.offset = 24; +		info->var.transp.length = 8; +		break; +	default: +		break; +	} + +	drm_fb->fbdev = info; +	par->dev = dev; +	par->fb = drm_fb; + +	register_framebuffer(info); + +	DRM_INFO("nv50drmfb initialised\n"); + +	return 0; +} + +int nv50_fbcon_destroy(struct drm_device *dev) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct drm_framebuffer *drm_fb; +	struct fb_info *info; +	struct mem_block *block; +	struct drm_file *file_priv; + +	list_for_each_entry(drm_fb, &dev->mode_config.fb_kernel_list, filp_head) { +		break; /* first entry is the only entry */ +	} + +	if (!drm_fb) { +		DRM_ERROR("No framebuffer to destroy\n"); +		return -EINVAL; +	} + +	info = drm_fb->fbdev; +	if (!info) { +		DRM_ERROR("No fb_info\n"); +		return -EINVAL; +	} + +	unregister_framebuffer(info); + +	block = find_block_by_handle(dev_priv->fb_heap, drm_fb->mm_handle); +	if (!block) { +		DRM_ERROR("can't find mem_block\n"); +		return -EINVAL; +	} + +	/* we need to free this after memory is freed */ +	file_priv = block->file_priv; + +	/* free memory */ +	nouveau_mem_free(dev, block); + +	if (file_priv) { +		kfree(file_priv); +		file_priv = NULL; +	} + +	framebuffer_release(info); + +	return 0; +} diff --git a/linux-core/nv50_fbcon.h b/linux-core/nv50_fbcon.h new file mode 100644 index 00000000..98e7101a --- /dev/null +++ b/linux-core/nv50_fbcon.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 __NV50_FBCON_H__ +#define __NV50_FBCON_H__ + +#include "nv50_kms_wrapper.h" + +struct nv50_fbcon_par { +	struct drm_framebuffer *fb; +	struct drm_device *dev; +	bool use_preferred_mode; +}; + +int nv50_fbcon_init(struct drm_device *dev); +int nv50_fbcon_destroy(struct drm_device *dev); + +#endif /* __NV50_FBCON_H__ */
\ No newline at end of file diff --git a/linux-core/nv50_i2c.c b/linux-core/nv50_i2c.c new file mode 100644 index 00000000..30e317c5 --- /dev/null +++ b/linux-core/nv50_i2c.c @@ -0,0 +1,400 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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. + * + */ + +/* This is largely a clone from xorg i2c functions, as i had serious trouble getting an i2c_bit_algo adaptor running. */ + +#include "nv50_i2c.h" + +static uint32_t nv50_i2c_port(int index) +{ +	uint32_t port = 0; + +	switch (index) { +		case 0: +			port = NV50_PCONNECTOR_I2C_PORT_0; +			break; +		case 1: +			port = NV50_PCONNECTOR_I2C_PORT_1; +			break; +		case 2: +			port = NV50_PCONNECTOR_I2C_PORT_2; +			break; +		case 3: +			port = NV50_PCONNECTOR_I2C_PORT_3; +			break; +		case 4: +			port = NV50_PCONNECTOR_I2C_PORT_4; +			break; +		case 5: +			port = NV50_PCONNECTOR_I2C_PORT_5; +			break; +		default: +			break; +	} + +	if (!port) { +		DRM_ERROR("Invalid i2c port, returning 0.\n"); +		BUG(); +	} + +	return port; +} + +static void nv50_i2c_set_bits(struct nv50_i2c_channel *chan, int clock_high, int data_high) +{ +	struct drm_nouveau_private *dev_priv = chan->dev->dev_private; +	uint32_t port = nv50_i2c_port(chan->index); + +	if (!port) +		return; + +	NV_WRITE(port, 4 | (data_high << 1) | clock_high); +} + +static void nv50_i2c_get_bits(struct nv50_i2c_channel *chan, int *clock_high, int *data_high) +{ +	struct drm_nouveau_private *dev_priv = chan->dev->dev_private; +	uint32_t port = nv50_i2c_port(chan->index); +	uint32_t val; + +	if (!port) +		return; + +	val = NV_READ(port); + +	if (val & 1) +		*clock_high = 1; +	else +		*clock_high = 0; + +	if (val & 2) +		*data_high = 1; +	else +		*data_high = 0; +} + +static bool nv50_i2c_raise_clock(struct nv50_i2c_channel *chan, int data) +{ +	int i, clock; + +	nv50_i2c_set_bits(chan, 1, data); +	udelay(2); + +	for (i = 2200; i > 0; i -= 2) { +		nv50_i2c_get_bits(chan, &clock, &data); +		if (clock) +			return true; +		udelay(2); +	} + +	printk("a timeout occured in nv50_i2c_raise_clock\n"); + +	return false; +} + +static bool nv50_i2c_start(struct nv50_i2c_channel *chan) +{ +	if (!nv50_i2c_raise_clock(chan, 1)) +		return false; + +	nv50_i2c_set_bits(chan, 1, 0); +	udelay(5); + +	nv50_i2c_set_bits(chan, 0, 0); +	udelay(5); + +	return true; +} + +static void nv50_i2c_stop(struct nv50_i2c_channel *chan) +{ +	nv50_i2c_set_bits(chan, 0, 0); +	udelay(2); + +	nv50_i2c_set_bits(chan, 1, 0); +	udelay(5); + +	nv50_i2c_set_bits(chan, 1, 1); +	udelay(5); +} + +static bool nv50_i2c_write_bit(struct nv50_i2c_channel *chan, int data) +{ +	bool rval; + +	nv50_i2c_set_bits(chan, 0, data); +	udelay(2); + +	rval = nv50_i2c_raise_clock(chan, data); +	udelay(5); + +	nv50_i2c_set_bits(chan, 0, data); +	udelay(5); + +	return rval; +} + +static bool nv50_i2c_read_bit(struct nv50_i2c_channel *chan, int *data) +{ +	bool rval; +	int clock; + +	rval = nv50_i2c_raise_clock(chan, 1); +	udelay(5); + +	nv50_i2c_get_bits(chan, &clock, data); +	udelay(5); + +	nv50_i2c_set_bits(chan, 0, 1); +	udelay(5); + +	return rval; +} + +static bool nv50_i2c_write_byte(struct nv50_i2c_channel *chan, uint8_t byte) +{ +	bool rval; +	int i, clock, data; + +	for (i = 7; i >= 0; i--) +		if (!nv50_i2c_write_bit(chan, (byte >> i) & 1)) +			return false; + +	nv50_i2c_set_bits(chan, 0, 1); +	udelay(5); + +	rval = nv50_i2c_raise_clock(chan, 1); + +	if (rval) { +		for (i = 40; i > 0; i -= 2) { +			udelay(2); +			nv50_i2c_get_bits(chan, &clock, &data); +			if (data == 0)  +				break; +		} + +		if (i <= 0) { +			printk("a timeout occured in nv50_i2c_write_byte\n"); +			rval = false; +		} +	} + +	nv50_i2c_set_bits(chan, 0, 1); +	udelay(5); + +	return rval; +} + +static bool nv50_i2c_read_byte(struct nv50_i2c_channel *chan, uint8_t *byte, bool last) +{ +	int i, bit; + +	nv50_i2c_set_bits(chan, 0, 1); +	udelay(5); + +	*byte = 0; + +	for (i = 7; i >= 0; i--) { +		if (nv50_i2c_read_bit(chan, &bit)) { +			if (bit) +				*byte |= (1 << i); +		} else { +			return false; +		} +	} + +	if (!nv50_i2c_write_bit(chan, last ? 1 : 0)) +		return false; + +	return true; +} + +/* only 7 bits addresses. */ +static bool nv50_i2c_address(struct nv50_i2c_channel *chan, uint8_t address, bool write) +{ +	if (nv50_i2c_start(chan)) { +		uint8_t real_addr = (address << 1); +		if (!write) +			real_addr |= 1; + +		if (nv50_i2c_write_byte(chan, real_addr)) +			return true; + +		/* failure, so issue stop */ +		nv50_i2c_stop(chan); +	} + +	return false; +} + +static bool nv50_i2c_read(struct nv50_i2c_channel *chan, uint8_t address, uint8_t *buffer, uint32_t length) +{ +	int i, j; +	bool rval, last; + +	/* retries */ +	for (i = 0; i < 4; i++) { +		rval = nv50_i2c_address(chan, address, false); +		if (!rval) +			return false; + +		for (j = 0; j < length; j++) { +			last = false; +			if (j == (length - 1)) +				last = true; +			rval = nv50_i2c_read_byte(chan, &buffer[j], last); +			if (!rval) { +				nv50_i2c_stop(chan); +				break; +			} +		} + +		nv50_i2c_stop(chan); + +		/* done */ +		if (rval) +			break; +	} + +	if (!rval) +		printk("nv50_i2c_read failed\n"); + +	return rval; +} + +static bool nv50_i2c_write(struct nv50_i2c_channel *chan, uint8_t address, uint8_t *buffer, uint32_t length) +{ +	int i, j; +	bool rval; + +	/* retries */ +	for (i = 0; i < 4; i++) { +		rval = nv50_i2c_address(chan, address, true); +		if (!rval) +			return false; + +		for (j = 0; j < length; j++) { +			rval = nv50_i2c_write_byte(chan, buffer[j]); +			if (!rval) { +				break; +			} +		} + +		nv50_i2c_stop(chan); + +		/* done */ +		if (rval) +			break; +	} + +	if (!rval) +		printk("nv50_i2c_write failed\n"); + +	return rval; +} + +static int nv50_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) +{ +	struct nv50_i2c_channel *chan = i2c_get_adapdata(i2c_adap); +	bool rval; +	int i; + +	for (i = 0; i < num; i++) { +		if (msgs[i].flags & I2C_M_RD) { /* read */ +			rval = nv50_i2c_read(chan, msgs[i].addr, msgs[i].buf, msgs[i].len); +		} else { /* write */ +			rval = nv50_i2c_write(chan, msgs[i].addr, msgs[i].buf, msgs[i].len); +		} + +		if (!rval) +			break; +	} + +	if (rval) +		return i; +	else +		return -EINVAL; +} + +static u32 nv50_i2c_functionality(struct i2c_adapter *adap) +{ +	return I2C_FUNC_I2C; +} + +static const struct i2c_algorithm nv50_i2c_algo = { +	.master_xfer	= nv50_i2c_xfer, +	.functionality	= nv50_i2c_functionality, +}; + +static int nv50_i2c_register_bus(struct i2c_adapter *adap) +{ +	adap->algo = &nv50_i2c_algo; + +	adap->timeout = 40; +	adap->retries = 4; + +	return i2c_add_adapter(adap); +} + +#define I2C_HW_B_NOUVEAU 0x010030 +struct nv50_i2c_channel *nv50_i2c_channel_create(struct drm_device *dev, uint32_t index) +{ +	struct nv50_i2c_channel *chan; + +	chan = kzalloc(sizeof(struct nv50_i2c_channel), GFP_KERNEL); + +	if (!chan) +		goto out; + +	DRM_INFO("Creating i2c bus with index %d\n", index); + +	chan->dev = dev; +	chan->index = index; +	snprintf(chan->adapter.name, I2C_NAME_SIZE, "nv50 i2c %d", index); +	chan->adapter.owner = THIS_MODULE; +	chan->adapter.id = I2C_HW_B_NOUVEAU; +	chan->adapter.dev.parent = &dev->pdev->dev; + +	i2c_set_adapdata(&chan->adapter, chan); + +	if (nv50_i2c_register_bus(&chan->adapter)) +		goto out; + +	return chan; + +out: +	kfree(chan); +	return NULL; +} + +void nv50_i2c_channel_destroy(struct nv50_i2c_channel *chan) +{ +	if (!chan) +		return; + +	i2c_del_adapter(&chan->adapter); +	kfree(chan); +} diff --git a/linux-core/nv50_i2c.h b/linux-core/nv50_i2c.h new file mode 100644 index 00000000..1740f8ec --- /dev/null +++ b/linux-core/nv50_i2c.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 __NV50_I2C_H__ +#define __NV50_I2C_H__ + +#include <linux/i2c.h> +#include <linux/i2c-id.h> +#include <linux/i2c-algo-bit.h> +#include "drmP.h" +#include "drm.h" +#include "nv50_display.h" + +struct nv50_i2c_channel { +	struct drm_device *dev; + +	uint32_t index; +	struct i2c_adapter adapter; +}; + +struct nv50_i2c_channel *nv50_i2c_channel_create(struct drm_device *dev, uint32_t index); +void nv50_i2c_channel_destroy(struct nv50_i2c_channel *chan); + +#endif /* __NV50_I2C_H__ */ diff --git a/linux-core/nv50_kms_wrapper.c b/linux-core/nv50_kms_wrapper.c new file mode 100644 index 00000000..8ae72f48 --- /dev/null +++ b/linux-core/nv50_kms_wrapper.c @@ -0,0 +1,1451 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 "nv50_kms_wrapper.h" +#include "drm_crtc_helper.h" /* be careful what you use from this */ + +/* This file serves as the interface between the common kernel modesetting code and the device dependent implementation. */ + +/* + * Get private functions. + */ + +struct nv50_kms_priv *nv50_get_kms_priv(struct drm_device *dev) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	return dev_priv->kms_priv; +} + +/* + * Allocation functions. + */ + +static void *nv50_kms_alloc_crtc(struct drm_device *dev) +{ +	struct nv50_kms_priv *kms_priv = nv50_get_kms_priv(dev); +	struct nv50_kms_crtc *crtc = kzalloc(sizeof(struct nv50_kms_crtc), GFP_KERNEL); + +	if (!crtc) +		return NULL; + +	list_add_tail(&crtc->item, &kms_priv->crtcs); + +	return &(crtc->priv); +} + +static void *nv50_kms_alloc_output(struct drm_device *dev) +{ +	struct nv50_kms_priv *kms_priv = nv50_get_kms_priv(dev); +	struct nv50_kms_encoder *encoder = kzalloc(sizeof(struct nv50_kms_encoder), GFP_KERNEL); + +	if (!encoder) +		return NULL; + +	list_add_tail(&encoder->item, &kms_priv->encoders); + +	return &(encoder->priv); +} + +static void *nv50_kms_alloc_connector(struct drm_device *dev) +{ +	struct nv50_kms_priv *kms_priv = nv50_get_kms_priv(dev); +	struct nv50_kms_connector *connector = kzalloc(sizeof(struct nv50_kms_connector), GFP_KERNEL); + +	if (!connector) +		return NULL; + +	list_add_tail(&connector->item, &kms_priv->connectors); + +	return &(connector->priv); +} + +static void nv50_kms_free_crtc(void *crtc) +{ +	struct nv50_kms_crtc *kms_crtc = from_nv50_crtc(crtc); + +	list_del(&kms_crtc->item); + +	kfree(kms_crtc); +} + +static void nv50_kms_free_output(void *output) +{ +	struct nv50_kms_encoder *kms_encoder = from_nv50_output(output); + +	list_del(&kms_encoder->item); + +	kfree(kms_encoder); +} + +static void nv50_kms_free_connector(void *connector) +{ +	struct nv50_kms_connector *kms_connector = from_nv50_connector(connector); + +	list_del(&kms_connector->item); + +	kfree(kms_connector); +} + +/* + * Mode conversion functions. + */ + +static struct nouveau_hw_mode *nv50_kms_to_hw_mode(struct drm_display_mode *mode) +{ +	struct nouveau_hw_mode *hw_mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL); +	if (!hw_mode) +		return NULL; + +	/* create hw values. */ +	hw_mode->clock = mode->clock; +	hw_mode->flags = hw_mode->flags; + +	hw_mode->hdisplay = mode->hdisplay; +	hw_mode->hsync_start = mode->hsync_start; +	hw_mode->hsync_end = mode->hsync_end; +	hw_mode->htotal = mode->htotal; + +	hw_mode->hblank_start = mode->hdisplay + 1; +	hw_mode->hblank_end = mode->htotal; + +	hw_mode->vdisplay = mode->vdisplay; +	hw_mode->vsync_start = mode->vsync_start; +	hw_mode->vsync_end = mode->vsync_end; +	hw_mode->vtotal = mode->vtotal; + +	hw_mode->vblank_start = mode->vdisplay + 1; +	hw_mode->vblank_end = mode->vtotal; + +	return hw_mode; +} + +/* + * State mirroring functions. + */ + +static void nv50_kms_mirror_routing(struct drm_device *dev) +{ +	struct nv50_display *display = nv50_get_display(dev); +	struct nv50_crtc *crtc = NULL; +	struct nv50_output *output = NULL; +	struct nv50_connector *connector = NULL; +	struct drm_connector *drm_connector = NULL; +	struct drm_crtc *drm_crtc = NULL; + +	/* Wipe all previous connections. */ +	list_for_each_entry(connector, &display->connectors, item) { +		connector->output = NULL; +	} + +	list_for_each_entry(output, &display->outputs, item) { +		output->crtc = NULL; +	} + +	list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) { +		if (drm_connector->encoder) { +			output = to_nv50_output(drm_connector->encoder); +			connector = to_nv50_connector(drm_connector); + +			/* hook up output to connector. */ +			connector->output = output; + +			if (drm_connector->encoder->crtc) { +				crtc = to_nv50_crtc(drm_connector->encoder->crtc); + +				/* hook up output to crtc. */ +				output->crtc = crtc; +			} +		} +	} + +	/* mirror crtc active state */ +	list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) { +		crtc = to_nv50_crtc(drm_crtc); + +		crtc->enabled = drm_crtc->enabled; +	} +} + +/* + * FB functions. + */ + +static void nv50_kms_framebuffer_destroy(struct drm_framebuffer *drm_framebuffer) +{ +	drm_framebuffer_cleanup(drm_framebuffer); + +	kfree(drm_framebuffer); +} + +static const struct drm_framebuffer_funcs nv50_kms_fb_funcs = { +	.destroy = nv50_kms_framebuffer_destroy, +}; + +/* + * Mode config functions. + */ + +static struct drm_framebuffer *nv50_kms_framebuffer_create(struct drm_device *dev, +						struct drm_file *file_priv, struct drm_mode_fb_cmd *mode_cmd) +{ +	struct drm_framebuffer *drm_framebuffer = kzalloc(sizeof(struct drm_framebuffer), GFP_KERNEL); +	if (!drm_framebuffer) +		return NULL; + +	drm_framebuffer_init(dev, drm_framebuffer, &nv50_kms_fb_funcs); +	drm_helper_mode_fill_fb_struct(drm_framebuffer, mode_cmd); + +	return drm_framebuffer; +} + +static int nv50_kms_fb_changed(struct drm_device *dev) +{ +	return 0; /* not needed until nouveaufb? */ +} + +static const struct drm_mode_config_funcs nv50_kms_mode_funcs = { +	.resize_fb = NULL, +	.fb_create = nv50_kms_framebuffer_create, +	.fb_changed = nv50_kms_fb_changed, +}; + +/* + * CRTC functions. + */ + +static int nv50_kms_crtc_cursor_set(struct drm_crtc *drm_crtc,  +				    struct drm_file *file_priv, +				    uint32_t buffer_handle, +				    uint32_t width, uint32_t height) +{ +	struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc); +	struct nv50_display *display = nv50_get_display(crtc->dev); +	int rval = 0; + +	if (width != 64 || height != 64) +		return -EINVAL; + +	/* set bo before doing show cursor */ +	if (buffer_handle) { +		rval = crtc->cursor->set_bo(crtc, (drm_handle_t) buffer_handle); +		if (rval != 0) +			goto out; +	} + +	crtc->cursor->visible = buffer_handle ? true : false; + +	if (buffer_handle) { +		rval = crtc->cursor->show(crtc); +		if (rval != 0) +			goto out; +	} else { /* no handle implies hiding the cursor */ +		rval = crtc->cursor->hide(crtc); +		goto out; +	} + +	if (rval != 0) +		return rval; + +out: +	/* in case this triggers any other cursor changes */ +	display->update(display); + +	return rval; +} + +static int nv50_kms_crtc_cursor_move(struct drm_crtc *drm_crtc, int x, int y) +{ +	struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc); + +	return crtc->cursor->set_pos(crtc, x, y); +} + +void nv50_kms_crtc_gamma_set(struct drm_crtc *drm_crtc, u16 *r, u16 *g, u16 *b, +		uint32_t size) +{ +	struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc); + +	if (size != 256) +		return; + +	crtc->lut->set(crtc, (uint16_t *)r, (uint16_t *)g, (uint16_t *)b); +} + +int nv50_kms_crtc_set_config(struct drm_mode_set *set) +{ +	int rval = 0, i; +	uint32_t crtc_mask = 0; +	struct drm_device *dev = NULL; +	struct drm_nouveau_private *dev_priv = NULL; +	struct nv50_display *display = NULL; +	struct drm_connector *drm_connector = NULL; +	struct drm_encoder *drm_encoder = NULL; +	struct drm_crtc *drm_crtc = NULL; + +	struct nv50_crtc *crtc = NULL; +	struct nv50_output *output = NULL; +	struct nv50_connector *connector = NULL; +	struct nouveau_hw_mode *hw_mode = NULL; +	struct nv50_fb_info fb_info; + +	bool blank = false; +	bool switch_fb = false; +	bool modeset = false; + +	NV50_DEBUG("\n"); + +	/* +	 * Supported operations: +	 * - Switch mode. +	 * - Switch framebuffer. +	 * - Blank screen. +	 */ + +	/* Sanity checking */ +	if (!set) { +		DRM_ERROR("Sanity check failed\n"); +		goto out; +	} + +	if (!set->crtc) { +		DRM_ERROR("Sanity check failed\n"); +		goto out; +	} + +	if (set->mode) { +		if (set->fb) { +			if (!drm_mode_equal(set->mode, &set->crtc->mode)) +				modeset = true; + +			if (set->fb != set->crtc->fb) +				switch_fb = true; + +			if (set->x != set->crtc->x || set->y != set->crtc->y) +				switch_fb = true; +		} +	} else { +		blank = true; +	} + +	if (!set->connectors && !blank) { +		DRM_ERROR("Sanity check failed\n"); +		goto out; +	} + +	/* Basic variable setting */ +	dev = set->crtc->dev; +	dev_priv = dev->dev_private; +	display = nv50_get_display(dev); +	crtc = to_nv50_crtc(set->crtc); + +	/** +	 * Wiring up the encoders and connectors. +	 */ + +	/* for switch_fb we verify if any important changes happened */ +	if (!blank) { +		/* Mode validation */ +		hw_mode = nv50_kms_to_hw_mode(set->mode); + +		rval = crtc->validate_mode(crtc, hw_mode); + +		if (rval != MODE_OK) { +			DRM_ERROR("Mode not ok\n"); +			goto out; +		} + +		for (i = 0; i < set->num_connectors; i++) { +			drm_connector = set->connectors[i]; +			if (!drm_connector) { +				DRM_ERROR("No connector\n"); +				goto out; +			} +			connector = to_nv50_connector(drm_connector); + +			/* This is to ensure it knows the connector subtype. */ +			drm_connector->funcs->fill_modes(drm_connector, 0, 0); + +			output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector)); +			if (!output) { +				DRM_ERROR("No output\n"); +				goto out; +			} + +			rval = output->validate_mode(output, hw_mode); +			if (rval != MODE_OK) { +				DRM_ERROR("Mode not ok\n"); +				goto out; +			} + +			/* verify if any "sneaky" changes happened */ +			if (output != connector->output) +				modeset = true; + +			if (output->crtc != crtc) +				modeset = true; +		} +	} + +	/* Now we verified if anything changed, fail if nothing has. */ +	if (!modeset && !switch_fb && !blank) +		DRM_INFO("A seemingly empty modeset encountered, this could be a bug.\n"); + +	/* Validation done, move on to cleaning of existing structures. */ +	if (modeset) { +		/* find encoders that use this crtc. */ +		list_for_each_entry(drm_encoder, &dev->mode_config.encoder_list, head) { +			if (drm_encoder->crtc == set->crtc) { +				/* find the connector that goes with it */ +				list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) { +					if (drm_connector->encoder == drm_encoder) { +						drm_connector->encoder =  NULL; +						break; +					} +				} +				drm_encoder->crtc = NULL; +			} +		} + +		/* now find if our desired encoders or connectors are in use already. */ +		for (i = 0; i < set->num_connectors; i++) { +			drm_connector = set->connectors[i]; +			if (!drm_connector) { +				DRM_ERROR("No connector\n"); +				goto out; +			} + +			if (!drm_connector->encoder) +				continue; + +			drm_encoder = drm_connector->encoder; +			drm_connector->encoder = NULL; + +			if (!drm_encoder->crtc) +				continue; + +			drm_crtc = drm_encoder->crtc; +			drm_encoder->crtc = NULL; + +			drm_crtc->enabled = false; +		} + +		/* Time to wire up the public encoder, the private one will be handled later. */ +		for (i = 0; i < set->num_connectors; i++) { +			drm_connector = set->connectors[i]; +			if (!drm_connector) { +				DRM_ERROR("No connector\n"); +				goto out; +			} + +			output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector)); +			if (!output) { +				DRM_ERROR("No output\n"); +				goto out; +			} + +			/* find the encoder public structure that matches out output structure. */ +			drm_encoder = to_nv50_kms_encoder(output); + +			if (!drm_encoder) { +				DRM_ERROR("No encoder\n"); +				goto out; +			} + +			drm_encoder->crtc = set->crtc; +			set->crtc->enabled = true; +			drm_connector->encoder = drm_encoder; +		} +	} + +	/** +	 * Disable crtc. +	 */ + +	if (blank) { +		crtc = to_nv50_crtc(set->crtc); + +		set->crtc->enabled = false; + +		/* disconnect encoders and connectors */ +		for (i = 0; i < set->num_connectors; i++) { +			drm_connector = set->connectors[i]; + +			if (!drm_connector->encoder) +				continue; + +			drm_connector->encoder->crtc = NULL; +			drm_connector->encoder = NULL; +		} +	} + +	/** +	 * All state should now be updated, now onto the real work. +	 */ + +	/* mirror everything to the private structs */ +	nv50_kms_mirror_routing(dev); + +	/** +	 * Bind framebuffer. +	 */ + +	if (switch_fb) { +		crtc = to_nv50_crtc(set->crtc); + +		/* set framebuffer */ +		set->crtc->fb = set->fb; + +		/* set private framebuffer */ +		crtc = to_nv50_crtc(set->crtc); +		fb_info.block = find_block_by_handle(dev_priv->fb_heap, set->fb->mm_handle); +		fb_info.width = set->fb->width; +		fb_info.height = set->fb->height; +		fb_info.depth = set->fb->depth; +		fb_info.bpp = set->fb->bits_per_pixel; +		fb_info.pitch = set->fb->pitch; +		fb_info.x = set->x; +		fb_info.y = set->y; + +		rval = crtc->fb->bind(crtc, &fb_info); +		if (rval != 0) { +			DRM_ERROR("fb_bind failed\n"); +			goto out; +		} +	} + +	/* this is !cursor_show */ +	if (!crtc->cursor->enabled) { +		rval = crtc->cursor->enable(crtc); +		if (rval != 0) { +			DRM_ERROR("cursor_enable failed\n"); +			goto out; +		} +	} + +	/** +	 * Blanking. +	 */ + +	if (blank) { +		crtc = to_nv50_crtc(set->crtc); + +		rval = crtc->blank(crtc, true); +		if (rval != 0) { +			DRM_ERROR("blanking failed\n"); +			goto out; +		} + +		/* detach any outputs that are currently unused */ +		list_for_each_entry(drm_encoder, &dev->mode_config.encoder_list, head) { +			if (!drm_encoder->crtc) { +				output = to_nv50_output(drm_encoder); + +				rval = output->execute_mode(output, true); +				if (rval != 0) { +					DRM_ERROR("detaching output failed\n"); +					goto out; +				} +			} +		} +	} + +	/** +	 * Change framebuffer, without changing mode. +	 */ + +	if (switch_fb && !modeset && !blank) { +		crtc = to_nv50_crtc(set->crtc); + +		rval = crtc->set_fb(crtc); +		if (rval != 0) { +			DRM_ERROR("set_fb failed\n"); +			goto out; +		} + +		/* this also sets the fb offset */ +		rval = crtc->blank(crtc, false); +		if (rval != 0) { +			DRM_ERROR("unblanking failed\n"); +			goto out; +		} +	} + +	/** +	 * Normal modesetting. +	 */ + +	if (modeset) { +		crtc = to_nv50_crtc(set->crtc); + +		/* disconnect unused outputs */ +		list_for_each_entry(output, &display->outputs, item) { +			if (output->crtc) { +				crtc_mask |= 1 << output->crtc->index; +			} else { +				rval = output->execute_mode(output, true); +				if (rval != 0) { +					DRM_ERROR("detaching output failed\n"); +					goto out; +				} +			} +		} + +		/* blank any unused crtcs */ +		list_for_each_entry(crtc, &display->crtcs, item) { +			if (!(crtc_mask & (1 << crtc->index))) +				crtc->blank(crtc, true); +		} + +		crtc = to_nv50_crtc(set->crtc); + +		rval = crtc->set_mode(crtc, hw_mode); +		if (rval != 0) { +			DRM_ERROR("crtc mode set failed\n"); +			goto out; +		} + +		/* find native mode. */ +		list_for_each_entry(output, &display->outputs, item) { +			if (output->crtc != crtc) +				continue; + +			*crtc->native_mode = *output->native_mode; +			list_for_each_entry(connector, &display->connectors, item) { +				if (connector->output != output) +					continue; + +				crtc->requested_scaling_mode = connector->requested_scaling_mode; +				crtc->use_dithering = connector->use_dithering; +				break; +			} + +			if (crtc->requested_scaling_mode == SCALE_NON_GPU) +				crtc->use_native_mode = false; +			else +				crtc->use_native_mode = true; + +			break; /* no use in finding more than one mode */ +		} + +		rval = crtc->execute_mode(crtc); +		if (rval != 0) { +			DRM_ERROR("crtc execute mode failed\n"); +			goto out; +		} + +		list_for_each_entry(output, &display->outputs, item) { +			if (output->crtc != crtc) +				continue; + +			rval = output->execute_mode(output, false); +			if (rval != 0) { +				DRM_ERROR("output execute mode failed\n"); +				goto out; +			} +		} + +		rval = crtc->set_scale(crtc); +		if (rval != 0) { +			DRM_ERROR("crtc set scale failed\n"); +			goto out; +		} + +		/* next line changes crtc, so putting it here is important */ +		display->last_crtc = crtc->index; +	} + +	/* always reset dpms, regardless if any other modesetting is done. */ +	if (!blank) { +		/* this is executed immediately */ +		list_for_each_entry(output, &display->outputs, item) { +			if (output->crtc != crtc) +				continue; + +			rval = output->set_power_mode(output, DRM_MODE_DPMS_ON); +			if (rval != 0) { +				DRM_ERROR("output set power mode failed\n"); +				goto out; +			} +		} + +		/* update dpms state to DPMSModeOn */ +		for (i = 0; i < set->num_connectors; i++) { +			drm_connector = set->connectors[i]; +			if (!drm_connector) { +				DRM_ERROR("No connector\n"); +				goto out; +			} + +			rval = drm_connector_property_set_value(drm_connector, +					dev->mode_config.dpms_property, +					DRM_MODE_DPMS_ON); +			if (rval != 0) { +				DRM_ERROR("failed to update dpms state\n"); +				goto out; +			} +		} +	} + +	display->update(display); + +	/* Update the current mode, now that all has gone well. */ +	if (modeset) { +		set->crtc->mode = *(set->mode); +		set->crtc->x = set->x; +		set->crtc->y = set->y; +	} + +	kfree(hw_mode); + +	return 0; + +out: +	kfree(hw_mode); + +	if (rval != 0) +		return rval; +	else +		return -EINVAL; +} + +static void nv50_kms_crtc_destroy(struct drm_crtc *drm_crtc) +{ +	struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc); + +	drm_crtc_cleanup(drm_crtc); + +	/* this will even destroy the public structure. */ +	crtc->destroy(crtc); +} + +static const struct drm_crtc_funcs nv50_kms_crtc_funcs = { +	.save = NULL, +	.restore = NULL, +	.cursor_set = nv50_kms_crtc_cursor_set, +	.cursor_move = nv50_kms_crtc_cursor_move, +	.gamma_set = nv50_kms_crtc_gamma_set, +	.set_config = nv50_kms_crtc_set_config, +	.destroy = nv50_kms_crtc_destroy, +}; + +static int nv50_kms_crtcs_init(struct drm_device *dev) +{ +	struct nv50_display *display = nv50_get_display(dev); +	struct nv50_crtc *crtc = NULL; + +	/* +	 * This may look a bit confusing, but: +	 * The internal structure is already allocated and so is the public one. +	 * Just a matter of getting to the memory and register it. +	 */ +	list_for_each_entry(crtc, &display->crtcs, item) { +		struct drm_crtc *drm_crtc = to_nv50_kms_crtc(crtc); + +		drm_crtc_init(dev, drm_crtc, &nv50_kms_crtc_funcs); + +		/* init lut storage */ +		drm_mode_crtc_set_gamma_size(drm_crtc, 256); +	} + +	return 0; +} + +/* + * Encoder functions + */ + +static void nv50_kms_encoder_destroy(struct drm_encoder *drm_encoder) +{ +	struct nv50_output *output = to_nv50_output(drm_encoder); + +	drm_encoder_cleanup(drm_encoder); + +	/* this will even destroy the public structure. */ +	output->destroy(output); +} + +static const struct drm_encoder_funcs nv50_kms_encoder_funcs = { +	.destroy = nv50_kms_encoder_destroy, +}; + +static int nv50_kms_encoders_init(struct drm_device *dev) +{ +	struct nv50_display *display = nv50_get_display(dev); +	struct nv50_output *output = NULL; + +	list_for_each_entry(output, &display->outputs, item) { +		struct drm_encoder *drm_encoder = to_nv50_kms_encoder(output); +		uint32_t type = DRM_MODE_ENCODER_NONE; + +		switch (output->type) { +			case OUTPUT_DAC: +				type = DRM_MODE_ENCODER_DAC; +				break; +			case OUTPUT_TMDS: +				type = DRM_MODE_ENCODER_TMDS; +				break; +			case OUTPUT_LVDS: +				type = DRM_MODE_ENCODER_LVDS; +				break; +			case OUTPUT_TV: +				type = DRM_MODE_ENCODER_TVDAC; +				break; +			default: +				type = DRM_MODE_ENCODER_NONE; +				break; +		} + +		if (type == DRM_MODE_ENCODER_NONE) { +			DRM_ERROR("DRM_MODE_ENCODER_NONE encountered\n"); +			continue; +		} + +		drm_encoder_init(dev, drm_encoder, &nv50_kms_encoder_funcs, type); + +		/* I've never seen possible crtc's restricted. */ +		drm_encoder->possible_crtcs = 3; +		drm_encoder->possible_clones = 0; +	} + +	return 0; +} + +/* + * Connector functions + */ + + +/* These 2 functions wrap the connector properties that deal with multiple encoders per connector. */ +bool nv50_kms_connector_get_digital(struct drm_connector *drm_connector) +{ +	struct drm_device *dev = drm_connector->dev; + +	switch (drm_connector->connector_type) { +		case DRM_MODE_CONNECTOR_VGA: +		case DRM_MODE_CONNECTOR_SVIDEO: +			return false; +		case DRM_MODE_CONNECTOR_DVID: +		case DRM_MODE_CONNECTOR_LVDS: +			return true; +		default: +			break; +	} + +	if (drm_connector->connector_type == DRM_MODE_CONNECTOR_DVII) { +		int rval; +		uint64_t prop_val; + +		rval = drm_connector_property_get_value(drm_connector, dev->mode_config.dvi_i_select_subconnector_property, &prop_val); +		if (rval) { +			DRM_ERROR("Unable to find select subconnector property, defaulting to DVI-D\n"); +			return true; +		} + +		/* Is a subconnector explicitly selected? */ +		switch (prop_val) { +			case DRM_MODE_SUBCONNECTOR_DVID: +				return true; +			case DRM_MODE_SUBCONNECTOR_DVIA: +				return false; +			default: +				break; +		} + +		rval = drm_connector_property_get_value(drm_connector, dev->mode_config.dvi_i_subconnector_property, &prop_val); +		if (rval) { +			DRM_ERROR("Unable to find subconnector property, defaulting to DVI-D\n"); +			return true; +		} + +		/* Do we know what subconnector we currently have connected? */ +		switch (prop_val) { +			case DRM_MODE_SUBCONNECTOR_DVID: +				return true; +			case DRM_MODE_SUBCONNECTOR_DVIA: +				return false; +			default: +				DRM_ERROR("Unknown subconnector value, defaulting to DVI-D\n"); +				return true; +		} +	} + +	DRM_ERROR("Unknown connector type, defaulting to analog\n"); +	return false; +} + +static void nv50_kms_connector_set_digital(struct drm_connector *drm_connector, int digital, bool force) +{ +	struct drm_device *dev = drm_connector->dev; + +	if (drm_connector->connector_type == DRM_MODE_CONNECTOR_DVII) { +		uint64_t cur_value, new_value; + +		int rval = drm_connector_property_get_value(drm_connector, dev->mode_config.dvi_i_subconnector_property, &cur_value); +		if (rval) { +			DRM_ERROR("Unable to find subconnector property\n"); +			return; +		} + +		/* Only set when unknown or when forced to do so. */ +		if (cur_value != DRM_MODE_SUBCONNECTOR_Unknown && !force) +			return; + +		if (digital == 1) +			new_value = DRM_MODE_SUBCONNECTOR_DVID; +		else if (digital == 0) +			new_value = DRM_MODE_SUBCONNECTOR_DVIA; +		else +			new_value = DRM_MODE_SUBCONNECTOR_Unknown; +		drm_connector_property_set_value(drm_connector, dev->mode_config.dvi_i_subconnector_property, new_value); +	} +} + +void nv50_kms_connector_detect_all(struct drm_device *dev) +{ +	struct drm_connector *drm_connector = NULL; + +	list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) { +		drm_connector->funcs->detect(drm_connector); +	} +} + +static enum drm_connector_status nv50_kms_connector_detect(struct drm_connector *drm_connector) +{ +	struct drm_device *dev = drm_connector->dev; +	struct nv50_connector *connector = to_nv50_connector(drm_connector); +	struct nv50_output *output = NULL; +	int hpd_detect = 0, load_detect = 0, i2c_detect = 0; +	int old_status = drm_connector->status; + +	/* hotplug detect */ +	hpd_detect = connector->hpd_detect(connector); + +	/* load detect */ +	output = connector->to_output(connector, false); /* analog */ +	if (output && output->detect) +		load_detect = output->detect(output); + +	if (hpd_detect < 0 || load_detect < 0) /* did an error occur? */ +		i2c_detect = connector->i2c_detect(connector); + +	if (load_detect == 1) { +		nv50_kms_connector_set_digital(drm_connector, 0, true); /* analog, forced */ +	} else if (hpd_detect == 1 && load_detect == 0) { +		nv50_kms_connector_set_digital(drm_connector, 1, true); /* digital, forced */ +	} else { +		nv50_kms_connector_set_digital(drm_connector, -1, true); /* unknown, forced */ +	} + +	if (hpd_detect == 1 || load_detect == 1 || i2c_detect == 1) +		drm_connector->status = connector_status_connected; +	else +		drm_connector->status = connector_status_disconnected; + +	/* update our modes whenever there is reason to */ +	if (old_status != drm_connector->status) { +		drm_connector->funcs->fill_modes(drm_connector, 0, 0); + +		/* notify fb of changes */ +		dev->mode_config.funcs->fb_changed(dev); +	} + +	return drm_connector->status; +} + +static void nv50_kms_connector_destroy(struct drm_connector *drm_connector) +{ +	struct nv50_connector *connector = to_nv50_connector(drm_connector); + +	drm_sysfs_connector_remove(drm_connector); +	drm_connector_cleanup(drm_connector); + +	/* this will even destroy the public structure. */ +	connector->destroy(connector); +} + +/* + * Detailed mode info for a standard 640x480@60Hz monitor + */ +static struct drm_display_mode std_mode[] = { +	/*{ DRM_MODE("640x480", DRM_MODE_TYPE_DEFAULT, 25200, 640, 656, +		 752, 800, 0, 480, 490, 492, 525, 0, +		 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },*/ /* 640x480@60Hz */ +	{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DEFAULT, 135000, 1280, 1296, +		   1440, 1688, 0, 1024, 1025, 1028, 1066, 0, +		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */ +}; + +static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, uint32_t maxX, uint32_t maxY) +{ +	struct nv50_connector *connector = to_nv50_connector(drm_connector); +	struct drm_device *dev = drm_connector->dev; +	int rval = 0; +	bool connected = false; +	struct drm_display_mode *mode, *t; +	struct edid *edid = NULL; + +	NV50_DEBUG("%s\n", drm_get_connector_name(drm_connector)); +	/* set all modes to the unverified state */ +	list_for_each_entry_safe(mode, t, &drm_connector->modes, head) +		mode->status = MODE_UNVERIFIED; + +	if (nv50_kms_connector_detect(drm_connector) == connector_status_connected) +		connected = true; + +	if (connected) +		NV50_DEBUG("%s is connected\n", drm_get_connector_name(drm_connector)); +	else +		NV50_DEBUG("%s is disconnected\n", drm_get_connector_name(drm_connector)); + +	/* Not all connnectors have an i2c channel. */ +	if (connected && connector->i2c_chan) +		edid = (struct edid *) drm_do_probe_ddc_edid(&connector->i2c_chan->adapter); + +	/* This will remove edid if needed. */ +	drm_mode_connector_update_edid_property(drm_connector, edid); + +	if (edid) { +		rval = drm_add_edid_modes(drm_connector, edid); + +		/* Only update when relevant and when detect couldn't determine type. */ +		nv50_kms_connector_set_digital(drm_connector, edid->digital ? 1 : 0, false); + +		kfree(edid); +	} + +	if (rval) /* number of modes  > 1 */ +		drm_mode_connector_list_update(drm_connector); + +	if (maxX && maxY) +		drm_mode_validate_size(dev, &drm_connector->modes, maxX, maxY, 0); + +	list_for_each_entry_safe(mode, t, &drm_connector->modes, head) { +		if (mode->status == MODE_OK) { +			struct nouveau_hw_mode *hw_mode = nv50_kms_to_hw_mode(mode); +			struct nv50_output *output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector)); + +			mode->status = output->validate_mode(output, hw_mode); +			/* find native mode, TODO: also check if we actually found one */ +			if (mode->status == MODE_OK) { +				if (mode->type & DRM_MODE_TYPE_PREFERRED) +					*output->native_mode = *hw_mode; +			} +			kfree(hw_mode); +		} +	} + +	/* revalidate now that we have native mode */ +	list_for_each_entry_safe(mode, t, &drm_connector->modes, head) { +		if (mode->status == MODE_OK) { +			struct nouveau_hw_mode *hw_mode = nv50_kms_to_hw_mode(mode); +			struct nv50_output *output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector)); + +			mode->status = output->validate_mode(output, hw_mode); +			kfree(hw_mode); +		} +	} + +	drm_mode_prune_invalid(dev, &drm_connector->modes, true); + +	/* pruning is done, so bail out. */ +	if (!connected) +		return; + +	if (list_empty(&drm_connector->modes)) { +		struct drm_display_mode *stdmode; +		struct nouveau_hw_mode *hw_mode; +		struct nv50_output *output; + +		NV50_DEBUG("No valid modes on %s\n", drm_get_connector_name(drm_connector)); + +		/* Making up native modes for LVDS is a bad idea. */ +		if (drm_connector->connector_type == DRM_MODE_CONNECTOR_LVDS) +			return; + +		/* Should we do this here ??? +		 * When no valid EDID modes are available we end up +		 * here and bailed in the past, now we add a standard +		 * 640x480@60Hz mode and carry on. +		 */ +		stdmode = drm_mode_duplicate(dev, &std_mode[0]); +		drm_mode_probed_add(drm_connector, stdmode); +		drm_mode_list_concat(&drm_connector->probed_modes, +				     &drm_connector->modes); + +		/* also add it as native mode */ +		hw_mode = nv50_kms_to_hw_mode(mode); +		output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector)); + +		if (hw_mode) +			*output->native_mode = *hw_mode; + +		DRM_DEBUG("Adding standard 640x480 @ 60Hz to %s\n", +			  drm_get_connector_name(drm_connector)); +	} + +	drm_mode_sort(&drm_connector->modes); + +	NV50_DEBUG("Probed modes for %s\n", drm_get_connector_name(drm_connector)); + +	list_for_each_entry_safe(mode, t, &drm_connector->modes, head) { +		mode->vrefresh = drm_mode_vrefresh(mode); + +		/* is this needed, as it's unused by the driver? */ +		drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); +		drm_mode_debug_printmodeline(mode); +	} +} + +static int nv50_kms_connector_set_property(struct drm_connector *drm_connector, +					struct drm_property *property, +					uint64_t value) +{ +	struct drm_device *dev = drm_connector->dev; +	struct nv50_connector *connector = to_nv50_connector(drm_connector); +	int rval = 0; +	bool delay_change = false; + +	/* DPMS */ +	if (property == dev->mode_config.dpms_property && drm_connector->encoder) { +		struct nv50_output *output = to_nv50_output(drm_connector->encoder); + +		rval = output->set_power_mode(output, (int) value); + +		return rval; +	} + +	/* Scaling mode */ +	if (property == dev->mode_config.scaling_mode_property) { +		struct nv50_crtc *crtc = NULL; +		struct nv50_display *display = nv50_get_display(dev); +		int internal_value = 0; + +		switch (value) { +			case DRM_MODE_SCALE_NON_GPU: +				internal_value = SCALE_NON_GPU; +				break; +			case DRM_MODE_SCALE_FULLSCREEN: +				internal_value = SCALE_FULLSCREEN; +				break; +			case DRM_MODE_SCALE_NO_SCALE: +				internal_value = SCALE_NOSCALE; +				break; +			case DRM_MODE_SCALE_ASPECT: +				internal_value = SCALE_ASPECT; +				break; +			default: +				break; +		} + +		/* LVDS always needs gpu scaling */ +		if (connector->type == CONNECTOR_LVDS && internal_value == SCALE_NON_GPU) +			return -EINVAL; + +		connector->requested_scaling_mode = internal_value; + +		if (drm_connector->encoder && drm_connector->encoder->crtc) +			crtc = to_nv50_crtc(drm_connector->encoder->crtc); + +		if (!crtc) +			return 0; + +		crtc->requested_scaling_mode = connector->requested_scaling_mode; + +		/* going from and to a gpu scaled regime requires a modesetting, so wait until next modeset */ +		if (crtc->scaling_mode == SCALE_NON_GPU || internal_value == SCALE_NON_GPU) { +			DRM_INFO("Moving from or to a non-gpu scaled mode, this will be processed upon next modeset."); +			delay_change = true; +		} + +		if (delay_change) +			return 0; + +		rval = crtc->set_scale(crtc); +		if (rval) +			return rval; + +		/* process command buffer */ +		display->update(display); + +		return 0; +	} + +	/* Dithering */ +	if (property == dev->mode_config.dithering_mode_property) { +		struct nv50_crtc *crtc = NULL; +		struct nv50_display *display = nv50_get_display(dev); + +		if (value == DRM_MODE_DITHERING_ON) +			connector->use_dithering = true; +		else +			connector->use_dithering = false; + +		if (drm_connector->encoder && drm_connector->encoder->crtc) +			crtc = to_nv50_crtc(drm_connector->encoder->crtc); + +		if (!crtc) +			return 0; + +		/* update hw state */ +		crtc->use_dithering = connector->use_dithering; +		rval = crtc->set_dither(crtc); +		if (rval) +			return rval; + +		/* process command buffer */ +		display->update(display); + +		return 0; +	} + +	return -EINVAL; +} + +static const struct drm_connector_funcs nv50_kms_connector_funcs = { +	.save = NULL, +	.restore = NULL, +	.detect = nv50_kms_connector_detect, +	.destroy = nv50_kms_connector_destroy, +	.fill_modes = nv50_kms_connector_fill_modes, +	.set_property = nv50_kms_connector_set_property +}; + +static int nv50_kms_get_scaling_mode(struct drm_connector *drm_connector) +{ +	struct nv50_connector *connector = NULL; +	int drm_mode = 0; + +	if (!drm_connector) { +		DRM_ERROR("drm_connector is NULL\n"); +		return 0; +	} + +	connector = to_nv50_connector(drm_connector); + +	switch (connector->requested_scaling_mode) { +		case SCALE_NON_GPU: +			drm_mode = DRM_MODE_SCALE_NON_GPU; +			break; +		case SCALE_FULLSCREEN: +			drm_mode = DRM_MODE_SCALE_FULLSCREEN; +			break; +		case SCALE_NOSCALE: +			drm_mode = DRM_MODE_SCALE_NO_SCALE; +			break; +		case SCALE_ASPECT: +			drm_mode = DRM_MODE_SCALE_ASPECT; +			break; +		default: +			break; +	} + +	return drm_mode; +} + +static int nv50_kms_connectors_init(struct drm_device *dev) +{ +	struct nv50_display *display = nv50_get_display(dev); +	struct nv50_connector *connector = NULL; +	int i; + +	/* Initialise some optional connector properties. */ +	drm_mode_create_scaling_mode_property(dev); +	drm_mode_create_dithering_property(dev); + +	list_for_each_entry(connector, &display->connectors, item) { +		struct drm_connector *drm_connector = to_nv50_kms_connector(connector); +		uint32_t type = DRM_MODE_CONNECTOR_Unknown; + +		switch (connector->type) { +			case CONNECTOR_VGA: +				type = DRM_MODE_CONNECTOR_VGA; +				break; +			case CONNECTOR_DVI_D: +				type = DRM_MODE_CONNECTOR_DVID; +				break; +			case CONNECTOR_DVI_I: +				type = DRM_MODE_CONNECTOR_DVII; +				break; +			case CONNECTOR_LVDS: +				type = DRM_MODE_CONNECTOR_LVDS; +				break; +			case CONNECTOR_TV: +				type = DRM_MODE_CONNECTOR_SVIDEO; +				break; +			default: +				type = DRM_MODE_CONNECTOR_Unknown; +				break; +		} + +		if (type == DRM_MODE_CONNECTOR_Unknown) { +			DRM_ERROR("DRM_MODE_CONNECTOR_Unknown encountered\n"); +			continue; +		} + +		/* It should be allowed sometimes, but let's be safe for the moment. */ +		drm_connector->interlace_allowed = false; +		drm_connector->doublescan_allowed = false; + +		drm_connector_init(dev, drm_connector, &nv50_kms_connector_funcs, type); + +		/* Init DVI-I specific properties */ +		if (type == DRM_MODE_CONNECTOR_DVII) { +			drm_mode_create_dvi_i_properties(dev); +			drm_connector_attach_property(drm_connector, dev->mode_config.dvi_i_subconnector_property, 0); +			drm_connector_attach_property(drm_connector, dev->mode_config.dvi_i_select_subconnector_property, 0); +		} + +		/* If supported in the future, it will have to use the scalers internally and not expose them. */ +		if (type != DRM_MODE_CONNECTOR_SVIDEO) { +			drm_connector_attach_property(drm_connector, dev->mode_config.scaling_mode_property, nv50_kms_get_scaling_mode(drm_connector)); +		} + +		drm_connector_attach_property(drm_connector, dev->mode_config.dithering_mode_property, connector->use_dithering ? DRM_MODE_DITHERING_ON : DRM_MODE_DITHERING_OFF); + +		/* attach encoders, possibilities are analog + digital */ +		for (i = 0; i < 2; i++) { +			struct drm_encoder *drm_encoder = NULL; +			struct nv50_output *output = connector->to_output(connector, i); +			if (!output) +				continue; + +			drm_encoder = to_nv50_kms_encoder(output); +			if (!drm_encoder) { +				DRM_ERROR("No struct drm_connector to match struct nv50_output\n"); +				continue; +			} + +			drm_mode_connector_attach_encoder(drm_connector, drm_encoder); +		} + +		drm_sysfs_connector_add(drm_connector); +	} + +	return 0; +} + +/* + * Main functions + */ + +int nv50_kms_init(struct drm_device *dev) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_kms_priv *kms_priv = kzalloc(sizeof(struct nv50_kms_priv), GFP_KERNEL); +	struct nv50_display *display = NULL; +	int rval = 0; + +	if (!kms_priv) +		return -ENOMEM; + +	dev_priv->kms_priv = kms_priv; + +	/* function pointers */ +	/* an allocation interface that deals with the outside world, without polluting the core. */ +	dev_priv->alloc_crtc = nv50_kms_alloc_crtc; +	dev_priv->alloc_output = nv50_kms_alloc_output; +	dev_priv->alloc_connector = nv50_kms_alloc_connector; + +	dev_priv->free_crtc = nv50_kms_free_crtc; +	dev_priv->free_output = nv50_kms_free_output; +	dev_priv->free_connector = nv50_kms_free_connector; + +	/* bios is needed for tables. */ +	rval = nouveau_parse_bios(dev); +	if (rval != 0) +		goto out; + +	/* init basic kernel modesetting */ +	drm_mode_config_init(dev); + +	dev->mode_config.min_width = 0; +	dev->mode_config.min_height = 0; + +	dev->mode_config.funcs = (void *)&nv50_kms_mode_funcs; + +	dev->mode_config.max_width = 8192; +	dev->mode_config.max_height = 8192; + +	dev->mode_config.fb_base = dev_priv->fb_phys; + +	/* init kms lists */ +	INIT_LIST_HEAD(&kms_priv->crtcs); +	INIT_LIST_HEAD(&kms_priv->encoders); +	INIT_LIST_HEAD(&kms_priv->connectors); + +	/* init the internal core, must be done first. */ +	rval = nv50_display_create(dev); +	if (rval != 0) +		goto out; + +	display = nv50_get_display(dev); +	if (!display) { +		rval = -EINVAL; +		goto out; +	} + +	/* pre-init now */ +	rval = display->pre_init(display); +	if (rval != 0) +		goto out; + +	/* init external layer */ +	rval = nv50_kms_crtcs_init(dev); +	if (rval != 0) +		goto out; + +	rval = nv50_kms_encoders_init(dev); +	if (rval != 0) +		goto out; + +	rval = nv50_kms_connectors_init(dev); +	if (rval != 0) +		goto out; + +	/* init now, this'll kill the textmode */ +	rval = display->init(display); +	if (rval != 0) +		goto out; + +	/* process cmdbuffer */ +	display->update(display); + +	return 0; + +out: +	kfree(kms_priv); +	dev_priv->kms_priv = NULL; + +	return rval; +} + +int nv50_kms_destroy(struct drm_device *dev) +{ +	drm_mode_config_cleanup(dev); + +	return 0; +} + diff --git a/linux-core/nv50_kms_wrapper.h b/linux-core/nv50_kms_wrapper.h new file mode 100644 index 00000000..60384804 --- /dev/null +++ b/linux-core/nv50_kms_wrapper.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 __NV50_KMS_WRAPPER_H__ +#define __NV50_KMS_WRAPPER_H__ + +#include "drmP.h" +#include "drm.h" + +#include "nv50_display.h" +#include "nv50_crtc.h" +#include "nv50_cursor.h" +#include "nv50_lut.h" +#include "nv50_fb.h" +#include "nv50_output.h" +#include "nv50_connector.h" + +#include "drm_crtc.h" +#include "drm_edid.h" + +/* Link internal modesetting structure to interface. */ + +struct nv50_kms_crtc { +	struct list_head item; + +	struct nv50_crtc priv; +	struct drm_crtc pub; +}; + +struct nv50_kms_encoder { +	struct list_head item; + +	struct nv50_output priv; +	struct drm_encoder pub; +}; + +struct nv50_kms_connector { +	struct list_head item; + +	struct nv50_connector priv; +	struct drm_connector pub; +}; + +struct nv50_kms_priv { +	struct list_head crtcs; +	struct list_head encoders; +	struct list_head connectors; +}; + +/* Get private functions. */ +#define from_nv50_kms_crtc(x) container_of(x, struct nv50_kms_crtc, pub) +#define from_nv50_crtc(x) container_of(x, struct nv50_kms_crtc, priv) +#define from_nv50_kms_encoder(x) container_of(x, struct nv50_kms_encoder, pub) +#define from_nv50_output(x) container_of(x, struct nv50_kms_encoder, priv) +#define from_nv50_kms_connector(x) container_of(x, struct nv50_kms_connector, pub) +#define from_nv50_connector(x) container_of(x, struct nv50_kms_connector, priv) + +#define to_nv50_crtc(x) (&(from_nv50_kms_crtc(x)->priv)) +#define to_nv50_kms_crtc(x) (&(from_nv50_crtc(x)->pub)) +#define to_nv50_output(x) (&(from_nv50_kms_encoder(x)->priv)) +#define to_nv50_kms_encoder(x) (&(from_nv50_output(x)->pub)) +#define to_nv50_connector(x) (&(from_nv50_kms_connector(x)->priv)) +#define to_nv50_kms_connector(x) (&(from_nv50_connector(x)->pub)) + +struct nv50_kms_priv *nv50_get_kms_priv(struct drm_device *dev); +void nv50_kms_connector_detect_all(struct drm_device *dev); +bool nv50_kms_connector_get_digital(struct drm_connector *drm_connector); + +int nv50_kms_init(struct drm_device *dev); +int nv50_kms_destroy(struct drm_device *dev); + +#endif /* __NV50_KMS_WRAPPER_H__ */ diff --git a/linux-core/nv50_lut.c b/linux-core/nv50_lut.c new file mode 100644 index 00000000..7982a926 --- /dev/null +++ b/linux-core/nv50_lut.c @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 "nv50_lut.h" +#include "nv50_fb.h" +#include "nv50_crtc.h" +#include "nv50_display.h" + +static int nv50_lut_alloc(struct nv50_crtc *crtc) +{ +	struct mem_block *block; +	struct drm_file *file_priv = kzalloc(sizeof(struct drm_file), GFP_KERNEL); +	uint32_t flags = NOUVEAU_MEM_FB | NOUVEAU_MEM_MAPPED; +	int rval = 0; + +	NV50_DEBUG("\n"); + +	if (!file_priv) +		return -ENOMEM; + +	/* Any file_priv should do as it's pointer is used as identification. */ +	block = nouveau_mem_alloc(crtc->dev, 0, 4096, flags, file_priv); + +	if (!block) { +		rval = -ENOMEM; +		goto out; +	} + +	crtc->lut->block = block; + +	return 0; + +out: +	if (file_priv) +		kfree(file_priv); + +	return rval; +} + +static int nv50_lut_free(struct nv50_crtc *crtc) +{ +	struct drm_file *file_priv = crtc->lut->block->file_priv; + +	NV50_DEBUG("\n"); + +	nouveau_mem_free(crtc->dev, crtc->lut->block); + +	kfree(file_priv); + +	return 0; +} + +#define NV50_LUT_INDEX(val, w) ((val << (8 - w)) | (val >> ((w << 1) - 8))) +static int nv50_lut_set(struct nv50_crtc *crtc, uint16_t *red, uint16_t *green, uint16_t *blue) +{ +	uint32_t index = 0, i; +	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; +	void __iomem *lut = NULL; + +	NV50_DEBUG("\n"); + +	if (!crtc->lut || !crtc->lut->block) { +		DRM_ERROR("Something wrong with the LUT\n"); +		return -EINVAL; +	} + +	/* 16 bits, red, green, blue, unused, total of 64 bits per index */ +	/* maybe switch to ioremap_wc once it becomes available. */ +	lut = ioremap(dev_priv->fb_phys + crtc->lut->block->start, crtc->lut->block->size); +	if (!lut) { +		DRM_ERROR("ioremap failed on LUT\n"); +		return -EINVAL; +	} + +	/* 10 bits lut, with 14 bits values. */ +	switch (crtc->fb->depth) { +		case 15: +			/* R5G5B5 */ +			for (i = 0; i < 32; i++) { +				index = NV50_LUT_INDEX(i, 5); +				writew(red[i] >> 2, lut + 8*index + 0); +				writew(green[i] >> 2, lut + 8*index + 2); +				writew(blue[i] >> 2, lut + 8*index + 4); +			} +			break; +		case 16: +			/* R5G6B5 */ +			for (i = 0; i < 32; i++) { +				index = NV50_LUT_INDEX(i, 5); +				writew(red[i] >> 2, lut + 8*index + 0); +				writew(blue[i] >> 2, lut + 8*index + 4); +			} + +			/* Green has an extra bit. */ +			for (i = 0; i < 64; i++) { +				index = NV50_LUT_INDEX(i, 6); +				writew(green[i] >> 2, lut + 8*index + 2); +			} +			break; +		default: +			/* R8G8B8 */ +			for (i = 0; i < 256; i++) { +				writew(red[i] >> 2, lut + 8*i + 0); +				writew(green[i] >> 2, lut + 8*i + 2); +				writew(blue[i] >> 2, lut + 8*i + 4); +			} +			break; +	} + +	crtc->lut->depth = crtc->fb->depth; + +	/* Cleaning time. */ +	iounmap(lut); + +	return 0; +} + +int nv50_lut_create(struct nv50_crtc *crtc) +{ +	int rval = 0; + +	NV50_DEBUG("\n"); + +	if (!crtc) +		return -EINVAL; + +	crtc->lut = kzalloc(sizeof(struct nv50_lut), GFP_KERNEL); + +	if (!crtc->lut) +		return -ENOMEM; + +	rval = nv50_lut_alloc(crtc); +	if (rval != 0) { +		goto out; +	} + +	/* lut will be inited when fb is bound */ +	crtc->lut->depth = 0; + +	/* function pointers */ +	crtc->lut->set = nv50_lut_set; + +	return 0; + +out: +	if (crtc->lut) +		kfree(crtc->lut); + +	return rval; +} + +int nv50_lut_destroy(struct nv50_crtc *crtc) +{ +	int rval = 0; + +	NV50_DEBUG("\n"); + +	if (!crtc) +		return -EINVAL; + +	rval = nv50_lut_free(crtc); + +	kfree(crtc->lut); +	crtc->lut = NULL; + +	if (rval != 0) +		return rval; + +	return 0; +} diff --git a/linux-core/nv50_lut.h b/linux-core/nv50_lut.h new file mode 100644 index 00000000..06704830 --- /dev/null +++ b/linux-core/nv50_lut.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 __NV50_LUT_H__ +#define __NV50_LUT_H__ + +#include "nv50_display.h" + +struct nv50_crtc; + +struct nv50_lut { +	struct mem_block *block; + +	int depth; /* check against fb to see if it needs to be reuploaded */ + +	int (*set) (struct nv50_crtc *crtc, uint16_t *red, uint16_t *green, uint16_t *blue); +}; + +int nv50_lut_create(struct nv50_crtc *crtc); +int nv50_lut_destroy(struct nv50_crtc *crtc); + +#endif /* __NV50_LUT_H__ */ diff --git a/linux-core/nv50_output.c b/linux-core/nv50_output.c new file mode 100644 index 00000000..de0017b9 --- /dev/null +++ b/linux-core/nv50_output.c @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 "nv50_output.h" + +int nv50_output_or_offset(struct nv50_output *output) +{ +	struct drm_nouveau_private *dev_priv = output->dev->dev_private; +	return ffs(dev_priv->dcb_table.entry[output->dcb_entry].or) - 1; +} diff --git a/linux-core/nv50_output.h b/linux-core/nv50_output.h new file mode 100644 index 00000000..ac6d714f --- /dev/null +++ b/linux-core/nv50_output.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 __NV50_OUTPUT_H__ +#define __NV50_OUTPUT_H__ + +#include "nv50_crtc.h" + +#define OUTPUT_UNKNOWN 0 +#define OUTPUT_DAC 1 +#define OUTPUT_TMDS 2 +#define OUTPUT_LVDS 3 +#define OUTPUT_TV 4 + +struct nv50_output { +	struct list_head item; + +	struct drm_device *dev; +	int bus; +	int dcb_entry; +	int type; + +	struct nv50_crtc *crtc; +	struct nouveau_hw_mode *native_mode; + +	int (*validate_mode) (struct nv50_output *output, struct nouveau_hw_mode *mode); +	int (*execute_mode) (struct nv50_output *output, bool disconnect); +	int (*set_clock_mode) (struct nv50_output *output); +	/* this is not a normal modeset call, it is a direct register write, so it's executed immediately */ +	int (*set_power_mode) (struct nv50_output *output, int mode); +	int (*detect) (struct nv50_output *output); +	int (*destroy) (struct nv50_output *output); +}; + +int nv50_output_or_offset(struct nv50_output *output); +int nv50_sor_create(struct drm_device *dev, int dcb_entry); +int nv50_dac_create(struct drm_device *dev, int dcb_entry); + +#endif /* __NV50_OUTPUT_H__ */ diff --git a/linux-core/nv50_sor.c b/linux-core/nv50_sor.c new file mode 100644 index 00000000..41116923 --- /dev/null +++ b/linux-core/nv50_sor.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2008 Maarten Maathuis. + * 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 THE COPYRIGHT OWNER(S) 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 "nv50_output.h" + +static int nv50_sor_validate_mode(struct nv50_output *output, struct nouveau_hw_mode *mode) +{ +	NV50_DEBUG("\n"); + +	if (mode->clock > 165000) /* no dual link until we figure it out completely */ +		return MODE_CLOCK_HIGH; + +	if (mode->clock < 25000) +		return MODE_CLOCK_LOW; + +	if (output->native_mode->hdisplay > 0 && output->native_mode->vdisplay > 0) { +		if (mode->hdisplay > output->native_mode->hdisplay || mode->vdisplay > output->native_mode->vdisplay) +			return MODE_PANEL; +	} + +	return MODE_OK; +} + +static int nv50_sor_execute_mode(struct nv50_output *output, bool disconnect) +{ +	struct drm_nouveau_private *dev_priv = output->dev->dev_private; +	struct nv50_crtc *crtc = output->crtc; +	struct nouveau_hw_mode *desired_mode = NULL; + +	uint32_t offset = nv50_output_or_offset(output) * 0x40; + +	uint32_t mode_ctl = NV50_SOR_MODE_CTRL_OFF; + +	NV50_DEBUG("or %d\n", nv50_output_or_offset(output)); + +	if (disconnect) { +		NV50_DEBUG("Disconnecting SOR\n"); +		OUT_MODE(NV50_SOR0_MODE_CTRL + offset, mode_ctl); +		return 0; +	} + +	desired_mode = (crtc->use_native_mode ? crtc->native_mode : crtc->mode); + +	if (output->type == OUTPUT_LVDS) { +		mode_ctl |= NV50_SOR_MODE_CTRL_LVDS; +	} else { +		mode_ctl |= NV50_SOR_MODE_CTRL_TMDS; +		if (desired_mode->clock > 165000) +			mode_ctl |= NV50_SOR_MODE_CTRL_TMDS_DUAL_LINK; +	} + +	if (crtc->index == 1) +		mode_ctl |= NV50_SOR_MODE_CTRL_CRTC1; +	else +		mode_ctl |= NV50_SOR_MODE_CTRL_CRTC0; + +	if (desired_mode->flags & DRM_MODE_FLAG_NHSYNC) +		mode_ctl |= NV50_SOR_MODE_CTRL_NHSYNC; + +	if (desired_mode->flags & DRM_MODE_FLAG_NVSYNC) +		mode_ctl |= NV50_SOR_MODE_CTRL_NVSYNC; + +	OUT_MODE(NV50_SOR0_MODE_CTRL + offset, mode_ctl); + +	return 0; +} + +static int nv50_sor_set_clock_mode(struct nv50_output *output) +{ +	struct drm_nouveau_private *dev_priv = output->dev->dev_private; +	struct nv50_crtc *crtc = output->crtc; + +	uint32_t limit = 165000; +	struct nouveau_hw_mode *hw_mode; + +	NV50_DEBUG("or %d\n", nv50_output_or_offset(output)); + +	/* We don't yet know what to do, if anything at all. */ +	if (output->type == OUTPUT_LVDS) +		return 0; + +	if (crtc->use_native_mode) +		hw_mode = crtc->native_mode; +	else +		hw_mode = crtc->mode; + +	/* 0x70000 was a late addition to nv, mentioned as fixing tmds initialisation on certain gpu's. */ +	/* I presume it's some kind of clock setting, but what precisely i do not know. */ +	NV_WRITE(NV50_PDISPLAY_SOR_CLK_CLK_CTRL2(nv50_output_or_offset(output)), 0x70000 | ((hw_mode->clock > limit) ? 0x101 : 0)); + +	return 0; +} + +static int nv50_sor_set_power_mode(struct nv50_output *output, int mode) +{ +	struct drm_nouveau_private *dev_priv = output->dev->dev_private; +	uint32_t val; +	int or = nv50_output_or_offset(output); + +	NV50_DEBUG("or %d\n", nv50_output_or_offset(output)); + +	/* wait for it to be done */ +	while (NV_READ(NV50_PDISPLAY_SOR_REGS_DPMS_CTRL(or)) & NV50_PDISPLAY_SOR_REGS_DPMS_CTRL_PENDING); + +	val = NV_READ(NV50_PDISPLAY_SOR_REGS_DPMS_CTRL(or)); + +	if (mode == DRM_MODE_DPMS_ON) +		val |= NV50_PDISPLAY_SOR_REGS_DPMS_CTRL_ON; +	else +		val &= ~NV50_PDISPLAY_SOR_REGS_DPMS_CTRL_ON; + +	NV_WRITE(NV50_PDISPLAY_SOR_REGS_DPMS_CTRL(or), val | NV50_PDISPLAY_SOR_REGS_DPMS_CTRL_PENDING); + +	return 0; +} + +static int nv50_sor_destroy(struct nv50_output *output) +{ +	struct drm_device *dev = output->dev; +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_display *display = nv50_get_display(dev); + +	NV50_DEBUG("\n"); + +	if (!display || !output) +		return -EINVAL; + +	list_del(&output->item); + +	kfree(output->native_mode); +	if (dev_priv->free_output) +		dev_priv->free_output(output); + +	return 0; +} + +int nv50_sor_create(struct drm_device *dev, int dcb_entry) +{ +	struct drm_nouveau_private *dev_priv = dev->dev_private; +	struct nv50_output *output = NULL; +	struct nv50_display *display = NULL; +	struct dcb_entry *entry = NULL; +	int rval = 0; + +	NV50_DEBUG("\n"); + +	/* This allows the public layer to do it's thing. */ +	if (dev_priv->alloc_output) +		output = dev_priv->alloc_output(dev); + +	if (!output) +		return -ENOMEM; + +	output->dev = dev; + +	display = nv50_get_display(dev); +	if (!display) { +		rval = -EINVAL; +		goto out; +	} + +	entry = &dev_priv->dcb_table.entry[dcb_entry]; +	if (!entry) { +		rval = -EINVAL; +		goto out; +	} + +	switch (entry->type) { +		case DCB_OUTPUT_TMDS: +			output->type = OUTPUT_TMDS; +			DRM_INFO("Detected a TMDS output\n"); +			break; +		case DCB_OUTPUT_LVDS: +			output->type = OUTPUT_LVDS; +			DRM_INFO("Detected a LVDS output\n"); +			break; +		default: +			rval = -EINVAL; +			goto out; +	} + +	output->dcb_entry = dcb_entry; +	output->bus = entry->bus; + +	list_add_tail(&output->item, &display->outputs); + +	output->native_mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL); +	if (!output->native_mode) { +		rval = -ENOMEM; +		goto out; +	} + +	/* Set function pointers. */ +	output->validate_mode = nv50_sor_validate_mode; +	output->execute_mode = nv50_sor_execute_mode; +	output->set_clock_mode = nv50_sor_set_clock_mode; +	output->set_power_mode = nv50_sor_set_power_mode; +	output->detect = NULL; +	output->destroy = nv50_sor_destroy; + +	/* Some default state, unknown what it precisely means. */ +	if (output->type == OUTPUT_TMDS) { +		NV_WRITE(NV50_PDISPLAY_SOR_REGS_UNK_00C(nv50_output_or_offset(output)), 0x03010700); +		NV_WRITE(NV50_PDISPLAY_SOR_REGS_UNK_010(nv50_output_or_offset(output)), 0x0000152f); +		NV_WRITE(NV50_PDISPLAY_SOR_REGS_UNK_014(nv50_output_or_offset(output)), 0x00000000); +		NV_WRITE(NV50_PDISPLAY_SOR_REGS_UNK_018(nv50_output_or_offset(output)), 0x00245af8); +	} + +	return 0; + +out: +	if (output->native_mode) +		kfree(output->native_mode); +	if (dev_priv->free_output) +		dev_priv->free_output(output); +	return rval; +} diff --git a/linux-core/radeon_atombios.c b/linux-core/radeon_atombios.c new file mode 100644 index 00000000..ee628732 --- /dev/null +++ b/linux-core/radeon_atombios.c @@ -0,0 +1,361 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: Dave Airlie + *          Alex Deucher + */ +#include "drmP.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + +#include "atom.h" +#include "atom-bits.h" + + +union atom_supported_devices { +  struct _ATOM_SUPPORTED_DEVICES_INFO info; +  struct _ATOM_SUPPORTED_DEVICES_INFO_2 info_2; +  struct _ATOM_SUPPORTED_DEVICES_INFO_2d1 info_2d1; +}; + +static inline struct radeon_i2c_bus_rec radeon_lookup_gpio_for_ddc(struct drm_device *dev, uint8_t id) +{ +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct atom_context *ctx = dev_priv->mode_info.atom_context; +	ATOM_GPIO_I2C_ASSIGMENT gpio; +	struct radeon_i2c_bus_rec i2c; +	int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info); +	struct _ATOM_GPIO_I2C_INFO *i2c_info; +	uint16_t data_offset; + +	memset(&i2c, 0, sizeof(struct radeon_i2c_bus_rec)); +	i2c.valid = false; + +	atom_parse_data_header(ctx, index, NULL, NULL, NULL, &data_offset); + +	i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset); + +	gpio = i2c_info->asGPIO_Info[id]; + +	i2c.mask_clk_reg = le16_to_cpu(gpio.usClkMaskRegisterIndex) * 4; +	i2c.mask_data_reg = le16_to_cpu(gpio.usDataMaskRegisterIndex) * 4; +	i2c.put_clk_reg = le16_to_cpu(gpio.usClkEnRegisterIndex) * 4; +	i2c.put_data_reg = le16_to_cpu(gpio.usDataEnRegisterIndex) * 4; +	i2c.get_clk_reg = le16_to_cpu(gpio.usClkY_RegisterIndex) * 4; +	i2c.get_data_reg = le16_to_cpu(gpio.usDataY_RegisterIndex) * 4; +	i2c.mask_clk_mask = (1 << gpio.ucClkMaskShift); +	i2c.mask_data_mask = (1 << gpio.ucDataMaskShift); +	i2c.put_clk_mask = (1 << gpio.ucClkEnShift); +	i2c.put_data_mask = (1 << gpio.ucDataEnShift); +	i2c.get_clk_mask = (1 << gpio.ucClkY_Shift); +	i2c.get_data_mask = (1 <<  gpio.ucDataY_Shift); +	i2c.valid = true; + +	return i2c; +} + +static void radeon_atom_apply_quirks(struct drm_device *dev, int index) +{ +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_mode_info *mode_info = &dev_priv->mode_info; + +	if ((dev->pdev->device == 0x791e) && +	    (dev->pdev->subsystem_vendor == 0x1043) && +	    (dev->pdev->subsystem_device == 0x826d)) { +		if ((mode_info->bios_connector[index].connector_type == CONNECTOR_HDMI_TYPE_A) && +		    (mode_info->bios_connector[index].tmds_type == TMDS_LVTMA)) { +			mode_info->bios_connector[index].connector_type = CONNECTOR_DVI_D; +		} +	} + +	if ((dev->pdev->device == 0x5653) && +	    (dev->pdev->subsystem_vendor == 0x1462) && +	    (dev->pdev->subsystem_device == 0x0291)) { +		if (mode_info->bios_connector[index].connector_type == CONNECTOR_LVDS) { +			mode_info->bios_connector[index].ddc_i2c.valid = false; +		} +	} +}    + +bool radeon_get_atom_connector_info_from_bios_connector_table(struct drm_device *dev) +{ +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_mode_info *mode_info = &dev_priv->mode_info; +	struct atom_context *ctx = mode_info->atom_context; +	int index = GetIndexIntoMasterTable(DATA, SupportedDevicesInfo); +	uint16_t size, data_offset; +	uint8_t frev, crev; +	uint16_t device_support; + +	union atom_supported_devices *supported_devices; +	int i,j; +	atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset); + +	supported_devices = (union atom_supported_devices *)(ctx->bios + data_offset); + +	device_support = le16_to_cpu(supported_devices->info.usDeviceSupport); + +	for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) { + +		ATOM_CONNECTOR_INFO_I2C ci = supported_devices->info.asConnInfo[i]; + +		if (!(device_support & (1 << i))) { +			mode_info->bios_connector[i].valid = false; +			continue; +		} + +		if (i == ATOM_DEVICE_CV_INDEX) { +			DRM_DEBUG("Skipping Component Video\n"); +			mode_info->bios_connector[i].valid = false; +			continue; +		} + +		if (i == ATOM_DEVICE_TV1_INDEX) { +			DRM_DEBUG("Skipping TV Out\n"); +			mode_info->bios_connector[i].valid = false; +			continue; +		} + +		mode_info->bios_connector[i].valid = true; +		mode_info->bios_connector[i].output_id = ci.sucI2cId.sbfAccess.bfI2C_LineMux; +		mode_info->bios_connector[i].devices = 1 << i; +		mode_info->bios_connector[i].connector_type = ci.sucConnectorInfo.sbfAccess.bfConnectorType; + +		if (mode_info->bios_connector[i].connector_type == CONNECTOR_NONE) { +			mode_info->bios_connector[i].valid = false; +			continue; +		} + +		mode_info->bios_connector[i].dac_type = ci.sucConnectorInfo.sbfAccess.bfAssociatedDAC; +		 +		if ((i == ATOM_DEVICE_TV1_INDEX) || +		    (i == ATOM_DEVICE_TV2_INDEX) || +		    (i == ATOM_DEVICE_TV1_INDEX)) +			mode_info->bios_connector[i].ddc_i2c.valid = false; +		else if ((dev_priv->chip_family == CHIP_RS600) || +			 (dev_priv->chip_family == CHIP_RS690) || +			 (dev_priv->chip_family == CHIP_RS740)) { +			if ((i == ATOM_DEVICE_DFP2_INDEX) || (i == ATOM_DEVICE_DFP3_INDEX)) +				mode_info->bios_connector[i].ddc_i2c = +					radeon_lookup_gpio_for_ddc(dev, ci.sucI2cId.sbfAccess.bfI2C_LineMux + 1); +			else +				mode_info->bios_connector[i].ddc_i2c = +					radeon_lookup_gpio_for_ddc(dev, ci.sucI2cId.sbfAccess.bfI2C_LineMux); +		} else +			mode_info->bios_connector[i].ddc_i2c =  +				radeon_lookup_gpio_for_ddc(dev, ci.sucI2cId.sbfAccess.bfI2C_LineMux); + +		if (i == ATOM_DEVICE_DFP1_INDEX) +			mode_info->bios_connector[i].tmds_type = TMDS_INT; +		else if (i == ATOM_DEVICE_DFP2_INDEX) { +			if ((dev_priv->chip_family == CHIP_RS600) || +			    (dev_priv->chip_family == CHIP_RS690) || +			    (dev_priv->chip_family == CHIP_RS740)) +				mode_info->bios_connector[i].tmds_type = TMDS_DDIA; +			else +				mode_info->bios_connector[i].tmds_type = TMDS_EXT; +		} else if (i == ATOM_DEVICE_DFP3_INDEX) +			mode_info->bios_connector[i].tmds_type = TMDS_LVTMA; +		else +			mode_info->bios_connector[i].tmds_type = TMDS_NONE; + +		/* Always set the connector type to VGA for CRT1/CRT2. if they are +		 * shared with a DVI port, we'll pick up the DVI connector below when we +		 * merge the outputs +		 */ +		if ((i == ATOM_DEVICE_CRT1_INDEX || i == ATOM_DEVICE_CRT2_INDEX) && +		    (mode_info->bios_connector[i].connector_type == CONNECTOR_DVI_I || +		     mode_info->bios_connector[i].connector_type == CONNECTOR_DVI_D || +		     mode_info->bios_connector[i].connector_type == CONNECTOR_DVI_A)) { +			mode_info->bios_connector[i].connector_type = CONNECTOR_VGA; +		} + +		if (crev > 1) { +			ATOM_CONNECTOR_INC_SRC_BITMAP isb = supported_devices->info_2.asIntSrcInfo[i]; + +			switch(isb.ucIntSrcBitmap) { +			case 0x4: +				mode_info->bios_connector[i].hpd_mask = 0x1; +				break; +			case 0xa: +				mode_info->bios_connector[i].hpd_mask = 0x100; +				break; +			default: +				mode_info->bios_connector[i].hpd_mask = 0; +				break; +			} +		} else { +			mode_info->bios_connector[i].hpd_mask = 0; +		} + +		radeon_atom_apply_quirks(dev, i); +	} + +	/* CRTs/DFPs may share a port */ +	for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) { +		if (!mode_info->bios_connector[i].valid) +			continue; + +		for (j = 0; j < ATOM_MAX_SUPPORTED_DEVICE; j++) { +			if (mode_info->bios_connector[j].valid && (i != j)) { +				if (mode_info->bios_connector[i].output_id == +				    mode_info->bios_connector[j].output_id) { +					if (((i == ATOM_DEVICE_DFP1_INDEX) || +					     (i == ATOM_DEVICE_DFP2_INDEX) || +					     (i == ATOM_DEVICE_DFP3_INDEX)) && +					    ((j == ATOM_DEVICE_CRT1_INDEX) || +					     (j == ATOM_DEVICE_CRT2_INDEX))) { +						mode_info->bios_connector[i].dac_type = mode_info->bios_connector[j].dac_type; +						mode_info->bios_connector[i].devices |= mode_info->bios_connector[j].devices; +						mode_info->bios_connector[i].hpd_mask = mode_info->bios_connector[j].hpd_mask; +						mode_info->bios_connector[j].valid = false; +					} else if (((j == ATOM_DEVICE_DFP1_INDEX) || +						    (j == ATOM_DEVICE_DFP2_INDEX) || +						    (j == ATOM_DEVICE_DFP3_INDEX)) && +						   ((i == ATOM_DEVICE_CRT1_INDEX) || +						    (i == ATOM_DEVICE_CRT2_INDEX))) { +						mode_info->bios_connector[j].dac_type = mode_info->bios_connector[i].dac_type; +						mode_info->bios_connector[j].devices |= mode_info->bios_connector[i].devices; +						mode_info->bios_connector[j].hpd_mask = mode_info->bios_connector[i].hpd_mask; +						mode_info->bios_connector[i].valid = false; +					} +				} +			} +		} +	} + +	 +	DRM_DEBUG("BIOS Connector table\n"); +	for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) { +		if (!mode_info->bios_connector[i].valid) +			continue; + +		DRM_DEBUG("Port %d: ddc_type 0x%x, dac_type %d, tmds_type %d, connector type %d, hpd_mask %d\n", +			  i, mode_info->bios_connector[i].ddc_i2c.mask_clk_reg, +			  mode_info->bios_connector[i].dac_type, +			  mode_info->bios_connector[i].tmds_type, +			  mode_info->bios_connector[i].connector_type, +			  mode_info->bios_connector[i].hpd_mask); +	} +	return true; +} + +union firmware_info { +	ATOM_FIRMWARE_INFO info; +	ATOM_FIRMWARE_INFO_V1_2 info_12; +	ATOM_FIRMWARE_INFO_V1_3 info_13; +	ATOM_FIRMWARE_INFO_V1_4 info_14; +}; +	 +bool radeon_atom_get_clock_info(struct drm_device *dev) +{ +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_mode_info *mode_info = &dev_priv->mode_info; +	int index = GetIndexIntoMasterTable(DATA, FirmwareInfo); +	union firmware_info *firmware_info; +	uint8_t frev, crev; +	struct radeon_pll *pll = &mode_info->pll; +	uint16_t data_offset; + +	atom_parse_data_header(mode_info->atom_context, index, NULL, &frev, &crev, &data_offset); + +	firmware_info = (union firmware_info *)(mode_info->atom_context->bios + data_offset); + +	pll->reference_freq = le16_to_cpu(firmware_info->info.usReferenceClock); +	pll->reference_div = 0; + +	pll->pll_out_min = le16_to_cpu(firmware_info->info.usMinPixelClockPLL_Output); +	pll->pll_out_max = le32_to_cpu(firmware_info->info.ulMaxPixelClockPLL_Output);	 +	 +	if (pll->pll_out_min == 0) { +		if (radeon_is_avivo(dev_priv)) +			pll->pll_out_min = 64800; +		else +			pll->pll_out_min = 20000; +	} + +	pll->pll_in_min = le16_to_cpu(firmware_info->info.usMinPixelClockPLL_Input); +	pll->pll_in_max = le16_to_cpu(firmware_info->info.usMaxPixelClockPLL_Input); + +	pll->xclk = le16_to_cpu(firmware_info->info.usMaxPixelClock); + +	return true;				      +} + +union lvds_info { +	struct _ATOM_LVDS_INFO info; +	struct _ATOM_LVDS_INFO_V12 info_12; +}; + +void radeon_get_lvds_info(struct radeon_encoder *encoder) +{ +	struct drm_device *dev = encoder->base.dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_mode_info *mode_info = &dev_priv->mode_info; +	int index = GetIndexIntoMasterTable(DATA, LVDS_Info); +	uint16_t data_offset; +	union lvds_info *lvds_info; +	uint8_t frev, crev; + +	atom_parse_data_header(mode_info->atom_context, index, NULL, &frev, &crev, &data_offset); + +	lvds_info = (union lvds_info *)(mode_info->atom_context->bios + data_offset); + +	encoder->dotclock = le16_to_cpu(lvds_info->info.sLCDTiming.usPixClk) * 10; +	encoder->panel_xres = le16_to_cpu(lvds_info->info.sLCDTiming.usHActive); +	encoder->panel_yres = le16_to_cpu(lvds_info->info.sLCDTiming.usVActive); +	encoder->hblank = le16_to_cpu(lvds_info->info.sLCDTiming.usHBlanking_Time); +	encoder->hoverplus = le16_to_cpu(lvds_info->info.sLCDTiming.usHSyncOffset); +	encoder->hsync_width = le16_to_cpu(lvds_info->info.sLCDTiming.usHSyncWidth); + +	encoder->vblank = le16_to_cpu(lvds_info->info.sLCDTiming.usVBlanking_Time); +	encoder->hoverplus = le16_to_cpu(lvds_info->info.sLCDTiming.usVSyncOffset); +	encoder->hsync_width = le16_to_cpu(lvds_info->info.sLCDTiming.usVSyncWidth); +	encoder->panel_pwr_delay = le16_to_cpu(lvds_info->info.usOffDelayInMs);  +} + +void radeon_atom_dyn_clk_setup(struct drm_device *dev, int enable) +{ +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_mode_info *mode_info = &dev_priv->mode_info; +	struct atom_context *ctx = mode_info->atom_context; +	DYNAMIC_CLOCK_GATING_PS_ALLOCATION args; +	int index = GetIndexIntoMasterTable(COMMAND, DynamicClockGating); + +	args.ucEnable = enable; +	 +	atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +} + +void radeon_atom_static_pwrmgt_setup(struct drm_device *dev, int enable) +{ +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_mode_info *mode_info = &dev_priv->mode_info; +	struct atom_context *ctx = mode_info->atom_context; +	ENABLE_ASIC_STATIC_PWR_MGT_PS_ALLOCATION args; +	int index = GetIndexIntoMasterTable(COMMAND, EnableASIC_StaticPwrMgt); + +	args.ucEnable = enable; +	 +	atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +} + diff --git a/linux-core/radeon_buffer.c b/linux-core/radeon_buffer.c new file mode 100644 index 00000000..e9ba11d4 --- /dev/null +++ b/linux-core/radeon_buffer.c @@ -0,0 +1,266 @@ +/************************************************************************** + *  + * Copyright 2007 Dave Airlie + * 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 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 + * THE COPYRIGHT HOLDERS, AUTHORS 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. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + *  + *  + **************************************************************************/ +/* + * Authors: Dave Airlie <airlied@linux.ie> + */ + +#include "drmP.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + +struct drm_ttm_backend *radeon_create_ttm_backend_entry(struct drm_device * dev) +{ +	drm_radeon_private_t *dev_priv = dev->dev_private; + +	if(dev_priv->flags & RADEON_IS_AGP) +		return drm_agp_init_ttm(dev); +	else +		return ati_pcigart_init_ttm(dev, &dev_priv->gart_info, radeon_gart_flush); +} + +int radeon_fence_types(struct drm_buffer_object *bo, uint32_t * class, uint32_t * type) +{ +	*class = 0; +	*type = 1; +	return 0; +} + +int radeon_invalidate_caches(struct drm_device * dev, uint64_t flags) +{ +	drm_radeon_private_t *dev_priv = dev->dev_private; +	RING_LOCALS; + +	BEGIN_RING(4); +	RADEON_FLUSH_CACHE(); +	RADEON_FLUSH_ZCACHE(); +	ADVANCE_RING(); +	return 0; +} + +int radeon_init_mem_type(struct drm_device * dev, uint32_t type, +			 struct drm_mem_type_manager * man) +{ +	drm_radeon_private_t *dev_priv = dev->dev_private; + +	switch (type) { +	case DRM_BO_MEM_LOCAL: +		man->flags = _DRM_FLAG_MEMTYPE_MAPPABLE | +		    _DRM_FLAG_MEMTYPE_CACHED; +		man->drm_bus_maptype = 0; +		break; +	case DRM_BO_MEM_VRAM: +		man->flags =  _DRM_FLAG_MEMTYPE_FIXED | _DRM_FLAG_MEMTYPE_MAPPABLE | _DRM_FLAG_NEEDS_IOREMAP; +		man->io_addr = NULL; +		man->drm_bus_maptype = _DRM_FRAME_BUFFER; +		man->io_offset = drm_get_resource_start(dev, 0); +		man->io_size = drm_get_resource_len(dev, 0); +		break; +	case DRM_BO_MEM_TT: +		if (dev_priv->flags & RADEON_IS_AGP) { +			if (!(drm_core_has_AGP(dev) && dev->agp)) { +				DRM_ERROR("AGP is not enabled for memory type %u\n", +					  (unsigned)type); +				return -EINVAL; +			} +			man->io_offset = dev->agp->agp_info.aper_base; +			man->io_size = dev->agp->agp_info.aper_size * 1024 * 1024; +			man->io_addr = NULL; +			man->flags = _DRM_FLAG_MEMTYPE_MAPPABLE | +				_DRM_FLAG_MEMTYPE_CSELECT | _DRM_FLAG_NEEDS_IOREMAP; +			man->drm_bus_maptype = _DRM_AGP; +		} else { +			man->io_offset = dev_priv->gart_vm_start; +			man->io_size = dev_priv->gart_size; +			man->io_addr = NULL; +			man->flags = _DRM_FLAG_MEMTYPE_CSELECT | _DRM_FLAG_MEMTYPE_MAPPABLE | _DRM_FLAG_MEMTYPE_CMA; +			man->drm_bus_maptype = _DRM_SCATTER_GATHER; +		} +		break; +	default: +		DRM_ERROR("Unsupported memory type %u\n", (unsigned)type); +		return -EINVAL; +	} +	return 0; +} + +static void radeon_emit_copy_blit(struct drm_device * dev, +				  uint32_t src_offset, +				  uint32_t dst_offset, +				  uint32_t pages, int direction) +{ +	uint32_t cur_pages; +	uint32_t stride = PAGE_SIZE; +	drm_radeon_private_t *dev_priv = dev->dev_private; +	uint32_t format, height; +	RING_LOCALS; + +	if (!dev_priv) +		return; + +	/* 32-bit copy format */ +	format = RADEON_COLOR_FORMAT_ARGB8888; + +	/* radeon limited to 16k stride */ +	stride &= 0x3fff; +	while(pages > 0) { +		cur_pages = pages; +		if (cur_pages > 2048) +			cur_pages = 2048; +		pages -= cur_pages; + +		/* needs verification */ +		BEGIN_RING(7);		 +		OUT_RING(CP_PACKET3(RADEON_CNTL_BITBLT_MULTI, 5)); +		OUT_RING(RADEON_GMC_SRC_PITCH_OFFSET_CNTL | +			 RADEON_GMC_DST_PITCH_OFFSET_CNTL | +			 RADEON_GMC_BRUSH_NONE | +			 (format << 8) | +			 RADEON_GMC_SRC_DATATYPE_COLOR | +			 RADEON_ROP3_S | +			 RADEON_DP_SRC_SOURCE_MEMORY | +			 RADEON_GMC_CLR_CMP_CNTL_DIS | RADEON_GMC_WR_MSK_DIS); +		if (direction) { +			OUT_RING((stride << 22) | (src_offset >> 10)); +			OUT_RING((stride << 22) | (dst_offset >> 10)); +		} else { +			OUT_RING((stride << 22) | (dst_offset >> 10)); +			OUT_RING((stride << 22) | (src_offset >> 10)); +		} +		OUT_RING(0); +		OUT_RING(pages); /* x - y */ +		OUT_RING((stride << 16) | cur_pages); +		ADVANCE_RING(); +	} + +	BEGIN_RING(2); +	RADEON_WAIT_UNTIL_2D_IDLE(); +	ADVANCE_RING(); + +	return; +} + +static int radeon_move_blit(struct drm_buffer_object * bo, +			    int evict, int no_wait, struct drm_bo_mem_reg *new_mem) +{ +	struct drm_bo_mem_reg *old_mem = &bo->mem; +	int dir = 0; + +	if ((old_mem->mem_type == new_mem->mem_type) && +	    (new_mem->mm_node->start < +	     old_mem->mm_node->start + old_mem->mm_node->size)) { +		dir = 1; +	} + +	radeon_emit_copy_blit(bo->dev, +			      old_mem->mm_node->start << PAGE_SHIFT, +			      new_mem->mm_node->start << PAGE_SHIFT, +			      new_mem->num_pages, dir); + +	 +	return drm_bo_move_accel_cleanup(bo, evict, no_wait, 0, +					 DRM_FENCE_TYPE_EXE, 0, +					 new_mem); +} + +static int radeon_move_flip(struct drm_buffer_object * bo, +			    int evict, int no_wait, struct drm_bo_mem_reg * new_mem) +{ +	struct drm_device *dev = bo->dev; +	struct drm_bo_mem_reg tmp_mem; +	int ret; + +	tmp_mem = *new_mem; +	tmp_mem.mm_node = NULL; +	//	tmp_mem.mask = DRM_BO_FLAG_MEM_TT | +	//	    DRM_BO_FLAG_CACHED | DRM_BO_FLAG_FORCE_CACHING; + +	ret = drm_bo_mem_space(bo, &tmp_mem, no_wait); +	if (ret) +		return ret; + +	ret = drm_ttm_bind(bo->ttm, &tmp_mem); +	if (ret) +		goto out_cleanup; + +	ret = radeon_move_blit(bo, 1, no_wait, &tmp_mem); +	if (ret) +		goto out_cleanup; + +	ret = drm_bo_move_ttm(bo, evict, no_wait, new_mem); +out_cleanup: +	if (tmp_mem.mm_node) { +		mutex_lock(&dev->struct_mutex); +		if (tmp_mem.mm_node != bo->pinned_node) +			drm_memrange_put_block(tmp_mem.mm_node); +		tmp_mem.mm_node = NULL; +		mutex_unlock(&dev->struct_mutex); +	} +	return ret; +} + +int radeon_move(struct drm_buffer_object * bo, +		int evict, int no_wait, struct drm_bo_mem_reg * new_mem) +{ +	struct drm_bo_mem_reg *old_mem = &bo->mem; + +	return drm_bo_move_memcpy(bo, evict, no_wait, new_mem); +#if 0 +	DRM_DEBUG("\n"); +	if (old_mem->mem_type == DRM_BO_MEM_LOCAL) { +		return drm_bo_move_memcpy(bo, evict, no_wait, new_mem); +	} else if (new_mem->mem_type == DRM_BO_MEM_LOCAL) { +		if (radeon_move_flip(bo, evict, no_wait, new_mem)) +			return drm_bo_move_memcpy(bo, evict, no_wait, new_mem); +	} else { +		if (radeon_move_blit(bo, evict, no_wait, new_mem)) +			return drm_bo_move_memcpy(bo, evict, no_wait, new_mem); +	} +	return 0; +#endif +} + + +/* + * i915_evict_flags: + * + * @bo: the buffer object to be evicted + * + * Return the bo flags for a buffer which is not mapped to the hardware. + * These will be placed in proposed_flags so that when the move is + * finished, they'll end up in bo->mem.flags + */ +uint64_t radeon_evict_flags(struct drm_buffer_object *bo) +{ +	switch (bo->mem.mem_type) { +	case DRM_BO_MEM_LOCAL: +	case DRM_BO_MEM_TT: +		return DRM_BO_FLAG_MEM_LOCAL; +	default: +		return DRM_BO_FLAG_MEM_TT | DRM_BO_FLAG_CACHED; +	} +} diff --git a/linux-core/radeon_combios.c b/linux-core/radeon_combios.c new file mode 100644 index 00000000..e2b768ca --- /dev/null +++ b/linux-core/radeon_combios.c @@ -0,0 +1,381 @@ +/* + * Copyright 2004 ATI Technologies Inc., Markham, Ontario + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: Dave Airlie + *          Alex Deucher + */ +#include "drmP.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + +/* old legacy ATI BIOS routines */ + +enum radeon_combios_ddc +{ +    DDC_NONE_DETECTED, +    DDC_MONID, +    DDC_DVI, +    DDC_VGA, +    DDC_CRT2, +    DDC_LCD, +    DDC_GPIO, +}; + +enum radeon_combios_connector +{ +    CONNECTOR_NONE_LEGACY, +    CONNECTOR_PROPRIETARY_LEGACY, +    CONNECTOR_CRT_LEGACY, +    CONNECTOR_DVI_I_LEGACY, +    CONNECTOR_DVI_D_LEGACY, +    CONNECTOR_CTV_LEGACY, +    CONNECTOR_STV_LEGACY, +    CONNECTOR_UNSUPPORTED_LEGACY +}; + +struct radeon_i2c_bus_rec combios_setup_i2c_bus(int ddc_line) +{ +	struct radeon_i2c_bus_rec i2c; + +	i2c.mask_clk_mask = RADEON_GPIO_EN_1 | RADEON_GPIO_Y_1; +	i2c.mask_data_mask =  RADEON_GPIO_EN_0 | RADEON_GPIO_Y_0; +	i2c.put_clk_mask = RADEON_GPIO_EN_1; +	i2c.put_data_mask = RADEON_GPIO_EN_0; +	i2c.get_clk_mask = RADEON_GPIO_Y_1; +	i2c.get_data_mask = RADEON_GPIO_Y_0; +	if ((ddc_line == RADEON_LCD_GPIO_MASK) || +	    (ddc_line == RADEON_MDGPIO_EN_REG)) { +		i2c.mask_clk_reg = ddc_line; +		i2c.mask_data_reg = ddc_line; +		i2c.put_clk_reg = ddc_line; +		i2c.put_data_reg = ddc_line; +		i2c.get_clk_reg = ddc_line + 4; +		i2c.get_data_reg = ddc_line + 4; +	} else { +		i2c.mask_clk_reg = ddc_line; +		i2c.mask_data_reg = ddc_line; +		i2c.put_clk_reg = ddc_line; +		i2c.put_data_reg = ddc_line; +		i2c.get_clk_reg = ddc_line; +		i2c.get_data_reg = ddc_line; +	} +	 +	if (ddc_line) +		i2c.valid = true; +	else +		i2c.valid = false; +	 +	return i2c; +} + +bool radeon_combios_get_clock_info(struct drm_device *dev) +{ +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_mode_info *mode_info = &dev_priv->mode_info; +	uint16_t pll_info_block; +	struct radeon_pll *pll = &mode_info->pll; +	int rev; + +	pll_info_block = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x30); +	rev = radeon_bios8(dev_priv, pll_info_block); + +	pll->reference_freq = radeon_bios16(dev_priv, pll_info_block + 0xe); +	pll->reference_div = radeon_bios16(dev_priv, pll_info_block + 0x10); +	pll->pll_out_min = radeon_bios32(dev_priv, pll_info_block + 0x12); +	pll->pll_out_max = radeon_bios32(dev_priv, pll_info_block + 0x16); + +	if (rev > 9) { +		pll->pll_in_min = radeon_bios32(dev_priv, pll_info_block + 0x36); +		pll->pll_in_max = radeon_bios32(dev_priv, pll_info_block + 0x3a); +	} else { +		pll->pll_in_min = 40; +		pll->pll_in_max = 500; +	} + +	pll->xclk = radeon_bios16(dev_priv, pll_info_block + 0x08); + +	// sclk/mclk use fixed point +	 +	return true; + +} + +bool radeon_combios_get_lvds_info(struct radeon_encoder *encoder) +{ +	struct drm_device *dev = encoder->base.dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	uint16_t tmp; +	char stmp[30]; +	int tmp0; +	int i; + +	tmp = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x40); +	if (!tmp) { +		DRM_INFO("No panel info found in BIOS\n"); +		return false; + +	} + +	for (i = 0; i < 24; i++) +		stmp[i] = radeon_bios8(dev_priv, tmp + i + 1); +	stmp[24] = 0; + +	DRM_INFO("Panel ID String: %s\n", stmp); + +	encoder->panel_xres = radeon_bios16(dev_priv, tmp + 25); +	encoder->panel_yres = radeon_bios16(dev_priv, tmp + 27); + +	DRM_INFO("Panel Size %dx%d\n", encoder->panel_xres, encoder->panel_yres); + +	encoder->panel_pwr_delay = radeon_bios16(dev_priv, tmp + 44); +	if (encoder->panel_pwr_delay > 2000 || encoder->panel_pwr_delay < 0) +		encoder->panel_pwr_delay = 2000; + +	for (i = 0; i < 32; i++) { +		tmp0 = radeon_bios16(dev_priv, tmp + 64 + i * 2); +		if (tmp0 == 0) break; + +		if ((radeon_bios16(dev_priv, tmp0) == encoder->panel_xres) && +		    (radeon_bios16(dev_priv, tmp0 + 2) == encoder->panel_yres)) { +			encoder->hblank = (radeon_bios16(dev_priv, tmp0 + 17) - +					   radeon_bios16(dev_priv, tmp0 + 19)) * 8; +			encoder->hoverplus = (radeon_bios16(dev_priv, tmp0 + 21) - +					      radeon_bios16(dev_priv, tmp0 + 19) - 1) * 8; +			encoder->hsync_width = radeon_bios8(dev_priv, tmp0 + 23) * 8; + +			encoder->vblank = (radeon_bios16(dev_priv, tmp0 + 24) - +					   radeon_bios16(dev_priv, tmp0 + 26)); +			encoder->voverplus = ((radeon_bios16(dev_priv, tmp0 + 28) & 0x7fff) - +					      radeon_bios16(dev_priv, tmp0 + 26)); +			encoder->vsync_width = ((radeon_bios16(dev_priv, tmp0 + 28) & 0xf800) >> 11); +			encoder->dotclock = radeon_bios16(dev_priv, tmp0 + 9) * 10; +			encoder->flags = 0; +		} +	} +	return true; +} + +static void radeon_apply_legacy_quirks(struct drm_device *dev, int bios_index) +{ +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_mode_info *mode_info = &dev_priv->mode_info; + +	/* on XPRESS chips, CRT2_DDC and MONID_DCC both use the  +	 * MONID gpio, but use different pins. +	 * CRT2_DDC uses the standard pinout, MONID_DDC uses +	 * something else. +	 */ +	if ((dev_priv->chip_family == CHIP_RS400 || +	     dev_priv->chip_family == CHIP_RS480) && +	    mode_info->bios_connector[bios_index].connector_type == CONNECTOR_VGA && +	    mode_info->bios_connector[bios_index].ddc_i2c.mask_clk_reg == RADEON_GPIO_CRT2_DDC) { +		mode_info->bios_connector[bios_index].ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_MONID); +	} + +	/* XPRESS desktop chips seem to have a proprietary connector listed for +	 * DVI-D, try and do the right thing here. +	 */ +	if ((dev_priv->flags & RADEON_IS_MOBILITY) && +	    (mode_info->bios_connector[bios_index].connector_type == CONNECTOR_LVDS)) { +	  DRM_INFO("proprietary connector found. assuming DVI-D\n"); +	  mode_info->bios_connector[bios_index].dac_type = DAC_NONE; +	  mode_info->bios_connector[bios_index].tmds_type = TMDS_EXT; +	  mode_info->bios_connector[bios_index].connector_type = CONNECTOR_DVI_D; +	} + +	/* Certain IBM chipset RN50s have a BIOS reporting two VGAs, +	   one with VGA DDC and one with CRT2 DDC. - kill the CRT2 DDC one */ +	if (dev->pdev->device == 0x515e && +	    dev->pdev->subsystem_vendor == 0x1014) { +		if (mode_info->bios_connector[bios_index].connector_type == CONNECTOR_VGA && +		    mode_info->bios_connector[bios_index].ddc_i2c.mask_clk_reg == RADEON_GPIO_CRT2_DDC) { +			mode_info->bios_connector[bios_index].valid = false; +		} +	} + +	/* Some RV100 cards with 2 VGA ports show up with DVI+VGA */ +	if (dev->pdev->device == 0x5159 && +	    dev->pdev->subsystem_vendor == 0x1002 && +	    dev->pdev->subsystem_device == 0x013a) { +		if (mode_info->bios_connector[bios_index].connector_type == CONNECTOR_DVI_I) +			mode_info->bios_connector[bios_index].connector_type = CONNECTOR_VGA; + +	} + +} + +bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev) +{ +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_mode_info *mode_info = &dev_priv->mode_info; +	uint32_t offset, entry; +	uint16_t tmp0, tmp1, tmp; +	enum radeon_combios_ddc ddctype; +	enum radeon_combios_connector connector_type; +	int i; +	 +	DRM_DEBUG("\n"); +	offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x50); +	if (offset) { +		for (i = 0; i < 4; i++) { +			entry = offset + 2 + i * 2; +	 +			if (!radeon_bios16(dev_priv, entry)) +				    break; +			 +			mode_info->bios_connector[i].valid = true; + +			tmp = radeon_bios16(dev_priv, entry); + +			connector_type = (tmp >> 12) & 0xf; +			mode_info->bios_connector[i].connector_type = connector_type; + +			switch(connector_type) { +			case CONNECTOR_PROPRIETARY_LEGACY: +				mode_info->bios_connector[i].connector_type = CONNECTOR_LVDS; +				break; +			case CONNECTOR_CRT_LEGACY: +				mode_info->bios_connector[i].connector_type = CONNECTOR_VGA; +				break; +			case CONNECTOR_DVI_I_LEGACY: +				mode_info->bios_connector[i].connector_type = CONNECTOR_DVI_I; +				break; +			case CONNECTOR_DVI_D_LEGACY: +				mode_info->bios_connector[i].connector_type = CONNECTOR_DVI_D; +				break; +			case CONNECTOR_CTV_LEGACY: +				mode_info->bios_connector[i].connector_type = CONNECTOR_CTV; +				break; +			case CONNECTOR_STV_LEGACY: +				mode_info->bios_connector[i].connector_type = CONNECTOR_STV; +				break; +			default: +				DRM_ERROR("Unknown connector type: %d\n", connector_type); +				mode_info->bios_connector[i].valid = false; +				break; +			} + +			mode_info->bios_connector[i].ddc_i2c.valid = false; + +			ddctype = (tmp >> 8) & 0xf; +			switch (ddctype) { +			case DDC_MONID: +				mode_info->bios_connector[i].ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_MONID); +				break; +			case DDC_DVI: +				mode_info->bios_connector[i].ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC); +				break; +			case DDC_VGA: +				mode_info->bios_connector[i].ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC); +				break; +			case DDC_CRT2: +				mode_info->bios_connector[i].ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_CRT2_DDC); +				break; +			default: +				break; +			} + +			if (tmp & 0x1) +				mode_info->bios_connector[i].dac_type = DAC_TVDAC; +			else +				mode_info->bios_connector[i].dac_type = DAC_PRIMARY; + +			if ((dev_priv->chip_family == CHIP_RS300) || +			    (dev_priv->chip_family == CHIP_RS400) || +			    (dev_priv->chip_family == CHIP_RS480)) +				mode_info->bios_connector[i].dac_type = DAC_TVDAC; + +			if ((tmp >> 4) & 0x1) +				mode_info->bios_connector[i].tmds_type = TMDS_EXT; +			else +				mode_info->bios_connector[i].tmds_type = TMDS_INT; + +			radeon_apply_legacy_quirks(dev, i); +		} +	} else { +		DRM_INFO("no connector table found in BIOS\n"); +		offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x34); +		if (offset) { +			DRM_DEBUG("Found DFP table, assuming DVI connector\n"); + +			mode_info->bios_connector[0].valid = true; +			mode_info->bios_connector[0].connector_type = CONNECTOR_DVI_I; +			mode_info->bios_connector[0].dac_type = DAC_PRIMARY; +			mode_info->bios_connector[0].tmds_type = TMDS_INT; +			mode_info->bios_connector[0].ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC); +		} else { +			DRM_DEBUG("No table found\n"); +			return false; +		} +	} + +	if (dev_priv->flags & RADEON_IS_MOBILITY) { +		offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x40); +		if (offset) { +			mode_info->bios_connector[4].valid = true; +			mode_info->bios_connector[4].connector_type = CONNECTOR_LVDS; +			mode_info->bios_connector[4].dac_type = DAC_NONE; +			mode_info->bios_connector[4].tmds_type = TMDS_NONE; +			mode_info->bios_connector[4].ddc_i2c.valid = false; + +			tmp = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x42); +			if (tmp) { +				tmp0 = radeon_bios16(dev_priv, tmp + 0x15); +				if (tmp0) { +					tmp1 = radeon_bios8(dev_priv, tmp0 + 2) & 0x07; +					if (tmp1) { +						ddctype = tmp1; + +						switch(ddctype) { +						case DDC_MONID: +						case DDC_DVI: +						case DDC_CRT2: +						case DDC_LCD: +						case DDC_GPIO: +						default: +							break; +						} +						DRM_DEBUG("LCD DDC Info Table found!\n"); +					} +				} +			} else +				mode_info->bios_connector[4].ddc_i2c.valid = false; +		} +	} + +	DRM_DEBUG("BIOS Connector table\n"); +	for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) { +		if (!mode_info->bios_connector[i].valid) +			continue; +		 +		DRM_DEBUG("Port %d: ddc_type 0x%x, dac_type %d, tmds_type %d, connector type %d, hpd_mask %d\n", +			  i, mode_info->bios_connector[i].ddc_i2c.mask_clk_reg, +			  mode_info->bios_connector[i].dac_type, +			  mode_info->bios_connector[i].tmds_type, +			  mode_info->bios_connector[i].connector_type, +			  mode_info->bios_connector[i].hpd_mask); +	} + +	return true; +} +		 diff --git a/linux-core/radeon_connectors.c b/linux-core/radeon_connectors.c new file mode 100644 index 00000000..344b4f77 --- /dev/null +++ b/linux-core/radeon_connectors.c @@ -0,0 +1,344 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: Dave Airlie + *          Alex Deucher + */ +#include "drmP.h" +#include "drm_edid.h" +#include "drm_crtc_helper.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + +static int radeon_lvds_get_modes(struct drm_connector *connector) +{ +	struct radeon_connector *radeon_connector = to_radeon_connector(connector); +	struct drm_encoder *lvds_encoder; +	int ret = 0; +	struct edid *edid; + +	avivo_i2c_do_lock(radeon_connector, 1); +	edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); +	avivo_i2c_do_lock(radeon_connector, 0); +	if (edid) { +		drm_mode_connector_update_edid_property(&radeon_connector->base, edid); +		ret = drm_add_edid_modes(&radeon_connector->base, edid); +		kfree(edid); +		return 0; +	} + +#if 0 +	lvds_encoder = radeon_best_single_encoder(connector); + +	if (!lvds_encoder) +		return ret; + +	radeon_encoder_update_panel_size(lvds_encoder, connector); +#endif   +	return ret; +} + +static int radeon_lvds_mode_valid(struct drm_connector *connector, +				  struct drm_display_mode *mode) +{ + +	return MODE_OK; +} + +static enum drm_connector_status radeon_lvds_detect(struct drm_connector *connector) +{ +	return connector_status_connected; +} + + + +struct drm_encoder *radeon_best_single_encoder(struct drm_connector *connector) +{ +	int enc_id = connector->encoder_ids[0]; +	struct drm_mode_object *obj; +	struct drm_encoder *encoder; + +	/* pick the encoder ids */ +	if (enc_id) { +		obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER); +		if (!obj) +			return NULL; +		encoder = obj_to_encoder(obj); +		return encoder; +	} +	return NULL; +} + +static void radeon_connector_destroy(struct drm_connector *connector) +{ +	struct radeon_connector *radeon_connector = to_radeon_connector(connector); + +	if (radeon_connector->ddc_bus) +		radeon_i2c_destroy(radeon_connector->ddc_bus); +	drm_sysfs_connector_remove(connector); +	drm_connector_cleanup(connector); +	kfree(connector); +} + +struct drm_connector_helper_funcs radeon_lvds_connector_helper_funcs = { +	.get_modes = radeon_lvds_get_modes, +	.mode_valid = radeon_lvds_mode_valid, +	.best_encoder = radeon_best_single_encoder, +}; + +struct drm_connector_funcs radeon_lvds_connector_funcs = { +	.detect = radeon_lvds_detect, +	.fill_modes = drm_helper_probe_single_connector_modes, +	.destroy = radeon_connector_destroy, +}; + +static int radeon_atom_vga_get_modes(struct drm_connector *connector) +{ +	struct radeon_connector *radeon_connector = to_radeon_connector(connector); +	int ret; + +	ret = radeon_ddc_get_modes(radeon_connector); +	 +	return ret; +} + +static int radeon_atom_vga_mode_valid(struct drm_connector *connector, +				  struct drm_display_mode *mode) +{ + +	return MODE_OK; +} + +static enum drm_connector_status radeon_atom_vga_detect(struct drm_connector *connector) +{ +	struct edid *edid; +	struct radeon_connector *radeon_connector = to_radeon_connector(connector); +	struct drm_encoder *encoder; +	struct drm_encoder_helper_funcs *encoder_funcs; + +	avivo_i2c_do_lock(radeon_connector, 1); +	edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); +	avivo_i2c_do_lock(radeon_connector, 0); +	if (edid) { +		kfree(edid); +		return connector_status_connected; +	} + +	/* if EDID fails to a load detect */ +	encoder = radeon_best_single_encoder(connector); +	if (!encoder) +		return connector_status_disconnected; + +	encoder_funcs = encoder->helper_private; +	return encoder_funcs->detect(encoder, connector); +} + +struct drm_connector_helper_funcs radeon_atom_vga_connector_helper_funcs = { +	.get_modes = radeon_atom_vga_get_modes, +	.mode_valid = radeon_atom_vga_mode_valid, +	.best_encoder = radeon_best_single_encoder, +}; + +struct drm_connector_funcs radeon_atom_vga_connector_funcs = { +	.detect = radeon_atom_vga_detect, +	.fill_modes = drm_helper_probe_single_connector_modes, +	.destroy = radeon_connector_destroy, +}; + + +static enum drm_connector_status radeon_atom_dvi_detect(struct drm_connector *connector) +{ +	struct edid *edid; +	struct radeon_connector *radeon_connector = to_radeon_connector(connector); +	struct drm_encoder *encoder; +	struct drm_encoder_helper_funcs *encoder_funcs; +	struct drm_mode_object *obj; +	int i; +	enum drm_connector_status ret; + +	avivo_i2c_do_lock(radeon_connector, 1); +	edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); +	avivo_i2c_do_lock(radeon_connector, 0); +	if (edid) { +		/* if the monitor is digital - set the bits */ +		if (edid->digital) +			radeon_connector->use_digital = 1; +		else +			radeon_connector->use_digital = 0; + +		kfree(edid); +		return connector_status_connected; +	} + +	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { +		if (connector->encoder_ids[i] == 0) +			break; + +		obj = drm_mode_object_find(connector->dev, connector->encoder_ids[i], DRM_MODE_OBJECT_ENCODER); +		if (!obj) +			continue; + +		encoder = obj_to_encoder(obj); + +		encoder_funcs = encoder->helper_private; +		if (encoder_funcs->detect) { +			ret = encoder_funcs->detect(encoder, connector); +			if (ret == connector_status_connected) { +				radeon_connector->use_digital = 0; +				return ret; +			} +		} +	} +	return connector_status_disconnected; +} + +/* okay need to be smart in here about which encoder to pick */ +struct drm_encoder *radeon_atom_dvi_encoder(struct drm_connector *connector) +{ +	int enc_id = connector->encoder_ids[0]; +	struct radeon_connector *radeon_connector = to_radeon_connector(connector); +	struct drm_mode_object *obj; +	struct drm_encoder *encoder; +	int i; +	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { +		if (connector->encoder_ids[i] == 0) +			break; + +		obj = drm_mode_object_find(connector->dev, connector->encoder_ids[i], DRM_MODE_OBJECT_ENCODER); +		if (!obj) +			continue; + +		encoder = obj_to_encoder(obj); + +		if (radeon_connector->use_digital) { +			if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS) +				return encoder; +		} else { +			if (encoder->encoder_type == DRM_MODE_ENCODER_DAC || +			    encoder->encoder_type == DRM_MODE_ENCODER_TVDAC) +				return encoder; +		} +	} + +	/* see if we have a default encoder  TODO */ +	 +	/* then check use digitial */ +	/* pick the first one */ +	if (enc_id) { +		obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER); +		if (!obj) +			return NULL; +		encoder = obj_to_encoder(obj); +		return encoder; +	} +	return NULL; +} + +struct drm_connector_helper_funcs radeon_atom_dvi_connector_helper_funcs = { +	.get_modes = radeon_atom_vga_get_modes, +	.mode_valid = radeon_atom_vga_mode_valid, +	.best_encoder = radeon_atom_dvi_encoder, +}; + +struct drm_connector_funcs radeon_atom_dvi_connector_funcs = { +	.detect = radeon_atom_dvi_detect, +	.fill_modes = drm_helper_probe_single_connector_modes, +	.destroy = radeon_connector_destroy, +}; + + +static struct connector_funcs { +	int conn_id; +	struct drm_connector_funcs *connector_funcs; +	struct drm_connector_helper_funcs *helper_funcs; +	int conn_type; +	char *i2c_id; +} connector_fns[] = {  +	{ CONNECTOR_NONE, NULL, NULL, DRM_MODE_CONNECTOR_Unknown }, +	{ CONNECTOR_VGA, &radeon_atom_vga_connector_funcs, &radeon_atom_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA , "VGA"}, +	{ CONNECTOR_LVDS, &radeon_lvds_connector_funcs, &radeon_lvds_connector_helper_funcs, DRM_MODE_CONNECTOR_LVDS, "LVDS" }, +	{ CONNECTOR_DVI_A, &radeon_atom_vga_connector_funcs, &radeon_atom_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_DVIA, "DVI" }, +	{ CONNECTOR_DVI_I, &radeon_atom_dvi_connector_funcs, &radeon_atom_dvi_connector_helper_funcs, DRM_MODE_CONNECTOR_DVII, "DVI" }, + +#if 0 +	{ CONNECTOR_DVI_D, radeon_vga_connector_funcs, radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA }, + +	{ CONNECTOR_STV, radeon_vga_connector_funcs, radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA }, +	{ CONNECTOR_CTV, radeon_vga_connector_funcs, radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA }, +	{ CONNECTOR_DIGITAL, radeon_vga_connector_funcs, radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA }, +	{ CONNECTOR_SCART, radeon_vga_connector_funcs, radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA }, + +	{ CONNECTOR_HDMI_TYPE_A, radeon_vga_connector_funcs, radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA }, +	{ CONNECTOR_HDMI_TYPE_B, radeon_vga_connector_funcs, radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA }, +	{ CONNECTOR_HDMI_TYPE_B, radeon_vga_connector_funcs, radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA }, +	{ CONNECTOR_HDMI_TYPE_B, radeon_vga_connector_funcs, radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA }, +	{ CONNECTOR_DIN, radeon_vga_connector_funcs, radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA }, +	{ CONNECTOR_DISPLAY_PORT, radeon_vga_connector_funcs, radeon_vga_connector_helper_funcs, DRM_MODE_CONNECTOR_VGA }, +#endif +};	 +	 +struct drm_connector *radeon_connector_add(struct drm_device *dev, int bios_index) +{ +	struct radeon_connector *radeon_connector; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_mode_info *mode_info = &dev_priv->mode_info; +	struct drm_connector *connector;	 +	int table_idx; + +	for (table_idx = 0; table_idx < ARRAY_SIZE(connector_fns); table_idx++) { +		if (connector_fns[table_idx].conn_id == mode_info->bios_connector[bios_index].connector_type) +			break; +	} + +	if (table_idx == ARRAY_SIZE(connector_fns)) +		return NULL; + +	radeon_connector = kzalloc(sizeof(struct radeon_connector), GFP_KERNEL); +	if (!radeon_connector) { +		return NULL; +	} + +	connector = &radeon_connector->base; + +	drm_connector_init(dev, &radeon_connector->base, connector_fns[table_idx].connector_funcs, +			   connector_fns[table_idx].conn_type); + +	drm_connector_helper_add(&radeon_connector->base, connector_fns[table_idx].helper_funcs); +	 +	if (mode_info->bios_connector[bios_index].ddc_i2c.valid) { +		radeon_connector->ddc_bus = radeon_i2c_create(dev, &mode_info->bios_connector[bios_index].ddc_i2c, +							      connector_fns[table_idx].i2c_id); +		if (!radeon_connector->ddc_bus) +			goto failed; +	} + +	drm_sysfs_connector_add(connector); +	return connector; + + +failed: +	if (radeon_connector->ddc_bus) +		radeon_i2c_destroy(radeon_connector->ddc_bus); +	drm_connector_cleanup(connector); +	kfree(connector); +	return NULL; +} diff --git a/linux-core/radeon_display.c b/linux-core/radeon_display.c new file mode 100644 index 00000000..7a7b5856 --- /dev/null +++ b/linux-core/radeon_display.c @@ -0,0 +1,725 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: Dave Airlie + *          Alex Deucher + */ +#include "drmP.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + +#include "atom.h" +#include <asm/div64.h> + +#include "drm_crtc_helper.h" + +#define CURSOR_WIDTH 64 +#define CURSOR_HEIGHT 64 + +int radeon_ddc_dump(struct drm_connector *connector); + + + +static void avivo_crtc_load_lut(struct drm_crtc *crtc) +{ +	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); +	struct drm_device *dev = crtc->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	int i; + +	DRM_DEBUG("%d\n", radeon_crtc->crtc_id); +	RADEON_WRITE(AVIVO_DC_LUTA_CONTROL + radeon_crtc->crtc_offset, 0); +	 +	RADEON_WRITE(AVIVO_DC_LUTA_BLACK_OFFSET_BLUE + radeon_crtc->crtc_offset, 0); +	RADEON_WRITE(AVIVO_DC_LUTA_BLACK_OFFSET_GREEN + radeon_crtc->crtc_offset, 0); +	RADEON_WRITE(AVIVO_DC_LUTA_BLACK_OFFSET_RED + radeon_crtc->crtc_offset, 0); +		 +	RADEON_WRITE(AVIVO_DC_LUTA_WHITE_OFFSET_BLUE + radeon_crtc->crtc_offset, 0xffff); +	RADEON_WRITE(AVIVO_DC_LUTA_WHITE_OFFSET_GREEN + radeon_crtc->crtc_offset, 0xffff); +	RADEON_WRITE(AVIVO_DC_LUTA_WHITE_OFFSET_RED + radeon_crtc->crtc_offset, 0xffff); + +	RADEON_WRITE(AVIVO_DC_LUT_RW_SELECT, radeon_crtc->crtc_id); +	RADEON_WRITE(AVIVO_DC_LUT_RW_MODE, 0); +	RADEON_WRITE(AVIVO_DC_LUT_WRITE_EN_MASK, 0x0000003f); + +	for (i = 0; i < 256; i++) { +		 +		RADEON_WRITE8(AVIVO_DC_LUT_RW_INDEX, i); +		RADEON_WRITE(AVIVO_DC_LUT_30_COLOR, +			     (radeon_crtc->lut_r[i] << 22) | +			     (radeon_crtc->lut_g[i] << 12) | +			     (radeon_crtc->lut_b[i] << 2)); +	} + +	RADEON_WRITE(AVIVO_D1GRPH_LUT_SEL + radeon_crtc->crtc_offset, radeon_crtc->crtc_id); +} + + +void radeon_crtc_load_lut(struct drm_crtc *crtc) +{ +	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); +	struct drm_device *dev = crtc->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	u32 temp; +	int i; +	if (!crtc->enabled) +		return; + +	 +	if (radeon_is_avivo(dev_priv)) { +		avivo_crtc_load_lut(crtc); +		return; +	} + +	temp = RADEON_READ(RADEON_DAC_CNTL2); +	if (radeon_crtc->crtc_id == 0) +		temp &= (uint32_t)~RADEON_DAC2_PALETTE_ACC_CTL; +	else +		temp |= RADEON_DAC2_PALETTE_ACC_CTL; +	RADEON_WRITE(RADEON_DAC_CNTL2, temp); + +	for (i = 0; i < 256; i++) { +//		OUTPAL(i, radeon_crtc->lut_r[i], radeon_crtc->lut_g[i], radeon_crtc->lut_b[i]); +	} + +} + +/** Sets the color ramps on behalf of RandR */ +void radeon_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, +			      u16 blue, int regno) +{ +	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + +	if (regno==0) +		DRM_DEBUG("gamma set %d\n", radeon_crtc->crtc_id); +	radeon_crtc->lut_r[regno] = red >> 8; +	radeon_crtc->lut_g[regno] = green >> 8; +	radeon_crtc->lut_b[regno] = blue >> 8; +} + +void radeon_crtc_dpms(struct drm_crtc *crtc, int mode) +{ +} + + + +static bool radeon_crtc_mode_fixup(struct drm_crtc *crtc, +				   struct drm_display_mode *mode, +				   struct drm_display_mode *adjusted_mode) +{ +	return true; +} + +static void radeon_crtc_mode_set(struct drm_crtc *crtc, +				 struct drm_display_mode *mode, +				 struct drm_display_mode *adjusted_mode, +				 int x, int y) +{ + +} + +void radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y) +{ +} + +static void radeon_crtc_prepare(struct drm_crtc *crtc) +{ +} + +static void radeon_crtc_commit(struct drm_crtc *crtc) +{ +} + +static void avivo_lock_cursor(struct drm_crtc *crtc, bool lock) +{ +	struct drm_radeon_private *dev_priv = crtc->dev->dev_private; +	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + +	uint32_t tmp; + +	tmp = RADEON_READ(AVIVO_D1CUR_UPDATE + radeon_crtc->crtc_offset); +	if (lock) +		tmp |= AVIVO_D1CURSOR_UPDATE_LOCK; +	else +		tmp &= ~AVIVO_D1CURSOR_UPDATE_LOCK; + +	RADEON_WRITE(AVIVO_D1CUR_UPDATE + radeon_crtc->crtc_offset, tmp); +} + +static int radeon_crtc_cursor_set(struct drm_crtc *crtc, +				  struct drm_file *file_priv, +				  uint32_t handle, +				  uint32_t width, +				  uint32_t height) +{ +	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); +	struct drm_radeon_private *dev_priv = crtc->dev->dev_private; +	struct drm_gem_object *obj; +	struct drm_radeon_gem_object *obj_priv; + +	if (!handle) { +		RADEON_WRITE(AVIVO_D1CUR_CONTROL + radeon_crtc->crtc_offset, 0); +		return 0; +		/* turn off cursor */ +	} + +	obj = drm_gem_object_lookup(crtc->dev, file_priv, handle); +	if (!obj) { +		DRM_ERROR("Cannot find cursor object %x for crtc %d\n", handle, radeon_crtc->crtc_id); +		return -EINVAL; +	} + +	obj_priv = obj->driver_private; +	 +	RADEON_WRITE(AVIVO_D1CUR_CONTROL + radeon_crtc->crtc_offset, 0); +	if (radeon_is_avivo(dev_priv)) { +		RADEON_WRITE(AVIVO_D1CUR_SURFACE_ADDRESS + radeon_crtc->crtc_offset, +			     dev_priv->fb_location + obj_priv->bo->offset); +		RADEON_WRITE(AVIVO_D1CUR_SIZE + radeon_crtc->crtc_offset, +			     (CURSOR_WIDTH - 1) << 16 | (CURSOR_HEIGHT - 1)); +		RADEON_WRITE(AVIVO_D1CUR_CONTROL + radeon_crtc->crtc_offset, +			     AVIVO_D1CURSOR_EN | (AVIVO_D1CURSOR_MODE_24BPP << AVIVO_D1CURSOR_MODE_SHIFT)); +	} + +	mutex_lock(&crtc->dev->struct_mutex); +	drm_gem_object_unreference(obj); +	mutex_unlock(&crtc->dev->struct_mutex); +	 +	return 0; +} + +static int radeon_crtc_cursor_move(struct drm_crtc *crtc, +				   int x, int y) +{ +	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); +	struct drm_radeon_private *dev_priv = crtc->dev->dev_private; +	int xorigin = 0, yorigin = 0; + +	if (x < 0) xorigin = -x+1; +	if (y < 0) yorigin = -x+1; +	if (xorigin >= CURSOR_WIDTH) xorigin = CURSOR_WIDTH - 1; +	if (yorigin >= CURSOR_WIDTH) yorigin = CURSOR_WIDTH - 1; + +	if (crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) +		y /= 2; +	else if (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN) +		y *= 2; + +	if (radeon_is_avivo(dev_priv)) { +		avivo_lock_cursor(crtc, true); + +		RADEON_WRITE(AVIVO_D1CUR_POSITION + radeon_crtc->crtc_offset, +			     ((xorigin ? 0: x) << 16) | +			     (yorigin ? 0 : y)); +		RADEON_WRITE(AVIVO_D1CUR_HOT_SPOT + radeon_crtc->crtc_offset, (xorigin << 16) | yorigin); +		avivo_lock_cursor(crtc, false); +	} +	 +	return 0; +} + +static void radeon_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, +				  u16 *blue, uint32_t size) +{ +	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); +	int i; + +	if (size != 256) +		return; + +	for (i = 0; i < 256; i++) { +		radeon_crtc->lut_r[i] = red[i] >> 8; +		radeon_crtc->lut_g[i] = green[i] >> 8; +		radeon_crtc->lut_b[i] = blue[i] >> 8; +	} + +	radeon_crtc_load_lut(crtc); +} + +static void radeon_crtc_destroy(struct drm_crtc *crtc) +{ +	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + +	drm_crtc_cleanup(crtc); +	kfree(radeon_crtc); +} + +static const struct drm_crtc_helper_funcs radeon_helper_funcs = { +	.dpms = radeon_crtc_dpms, +	.mode_fixup = radeon_crtc_mode_fixup, +	.mode_set = radeon_crtc_mode_set, +	.mode_set_base = radeon_crtc_set_base, +	.prepare = radeon_crtc_prepare, +	.commit = radeon_crtc_commit, +}; + +static const struct drm_crtc_funcs radeon_crtc_funcs = { +	.cursor_set = radeon_crtc_cursor_set, +	.cursor_move = radeon_crtc_cursor_move, +	.gamma_set = radeon_crtc_gamma_set, +	.set_config = drm_crtc_helper_set_config, +	.destroy = radeon_crtc_destroy, +}; + +static void radeon_crtc_init(struct drm_device *dev, int index) +{ +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_crtc *radeon_crtc; +	int i; + +	radeon_crtc = kzalloc(sizeof(struct radeon_crtc) + (RADEONFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL); +	//	radeon_crtc = kzalloc(sizeof(struct radeon_crtc), GFP_KERNEL); +	if (radeon_crtc == NULL) +		return; + +	drm_crtc_init(dev, &radeon_crtc->base, &radeon_crtc_funcs); + +	drm_mode_crtc_set_gamma_size(&radeon_crtc->base, 256); +	radeon_crtc->crtc_id = index; + +	radeon_crtc->mode_set.crtc = &radeon_crtc->base; +	radeon_crtc->mode_set.connectors = (struct drm_connector **)(radeon_crtc + 1); +	radeon_crtc->mode_set.num_connectors = 0; + +	for (i = 0; i < 256; i++) { +		radeon_crtc->lut_r[i] = i; +		radeon_crtc->lut_g[i] = i; +		radeon_crtc->lut_b[i] = i; +	} + +	if (dev_priv->is_atom_bios && dev_priv->chip_family > CHIP_RS690) +		radeon_atombios_init_crtc(dev, radeon_crtc); +	else +		drm_crtc_helper_add(&radeon_crtc->base, &radeon_helper_funcs); +} + +bool radeon_legacy_setup_enc_conn(struct drm_device *dev) +{ + +	radeon_get_legacy_connector_info_from_bios(dev); +	return false; +} + +bool radeon_setup_enc_conn(struct drm_device *dev) +{ +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_mode_info *mode_info = &dev_priv->mode_info; +	/* do all the mac and stuff */ +	struct drm_connector *connector; +	struct drm_encoder *encoder; +	int i; + +	if (dev_priv->is_atom_bios) +		radeon_get_atom_connector_info_from_bios_connector_table(dev); +	else +		radeon_get_legacy_connector_info_from_bios(dev); + +	for (i = 0; i < RADEON_MAX_BIOS_CONNECTOR; i++) { +		if (!mode_info->bios_connector[i].valid) +			continue; + +		/* add a connector for this */ +		if (mode_info->bios_connector[i].connector_type == CONNECTOR_NONE) +			continue; + +		connector = radeon_connector_add(dev, i); +		if (!connector) +			continue; + +		encoder = NULL; +		/* if we find an LVDS connector */ +		if (mode_info->bios_connector[i].connector_type == CONNECTOR_LVDS) { +			if (radeon_is_avivo(dev_priv)) +				encoder = radeon_encoder_lvtma_add(dev, i); +			else +				encoder = radeon_encoder_legacy_lvds_add(dev, i); +			if (encoder) +				drm_mode_connector_attach_encoder(connector, encoder); +		} + +		/* DAC on DVI or VGA */ +		if ((mode_info->bios_connector[i].connector_type == CONNECTOR_DVI_I) || +		    (mode_info->bios_connector[i].connector_type == CONNECTOR_DVI_A) || +		    (mode_info->bios_connector[i].connector_type == CONNECTOR_VGA)) { +			if (radeon_is_avivo(dev_priv)) +				encoder = radeon_encoder_atom_dac_add(dev, i, mode_info->bios_connector[i].dac_type, 0); +			if (encoder) +				drm_mode_connector_attach_encoder(connector, encoder); +		} + +		/* TMDS on DVI */ +		if ((mode_info->bios_connector[i].connector_type == CONNECTOR_DVI_I) || +		    (mode_info->bios_connector[i].connector_type == CONNECTOR_DVI_D)) { +			if (radeon_is_avivo(dev_priv)) +				encoder = radeon_encoder_atom_tmds_add(dev, i, mode_info->bios_connector[i].dac_type); +			if (encoder) +				drm_mode_connector_attach_encoder(connector, encoder); +		} + +		/* TVDAC on DIN */ +		if (mode_info->bios_connector[i].connector_type == CONNECTOR_DIN) { +			if (radeon_is_avivo(dev_priv)) +				encoder = radeon_encoder_atom_dac_add(dev, i, mode_info->bios_connector[i].dac_type, 1); +			if (encoder) +				drm_mode_connector_attach_encoder(connector, encoder); +		} +	} + +	list_for_each_entry(connector, &dev->mode_config.connector_list, head) +		radeon_ddc_dump(connector); +	return true; +} + + + +void avivo_i2c_do_lock(struct radeon_connector *radeon_connector, int lock_state) +{ +	struct drm_radeon_private *dev_priv = radeon_connector->base.dev->dev_private; +	uint32_t temp; +	struct radeon_i2c_bus_rec *rec = &radeon_connector->ddc_bus->rec; + +	temp = RADEON_READ(rec->mask_clk_reg); +	if (lock_state) +		temp |= rec->put_clk_mask; +	else +		temp &= ~rec->put_clk_mask; +	RADEON_WRITE(rec->mask_clk_reg, temp); +	temp = RADEON_READ(rec->mask_clk_reg); + +	temp = RADEON_READ(rec->mask_data_reg); +	if (lock_state) +		temp |= rec->put_data_mask; +	else +		temp &= ~rec->put_data_mask; +	RADEON_WRITE(rec->mask_data_reg, temp); +	temp = RADEON_READ(rec->mask_data_reg); +} + +int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) +{ +	struct drm_radeon_private *dev_priv = radeon_connector->base.dev->dev_private; +	struct edid *edid; +	int ret = 0; + +	if (!radeon_connector->ddc_bus) +		return -1; +	 +	if (radeon_is_avivo(dev_priv)) +		avivo_i2c_do_lock(radeon_connector, 1); +	edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); +	if (radeon_is_avivo(dev_priv)) +		avivo_i2c_do_lock(radeon_connector, 0); +	if (edid) { +		drm_mode_connector_update_edid_property(&radeon_connector->base, edid); +		ret = drm_add_edid_modes(&radeon_connector->base, edid); +		kfree(edid); +		return ret; +	} +	return -1; +} + +int radeon_ddc_dump(struct drm_connector *connector) +{ +	struct edid *edid; +	struct radeon_connector *radeon_connector = to_radeon_connector(connector); +	int ret = 0; + +	if (!radeon_connector->ddc_bus) +		return -1; +	edid = drm_get_edid(connector, &radeon_connector->ddc_bus->adapter); +	if (edid) { +		kfree(edid); +	} +	return ret; +} + +static inline uint32_t radeon_div(uint64_t n, uint32_t d) +{ +	uint64_t x, y, result; +	uint64_t mod; +	 +	n += d / 2; + +	mod = do_div(n, d); +	return n; +} + +void radeon_compute_pll(struct radeon_pll *pll, +			uint64_t freq, +			uint32_t *dot_clock_p, +			uint32_t *fb_div_p, +			uint32_t *ref_div_p, +			uint32_t *post_div_p, +			int flags) +{ +	uint32_t min_ref_div = pll->min_ref_div; +	uint32_t max_ref_div = pll->max_ref_div; +	uint32_t best_vco = pll->best_vco; +	uint32_t best_post_div = 1; +	uint32_t best_ref_div = 1; +	uint32_t best_feedback_div = 1; +	uint32_t best_freq = -1; +	uint32_t best_error = 0xffffffff; +	uint32_t best_vco_diff = 1; +	uint32_t post_div; + +	DRM_DEBUG("PLL freq %llu\n", freq); +	freq = freq * 1000; + +	if (flags & RADEON_PLL_USE_REF_DIV) +		min_ref_div = max_ref_div = pll->reference_div; +	else { +		while (min_ref_div < max_ref_div-1) { +			uint32_t mid=(min_ref_div+max_ref_div)/2; +			uint32_t pll_in = pll->reference_freq / mid; +			if (pll_in < pll->pll_in_min) +				max_ref_div = mid; +			else if (pll_in > pll->pll_in_max) +				min_ref_div = mid; +			else +				break; +		} +	} + +	for (post_div = pll->min_post_div; post_div <= pll->max_post_div; ++post_div) { +		uint32_t ref_div; + +		if ((flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1)) +			continue; + +		/* legacy radeons only have a few post_divs */ +		if (flags & RADEON_PLL_LEGACY) { +			if ((post_div == 5) || +			    (post_div == 7) || +			    (post_div == 9) || +			    (post_div == 10) || +			    (post_div == 11)) +				continue; +		} + +		for (ref_div = min_ref_div; ref_div <= max_ref_div; ++ref_div) { +			uint32_t feedback_div, current_freq, error, vco_diff; +			uint32_t pll_in = pll->reference_freq / ref_div; +			uint32_t min_feed_div = pll->min_feedback_div; +			uint32_t max_feed_div = pll->max_feedback_div+1; + +			if (pll_in < pll->pll_in_min || pll_in > pll->pll_in_max) +				continue; + +			while (min_feed_div < max_feed_div) { +				uint32_t vco; +				feedback_div = (min_feed_div+max_feed_div)/2; + +				vco = radeon_div((uint64_t)pll->reference_freq * feedback_div, +						 ref_div); + +				if (vco < pll->pll_out_min) { +					min_feed_div = feedback_div+1; +					continue; +				} else if(vco > pll->pll_out_max) { +					max_feed_div = feedback_div; +					continue; +				} + +				current_freq = radeon_div((uint64_t)pll->reference_freq * 10000 * feedback_div, +							  ref_div * post_div); +				 +				error = abs(current_freq - freq); +				vco_diff = abs(vco - best_vco); + +				if ((best_vco == 0 && error < best_error) || +				    (best_vco != 0 && +				     (error < best_error - 100 || +				      (abs(error - best_error) < 100 && vco_diff < best_vco_diff )))) { +					best_post_div = post_div; +					best_ref_div = ref_div; +					best_feedback_div = feedback_div; +					best_freq = current_freq; +					best_error = error; +					best_vco_diff = vco_diff; +				} else if (current_freq == freq) { +					if (best_freq == -1) { +						best_post_div = post_div; +						best_ref_div = ref_div; +						best_feedback_div = feedback_div; +						best_freq = current_freq; +						best_error = error; +						best_vco_diff = vco_diff; +					} else if ((flags & RADEON_PLL_PREFER_LOW_REF_DIV) && (ref_div < best_ref_div)) { +						best_post_div = post_div; +						best_ref_div = ref_div; +						best_feedback_div = feedback_div; +						best_freq = current_freq; +						best_error = error; +						best_vco_diff = vco_diff; +					} +				} +				 +				if (current_freq < freq) +					min_feed_div = feedback_div+1; +				else +					max_feed_div = feedback_div; +			} +		} +	} +	 +	*dot_clock_p = best_freq / 10000; +	*fb_div_p = best_feedback_div; +	*ref_div_p = best_ref_div; +	*post_div_p = best_post_div; +} + +void radeon_get_clock_info(struct drm_device *dev) +{ +	drm_radeon_private_t *dev_priv = dev->dev_private; +	struct radeon_pll *pll = &dev_priv->mode_info.pll; +	int ret; + +	if (dev_priv->is_atom_bios) +		ret = radeon_atom_get_clock_info(dev); +	else +		ret = radeon_combios_get_clock_info(dev); + +	if (ret) { + +		if (pll->reference_div < 2) pll->reference_div = 12; +	} else { +		// TODO FALLBACK +	} + +	if (radeon_is_avivo(dev_priv)) { +		pll->min_post_div = 2; +		pll->max_post_div = 0x7f; +	} else { +		pll->min_post_div = 1; +		pll->max_post_div = 12; // 16 on crtc 0?? +	} + +	pll->min_ref_div = 2; +	pll->max_ref_div = 0x3ff; +	pll->min_feedback_div = 4; +	pll->max_feedback_div = 0x7ff; +	pll->best_vco = 0; + +} + +static void radeon_user_framebuffer_destroy(struct drm_framebuffer *fb) +{ +	struct radeon_framebuffer *radeon_fb = to_radeon_framebuffer(fb); +	struct drm_device *dev = fb->dev; + +	if (fb->fbdev) +		radeonfb_remove(dev, fb); + +	drm_framebuffer_cleanup(fb); +	kfree(radeon_fb); +} + +static const struct drm_framebuffer_funcs radeon_fb_funcs = { +	.destroy = radeon_user_framebuffer_destroy, +}; + +struct drm_framebuffer *radeon_user_framebuffer_create(struct drm_device *dev, +						       struct drm_file *filp, +						       struct drm_mode_fb_cmd *mode_cmd) +{ + +	struct radeon_framebuffer *radeon_fb; + +	radeon_fb = kzalloc(sizeof(*radeon_fb), GFP_KERNEL); +	if (!radeon_fb) +		return NULL; + +	drm_framebuffer_init(dev, &radeon_fb->base, &radeon_fb_funcs); +	drm_helper_mode_fill_fb_struct(&radeon_fb->base, mode_cmd); + +	if (filp) { +		radeon_fb->obj = drm_gem_object_lookup(dev, filp, +						       mode_cmd->handle); +		if (!radeon_fb->obj) { +			kfree(radeon_fb); +			return NULL; +		} +		drm_gem_object_unreference(radeon_fb->obj); +	} +	return &radeon_fb->base; +} + +static const struct drm_mode_config_funcs radeon_mode_funcs = { +	.fb_create = radeon_user_framebuffer_create, +	.fb_changed = radeonfb_probe, +}; + + +int radeon_modeset_init(struct drm_device *dev) +{ +	drm_radeon_private_t *dev_priv = dev->dev_private; +	static struct card_info card; +	size_t size; +	int num_crtc = 2, i; +	int ret; + +	drm_mode_config_init(dev); + +	dev->mode_config.funcs = (void *)&radeon_mode_funcs; + +	if (radeon_is_avivo(dev_priv)) { +		    dev->mode_config.max_width = 8192; +		    dev->mode_config.max_height = 8192; +	} else { +		    dev->mode_config.max_width = 4096; +		    dev->mode_config.max_height = 4096; +	} + +	dev->mode_config.fb_base = dev_priv->fb_aper_offset; + +	/* allocate crtcs - TODO single crtc */ +	for (i = 0; i < num_crtc; i++) { +		radeon_crtc_init(dev, i); +	} + +	/* okay we should have all the bios connectors */ + +	ret = radeon_setup_enc_conn(dev); + +	if (!ret) +		return ret; +	 +	drm_helper_initial_config(dev, false); + +	return 0; +} + + +int radeon_load_modeset_init(struct drm_device *dev) +{ +	int ret; +	ret = radeon_modeset_init(dev); + +	return ret; +} + +void radeon_modeset_cleanup(struct drm_device *dev) +{ +	drm_mode_config_cleanup(dev); +} + diff --git a/linux-core/radeon_drv.c b/linux-core/radeon_drv.c index 934fa0b9..7676ca4c 100644 --- a/linux-core/radeon_drv.c +++ b/linux-core/radeon_drv.c @@ -37,10 +37,17 @@  #include "drm_pciids.h"  int radeon_no_wb; +int radeon_dynclks = 1;  MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers\n");  module_param_named(no_wb, radeon_no_wb, int, 0444); +unsigned int radeon_modeset = 0; +module_param_named(modeset, radeon_modeset, int, 0400); + +MODULE_PARM_DESC(dynclks, "Disable/Enable dynamic clocks"); +module_param_named(dynclks, radeon_dynclks, int, 0444); +  static int dri_library_name(struct drm_device * dev, char * buf)  {  	drm_radeon_private_t *dev_priv = dev->dev_private; @@ -78,11 +85,29 @@ static struct pci_device_id pciidlist[] = {  	radeon_PCI_IDS  }; +extern struct drm_fence_driver radeon_fence_driver; + +static uint32_t radeon_mem_prios[] = {DRM_BO_MEM_VRAM, DRM_BO_MEM_TT, DRM_BO_MEM_LOCAL}; +static uint32_t radeon_busy_prios[] = {DRM_BO_MEM_TT, DRM_BO_MEM_VRAM, DRM_BO_MEM_LOCAL}; + +static struct drm_bo_driver radeon_bo_driver = { +	.mem_type_prio = radeon_mem_prios, +	.mem_busy_prio = radeon_busy_prios, +	.num_mem_type_prio = sizeof(radeon_mem_prios)/sizeof(uint32_t), +	.num_mem_busy_prio = sizeof(radeon_busy_prios)/sizeof(uint32_t), +	.create_ttm_backend_entry = radeon_create_ttm_backend_entry, +	.fence_type = radeon_fence_types, +	.invalidate_caches = radeon_invalidate_caches, +	.init_mem_type = radeon_init_mem_type, +	.move = radeon_move, +	.evict_flags = radeon_evict_flags, +}; +  static int probe(struct pci_dev *pdev, const struct pci_device_id *ent);  static struct drm_driver driver = {  	.driver_features =  	    DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG | -	    DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED, +	    DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED | DRIVER_GEM,  	.dev_priv_size = sizeof(drm_radeon_buf_priv_t),  	.load = radeon_driver_load,  	.firstopen = radeon_driver_firstopen, @@ -105,7 +130,11 @@ static struct drm_driver driver = {  	.get_map_ofs = drm_core_get_map_ofs,  	.get_reg_ofs = drm_core_get_reg_ofs,  	.ioctls = radeon_ioctls, +	.gem_init_object = radeon_gem_init_object, +	.gem_free_object = radeon_gem_free_object,  	.dma_ioctl = radeon_cp_buffers, +	.master_create = radeon_master_create, +	.master_destroy = radeon_master_destroy,  	.fops = {  		.owner = THIS_MODULE,  		.open = drm_open, @@ -125,6 +154,9 @@ static struct drm_driver driver = {  		.remove = __devexit_p(drm_cleanup_pci),  	}, +	.fence_driver = &radeon_fence_driver, +	.bo_driver = &radeon_bo_driver, +  	.name = DRIVER_NAME,  	.desc = DRIVER_DESC,  	.date = DRIVER_DATE, @@ -141,6 +173,10 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *ent)  static int __init radeon_init(void)  {  	driver.num_ioctls = radeon_max_ioctl; + +	if (radeon_modeset == 1) +		driver.driver_features |= DRIVER_MODESET; +  	return drm_init(&driver, pciidlist);  } diff --git a/linux-core/radeon_encoders.c b/linux-core/radeon_encoders.c new file mode 100644 index 00000000..1b75bd6a --- /dev/null +++ b/linux-core/radeon_encoders.c @@ -0,0 +1,962 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: Dave Airlie + *          Alex Deucher + */ +#include "drmP.h" +#include "drm_crtc_helper.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + +extern int atom_debug; + +static void radeon_rmx_mode_fixup(struct drm_encoder *encoder, +				  struct drm_display_mode *mode, +				  struct drm_display_mode *adjusted_mode) +{ +	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); +	if (mode->hdisplay < radeon_encoder->panel_xres || +	    mode->vdisplay < radeon_encoder->panel_yres) { +		radeon_encoder->flags |= RADEON_USE_RMX; +		adjusted_mode->hdisplay = radeon_encoder->panel_xres; +		adjusted_mode->vdisplay = radeon_encoder->panel_yres; +		adjusted_mode->htotal = radeon_encoder->panel_xres + radeon_encoder->hblank; +		adjusted_mode->hsync_start = radeon_encoder->panel_xres + radeon_encoder->hoverplus; +		adjusted_mode->hsync_end = adjusted_mode->hsync_start + radeon_encoder->hsync_width; +		adjusted_mode->vtotal = radeon_encoder->panel_yres + radeon_encoder->vblank; +		adjusted_mode->vsync_start = radeon_encoder->panel_yres + radeon_encoder->voverplus; +		adjusted_mode->vsync_end = adjusted_mode->vsync_start + radeon_encoder->vsync_width; +		/* update crtc values */ +		drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); +		/* adjust crtc values */ +		adjusted_mode->crtc_hdisplay = radeon_encoder->panel_xres; +		adjusted_mode->crtc_vdisplay = radeon_encoder->panel_yres; +		adjusted_mode->crtc_htotal = adjusted_mode->crtc_hdisplay + radeon_encoder->hblank; +		adjusted_mode->crtc_hsync_start = adjusted_mode->crtc_hdisplay + radeon_encoder->hoverplus; +		adjusted_mode->crtc_hsync_end = adjusted_mode->crtc_hsync_start + radeon_encoder->hsync_width; +		adjusted_mode->crtc_vtotal = adjusted_mode->crtc_vdisplay + radeon_encoder->vblank; +		adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + radeon_encoder->voverplus; +		adjusted_mode->crtc_vsync_end = adjusted_mode->crtc_vsync_start + radeon_encoder->vsync_width; +	} else { +		adjusted_mode->htotal = radeon_encoder->panel_xres + radeon_encoder->hblank; +		adjusted_mode->hsync_start = radeon_encoder->panel_xres + radeon_encoder->hoverplus; +		adjusted_mode->hsync_end = adjusted_mode->hsync_start + radeon_encoder->hsync_width; +		adjusted_mode->vtotal = radeon_encoder->panel_yres + radeon_encoder->vblank; +		adjusted_mode->vsync_start = radeon_encoder->panel_yres + radeon_encoder->voverplus; +		adjusted_mode->vsync_end = adjusted_mode->vsync_start + radeon_encoder->vsync_width; +		/* update crtc values */ +		drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); +		/* adjust crtc values */ +		adjusted_mode->crtc_htotal = adjusted_mode->crtc_hdisplay + radeon_encoder->hblank; +		adjusted_mode->crtc_hsync_start = adjusted_mode->crtc_hdisplay + radeon_encoder->hoverplus; +		adjusted_mode->crtc_hsync_end = adjusted_mode->crtc_hsync_start + radeon_encoder->hsync_width; +		adjusted_mode->crtc_vtotal = adjusted_mode->crtc_vdisplay + radeon_encoder->vblank; +		adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + radeon_encoder->voverplus; +		adjusted_mode->crtc_vsync_end = adjusted_mode->crtc_vsync_start + radeon_encoder->vsync_width; +	} +	adjusted_mode->clock = radeon_encoder->dotclock; +	adjusted_mode->flags = radeon_encoder->flags; +} + + +static int atom_dac_find_atom_type(struct radeon_encoder *radeon_encoder, struct drm_connector *connector) +{ +	struct drm_device *dev = radeon_encoder->base.dev; +	struct drm_connector *connector_find; +	int atom_type = -1; + +	if (!connector) { +		list_for_each_entry(connector_find, &dev->mode_config.connector_list, head) { +			if (connector_find->encoder == &radeon_encoder->base) +				connector = connector_find; +		} +	} +	if (connector) { +		/* look for the encoder in the connector list - +		   check if we the DAC is enabled on a VGA or STV/CTV or CV connector */ +		/* work out the ATOM_DEVICE bits */ +		switch (connector->connector_type) { +		case CONNECTOR_VGA: +		case CONNECTOR_DVI_I: +		case CONNECTOR_DVI_A: +			if (radeon_encoder->atom_device & ATOM_DEVICE_CRT1_SUPPORT) +				atom_type = ATOM_DEVICE_CRT1_INDEX; +			else if (radeon_encoder->atom_device & ATOM_DEVICE_CRT2_SUPPORT) +				atom_type = ATOM_DEVICE_CRT2_INDEX; +			break; +		case CONNECTOR_STV: +		case CONNECTOR_CTV: +			if (radeon_encoder->atom_device & ATOM_DEVICE_TV1_SUPPORT) +				atom_type = ATOM_DEVICE_TV1_INDEX; +			break; +		case CONNECTOR_DIN: +			if (radeon_encoder->atom_device & ATOM_DEVICE_TV1_SUPPORT) +				atom_type = ATOM_DEVICE_TV1_INDEX; +			if (radeon_encoder->atom_device & ATOM_DEVICE_CV_SUPPORT) +				atom_type = ATOM_DEVICE_CV_INDEX; +			break; +		} +	} + +	return atom_type; +} + +/* LVTMA encoder for LVDS usage */ +static void atombios_display_device_control(struct drm_encoder *encoder, int index, uint8_t state) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION args; + +	memset(&args, 0, sizeof(args)); +	args.ucAction = state; + +	atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +} + + +static void atombios_scaler_setup(struct drm_encoder *encoder, struct drm_display_mode *mode) +{ +	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); +	struct drm_device *dev = encoder->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); +	ENABLE_SCALER_PS_ALLOCATION args; +	int index = GetIndexIntoMasterTable(COMMAND, EnableScaler); + +	memset(&args, 0, sizeof(args)); +	args.ucScaler = radeon_crtc->crtc_id; + +	if (radeon_encoder->flags & RADEON_USE_RMX) { +		if (radeon_encoder->rmx_type == RMX_FULL) +			args.ucEnable = ATOM_SCALER_EXPANSION; +		else if (radeon_encoder->rmx_type == RMX_CENTER) +			args.ucEnable = ATOM_SCALER_CENTER; +	} else +		args.ucEnable = ATOM_SCALER_DISABLE; + +	atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +} + +void atombios_set_crtc_source(struct drm_encoder *encoder, int source) +{ +	int index = GetIndexIntoMasterTable(COMMAND, SelectCRTC_Source); +	struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); +	struct drm_radeon_private *dev_priv = encoder->dev->dev_private; +	uint8_t frev, crev; +	SELECT_CRTC_SOURCE_PS_ALLOCATION crtc_src_param; +	SELECT_CRTC_SOURCE_PARAMETERS_V2 crtc_src_param2; +	uint32_t *param = NULL; + +	atom_parse_cmd_header(dev_priv->mode_info.atom_context, index, &frev, &crev); +	switch (frev) { +	case 1: { +		switch (crev) { +		case 0: +		case 1: +		default: +			memset(&crtc_src_param, 0, sizeof(crtc_src_param)); +			crtc_src_param.ucCRTC = radeon_crtc->crtc_id; +			crtc_src_param.ucDevice = source; +			param = (uint32_t *)&crtc_src_param; +			break; +		case 2: +			memset(&crtc_src_param2, 0, sizeof(crtc_src_param2)); +			crtc_src_param2.ucCRTC = radeon_crtc->crtc_id; +			crtc_src_param2.ucEncoderID = source; +			switch (source) { +			case ATOM_DEVICE_CRT1_INDEX: +			case ATOM_DEVICE_CRT2_INDEX: +				crtc_src_param2.ucEncodeMode = ATOM_ENCODER_MODE_CRT; +				break; +			case ATOM_DEVICE_DFP1_INDEX: +			case ATOM_DEVICE_DFP2_INDEX: +			case ATOM_DEVICE_DFP3_INDEX: +				crtc_src_param2.ucEncodeMode = ATOM_ENCODER_MODE_DVI; +				// TODO ENCODER MODE +				break; +			case ATOM_DEVICE_LCD1_INDEX: +				crtc_src_param2.ucEncodeMode = ATOM_ENCODER_MODE_LVDS; +				break; +			case ATOM_DEVICE_TV1_INDEX: +				crtc_src_param2.ucEncodeMode = ATOM_ENCODER_MODE_TV; +				break; +			case ATOM_DEVICE_CV_INDEX: +				crtc_src_param2.ucEncodeMode = ATOM_ENCODER_MODE_CV; +				break; +			} +			param = (uint32_t *)&crtc_src_param2; +			break; +		} +	} +		break; +	default: +		return; +	} +	 +	atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)param); + +} + +static void radeon_dfp_disable_dither(struct drm_encoder *encoder, int device) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; + +	switch (device) { +	case ATOM_DEVICE_DFP1_INDEX: +		RADEON_WRITE(AVIVO_TMDSA_BIT_DEPTH_CONTROL, 0); /* TMDSA */ +		break; +	case ATOM_DEVICE_DFP2_INDEX: +		if ((dev_priv->chip_family == CHIP_RS600) || +		    (dev_priv->chip_family == CHIP_RS690) || +		    (dev_priv->chip_family == CHIP_RS740)) +			RADEON_WRITE(AVIVO_DDIA_BIT_DEPTH_CONTROL, 0); /* DDIA */ +		else +			RADEON_WRITE(AVIVO_DVOA_BIT_DEPTH_CONTROL, 0); /* DVO */ +		break; +		/*case ATOM_DEVICE_LCD1_INDEX:*/ /* LVDS panels need dither enabled */ +	case ATOM_DEVICE_DFP3_INDEX: +		RADEON_WRITE(AVIVO_LVTMA_BIT_DEPTH_CONTROL, 0); /* LVTMA */ +		break; +	default: +		break; +	} +} + + +static void radeon_lvtma_mode_set(struct drm_encoder *encoder, +				  struct drm_display_mode *mode, +				  struct drm_display_mode *adjusted_mode) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	LVDS_ENCODER_CONTROL_PS_ALLOCATION args; +	int index = GetIndexIntoMasterTable(COMMAND, LVDSEncoderControl); + +	memset(&args, 0, sizeof(args)); +	atombios_scaler_setup(encoder, mode); +	atombios_set_crtc_source(encoder, ATOM_DEVICE_LCD1_INDEX); + +	args.ucAction = 1; +	if (adjusted_mode->clock > 165000) +		args.ucMisc = 1; +	else +		args.ucMisc = 0; +	args.usPixelClock = cpu_to_le16(adjusted_mode->clock / 10); +	 +	printk("executing set LVDS encoder\n"); +	atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +} + + +static void radeon_lvtma_dpms(struct drm_encoder *encoder, int mode) +{ +	struct drm_device *dev = encoder->dev; +	int index = GetIndexIntoMasterTable(COMMAND, LCD1OutputControl); + +	switch(mode) { +	case DRM_MODE_DPMS_ON: +		atombios_display_device_control(encoder, index, ATOM_ENABLE); +		break; +	case DRM_MODE_DPMS_STANDBY:	 +	case DRM_MODE_DPMS_SUSPEND: +	case DRM_MODE_DPMS_OFF: +		atombios_display_device_control(encoder, index, ATOM_DISABLE); +		break; +	} +} + +static bool radeon_lvtma_mode_fixup(struct drm_encoder *encoder, +				    struct drm_display_mode *mode, +				    struct drm_display_mode *adjusted_mode) +{ +	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + +	radeon_encoder->flags &= ~RADEON_USE_RMX; + +	if (radeon_encoder->rmx_type != RMX_OFF) +		radeon_rmx_mode_fixup(encoder, mode, adjusted_mode); +	 +	return true; +} + +static void radeon_lvtma_prepare(struct drm_encoder *encoder) +{ +	radeon_lvtma_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void radeon_lvtma_commit(struct drm_encoder *encoder) +{ +	radeon_lvtma_dpms(encoder, DRM_MODE_DPMS_ON); +} + +static const struct drm_encoder_helper_funcs radeon_atom_lvtma_helper_funcs = { +	.dpms = radeon_lvtma_dpms, +	.mode_fixup = radeon_lvtma_mode_fixup, +	.prepare = radeon_lvtma_prepare, +	.mode_set = radeon_lvtma_mode_set, +	.commit = radeon_lvtma_commit, +}; + +static void radeon_enc_destroy(struct drm_encoder *encoder) +{ +	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); +	drm_encoder_cleanup(encoder); +	kfree(radeon_encoder); +} + +static const struct drm_encoder_funcs radeon_atom_lvtma_enc_funcs = { +	.destroy = radeon_enc_destroy, +}; + +struct drm_encoder *radeon_encoder_lvtma_add(struct drm_device *dev, int bios_index) +{ +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_mode_info *mode_info = &dev_priv->mode_info; +	struct radeon_encoder *radeon_encoder; +	struct drm_encoder *encoder; +	radeon_encoder = kzalloc(sizeof(struct radeon_encoder), GFP_KERNEL); +	if (!radeon_encoder) { +		return NULL; +	} + +	encoder = &radeon_encoder->base; + +	encoder->possible_crtcs = 0x3; +	encoder->possible_clones = 0; +	drm_encoder_init(dev, encoder, &radeon_atom_lvtma_enc_funcs, +			 DRM_MODE_ENCODER_LVDS); + +	drm_encoder_helper_add(encoder, &radeon_atom_lvtma_helper_funcs); +	radeon_encoder->atom_device = mode_info->bios_connector[bios_index].devices; + +	/* TODO get the LVDS info from the BIOS for panel size etc. */ +	/* get the lvds info from the bios */ +	radeon_get_lvds_info(radeon_encoder); + +	/* LVDS gets default RMX full scaling */ +	radeon_encoder->rmx_type = RMX_FULL; + +	return encoder; +} + +static void radeon_atom_dac_dpms(struct drm_encoder *encoder, int mode) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); +	int atom_type = -1; +	int index; + +	atom_type = atom_dac_find_atom_type(radeon_encoder, NULL); +	if (atom_type == -1) +		return; +	 +	switch(atom_type) { +	case ATOM_DEVICE_CRT1_INDEX: +		index = GetIndexIntoMasterTable(COMMAND, DAC1OutputControl); +		break; +	case ATOM_DEVICE_CRT2_INDEX: +		index = GetIndexIntoMasterTable(COMMAND, DAC2OutputControl); +		break; +	case ATOM_DEVICE_TV1_INDEX: +		index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl); +		break; +	case ATOM_DEVICE_CV_INDEX: +		index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl); +		break; +	default: +		return; +	} + +	switch(mode) { +	case DRM_MODE_DPMS_ON: +		atombios_display_device_control(encoder, index, ATOM_ENABLE); +		break; +	case DRM_MODE_DPMS_STANDBY:	 +	case DRM_MODE_DPMS_SUSPEND: +	case DRM_MODE_DPMS_OFF: +		atombios_display_device_control(encoder, index, ATOM_DISABLE); +		break; +	} +} + +static bool radeon_atom_dac_mode_fixup(struct drm_encoder *encoder, +				  struct drm_display_mode *mode, +				  struct drm_display_mode *adjusted_mode) +{ +	return true; +} + +static void radeon_atom_dac_prepare(struct drm_encoder *encoder) +{ +	radeon_atom_dac_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void radeon_atom_dac_commit(struct drm_encoder *encoder) +{ +	radeon_atom_dac_dpms(encoder, DRM_MODE_DPMS_ON); +} + +static int atombios_dac_setup(struct drm_encoder *encoder, +			      struct drm_display_mode *mode, +			      int atom_type) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); +	DAC_ENCODER_CONTROL_PS_ALLOCATION args; +	int id = (radeon_encoder->type.dac == DAC_TVDAC); +	int index; + +	memset(&args, 0, sizeof(args)); +	if (id == 0) +		index = GetIndexIntoMasterTable(COMMAND, DAC1EncoderControl); +	else +		index = GetIndexIntoMasterTable(COMMAND, DAC2EncoderControl); + +	args.ucAction = 1; +	args.usPixelClock = cpu_to_le16(mode->clock / 10); +	if ((atom_type == ATOM_DEVICE_CRT1_INDEX) || +	    (atom_type == ATOM_DEVICE_CRT2_INDEX)) +		args.ucDacStandard = id ? ATOM_DAC2_PS2 : ATOM_DAC1_PS2; +	else if (atom_type == ATOM_DEVICE_CV_INDEX) +		args.ucDacStandard = id ? ATOM_DAC2_CV : ATOM_DAC1_CV; +	else if (atom_type == ATOM_DEVICE_TV1_INDEX) +		args.ucDacStandard = id ? ATOM_DAC2_NTSC : ATOM_DAC1_NTSC; +	/* TODO PAL */ +	atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +	 +	return 0; +} + +static int atombios_tv1_setup(struct drm_encoder *encoder, +			      struct drm_display_mode *mode, +			      int atom_type) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); +	TV_ENCODER_CONTROL_PS_ALLOCATION args; +	int index = GetIndexIntoMasterTable(COMMAND, TVEncoderControl); + +	memset(&args, 0, sizeof(args)); +	args.sTVEncoder.ucAction = 1; +	if (atom_type == ATOM_DEVICE_CV_INDEX) +		args.sTVEncoder.ucTvStandard = ATOM_TV_CV; +	else { +		// TODO PAL +		args.sTVEncoder.ucTvStandard = ATOM_TV_NTSC; +	} + +	args.sTVEncoder.usPixelClock = cpu_to_le16(mode->clock / 10); +	 +	atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +	return 0; +} + +static void radeon_atom_dac_mode_set(struct drm_encoder *encoder, +				     struct drm_display_mode *mode, +				     struct drm_display_mode *adjusted_mode) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); +	int atom_type = -1; + +	atom_type = atom_dac_find_atom_type(radeon_encoder, NULL); +	if (atom_type == -1) +		return; + +	atombios_scaler_setup(encoder, mode); +	atombios_set_crtc_source(encoder, atom_type); + +	atombios_dac_setup(encoder, adjusted_mode, atom_type); +	if ((atom_type == ATOM_DEVICE_TV1_INDEX) || +	    (atom_type == ATOM_DEVICE_CV_INDEX)) +		atombios_tv1_setup(encoder, adjusted_mode, atom_type); +	 +} + +static bool atom_dac_load_detect(struct drm_encoder *encoder, int atom_devices) +{ +	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); +	struct drm_device *dev = encoder->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	DAC_LOAD_DETECTION_PS_ALLOCATION args; +	int index = GetIndexIntoMasterTable(COMMAND, DAC_LoadDetection); + +	memset(&args, 0, sizeof(args)); +	args.sDacload.ucMisc = 0; +	args.sDacload.ucDacType = (radeon_encoder->type.dac == DAC_PRIMARY) ? ATOM_DAC_A : ATOM_DAC_B; + +	if (atom_devices & ATOM_DEVICE_CRT1_SUPPORT) +		args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CRT1_SUPPORT); +	else if (atom_devices & ATOM_DEVICE_CRT2_SUPPORT) +		args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CRT2_SUPPORT); +	else if (atom_devices & ATOM_DEVICE_CV_SUPPORT) { +		args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CV_SUPPORT); +		if (radeon_is_dce3(dev_priv)) +			args.sDacload.ucMisc = 1; +	} else if (atom_devices & ATOM_DEVICE_TV1_SUPPORT) { +		args.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_TV1_SUPPORT); +		if (radeon_is_dce3(dev_priv)) +			args.sDacload.ucMisc = 1; +	} else +		return false; +	 +	DRM_DEBUG("writing %x %x\n", args.sDacload.usDeviceID, args.sDacload.ucDacType); +	atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args); +	return true; +}        + +static enum drm_connector_status radeon_atom_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); +	int atom_type = -1; +	uint32_t bios_0_scratch; + +	atom_type = atom_dac_find_atom_type(radeon_encoder, connector); +	if (atom_type == -1) { +		DRM_DEBUG("exit after find \n"); +		return connector_status_unknown; +	} + +	if(!atom_dac_load_detect(encoder, (1 << atom_type))) { +		DRM_DEBUG("detect returned false \n"); +		return connector_status_unknown; +	} + + +	if (dev_priv->chip_family >= CHIP_R600)  +		bios_0_scratch = RADEON_READ(R600_BIOS_0_SCRATCH); +	else +		bios_0_scratch = RADEON_READ(RADEON_BIOS_0_SCRATCH); + +	DRM_DEBUG("Bios 0 scratch %x\n", bios_0_scratch); +	if (radeon_encoder->atom_device & ATOM_DEVICE_CRT1_SUPPORT) { +		if (bios_0_scratch & ATOM_S0_CRT1_MASK) +			return connector_status_connected; +	} else if (radeon_encoder->atom_device & ATOM_DEVICE_CRT2_SUPPORT) { +		if (bios_0_scratch & ATOM_S0_CRT2_MASK) +			return connector_status_connected; +	} else if (radeon_encoder->atom_device & ATOM_DEVICE_CV_SUPPORT) { +		if (bios_0_scratch & (ATOM_S0_CV_MASK|ATOM_S0_CV_MASK_A)) +			return connector_status_connected; +	} else if (radeon_encoder->atom_device & ATOM_DEVICE_TV1_SUPPORT) { +		if (bios_0_scratch & (ATOM_S0_TV1_COMPOSITE | ATOM_S0_TV1_COMPOSITE_A)) +			return connector_status_connected; // CTV +		else if (bios_0_scratch & (ATOM_S0_TV1_SVIDEO | ATOM_S0_TV1_SVIDEO_A)) +			return connector_status_connected; // STV +	} +	return connector_status_disconnected; +} + +static const struct drm_encoder_helper_funcs radeon_atom_dac_helper_funcs = { +	.dpms = radeon_atom_dac_dpms, +	.mode_fixup = radeon_atom_dac_mode_fixup, +	.prepare = radeon_atom_dac_prepare, +	.mode_set = radeon_atom_dac_mode_set, +	.commit = radeon_atom_dac_commit, +	.detect = radeon_atom_dac_detect, +}; + +static const struct drm_encoder_funcs radeon_atom_dac_enc_funcs = { +	. destroy = radeon_enc_destroy, +}; + + +static void atombios_tmds1_setup(struct drm_encoder *encoder, +				 struct drm_display_mode *mode) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); +	TMDS1_ENCODER_CONTROL_PS_ALLOCATION args; +	int index = GetIndexIntoMasterTable(COMMAND, TMDS1EncoderControl); + +	memset(&args, 0, sizeof(args)); +	args.ucAction = 1; +	if (mode->clock > 165000) +		args.ucMisc = 1; +	else +		args.ucMisc = 0; + +	args.usPixelClock = cpu_to_le16(mode->clock / 10); + +	atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args);	 +} + +static void atombios_tmds2_setup(struct drm_encoder *encoder, +				 struct drm_display_mode *mode) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); +	TMDS2_ENCODER_CONTROL_PS_ALLOCATION args; +	int index = GetIndexIntoMasterTable(COMMAND, TMDS2EncoderControl); + +	memset(&args, 0, sizeof(args)); +	args.ucAction = 1; +	if (mode->clock > 165000) +		args.ucMisc = 1; +	else +		args.ucMisc = 0; + +	args.usPixelClock = cpu_to_le16(mode->clock / 10); + +	atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args);	 +} + + +static void atombios_ext_tmds_setup(struct drm_encoder *encoder, +				    struct drm_display_mode *mode) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); +	ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION args; +	int index = GetIndexIntoMasterTable(COMMAND, DVOEncoderControl); + +	memset(&args, 0, sizeof(args)); +	args.sXTmdsEncoder.ucEnable = 1; + +	if (mode->clock > 165000) +		args.sXTmdsEncoder.ucMisc = 1; +	else +		args.sXTmdsEncoder.ucMisc = 0; + +	// TODO 6-bit DAC +//	args.usPixelClock = cpu_to_le16(mode->clock / 10); + +	atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args);	 +} + +static void atombios_dig1_setup(struct drm_encoder *encoder, +				struct drm_display_mode *mode) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	DIG_ENCODER_CONTROL_PS_ALLOCATION args; +	int index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl); + +	args.ucAction = 1; +	args.usPixelClock = mode->clock / 10; +	args.ucConfig = ATOM_ENCODER_CONFIG_TRANSMITTER1; + +	// TODO coherent mode +//	if (encoder->coherent_mode) +//		args.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT; + +	if (mode->clock > 165000) { +		args.ucConfig |= ATOM_ENCODER_CONFIG_LINKA_B; +		args.ucLaneNum = 8; +	} else { +		args.ucConfig |= ATOM_ENCODER_CONFIG_LINKA; +		args.ucLaneNum = 4; +	} + +	// TODO Encoder MODE +	atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args);	 +} + +static void atombios_ddia_setup(struct drm_encoder *encoder, +				struct drm_display_mode *mode) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	DVO_ENCODER_CONTROL_PS_ALLOCATION args; +	int index = GetIndexIntoMasterTable(COMMAND, DVOEncoderControl); + +	args.sDVOEncoder.ucAction = ATOM_ENABLE; +	args.sDVOEncoder.usPixelClock = mode->clock / 10; + +	if (mode->clock > 165000) +		args.sDVOEncoder.usDevAttr.sDigAttrib.ucAttribute = PANEL_ENCODER_MISC_DUAL; +	else +		args.sDVOEncoder.usDevAttr.sDigAttrib.ucAttribute = 0; + +	atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args);	 +} + +struct drm_encoder *radeon_encoder_atom_dac_add(struct drm_device *dev, int bios_index, int dac_type, int with_tv) +{ +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_mode_info *mode_info = &dev_priv->mode_info; +	struct radeon_encoder *radeon_encoder = NULL; +	struct drm_encoder *encoder; +	int type = with_tv ? DRM_MODE_ENCODER_TVDAC : DRM_MODE_ENCODER_DAC; +	int found = 0; +	int digital_enc_mask = ~(ATOM_DEVICE_DFP1_SUPPORT | ATOM_DEVICE_DFP2_SUPPORT | ATOM_DEVICE_DFP3_SUPPORT | +				ATOM_DEVICE_LCD1_SUPPORT); +	/* we may already have added this encoder */ +	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { +		if (encoder->encoder_type != DRM_MODE_ENCODER_DAC || +		    encoder->encoder_type != DRM_MODE_ENCODER_TVDAC) +			continue; + +		radeon_encoder = to_radeon_encoder(encoder); +		if (radeon_encoder->type.dac == dac_type) { +			found = 1; +			break; +		} +	} + +	if (found) { +		/* upgrade to a TV controlling DAC */ +		if (type == DRM_MODE_ENCODER_TVDAC) +			encoder->encoder_type = type; +		radeon_encoder->atom_device |= mode_info->bios_connector[bios_index].devices; +		radeon_encoder->atom_device &= digital_enc_mask; +		return encoder; +	} + +	radeon_encoder = kzalloc(sizeof(struct radeon_encoder), GFP_KERNEL); +	if (!radeon_encoder) { +		return NULL; +	} + +	encoder = &radeon_encoder->base; + +	encoder->possible_crtcs = 0x3; +	encoder->possible_clones = 0; +	drm_encoder_init(dev, encoder, &radeon_atom_dac_enc_funcs, +			 type); + +	drm_encoder_helper_add(encoder, &radeon_atom_dac_helper_funcs); +	radeon_encoder->type.dac = dac_type; +	radeon_encoder->atom_device = mode_info->bios_connector[bios_index].devices; + +	/* mask off any digital encoders */ +	radeon_encoder->atom_device &= digital_enc_mask; +	return encoder; +} + +static void radeon_atom_tmds_dpms(struct drm_encoder *encoder, int mode) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); +	int atom_type = -1; +	int index = -1; + +	if (radeon_encoder->atom_device & ATOM_DEVICE_DFP1_SUPPORT) +		atom_type = ATOM_DEVICE_DFP1_INDEX; +	if (radeon_encoder->atom_device & ATOM_DEVICE_DFP2_SUPPORT) +		atom_type = ATOM_DEVICE_DFP2_INDEX; +	if (radeon_encoder->atom_device & ATOM_DEVICE_DFP3_SUPPORT) +		atom_type = ATOM_DEVICE_DFP3_INDEX; + +	if (atom_type == -1) +		return; + +	switch(atom_type) { +	case ATOM_DEVICE_DFP1_INDEX: +		index = GetIndexIntoMasterTable(COMMAND, TMDSAOutputControl); +		break; +	case ATOM_DEVICE_DFP2_INDEX: +		index = GetIndexIntoMasterTable(COMMAND, DVOOutputControl); +		break; +	case ATOM_DEVICE_DFP3_INDEX: +		index = GetIndexIntoMasterTable(COMMAND, LVTMAOutputControl); +		break; +	} + +	if (index == -1) +		return; + +	switch(mode) { +	case DRM_MODE_DPMS_ON: +		atombios_display_device_control(encoder, index, ATOM_ENABLE); +		break; +	case DRM_MODE_DPMS_STANDBY:	 +	case DRM_MODE_DPMS_SUSPEND: +	case DRM_MODE_DPMS_OFF: +		atombios_display_device_control(encoder, index, ATOM_DISABLE); +		break; +	} +} + +static bool radeon_atom_tmds_mode_fixup(struct drm_encoder *encoder, +				  struct drm_display_mode *mode, +				  struct drm_display_mode *adjusted_mode) +{ +	return true; +} + +static void radeon_atom_tmds_mode_set(struct drm_encoder *encoder, +				      struct drm_display_mode *mode, +				      struct drm_display_mode *adjusted_mode) +{ +	struct drm_device *dev = encoder->dev; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); +	int atom_type; + +	if (radeon_encoder->atom_device & ATOM_DEVICE_DFP1_SUPPORT) +		atom_type = ATOM_DEVICE_DFP1_INDEX; +	if (radeon_encoder->atom_device & ATOM_DEVICE_DFP2_SUPPORT) +		atom_type = ATOM_DEVICE_DFP2_INDEX; +	if (radeon_encoder->atom_device & ATOM_DEVICE_DFP3_SUPPORT) +		atom_type = ATOM_DEVICE_DFP3_INDEX; + +	atombios_scaler_setup(encoder, mode); +	atombios_set_crtc_source(encoder, atom_type); + +	if (atom_type == ATOM_DEVICE_DFP1_INDEX) +		atombios_tmds1_setup(encoder, adjusted_mode); +	if (atom_type == ATOM_DEVICE_DFP2_INDEX) { +		if ((dev_priv->chip_family == CHIP_RS600) || +		    (dev_priv->chip_family == CHIP_RS690) || +		    (dev_priv->chip_family == CHIP_RS740)) +			atombios_ddia_setup(encoder, adjusted_mode); +		else +			atombios_ext_tmds_setup(encoder, adjusted_mode); +	} +	if (atom_type == ATOM_DEVICE_DFP3_INDEX) +		atombios_tmds2_setup(encoder, adjusted_mode); +	radeon_dfp_disable_dither(encoder, atom_type); + + +} + +static void radeon_atom_tmds_prepare(struct drm_encoder *encoder) +{ +	radeon_atom_tmds_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void radeon_atom_tmds_commit(struct drm_encoder *encoder) +{ +	radeon_atom_tmds_dpms(encoder, DRM_MODE_DPMS_ON); +} + +static const struct drm_encoder_helper_funcs radeon_atom_tmds_helper_funcs = { +	.dpms = radeon_atom_tmds_dpms, +	.mode_fixup = radeon_atom_tmds_mode_fixup, +	.prepare = radeon_atom_tmds_prepare, +	.mode_set = radeon_atom_tmds_mode_set, +	.commit = radeon_atom_tmds_commit, +	/* no detect for TMDS */ +}; + +static const struct drm_encoder_funcs radeon_atom_tmds_enc_funcs = { +	. destroy = radeon_enc_destroy, +}; + +struct drm_encoder *radeon_encoder_atom_tmds_add(struct drm_device *dev, int bios_index, int tmds_type) +{ +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_mode_info *mode_info = &dev_priv->mode_info; +	struct radeon_encoder *radeon_encoder = NULL; +	struct drm_encoder *encoder; +	int analog_enc_mask = ~(ATOM_DEVICE_CRT1_SUPPORT | ATOM_DEVICE_CRT2_SUPPORT); + +		radeon_encoder = kzalloc(sizeof(struct radeon_encoder), GFP_KERNEL); +	if (!radeon_encoder) { +		return NULL; +	} + +	encoder = &radeon_encoder->base; + +	encoder->possible_crtcs = 0x3; +	encoder->possible_clones = 0; +	drm_encoder_init(dev, encoder, &radeon_atom_tmds_enc_funcs, +			 DRM_MODE_ENCODER_TMDS); + +	drm_encoder_helper_add(encoder, &radeon_atom_tmds_helper_funcs); + +	radeon_encoder->atom_device = mode_info->bios_connector[bios_index].devices; + +	/* mask off any analog encoders */ +	radeon_encoder->atom_device &= analog_enc_mask; +	return encoder; +} + +static void radeon_legacy_lvds_dpms(struct drm_encoder *encoder, int mode) +{ +	struct drm_device *dev = encoder->dev; +} + +static void radeon_legacy_lvds_prepare(struct drm_encoder *encoder) +{ +	radeon_legacy_lvds_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void radeon_legacy_lvds_commit(struct drm_encoder *encoder) +{ +	radeon_legacy_lvds_dpms(encoder, DRM_MODE_DPMS_ON); +} + +static void radeon_legacy_lvds_mode_set(struct drm_encoder *encoder, +				  struct drm_display_mode *mode, +				  struct drm_display_mode *adjusted_mode) +{ + + +} + +static const struct drm_encoder_helper_funcs radeon_legacy_lvds_helper_funcs = { +	.dpms = radeon_legacy_lvds_dpms, +	.mode_fixup = radeon_lvtma_mode_fixup, +	.prepare = radeon_legacy_lvds_prepare, +	.mode_set = radeon_legacy_lvds_mode_set, +	.commit = radeon_legacy_lvds_commit, +}; + + +static const struct drm_encoder_funcs radeon_legacy_lvds_enc_funcs = { +	.destroy = radeon_enc_destroy, +}; + +struct drm_encoder *radeon_encoder_legacy_lvds_add(struct drm_device *dev, int bios_index) +{ +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct radeon_mode_info *mode_info = &dev_priv->mode_info; +	struct radeon_encoder *radeon_encoder; +	struct drm_encoder *encoder; +	radeon_encoder = kzalloc(sizeof(struct radeon_encoder), GFP_KERNEL); +	if (!radeon_encoder) { +		return NULL; +	} + +	encoder = &radeon_encoder->base; + +	encoder->possible_crtcs = 0x3; +	encoder->possible_clones = 0; +	drm_encoder_init(dev, encoder, &radeon_legacy_lvds_enc_funcs, +			 DRM_MODE_ENCODER_LVDS); + +	drm_encoder_helper_add(encoder, &radeon_legacy_lvds_helper_funcs); + +	/* TODO get the LVDS info from the BIOS for panel size etc. */ +	/* get the lvds info from the bios */ +	radeon_combios_get_lvds_info(radeon_encoder); + +	/* LVDS gets default RMX full scaling */ +	radeon_encoder->rmx_type = RMX_FULL; + +	return encoder; +} diff --git a/linux-core/radeon_fb.c b/linux-core/radeon_fb.c new file mode 100644 index 00000000..5cb118c5 --- /dev/null +++ b/linux-core/radeon_fb.c @@ -0,0 +1,1163 @@ +/* + * Copyright © 2007 David Airlie + * + * 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 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + *     David Airlie + */ +    /* +     *  Modularization +     */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/init.h> + +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "drm_crtc_helper.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + +struct radeonfb_par { +	struct drm_device *dev; +	struct drm_display_mode *our_mode; +	struct radeon_framebuffer *radeon_fb; +	int crtc_count; +	/* crtc currently bound to this */ +	uint32_t crtc_ids[2]; +}; +/* +static int +var_to_refresh(const struct fb_var_screeninfo *var) +{ +	int xtot = var->xres + var->left_margin + var->right_margin + +		var->hsync_len; +	int ytot = var->yres + var->upper_margin + var->lower_margin + +		var->vsync_len; + +	return (1000000000 / var->pixclock * 1000 + 500) / xtot / ytot; +}*/ + +static int radeonfb_setcolreg(unsigned regno, unsigned red, unsigned green, +			unsigned blue, unsigned transp, +			struct fb_info *info) +{ +	struct radeonfb_par *par = info->par; +	struct drm_device *dev = par->dev; +	struct drm_crtc *crtc; +	int i; + +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); +		struct drm_mode_set *modeset = &radeon_crtc->mode_set; +		struct drm_framebuffer *fb = modeset->fb; + +		for (i = 0; i < par->crtc_count; i++) +			if (crtc->base.id == par->crtc_ids[i]) +				break; + +		if (i == par->crtc_count) +			continue; +		 + +		if (regno > 255) +			return 1; + +		if (fb->depth == 8) { +			radeon_crtc_fb_gamma_set(crtc, red, green, blue, regno); +			return 0; +		} + +		if (regno < 16) { +			switch (fb->depth) { +			case 15: +				fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) | +					((green & 0xf800) >>  6) | +					((blue & 0xf800) >> 11); +				break; +			case 16: +				fb->pseudo_palette[regno] = (red & 0xf800) | +					((green & 0xfc00) >>  5) | +					((blue  & 0xf800) >> 11); +				break; +			case 24: +			case 32: +				fb->pseudo_palette[regno] = ((red & 0xff00) << 8) | +					(green & 0xff00) | +					((blue  & 0xff00) >> 8); +				break; +			} +		} +	} +	return 0; +} + +static int radeonfb_check_var(struct fb_var_screeninfo *var, +			struct fb_info *info) +{ +	struct radeonfb_par *par = info->par; +	struct radeon_framebuffer *radeon_fb = par->radeon_fb; +	struct drm_framebuffer *fb = &radeon_fb->base; +	int depth; + +	if (var->pixclock == -1 || !var->pixclock) +		return -EINVAL; + +	/* Need to resize the fb object !!! */ +	if (var->xres > fb->width || var->yres > fb->height) { +		DRM_ERROR("Requested width/height is greater than current fb object %dx%d > %dx%d\n",var->xres,var->yres,fb->width,fb->height); +		DRM_ERROR("Need resizing code.\n"); +		return -EINVAL; +	} + +	switch (var->bits_per_pixel) { +	case 16: +		depth = (var->green.length == 6) ? 16 : 15; +		break; +	case 32: +		depth = (var->transp.length > 0) ? 32 : 24; +		break; +	default: +		depth = var->bits_per_pixel; +		break; +	} +		 +	switch (depth) { +	case 8: +		var->red.offset = 0; +		var->green.offset = 0; +		var->blue.offset = 0; +		var->red.length = 8; +		var->green.length = 8; +		var->blue.length = 8; +		var->transp.length = 0; +		var->transp.offset = 0; +		break; +	case 15: +		var->red.offset = 10; +		var->green.offset = 5; +		var->blue.offset = 0; +		var->red.length = 5; +		var->green.length = 5; +		var->blue.length = 5; +		var->transp.length = 1; +		var->transp.offset = 15; +		break; +	case 16: +		var->red.offset = 11; +		var->green.offset = 5; +		var->blue.offset = 0; +		var->red.length = 5; +		var->green.length = 6; +		var->blue.length = 5; +		var->transp.length = 0; +		var->transp.offset = 0; +		break; +	case 24: +		var->red.offset = 16; +		var->green.offset = 8; +		var->blue.offset = 0; +		var->red.length = 8; +		var->green.length = 8; +		var->blue.length = 8; +		var->transp.length = 0; +		var->transp.offset = 0; +		break; +	case 32: +		var->red.offset = 16; +		var->green.offset = 8; +		var->blue.offset = 0; +		var->red.length = 8; +		var->green.length = 8; +		var->blue.length = 8; +		var->transp.length = 8; +		var->transp.offset = 24; +		break; +	default: +		return -EINVAL;  +	} + +	return 0; +} + +/* this will let fbcon do the mode init */ +/* FIXME: take mode config lock? */ +static int radeonfb_set_par(struct fb_info *info) +{ +	struct radeonfb_par *par = info->par; +	struct drm_device *dev = par->dev; +	struct fb_var_screeninfo *var = &info->var; +	int i; + +	DRM_DEBUG("%d %d\n", var->xres, var->pixclock); + +	if (var->pixclock != -1) { + +		DRM_ERROR("PIXEL CLCOK SET\n"); +#if 0 +		struct radeon_framebuffer *radeon_fb = par->radeon_fb; +		struct drm_framebuffer *fb = &radeon_fb->base; +		struct drm_display_mode *drm_mode, *search_mode; +		struct drm_connector *connector = NULL; +		struct drm_device *dev = par->dev; + +		int found = 0; + +		switch (var->bits_per_pixel) { +		case 16: +			fb->depth = (var->green.length == 6) ? 16 : 15; +			break; +		case 32: +			fb->depth = (var->transp.length > 0) ? 32 : 24; +			break; +		default: +			fb->depth = var->bits_per_pixel; +			break; +		} +		 +		fb->bits_per_pixel = var->bits_per_pixel; +		 +		info->fix.line_length = fb->pitch; +		info->fix.smem_len = info->fix.line_length * fb->height; +		info->fix.visual = (fb->depth == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; +		 +		info->screen_size = info->fix.smem_len; /* ??? */ +		/* reuse desired mode if possible */ +		/* create a drm mode */ +		drm_mode = drm_mode_create(dev); +		drm_mode->hdisplay = var->xres; +		drm_mode->hsync_start = drm_mode->hdisplay + var->right_margin; +		drm_mode->hsync_end = drm_mode->hsync_start + var->hsync_len; +		drm_mode->htotal = drm_mode->hsync_end + var->left_margin; +		drm_mode->vdisplay = var->yres; +		drm_mode->vsync_start = drm_mode->vdisplay + var->lower_margin; +		drm_mode->vsync_end = drm_mode->vsync_start + var->vsync_len; +		drm_mode->vtotal = drm_mode->vsync_end + var->upper_margin; +		drm_mode->clock = PICOS2KHZ(var->pixclock); +		drm_mode->vrefresh = drm_mode_vrefresh(drm_mode); +		drm_mode->flags = 0; +		drm_mode->flags |= var->sync & FB_SYNC_HOR_HIGH_ACT ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; +		drm_mode->flags |= var->sync & FB_SYNC_VERT_HIGH_ACT ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; +		 +		drm_mode_set_name(drm_mode); +		drm_mode_set_crtcinfo(drm_mode, CRTC_INTERLACE_HALVE_V); +		 +		found = 0; +		list_for_each_entry(connector, &dev->mode_config.connector_list, head) { +			if (connector->encoder && +			    connector->encoder->crtc == par->set.crtc){ +				found = 1; +				break; +			} +		} +		 +		/* no connector bound, bail */ +		if (!found) +			return -EINVAL; +		 +		found = 0; +		drm_mode_debug_printmodeline(drm_mode); +		list_for_each_entry(search_mode, &connector->modes, head) { +			drm_mode_debug_printmodeline(search_mode); +			if (drm_mode_equal(drm_mode, search_mode)) { +				drm_mode_destroy(dev, drm_mode); +				drm_mode = search_mode; +				found = 1; +				break; +			} +		} +		 +		/* If we didn't find a matching mode that exists on our connector, +		 * create a new attachment for the incoming user specified mode +		 */ +		if (!found) { +			if (par->our_mode) { +				/* this also destroys the mode */ +				drm_mode_detachmode_crtc(dev, par->our_mode); +			} +			 +			par->set.mode = drm_mode; +			par->our_mode = drm_mode; +			drm_mode_debug_printmodeline(drm_mode); +			/* attach mode */ +			drm_mode_attachmode_crtc(dev, par->set.crtc, par->set.mode); +		} else { +			par->set.mode = drm_mode; +			if (par->our_mode) +				drm_mode_detachmode_crtc(dev, par->our_mode); +			par->our_mode = NULL; +		} +		return par->set.crtc->funcs->set_config(&par->set); +#endif +		return -EINVAL; +	} else { +		struct drm_crtc *crtc; +		int ret; + +		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +			struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + +			for (i = 0; i < par->crtc_count; i++) +				if (crtc->base.id == par->crtc_ids[i]) +					break; + +			if (i == par->crtc_count) +				continue; + +			if (crtc->fb == radeon_crtc->mode_set.fb) { +				ret = crtc->funcs->set_config(&radeon_crtc->mode_set); +				if (ret) +					return ret; +			} +		} +		return 0; +	} +} + +#if 0 +static void radeonfb_copyarea(struct fb_info *info, +			const struct fb_copyarea *region) +{ +	struct radeonfb_par *par = info->par; +	struct drm_device *dev = par->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	u32 src_x1, src_y1, dst_x1, dst_y1, dst_x2, dst_y2, offset; +	u32 cmd, rop_depth_pitch, src_pitch; +	RING_LOCALS; + +	cmd = XY_SRC_COPY_BLT_CMD; +	src_x1 = region->sx; +	src_y1 = region->sy; +	dst_x1 = region->dx; +	dst_y1 = region->dy; +	dst_x2 = region->dx + region->width; +	dst_y2 = region->dy + region->height; +	offset = par->fb->offset; +	rop_depth_pitch = BLT_ROP_GXCOPY | par->fb->pitch; +	src_pitch = par->fb->pitch; + +	switch (par->fb->bits_per_pixel) { +	case 16: +		rop_depth_pitch |= BLT_DEPTH_16_565; +		break; +	case 32: +		rop_depth_pitch |= BLT_DEPTH_32; +		cmd |= XY_SRC_COPY_BLT_WRITE_ALPHA | XY_SRC_COPY_BLT_WRITE_RGB; +		break; +	} + +	BEGIN_LP_RING(8); +	OUT_RING(cmd); +	OUT_RING(rop_depth_pitch); +	OUT_RING((dst_y1 << 16) | (dst_x1 & 0xffff)); +	OUT_RING((dst_y2 << 16) | (dst_x2 & 0xffff)); +	OUT_RING(offset); +	OUT_RING((src_y1 << 16) | (src_x1 & 0xffff)); +	OUT_RING(src_pitch); +	OUT_RING(offset); +	ADVANCE_LP_RING(); +} + +#define ROUND_UP_TO(x, y)	(((x) + (y) - 1) / (y) * (y)) +#define ROUND_DOWN_TO(x, y)	((x) / (y) * (y)) + +void radeonfb_imageblit(struct fb_info *info, const struct fb_image *image) +{ +	struct radeonfb_par *par = info->par; +	struct drm_device *dev = par->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	u32 cmd, rop_pitch_depth, tmp; +	int nbytes, ndwords, pad; +	u32 dst_x1, dst_y1, dst_x2, dst_y2, offset, bg, fg; +	int dat, ix, iy, iw; +	int i, j; +	RING_LOCALS; + +	/* size in bytes of a padded scanline */ +	nbytes = ROUND_UP_TO(image->width, 16) / 8; + +	/* Total bytes of padded scanline data to write out. */ +	nbytes *= image->height; + +	/* +	* Check if the glyph data exceeds the immediate mode limit. +	* It would take a large font (1K pixels) to hit this limit. +	*/ +	if (nbytes > 128 || image->depth != 1) +		return cfb_imageblit(info, image); + +	/* Src data is packaged a dword (32-bit) at a time. */ +	ndwords = ROUND_UP_TO(nbytes, 4) / 4; + +	/* +	* Ring has to be padded to a quad word. But because the command starts +	with 7 bytes, pad only if there is an even number of ndwords +	*/ +	pad = !(ndwords % 2); + +	DRM_DEBUG("imageblit %dx%dx%d to (%d,%d)\n", image->width, +		image->height, image->depth, image->dx, image->dy); +	DRM_DEBUG("nbytes: %d, ndwords: %d, pad: %d\n", nbytes, ndwords, pad); + +	tmp = (XY_MONO_SRC_COPY_IMM_BLT & 0xff) + ndwords; +	cmd = (XY_MONO_SRC_COPY_IMM_BLT & ~0xff) | tmp; +	offset = par->fb->offset; +	dst_x1 = image->dx; +	dst_y1 = image->dy; +	dst_x2 = image->dx + image->width; +	dst_y2 = image->dy + image->height; +	rop_pitch_depth = BLT_ROP_GXCOPY | par->fb->pitch; + +	switch (par->fb->bits_per_pixel) { +	case 8: +		rop_pitch_depth |= BLT_DEPTH_8; +		fg = image->fg_color; +		bg = image->bg_color; +		break; +	case 16: +		rop_pitch_depth |= BLT_DEPTH_16_565; +		fg = par->fb->pseudo_palette[image->fg_color]; +		bg = par->fb->pseudo_palette[image->bg_color]; +		break; +	case 32: +		rop_pitch_depth |= BLT_DEPTH_32; +		cmd |= XY_SRC_COPY_BLT_WRITE_ALPHA | XY_SRC_COPY_BLT_WRITE_RGB; +		fg = par->fb->pseudo_palette[image->fg_color]; +		bg = par->fb->pseudo_palette[image->bg_color]; +		break; +	default: +		DRM_ERROR("unknown depth %d\n", par->fb->bits_per_pixel); +		break; +	} +	 +	BEGIN_LP_RING(8 + ndwords); +	OUT_RING(cmd); +	OUT_RING(rop_pitch_depth); +	OUT_RING((dst_y1 << 16) | (dst_x1 & 0xffff)); +	OUT_RING((dst_y2 << 16) | (dst_x2 & 0xffff)); +	OUT_RING(offset); +	OUT_RING(bg); +	OUT_RING(fg); +	ix = iy = 0; +	iw = ROUND_UP_TO(image->width, 8) / 8; +	while (ndwords--) { +		dat = 0; +		for (j = 0; j < 2; ++j) { +			for (i = 0; i < 2; ++i) { +				if (ix != iw || i == 0) +					dat |= image->data[iy*iw + ix++] << (i+j*2)*8; +			} +			if (ix == iw && iy != (image->height - 1)) { +				ix = 0; +				++iy; +			} +		} +		OUT_RING(dat); +	} +	if (pad) +		OUT_RING(MI_NOOP); +	ADVANCE_LP_RING(); +} +#endif +static int radeonfb_pan_display(struct fb_var_screeninfo *var, +				struct fb_info *info) +{ +	struct radeonfb_par *par = info->par; +	struct drm_device *dev = par->dev; +	struct drm_mode_set *modeset; +	struct drm_crtc *crtc; +	struct radeon_crtc *radeon_crtc; +	int ret = 0; +	int i; + +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		 +		for (i = 0; i < par->crtc_count; i++) +			if (crtc->base.id == par->crtc_ids[i]) +				break; + +		if (i == par->crtc_count) +			continue; + +		radeon_crtc = to_radeon_crtc(crtc); +		modeset = &radeon_crtc->mode_set; + +		modeset->x = var->xoffset; +		modeset->y = var->yoffset; + +		if (modeset->num_connectors) { +			ret = crtc->funcs->set_config(modeset); +		   +			if (!ret) { +				info->var.xoffset = var->xoffset; +				info->var.yoffset = var->yoffset; +			} +		} +	} + +	return ret; +} + +static void radeonfb_on(struct fb_info *info) +{ +	struct radeonfb_par *par = info->par; +	struct drm_device *dev = par->dev; +	struct drm_crtc *crtc; +	struct drm_encoder *encoder; +	int i; + +	/* +	 * For each CRTC in this fb, find all associated encoders +	 * and turn them off, then turn off the CRTC. +	 */ +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + +		for (i = 0; i < par->crtc_count; i++) +			if (crtc->base.id == par->crtc_ids[i]) +				break; + +		crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); + +		/* Found a CRTC on this fb, now find encoders */ +		list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { +			if (encoder->crtc == crtc) { +				struct drm_encoder_helper_funcs *encoder_funcs; +				encoder_funcs = encoder->helper_private; +				encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); +			} +		} +	} +} + +static void radeonfb_off(struct fb_info *info, int dpms_mode) +{ +	struct radeonfb_par *par = info->par; +	struct drm_device *dev = par->dev; +	struct drm_crtc *crtc; +	struct drm_encoder *encoder; +	int i; + +	/* +	 * For each CRTC in this fb, find all associated encoders +	 * and turn them off, then turn off the CRTC. +	 */ +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + +		for (i = 0; i < par->crtc_count; i++) +			if (crtc->base.id == par->crtc_ids[i]) +				break; + +		/* Found a CRTC on this fb, now find encoders */ +		list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { +			if (encoder->crtc == crtc) { +				struct drm_encoder_helper_funcs *encoder_funcs; +				encoder_funcs = encoder->helper_private; +				encoder_funcs->dpms(encoder, dpms_mode); +			} +		} +		if (dpms_mode == DRM_MODE_DPMS_OFF) +			crtc_funcs->dpms(crtc, dpms_mode); +	} +} + +int radeonfb_blank(int blank, struct fb_info *info) +{ +	switch (blank) { +	case FB_BLANK_UNBLANK: +		radeonfb_on(info); +		break; +	case FB_BLANK_NORMAL: +		radeonfb_off(info, DRM_MODE_DPMS_STANDBY); +		break; +	case FB_BLANK_HSYNC_SUSPEND: +		radeonfb_off(info, DRM_MODE_DPMS_STANDBY); +		break; +	case FB_BLANK_VSYNC_SUSPEND: +		radeonfb_off(info, DRM_MODE_DPMS_SUSPEND); +		break; +	case FB_BLANK_POWERDOWN: +		radeonfb_off(info, DRM_MODE_DPMS_OFF); +		break; +	} +	return 0; +} + +static struct fb_ops radeonfb_ops = { +	.owner = THIS_MODULE, +	//.fb_open = radeonfb_open, +	//.fb_read = radeonfb_read, +	//.fb_write = radeonfb_write, +	//.fb_release = radeonfb_release, +	//.fb_ioctl = radeonfb_ioctl, +	.fb_check_var = radeonfb_check_var, +	.fb_set_par = radeonfb_set_par, +	.fb_setcolreg = radeonfb_setcolreg, +	.fb_fillrect = cfb_fillrect, +	.fb_copyarea = cfb_copyarea, //radeonfb_copyarea, +	.fb_imageblit = cfb_imageblit, //radeonfb_imageblit, +	.fb_pan_display = radeonfb_pan_display, +	.fb_blank = radeonfb_blank, +}; + +/** + * Curretly it is assumed that the old framebuffer is reused. + * + * LOCKING + * caller should hold the mode config lock. + * + */ +int radeonfb_resize(struct drm_device *dev, struct drm_crtc *crtc) +{ +	struct fb_info *info; +	struct drm_framebuffer *fb; +	struct drm_display_mode *mode = crtc->desired_mode; + +	fb = crtc->fb; +	if (!fb) +		return 1; + +	info = fb->fbdev; +	if (!info) +		return 1; + +	if (!mode) +		return 1; + +	info->var.xres = mode->hdisplay; +	info->var.right_margin = mode->hsync_start - mode->hdisplay; +	info->var.hsync_len = mode->hsync_end - mode->hsync_start; +	info->var.left_margin = mode->htotal - mode->hsync_end; +	info->var.yres = mode->vdisplay; +	info->var.lower_margin = mode->vsync_start - mode->vdisplay; +	info->var.vsync_len = mode->vsync_end - mode->vsync_start; +	info->var.upper_margin = mode->vtotal - mode->vsync_end; +	info->var.pixclock = 10000000 / mode->htotal * 1000 / mode->vtotal * 100; +	/* avoid overflow */ +	info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh; + +	return 0; +} +EXPORT_SYMBOL(radeonfb_resize); + +static struct drm_mode_set panic_mode; + +int radeonfb_panic(struct notifier_block *n, unsigned long ununsed, +		  void *panic_str) +{ +	DRM_ERROR("panic occurred, switching back to text console\n"); +	drm_crtc_helper_set_config(&panic_mode); + +	return 0; +} +EXPORT_SYMBOL(radeonfb_panic); +  +static struct notifier_block paniced = { +	.notifier_call = radeonfb_panic, +}; + +static int radeon_align_pitch(struct drm_device *dev, int width, int bpp) +{ +	struct drm_radeon_private *dev_priv = dev->dev_private; +	int aligned = width; +	int align_large = (radeon_is_avivo(dev_priv)); +	int pitch_mask = 0; + +	switch(bpp / 8) { +	case 1: pitch_mask = align_large ? 255 : 127; break; +	case 2: pitch_mask = align_large ? 127 : 31; break; +	case 3:  +	case 4: pitch_mask = align_large ? 63 : 15; break; +	} + +	aligned += pitch_mask; +	aligned &= ~pitch_mask; +	return aligned; +} + +int radeonfb_create(struct drm_device *dev, uint32_t fb_width, uint32_t fb_height,  +		   uint32_t surface_width, uint32_t surface_height, +		   struct radeon_framebuffer **radeon_fb_p) +{ +	struct fb_info *info; +	struct radeonfb_par *par; +	struct drm_framebuffer *fb; +	struct radeon_framebuffer *radeon_fb; +	struct drm_mode_fb_cmd mode_cmd; +	struct drm_gem_object *fbo = NULL; +	struct drm_radeon_gem_object *obj_priv; +	struct device *device = &dev->pdev->dev;  +	int size, aligned_size, ret; + +	mode_cmd.width = surface_width;/* crtc->desired_mode->hdisplay; */ +	mode_cmd.height = surface_height;/* crtc->desired_mode->vdisplay; */ +	 +	mode_cmd.bpp = 32; +	/* need to align pitch with crtc limits */ +	mode_cmd.pitch = radeon_align_pitch(dev, mode_cmd.width, mode_cmd.bpp) * ((mode_cmd.bpp + 1) / 8); +	mode_cmd.depth = 24; + +	size = mode_cmd.pitch * mode_cmd.height; +	aligned_size = ALIGN(size, PAGE_SIZE); + +	fbo = radeon_gem_object_alloc(dev, aligned_size, 1, RADEON_GEM_DOMAIN_VRAM); +	if (!fbo) { +		printk(KERN_ERR "failed to allocate framebuffer\n"); +		ret = -ENOMEM; +		goto out; +	} +	obj_priv = fbo->driver_private; + +	ret = radeon_gem_object_pin(fbo, PAGE_SIZE); +	if (ret) { +		DRM_ERROR("failed to pin fb: %d\n", ret); +		mutex_lock(&dev->struct_mutex); +		goto out_unref; +	} + +	mutex_lock(&dev->struct_mutex); +	fb = radeon_user_framebuffer_create(dev, NULL, &mode_cmd); +	if (!fb) { +		DRM_ERROR("failed to allocate fb.\n"); +		ret = -ENOMEM; +		goto out_unref; +	} + +	list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list); + +	radeon_fb = to_radeon_framebuffer(fb); +	*radeon_fb_p = radeon_fb; + +	radeon_fb->obj = fbo; + +	info = framebuffer_alloc(sizeof(struct radeonfb_par), device); +	if (!info) { +		ret = -ENOMEM; +		goto out_unref; +	} + +	par = info->par; + +	strcpy(info->fix.id, "radeondrmfb"); +	info->fix.type = FB_TYPE_PACKED_PIXELS; +	info->fix.visual = FB_VISUAL_TRUECOLOR; +	info->fix.type_aux = 0; +	info->fix.xpanstep = 1; /* doing it in hw */ +	info->fix.ypanstep = 1; /* doing it in hw */ +	info->fix.ywrapstep = 0; +	info->fix.accel = FB_ACCEL_I830; +	info->fix.type_aux = 0; + +	info->flags = FBINFO_DEFAULT; + +	info->fbops = &radeonfb_ops; + +	info->fix.line_length = fb->pitch; +	info->fix.smem_start = dev->mode_config.fb_base + obj_priv->bo->offset; +	info->fix.smem_len = size; + +	info->flags = FBINFO_DEFAULT; + +	ret = drm_bo_kmap(obj_priv->bo, 0, PAGE_ALIGN(size) >> PAGE_SHIFT, +			  &radeon_fb->kmap_obj); +	info->screen_base = radeon_fb->kmap_obj.virtual; +	if (!info->screen_base) { +		ret = -ENOSPC; +		goto out_unref; +	} +	info->screen_size = size; + +	memset(info->screen_base, 0, size); + +	info->pseudo_palette = fb->pseudo_palette; +	info->var.xres_virtual = fb->width; +	info->var.yres_virtual = fb->height; +	info->var.bits_per_pixel = fb->bits_per_pixel; +	info->var.xoffset = 0; +	info->var.yoffset = 0; +	info->var.activate = FB_ACTIVATE_NOW; +	info->var.height = -1; +	info->var.width = -1; + +	info->var.xres = fb_width; +	info->var.yres = fb_height; + +	info->fix.mmio_start = pci_resource_start(dev->pdev, 2); +	info->fix.mmio_len = pci_resource_len(dev->pdev, 2); + +	info->pixmap.size = 64*1024; +	info->pixmap.buf_align = 8; +	info->pixmap.access_align = 32; +	info->pixmap.flags = FB_PIXMAP_SYSTEM; +	info->pixmap.scan_align = 1; + +	DRM_DEBUG("fb depth is %d\n", fb->depth); +	DRM_DEBUG("   pitch is %d\n", fb->pitch); +	switch(fb->depth) { +	case 8: +		info->var.red.offset = 0; +		info->var.green.offset = 0; +		info->var.blue.offset = 0; +		info->var.red.length = 8; /* 8bit DAC */ +		info->var.green.length = 8; +		info->var.blue.length = 8; +		info->var.transp.offset = 0; +		info->var.transp.length = 0; +		break; + 	case 15: +		info->var.red.offset = 10; +		info->var.green.offset = 5; +		info->var.blue.offset = 0; +		info->var.red.length = 5; +		info->var.green.length = 5; +		info->var.blue.length = 5; +		info->var.transp.offset = 15; +		info->var.transp.length = 1; +		break; +	case 16: +		info->var.red.offset = 11; +		info->var.green.offset = 5; +		info->var.blue.offset = 0; +		info->var.red.length = 5; +		info->var.green.length = 6; +		info->var.blue.length = 5; +		info->var.transp.offset = 0; + 		break; +	case 24: +		info->var.red.offset = 16; +		info->var.green.offset = 8; +		info->var.blue.offset = 0; +		info->var.red.length = 8; +		info->var.green.length = 8; +		info->var.blue.length = 8; +		info->var.transp.offset = 0; +		info->var.transp.length = 0; +		break; +	case 32: +		info->var.red.offset = 16; +		info->var.green.offset = 8; +		info->var.blue.offset = 0; +		info->var.red.length = 8; +		info->var.green.length = 8; +		info->var.blue.length = 8; +		info->var.transp.offset = 24; +		info->var.transp.length = 8; +		break; +	default: +		break; +	} + +	fb->fbdev = info; + +	par->radeon_fb = radeon_fb; +	par->dev = dev; + +	/* To allow resizeing without swapping buffers */ +	printk("allocated %p %dx%d fb: 0x%08x, bo %p\n", dev, radeon_fb->base.width, +	       radeon_fb->base.height, obj_priv->bo->offset, fbo); + +	mutex_unlock(&dev->struct_mutex); +	return 0; + +out_unref: +	drm_gem_object_unreference(fbo); +	mutex_unlock(&dev->struct_mutex); +out: +	return ret; +} + +static int radeonfb_multi_fb_probe_crtc(struct drm_device *dev, struct drm_crtc *crtc) +{ +	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); +	struct radeon_framebuffer *radeon_fb; +	struct drm_framebuffer *fb; +	struct drm_connector *connector; +	struct fb_info *info; +	struct radeonfb_par *par; +	struct drm_mode_set *modeset; +	unsigned int width, height; +	int new_fb = 0; +	int ret, i, conn_count; + +	if (!drm_helper_crtc_in_use(crtc)) +		return 0; + +	if (!crtc->desired_mode) +		return 0; + +	width = crtc->desired_mode->hdisplay; +	height = crtc->desired_mode->vdisplay; + +	/* is there an fb bound to this crtc already */ +	if (!radeon_crtc->mode_set.fb) { +		ret = radeonfb_create(dev, width, height, width, height, &radeon_fb); +		if (ret) +			return -EINVAL; +		new_fb = 1; +	} else { +		fb = radeon_crtc->mode_set.fb; +		radeon_fb = to_radeon_framebuffer(fb); +		if ((radeon_fb->base.width < width) || (radeon_fb->base.height < height)) +			return -EINVAL; +	} +	 +	info = radeon_fb->base.fbdev; +	par = info->par; + +	modeset = &radeon_crtc->mode_set; +	modeset->fb = &radeon_fb->base; +	conn_count = 0; +	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { +		if (connector->encoder) +			if (connector->encoder->crtc == modeset->crtc) { +				modeset->connectors[conn_count] = connector; +				conn_count++; +				if (conn_count > RADEONFB_CONN_LIMIT) +					BUG(); +			} +	} +	 +	for (i = conn_count; i < RADEONFB_CONN_LIMIT; i++) +		modeset->connectors[i] = NULL; + +	par->crtc_ids[0] = crtc->base.id; + +	modeset->num_connectors = conn_count; +	if (modeset->mode != modeset->crtc->desired_mode) +		modeset->mode = modeset->crtc->desired_mode; + +	par->crtc_count = 1; + +	if (new_fb) { +		info->var.pixclock = -1; +		if (register_framebuffer(info) < 0) +			return -EINVAL; +	} else +		radeonfb_set_par(info); + +	printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, +	       info->fix.id); + +	/* Switch back to kernel console on panic */ +	panic_mode = *modeset; +	atomic_notifier_chain_register(&panic_notifier_list, &paniced); +	printk(KERN_INFO "registered panic notifier\n"); + +	return 0; +} + +static int radeonfb_multi_fb_probe(struct drm_device *dev) +{ + +	struct drm_crtc *crtc; +	int ret = 0; + +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		ret = radeonfb_multi_fb_probe_crtc(dev, crtc); +		if (ret) +			return ret; +	} +	return ret; +} + +static int radeonfb_single_fb_probe(struct drm_device *dev) +{ +	struct drm_crtc *crtc; +	struct drm_connector *connector; +	unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1; +	unsigned int surface_width = 0, surface_height = 0; +	int new_fb = 0; +	int crtc_count = 0; +	int ret, i, conn_count = 0; +	struct radeon_framebuffer *radeon_fb; +	struct fb_info *info; +	struct radeonfb_par *par; +	struct drm_mode_set *modeset = NULL; + +	DRM_DEBUG("\n"); +	/* first up get a count of crtcs now in use and new min/maxes width/heights */ +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		if (drm_helper_crtc_in_use(crtc)) { +			if (crtc->desired_mode) { +				if (crtc->desired_mode->hdisplay < fb_width) +					fb_width = crtc->desired_mode->hdisplay; +				 +				if (crtc->desired_mode->vdisplay < fb_height) +					fb_height = crtc->desired_mode->vdisplay; +				 +				if (crtc->desired_mode->hdisplay > surface_width) +					surface_width = crtc->desired_mode->hdisplay; +				 +				if (crtc->desired_mode->vdisplay > surface_height) +					surface_height = crtc->desired_mode->vdisplay; + +			} +		crtc_count++; +		} +	} + +	if (crtc_count == 0 || fb_width == -1 || fb_height == -1) { +		/* hmm everyone went away - assume VGA cable just fell out +		   and will come back later. */ +		return 0; +	} + +	/* do we have an fb already? */ +	if (list_empty(&dev->mode_config.fb_kernel_list)) { +		/* create an fb if we don't have one */ +		ret = radeonfb_create(dev, fb_width, fb_height, surface_width, surface_height, &radeon_fb); +		if (ret) +			return -EINVAL; +		new_fb = 1; +	} else { +		struct drm_framebuffer *fb; +		fb = list_first_entry(&dev->mode_config.fb_kernel_list, struct drm_framebuffer, filp_head); +		radeon_fb = to_radeon_framebuffer(fb); + +		/* if someone hotplugs something bigger than we have already allocated, we are pwned. +		   As really we can't resize an fbdev that is in the wild currently due to fbdev +		   not really being designed for the lower layers moving stuff around under it. +		   - so in the grand style of things - punt. */ +		if ((fb->width < surface_width) || (fb->height < surface_height)) { +			DRM_ERROR("Framebuffer not large enough to scale console onto.\n"); +			return -EINVAL; +		} +	} + +	info = radeon_fb->base.fbdev; +	par = info->par; + +	crtc_count = 0; +	/* okay we need to setup new connector sets in the crtcs */ +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); +		modeset = &radeon_crtc->mode_set; +		modeset->fb = &radeon_fb->base; +		conn_count = 0; +		list_for_each_entry(connector, &dev->mode_config.connector_list, head) { +			if (connector->encoder) +				if(connector->encoder->crtc == modeset->crtc) { +					modeset->connectors[conn_count] = connector; +					conn_count++; +					if (conn_count > RADEONFB_CONN_LIMIT) +						BUG(); +				} +		} + +		for (i = conn_count; i < RADEONFB_CONN_LIMIT; i++) +			modeset->connectors[i] = NULL; + +		 +		par->crtc_ids[crtc_count++] = crtc->base.id; + +		modeset->num_connectors = conn_count; +		if (modeset->mode != modeset->crtc->desired_mode) +			modeset->mode = modeset->crtc->desired_mode; +	} +	par->crtc_count = crtc_count; + +	if (new_fb) { +		info->var.pixclock = -1; +		if (register_framebuffer(info) < 0) +			return -EINVAL; +	} else +		radeonfb_set_par(info); +		 +	printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, +	       info->fix.id); + +	/* Switch back to kernel console on panic */ +	panic_mode = *modeset; +	atomic_notifier_chain_register(&panic_notifier_list, &paniced); +	printk(KERN_INFO "registered panic notifier\n"); + +	return 0; +} + +int radeonfb_probe(struct drm_device *dev) +{ +	int ret; + +	DRM_DEBUG("\n"); + +	/* something has changed in the lower levels of hell - deal with it  +	   here */ + +	/* two modes : a) 1 fb to rule all crtcs. +	               b) one fb per crtc. +	   two actions 1) new connected device + 	               2) device removed. +	   case a/1 : if the fb surface isn't big enough - resize the surface fb. +	              if the fb size isn't big enough - resize fb into surface. +		      if everything big enough configure the new crtc/etc. +	   case a/2 : undo the configuration +	              possibly resize down the fb to fit the new configuration. +           case b/1 : see if it is on a new crtc - setup a new fb and add it. +	   case b/2 : teardown the new fb. +	*/ + +	/* mode a first */ +	/* search for an fb */ +	//	if (radeon_fbpercrtc == 1) { +	//		ret = radeonfb_multi_fb_probe(dev); +	//	} else { +	ret = radeonfb_single_fb_probe(dev); +		//	} + +	return ret; +} +EXPORT_SYMBOL(radeonfb_probe); + +int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb) +{ +	struct fb_info *info; +	struct radeon_framebuffer *radeon_fb = to_radeon_framebuffer(fb); + +	if (!fb) +		return -EINVAL; + +	info = fb->fbdev; +	 +	if (info) { +		unregister_framebuffer(info); +		drm_bo_kunmap(&radeon_fb->kmap_obj); +		mutex_lock(&dev->struct_mutex); +		drm_gem_object_unreference(radeon_fb->obj); +		mutex_unlock(&dev->struct_mutex); +		framebuffer_release(info); +	} + +	atomic_notifier_chain_unregister(&panic_notifier_list, &paniced); +	memset(&panic_mode, 0, sizeof(struct drm_mode_set)); +	return 0; +} +EXPORT_SYMBOL(radeonfb_remove); +MODULE_LICENSE("GPL"); diff --git a/linux-core/radeon_fence.c b/linux-core/radeon_fence.c new file mode 100644 index 00000000..1b327369 --- /dev/null +++ b/linux-core/radeon_fence.c @@ -0,0 +1,96 @@ +/************************************************************************** + *  + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA + * 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 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 + * THE COPYRIGHT HOLDERS, AUTHORS 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. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + *  + *  + **************************************************************************/ +/* + * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#include "drmP.h" +#include "drm.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + +int radeon_fence_emit_sequence(struct drm_device *dev, uint32_t class, +			       uint32_t flags, uint32_t *sequence, +			       uint32_t *native_type) +{ +	struct drm_radeon_private *dev_priv = (struct drm_radeon_private *) dev->dev_private; +	RING_LOCALS; + +	if (!dev_priv) +		return -EINVAL; + +	radeon_emit_irq(dev); +	*sequence = (uint32_t) dev_priv->counter; +	*native_type = DRM_FENCE_TYPE_EXE; + +	return 0; +} + +static void radeon_fence_poll(struct drm_device *dev, uint32_t fence_class, +			      uint32_t waiting_types) +{ +	struct drm_radeon_private *dev_priv = (struct drm_radeon_private *) dev->dev_private; +	uint32_t sequence; +        if (waiting_types & DRM_FENCE_TYPE_EXE) { + +                sequence = READ_BREADCRUMB(dev_priv); + +                drm_fence_handler(dev, 0, sequence, +                                  DRM_FENCE_TYPE_EXE, 0); +        } +} + +void radeon_fence_handler(struct drm_device * dev) +{ +	struct drm_fence_manager *fm = &dev->fm; +	struct drm_fence_class_manager *fc = &fm->fence_class[0]; + +	write_lock(&fm->lock); +	radeon_fence_poll(dev, 0, fc->waiting_types); +	write_unlock(&fm->lock); +} + +int radeon_fence_has_irq(struct drm_device *dev, uint32_t class, uint32_t flags) +{ +	/* +	 * We have an irq that tells us when we have a new breadcrumb. +	 */ +	return 1; +} + + +struct drm_fence_driver radeon_fence_driver = { +	.num_classes = 1, +	.wrap_diff = (1U << (BREADCRUMB_BITS -1)), +	.flush_diff = (1U << (BREADCRUMB_BITS - 2)), +	.sequence_mask = BREADCRUMB_MASK, +	.emit = radeon_fence_emit_sequence, +	.has_irq = radeon_fence_has_irq, +	.poll = radeon_fence_poll, +}; + diff --git a/linux-core/radeon_gem.c b/linux-core/radeon_gem.c new file mode 100644 index 00000000..d536aed2 --- /dev/null +++ b/linux-core/radeon_gem.c @@ -0,0 +1,687 @@ +/* + * Copyright 2008 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: Dave Airlie + */ +#include "drmP.h" +#include "drm.h" + +#include "radeon_drm.h" +#include "radeon_drv.h" + +int radeon_gem_init_object(struct drm_gem_object *obj) +{ +	struct drm_radeon_gem_object *obj_priv; + +	obj_priv = drm_calloc(1, sizeof(*obj_priv), DRM_MEM_DRIVER); +	if (!obj_priv) { +		return -ENOMEM; +	} + +	obj->driver_private = obj_priv; +	obj_priv->obj = obj; +	 +	return 0; +} + +void radeon_gem_free_object(struct drm_gem_object *obj) +{ + +	struct drm_radeon_gem_object *obj_priv = obj->driver_private; + +	/* tear down the buffer object - gem holds struct mutex */ +	drm_bo_takedown_vm_locked(obj_priv->bo); +	drm_bo_usage_deref_locked(&obj_priv->bo); +	drm_free(obj->driver_private, 1, DRM_MEM_DRIVER); +} + +int radeon_gem_info_ioctl(struct drm_device *dev, void *data, +			  struct drm_file *file_priv) +{ +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct drm_radeon_gem_info *args = data; + +	args->vram_start = dev_priv->mm.vram_offset; +	args->vram_size = dev_priv->mm.vram_size; +	args->vram_visible = dev_priv->mm.vram_visible; + +	args->gart_start = dev_priv->mm.gart_start; +	args->gart_size = dev_priv->mm.gart_size; + +	return 0; +} + +struct drm_gem_object *radeon_gem_object_alloc(struct drm_device *dev, int size, int alignment, +					       int initial_domain) +{ +	struct drm_gem_object *obj; +	struct drm_radeon_gem_object *obj_priv; +	int ret; +	uint32_t flags; + +	DRM_DEBUG("size 0x%x, alignment %d, initial_domain %d\n", size, alignment, initial_domain); +	obj = drm_gem_object_alloc(dev, size); +	if (!obj) +		return NULL;; + +	obj_priv = obj->driver_private; +	if (initial_domain == RADEON_GEM_DOMAIN_VRAM) +		flags = DRM_BO_FLAG_MEM_VRAM | DRM_BO_FLAG_MAPPABLE; +	else +		flags = DRM_BO_FLAG_MEM_TT | DRM_BO_FLAG_MAPPABLE; + +	flags |= DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE | DRM_BO_FLAG_EXE; +	/* create a TTM BO */ +	ret = drm_buffer_object_create(dev, +				       size, drm_bo_type_device, +				       flags, 0, alignment, +				       0, &obj_priv->bo); +	if (ret) +		goto fail; + +	return obj; +fail: + +	return NULL; +} + +int radeon_gem_create_ioctl(struct drm_device *dev, void *data, +			    struct drm_file *file_priv) +{ +	struct drm_radeon_gem_create *args = data; +	struct drm_radeon_gem_object *obj_priv; +	struct drm_gem_object *obj; +	int ret = 0; +	uint32_t flags; +	int handle; + +	/* create a gem object to contain this object in */ +	args->size = roundup(args->size, PAGE_SIZE); + +	obj = radeon_gem_object_alloc(dev, args->size, args->alignment, args->initial_domain); +	if (!obj) +		return -EINVAL; + +	obj_priv = obj->driver_private; +	DRM_DEBUG("obj is %p bo is %p, %d\n", obj, obj_priv->bo, obj_priv->bo->num_pages); +	ret = drm_gem_handle_create(file_priv, obj, &handle); +	mutex_lock(&dev->struct_mutex); +	drm_gem_object_handle_unreference(obj); +	mutex_unlock(&dev->struct_mutex); + +	if (ret) +		goto fail; + +	args->handle = handle; + +	return 0; +fail: +	drm_gem_object_unreference(obj); + +	return ret; +} + +int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data, +				struct drm_file *file_priv) +{ +	/* transition the BO to a domain - just validate the BO into a certain domain */ +	struct drm_radeon_gem_set_domain *args = data; +	struct drm_gem_object *obj; +	struct drm_radeon_gem_object *obj_priv; +	int ret; +	/* for now if someone requests domain CPU - just make sure the buffer is finished with */ + +	/* just do a BO wait for now */ +	obj = drm_gem_object_lookup(dev, file_priv, args->handle); +	if (obj == NULL) +		return -EINVAL; + +	obj_priv = obj->driver_private; + +	mutex_lock(&obj_priv->bo->mutex); +	ret = drm_bo_wait(obj_priv->bo, 0, 1, 0, 0); +	mutex_unlock(&obj_priv->bo->mutex); + +	mutex_lock(&dev->struct_mutex); +	drm_gem_object_unreference(obj); +	mutex_unlock(&dev->struct_mutex); +	return ret; +} + +int radeon_gem_pread_ioctl(struct drm_device *dev, void *data, +			  struct drm_file *file_priv) +{ +	return -ENOSYS; +} + +int radeon_gem_pwrite_ioctl(struct drm_device *dev, void *data, +			  struct drm_file *file_priv) +{ +	return -ENOSYS; +} + +int radeon_gem_mmap_ioctl(struct drm_device *dev, void *data, +			  struct drm_file *file_priv) +{ +	struct drm_radeon_gem_mmap *args = data; +	struct drm_gem_object *obj; +	struct drm_radeon_gem_object *obj_priv; +	loff_t offset; +	unsigned long addr; + +	obj = drm_gem_object_lookup(dev, file_priv, args->handle); +	if (obj == NULL) +		return -EINVAL; + +	offset = args->offset; + +	DRM_DEBUG("got here %p\n", obj); +	obj_priv = obj->driver_private; + +	DRM_DEBUG("got here %p %p %lld %ld\n", obj, obj_priv->bo, args->size, obj_priv->bo->num_pages); +	if (!obj_priv->bo) { +		mutex_lock(&dev->struct_mutex); +		drm_gem_object_unreference(obj); +		mutex_unlock(&dev->struct_mutex); +		return -EINVAL; +	} + +	down_write(¤t->mm->mmap_sem); +	addr = do_mmap_pgoff(file_priv->filp, 0, args->size, +			     PROT_READ | PROT_WRITE, MAP_SHARED, +			     obj_priv->bo->map_list.hash.key); +	up_write(¤t->mm->mmap_sem); + +	DRM_DEBUG("got here %p\n", obj); +	mutex_lock(&dev->struct_mutex); +	drm_gem_object_unreference(obj); +	mutex_unlock(&dev->struct_mutex); +	if (IS_ERR((void *)addr)) +		return addr; + +	args->addr_ptr = (uint64_t) addr; + +	return 0; +	 +} + +int radeon_gem_pin_ioctl(struct drm_device *dev, void *data, +			 struct drm_file *file_priv) +{ +	struct drm_radeon_gem_pin *args = data; +	struct drm_gem_object *obj; +	struct drm_radeon_gem_object *obj_priv; +	int ret; + +	obj = drm_gem_object_lookup(dev, file_priv, args->handle); +	if (obj == NULL) +		return -EINVAL; + +	obj_priv = obj->driver_private; + +	DRM_DEBUG("got here %p %p %d\n", obj, obj_priv->bo, atomic_read(&obj_priv->bo->usage)); +	/* validate into a pin with no fence */ + +	ret = drm_bo_do_validate(obj_priv->bo, 0, DRM_BO_FLAG_NO_EVICT, +				 DRM_BO_HINT_DONT_FENCE, +				 0, NULL); + +	args->offset = obj_priv->bo->offset; +	DRM_DEBUG("got here %p %p\n", obj, obj_priv->bo); + +	mutex_lock(&dev->struct_mutex); +	drm_gem_object_unreference(obj); +	mutex_unlock(&dev->struct_mutex); +	return ret; +} + +int radeon_gem_unpin_ioctl(struct drm_device *dev, void *data, +			   struct drm_file *file_priv) +{ +	struct drm_radeon_gem_unpin *args = data; +	struct drm_gem_object *obj; +	struct drm_radeon_gem_object *obj_priv; +	int ret; + +	obj = drm_gem_object_lookup(dev, file_priv, args->handle); +	if (obj == NULL) +		return -EINVAL; + +	obj_priv = obj->driver_private; + +	/* validate into a pin with no fence */ + +	ret = drm_bo_do_validate(obj_priv->bo, DRM_BO_FLAG_NO_EVICT, DRM_BO_FLAG_NO_EVICT, +				 DRM_BO_HINT_DONT_FENCE, +				 0, NULL); + +	mutex_lock(&dev->struct_mutex); +	drm_gem_object_unreference(obj); +	mutex_unlock(&dev->struct_mutex); +	return ret; +} + +int radeon_gem_busy(struct drm_device *dev, void *data, +		     struct drm_file *file_priv) +{ +	return 0; +} + +int radeon_gem_execbuffer(struct drm_device *dev, void *data, +			  struct drm_file *file_priv) +{ +	return -ENOSYS; + + +} + +int radeon_gem_indirect_ioctl(struct drm_device *dev, void *data, +			      struct drm_file *file_priv) +{ +	struct drm_radeon_gem_indirect *args = data; +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct drm_gem_object *obj; +	struct drm_radeon_gem_object *obj_priv; +	uint32_t start, end; +	int ret; +	RING_LOCALS; + +	obj = drm_gem_object_lookup(dev, file_priv, args->handle); +	if (obj == NULL) +		return -EINVAL; + +	obj_priv = obj->driver_private; + +	DRM_DEBUG("got here %p %d\n", obj, args->used); +	//RING_SPACE_TEST_WITH_RETURN(dev_priv); +	//VB_AGE_TEST_WITH_RETURN(dev_priv); + +	ret = drm_bo_do_validate(obj_priv->bo, 0, DRM_BO_FLAG_NO_EVICT, +				 0 , 0, NULL); +	if (ret) +		return ret; + +	/* Wait for the 3D stream to idle before the indirect buffer +	 * containing 2D acceleration commands is processed. +	 */ +	BEGIN_RING(2); + +	RADEON_WAIT_UNTIL_3D_IDLE(); + +	ADVANCE_RING(); +	 +	start = 0; +	end = args->used; + +	if (start != end) { +		int offset = (dev_priv->gart_vm_start +  +			      + obj_priv->bo->offset + start); +		int dwords = (end - start + 3) / sizeof(u32); + +#if 0 +		/* Indirect buffer data must be an even number of +		 * dwords, so if we've been given an odd number we must +		 * pad the data with a Type-2 CP packet. +		 */ +		if (dwords & 1) { +			u32 *data = (u32 *) +			    ((char *)dev->agp_buffer_map->handle +			     + buf->offset + start); +			data[dwords++] = RADEON_CP_PACKET2; +		} +#endif +		/* Fire off the indirect buffer */ +		BEGIN_RING(3); + +		OUT_RING(CP_PACKET0(RADEON_CP_IB_BASE, 1)); +		OUT_RING(offset); +		OUT_RING(dwords); + +		ADVANCE_RING(); +	} + +	COMMIT_RING(); + +	/* we need to fence the buffer */ +	ret = drm_fence_buffer_objects(dev, NULL, 0, NULL, &obj_priv->fence); +	if (ret) { +	   +		drm_putback_buffer_objects(dev); +		ret = 0; +		goto fail; +	} + +	/* dereference he fence object */ +	drm_fence_usage_deref_unlocked(&obj_priv->fence); + +	mutex_lock(&dev->struct_mutex); +	drm_gem_object_unreference(obj); +	mutex_unlock(&dev->struct_mutex); +	ret = 0; + fail: +	return ret; +} + +/* + * Depending on card genertation, chipset bugs, etc... the amount of vram + * accessible to the CPU can vary. This function is our best shot at figuring + * it out. Returns a value in KB. + */ +static uint32_t radeon_get_accessible_vram(struct drm_device *dev) +{ +	drm_radeon_private_t *dev_priv = dev->dev_private; +	uint32_t aper_size; +	u8 byte; + +	if (dev_priv->chip_family >= CHIP_R600) +		aper_size = RADEON_READ(R600_CONFIG_APER_SIZE) / 1024; +	else +		aper_size = RADEON_READ(RADEON_CONFIG_APER_SIZE) / 1024; + +	/* Set HDP_APER_CNTL only on cards that are known not to be broken, +	 * that is has the 2nd generation multifunction PCI interface +	 */ +	if (dev_priv->chip_family == CHIP_RV280 || +	    dev_priv->chip_family == CHIP_RV350 || +	    dev_priv->chip_family == CHIP_RV380 || +	    dev_priv->chip_family == CHIP_R420 || +	    dev_priv->chip_family == CHIP_RV410 || +	    dev_priv->chip_family >= CHIP_RS600) { +		uint32_t temp = RADEON_READ(RADEON_HOST_PATH_CNTL); +		temp |= RADEON_HDP_APER_CNTL; +		RADEON_WRITE(RADEON_HOST_PATH_CNTL, temp); +		return aper_size * 2; +	} +	 +	/* Older cards have all sorts of funny issues to deal with. First +	 * check if it's a multifunction card by reading the PCI config +	 * header type... Limit those to one aperture size +	 */ +	pci_read_config_byte(dev->pdev, 0xe, &byte); +	if (byte & 0x80) +		return aper_size; +	 +	/* Single function older card. We read HDP_APER_CNTL to see how the BIOS +	 * have set it up. We don't write this as it's broken on some ASICs but +	 * we expect the BIOS to have done the right thing (might be too optimistic...) +	 */ +	if (RADEON_READ(RADEON_HOST_PATH_CNTL) & RADEON_HDP_APER_CNTL) +		return aper_size * 2; + +	return aper_size; +}	 + +/* code from the DDX - do memory sizing */ +void radeon_vram_setup(struct drm_device *dev) +{ +	drm_radeon_private_t *dev_priv = dev->dev_private; +	uint32_t vram; +	uint32_t accessible,  bar_size; + +	if ((dev_priv->chip_family <= CHIP_RV515) && (dev_priv->flags & RADEON_IS_IGP)) { +		uint32_t tom = RADEON_READ(RADEON_NB_TOM); + +		vram = (((tom >> 16) - (tom & 0xffff) + 1) << 6); +		RADEON_WRITE(RADEON_CONFIG_MEMSIZE, vram * 1024); +	} else { +		if (dev_priv->chip_family >= CHIP_R600) +			vram = RADEON_READ(R600_CONFIG_MEMSIZE) / 1024; +		else { +			vram = RADEON_READ(RADEON_CONFIG_MEMSIZE) / 1024; + +			/* Some production boards of m6 will return 0 if it's 8 MB */ +			if (vram == 0) { +				vram = 8192; +				RADEON_WRITE(RADEON_CONFIG_MEMSIZE, 0x800000); +			} +		} +	} + +	accessible = radeon_get_accessible_vram(dev); + +	bar_size = drm_get_resource_len(dev, 0) / 1024; +	if (bar_size == 0) +		bar_size = 0x20000; +	if (accessible > bar_size) +		accessible = bar_size; + +	DRM_INFO("Detected VRAM RAM=%dK, accessible=%uK, BAR=%uK\n", +		 vram, accessible, bar_size); + +	dev_priv->mm.vram_offset = dev_priv->fb_aper_offset; +	dev_priv->mm.vram_size = vram * 1024; +	dev_priv->mm.vram_visible = accessible * 1024; + + +} + +static int radeon_gart_init(struct drm_device *dev) +{ +	drm_radeon_private_t *dev_priv = dev->dev_private; +	int ret; +	u32 base = 0; + +	/* setup a 32MB GART */ +	dev_priv->gart_size = dev_priv->mm.gart_size; + +#if __OS_HAS_AGP +	/* setup VRAM vs GART here */ +	if (dev_priv->flags & RADEON_IS_AGP) { +		base = dev->agp->base; +		if ((base + dev_priv->gart_size - 1) >= dev_priv->fb_location && +		    base < (dev_priv->fb_location + dev_priv->fb_size - 1)) { +			DRM_INFO("Can't use agp base @0x%08xlx, won't fit\n", +				 dev->agp->base); +			base = 0; +		} +	} +#endif + +	if (base == 0) { +		base = dev_priv->fb_location + dev_priv->fb_size; +		if (base < dev_priv->fb_location || +		    ((base + dev_priv->gart_size) & 0xfffffffful) < base) +			base = dev_priv->fb_location +				- dev_priv->gart_size; +	} +	/* start on the card */ +	dev_priv->gart_vm_start = base & 0xffc00000u; +	if (dev_priv->gart_vm_start != base) +		DRM_INFO("GART aligned down from 0x%08x to 0x%08x\n", +			 base, dev_priv->gart_vm_start); + +	/* if on PCIE we need to allocate an fb object for the PCIE GART table */ +	if (dev_priv->flags & RADEON_IS_PCIE) { +		ret = drm_buffer_object_create(dev, RADEON_PCIGART_TABLE_SIZE, +					       drm_bo_type_kernel, +					       DRM_BO_FLAG_READ | DRM_BO_FLAG_MEM_VRAM | DRM_BO_FLAG_MAPPABLE | DRM_BO_FLAG_NO_EVICT, +					       0, 1, 0, &dev_priv->mm.pcie_table); +		if (ret) +			return -EINVAL; + +		DRM_DEBUG("pcie table bo created %p, %x\n", dev_priv->mm.pcie_table, dev_priv->mm.pcie_table->offset); +		ret = drm_bo_kmap(dev_priv->mm.pcie_table, 0, RADEON_PCIGART_TABLE_SIZE >> PAGE_SHIFT, +				  &dev_priv->mm.pcie_table_map); +		if (ret) +			return -EINVAL; + +		dev_priv->pcigart_offset_set = 2; +		dev_priv->gart_info.bus_addr =  dev_priv->fb_location + dev_priv->mm.pcie_table->offset; +		dev_priv->gart_info.addr = dev_priv->mm.pcie_table_map.virtual; +		dev_priv->gart_info.gart_reg_if = DRM_ATI_GART_PCIE; +		dev_priv->gart_info.gart_table_location = DRM_ATI_GART_FB; +		memset(dev_priv->gart_info.addr, 0, RADEON_PCIGART_TABLE_SIZE); +	} else if (!(dev_priv->flags & RADEON_IS_AGP)) { +		/* allocate PCI GART table */ +		dev_priv->gart_info.table_mask = DMA_BIT_MASK(32); +		ret = drm_ati_alloc_pcigart_table(dev, &dev_priv->gart_info); +		if (ret) { +			DRM_ERROR("cannot allocate PCI GART page!\n"); +			return -EINVAL; +		} + +		dev_priv->gart_info.gart_table_location = DRM_ATI_GART_MAIN; +		if (dev_priv->flags & RADEON_IS_IGPGART) +			dev_priv->gart_info.gart_reg_if = DRM_ATI_GART_IGP; +		else +			dev_priv->gart_info.gart_reg_if = DRM_ATI_GART_PCI; +		dev_priv->gart_info.addr = NULL; +		dev_priv->gart_info.bus_addr = 0; +	} +	 +	/* gart values setup - start the GART */ +	if (dev_priv->flags & RADEON_IS_AGP) { +		radeon_set_pcigart(dev_priv, 0); +	} else { +		radeon_set_pcigart(dev_priv, 1); +	} +		 +	return 0; +} + +int radeon_alloc_gart_objects(struct drm_device *dev) +{ +	drm_radeon_private_t *dev_priv = dev->dev_private; +	int ret; + +	ret = drm_buffer_object_create(dev, RADEON_DEFAULT_RING_SIZE, +				       drm_bo_type_kernel, +				       DRM_BO_FLAG_READ | DRM_BO_FLAG_MEM_TT | +				       DRM_BO_FLAG_MAPPABLE | DRM_BO_FLAG_NO_EVICT, +				       0, 1, 0, &dev_priv->mm.ring); +	if (ret) { +		DRM_ERROR("failed to allocate ring\n"); +		return -EINVAL; +	} + +	ret = drm_bo_kmap(dev_priv->mm.ring, 0, RADEON_DEFAULT_RING_SIZE >> PAGE_SHIFT, +			  &dev_priv->mm.ring_map); +	if (ret) { +		DRM_ERROR("failed to map ring\n"); +		return -EINVAL; +	} + +	ret = drm_buffer_object_create(dev, PAGE_SIZE, +				       drm_bo_type_kernel, +				       DRM_BO_FLAG_WRITE |DRM_BO_FLAG_READ | DRM_BO_FLAG_MEM_TT | +				       DRM_BO_FLAG_MAPPABLE | DRM_BO_FLAG_NO_EVICT, +				       0, 1, 0, &dev_priv->mm.ring_read_ptr); +	if (ret) { +		DRM_ERROR("failed to allocate ring read\n"); +		return -EINVAL; +	} + +	ret = drm_bo_kmap(dev_priv->mm.ring_read_ptr, 0, +			  PAGE_SIZE >> PAGE_SHIFT, +			  &dev_priv->mm.ring_read_ptr_map); +	if (ret) { +		DRM_ERROR("failed to map ring read\n"); +		return -EINVAL; +	} + +	DRM_DEBUG("Ring ptr %p mapped at %d %p, read ptr %p maped at %d %p\n", +		  dev_priv->mm.ring, dev_priv->mm.ring->offset, dev_priv->mm.ring_map.virtual, +		  dev_priv->mm.ring_read_ptr, dev_priv->mm.ring_read_ptr->offset, dev_priv->mm.ring_read_ptr_map.virtual); + +	return 0;			   + +} + +/* init memory manager - start with all of VRAM and a 32MB GART aperture for now */ +int radeon_gem_mm_init(struct drm_device *dev) +{ +	drm_radeon_private_t *dev_priv = dev->dev_private; +	int ret; + +	/* size the mappable VRAM memory for now */ +	radeon_vram_setup(dev); +	 +	drm_bo_init_mm(dev, DRM_BO_MEM_VRAM, 0, /*dev_priv->mm.vram_offset >> PAGE_SHIFT,*/ +		       (dev_priv->mm.vram_visible) >> PAGE_SHIFT, +		       0); + + +	dev_priv->mm.gart_size = (32 * 1024 * 1024); +	dev_priv->mm.gart_start = 0; +	ret = radeon_gart_init(dev); +	if (ret) +		return -EINVAL; +	 +	drm_bo_init_mm(dev, DRM_BO_MEM_TT, 0, +		       dev_priv->mm.gart_size >> PAGE_SHIFT, +		       0); + +	/* need to allocate some objects in the GART */ +	/* ring + ring read ptr */ +	ret = radeon_alloc_gart_objects(dev); +	if (ret) +		return -EINVAL; +	return 0; +} + +void radeon_gem_mm_fini(struct drm_device *dev) +{ +	drm_radeon_private_t *dev_priv = dev->dev_private; + +	mutex_lock(&dev->struct_mutex); + +	if (dev_priv->mm.ring_read_ptr) { +		drm_bo_kunmap(&dev_priv->mm.ring_read_ptr_map); +		drm_bo_usage_deref_locked(&dev_priv->mm.ring_read_ptr); +	} + +	if (dev_priv->mm.ring) { +		drm_bo_kunmap(&dev_priv->mm.ring_map); +		drm_bo_usage_deref_locked(&dev_priv->mm.ring); +	} + +	if (drm_bo_clean_mm(dev, DRM_BO_MEM_TT, 1)) { +		DRM_DEBUG("delaying takedown of TTM memory\n"); +	} + +	if (dev_priv->flags & RADEON_IS_PCIE) { +		if (dev_priv->mm.pcie_table) { +			drm_bo_kunmap(&dev_priv->mm.pcie_table_map); +			drm_bo_usage_deref_locked(&dev_priv->mm.pcie_table); +		} +	} + +	if (drm_bo_clean_mm(dev, DRM_BO_MEM_VRAM, 1)) { +		DRM_DEBUG("delaying takedown of TTM memory\n"); +	} + +	mutex_unlock(&dev->struct_mutex); +} + +int radeon_gem_object_pin(struct drm_gem_object *obj, +			  uint32_t alignment) +{ +	struct drm_radeon_gem_object *obj_priv; +	int ret; + +	obj_priv = obj->driver_private; + +	ret = drm_bo_do_validate(obj_priv->bo, 0, DRM_BO_FLAG_NO_EVICT, +				 DRM_BO_HINT_DONT_FENCE, 0, NULL); + +	return ret; +} + diff --git a/linux-core/radeon_i2c.c b/linux-core/radeon_i2c.c new file mode 100644 index 00000000..eccec650 --- /dev/null +++ b/linux-core/radeon_i2c.c @@ -0,0 +1,131 @@ +/* + * Copyright 2007-8 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: Dave Airlie + *          Alex Deucher + */ +#include "drmP.h" +#include "radeon_drm.h" +#include "radeon_drv.h" + + +static int get_clock(void *i2c_priv) +{ +	struct radeon_i2c_chan *i2c = i2c_priv; +	struct drm_radeon_private *dev_priv = i2c->dev->dev_private; +	struct radeon_i2c_bus_rec *rec = &i2c->rec; +	uint32_t val; + +	val = RADEON_READ(rec->get_clk_reg); +	val &= rec->get_clk_mask; + +	return (val != 0); +} + + +static int get_data(void *i2c_priv) +{ +	struct radeon_i2c_chan *i2c = i2c_priv; +	struct drm_radeon_private *dev_priv = i2c->dev->dev_private; +	struct radeon_i2c_bus_rec *rec = &i2c->rec; +	uint32_t val; + +	val = RADEON_READ(rec->get_data_reg); +	val &= rec->get_data_mask; +	return (val != 0); +} + +static void set_clock(void *i2c_priv, int clock) +{ +	struct radeon_i2c_chan *i2c = i2c_priv; +	struct drm_radeon_private *dev_priv = i2c->dev->dev_private; +	struct radeon_i2c_bus_rec *rec = &i2c->rec; +	uint32_t val; + +	val = RADEON_READ(rec->put_clk_reg) & (uint32_t)~(rec->put_clk_mask); +	val |= clock ? 0 : rec->put_clk_mask; +	RADEON_WRITE(rec->put_clk_reg, val); +} + +static void set_data(void *i2c_priv, int data) +{ +	struct radeon_i2c_chan *i2c = i2c_priv; +	struct drm_radeon_private *dev_priv = i2c->dev->dev_private; +	struct radeon_i2c_bus_rec *rec = &i2c->rec; +	uint32_t val; + +	val = RADEON_READ(rec->put_data_reg) & (uint32_t)~(rec->put_data_mask); +	val |= data ? 0 : rec->put_data_mask; +	RADEON_WRITE(rec->put_data_reg, val); +} + +struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev, +					  struct radeon_i2c_bus_rec *rec, +					  const char *name) +{ +	struct radeon_i2c_chan *i2c; +	int ret; + +	i2c = drm_calloc(1, sizeof(struct radeon_i2c_chan), DRM_MEM_DRIVER); +	if (i2c == NULL) +		return NULL; +	 +	i2c->adapter.owner = THIS_MODULE; +	i2c->adapter.id = I2C_HW_B_RADEON; +	i2c->adapter.algo_data = &i2c->algo; +	i2c->dev = dev; +	i2c->algo.setsda = set_data; +	i2c->algo.setscl = set_clock; +	i2c->algo.getsda = get_data; +	i2c->algo.getscl = get_clock; +	i2c->algo.udelay = 20; +	i2c->algo.timeout = usecs_to_jiffies(2200); +	i2c->algo.data = i2c; +	i2c->rec = *rec; +	i2c_set_adapdata(&i2c->adapter, i2c); + +	ret = i2c_bit_add_bus(&i2c->adapter); +	if (ret) { +		DRM_INFO("Failed to register i2c %s\n", name); +		goto out_free; +	} + +	return i2c; +out_free: +	drm_free(i2c, sizeof(struct radeon_i2c_chan), DRM_MEM_DRIVER); +	return NULL; +	 +} + +void radeon_i2c_destroy(struct radeon_i2c_chan *i2c) +{ +	if (!i2c) +		return; + +	i2c_del_adapter(&i2c->adapter); +	drm_free(i2c, sizeof(struct radeon_i2c_chan), DRM_MEM_DRIVER); +} + +struct drm_encoder *radeon_best_encoder(struct drm_connector *connector) +{ +	return NULL; +} diff --git a/linux-core/radeon_mode.h b/linux-core/radeon_mode.h new file mode 100644 index 00000000..f75e8272 --- /dev/null +++ b/linux-core/radeon_mode.h @@ -0,0 +1,255 @@ +/* + * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and + *                VA Linux Systems Inc., Fremont, California. + * Copyright 2008 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Original Authors: + *   Kevin E. Martin, Rickard E. Faith, Alan Hourihane + * + * Kernel port Author: Dave Airlie + */ + +#ifndef RADEON_MODE_H +#define RADEON_MODE_H + +#include <linux/i2c.h> +#include <linux/i2c-id.h> +#include <linux/i2c-algo-bit.h> + +#define to_radeon_crtc(x) container_of(x, struct radeon_crtc, base) +#define to_radeon_connector(x) container_of(x, struct radeon_connector, base) +#define to_radeon_encoder(x) container_of(x, struct radeon_encoder, base) +#define to_radeon_framebuffer(x) container_of(x, struct radeon_framebuffer, base) + +enum radeon_connector_type { +	CONNECTOR_NONE, +	CONNECTOR_VGA, +	CONNECTOR_DVI_I, +	CONNECTOR_DVI_D, +	CONNECTOR_DVI_A, +	CONNECTOR_STV, +	CONNECTOR_CTV, +	CONNECTOR_LVDS, +	CONNECTOR_DIGITAL, +	CONNECTOR_SCART, +	CONNECTOR_HDMI_TYPE_A, +	CONNECTOR_HDMI_TYPE_B, +	CONNECTOR_0XC, +	CONNECTOR_0XD, +	CONNECTOR_DIN, +	CONNECTOR_DISPLAY_PORT, +	CONNECTOR_UNSUPPORTED +}; + +enum radeon_dac_type { +	DAC_NONE = 0, +	DAC_PRIMARY = 1, +	DAC_TVDAC = 2, +	DAC_EXT = 3 +}; + +enum radeon_tmds_type { +	TMDS_NONE = 0, +	TMDS_INT = 1, +	TMDS_EXT = 2, +	TMDS_LVTMA = 3, +	TMDS_DDIA = 4, +	TMDS_UNIPHY = 5 +}; + +enum radeon_dvi_type { +	DVI_AUTO, +	DVI_DIGITAL, +	DVI_ANALOG +}; + +enum radeon_rmx_type { +	RMX_OFF, +	RMX_FULL, +	RMX_CENTER, +}; + +struct radeon_i2c_bus_rec { +	bool valid; +	uint32_t mask_clk_reg; +	uint32_t mask_data_reg; +	uint32_t put_clk_reg; +	uint32_t put_data_reg; +	uint32_t get_clk_reg; +	uint32_t get_data_reg; +	uint32_t mask_clk_mask; +	uint32_t mask_data_mask; +	uint32_t put_clk_mask; +	uint32_t put_data_mask; +	uint32_t get_clk_mask; +	uint32_t get_data_mask; +}; + +struct radeon_bios_connector { +	enum radeon_dac_type dac_type; +	enum radeon_tmds_type tmds_type; +	enum radeon_connector_type connector_type; +	bool valid; +	int output_id; +	int devices; +	int hpd_mask; +	struct radeon_i2c_bus_rec ddc_i2c; +	int igp_lane_info; +}; + +#define RADEON_MAX_BIOS_CONNECTOR 16 +	 +#define RADEON_PLL_USE_BIOS_DIVS   (1 << 0) +#define RADEON_PLL_NO_ODD_POST_DIV (1 << 1) +#define RADEON_PLL_USE_REF_DIV     (1 << 2) +#define RADEON_PLL_LEGACY          (1 << 3) +#define RADEON_PLL_PREFER_LOW_REF_DIV (1 << 4) + +struct radeon_pll { +    uint16_t          reference_freq; +    uint16_t          reference_div; +    uint32_t          pll_in_min; +    uint32_t          pll_in_max; +    uint32_t          pll_out_min; +    uint32_t          pll_out_max; +    uint16_t          xclk; + +    uint32_t          min_ref_div; +    uint32_t          max_ref_div; +    uint32_t          min_post_div; +    uint32_t          max_post_div; +    uint32_t          min_feedback_div; +    uint32_t          max_feedback_div; +    uint32_t          best_vco; +}; + +struct radeon_mode_info { +	struct atom_context *atom_context; +	struct radeon_bios_connector bios_connector[RADEON_MAX_BIOS_CONNECTOR]; +	struct radeon_pll pll; +}; + +struct radeon_crtc { +	struct drm_crtc base; +	int crtc_id; +	u8 lut_r[256], lut_g[256], lut_b[256]; +	bool enabled; +	bool can_tile; +	uint32_t crtc_offset; +	struct radeon_framebuffer *fbdev_fb; +	struct drm_mode_set mode_set; +}; + +struct radeon_i2c_chan { +	struct drm_device *dev; +	struct i2c_adapter adapter; +	struct i2c_algo_bit_data algo; +	struct radeon_i2c_bus_rec rec; +}; + + +#define RADEON_USE_RMX 1 + +struct radeon_encoder { +	struct drm_encoder base; +	uint32_t encoder_mode; +	uint32_t flags; +	enum radeon_rmx_type rmx_type; +	union { +		enum radeon_dac_type dac; +		enum radeon_tmds_type tmds; +	} type; +	int atom_device; /* atom devices */ +	uint32_t panel_xres, panel_yres; +	uint32_t hoverplus, hsync_width; +	uint32_t hblank; +	uint32_t voverplus, vsync_width; +	uint32_t vblank; +	uint32_t panel_pwr_delay; +	uint32_t dotclock; +}; + +struct radeon_connector { +	struct drm_connector base; +	struct radeon_i2c_chan *ddc_bus; +	int use_digital; +	 +}; + +struct radeon_framebuffer { +	struct drm_framebuffer base; +	struct drm_gem_object *obj; +	struct drm_bo_kmap_obj kmap_obj; +}; + +extern struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev, +						 struct radeon_i2c_bus_rec *rec, +						 const char *name); +extern void radeon_i2c_destroy(struct radeon_i2c_chan *i2c); +extern int radeon_ddc_get_modes(struct radeon_connector *radeon_connector); +extern struct drm_connector *radeon_connector_add(struct drm_device *dev, int bios_index); + +extern struct drm_encoder *radeon_best_encoder(struct drm_connector *connector); + +extern void radeon_compute_pll(struct radeon_pll *pll, +			       uint64_t freq, +			       uint32_t *dot_clock_p, +			       uint32_t *fb_div_p, +			       uint32_t *ref_div_p, +			       uint32_t *post_div_p, +			       int flags); + +struct drm_encoder *radeon_encoder_lvtma_add(struct drm_device *dev, int bios_index); +struct drm_encoder *radeon_encoder_atom_dac_add(struct drm_device *dev, int bios_index, int dac_id, int with_tv); +struct drm_encoder *radeon_encoder_atom_tmds_add(struct drm_device *dev, int bios_index, int tmds_type); +struct drm_encoder *radeon_encoder_legacy_lvds_add(struct drm_device *dev, int bios_index); + +extern void radeon_crtc_load_lut(struct drm_crtc *crtc); +extern void atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y); +extern void atombios_crtc_mode_set(struct drm_crtc *crtc, +				   struct drm_display_mode *mode, +				   struct drm_display_mode *adjusted_mode, +				   int x, int y); +extern void atombios_crtc_dpms(struct drm_crtc *crtc, int mode); +extern bool radeon_atom_get_clock_info(struct drm_device *dev); +extern bool radeon_combios_get_clock_info(struct drm_device *dev); +extern void radeon_get_lvds_info(struct radeon_encoder *encoder); +extern bool radeon_combios_get_lvds_info(struct radeon_encoder *encoder); +extern void radeon_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, +				     u16 blue, int regno); +struct drm_framebuffer *radeon_user_framebuffer_create(struct drm_device *dev, +						       struct drm_file *filp, +						       struct drm_mode_fb_cmd *mode_cmd); + +int radeonfb_probe(struct drm_device *dev); + +int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb); +bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev); +void radeon_atombios_init_crtc(struct drm_device *dev, +			       struct radeon_crtc *radeon_crtc); +void avivo_i2c_do_lock(struct radeon_connector *radeon_connector, int lock_state); + +void radeon_atom_static_pwrmgt_setup(struct drm_device *dev, int enable); +void radeon_atom_dyn_clk_setup(struct drm_device *dev, int enable); +void radeon_get_clock_info(struct drm_device *dev); +extern bool radeon_get_atom_connector_info_from_bios_connector_table(struct drm_device *dev); + +#endif diff --git a/linux-core/radeon_ms.h b/linux-core/radeon_ms.h new file mode 120000 index 00000000..da340c5d --- /dev/null +++ b/linux-core/radeon_ms.h @@ -0,0 +1 @@ +../shared-core/radeon_ms.h
\ No newline at end of file diff --git a/linux-core/radeon_ms_bo.c b/linux-core/radeon_ms_bo.c new file mode 120000 index 00000000..d05df59f --- /dev/null +++ b/linux-core/radeon_ms_bo.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_bo.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_bus.c b/linux-core/radeon_ms_bus.c new file mode 120000 index 00000000..50f649d0 --- /dev/null +++ b/linux-core/radeon_ms_bus.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_bus.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_combios.c b/linux-core/radeon_ms_combios.c new file mode 120000 index 00000000..d7b99958 --- /dev/null +++ b/linux-core/radeon_ms_combios.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_combios.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_combios.h b/linux-core/radeon_ms_combios.h new file mode 120000 index 00000000..5b19c708 --- /dev/null +++ b/linux-core/radeon_ms_combios.h @@ -0,0 +1 @@ +../shared-core/radeon_ms_combios.h
\ No newline at end of file diff --git a/linux-core/radeon_ms_compat.c b/linux-core/radeon_ms_compat.c new file mode 100644 index 00000000..6efdc786 --- /dev/null +++ b/linux-core/radeon_ms_compat.c @@ -0,0 +1,67 @@ +/* + * Copyright 2007 Dave Airlie + * Copyright 2007 Alex Deucher + * Copyright 2007 Michel Dänzer + * Copyright 2007 Roland Scheidegger + * Copyright 2007 Vladimir Dergachev + * Copyright 2007 Nicolai Haehnle + * Copyright 2007 Aapo Tahkola + * Copyright 2007 Ben Skeggs + * Copyright 2007 Jérôme Glisse + * 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: + *    Jerome Glisse <glisse@freedesktop.org> + */ +#include "drmP.h" +#include "drm.h" +#include "radeon_ms.h" + +/** + * Called whenever a 32-bit process running under a 64-bit kernel + * performs an ioctl on /dev/dri/card<n>. + + * \param filp file pointer. + * \param cmd command. + * \param arg user argument. + * \return zero on success or negative number on failure. + */ +long radeon_ms_compat_ioctl(struct file *filp, unsigned int cmd, +			    unsigned long arg) +{ +	unsigned int nr = DRM_IOCTL_NR(cmd); +	drm_ioctl_compat_t *fn = NULL; +	int ret; + +	if (nr < DRM_COMMAND_BASE) +		return drm_compat_ioctl(filp, cmd, arg); + +	lock_kernel(); +	if (fn != NULL) +		ret = (*fn)(filp, cmd, arg); +	else +		ret = drm_ioctl(filp->f_dentry->d_inode, filp, cmd, arg); +	unlock_kernel(); + +	return ret; +} diff --git a/linux-core/radeon_ms_cp.c b/linux-core/radeon_ms_cp.c new file mode 120000 index 00000000..6aee3e6e --- /dev/null +++ b/linux-core/radeon_ms_cp.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_cp.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_cp_mc.c b/linux-core/radeon_ms_cp_mc.c new file mode 120000 index 00000000..0ae1a647 --- /dev/null +++ b/linux-core/radeon_ms_cp_mc.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_cp_mc.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_crtc.c b/linux-core/radeon_ms_crtc.c new file mode 120000 index 00000000..31f11447 --- /dev/null +++ b/linux-core/radeon_ms_crtc.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_crtc.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_dac.c b/linux-core/radeon_ms_dac.c new file mode 120000 index 00000000..cb523cfb --- /dev/null +++ b/linux-core/radeon_ms_dac.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_dac.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_drm.c b/linux-core/radeon_ms_drm.c new file mode 120000 index 00000000..8bbf19a5 --- /dev/null +++ b/linux-core/radeon_ms_drm.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_drm.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_drm.h b/linux-core/radeon_ms_drm.h new file mode 120000 index 00000000..5d9d7319 --- /dev/null +++ b/linux-core/radeon_ms_drm.h @@ -0,0 +1 @@ +../shared-core/radeon_ms_drm.h
\ No newline at end of file diff --git a/linux-core/radeon_ms_drv.c b/linux-core/radeon_ms_drv.c new file mode 100644 index 00000000..fc3c106a --- /dev/null +++ b/linux-core/radeon_ms_drv.c @@ -0,0 +1,144 @@ +/* + * Copyright 2007 Jerome Glisse. + * 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: + *    Jerome Glisse <glisse@freedesktop.org> + */ +#include "drm_pciids.h" +#include "radeon_ms.h" + +extern struct drm_fence_driver r3xx_fence_driver; +extern struct drm_bo_driver radeon_ms_bo_driver; +extern struct drm_ioctl_desc radeon_ms_ioctls[]; +extern int radeon_ms_num_ioctls; + +static int radeon_ms_driver_dri_library_name(struct drm_device * dev, +					     char * buf); +static int radeon_ms_driver_probe(struct pci_dev *pdev, +				  const struct pci_device_id *ent); + +static struct pci_device_id pciidlist[] = { +	radeon_ms_PCI_IDS +}; + +static struct drm_driver driver = { +	.load = radeon_ms_driver_load, +	.firstopen = NULL, +	.open = radeon_ms_driver_open, +	.preclose = NULL, +	.postclose = NULL, +	.lastclose = radeon_ms_driver_lastclose, +	.unload = radeon_ms_driver_unload, +	.dma_ioctl = radeon_ms_driver_dma_ioctl, +	.dma_ready = NULL, +	.dma_quiescent = NULL, +	.context_ctor = NULL, +	.context_dtor = NULL, +	.kernel_context_switch = NULL, +	.kernel_context_switch_unlock = NULL, +	.dri_library_name = radeon_ms_driver_dri_library_name, +	.device_is_agp = NULL, +	.irq_handler = radeon_ms_irq_handler, +	.irq_preinstall = radeon_ms_irq_preinstall, +	.irq_postinstall = radeon_ms_irq_postinstall, +	.irq_uninstall = radeon_ms_irq_uninstall, +	.reclaim_buffers = drm_core_reclaim_buffers, +	.reclaim_buffers_locked = NULL, +	.reclaim_buffers_idlelocked = NULL, +	.get_map_ofs = drm_core_get_map_ofs, +	.get_reg_ofs = drm_core_get_reg_ofs, +	.set_version = NULL, +	.fb_probe = radeonfb_probe, +	.fb_remove = radeonfb_remove, +	.fence_driver = &r3xx_fence_driver, +	.bo_driver = &radeon_ms_bo_driver, +	.major = DRIVER_MAJOR, +	.minor = DRIVER_MINOR, +	.patchlevel = DRIVER_PATCHLEVEL, +	.name = DRIVER_NAME, +	.desc = DRIVER_DESC, +	.date = DRIVER_DATE, +	.driver_features = +	    DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG | +  	    DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED, +	.dev_priv_size = 0,  +	.ioctls = radeon_ms_ioctls, +	.num_ioctls = 0, +	.fops = { +		.owner = THIS_MODULE, +		.open = drm_open, +		.release = drm_release, +		.ioctl = drm_ioctl, +		.mmap = drm_mmap, +		.poll = drm_poll, +		.fasync = drm_fasync, +#if defined(CONFIG_COMPAT) && LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) +		.compat_ioctl = radeon_ms_compat_ioctl, +#endif +		}, +	.pci_driver = { +		.name = DRIVER_NAME, +		.id_table = pciidlist, +		.probe = radeon_ms_driver_probe, +		.remove = __devexit_p(drm_cleanup_pci), +	}, +}; + +static int radeon_ms_driver_probe(struct pci_dev *pdev, +				  const struct pci_device_id *ent) +{ +	return drm_get_dev(pdev, ent, &driver); +} + +static int radeon_ms_driver_dri_library_name(struct drm_device * dev, +					     char * buf) +{ +	struct drm_radeon_private *dev_priv = dev->dev_private; +	int ret; + +	switch (dev_priv->family) { +	default: +		ret = snprintf(buf, PAGE_SIZE, "\n"); +	} +	return ret; +} + +static void __exit radeon_ms_driver_exit(void) +{ +	drm_exit(&driver); +} + +static int __init radeon_ms_driver_init(void) +{ +	driver.num_ioctls = radeon_ms_num_ioctls; +	driver.driver_features |= DRIVER_MODESET; +	return drm_init(&driver, pciidlist); +} + +module_init(radeon_ms_driver_init); +module_exit(radeon_ms_driver_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL and additional rights"); diff --git a/linux-core/radeon_ms_drv.h b/linux-core/radeon_ms_drv.h new file mode 100644 index 00000000..b80ed442 --- /dev/null +++ b/linux-core/radeon_ms_drv.h @@ -0,0 +1,46 @@ +/* + * Copyright 2007 Dave Airlie + * Copyright 2007 Alex Deucher + * Copyright 2007 Michel Dänzer + * Copyright 2007 Roland Scheidegger + * Copyright 2007 Vladimir Dergachev + * Copyright 2007 Nicolai Haehnle + * Copyright 2007 Aapo Tahkola + * Copyright 2007 Ben Skeggs + * Copyright 2007 Jérôme Glisse + * 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: + *    Jérôme Glisse <glisse@freedesktop.org> + */ +#ifndef __RADEON_MS_DRV_H__ +#define __RADEON_MS_DRV_H__ + +#include <linux/i2c.h> +#include <linux/i2c-id.h> +#include <linux/i2c-algo-bit.h> +#include <linux/pci.h> +#include "drm.h" +#include "drmP.h" + +#endif diff --git a/linux-core/radeon_ms_exec.c b/linux-core/radeon_ms_exec.c new file mode 120000 index 00000000..cb397fbf --- /dev/null +++ b/linux-core/radeon_ms_exec.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_exec.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_family.c b/linux-core/radeon_ms_family.c new file mode 120000 index 00000000..1f12e092 --- /dev/null +++ b/linux-core/radeon_ms_family.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_family.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_fb.c b/linux-core/radeon_ms_fb.c new file mode 100644 index 00000000..0ac9207c --- /dev/null +++ b/linux-core/radeon_ms_fb.c @@ -0,0 +1,462 @@ +/* + * Copyright © 2007 David Airlie + * Copyright © 2007 Jerome Glisse + * + * 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 on 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 + * NON-INFRINGEMENT.  IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR + * THEIR 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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/init.h> + +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "radeon_ms.h" +#include "amd.h" + + +static int radeonfb_setcolreg(unsigned regno, unsigned red, +			       unsigned green, unsigned blue, +			       unsigned transp, struct fb_info *info) +{ +	struct amd_fb *par = info->par; +	struct drm_framebuffer *fb = par->fb; +	struct drm_crtc *crtc = par->crtc; + +	if (regno > 255) { +		return 1; +	} +	//	if (crtc->funcs->gamma_set) { +	//		crtc->funcs->gamma_set(crtc, red, green, blue, regno); +	//	} +	if (regno < 16) { +		switch (fb->depth) { +		case 15: +			fb->pseudo_palette[regno] = ((red & 0xf800) >>  1) | +				((green & 0xf800) >>  6) | +				((blue & 0xf800) >> 11); +			break; +		case 16: +			fb->pseudo_palette[regno] = (red & 0xf800) | +				((green & 0xfc00) >>  5) | +				((blue  & 0xf800) >> 11); +			break; +		case 24: +		case 32: +			fb->pseudo_palette[regno] = ((red & 0xff00) << 8) | +				(green & 0xff00) | +				((blue  & 0xff00) >> 8); +			break; +		} +	} +	return 0; +} + +static int radeonfb_check_var(struct fb_var_screeninfo *var, +			       struct fb_info *info) +{ +        struct amd_fb *par = info->par; +	struct drm_framebuffer *fb = par->fb; + +        if (!var->pixclock) +                return -EINVAL; + +        /* Need to resize the fb object !!! */ +        if (var->xres > fb->width || var->yres > fb->height) { +                DRM_ERROR("Requested width/height is greater than " +			  "current fb object %dx%d > %dx%d\n", +			  var->xres, var->yres, fb->width, fb->height); +                DRM_ERROR("Need resizing code.\n"); +                return -EINVAL; +        } + +        switch (var->bits_per_pixel) { +        case 16: +		if (var->green.length == 5) { +			var->red.offset = 10; +			var->green.offset = 5; +			var->blue.offset = 0; +			var->red.length = 5; +			var->green.length = 5; +			var->blue.length = 5; +			var->transp.length = 0; +			var->transp.offset = 0; +		} else { +	                var->red.offset = 11; +			var->green.offset = 6; +			var->blue.offset = 0; +			var->red.length = 5; +			var->green.length = 6; +			var->blue.length = 5; +			var->transp.length = 0; +			var->transp.offset = 0; +		} +                break; +	case 32: +                if (var->transp.length) { +			var->red.offset = 16; +			var->green.offset = 8; +			var->blue.offset = 0; +			var->red.length = 8; +			var->green.length = 8; +			var->blue.length = 8; +			var->transp.length = 8; +			var->transp.offset = 24; +		} else { +			var->red.offset = 16; +			var->green.offset = 8; +			var->blue.offset = 0; +			var->red.length = 8; +			var->green.length = 8; +			var->blue.length = 8; +			var->transp.length = 0; +			var->transp.offset = 0; +		} +		break; +        default: +		return -EINVAL;  +        } +	return 0; +} + +static bool radeonfb_mode_equal(struct drm_display_mode *mode1, +			        struct drm_display_mode *mode2) +{ +	if (mode1->hdisplay == mode2->hdisplay && +	    mode1->hsync_start == mode2->hsync_start && +	    mode1->hsync_end == mode2->hsync_end && +	    mode1->htotal == mode2->htotal && +	    mode1->hskew == mode2->hskew && +	    mode1->vdisplay == mode2->vdisplay && +	    mode1->vsync_start == mode2->vsync_start && +	    mode1->vsync_end == mode2->vsync_end && +	    mode1->vtotal == mode2->vtotal && +	    mode1->vscan == mode2->vscan && +	    mode1->flags == mode2->flags) { +	    	/* FIXME: what about adding a margin for clock ? */ +		if (mode1->clock == mode2->clock) +			return true; +		return false; +	} +	 +	return false; +} + +static int radeonfb_set_par(struct fb_info *info) +{ +	struct amd_fb *par = info->par; +	struct drm_framebuffer *fb = par->fb; +	struct drm_device *dev = par->dev; +        struct drm_display_mode *drm_mode, *search_mode; +        struct drm_output *output; +        struct fb_var_screeninfo *var = &info->var; +	int found = 0; + +        switch (var->bits_per_pixel) { +        case 16: +		fb->depth = (var->green.length == 6) ? 16 : 15; +		break; +        case 32: +		fb->depth = (var->transp.length > 0) ? 32 : 24; +		break; +	default: +		return -EINVAL;  +	} +	fb->bits_per_pixel = var->bits_per_pixel; + +	info->fix.line_length = fb->pitch; +	info->fix.smem_len = info->fix.line_length * fb->height; +	info->fix.visual = FB_VISUAL_TRUECOLOR; +	info->screen_size = info->fix.smem_len; /* ??? */ + +        /* Should we walk the output's modelist or just create our own ??? +         * For now, we create and destroy a mode based on the incoming  +         * parameters. But there's commented out code below which scans  +         * the output list too. +         */ +	drm_mode = drm_mode_create(dev); +	drm_mode->hdisplay = var->xres; +	drm_mode->hsync_start = drm_mode->hdisplay + var->right_margin; +	drm_mode->hsync_end = drm_mode->hsync_start + var->hsync_len; +	drm_mode->htotal = drm_mode->hsync_end + var->left_margin; +	drm_mode->vdisplay = var->yres; +	drm_mode->vsync_start = drm_mode->vdisplay + var->lower_margin; +	drm_mode->vsync_end = drm_mode->vsync_start + var->vsync_len; +	drm_mode->vtotal = drm_mode->vsync_end + var->upper_margin; +	drm_mode->clock = PICOS2KHZ(var->pixclock); +	drm_mode->vrefresh = drm_mode_vrefresh(drm_mode); +	drm_mode_set_name(drm_mode); +	drm_mode_set_crtcinfo(drm_mode, CRTC_INTERLACE_HALVE_V); + +        list_for_each_entry(output, &dev->mode_config.output_list, head) { +                if (output->crtc == par->crtc) +                        break; +        } + +	drm_mode_debug_printmodeline(drm_mode);     +        list_for_each_entry(search_mode, &output->modes, head) { +		drm_mode_debug_printmodeline(search_mode); +		if (radeonfb_mode_equal(drm_mode, search_mode)) { +			drm_mode_destroy(dev, drm_mode); +			drm_mode = search_mode; +			found = 1; +			break; +		} +	} + +	if (!found) { +		if (par->fb_mode) { +			drm_mode_detachmode_crtc(dev, par->fb_mode); +		} +		par->fb_mode = drm_mode; +		drm_mode_debug_printmodeline(drm_mode); +		/* attach mode */ +		drm_mode_attachmode_crtc(dev, par->crtc, par->fb_mode); +	} + +	if (par->crtc->enabled) { +		if (!drm_mode_equal(&par->crtc->mode, drm_mode) || +		    par->crtc->fb != par->fb) { +			par->crtc->fb = par->fb; +			if (!drm_crtc_set_mode(par->crtc, drm_mode, 0, 0)) { +				return -EINVAL; +			} +		} +	} + +	return 0; +} + +static struct fb_ops radeonfb_ops = { +	.owner = THIS_MODULE, +	//	.fb_open = radeonfb_open, +	//	.fb_read = radeonfb_read, +	//	.fb_write = radeonfb_write, +	//	.fb_release = radeonfb_release, +	//	.fb_ioctl = radeonfb_ioctl, +	.fb_check_var = radeonfb_check_var, +	.fb_set_par = radeonfb_set_par, +	.fb_setcolreg = radeonfb_setcolreg, +	.fb_fillrect = cfb_fillrect, +	.fb_copyarea = cfb_copyarea, +	.fb_imageblit = cfb_imageblit, +}; + +int radeonfb_probe(struct drm_device *dev, struct drm_crtc *crtc, struct drm_output *output) +{ +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct fb_info *info; +	struct amd_fb *par; +	struct device *device = &dev->pdev->dev;  +	struct drm_framebuffer *fb; +	struct drm_display_mode *mode = crtc->desired_mode; +	int ret; + +	info = framebuffer_alloc(sizeof(struct amd_fb), device); +	if (!info){ +		DRM_INFO("[radeon_ms] framebuffer_alloc failed\n"); +		return -EINVAL; +	} + +	fb = drm_framebuffer_create(dev); +	if (!fb) { +		framebuffer_release(info); +		DRM_ERROR("[radeon_ms] failed to allocate fb.\n"); +		return -EINVAL; +	} +	crtc->fb = fb; + +	fb->width = crtc->desired_mode->hdisplay; +	fb->height = crtc->desired_mode->vdisplay; +	fb->bits_per_pixel = 32; +	fb->pitch = fb->width * ((fb->bits_per_pixel + 1) / 8); +	fb->depth = 24; +	/* one page alignment should be fine for constraint (micro|macro tiling, +	 * bit depth, color buffer offset, ...) */ +	ret = drm_buffer_object_create(dev, fb->width * fb->height * 4,  +				       drm_bo_type_kernel, +				       DRM_BO_FLAG_READ | +				       DRM_BO_FLAG_WRITE | +				       DRM_BO_FLAG_NO_EVICT | +				       DRM_BO_FLAG_MEM_VRAM, +				       DRM_BO_HINT_DONT_FENCE, +				       1, +				       0, +				       &fb->bo); +	if (ret || fb->bo == NULL) { +		DRM_ERROR("[radeon_ms] failed to allocate framebuffer\n"); +		drm_framebuffer_destroy(fb); +		framebuffer_release(info); +		return -EINVAL; +	} + +	DRM_INFO("[radeon_ms] framebuffer %dx%d at 0x%08lX\n", +		 fb->width, fb->height, fb->bo->offset); + +	fb->fbdev = info; +	par = info->par; +	dev_priv->fb = par; +	par->dev = dev; +	par->crtc = crtc; +	par->fb = fb; +	info->fbops = &radeonfb_ops; +	strcpy(info->fix.id, "radeonfb"); +	info->fix.type = FB_TYPE_PACKED_PIXELS; +	info->fix.visual = FB_VISUAL_TRUECOLOR; +	info->fix.type_aux = 0; +	info->fix.xpanstep = 8; +	info->fix.ypanstep = 1; +	info->fix.ywrapstep = 0; +	info->fix.accel = FB_ACCEL_ATI_RADEON; +	info->fix.type_aux = 0; +	info->fix.mmio_start = 0; +	info->fix.mmio_len = 0; +	info->fix.line_length = fb->pitch; +	info->fix.smem_start = fb->bo->offset + dev->mode_config.fb_base; +	info->fix.smem_len = info->fix.line_length * fb->height; +	info->flags = FBINFO_DEFAULT; +	DRM_INFO("[radeon_ms] fb physical start : 0x%lX\n", info->fix.smem_start); +	DRM_INFO("[radeon_ms] fb physical size  : %d\n", info->fix.smem_len); + + 	ret = drm_bo_kmap(fb->bo, 0, fb->bo->num_pages, &fb->kmap); +  	if (ret) { +  		DRM_ERROR("error mapping fb: %d\n", ret); +	} +	info->screen_base = fb->kmap.virtual; +	info->screen_size = info->fix.smem_len; /* FIXME */ +	info->pseudo_palette = fb->pseudo_palette; +	info->var.xres_virtual = fb->width; +	info->var.yres_virtual = fb->height; +	info->var.bits_per_pixel = fb->bits_per_pixel; +	info->var.xoffset = 0; +	info->var.yoffset = 0; +	info->var.activate = FB_ACTIVATE_NOW; +	info->var.height = -1; +	info->var.width = -1; +	info->var.vmode = FB_VMODE_NONINTERLACED; + +	info->var.xres = mode->hdisplay; +	info->var.right_margin = mode->hsync_start - mode->hdisplay; +	info->var.hsync_len = mode->hsync_end - mode->hsync_start; +	info->var.left_margin = mode->htotal - mode->hsync_end; +	info->var.yres = mode->vdisplay; +	info->var.lower_margin = mode->vsync_start - mode->vdisplay; +	info->var.vsync_len = mode->vsync_end - mode->vsync_start; +	info->var.upper_margin = mode->vtotal - mode->vsync_end; +	info->var.pixclock = 10000000 / mode->htotal * 1000 / +		mode->vtotal * 100; +	/* avoid overflow */ +	info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh; + +	info->pixmap.size = 64*1024; +	info->pixmap.buf_align = 8; +	info->pixmap.access_align = 32; +	info->pixmap.flags = FB_PIXMAP_SYSTEM; +	info->pixmap.scan_align = 1; + +	DRM_DEBUG("fb depth is %d\n", fb->depth); +	DRM_DEBUG("   pitch is %d\n", fb->pitch); +	switch(fb->depth) { + 	case 15: +                info->var.red.offset = 10; +		info->var.green.offset = 5; +		info->var.blue.offset = 0; +		info->var.red.length = info->var.green.length = +			info->var.blue.length = 5; +		info->var.transp.offset = 15; +                info->var.transp.length = 1; +                break; +	case 16: +                info->var.red.offset = 11; +                info->var.green.offset = 5; +                info->var.blue.offset = 0; +                info->var.red.length = 5; +                info->var.green.length = 6; +                info->var.blue.length = 5; +                info->var.transp.offset = 0; + 		break; +	case 24: +                info->var.red.offset = 16; +                info->var.green.offset = 8; +                info->var.blue.offset = 0; +                info->var.red.length = info->var.green.length = +                        info->var.blue.length = 8; +                info->var.transp.offset = 0; +                info->var.transp.length = 0; +                break; +	case 32: +		info->var.red.offset = 16; +		info->var.green.offset = 8; +		info->var.blue.offset = 0; +		info->var.red.length = info->var.green.length = +			info->var.blue.length = 8; +		info->var.transp.offset = 24; +		info->var.transp.length = 8; +		break; +	default: +		DRM_ERROR("only support 15, 16, 24 or 32bits per pixel " +			  "got %d\n", fb->depth); +		return -EINVAL; +		break; +	} + +	if (register_framebuffer(info) < 0) { +		return -EINVAL; +	} + +	DRM_INFO("[radeon_ms] fb%d: %s frame buffer device\n", info->node, +		 info->fix.id); +	return 0; +} +EXPORT_SYMBOL(radeonfb_probe); + +int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *kern_fb) +{ +	struct drm_radeon_private *dev_priv = dev->dev_private; +	struct amd_fb *fb = dev_priv->fb; +	struct fb_info *info; + +	if (fb == NULL || fb->fb == NULL || fb->fb->fbdev == NULL) { +		DRM_INFO("[radeon_ms] %s: no crtc, or fb or fbdev\n", +			 __func__); +		return 0; +	} +	info = fb->fb->fbdev; +	unregister_framebuffer(info); +	drm_bo_kunmap(&fb->fb->kmap); +	drm_bo_usage_deref_unlocked(&fb->fb->bo); +	drm_framebuffer_destroy(fb->fb); +	framebuffer_release(info); +	dev_priv->fb = NULL; +	return 0; +} +EXPORT_SYMBOL(radeonfb_remove); +MODULE_LICENSE("GPL"); diff --git a/linux-core/radeon_ms_fence.c b/linux-core/radeon_ms_fence.c new file mode 120000 index 00000000..383cc070 --- /dev/null +++ b/linux-core/radeon_ms_fence.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_fence.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_gpu.c b/linux-core/radeon_ms_gpu.c new file mode 120000 index 00000000..fa5e05bf --- /dev/null +++ b/linux-core/radeon_ms_gpu.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_gpu.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_i2c.c b/linux-core/radeon_ms_i2c.c new file mode 120000 index 00000000..1863e6d6 --- /dev/null +++ b/linux-core/radeon_ms_i2c.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_i2c.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_irq.c b/linux-core/radeon_ms_irq.c new file mode 120000 index 00000000..c4e60ba6 --- /dev/null +++ b/linux-core/radeon_ms_irq.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_irq.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_output.c b/linux-core/radeon_ms_output.c new file mode 120000 index 00000000..6a38b671 --- /dev/null +++ b/linux-core/radeon_ms_output.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_output.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_properties.c b/linux-core/radeon_ms_properties.c new file mode 120000 index 00000000..e2e0dc0b --- /dev/null +++ b/linux-core/radeon_ms_properties.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_properties.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_properties.h b/linux-core/radeon_ms_properties.h new file mode 120000 index 00000000..59783e89 --- /dev/null +++ b/linux-core/radeon_ms_properties.h @@ -0,0 +1 @@ +../shared-core/radeon_ms_properties.h
\ No newline at end of file diff --git a/linux-core/radeon_ms_reg.h b/linux-core/radeon_ms_reg.h new file mode 120000 index 00000000..24b01b42 --- /dev/null +++ b/linux-core/radeon_ms_reg.h @@ -0,0 +1 @@ +../shared-core/radeon_ms_reg.h
\ No newline at end of file diff --git a/linux-core/radeon_ms_rom.c b/linux-core/radeon_ms_rom.c new file mode 120000 index 00000000..80f5f606 --- /dev/null +++ b/linux-core/radeon_ms_rom.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_rom.c
\ No newline at end of file diff --git a/linux-core/radeon_ms_rom.h b/linux-core/radeon_ms_rom.h new file mode 120000 index 00000000..f20e42be --- /dev/null +++ b/linux-core/radeon_ms_rom.h @@ -0,0 +1 @@ +../shared-core/radeon_ms_rom.h
\ No newline at end of file diff --git a/linux-core/radeon_ms_state.c b/linux-core/radeon_ms_state.c new file mode 120000 index 00000000..2d2e2ef7 --- /dev/null +++ b/linux-core/radeon_ms_state.c @@ -0,0 +1 @@ +../shared-core/radeon_ms_state.c
\ No newline at end of file diff --git a/linux-core/radeon_reg.h b/linux-core/radeon_reg.h new file mode 100644 index 00000000..04cfa732 --- /dev/null +++ b/linux-core/radeon_reg.h @@ -0,0 +1,5245 @@ +/* + * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and + *                VA Linux Systems Inc., Fremont, 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 on 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 + * NON-INFRINGEMENT.  IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR + * THEIR 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: + *   Kevin E. Martin <martin@xfree86.org> + *   Rickard E. Faith <faith@valinux.com> + *   Alan Hourihane <alanh@fairlite.demon.co.uk> + * + * References: + * + * !!!! FIXME !!!! + *   RAGE 128 VR/ RAGE 128 GL Register Reference Manual (Technical + *   Reference Manual P/N RRG-G04100-C Rev. 0.04), ATI Technologies: April + *   1999. + * + * !!!! FIXME !!!! + *   RAGE 128 Software Development Manual (Technical Reference Manual P/N + *   SDK-G04000 Rev. 0.01), ATI Technologies: June 1999. + * + */ + +/* !!!! FIXME !!!!  NOTE: THIS FILE HAS BEEN CONVERTED FROM r128_reg.h + * AND CONTAINS REGISTERS AND REGISTER DEFINITIONS THAT ARE NOT CORRECT + * ON THE RADEON.  A FULL AUDIT OF THIS CODE IS NEEDED!  */ + +#ifndef _RADEON_REG_H_ +#define _RADEON_REG_H_ + +#define ATI_DATATYPE_VQ				0 +#define ATI_DATATYPE_CI4			1 +#define ATI_DATATYPE_CI8			2 +#define ATI_DATATYPE_ARGB1555			3 +#define ATI_DATATYPE_RGB565			4 +#define ATI_DATATYPE_RGB888			5 +#define ATI_DATATYPE_ARGB8888			6 +#define ATI_DATATYPE_RGB332			7 +#define ATI_DATATYPE_Y8				8 +#define ATI_DATATYPE_RGB8			9 +#define ATI_DATATYPE_CI16			10 +#define ATI_DATATYPE_VYUY_422			11 +#define ATI_DATATYPE_YVYU_422			12 +#define ATI_DATATYPE_AYUV_444			14 +#define ATI_DATATYPE_ARGB4444			15 + +				/* Registers for 2D/Video/Overlay */ +#define RADEON_ADAPTER_ID                   0x0f2c /* PCI */ +#define RADEON_AGP_BASE                     0x0170 +#define RADEON_AGP_CNTL                     0x0174 +#       define RADEON_AGP_APER_SIZE_256MB   (0x00 << 0) +#       define RADEON_AGP_APER_SIZE_128MB   (0x20 << 0) +#       define RADEON_AGP_APER_SIZE_64MB    (0x30 << 0) +#       define RADEON_AGP_APER_SIZE_32MB    (0x38 << 0) +#       define RADEON_AGP_APER_SIZE_16MB    (0x3c << 0) +#       define RADEON_AGP_APER_SIZE_8MB     (0x3e << 0) +#       define RADEON_AGP_APER_SIZE_4MB     (0x3f << 0) +#       define RADEON_AGP_APER_SIZE_MASK    (0x3f << 0) +#define RADEON_STATUS_PCI_CONFIG            0x06 +#       define RADEON_CAP_LIST              0x100000 +#define RADEON_CAPABILITIES_PTR_PCI_CONFIG  0x34 /* offset in PCI config*/ +#       define RADEON_CAP_PTR_MASK          0xfc /* mask off reserved bits of CAP_PTR */ +#       define RADEON_CAP_ID_NULL           0x00 /* End of capability list */ +#       define RADEON_CAP_ID_AGP            0x02 /* AGP capability ID */ +#       define RADEON_CAP_ID_EXP            0x10 /* PCI Express */ +#define RADEON_AGP_COMMAND                  0x0f60 /* PCI */ +#define RADEON_AGP_COMMAND_PCI_CONFIG       0x0060 /* offset in PCI config*/ +#       define RADEON_AGP_ENABLE            (1<<8) +#define RADEON_AGP_PLL_CNTL                 0x000b /* PLL */ +#define RADEON_AGP_STATUS                   0x0f5c /* PCI */ +#       define RADEON_AGP_1X_MODE           0x01 +#       define RADEON_AGP_2X_MODE           0x02 +#       define RADEON_AGP_4X_MODE           0x04 +#       define RADEON_AGP_FW_MODE           0x10 +#       define RADEON_AGP_MODE_MASK         0x17 +#       define RADEON_AGPv3_MODE            0x08 +#       define RADEON_AGPv3_4X_MODE         0x01 +#       define RADEON_AGPv3_8X_MODE         0x02 +#define RADEON_ATTRDR                       0x03c1 /* VGA */ +#define RADEON_ATTRDW                       0x03c0 /* VGA */ +#define RADEON_ATTRX                        0x03c0 /* VGA */ +#define RADEON_AUX_SC_CNTL                  0x1660 +#       define RADEON_AUX1_SC_EN            (1 << 0) +#       define RADEON_AUX1_SC_MODE_OR       (0 << 1) +#       define RADEON_AUX1_SC_MODE_NAND     (1 << 1) +#       define RADEON_AUX2_SC_EN            (1 << 2) +#       define RADEON_AUX2_SC_MODE_OR       (0 << 3) +#       define RADEON_AUX2_SC_MODE_NAND     (1 << 3) +#       define RADEON_AUX3_SC_EN            (1 << 4) +#       define RADEON_AUX3_SC_MODE_OR       (0 << 5) +#       define RADEON_AUX3_SC_MODE_NAND     (1 << 5) +#define RADEON_AUX1_SC_BOTTOM               0x1670 +#define RADEON_AUX1_SC_LEFT                 0x1664 +#define RADEON_AUX1_SC_RIGHT                0x1668 +#define RADEON_AUX1_SC_TOP                  0x166c +#define RADEON_AUX2_SC_BOTTOM               0x1680 +#define RADEON_AUX2_SC_LEFT                 0x1674 +#define RADEON_AUX2_SC_RIGHT                0x1678 +#define RADEON_AUX2_SC_TOP                  0x167c +#define RADEON_AUX3_SC_BOTTOM               0x1690 +#define RADEON_AUX3_SC_LEFT                 0x1684 +#define RADEON_AUX3_SC_RIGHT                0x1688 +#define RADEON_AUX3_SC_TOP                  0x168c +#define RADEON_AUX_WINDOW_HORZ_CNTL         0x02d8 +#define RADEON_AUX_WINDOW_VERT_CNTL         0x02dc + +#define RADEON_BASE_CODE                    0x0f0b +#define RADEON_BIOS_0_SCRATCH               0x0010 +#       define RADEON_FP_PANEL_SCALABLE     (1 << 16) +#       define RADEON_FP_PANEL_SCALE_EN     (1 << 17) +#       define RADEON_FP_CHIP_SCALE_EN      (1 << 18) +#       define RADEON_DRIVER_BRIGHTNESS_EN  (1 << 26) +#       define RADEON_DISPLAY_ROT_MASK      (3 << 28) +#       define RADEON_DISPLAY_ROT_00        (0 << 28) +#       define RADEON_DISPLAY_ROT_90        (1 << 28) +#       define RADEON_DISPLAY_ROT_180       (2 << 28) +#       define RADEON_DISPLAY_ROT_270       (3 << 28) +#define RADEON_BIOS_1_SCRATCH               0x0014 +#define RADEON_BIOS_2_SCRATCH               0x0018 +#define RADEON_BIOS_3_SCRATCH               0x001c +#define RADEON_BIOS_4_SCRATCH               0x0020 +#       define RADEON_CRT1_ATTACHED_MASK    (3 << 0) +#       define RADEON_CRT1_ATTACHED_MONO    (1 << 0) +#       define RADEON_CRT1_ATTACHED_COLOR   (2 << 0) +#       define RADEON_LCD1_ATTACHED         (1 << 2) +#       define RADEON_DFP1_ATTACHED         (1 << 3) +#       define RADEON_TV1_ATTACHED_MASK     (3 << 4) +#       define RADEON_TV1_ATTACHED_COMP     (1 << 4) +#       define RADEON_TV1_ATTACHED_SVIDEO   (2 << 4) +#       define RADEON_CRT2_ATTACHED_MASK    (3 << 8) +#       define RADEON_CRT2_ATTACHED_MONO    (1 << 8) +#       define RADEON_CRT2_ATTACHED_COLOR   (2 << 8) +#       define RADEON_DFP2_ATTACHED         (1 << 11) +#define RADEON_BIOS_5_SCRATCH               0x0024 +#       define RADEON_LCD1_ON               (1 << 0) +#       define RADEON_CRT1_ON               (1 << 1) +#       define RADEON_TV1_ON                (1 << 2) +#       define RADEON_DFP1_ON               (1 << 3) +#       define RADEON_CRT2_ON               (1 << 5) +#       define RADEON_CV1_ON                (1 << 6) +#       define RADEON_DFP2_ON               (1 << 7) +#       define RADEON_LCD1_CRTC_MASK        (1 << 8) +#       define RADEON_LCD1_CRTC_SHIFT       8 +#       define RADEON_CRT1_CRTC_MASK        (1 << 9) +#       define RADEON_CRT1_CRTC_SHIFT       9 +#       define RADEON_TV1_CRTC_MASK         (1 << 10) +#       define RADEON_TV1_CRTC_SHIFT        10 +#       define RADEON_DFP1_CRTC_MASK        (1 << 11) +#       define RADEON_DFP1_CRTC_SHIFT       11 +#       define RADEON_CRT2_CRTC_MASK        (1 << 12) +#       define RADEON_CRT2_CRTC_SHIFT       12 +#       define RADEON_CV1_CRTC_MASK         (1 << 13) +#       define RADEON_CV1_CRTC_SHIFT        13 +#       define RADEON_DFP2_CRTC_MASK        (1 << 14) +#       define RADEON_DFP2_CRTC_SHIFT       14 +#define RADEON_BIOS_6_SCRATCH               0x0028 +#       define RADEON_ACC_MODE_CHANGE       (1 << 2) +#       define RADEON_EXT_DESKTOP_MODE      (1 << 3) +#       define RADEON_LCD_DPMS_ON           (1 << 20) +#       define RADEON_CRT_DPMS_ON           (1 << 21) +#       define RADEON_TV_DPMS_ON            (1 << 22) +#       define RADEON_DFP_DPMS_ON           (1 << 23) +#       define RADEON_DPMS_MASK             (3 << 24) +#       define RADEON_DPMS_ON               (0 << 24) +#       define RADEON_DPMS_STANDBY          (1 << 24) +#       define RADEON_DPMS_SUSPEND          (2 << 24) +#       define RADEON_DPMS_OFF              (3 << 24) +#       define RADEON_SCREEN_BLANKING       (1 << 26) +#       define RADEON_DRIVER_CRITICAL       (1 << 27) +#       define RADEON_DISPLAY_SWITCHING_DIS (1 << 30) +#define RADEON_BIOS_7_SCRATCH               0x002c +#       define RADEON_SYS_HOTKEY            (1 << 10) +#       define RADEON_DRV_LOADED            (1 << 12) +#define RADEON_BIOS_ROM                     0x0f30 /* PCI */ +#define RADEON_BIST                         0x0f0f /* PCI */ +#define RADEON_BRUSH_DATA0                  0x1480 +#define RADEON_BRUSH_DATA1                  0x1484 +#define RADEON_BRUSH_DATA10                 0x14a8 +#define RADEON_BRUSH_DATA11                 0x14ac +#define RADEON_BRUSH_DATA12                 0x14b0 +#define RADEON_BRUSH_DATA13                 0x14b4 +#define RADEON_BRUSH_DATA14                 0x14b8 +#define RADEON_BRUSH_DATA15                 0x14bc +#define RADEON_BRUSH_DATA16                 0x14c0 +#define RADEON_BRUSH_DATA17                 0x14c4 +#define RADEON_BRUSH_DATA18                 0x14c8 +#define RADEON_BRUSH_DATA19                 0x14cc +#define RADEON_BRUSH_DATA2                  0x1488 +#define RADEON_BRUSH_DATA20                 0x14d0 +#define RADEON_BRUSH_DATA21                 0x14d4 +#define RADEON_BRUSH_DATA22                 0x14d8 +#define RADEON_BRUSH_DATA23                 0x14dc +#define RADEON_BRUSH_DATA24                 0x14e0 +#define RADEON_BRUSH_DATA25                 0x14e4 +#define RADEON_BRUSH_DATA26                 0x14e8 +#define RADEON_BRUSH_DATA27                 0x14ec +#define RADEON_BRUSH_DATA28                 0x14f0 +#define RADEON_BRUSH_DATA29                 0x14f4 +#define RADEON_BRUSH_DATA3                  0x148c +#define RADEON_BRUSH_DATA30                 0x14f8 +#define RADEON_BRUSH_DATA31                 0x14fc +#define RADEON_BRUSH_DATA32                 0x1500 +#define RADEON_BRUSH_DATA33                 0x1504 +#define RADEON_BRUSH_DATA34                 0x1508 +#define RADEON_BRUSH_DATA35                 0x150c +#define RADEON_BRUSH_DATA36                 0x1510 +#define RADEON_BRUSH_DATA37                 0x1514 +#define RADEON_BRUSH_DATA38                 0x1518 +#define RADEON_BRUSH_DATA39                 0x151c +#define RADEON_BRUSH_DATA4                  0x1490 +#define RADEON_BRUSH_DATA40                 0x1520 +#define RADEON_BRUSH_DATA41                 0x1524 +#define RADEON_BRUSH_DATA42                 0x1528 +#define RADEON_BRUSH_DATA43                 0x152c +#define RADEON_BRUSH_DATA44                 0x1530 +#define RADEON_BRUSH_DATA45                 0x1534 +#define RADEON_BRUSH_DATA46                 0x1538 +#define RADEON_BRUSH_DATA47                 0x153c +#define RADEON_BRUSH_DATA48                 0x1540 +#define RADEON_BRUSH_DATA49                 0x1544 +#define RADEON_BRUSH_DATA5                  0x1494 +#define RADEON_BRUSH_DATA50                 0x1548 +#define RADEON_BRUSH_DATA51                 0x154c +#define RADEON_BRUSH_DATA52                 0x1550 +#define RADEON_BRUSH_DATA53                 0x1554 +#define RADEON_BRUSH_DATA54                 0x1558 +#define RADEON_BRUSH_DATA55                 0x155c +#define RADEON_BRUSH_DATA56                 0x1560 +#define RADEON_BRUSH_DATA57                 0x1564 +#define RADEON_BRUSH_DATA58                 0x1568 +#define RADEON_BRUSH_DATA59                 0x156c +#define RADEON_BRUSH_DATA6                  0x1498 +#define RADEON_BRUSH_DATA60                 0x1570 +#define RADEON_BRUSH_DATA61                 0x1574 +#define RADEON_BRUSH_DATA62                 0x1578 +#define RADEON_BRUSH_DATA63                 0x157c +#define RADEON_BRUSH_DATA7                  0x149c +#define RADEON_BRUSH_DATA8                  0x14a0 +#define RADEON_BRUSH_DATA9                  0x14a4 +#define RADEON_BRUSH_SCALE                  0x1470 +#define RADEON_BRUSH_Y_X                    0x1474 +#define RADEON_BUS_CNTL                     0x0030 +#       define RADEON_BUS_MASTER_DIS         (1 << 6) +#       define RADEON_BUS_BIOS_DIS_ROM       (1 << 12) +#       define RADEON_BUS_RD_DISCARD_EN      (1 << 24) +#       define RADEON_BUS_RD_ABORT_EN        (1 << 25) +#       define RADEON_BUS_MSTR_DISCONNECT_EN (1 << 28) +#       define RADEON_BUS_WRT_BURST          (1 << 29) +#       define RADEON_BUS_READ_BURST         (1 << 30) +#define RADEON_BUS_CNTL1                    0x0034 +#       define RADEON_BUS_WAIT_ON_LOCK_EN    (1 << 4) + +#define RADEON_CACHE_CNTL                   0x1724 +#define RADEON_CACHE_LINE                   0x0f0c /* PCI */ +#define RADEON_CAPABILITIES_ID              0x0f50 /* PCI */ +#define RADEON_CAPABILITIES_PTR             0x0f34 /* PCI */ +#define RADEON_CLK_PIN_CNTL                 0x0001 /* PLL */ +#       define RADEON_SCLK_DYN_START_CNTL   (1 << 15) +#define RADEON_CLOCK_CNTL_DATA              0x000c +#define RADEON_CLOCK_CNTL_INDEX             0x0008 +#       define RADEON_PLL_WR_EN             (1 << 7) +#       define RADEON_PLL_DIV_SEL           (3 << 8) +#       define RADEON_PLL2_DIV_SEL_MASK     ~(3 << 8) +#define RADEON_CLK_PWRMGT_CNTL              0x0014 +#       define RADEON_ENGIN_DYNCLK_MODE     (1 << 12) +#       define RADEON_ACTIVE_HILO_LAT_MASK  (3 << 13) +#       define RADEON_ACTIVE_HILO_LAT_SHIFT 13 +#       define RADEON_DISP_DYN_STOP_LAT_MASK (1 << 12) +#       define RADEON_MC_BUSY               (1 << 16) +#       define RADEON_DLL_READY             (1 << 19) +#       define RADEON_CG_NO1_DEBUG_0        (1 << 24) +#       define RADEON_CG_NO1_DEBUG_MASK     (0x1f << 24) +#       define RADEON_DYN_STOP_MODE_MASK    (7 << 21) +#       define RADEON_TVPLL_PWRMGT_OFF      (1 << 30) +#       define RADEON_TVCLK_TURNOFF         (1 << 31) +#define RADEON_PLL_PWRMGT_CNTL              0x0015 +#       define RADEON_TCL_BYPASS_DISABLE    (1 << 20) +#define RADEON_CLR_CMP_CLR_3D               0x1a24 +#define RADEON_CLR_CMP_CLR_DST              0x15c8 +#define RADEON_CLR_CMP_CLR_SRC              0x15c4 +#define RADEON_CLR_CMP_CNTL                 0x15c0 +#       define RADEON_SRC_CMP_EQ_COLOR      (4 <<  0) +#       define RADEON_SRC_CMP_NEQ_COLOR     (5 <<  0) +#       define RADEON_CLR_CMP_SRC_SOURCE    (1 << 24) +#define RADEON_CLR_CMP_MASK                 0x15cc +#       define RADEON_CLR_CMP_MSK           0xffffffff +#define RADEON_CLR_CMP_MASK_3D              0x1A28 +#define RADEON_COMMAND                      0x0f04 /* PCI */ +#define RADEON_COMPOSITE_SHADOW_ID          0x1a0c +#define RADEON_CONFIG_APER_0_BASE           0x0100 +#define RADEON_CONFIG_APER_1_BASE           0x0104 +#define RADEON_CONFIG_APER_SIZE             0x0108 +#define RADEON_CONFIG_BONDS                 0x00e8 +#define RADEON_CONFIG_CNTL                  0x00e0 +#       define RADEON_CFG_ATI_REV_A11       (0   << 16) +#       define RADEON_CFG_ATI_REV_A12       (1   << 16) +#       define RADEON_CFG_ATI_REV_A13       (2   << 16) +#       define RADEON_CFG_ATI_REV_ID_MASK   (0xf << 16) +#define RADEON_CONFIG_MEMSIZE               0x00f8 +#define RADEON_CONFIG_MEMSIZE_EMBEDDED      0x0114 +#define RADEON_CONFIG_REG_1_BASE            0x010c +#define RADEON_CONFIG_REG_APER_SIZE         0x0110 +#define RADEON_CONFIG_XSTRAP                0x00e4 +#define RADEON_CONSTANT_COLOR_C             0x1d34 +#       define RADEON_CONSTANT_COLOR_MASK   0x00ffffff +#       define RADEON_CONSTANT_COLOR_ONE    0x00ffffff +#       define RADEON_CONSTANT_COLOR_ZERO   0x00000000 +#define RADEON_CRC_CMDFIFO_ADDR             0x0740 +#define RADEON_CRC_CMDFIFO_DOUT             0x0744 +#define RADEON_GRPH_BUFFER_CNTL             0x02f0 +#       define RADEON_GRPH_START_REQ_MASK          (0x7f) +#       define RADEON_GRPH_START_REQ_SHIFT         0 +#       define RADEON_GRPH_STOP_REQ_MASK           (0x7f<<8) +#       define RADEON_GRPH_STOP_REQ_SHIFT          8 +#       define RADEON_GRPH_CRITICAL_POINT_MASK     (0x7f<<16) +#       define RADEON_GRPH_CRITICAL_POINT_SHIFT    16 +#       define RADEON_GRPH_CRITICAL_CNTL           (1<<28) +#       define RADEON_GRPH_BUFFER_SIZE             (1<<29) +#       define RADEON_GRPH_CRITICAL_AT_SOF         (1<<30) +#       define RADEON_GRPH_STOP_CNTL               (1<<31) +#define RADEON_GRPH2_BUFFER_CNTL            0x03f0 +#       define RADEON_GRPH2_START_REQ_MASK         (0x7f) +#       define RADEON_GRPH2_START_REQ_SHIFT         0 +#       define RADEON_GRPH2_STOP_REQ_MASK          (0x7f<<8) +#       define RADEON_GRPH2_STOP_REQ_SHIFT         8 +#       define RADEON_GRPH2_CRITICAL_POINT_MASK    (0x7f<<16) +#       define RADEON_GRPH2_CRITICAL_POINT_SHIFT   16 +#       define RADEON_GRPH2_CRITICAL_CNTL          (1<<28) +#       define RADEON_GRPH2_BUFFER_SIZE            (1<<29) +#       define RADEON_GRPH2_CRITICAL_AT_SOF        (1<<30) +#       define RADEON_GRPH2_STOP_CNTL              (1<<31) +#define RADEON_CRTC_CRNT_FRAME              0x0214 +#define RADEON_CRTC_EXT_CNTL                0x0054 +#       define RADEON_CRTC_VGA_XOVERSCAN    (1 <<  0) +#       define RADEON_VGA_ATI_LINEAR        (1 <<  3) +#       define RADEON_XCRT_CNT_EN           (1 <<  6) +#       define RADEON_CRTC_HSYNC_DIS        (1 <<  8) +#       define RADEON_CRTC_VSYNC_DIS        (1 <<  9) +#       define RADEON_CRTC_DISPLAY_DIS      (1 << 10) +#       define RADEON_CRTC_SYNC_TRISTAT     (1 << 11) +#       define RADEON_CRTC_CRT_ON           (1 << 15) +#define RADEON_CRTC_EXT_CNTL_DPMS_BYTE      0x0055 +#       define RADEON_CRTC_HSYNC_DIS_BYTE   (1 <<  0) +#       define RADEON_CRTC_VSYNC_DIS_BYTE   (1 <<  1) +#       define RADEON_CRTC_DISPLAY_DIS_BYTE (1 <<  2) +#define RADEON_CRTC_GEN_CNTL                0x0050 +#       define RADEON_CRTC_DBL_SCAN_EN      (1 <<  0) +#       define RADEON_CRTC_INTERLACE_EN     (1 <<  1) +#       define RADEON_CRTC_CSYNC_EN         (1 <<  4) +#       define RADEON_CRTC_ICON_EN          (1 << 15) +#       define RADEON_CRTC_CUR_EN           (1 << 16) +#       define RADEON_CRTC_CUR_MODE_MASK    (7 << 20) +#       define RADEON_CRTC_EXT_DISP_EN      (1 << 24) +#       define RADEON_CRTC_EN               (1 << 25) +#       define RADEON_CRTC_DISP_REQ_EN_B    (1 << 26) +#define RADEON_CRTC2_GEN_CNTL               0x03f8 +#       define RADEON_CRTC2_DBL_SCAN_EN     (1 <<  0) +#       define RADEON_CRTC2_INTERLACE_EN    (1 <<  1) +#       define RADEON_CRTC2_SYNC_TRISTAT    (1 <<  4) +#       define RADEON_CRTC2_HSYNC_TRISTAT   (1 <<  5) +#       define RADEON_CRTC2_VSYNC_TRISTAT   (1 <<  6) +#       define RADEON_CRTC2_CRT2_ON         (1 <<  7) +#       define RADEON_CRTC2_PIX_WIDTH_SHIFT 8 +#       define RADEON_CRTC2_PIX_WIDTH_MASK  (0xf << 8) +#       define RADEON_CRTC2_ICON_EN         (1 << 15) +#       define RADEON_CRTC2_CUR_EN          (1 << 16) +#       define RADEON_CRTC2_CUR_MODE_MASK   (7 << 20) +#       define RADEON_CRTC2_DISP_DIS        (1 << 23) +#       define RADEON_CRTC2_EN              (1 << 25) +#       define RADEON_CRTC2_DISP_REQ_EN_B   (1 << 26) +#       define RADEON_CRTC2_CSYNC_EN        (1 << 27) +#       define RADEON_CRTC2_HSYNC_DIS       (1 << 28) +#       define RADEON_CRTC2_VSYNC_DIS       (1 << 29) +#define RADEON_CRTC_MORE_CNTL               0x27c +#       define RADEON_CRTC_AUTO_HORZ_CENTER_EN (1<<2) +#       define RADEON_CRTC_AUTO_VERT_CENTER_EN (1<<3) +#       define RADEON_CRTC_H_CUTOFF_ACTIVE_EN (1<<4) +#       define RADEON_CRTC_V_CUTOFF_ACTIVE_EN (1<<5) +#define RADEON_CRTC_GUI_TRIG_VLINE          0x0218 +#define RADEON_CRTC_H_SYNC_STRT_WID         0x0204 +#       define RADEON_CRTC_H_SYNC_STRT_PIX        (0x07  <<  0) +#       define RADEON_CRTC_H_SYNC_STRT_CHAR       (0x3ff <<  3) +#       define RADEON_CRTC_H_SYNC_STRT_CHAR_SHIFT 3 +#       define RADEON_CRTC_H_SYNC_WID             (0x3f  << 16) +#       define RADEON_CRTC_H_SYNC_WID_SHIFT       16 +#       define RADEON_CRTC_H_SYNC_POL             (1     << 23) +#define RADEON_CRTC2_H_SYNC_STRT_WID        0x0304 +#       define RADEON_CRTC2_H_SYNC_STRT_PIX        (0x07  <<  0) +#       define RADEON_CRTC2_H_SYNC_STRT_CHAR       (0x3ff <<  3) +#       define RADEON_CRTC2_H_SYNC_STRT_CHAR_SHIFT 3 +#       define RADEON_CRTC2_H_SYNC_WID             (0x3f  << 16) +#       define RADEON_CRTC2_H_SYNC_WID_SHIFT       16 +#       define RADEON_CRTC2_H_SYNC_POL             (1     << 23) +#define RADEON_CRTC_H_TOTAL_DISP            0x0200 +#       define RADEON_CRTC_H_TOTAL          (0x03ff << 0) +#       define RADEON_CRTC_H_TOTAL_SHIFT    0 +#       define RADEON_CRTC_H_DISP           (0x01ff << 16) +#       define RADEON_CRTC_H_DISP_SHIFT     16 +#define RADEON_CRTC2_H_TOTAL_DISP           0x0300 +#       define RADEON_CRTC2_H_TOTAL         (0x03ff << 0) +#       define RADEON_CRTC2_H_TOTAL_SHIFT   0 +#       define RADEON_CRTC2_H_DISP          (0x01ff << 16) +#       define RADEON_CRTC2_H_DISP_SHIFT    16 + +#define RADEON_CRTC_OFFSET_RIGHT	    0x0220 +#define RADEON_CRTC_OFFSET                  0x0224 +#	define RADEON_CRTC_OFFSET__GUI_TRIG_OFFSET (1<<30) +#	define RADEON_CRTC_OFFSET__OFFSET_LOCK 	   (1<<31) + +#define RADEON_CRTC2_OFFSET                 0x0324 +#	define RADEON_CRTC2_OFFSET__GUI_TRIG_OFFSET (1<<30) +#	define RADEON_CRTC2_OFFSET__OFFSET_LOCK	    (1<<31) +#define RADEON_CRTC_OFFSET_CNTL             0x0228 +#       define RADEON_CRTC_TILE_LINE_SHIFT              0 +#       define RADEON_CRTC_TILE_LINE_RIGHT_SHIFT        4 +#	define R300_CRTC_X_Y_MODE_EN_RIGHT		(1 << 6) +#	define R300_CRTC_MICRO_TILE_BUFFER_RIGHT_MASK   (3 << 7) +#	define R300_CRTC_MICRO_TILE_BUFFER_RIGHT_AUTO   (0 << 7) +#	define R300_CRTC_MICRO_TILE_BUFFER_RIGHT_SINGLE (1 << 7) +#	define R300_CRTC_MICRO_TILE_BUFFER_RIGHT_DOUBLE (2 << 7) +#	define R300_CRTC_MICRO_TILE_BUFFER_RIGHT_DIS    (3 << 7) +#	define R300_CRTC_X_Y_MODE_EN			(1 << 9) +#	define R300_CRTC_MICRO_TILE_BUFFER_MASK   	(3 << 10) +#	define R300_CRTC_MICRO_TILE_BUFFER_AUTO   	(0 << 10) +#	define R300_CRTC_MICRO_TILE_BUFFER_SINGLE 	(1 << 10) +#	define R300_CRTC_MICRO_TILE_BUFFER_DOUBLE 	(2 << 10) +#	define R300_CRTC_MICRO_TILE_BUFFER_DIS    	(3 << 10) +#	define R300_CRTC_MICRO_TILE_EN_RIGHT		(1 << 12) +#	define R300_CRTC_MICRO_TILE_EN			(1 << 13) +#	define R300_CRTC_MACRO_TILE_EN_RIGHT		(1 << 14) +#       define R300_CRTC_MACRO_TILE_EN                  (1 << 15) +#       define RADEON_CRTC_TILE_EN_RIGHT                (1 << 14) +#       define RADEON_CRTC_TILE_EN                      (1 << 15) +#       define RADEON_CRTC_OFFSET_FLIP_CNTL             (1 << 16) +#       define RADEON_CRTC_STEREO_OFFSET_EN             (1 << 17) + +#define R300_CRTC_TILE_X0_Y0	            0x0350 +#define R300_CRTC2_TILE_X0_Y0	            0x0358 + +#define RADEON_CRTC2_OFFSET_CNTL            0x0328 +#       define RADEON_CRTC2_OFFSET_FLIP_CNTL (1 << 16) +#       define RADEON_CRTC2_TILE_EN         (1 << 15) +#define RADEON_CRTC_PITCH                   0x022c +#	define RADEON_CRTC_PITCH__SHIFT		 0 +#	define RADEON_CRTC_PITCH__RIGHT_SHIFT	16 + +#define RADEON_CRTC2_PITCH                  0x032c +#define RADEON_CRTC_STATUS                  0x005c +#       define RADEON_CRTC_VBLANK_SAVE      (1 <<  1) +#       define RADEON_CRTC_VBLANK_SAVE_CLEAR  (1 <<  1) +#define RADEON_CRTC2_STATUS                  0x03fc +#       define RADEON_CRTC2_VBLANK_SAVE      (1 <<  1) +#       define RADEON_CRTC2_VBLANK_SAVE_CLEAR  (1 <<  1) +#define RADEON_CRTC_V_SYNC_STRT_WID         0x020c +#       define RADEON_CRTC_V_SYNC_STRT        (0x7ff <<  0) +#       define RADEON_CRTC_V_SYNC_STRT_SHIFT  0 +#       define RADEON_CRTC_V_SYNC_WID         (0x1f  << 16) +#       define RADEON_CRTC_V_SYNC_WID_SHIFT   16 +#       define RADEON_CRTC_V_SYNC_POL         (1     << 23) +#define RADEON_CRTC2_V_SYNC_STRT_WID        0x030c +#       define RADEON_CRTC2_V_SYNC_STRT       (0x7ff <<  0) +#       define RADEON_CRTC2_V_SYNC_STRT_SHIFT 0 +#       define RADEON_CRTC2_V_SYNC_WID        (0x1f  << 16) +#       define RADEON_CRTC2_V_SYNC_WID_SHIFT  16 +#       define RADEON_CRTC2_V_SYNC_POL        (1     << 23) +#define RADEON_CRTC_V_TOTAL_DISP            0x0208 +#       define RADEON_CRTC_V_TOTAL          (0x07ff << 0) +#       define RADEON_CRTC_V_TOTAL_SHIFT    0 +#       define RADEON_CRTC_V_DISP           (0x07ff << 16) +#       define RADEON_CRTC_V_DISP_SHIFT     16 +#define RADEON_CRTC2_V_TOTAL_DISP           0x0308 +#       define RADEON_CRTC2_V_TOTAL         (0x07ff << 0) +#       define RADEON_CRTC2_V_TOTAL_SHIFT   0 +#       define RADEON_CRTC2_V_DISP          (0x07ff << 16) +#       define RADEON_CRTC2_V_DISP_SHIFT    16 +#define RADEON_CRTC_VLINE_CRNT_VLINE        0x0210 +#       define RADEON_CRTC_CRNT_VLINE_MASK  (0x7ff << 16) +#define RADEON_CRTC2_CRNT_FRAME             0x0314 +#define RADEON_CRTC2_GUI_TRIG_VLINE         0x0318 +#define RADEON_CRTC2_STATUS                 0x03fc +#define RADEON_CRTC2_VLINE_CRNT_VLINE       0x0310 +#define RADEON_CRTC8_DATA                   0x03d5 /* VGA, 0x3b5 */ +#define RADEON_CRTC8_IDX                    0x03d4 /* VGA, 0x3b4 */ +#define RADEON_CUR_CLR0                     0x026c +#define RADEON_CUR_CLR1                     0x0270 +#define RADEON_CUR_HORZ_VERT_OFF            0x0268 +#define RADEON_CUR_HORZ_VERT_POSN           0x0264 +#define RADEON_CUR_OFFSET                   0x0260 +#       define RADEON_CUR_LOCK              (1 << 31) +#define RADEON_CUR2_CLR0                    0x036c +#define RADEON_CUR2_CLR1                    0x0370 +#define RADEON_CUR2_HORZ_VERT_OFF           0x0368 +#define RADEON_CUR2_HORZ_VERT_POSN          0x0364 +#define RADEON_CUR2_OFFSET                  0x0360 +#       define RADEON_CUR2_LOCK             (1 << 31) + +#define RADEON_DAC_CNTL                     0x0058 +#       define RADEON_DAC_RANGE_CNTL        (3 <<  0) +#       define RADEON_DAC_RANGE_CNTL_PS2    (2 <<  0) +#       define RADEON_DAC_RANGE_CNTL_MASK   0x03 +#       define RADEON_DAC_BLANKING          (1 <<  2) +#       define RADEON_DAC_CMP_EN            (1 <<  3) +#       define RADEON_DAC_CMP_OUTPUT        (1 <<  7) +#       define RADEON_DAC_8BIT_EN           (1 <<  8) +#       define RADEON_DAC_TVO_EN            (1 << 10) +#       define RADEON_DAC_VGA_ADR_EN        (1 << 13) +#       define RADEON_DAC_PDWN              (1 << 15) +#       define RADEON_DAC_MASK_ALL          (0xff << 24) +#define RADEON_DAC_CNTL2                    0x007c +#       define RADEON_DAC2_TV_CLK_SEL       (0 <<  1) +#       define RADEON_DAC2_DAC_CLK_SEL      (1 <<  0) +#       define RADEON_DAC2_DAC2_CLK_SEL     (1 <<  1) +#       define RADEON_DAC2_PALETTE_ACC_CTL  (1 <<  5) +#       define RADEON_DAC2_CMP_EN           (1 <<  7) +#       define RADEON_DAC2_CMP_OUT_R        (1 <<  8) +#       define RADEON_DAC2_CMP_OUT_G        (1 <<  9) +#       define RADEON_DAC2_CMP_OUT_B        (1 << 10) +#       define RADEON_DAC2_CMP_OUTPUT       (1 << 11) +#define RADEON_DAC_EXT_CNTL                 0x0280 +#       define RADEON_DAC2_FORCE_BLANK_OFF_EN (1 << 0) +#       define RADEON_DAC2_FORCE_DATA_EN      (1 << 1) +#       define RADEON_DAC_FORCE_BLANK_OFF_EN  (1 << 4) +#       define RADEON_DAC_FORCE_DATA_EN       (1 << 5) +#       define RADEON_DAC_FORCE_DATA_SEL_MASK (3 << 6) +#       define RADEON_DAC_FORCE_DATA_SEL_R    (0 << 6) +#       define RADEON_DAC_FORCE_DATA_SEL_G    (1 << 6) +#       define RADEON_DAC_FORCE_DATA_SEL_B    (2 << 6) +#       define RADEON_DAC_FORCE_DATA_SEL_RGB  (3 << 6) +#       define RADEON_DAC_FORCE_DATA_MASK   0x0003ff00 +#       define RADEON_DAC_FORCE_DATA_SHIFT  8 +#define RADEON_DAC_MACRO_CNTL               0x0d04 +#       define RADEON_DAC_PDWN_R            (1 << 16) +#       define RADEON_DAC_PDWN_G            (1 << 17) +#       define RADEON_DAC_PDWN_B            (1 << 18) +#define RADEON_TV_DAC_CNTL                  0x088c +#       define RADEON_TV_DAC_NBLANK         (1 << 0) +#       define RADEON_TV_DAC_NHOLD          (1 << 1) +#       define RADEON_TV_DAC_PEDESTAL       (1 <<  2) +#       define RADEON_TV_MONITOR_DETECT_EN  (1 <<  4) +#       define RADEON_TV_DAC_CMPOUT         (1 <<  5) +#       define RADEON_TV_DAC_STD_MASK       (3 <<  8) +#       define RADEON_TV_DAC_STD_PAL        (0 <<  8) +#       define RADEON_TV_DAC_STD_NTSC       (1 <<  8) +#       define RADEON_TV_DAC_STD_PS2        (2 <<  8) +#       define RADEON_TV_DAC_STD_RS343      (3 <<  8) +#       define RADEON_TV_DAC_BGSLEEP        (1 <<  6) +#       define RADEON_TV_DAC_BGADJ_MASK     (0xf <<  16) +#       define RADEON_TV_DAC_BGADJ_SHIFT    16 +#       define RADEON_TV_DAC_DACADJ_MASK    (0xf <<  20) +#       define RADEON_TV_DAC_DACADJ_SHIFT   20 +#       define RADEON_TV_DAC_RDACPD         (1 <<  24) +#       define RADEON_TV_DAC_GDACPD         (1 <<  25) +#       define RADEON_TV_DAC_BDACPD         (1 <<  26) +#       define RADEON_TV_DAC_RDACDET        (1 << 29) +#       define RADEON_TV_DAC_GDACDET        (1 << 30) +#       define RADEON_TV_DAC_BDACDET        (1 << 31) +#       define R420_TV_DAC_DACADJ_MASK      (0x1f <<  20) +#       define R420_TV_DAC_RDACPD           (1 <<  25) +#       define R420_TV_DAC_GDACPD           (1 <<  26) +#       define R420_TV_DAC_BDACPD           (1 <<  27) +#       define R420_TV_DAC_TVENABLE         (1 <<  28) +#define RADEON_DISP_HW_DEBUG                0x0d14 +#       define RADEON_CRT2_DISP1_SEL        (1 <<  5) +#define RADEON_DISP_OUTPUT_CNTL             0x0d64 +#       define RADEON_DISP_DAC_SOURCE_MASK  0x03 +#       define RADEON_DISP_DAC2_SOURCE_MASK  0x0c +#       define RADEON_DISP_DAC_SOURCE_CRTC2 0x01 +#       define RADEON_DISP_DAC_SOURCE_RMX   0x02 +#       define RADEON_DISP_DAC_SOURCE_LTU   0x03 +#       define RADEON_DISP_DAC2_SOURCE_CRTC2 0x04 +#       define RADEON_DISP_TVDAC_SOURCE_MASK  (0x03 << 2) +#       define RADEON_DISP_TVDAC_SOURCE_CRTC  0x0 +#       define RADEON_DISP_TVDAC_SOURCE_CRTC2 (0x01 << 2) +#       define RADEON_DISP_TVDAC_SOURCE_RMX   (0x02 << 2) +#       define RADEON_DISP_TVDAC_SOURCE_LTU   (0x03 << 2) +#       define RADEON_DISP_TRANS_MATRIX_MASK  (0x03 << 4) +#       define RADEON_DISP_TRANS_MATRIX_ALPHA_MSB (0x00 << 4) +#       define RADEON_DISP_TRANS_MATRIX_GRAPHICS  (0x01 << 4) +#       define RADEON_DISP_TRANS_MATRIX_VIDEO     (0x02 << 4) +#       define RADEON_DISP_TV_SOURCE_CRTC   (1 << 16) /* crtc1 or crtc2 */ +#       define RADEON_DISP_TV_SOURCE_LTU    (0 << 16) /* linear transform unit */ +#define RADEON_DISP_TV_OUT_CNTL             0x0d6c +#       define RADEON_DISP_TV_PATH_SRC_CRTC2 (1 << 16) +#       define RADEON_DISP_TV_PATH_SRC_CRTC1 (0 << 16) +#define RADEON_DAC_CRC_SIG                  0x02cc +#define RADEON_DAC_DATA                     0x03c9 /* VGA */ +#define RADEON_DAC_MASK                     0x03c6 /* VGA */ +#define RADEON_DAC_R_INDEX                  0x03c7 /* VGA */ +#define RADEON_DAC_W_INDEX                  0x03c8 /* VGA */ +#define RADEON_DDA_CONFIG                   0x02e0 +#define RADEON_DDA_ON_OFF                   0x02e4 +#define RADEON_DEFAULT_OFFSET               0x16e0 +#define RADEON_DEFAULT_PITCH                0x16e4 +#define RADEON_DEFAULT_SC_BOTTOM_RIGHT      0x16e8 +#       define RADEON_DEFAULT_SC_RIGHT_MAX  (0x1fff <<  0) +#       define RADEON_DEFAULT_SC_BOTTOM_MAX (0x1fff << 16) +#define RADEON_DESTINATION_3D_CLR_CMP_VAL   0x1820 +#define RADEON_DESTINATION_3D_CLR_CMP_MSK   0x1824 +#define RADEON_DEVICE_ID                    0x0f02 /* PCI */ +#define RADEON_DISP_MISC_CNTL               0x0d00 +#       define RADEON_SOFT_RESET_GRPH_PP    (1 << 0) +#define RADEON_DISP_MERGE_CNTL		  0x0d60 +#       define RADEON_DISP_ALPHA_MODE_MASK  0x03 +#       define RADEON_DISP_ALPHA_MODE_KEY   0 +#       define RADEON_DISP_ALPHA_MODE_PER_PIXEL 1 +#       define RADEON_DISP_ALPHA_MODE_GLOBAL 2 +#       define RADEON_DISP_RGB_OFFSET_EN    (1 << 8) +#       define RADEON_DISP_GRPH_ALPHA_MASK  (0xff << 16) +#       define RADEON_DISP_OV0_ALPHA_MASK   (0xff << 24) +#	define RADEON_DISP_LIN_TRANS_BYPASS (0x01 << 9) +#define RADEON_DISP2_MERGE_CNTL		    0x0d68 +#       define RADEON_DISP2_RGB_OFFSET_EN   (1 << 8) +#define RADEON_DISP_LIN_TRANS_GRPH_A        0x0d80 +#define RADEON_DISP_LIN_TRANS_GRPH_B        0x0d84 +#define RADEON_DISP_LIN_TRANS_GRPH_C        0x0d88 +#define RADEON_DISP_LIN_TRANS_GRPH_D        0x0d8c +#define RADEON_DISP_LIN_TRANS_GRPH_E        0x0d90 +#define RADEON_DISP_LIN_TRANS_GRPH_F        0x0d98 +#define RADEON_DP_BRUSH_BKGD_CLR            0x1478 +#define RADEON_DP_BRUSH_FRGD_CLR            0x147c +#define RADEON_DP_CNTL                      0x16c0 +#       define RADEON_DST_X_LEFT_TO_RIGHT   (1 <<  0) +#       define RADEON_DST_Y_TOP_TO_BOTTOM   (1 <<  1) +#       define RADEON_DP_DST_TILE_LINEAR    (0 <<  3) +#       define RADEON_DP_DST_TILE_MACRO     (1 <<  3) +#       define RADEON_DP_DST_TILE_MICRO     (2 <<  3) +#       define RADEON_DP_DST_TILE_BOTH      (3 <<  3) +#define RADEON_DP_CNTL_XDIR_YDIR_YMAJOR     0x16d0 +#       define RADEON_DST_Y_MAJOR             (1 <<  2) +#       define RADEON_DST_Y_DIR_TOP_TO_BOTTOM (1 << 15) +#       define RADEON_DST_X_DIR_LEFT_TO_RIGHT (1 << 31) +#define RADEON_DP_DATATYPE                  0x16c4 +#       define RADEON_HOST_BIG_ENDIAN_EN    (1 << 29) +#define RADEON_DP_GUI_MASTER_CNTL           0x146c +#       define RADEON_GMC_SRC_PITCH_OFFSET_CNTL   (1    <<  0) +#       define RADEON_GMC_DST_PITCH_OFFSET_CNTL   (1    <<  1) +#       define RADEON_GMC_SRC_CLIPPING            (1    <<  2) +#       define RADEON_GMC_DST_CLIPPING            (1    <<  3) +#       define RADEON_GMC_BRUSH_DATATYPE_MASK     (0x0f <<  4) +#       define RADEON_GMC_BRUSH_8X8_MONO_FG_BG    (0    <<  4) +#       define RADEON_GMC_BRUSH_8X8_MONO_FG_LA    (1    <<  4) +#       define RADEON_GMC_BRUSH_1X8_MONO_FG_BG    (4    <<  4) +#       define RADEON_GMC_BRUSH_1X8_MONO_FG_LA    (5    <<  4) +#       define RADEON_GMC_BRUSH_32x1_MONO_FG_BG   (6    <<  4) +#       define RADEON_GMC_BRUSH_32x1_MONO_FG_LA   (7    <<  4) +#       define RADEON_GMC_BRUSH_32x32_MONO_FG_BG  (8    <<  4) +#       define RADEON_GMC_BRUSH_32x32_MONO_FG_LA  (9    <<  4) +#       define RADEON_GMC_BRUSH_8x8_COLOR         (10   <<  4) +#       define RADEON_GMC_BRUSH_1X8_COLOR         (12   <<  4) +#       define RADEON_GMC_BRUSH_SOLID_COLOR       (13   <<  4) +#       define RADEON_GMC_BRUSH_NONE              (15   <<  4) +#       define RADEON_GMC_DST_8BPP_CI             (2    <<  8) +#       define RADEON_GMC_DST_15BPP               (3    <<  8) +#       define RADEON_GMC_DST_16BPP               (4    <<  8) +#       define RADEON_GMC_DST_24BPP               (5    <<  8) +#       define RADEON_GMC_DST_32BPP               (6    <<  8) +#       define RADEON_GMC_DST_8BPP_RGB            (7    <<  8) +#       define RADEON_GMC_DST_Y8                  (8    <<  8) +#       define RADEON_GMC_DST_RGB8                (9    <<  8) +#       define RADEON_GMC_DST_VYUY                (11   <<  8) +#       define RADEON_GMC_DST_YVYU                (12   <<  8) +#       define RADEON_GMC_DST_AYUV444             (14   <<  8) +#       define RADEON_GMC_DST_ARGB4444            (15   <<  8) +#       define RADEON_GMC_DST_DATATYPE_MASK       (0x0f <<  8) +#       define RADEON_GMC_DST_DATATYPE_SHIFT      8 +#       define RADEON_GMC_SRC_DATATYPE_MASK       (3    << 12) +#       define RADEON_GMC_SRC_DATATYPE_MONO_FG_BG (0    << 12) +#       define RADEON_GMC_SRC_DATATYPE_MONO_FG_LA (1    << 12) +#       define RADEON_GMC_SRC_DATATYPE_COLOR      (3    << 12) +#       define RADEON_GMC_BYTE_PIX_ORDER          (1    << 14) +#       define RADEON_GMC_BYTE_MSB_TO_LSB         (0    << 14) +#       define RADEON_GMC_BYTE_LSB_TO_MSB         (1    << 14) +#       define RADEON_GMC_CONVERSION_TEMP         (1    << 15) +#       define RADEON_GMC_CONVERSION_TEMP_6500    (0    << 15) +#       define RADEON_GMC_CONVERSION_TEMP_9300    (1    << 15) +#       define RADEON_GMC_ROP3_MASK               (0xff << 16) +#       define RADEON_DP_SRC_SOURCE_MASK          (7    << 24) +#       define RADEON_DP_SRC_SOURCE_MEMORY        (2    << 24) +#       define RADEON_DP_SRC_SOURCE_HOST_DATA     (3    << 24) +#       define RADEON_GMC_3D_FCN_EN               (1    << 27) +#       define RADEON_GMC_CLR_CMP_CNTL_DIS        (1    << 28) +#       define RADEON_GMC_AUX_CLIP_DIS            (1    << 29) +#       define RADEON_GMC_WR_MSK_DIS              (1    << 30) +#       define RADEON_GMC_LD_BRUSH_Y_X            (1    << 31) +#       define RADEON_ROP3_ZERO             0x00000000 +#       define RADEON_ROP3_DSa              0x00880000 +#       define RADEON_ROP3_SDna             0x00440000 +#       define RADEON_ROP3_S                0x00cc0000 +#       define RADEON_ROP3_DSna             0x00220000 +#       define RADEON_ROP3_D                0x00aa0000 +#       define RADEON_ROP3_DSx              0x00660000 +#       define RADEON_ROP3_DSo              0x00ee0000 +#       define RADEON_ROP3_DSon             0x00110000 +#       define RADEON_ROP3_DSxn             0x00990000 +#       define RADEON_ROP3_Dn               0x00550000 +#       define RADEON_ROP3_SDno             0x00dd0000 +#       define RADEON_ROP3_Sn               0x00330000 +#       define RADEON_ROP3_DSno             0x00bb0000 +#       define RADEON_ROP3_DSan             0x00770000 +#       define RADEON_ROP3_ONE              0x00ff0000 +#       define RADEON_ROP3_DPa              0x00a00000 +#       define RADEON_ROP3_PDna             0x00500000 +#       define RADEON_ROP3_P                0x00f00000 +#       define RADEON_ROP3_DPna             0x000a0000 +#       define RADEON_ROP3_D                0x00aa0000 +#       define RADEON_ROP3_DPx              0x005a0000 +#       define RADEON_ROP3_DPo              0x00fa0000 +#       define RADEON_ROP3_DPon             0x00050000 +#       define RADEON_ROP3_PDxn             0x00a50000 +#       define RADEON_ROP3_PDno             0x00f50000 +#       define RADEON_ROP3_Pn               0x000f0000 +#       define RADEON_ROP3_DPno             0x00af0000 +#       define RADEON_ROP3_DPan             0x005f0000 +#define RADEON_DP_GUI_MASTER_CNTL_C         0x1c84 +#define RADEON_DP_MIX                       0x16c8 +#define RADEON_DP_SRC_BKGD_CLR              0x15dc +#define RADEON_DP_SRC_FRGD_CLR              0x15d8 +#define RADEON_DP_WRITE_MASK                0x16cc +#define RADEON_DST_BRES_DEC                 0x1630 +#define RADEON_DST_BRES_ERR                 0x1628 +#define RADEON_DST_BRES_INC                 0x162c +#define RADEON_DST_BRES_LNTH                0x1634 +#define RADEON_DST_BRES_LNTH_SUB            0x1638 +#define RADEON_DST_HEIGHT                   0x1410 +#define RADEON_DST_HEIGHT_WIDTH             0x143c +#define RADEON_DST_HEIGHT_WIDTH_8           0x158c +#define RADEON_DST_HEIGHT_WIDTH_BW          0x15b4 +#define RADEON_DST_HEIGHT_Y                 0x15a0 +#define RADEON_DST_LINE_START               0x1600 +#define RADEON_DST_LINE_END                 0x1604 +#define RADEON_DST_LINE_PATCOUNT            0x1608 +#       define RADEON_BRES_CNTL_SHIFT       8 +#define RADEON_DST_OFFSET                   0x1404 +#define RADEON_DST_PITCH                    0x1408 +#define RADEON_DST_PITCH_OFFSET             0x142c +#define RADEON_DST_PITCH_OFFSET_C           0x1c80 +#       define RADEON_PITCH_SHIFT           21 +#       define RADEON_DST_TILE_LINEAR       (0 << 30) +#       define RADEON_DST_TILE_MACRO        (1 << 30) +#       define RADEON_DST_TILE_MICRO        (2 << 30) +#       define RADEON_DST_TILE_BOTH         (3 << 30) +#define RADEON_DST_WIDTH                    0x140c +#define RADEON_DST_WIDTH_HEIGHT             0x1598 +#define RADEON_DST_WIDTH_X                  0x1588 +#define RADEON_DST_WIDTH_X_INCY             0x159c +#define RADEON_DST_X                        0x141c +#define RADEON_DST_X_SUB                    0x15a4 +#define RADEON_DST_X_Y                      0x1594 +#define RADEON_DST_Y                        0x1420 +#define RADEON_DST_Y_SUB                    0x15a8 +#define RADEON_DST_Y_X                      0x1438 + +#define RADEON_FCP_CNTL                     0x0910 +#      define RADEON_FCP0_SRC_PCICLK             0 +#      define RADEON_FCP0_SRC_PCLK               1 +#      define RADEON_FCP0_SRC_PCLKb              2 +#      define RADEON_FCP0_SRC_HREF               3 +#      define RADEON_FCP0_SRC_GND                4 +#      define RADEON_FCP0_SRC_HREFb              5 +#define RADEON_FLUSH_1                      0x1704 +#define RADEON_FLUSH_2                      0x1708 +#define RADEON_FLUSH_3                      0x170c +#define RADEON_FLUSH_4                      0x1710 +#define RADEON_FLUSH_5                      0x1714 +#define RADEON_FLUSH_6                      0x1718 +#define RADEON_FLUSH_7                      0x171c +#define RADEON_FOG_3D_TABLE_START           0x1810 +#define RADEON_FOG_3D_TABLE_END             0x1814 +#define RADEON_FOG_3D_TABLE_DENSITY         0x181c +#define RADEON_FOG_TABLE_INDEX              0x1a14 +#define RADEON_FOG_TABLE_DATA               0x1a18 +#define RADEON_FP_CRTC_H_TOTAL_DISP         0x0250 +#define RADEON_FP_CRTC_V_TOTAL_DISP         0x0254 +#       define RADEON_FP_CRTC_H_TOTAL_MASK      0x000003ff +#       define RADEON_FP_CRTC_H_DISP_MASK       0x01ff0000 +#       define RADEON_FP_CRTC_V_TOTAL_MASK      0x00000fff +#       define RADEON_FP_CRTC_V_DISP_MASK       0x0fff0000 +#       define RADEON_FP_H_SYNC_STRT_CHAR_MASK  0x00001ff8 +#       define RADEON_FP_H_SYNC_WID_MASK        0x003f0000 +#       define RADEON_FP_V_SYNC_STRT_MASK       0x00000fff +#       define RADEON_FP_V_SYNC_WID_MASK        0x001f0000 +#       define RADEON_FP_CRTC_H_TOTAL_SHIFT     0x00000000 +#       define RADEON_FP_CRTC_H_DISP_SHIFT      0x00000010 +#       define RADEON_FP_CRTC_V_TOTAL_SHIFT     0x00000000 +#       define RADEON_FP_CRTC_V_DISP_SHIFT      0x00000010 +#       define RADEON_FP_H_SYNC_STRT_CHAR_SHIFT 0x00000003 +#       define RADEON_FP_H_SYNC_WID_SHIFT       0x00000010 +#       define RADEON_FP_V_SYNC_STRT_SHIFT      0x00000000 +#       define RADEON_FP_V_SYNC_WID_SHIFT       0x00000010 +#define RADEON_FP_GEN_CNTL                  0x0284 +#       define RADEON_FP_FPON                  (1 <<  0) +#       define RADEON_FP_BLANK_EN              (1 <<  1) +#       define RADEON_FP_TMDS_EN               (1 <<  2) +#       define RADEON_FP_PANEL_FORMAT          (1 <<  3) +#       define RADEON_FP_EN_TMDS               (1 <<  7) +#       define RADEON_FP_DETECT_SENSE          (1 <<  8) +#       define R200_FP_SOURCE_SEL_MASK         (3 <<  10) +#       define R200_FP_SOURCE_SEL_CRTC1        (0 <<  10) +#       define R200_FP_SOURCE_SEL_CRTC2        (1 <<  10) +#       define R200_FP_SOURCE_SEL_RMX          (2 <<  10) +#       define R200_FP_SOURCE_SEL_TRANS        (3 <<  10) +#       define RADEON_FP_SEL_CRTC1             (0 << 13) +#       define RADEON_FP_SEL_CRTC2             (1 << 13) +#       define RADEON_FP_CRTC_DONT_SHADOW_HPAR (1 << 15) +#       define RADEON_FP_CRTC_DONT_SHADOW_VPAR (1 << 16) +#       define RADEON_FP_CRTC_DONT_SHADOW_HEND (1 << 17) +#       define RADEON_FP_CRTC_USE_SHADOW_VEND  (1 << 18) +#       define RADEON_FP_RMX_HVSYNC_CONTROL_EN (1 << 20) +#       define RADEON_FP_DFP_SYNC_SEL          (1 << 21) +#       define RADEON_FP_CRTC_LOCK_8DOT        (1 << 22) +#       define RADEON_FP_CRT_SYNC_SEL          (1 << 23) +#       define RADEON_FP_USE_SHADOW_EN         (1 << 24) +#       define RADEON_FP_CRT_SYNC_ALT          (1 << 26) +#define RADEON_FP2_GEN_CNTL                 0x0288 +#       define RADEON_FP2_BLANK_EN             (1 <<  1) +#       define RADEON_FP2_ON                   (1 <<  2) +#       define RADEON_FP2_PANEL_FORMAT         (1 <<  3) +#       define RADEON_FP2_DETECT_SENSE         (1 <<  8) +#       define R200_FP2_SOURCE_SEL_MASK        (3 << 10) +#       define R200_FP2_SOURCE_SEL_CRTC1       (0 << 10) +#       define R200_FP2_SOURCE_SEL_CRTC2       (1 << 10) +#       define R200_FP2_SOURCE_SEL_RMX         (2 << 10) +#       define R200_FP2_SOURCE_SEL_TRANS_UNIT  (3 << 10) +#       define RADEON_FP2_SRC_SEL_MASK         (3 << 13) +#       define RADEON_FP2_SRC_SEL_CRTC2        (1 << 13) +#       define RADEON_FP2_FP_POL               (1 << 16) +#       define RADEON_FP2_LP_POL               (1 << 17) +#       define RADEON_FP2_SCK_POL              (1 << 18) +#       define RADEON_FP2_LCD_CNTL_MASK        (7 << 19) +#       define RADEON_FP2_PAD_FLOP_EN          (1 << 22) +#       define RADEON_FP2_CRC_EN               (1 << 23) +#       define RADEON_FP2_CRC_READ_EN          (1 << 24) +#       define RADEON_FP2_DVO_EN               (1 << 25) +#       define RADEON_FP2_DVO_RATE_SEL_SDR     (1 << 26) +#       define R200_FP2_DVO_RATE_SEL_SDR       (1 << 27) +#       define R300_FP2_DVO_CLOCK_MODE_SINGLE  (1 << 28) +#       define R300_FP2_DVO_DUAL_CHANNEL_EN    (1 << 29) +#define RADEON_FP_H_SYNC_STRT_WID           0x02c4 +#define RADEON_FP_H2_SYNC_STRT_WID          0x03c4 +#define RADEON_FP_HORZ_STRETCH              0x028c +#define RADEON_FP_HORZ2_STRETCH             0x038c +#       define RADEON_HORZ_STRETCH_RATIO_MASK 0xffff +#       define RADEON_HORZ_STRETCH_RATIO_MAX  4096 +#       define RADEON_HORZ_PANEL_SIZE         (0x1ff   << 16) +#       define RADEON_HORZ_PANEL_SHIFT        16 +#       define RADEON_HORZ_STRETCH_PIXREP     (0      << 25) +#       define RADEON_HORZ_STRETCH_BLEND      (1      << 26) +#       define RADEON_HORZ_STRETCH_ENABLE     (1      << 25) +#       define RADEON_HORZ_AUTO_RATIO         (1      << 27) +#       define RADEON_HORZ_FP_LOOP_STRETCH    (0x7    << 28) +#       define RADEON_HORZ_AUTO_RATIO_INC     (1      << 31) +#define RADEON_FP_HORZ_VERT_ACTIVE          0x0278 +#define RADEON_FP_V_SYNC_STRT_WID           0x02c8 +#define RADEON_FP_VERT_STRETCH              0x0290 +#define RADEON_FP_V2_SYNC_STRT_WID          0x03c8 +#define RADEON_FP_VERT2_STRETCH             0x0390 +#       define RADEON_VERT_PANEL_SIZE          (0xfff << 12) +#       define RADEON_VERT_PANEL_SHIFT         12 +#       define RADEON_VERT_STRETCH_RATIO_MASK  0xfff +#       define RADEON_VERT_STRETCH_RATIO_SHIFT 0 +#       define RADEON_VERT_STRETCH_RATIO_MAX   4096 +#       define RADEON_VERT_STRETCH_ENABLE      (1     << 25) +#       define RADEON_VERT_STRETCH_LINEREP     (0     << 26) +#       define RADEON_VERT_STRETCH_BLEND       (1     << 26) +#       define RADEON_VERT_AUTO_RATIO_EN       (1     << 27) +#	define RADEON_VERT_AUTO_RATIO_INC      (1     << 31) +#       define RADEON_VERT_STRETCH_RESERVED    0x71000000 +#define RS400_FP_2ND_GEN_CNTL               0x0384 +#       define RS400_FP_2ND_ON              (1 << 0) +#       define RS400_FP_2ND_BLANK_EN        (1 << 1) +#       define RS400_TMDS_2ND_EN            (1 << 2) +#       define RS400_PANEL_FORMAT_2ND       (1 << 3) +#       define RS400_FP_2ND_EN_TMDS         (1 << 7) +#       define RS400_FP_2ND_DETECT_SENSE    (1 << 8) +#       define RS400_FP_2ND_SOURCE_SEL_MASK        (3 << 10) +#       define RS400_FP_2ND_SOURCE_SEL_CRTC1       (0 << 10) +#       define RS400_FP_2ND_SOURCE_SEL_CRTC2       (1 << 10) +#       define RS400_FP_2ND_SOURCE_SEL_RMX         (2 << 10) +#       define RS400_FP_2ND_DETECT_EN       (1 << 12) +#       define RS400_HPD_2ND_SEL            (1 << 13) +#define RS400_FP2_2_GEN_CNTL                0x0388 +#       define RS400_FP2_2_BLANK_EN         (1 << 1) +#       define RS400_FP2_2_ON               (1 << 2) +#       define RS400_FP2_2_PANEL_FORMAT     (1 << 3) +#       define RS400_FP2_2_DETECT_SENSE     (1 << 8) +#       define RS400_FP2_2_SOURCE_SEL_MASK        (3 << 10) +#       define RS400_FP2_2_SOURCE_SEL_CRTC1       (0 << 10) +#       define RS400_FP2_2_SOURCE_SEL_CRTC2       (1 << 10) +#       define RS400_FP2_2_SOURCE_SEL_RMX         (2 << 10) +#       define RS400_FP2_2_DVO2_EN          (1 << 25) +#define RS400_TMDS2_CNTL                    0x0394 +#define RS400_TMDS2_TRANSMITTER_CNTL        0x03a4 +#       define RS400_TMDS2_PLLEN            (1 << 0) +#       define RS400_TMDS2_PLLRST           (1 << 1) + +#define RADEON_GEN_INT_CNTL                 0x0040 +#define RADEON_GEN_INT_STATUS               0x0044 +#       define RADEON_VSYNC_INT_AK          (1 <<  2) +#       define RADEON_VSYNC_INT             (1 <<  2) +#       define RADEON_VSYNC2_INT_AK         (1 <<  6) +#       define RADEON_VSYNC2_INT            (1 <<  6) +#define RADEON_GENENB                       0x03c3 /* VGA */ +#define RADEON_GENFC_RD                     0x03ca /* VGA */ +#define RADEON_GENFC_WT                     0x03da /* VGA, 0x03ba */ +#define RADEON_GENMO_RD                     0x03cc /* VGA */ +#define RADEON_GENMO_WT                     0x03c2 /* VGA */ +#define RADEON_GENS0                        0x03c2 /* VGA */ +#define RADEON_GENS1                        0x03da /* VGA, 0x03ba */ +#define RADEON_GPIO_MONID                   0x0068 /* DDC interface via I2C */ +#define RADEON_GPIO_MONIDB                  0x006c +#define RADEON_GPIO_CRT2_DDC                0x006c +#define RADEON_GPIO_DVI_DDC                 0x0064 +#define RADEON_GPIO_VGA_DDC                 0x0060 +#       define RADEON_GPIO_A_0              (1 <<  0) +#       define RADEON_GPIO_A_1              (1 <<  1) +#       define RADEON_GPIO_Y_0              (1 <<  8) +#       define RADEON_GPIO_Y_1              (1 <<  9) +#       define RADEON_GPIO_Y_SHIFT_0        8 +#       define RADEON_GPIO_Y_SHIFT_1        9 +#       define RADEON_GPIO_EN_0             (1 << 16) +#       define RADEON_GPIO_EN_1             (1 << 17) +#       define RADEON_GPIO_MASK_0           (1 << 24) /*??*/ +#       define RADEON_GPIO_MASK_1           (1 << 25) /*??*/ +#define RADEON_GRPH8_DATA                   0x03cf /* VGA */ +#define RADEON_GRPH8_IDX                    0x03ce /* VGA */ +#define RADEON_GUI_SCRATCH_REG0             0x15e0 +#define RADEON_GUI_SCRATCH_REG1             0x15e4 +#define RADEON_GUI_SCRATCH_REG2             0x15e8 +#define RADEON_GUI_SCRATCH_REG3             0x15ec +#define RADEON_GUI_SCRATCH_REG4             0x15f0 +#define RADEON_GUI_SCRATCH_REG5             0x15f4 + +#define RADEON_HEADER                       0x0f0e /* PCI */ +#define RADEON_HOST_DATA0                   0x17c0 +#define RADEON_HOST_DATA1                   0x17c4 +#define RADEON_HOST_DATA2                   0x17c8 +#define RADEON_HOST_DATA3                   0x17cc +#define RADEON_HOST_DATA4                   0x17d0 +#define RADEON_HOST_DATA5                   0x17d4 +#define RADEON_HOST_DATA6                   0x17d8 +#define RADEON_HOST_DATA7                   0x17dc +#define RADEON_HOST_DATA_LAST               0x17e0 +#define RADEON_HOST_PATH_CNTL               0x0130 +#       define RADEON_HDP_SOFT_RESET        (1 << 26) +#       define RADEON_HDP_APER_CNTL         (1 << 23) +#define RADEON_HTOTAL_CNTL                  0x0009 /* PLL */ +#       define RADEON_HTOT_CNTL_VGA_EN      (1 << 28) +#define RADEON_HTOTAL2_CNTL                 0x002e /* PLL */ + +       /* Multimedia I2C bus */ +#define RADEON_I2C_CNTL_0		    0x0090 +#define RADEON_I2C_DONE (1<<0) +#define RADEON_I2C_NACK (1<<1) +#define RADEON_I2C_HALT (1<<2) +#define RADEON_I2C_SOFT_RST (1<<5) +#define RADEON_I2C_DRIVE_EN (1<<6) +#define RADEON_I2C_DRIVE_SEL (1<<7) +#define RADEON_I2C_START (1<<8) +#define RADEON_I2C_STOP (1<<9) +#define RADEON_I2C_RECEIVE (1<<10) +#define RADEON_I2C_ABORT (1<<11) +#define RADEON_I2C_GO (1<<12) +#define RADEON_I2C_CNTL_1                   0x0094 +#define RADEON_I2C_SEL         (1<<16) +#define RADEON_I2C_EN          (1<<17) +#define RADEON_I2C_DATA			    0x0098 + +#define RADEON_DVI_I2C_CNTL_0		    0x02e0 +#define RADEON_DVI_I2C_CNTL_1               0x02e4 /* ? */ +#define RADEON_DVI_I2C_DATA		    0x02e8 + +#define RADEON_INTERRUPT_LINE               0x0f3c /* PCI */ +#define RADEON_INTERRUPT_PIN                0x0f3d /* PCI */ +#define RADEON_IO_BASE                      0x0f14 /* PCI */ + +#define RADEON_LATENCY                      0x0f0d /* PCI */ +#define RADEON_LEAD_BRES_DEC                0x1608 +#define RADEON_LEAD_BRES_LNTH               0x161c +#define RADEON_LEAD_BRES_LNTH_SUB           0x1624 +#define RADEON_LVDS_GEN_CNTL                0x02d0 +#       define RADEON_LVDS_ON               (1   <<  0) +#       define RADEON_LVDS_DISPLAY_DIS      (1   <<  1) +#       define RADEON_LVDS_PANEL_TYPE       (1   <<  2) +#       define RADEON_LVDS_PANEL_FORMAT     (1   <<  3) +#       define RADEON_LVDS_RST_FM           (1   <<  6) +#       define RADEON_LVDS_EN               (1   <<  7) +#       define RADEON_LVDS_BL_MOD_LEVEL_SHIFT 8 +#       define RADEON_LVDS_BL_MOD_LEVEL_MASK (0xff << 8) +#       define RADEON_LVDS_BL_MOD_EN        (1   << 16) +#       define RADEON_LVDS_DIGON            (1   << 18) +#       define RADEON_LVDS_BLON             (1   << 19) +#       define RADEON_LVDS_SEL_CRTC2        (1   << 23) +#define RADEON_LVDS_PLL_CNTL                0x02d4 +#       define RADEON_HSYNC_DELAY_SHIFT     28 +#       define RADEON_HSYNC_DELAY_MASK      (0xf << 28) +#       define RADEON_LVDS_PLL_EN           (1   << 16) +#       define RADEON_LVDS_PLL_RESET        (1   << 17) +#       define R300_LVDS_SRC_SEL_MASK       (3   << 18) +#       define R300_LVDS_SRC_SEL_CRTC1      (0   << 18) +#       define R300_LVDS_SRC_SEL_CRTC2      (1   << 18) +#       define R300_LVDS_SRC_SEL_RMX        (2   << 18) + +#define RADEON_MAX_LATENCY                  0x0f3f /* PCI */ +#define RADEON_MC_AGP_LOCATION              0x014c +#define RADEON_MC_FB_LOCATION               0x0148 +#define RADEON_DISPLAY_BASE_ADDR            0x23c +#define RADEON_DISPLAY2_BASE_ADDR           0x33c +#define RADEON_OV0_BASE_ADDR                0x43c +#define RADEON_NB_TOM                       0x15c +#define R300_MC_INIT_MISC_LAT_TIMER         0x180 +#define RADEON_MCLK_CNTL                    0x0012 /* PLL */ +#       define RADEON_FORCEON_MCLKA         (1 << 16) +#       define RADEON_FORCEON_MCLKB         (1 << 17) +#       define RADEON_FORCEON_YCLKA         (1 << 18) +#       define RADEON_FORCEON_YCLKB         (1 << 19) +#       define RADEON_FORCEON_MC            (1 << 20) +#       define RADEON_FORCEON_AIC           (1 << 21) +#       define R300_DISABLE_MC_MCLKA        (1 << 21) +#       define R300_DISABLE_MC_MCLKB        (1 << 21) +#define RADEON_MCLK_MISC                    0x001f /* PLL */ +#       define RADEON_MC_MCLK_MAX_DYN_STOP_LAT (1 << 12) +#       define RADEON_IO_MCLK_MAX_DYN_STOP_LAT (1 << 13) +#       define RADEON_MC_MCLK_DYN_ENABLE    (1 << 14) +#       define RADEON_IO_MCLK_DYN_ENABLE    (1 << 15) +#define RADEON_LCD_GPIO_MASK                0x01a0 +#define RADEON_GPIOPAD_EN                   0x01a0 +#define RADEON_LCD_GPIO_Y_REG               0x01a4 +#define RADEON_MDGPIO_A_REG                 0x01ac +#define RADEON_MDGPIO_EN_REG                0x01b0 +#define RADEON_MDGPIO_MASK                  0x0198 +#define RADEON_GPIOPAD_MASK                 0x0198 +#define RADEON_GPIOPAD_A		    0x019c +#define RADEON_MDGPIO_Y_REG                 0x01b4 +#define RADEON_MEM_ADDR_CONFIG              0x0148 +#define RADEON_MEM_BASE                     0x0f10 /* PCI */ +#define RADEON_MEM_CNTL                     0x0140 +#       define RADEON_MEM_NUM_CHANNELS_MASK 0x01 +#       define RADEON_MEM_USE_B_CH_ONLY     (1 <<  1) +#       define RV100_HALF_MODE              (1 <<  3) +#       define R300_MEM_NUM_CHANNELS_MASK   0x03 +#       define R300_MEM_USE_CD_CH_ONLY      (1 <<  2) +#define RADEON_MEM_TIMING_CNTL              0x0144 /* EXT_MEM_CNTL */ +#define RADEON_MEM_INIT_LAT_TIMER           0x0154 +#define RADEON_MEM_INTF_CNTL                0x014c +#define RADEON_MEM_SDRAM_MODE_REG           0x0158 +#       define RADEON_SDRAM_MODE_MASK       0xffff0000 +#       define RADEON_B3MEM_RESET_MASK      0x6fffffff +#       define RADEON_MEM_CFG_TYPE_DDR      (1 << 30) +#define RADEON_MEM_STR_CNTL                 0x0150 +#       define RADEON_MEM_PWRUP_COMPL_A     (1 <<  0) +#       define RADEON_MEM_PWRUP_COMPL_B     (1 <<  1) +#       define R300_MEM_PWRUP_COMPL_C       (1 <<  2) +#       define R300_MEM_PWRUP_COMPL_D       (1 <<  3) +#       define RADEON_MEM_PWRUP_COMPLETE    0x03 +#       define R300_MEM_PWRUP_COMPLETE      0x0f +#define RADEON_MC_STATUS                    0x0150 +#       define RADEON_MC_IDLE               (1 << 2) +#       define R300_MC_IDLE                 (1 << 4) +#define RADEON_MEM_VGA_RP_SEL               0x003c +#define RADEON_MEM_VGA_WP_SEL               0x0038 +#define RADEON_MIN_GRANT                    0x0f3e /* PCI */ +#define RADEON_MM_DATA                      0x0004 +#define RADEON_MM_INDEX                     0x0000 +#define RADEON_MPLL_CNTL                    0x000e /* PLL */ +#define RADEON_MPP_TB_CONFIG                0x01c0 /* ? */ +#define RADEON_MPP_GP_CONFIG                0x01c8 /* ? */ +#define RADEON_SEPROM_CNTL1                 0x01c0 +#       define RADEON_SCK_PRESCALE_SHIFT    24 +#       define RADEON_SCK_PRESCALE_MASK     (0xff << 24) +#define R300_MC_IND_INDEX                   0x01f8 +#       define R300_MC_IND_ADDR_MASK        0x3f +#       define R300_MC_IND_WR_EN            (1 << 8) +#define R300_MC_IND_DATA                    0x01fc +#define R300_MC_READ_CNTL_AB                0x017c +#       define R300_MEM_RBS_POSITION_A_MASK 0x03 +#define R300_MC_READ_CNTL_CD_mcind	    0x24 +#       define R300_MEM_RBS_POSITION_C_MASK 0x03 + +#define RADEON_N_VIF_COUNT                  0x0248 + +#define RADEON_OV0_AUTO_FLIP_CNTL           0x0470 +#       define  RADEON_OV0_AUTO_FLIP_CNTL_SOFT_BUF_NUM        0x00000007 +#       define  RADEON_OV0_AUTO_FLIP_CNTL_SOFT_REPEAT_FIELD   0x00000008 +#       define  RADEON_OV0_AUTO_FLIP_CNTL_SOFT_BUF_ODD        0x00000010 +#       define  RADEON_OV0_AUTO_FLIP_CNTL_IGNORE_REPEAT_FIELD 0x00000020 +#       define  RADEON_OV0_AUTO_FLIP_CNTL_SOFT_EOF_TOGGLE     0x00000040 +#       define  RADEON_OV0_AUTO_FLIP_CNTL_VID_PORT_SELECT     0x00000300 +#       define  RADEON_OV0_AUTO_FLIP_CNTL_P1_FIRST_LINE_EVEN  0x00010000 +#       define  RADEON_OV0_AUTO_FLIP_CNTL_SHIFT_EVEN_DOWN     0x00040000 +#       define  RADEON_OV0_AUTO_FLIP_CNTL_SHIFT_ODD_DOWN      0x00080000 +#       define  RADEON_OV0_AUTO_FLIP_CNTL_FIELD_POL_SOURCE    0x00800000 + +#define RADEON_OV0_COLOUR_CNTL              0x04E0 +#define RADEON_OV0_DEINTERLACE_PATTERN      0x0474 +#define RADEON_OV0_EXCLUSIVE_HORZ           0x0408 +#       define  RADEON_EXCL_HORZ_START_MASK        0x000000ff +#       define  RADEON_EXCL_HORZ_END_MASK          0x0000ff00 +#       define  RADEON_EXCL_HORZ_BACK_PORCH_MASK   0x00ff0000 +#       define  RADEON_EXCL_HORZ_EXCLUSIVE_EN      0x80000000 +#define RADEON_OV0_EXCLUSIVE_VERT           0x040C +#       define  RADEON_EXCL_VERT_START_MASK        0x000003ff +#       define  RADEON_EXCL_VERT_END_MASK          0x03ff0000 +#define RADEON_OV0_FILTER_CNTL              0x04A0 +#       define RADEON_FILTER_PROGRAMMABLE_COEF            0x0 +#       define RADEON_FILTER_HC_COEF_HORZ_Y               0x1 +#       define RADEON_FILTER_HC_COEF_HORZ_UV              0x2 +#       define RADEON_FILTER_HC_COEF_VERT_Y               0x4 +#       define RADEON_FILTER_HC_COEF_VERT_UV              0x8 +#       define RADEON_FILTER_HARDCODED_COEF               0xf +#       define RADEON_FILTER_COEF_MASK                    0xf + +#define RADEON_OV0_FOUR_TAP_COEF_0          0x04B0 +#define RADEON_OV0_FOUR_TAP_COEF_1          0x04B4 +#define RADEON_OV0_FOUR_TAP_COEF_2          0x04B8 +#define RADEON_OV0_FOUR_TAP_COEF_3          0x04BC +#define RADEON_OV0_FOUR_TAP_COEF_4          0x04C0 +#define RADEON_OV0_FLAG_CNTL                0x04DC +#define RADEON_OV0_GAMMA_000_00F            0x0d40 +#define RADEON_OV0_GAMMA_010_01F            0x0d44 +#define RADEON_OV0_GAMMA_020_03F            0x0d48 +#define RADEON_OV0_GAMMA_040_07F            0x0d4c +#define RADEON_OV0_GAMMA_080_0BF            0x0e00 +#define RADEON_OV0_GAMMA_0C0_0FF            0x0e04 +#define RADEON_OV0_GAMMA_100_13F            0x0e08 +#define RADEON_OV0_GAMMA_140_17F            0x0e0c +#define RADEON_OV0_GAMMA_180_1BF            0x0e10 +#define RADEON_OV0_GAMMA_1C0_1FF            0x0e14 +#define RADEON_OV0_GAMMA_200_23F            0x0e18 +#define RADEON_OV0_GAMMA_240_27F            0x0e1c +#define RADEON_OV0_GAMMA_280_2BF            0x0e20 +#define RADEON_OV0_GAMMA_2C0_2FF            0x0e24 +#define RADEON_OV0_GAMMA_300_33F            0x0e28 +#define RADEON_OV0_GAMMA_340_37F            0x0e2c +#define RADEON_OV0_GAMMA_380_3BF            0x0d50 +#define RADEON_OV0_GAMMA_3C0_3FF            0x0d54 +#define RADEON_OV0_GRAPHICS_KEY_CLR_LOW     0x04EC +#define RADEON_OV0_GRAPHICS_KEY_CLR_HIGH    0x04F0 +#define RADEON_OV0_H_INC                    0x0480 +#define RADEON_OV0_KEY_CNTL                 0x04F4 +#       define  RADEON_VIDEO_KEY_FN_MASK    0x00000003L +#       define  RADEON_VIDEO_KEY_FN_FALSE   0x00000000L +#       define  RADEON_VIDEO_KEY_FN_TRUE    0x00000001L +#       define  RADEON_VIDEO_KEY_FN_EQ      0x00000002L +#       define  RADEON_VIDEO_KEY_FN_NE      0x00000003L +#       define  RADEON_GRAPHIC_KEY_FN_MASK  0x00000030L +#       define  RADEON_GRAPHIC_KEY_FN_FALSE 0x00000000L +#       define  RADEON_GRAPHIC_KEY_FN_TRUE  0x00000010L +#       define  RADEON_GRAPHIC_KEY_FN_EQ    0x00000020L +#       define  RADEON_GRAPHIC_KEY_FN_NE    0x00000030L +#       define  RADEON_CMP_MIX_MASK         0x00000100L +#       define  RADEON_CMP_MIX_OR           0x00000000L +#       define  RADEON_CMP_MIX_AND          0x00000100L +#define RADEON_OV0_LIN_TRANS_A              0x0d20 +#define RADEON_OV0_LIN_TRANS_B              0x0d24 +#define RADEON_OV0_LIN_TRANS_C              0x0d28 +#define RADEON_OV0_LIN_TRANS_D              0x0d2c +#define RADEON_OV0_LIN_TRANS_E              0x0d30 +#define RADEON_OV0_LIN_TRANS_F              0x0d34 +#define RADEON_OV0_P1_BLANK_LINES_AT_TOP    0x0430 +#       define  RADEON_P1_BLNK_LN_AT_TOP_M1_MASK   0x00000fffL +#       define  RADEON_P1_ACTIVE_LINES_M1          0x0fff0000L +#define RADEON_OV0_P1_H_ACCUM_INIT          0x0488 +#define RADEON_OV0_P1_V_ACCUM_INIT          0x0428 +#       define  RADEON_OV0_P1_MAX_LN_IN_PER_LN_OUT 0x00000003L +#       define  RADEON_OV0_P1_V_ACCUM_INIT_MASK    0x01ff8000L +#define RADEON_OV0_P1_X_START_END           0x0494 +#define RADEON_OV0_P2_X_START_END           0x0498 +#define RADEON_OV0_P23_BLANK_LINES_AT_TOP   0x0434 +#       define  RADEON_P23_BLNK_LN_AT_TOP_M1_MASK  0x000007ffL +#       define  RADEON_P23_ACTIVE_LINES_M1         0x07ff0000L +#define RADEON_OV0_P23_H_ACCUM_INIT         0x048C +#define RADEON_OV0_P23_V_ACCUM_INIT         0x042C +#define RADEON_OV0_P3_X_START_END           0x049C +#define RADEON_OV0_REG_LOAD_CNTL            0x0410 +#       define  RADEON_REG_LD_CTL_LOCK                 0x00000001L +#       define  RADEON_REG_LD_CTL_VBLANK_DURING_LOCK   0x00000002L +#       define  RADEON_REG_LD_CTL_STALL_GUI_UNTIL_FLIP 0x00000004L +#       define  RADEON_REG_LD_CTL_LOCK_READBACK        0x00000008L +#       define  RADEON_REG_LD_CTL_FLIP_READBACK        0x00000010L +#define RADEON_OV0_SCALE_CNTL               0x0420 +#       define  RADEON_SCALER_HORZ_PICK_NEAREST    0x00000004L +#       define  RADEON_SCALER_VERT_PICK_NEAREST    0x00000008L +#       define  RADEON_SCALER_SIGNED_UV            0x00000010L +#       define  RADEON_SCALER_GAMMA_SEL_MASK       0x00000060L +#       define  RADEON_SCALER_GAMMA_SEL_BRIGHT     0x00000000L +#       define  RADEON_SCALER_GAMMA_SEL_G22        0x00000020L +#       define  RADEON_SCALER_GAMMA_SEL_G18        0x00000040L +#       define  RADEON_SCALER_GAMMA_SEL_G14        0x00000060L +#       define  RADEON_SCALER_COMCORE_SHIFT_UP_ONE 0x00000080L +#       define  RADEON_SCALER_SURFAC_FORMAT        0x00000f00L +#       define  RADEON_SCALER_SOURCE_15BPP         0x00000300L +#       define  RADEON_SCALER_SOURCE_16BPP         0x00000400L +#       define  RADEON_SCALER_SOURCE_32BPP         0x00000600L +#       define  RADEON_SCALER_SOURCE_YUV9          0x00000900L +#       define  RADEON_SCALER_SOURCE_YUV12         0x00000A00L +#       define  RADEON_SCALER_SOURCE_VYUY422       0x00000B00L +#       define  RADEON_SCALER_SOURCE_YVYU422       0x00000C00L +#       define  RADEON_SCALER_ADAPTIVE_DEINT       0x00001000L +#       define  RADEON_SCALER_TEMPORAL_DEINT       0x00002000L +#       define  RADEON_SCALER_CRTC_SEL             0x00004000L +#       define  RADEON_SCALER_SMART_SWITCH         0x00008000L +#       define  RADEON_SCALER_BURST_PER_PLANE      0x007F0000L +#       define  RADEON_SCALER_DOUBLE_BUFFER        0x01000000L +#       define  RADEON_SCALER_DIS_LIMIT            0x08000000L +#       define  RADEON_SCALER_LIN_TRANS_BYPASS     0x10000000L +#       define  RADEON_SCALER_INT_EMU              0x20000000L +#       define  RADEON_SCALER_ENABLE               0x40000000L +#       define  RADEON_SCALER_SOFT_RESET           0x80000000L +#define RADEON_OV0_STEP_BY                  0x0484 +#define RADEON_OV0_TEST                     0x04F8 +#define RADEON_OV0_V_INC                    0x0424 +#define RADEON_OV0_VID_BUF_PITCH0_VALUE     0x0460 +#define RADEON_OV0_VID_BUF_PITCH1_VALUE     0x0464 +#define RADEON_OV0_VID_BUF0_BASE_ADRS       0x0440 +#       define  RADEON_VIF_BUF0_PITCH_SEL          0x00000001L +#       define  RADEON_VIF_BUF0_TILE_ADRS          0x00000002L +#       define  RADEON_VIF_BUF0_BASE_ADRS_MASK     0x03fffff0L +#       define  RADEON_VIF_BUF0_1ST_LINE_LSBS_MASK 0x48000000L +#define RADEON_OV0_VID_BUF1_BASE_ADRS       0x0444 +#       define  RADEON_VIF_BUF1_PITCH_SEL          0x00000001L +#       define  RADEON_VIF_BUF1_TILE_ADRS          0x00000002L +#       define  RADEON_VIF_BUF1_BASE_ADRS_MASK     0x03fffff0L +#       define  RADEON_VIF_BUF1_1ST_LINE_LSBS_MASK 0x48000000L +#define RADEON_OV0_VID_BUF2_BASE_ADRS       0x0448 +#       define  RADEON_VIF_BUF2_PITCH_SEL          0x00000001L +#       define  RADEON_VIF_BUF2_TILE_ADRS          0x00000002L +#       define  RADEON_VIF_BUF2_BASE_ADRS_MASK     0x03fffff0L +#       define  RADEON_VIF_BUF2_1ST_LINE_LSBS_MASK 0x48000000L +#define RADEON_OV0_VID_BUF3_BASE_ADRS       0x044C +#define RADEON_OV0_VID_BUF4_BASE_ADRS       0x0450 +#define RADEON_OV0_VID_BUF5_BASE_ADRS       0x0454 +#define RADEON_OV0_VIDEO_KEY_CLR_HIGH       0x04E8 +#define RADEON_OV0_VIDEO_KEY_CLR_LOW        0x04E4 +#define RADEON_OV0_Y_X_START                0x0400 +#define RADEON_OV0_Y_X_END                  0x0404 +#define RADEON_OV1_Y_X_START                0x0600 +#define RADEON_OV1_Y_X_END                  0x0604 +#define RADEON_OVR_CLR                      0x0230 +#define RADEON_OVR_WID_LEFT_RIGHT           0x0234 +#define RADEON_OVR_WID_TOP_BOTTOM           0x0238 + +/* first capture unit */ + +#define RADEON_CAP0_BUF0_OFFSET           0x0920 +#define RADEON_CAP0_BUF1_OFFSET           0x0924 +#define RADEON_CAP0_BUF0_EVEN_OFFSET      0x0928 +#define RADEON_CAP0_BUF1_EVEN_OFFSET      0x092C + +#define RADEON_CAP0_BUF_PITCH             0x0930 +#define RADEON_CAP0_V_WINDOW              0x0934 +#define RADEON_CAP0_H_WINDOW              0x0938 +#define RADEON_CAP0_VBI0_OFFSET           0x093C +#define RADEON_CAP0_VBI1_OFFSET           0x0940 +#define RADEON_CAP0_VBI_V_WINDOW          0x0944 +#define RADEON_CAP0_VBI_H_WINDOW          0x0948 +#define RADEON_CAP0_PORT_MODE_CNTL        0x094C +#define RADEON_CAP0_TRIG_CNTL             0x0950 +#define RADEON_CAP0_DEBUG                 0x0954 +#define RADEON_CAP0_CONFIG                0x0958 +#       define RADEON_CAP0_CONFIG_CONTINUOS          0x00000001 +#       define RADEON_CAP0_CONFIG_START_FIELD_EVEN   0x00000002 +#       define RADEON_CAP0_CONFIG_START_BUF_GET      0x00000004 +#       define RADEON_CAP0_CONFIG_START_BUF_SET      0x00000008 +#       define RADEON_CAP0_CONFIG_BUF_TYPE_ALT       0x00000010 +#       define RADEON_CAP0_CONFIG_BUF_TYPE_FRAME     0x00000020 +#       define RADEON_CAP0_CONFIG_ONESHOT_MODE_FRAME 0x00000040 +#       define RADEON_CAP0_CONFIG_BUF_MODE_DOUBLE    0x00000080 +#       define RADEON_CAP0_CONFIG_BUF_MODE_TRIPLE    0x00000100 +#       define RADEON_CAP0_CONFIG_MIRROR_EN          0x00000200 +#       define RADEON_CAP0_CONFIG_ONESHOT_MIRROR_EN  0x00000400 +#       define RADEON_CAP0_CONFIG_VIDEO_SIGNED_UV    0x00000800 +#       define RADEON_CAP0_CONFIG_ANC_DECODE_EN      0x00001000 +#       define RADEON_CAP0_CONFIG_VBI_EN             0x00002000 +#       define RADEON_CAP0_CONFIG_SOFT_PULL_DOWN_EN  0x00004000 +#       define RADEON_CAP0_CONFIG_VIP_EXTEND_FLAG_EN 0x00008000 +#       define RADEON_CAP0_CONFIG_FAKE_FIELD_EN      0x00010000 +#       define RADEON_CAP0_CONFIG_ODD_ONE_MORE_LINE  0x00020000 +#       define RADEON_CAP0_CONFIG_EVEN_ONE_MORE_LINE 0x00040000 +#       define RADEON_CAP0_CONFIG_HORZ_DIVIDE_2      0x00080000 +#       define RADEON_CAP0_CONFIG_HORZ_DIVIDE_4      0x00100000 +#       define RADEON_CAP0_CONFIG_VERT_DIVIDE_2      0x00200000 +#       define RADEON_CAP0_CONFIG_VERT_DIVIDE_4      0x00400000 +#       define RADEON_CAP0_CONFIG_FORMAT_BROOKTREE   0x00000000 +#       define RADEON_CAP0_CONFIG_FORMAT_CCIR656     0x00800000 +#       define RADEON_CAP0_CONFIG_FORMAT_ZV          0x01000000 +#       define RADEON_CAP0_CONFIG_FORMAT_VIP         0x01800000 +#       define RADEON_CAP0_CONFIG_FORMAT_TRANSPORT   0x02000000 +#       define RADEON_CAP0_CONFIG_HORZ_DECIMATOR     0x04000000 +#       define RADEON_CAP0_CONFIG_VIDEO_IN_YVYU422   0x00000000 +#       define RADEON_CAP0_CONFIG_VIDEO_IN_VYUY422   0x20000000 +#       define RADEON_CAP0_CONFIG_VBI_DIVIDE_2       0x40000000 +#       define RADEON_CAP0_CONFIG_VBI_DIVIDE_4       0x80000000 +#define RADEON_CAP0_ANC_ODD_OFFSET        0x095C +#define RADEON_CAP0_ANC_EVEN_OFFSET       0x0960 +#define RADEON_CAP0_ANC_H_WINDOW          0x0964 +#define RADEON_CAP0_VIDEO_SYNC_TEST       0x0968 +#define RADEON_CAP0_ONESHOT_BUF_OFFSET    0x096C +#define RADEON_CAP0_BUF_STATUS            0x0970 +/* #define RADEON_CAP0_DWNSC_XRATIO       0x0978 */ +/* #define RADEON_CAP0_XSHARPNESS                 0x097C */ +#define RADEON_CAP0_VBI2_OFFSET           0x0980 +#define RADEON_CAP0_VBI3_OFFSET           0x0984 +#define RADEON_CAP0_ANC2_OFFSET           0x0988 +#define RADEON_CAP0_ANC3_OFFSET           0x098C +#define RADEON_VID_BUFFER_CONTROL         0x0900 + +/* second capture unit */ + +#define RADEON_CAP1_BUF0_OFFSET           0x0990 +#define RADEON_CAP1_BUF1_OFFSET           0x0994 +#define RADEON_CAP1_BUF0_EVEN_OFFSET      0x0998 +#define RADEON_CAP1_BUF1_EVEN_OFFSET      0x099C + +#define RADEON_CAP1_BUF_PITCH             0x09A0 +#define RADEON_CAP1_V_WINDOW              0x09A4 +#define RADEON_CAP1_H_WINDOW              0x09A8 +#define RADEON_CAP1_VBI_ODD_OFFSET        0x09AC +#define RADEON_CAP1_VBI_EVEN_OFFSET       0x09B0 +#define RADEON_CAP1_VBI_V_WINDOW                  0x09B4 +#define RADEON_CAP1_VBI_H_WINDOW                  0x09B8 +#define RADEON_CAP1_PORT_MODE_CNTL        0x09BC +#define RADEON_CAP1_TRIG_CNTL             0x09C0 +#define RADEON_CAP1_DEBUG                         0x09C4 +#define RADEON_CAP1_CONFIG                0x09C8 +#define RADEON_CAP1_ANC_ODD_OFFSET        0x09CC +#define RADEON_CAP1_ANC_EVEN_OFFSET       0x09D0 +#define RADEON_CAP1_ANC_H_WINDOW                  0x09D4 +#define RADEON_CAP1_VIDEO_SYNC_TEST       0x09D8 +#define RADEON_CAP1_ONESHOT_BUF_OFFSET    0x09DC +#define RADEON_CAP1_BUF_STATUS            0x09E0 +#define RADEON_CAP1_DWNSC_XRATIO                  0x09E8 +#define RADEON_CAP1_XSHARPNESS            0x09EC + +/* misc multimedia registers */ + +#define RADEON_IDCT_RUNS                  0x1F80 +#define RADEON_IDCT_LEVELS                0x1F84 +#define RADEON_IDCT_CONTROL               0x1FBC +#define RADEON_IDCT_AUTH_CONTROL          0x1F88 +#define RADEON_IDCT_AUTH                  0x1F8C + +#define RADEON_P2PLL_CNTL                   0x002a /* P2PLL */ +#       define RADEON_P2PLL_RESET                (1 <<  0) +#       define RADEON_P2PLL_SLEEP                (1 <<  1) +#       define RADEON_P2PLL_PVG_MASK             (7 << 11) +#       define RADEON_P2PLL_PVG_SHIFT            11 +#       define RADEON_P2PLL_ATOMIC_UPDATE_EN     (1 << 16) +#       define RADEON_P2PLL_VGA_ATOMIC_UPDATE_EN (1 << 17) +#       define RADEON_P2PLL_ATOMIC_UPDATE_VSYNC  (1 << 18) +#define RADEON_P2PLL_DIV_0                  0x002c +#       define RADEON_P2PLL_FB0_DIV_MASK    0x07ff +#       define RADEON_P2PLL_POST0_DIV_MASK  0x00070000 +#define RADEON_P2PLL_REF_DIV                0x002B /* PLL */ +#       define RADEON_P2PLL_REF_DIV_MASK    0x03ff +#       define RADEON_P2PLL_ATOMIC_UPDATE_R (1 << 15) /* same as _W */ +#       define RADEON_P2PLL_ATOMIC_UPDATE_W (1 << 15) /* same as _R */ +#       define R300_PPLL_REF_DIV_ACC_MASK   (0x3ff << 18) +#       define R300_PPLL_REF_DIV_ACC_SHIFT  18 +#define RADEON_PALETTE_DATA                 0x00b4 +#define RADEON_PALETTE_30_DATA              0x00b8 +#define RADEON_PALETTE_INDEX                0x00b0 +#define RADEON_PCI_GART_PAGE                0x017c +#define RADEON_PIXCLKS_CNTL                 0x002d +#       define RADEON_PIX2CLK_SRC_SEL_MASK     0x03 +#       define RADEON_PIX2CLK_SRC_SEL_CPUCLK   0x00 +#       define RADEON_PIX2CLK_SRC_SEL_PSCANCLK 0x01 +#       define RADEON_PIX2CLK_SRC_SEL_BYTECLK  0x02 +#       define RADEON_PIX2CLK_SRC_SEL_P2PLLCLK 0x03 +#       define RADEON_PIX2CLK_ALWAYS_ONb       (1<<6) +#       define RADEON_PIX2CLK_DAC_ALWAYS_ONb   (1<<7) +#       define RADEON_PIXCLK_TV_SRC_SEL        (1 << 8) +#       define RADEON_DISP_TVOUT_PIXCLK_TV_ALWAYS_ONb (1 << 9) +#       define R300_DVOCLK_ALWAYS_ONb          (1 << 10) +#       define RADEON_PIXCLK_BLEND_ALWAYS_ONb  (1 << 11) +#       define RADEON_PIXCLK_GV_ALWAYS_ONb     (1 << 12) +#       define RADEON_PIXCLK_DIG_TMDS_ALWAYS_ONb (1 << 13) +#       define R300_PIXCLK_DVO_ALWAYS_ONb      (1 << 13) +#       define RADEON_PIXCLK_LVDS_ALWAYS_ONb   (1 << 14) +#       define RADEON_PIXCLK_TMDS_ALWAYS_ONb   (1 << 15) +#       define R300_PIXCLK_TRANS_ALWAYS_ONb    (1 << 16) +#       define R300_PIXCLK_TVO_ALWAYS_ONb      (1 << 17) +#       define R300_P2G2CLK_ALWAYS_ONb         (1 << 18) +#       define R300_P2G2CLK_DAC_ALWAYS_ONb     (1 << 19) +#       define R300_DISP_DAC_PIXCLK_DAC2_BLANK_OFF (1 << 23) +#define RADEON_PLANE_3D_MASK_C              0x1d44 +#define RADEON_PLL_TEST_CNTL                0x0013 /* PLL */ +#       define RADEON_PLL_MASK_READ_B          (1 << 9) +#define RADEON_PMI_CAP_ID                   0x0f5c /* PCI */ +#define RADEON_PMI_DATA                     0x0f63 /* PCI */ +#define RADEON_PMI_NXT_CAP_PTR              0x0f5d /* PCI */ +#define RADEON_PMI_PMC_REG                  0x0f5e /* PCI */ +#define RADEON_PMI_PMCSR_REG                0x0f60 /* PCI */ +#define RADEON_PMI_REGISTER                 0x0f5c /* PCI */ +#define RADEON_PPLL_CNTL                    0x0002 /* PLL */ +#       define RADEON_PPLL_RESET                (1 <<  0) +#       define RADEON_PPLL_SLEEP                (1 <<  1) +#       define RADEON_PPLL_PVG_MASK             (7 << 11) +#       define RADEON_PPLL_PVG_SHIFT            11 +#       define RADEON_PPLL_ATOMIC_UPDATE_EN     (1 << 16) +#       define RADEON_PPLL_VGA_ATOMIC_UPDATE_EN (1 << 17) +#       define RADEON_PPLL_ATOMIC_UPDATE_VSYNC  (1 << 18) +#define RADEON_PPLL_DIV_0                   0x0004 /* PLL */ +#define RADEON_PPLL_DIV_1                   0x0005 /* PLL */ +#define RADEON_PPLL_DIV_2                   0x0006 /* PLL */ +#define RADEON_PPLL_DIV_3                   0x0007 /* PLL */ +#       define RADEON_PPLL_FB3_DIV_MASK     0x07ff +#       define RADEON_PPLL_POST3_DIV_MASK   0x00070000 +#define RADEON_PPLL_REF_DIV                 0x0003 /* PLL */ +#       define RADEON_PPLL_REF_DIV_MASK     0x03ff +#       define RADEON_PPLL_ATOMIC_UPDATE_R  (1 << 15) /* same as _W */ +#       define RADEON_PPLL_ATOMIC_UPDATE_W  (1 << 15) /* same as _R */ +#define RADEON_PWR_MNGMT_CNTL_STATUS        0x0f60 /* PCI */ + +#define RADEON_RBBM_GUICNTL                 0x172c +#       define RADEON_HOST_DATA_SWAP_NONE   (0 << 0) +#       define RADEON_HOST_DATA_SWAP_16BIT  (1 << 0) +#       define RADEON_HOST_DATA_SWAP_32BIT  (2 << 0) +#       define RADEON_HOST_DATA_SWAP_HDW    (3 << 0) +#define RADEON_RBBM_SOFT_RESET              0x00f0 +#       define RADEON_SOFT_RESET_CP         (1 <<  0) +#       define RADEON_SOFT_RESET_HI         (1 <<  1) +#       define RADEON_SOFT_RESET_SE         (1 <<  2) +#       define RADEON_SOFT_RESET_RE         (1 <<  3) +#       define RADEON_SOFT_RESET_PP         (1 <<  4) +#       define RADEON_SOFT_RESET_E2         (1 <<  5) +#       define RADEON_SOFT_RESET_RB         (1 <<  6) +#       define RADEON_SOFT_RESET_HDP        (1 <<  7) +#define RADEON_RBBM_STATUS                  0x0e40 +#       define RADEON_RBBM_FIFOCNT_MASK     0x007f +#       define RADEON_RBBM_ACTIVE           (1 << 31) +#define RADEON_RB2D_DSTCACHE_CTLSTAT        0x342c +#       define RADEON_RB2D_DC_FLUSH         (3 << 0) +#       define RADEON_RB2D_DC_FREE          (3 << 2) +#       define RADEON_RB2D_DC_FLUSH_ALL     0xf +#       define RADEON_RB2D_DC_BUSY          (1 << 31) +#define RADEON_RB2D_DSTCACHE_MODE           0x3428 +#define RADEON_DSTCACHE_CTLSTAT             0x1714 + +#define RADEON_RB3D_ZCACHE_MODE             0x3250 +#define RADEON_RB3D_ZCACHE_CTLSTAT          0x3254 +#       define RADEON_RB3D_ZC_FLUSH_ALL     0x5 +#define RADEON_RB3D_DSTCACHE_MODE           0x3258 +# define RADEON_RB3D_DC_CACHE_ENABLE            (0) +# define RADEON_RB3D_DC_2D_CACHE_DISABLE        (1) +# define RADEON_RB3D_DC_3D_CACHE_DISABLE        (2) +# define RADEON_RB3D_DC_CACHE_DISABLE           (3) +# define RADEON_RB3D_DC_2D_CACHE_LINESIZE_128   (1 << 2) +# define RADEON_RB3D_DC_3D_CACHE_LINESIZE_128   (2 << 2) +# define RADEON_RB3D_DC_2D_CACHE_AUTOFLUSH      (1 << 8) +# define RADEON_RB3D_DC_3D_CACHE_AUTOFLUSH      (2 << 8) +# define R200_RB3D_DC_2D_CACHE_AUTOFREE         (1 << 10) +# define R200_RB3D_DC_3D_CACHE_AUTOFREE         (2 << 10) +# define RADEON_RB3D_DC_FORCE_RMW               (1 << 16) +# define RADEON_RB3D_DC_DISABLE_RI_FILL         (1 << 24) +# define RADEON_RB3D_DC_DISABLE_RI_READ         (1 << 25) + +#define RADEON_RB3D_DSTCACHE_CTLSTAT            0x325C +# define RADEON_RB3D_DC_FLUSH                   (3 << 0) +# define RADEON_RB3D_DC_FREE                    (3 << 2) +# define RADEON_RB3D_DC_FLUSH_ALL               0xf +# define RADEON_RB3D_DC_BUSY                    (1 << 31) + +#define RADEON_REG_BASE                     0x0f18 /* PCI */ +#define RADEON_REGPROG_INF                  0x0f09 /* PCI */ +#define RADEON_REVISION_ID                  0x0f08 /* PCI */ + +#define RADEON_SC_BOTTOM                    0x164c +#define RADEON_SC_BOTTOM_RIGHT              0x16f0 +#define RADEON_SC_BOTTOM_RIGHT_C            0x1c8c +#define RADEON_SC_LEFT                      0x1640 +#define RADEON_SC_RIGHT                     0x1644 +#define RADEON_SC_TOP                       0x1648 +#define RADEON_SC_TOP_LEFT                  0x16ec +#define RADEON_SC_TOP_LEFT_C                0x1c88 +#       define RADEON_SC_SIGN_MASK_LO       0x8000 +#       define RADEON_SC_SIGN_MASK_HI       0x80000000 +#define RADEON_SCLK_CNTL                    0x000d /* PLL */ +#       define RADEON_SCLK_SRC_SEL_MASK     0x0007 +#       define RADEON_DYN_STOP_LAT_MASK     0x00007ff8 +#       define RADEON_CP_MAX_DYN_STOP_LAT   0x0008 +#       define RADEON_SCLK_FORCEON_MASK     0xffff8000 +#       define RADEON_SCLK_FORCE_DISP2      (1<<15) +#       define RADEON_SCLK_FORCE_CP         (1<<16) +#       define RADEON_SCLK_FORCE_HDP        (1<<17) +#       define RADEON_SCLK_FORCE_DISP1      (1<<18) +#       define RADEON_SCLK_FORCE_TOP        (1<<19) +#       define RADEON_SCLK_FORCE_E2         (1<<20) +#       define RADEON_SCLK_FORCE_SE         (1<<21) +#       define RADEON_SCLK_FORCE_IDCT       (1<<22) +#       define RADEON_SCLK_FORCE_VIP        (1<<23) +#       define RADEON_SCLK_FORCE_RE         (1<<24) +#       define RADEON_SCLK_FORCE_PB         (1<<25) +#       define RADEON_SCLK_FORCE_TAM        (1<<26) +#       define RADEON_SCLK_FORCE_TDM        (1<<27) +#       define RADEON_SCLK_FORCE_RB         (1<<28) +#       define RADEON_SCLK_FORCE_TV_SCLK    (1<<29) +#       define RADEON_SCLK_FORCE_SUBPIC     (1<<30) +#       define RADEON_SCLK_FORCE_OV0        (1<<31) +#       define R300_SCLK_FORCE_VAP          (1<<21) +#       define R300_SCLK_FORCE_SR           (1<<25) +#       define R300_SCLK_FORCE_PX           (1<<26) +#       define R300_SCLK_FORCE_TX           (1<<27) +#       define R300_SCLK_FORCE_US           (1<<28) +#       define R300_SCLK_FORCE_SU           (1<<30) +#define R300_SCLK_CNTL2                     0x1e   /* PLL */ +#       define R300_SCLK_TCL_MAX_DYN_STOP_LAT (1<<10) +#       define R300_SCLK_GA_MAX_DYN_STOP_LAT  (1<<11) +#       define R300_SCLK_CBA_MAX_DYN_STOP_LAT (1<<12) +#       define R300_SCLK_FORCE_TCL          (1<<13) +#       define R300_SCLK_FORCE_CBA          (1<<14) +#       define R300_SCLK_FORCE_GA           (1<<15) +#define RADEON_SCLK_MORE_CNTL               0x0035 /* PLL */ +#       define RADEON_SCLK_MORE_MAX_DYN_STOP_LAT 0x0007 +#       define RADEON_SCLK_MORE_FORCEON     0x0700 +#define RADEON_SDRAM_MODE_REG               0x0158 +#define RADEON_SEQ8_DATA                    0x03c5 /* VGA */ +#define RADEON_SEQ8_IDX                     0x03c4 /* VGA */ +#define RADEON_SNAPSHOT_F_COUNT             0x0244 +#define RADEON_SNAPSHOT_VH_COUNTS           0x0240 +#define RADEON_SNAPSHOT_VIF_COUNT           0x024c +#define RADEON_SRC_OFFSET                   0x15ac +#define RADEON_SRC_PITCH                    0x15b0 +#define RADEON_SRC_PITCH_OFFSET             0x1428 +#define RADEON_SRC_SC_BOTTOM                0x165c +#define RADEON_SRC_SC_BOTTOM_RIGHT          0x16f4 +#define RADEON_SRC_SC_RIGHT                 0x1654 +#define RADEON_SRC_X                        0x1414 +#define RADEON_SRC_X_Y                      0x1590 +#define RADEON_SRC_Y                        0x1418 +#define RADEON_SRC_Y_X                      0x1434 +#define RADEON_STATUS                       0x0f06 /* PCI */ +#define RADEON_SUBPIC_CNTL                  0x0540 /* ? */ +#define RADEON_SUB_CLASS                    0x0f0a /* PCI */ +#define RADEON_SURFACE_CNTL                 0x0b00 +#       define RADEON_SURF_TRANSLATION_DIS  (1 << 8) +#       define RADEON_NONSURF_AP0_SWP_16BPP (1 << 20) +#       define RADEON_NONSURF_AP0_SWP_32BPP (1 << 21) +#       define RADEON_NONSURF_AP1_SWP_16BPP (1 << 22) +#       define RADEON_NONSURF_AP1_SWP_32BPP (1 << 23) +#define RADEON_SURFACE0_INFO                0x0b0c +#       define RADEON_SURF_TILE_COLOR_MACRO (0 << 16) +#       define RADEON_SURF_TILE_COLOR_BOTH  (1 << 16) +#       define RADEON_SURF_TILE_DEPTH_32BPP (2 << 16) +#       define RADEON_SURF_TILE_DEPTH_16BPP (3 << 16) +#       define R200_SURF_TILE_NONE          (0 << 16) +#       define R200_SURF_TILE_COLOR_MACRO   (1 << 16) +#       define R200_SURF_TILE_COLOR_MICRO   (2 << 16) +#       define R200_SURF_TILE_COLOR_BOTH    (3 << 16) +#       define R200_SURF_TILE_DEPTH_32BPP   (4 << 16) +#       define R200_SURF_TILE_DEPTH_16BPP   (5 << 16) +#       define R300_SURF_TILE_NONE          (0 << 16) +#       define R300_SURF_TILE_COLOR_MACRO   (1 << 16) +#       define R300_SURF_TILE_DEPTH_32BPP   (2 << 16) +#       define RADEON_SURF_AP0_SWP_16BPP    (1 << 20) +#       define RADEON_SURF_AP0_SWP_32BPP    (1 << 21) +#       define RADEON_SURF_AP1_SWP_16BPP    (1 << 22) +#       define RADEON_SURF_AP1_SWP_32BPP    (1 << 23) +#define RADEON_SURFACE0_LOWER_BOUND         0x0b04 +#define RADEON_SURFACE0_UPPER_BOUND         0x0b08 +#define RADEON_SURFACE1_INFO                0x0b1c +#define RADEON_SURFACE1_LOWER_BOUND         0x0b14 +#define RADEON_SURFACE1_UPPER_BOUND         0x0b18 +#define RADEON_SURFACE2_INFO                0x0b2c +#define RADEON_SURFACE2_LOWER_BOUND         0x0b24 +#define RADEON_SURFACE2_UPPER_BOUND         0x0b28 +#define RADEON_SURFACE3_INFO                0x0b3c +#define RADEON_SURFACE3_LOWER_BOUND         0x0b34 +#define RADEON_SURFACE3_UPPER_BOUND         0x0b38 +#define RADEON_SURFACE4_INFO                0x0b4c +#define RADEON_SURFACE4_LOWER_BOUND         0x0b44 +#define RADEON_SURFACE4_UPPER_BOUND         0x0b48 +#define RADEON_SURFACE5_INFO                0x0b5c +#define RADEON_SURFACE5_LOWER_BOUND         0x0b54 +#define RADEON_SURFACE5_UPPER_BOUND         0x0b58 +#define RADEON_SURFACE6_INFO                0x0b6c +#define RADEON_SURFACE6_LOWER_BOUND         0x0b64 +#define RADEON_SURFACE6_UPPER_BOUND         0x0b68 +#define RADEON_SURFACE7_INFO                0x0b7c +#define RADEON_SURFACE7_LOWER_BOUND         0x0b74 +#define RADEON_SURFACE7_UPPER_BOUND         0x0b78 +#define RADEON_SW_SEMAPHORE                 0x013c + +#define RADEON_TEST_DEBUG_CNTL              0x0120 +#define RADEON_TEST_DEBUG_CNTL__TEST_DEBUG_OUT_EN 0x00000001 + +#define RADEON_TEST_DEBUG_MUX               0x0124 +#define RADEON_TEST_DEBUG_OUT               0x012c +#define RADEON_TMDS_PLL_CNTL                0x02a8 +#define RADEON_TMDS_TRANSMITTER_CNTL        0x02a4 +#       define RADEON_TMDS_TRANSMITTER_PLLEN  1 +#       define RADEON_TMDS_TRANSMITTER_PLLRST 2 +#define RADEON_TRAIL_BRES_DEC               0x1614 +#define RADEON_TRAIL_BRES_ERR               0x160c +#define RADEON_TRAIL_BRES_INC               0x1610 +#define RADEON_TRAIL_X                      0x1618 +#define RADEON_TRAIL_X_SUB                  0x1620 + +#define RADEON_VCLK_ECP_CNTL                0x0008 /* PLL */ +#       define RADEON_VCLK_SRC_SEL_MASK     0x03 +#       define RADEON_VCLK_SRC_SEL_CPUCLK   0x00 +#       define RADEON_VCLK_SRC_SEL_PSCANCLK 0x01 +#       define RADEON_VCLK_SRC_SEL_BYTECLK  0x02 +#       define RADEON_VCLK_SRC_SEL_PPLLCLK  0x03 +#       define RADEON_PIXCLK_ALWAYS_ONb     (1<<6) +#       define RADEON_PIXCLK_DAC_ALWAYS_ONb (1<<7) +#       define R300_DISP_DAC_PIXCLK_DAC_BLANK_OFF (1<<23) + +#define RADEON_VENDOR_ID                    0x0f00 /* PCI */ +#define RADEON_VGA_DDA_CONFIG               0x02e8 +#define RADEON_VGA_DDA_ON_OFF               0x02ec +#define RADEON_VID_BUFFER_CONTROL           0x0900 +#define RADEON_VIDEOMUX_CNTL                0x0190 + +                 /* VIP bus */ +#define RADEON_VIPH_CH0_DATA                0x0c00 +#define RADEON_VIPH_CH1_DATA                0x0c04 +#define RADEON_VIPH_CH2_DATA                0x0c08 +#define RADEON_VIPH_CH3_DATA                0x0c0c +#define RADEON_VIPH_CH0_ADDR                0x0c10 +#define RADEON_VIPH_CH1_ADDR                0x0c14 +#define RADEON_VIPH_CH2_ADDR                0x0c18 +#define RADEON_VIPH_CH3_ADDR                0x0c1c +#define RADEON_VIPH_CH0_SBCNT               0x0c20 +#define RADEON_VIPH_CH1_SBCNT               0x0c24 +#define RADEON_VIPH_CH2_SBCNT               0x0c28 +#define RADEON_VIPH_CH3_SBCNT               0x0c2c +#define RADEON_VIPH_CH0_ABCNT               0x0c30 +#define RADEON_VIPH_CH1_ABCNT               0x0c34 +#define RADEON_VIPH_CH2_ABCNT               0x0c38 +#define RADEON_VIPH_CH3_ABCNT               0x0c3c +#define RADEON_VIPH_CONTROL                 0x0c40 +#       define RADEON_VIP_BUSY 0 +#       define RADEON_VIP_IDLE 1 +#       define RADEON_VIP_RESET 2 +#       define RADEON_VIPH_EN               (1 << 21) +#define RADEON_VIPH_DV_LAT                  0x0c44 +#define RADEON_VIPH_BM_CHUNK                0x0c48 +#define RADEON_VIPH_DV_INT                  0x0c4c +#define RADEON_VIPH_TIMEOUT_STAT            0x0c50 +#define RADEON_VIPH_TIMEOUT_STAT__VIPH_REG_STAT 0x00000010 +#define RADEON_VIPH_TIMEOUT_STAT__VIPH_REG_AK   0x00000010 +#define RADEON_VIPH_TIMEOUT_STAT__VIPH_REGR_DIS 0x01000000 + +#define RADEON_VIPH_REG_DATA                0x0084 +#define RADEON_VIPH_REG_ADDR                0x0080 + + +#define RADEON_WAIT_UNTIL                   0x1720 +#       define RADEON_WAIT_CRTC_PFLIP       (1 << 0) +#       define RADEON_WAIT_RE_CRTC_VLINE    (1 << 1) +#       define RADEON_WAIT_FE_CRTC_VLINE    (1 << 2) +#       define RADEON_WAIT_CRTC_VLINE       (1 << 3) +#       define RADEON_WAIT_DMA_VID_IDLE     (1 << 8) +#       define RADEON_WAIT_DMA_GUI_IDLE     (1 << 9) +#       define RADEON_WAIT_CMDFIFO          (1 << 10) /* wait for CMDFIFO_ENTRIES */ +#       define RADEON_WAIT_OV0_FLIP         (1 << 11) +#       define RADEON_WAIT_AGP_FLUSH        (1 << 13) +#       define RADEON_WAIT_2D_IDLE          (1 << 14) +#       define RADEON_WAIT_3D_IDLE          (1 << 15) +#       define RADEON_WAIT_2D_IDLECLEAN     (1 << 16) +#       define RADEON_WAIT_3D_IDLECLEAN     (1 << 17) +#       define RADEON_WAIT_HOST_IDLECLEAN   (1 << 18) +#       define RADEON_CMDFIFO_ENTRIES_SHIFT 10 +#       define RADEON_CMDFIFO_ENTRIES_MASK  0x7f +#       define RADEON_WAIT_VAP_IDLE         (1 << 28) +#       define RADEON_WAIT_BOTH_CRTC_PFLIP  (1 << 30) +#       define RADEON_ENG_DISPLAY_SELECT_CRTC0    (0 << 31) +#       define RADEON_ENG_DISPLAY_SELECT_CRTC1    (1 << 31) + +#define RADEON_X_MPLL_REF_FB_DIV            0x000a /* PLL */ +#define RADEON_XCLK_CNTL                    0x000d /* PLL */ +#define RADEON_XDLL_CNTL                    0x000c /* PLL */ +#define RADEON_XPLL_CNTL                    0x000b /* PLL */ + + + +				/* Registers for 3D/TCL */ +#define RADEON_PP_BORDER_COLOR_0            0x1d40 +#define RADEON_PP_BORDER_COLOR_1            0x1d44 +#define RADEON_PP_BORDER_COLOR_2            0x1d48 +#define RADEON_PP_CNTL                      0x1c38 +#       define RADEON_STIPPLE_ENABLE        (1 <<  0) +#       define RADEON_SCISSOR_ENABLE        (1 <<  1) +#       define RADEON_PATTERN_ENABLE        (1 <<  2) +#       define RADEON_SHADOW_ENABLE         (1 <<  3) +#       define RADEON_TEX_ENABLE_MASK       (0xf << 4) +#       define RADEON_TEX_0_ENABLE          (1 <<  4) +#       define RADEON_TEX_1_ENABLE          (1 <<  5) +#       define RADEON_TEX_2_ENABLE          (1 <<  6) +#       define RADEON_TEX_3_ENABLE          (1 <<  7) +#       define RADEON_TEX_BLEND_ENABLE_MASK (0xf << 12) +#       define RADEON_TEX_BLEND_0_ENABLE    (1 << 12) +#       define RADEON_TEX_BLEND_1_ENABLE    (1 << 13) +#       define RADEON_TEX_BLEND_2_ENABLE    (1 << 14) +#       define RADEON_TEX_BLEND_3_ENABLE    (1 << 15) +#       define RADEON_PLANAR_YUV_ENABLE     (1 << 20) +#       define RADEON_SPECULAR_ENABLE       (1 << 21) +#       define RADEON_FOG_ENABLE            (1 << 22) +#       define RADEON_ALPHA_TEST_ENABLE     (1 << 23) +#       define RADEON_ANTI_ALIAS_NONE       (0 << 24) +#       define RADEON_ANTI_ALIAS_LINE       (1 << 24) +#       define RADEON_ANTI_ALIAS_POLY       (2 << 24) +#       define RADEON_ANTI_ALIAS_LINE_POLY  (3 << 24) +#       define RADEON_BUMP_MAP_ENABLE       (1 << 26) +#       define RADEON_BUMPED_MAP_T0         (0 << 27) +#       define RADEON_BUMPED_MAP_T1         (1 << 27) +#       define RADEON_BUMPED_MAP_T2         (2 << 27) +#       define RADEON_TEX_3D_ENABLE_0       (1 << 29) +#       define RADEON_TEX_3D_ENABLE_1       (1 << 30) +#       define RADEON_MC_ENABLE             (1 << 31) +#define RADEON_PP_FOG_COLOR                 0x1c18 +#       define RADEON_FOG_COLOR_MASK        0x00ffffff +#       define RADEON_FOG_VERTEX            (0 << 24) +#       define RADEON_FOG_TABLE             (1 << 24) +#       define RADEON_FOG_USE_DEPTH         (0 << 25) +#       define RADEON_FOG_USE_DIFFUSE_ALPHA (2 << 25) +#       define RADEON_FOG_USE_SPEC_ALPHA    (3 << 25) +#define RADEON_PP_LUM_MATRIX                0x1d00 +#define RADEON_PP_MISC                      0x1c14 +#       define RADEON_REF_ALPHA_MASK        0x000000ff +#       define RADEON_ALPHA_TEST_FAIL       (0 << 8) +#       define RADEON_ALPHA_TEST_LESS       (1 << 8) +#       define RADEON_ALPHA_TEST_LEQUAL     (2 << 8) +#       define RADEON_ALPHA_TEST_EQUAL      (3 << 8) +#       define RADEON_ALPHA_TEST_GEQUAL     (4 << 8) +#       define RADEON_ALPHA_TEST_GREATER    (5 << 8) +#       define RADEON_ALPHA_TEST_NEQUAL     (6 << 8) +#       define RADEON_ALPHA_TEST_PASS       (7 << 8) +#       define RADEON_ALPHA_TEST_OP_MASK    (7 << 8) +#       define RADEON_CHROMA_FUNC_FAIL      (0 << 16) +#       define RADEON_CHROMA_FUNC_PASS      (1 << 16) +#       define RADEON_CHROMA_FUNC_NEQUAL    (2 << 16) +#       define RADEON_CHROMA_FUNC_EQUAL     (3 << 16) +#       define RADEON_CHROMA_KEY_NEAREST    (0 << 18) +#       define RADEON_CHROMA_KEY_ZERO       (1 << 18) +#       define RADEON_SHADOW_ID_AUTO_INC    (1 << 20) +#       define RADEON_SHADOW_FUNC_EQUAL     (0 << 21) +#       define RADEON_SHADOW_FUNC_NEQUAL    (1 << 21) +#       define RADEON_SHADOW_PASS_1         (0 << 22) +#       define RADEON_SHADOW_PASS_2         (1 << 22) +#       define RADEON_RIGHT_HAND_CUBE_D3D   (0 << 24) +#       define RADEON_RIGHT_HAND_CUBE_OGL   (1 << 24) +#define RADEON_PP_ROT_MATRIX_0              0x1d58 +#define RADEON_PP_ROT_MATRIX_1              0x1d5c +#define RADEON_PP_TXFILTER_0                0x1c54 +#define RADEON_PP_TXFILTER_1                0x1c6c +#define RADEON_PP_TXFILTER_2                0x1c84 +#       define RADEON_MAG_FILTER_NEAREST                   (0  <<  0) +#       define RADEON_MAG_FILTER_LINEAR                    (1  <<  0) +#       define RADEON_MAG_FILTER_MASK                      (1  <<  0) +#       define RADEON_MIN_FILTER_NEAREST                   (0  <<  1) +#       define RADEON_MIN_FILTER_LINEAR                    (1  <<  1) +#       define RADEON_MIN_FILTER_NEAREST_MIP_NEAREST       (2  <<  1) +#       define RADEON_MIN_FILTER_NEAREST_MIP_LINEAR        (3  <<  1) +#       define RADEON_MIN_FILTER_LINEAR_MIP_NEAREST        (6  <<  1) +#       define RADEON_MIN_FILTER_LINEAR_MIP_LINEAR         (7  <<  1) +#       define RADEON_MIN_FILTER_ANISO_NEAREST             (8  <<  1) +#       define RADEON_MIN_FILTER_ANISO_LINEAR              (9  <<  1) +#       define RADEON_MIN_FILTER_ANISO_NEAREST_MIP_NEAREST (10 <<  1) +#       define RADEON_MIN_FILTER_ANISO_NEAREST_MIP_LINEAR  (11 <<  1) +#       define RADEON_MIN_FILTER_MASK                      (15 <<  1) +#       define RADEON_MAX_ANISO_1_TO_1                     (0  <<  5) +#       define RADEON_MAX_ANISO_2_TO_1                     (1  <<  5) +#       define RADEON_MAX_ANISO_4_TO_1                     (2  <<  5) +#       define RADEON_MAX_ANISO_8_TO_1                     (3  <<  5) +#       define RADEON_MAX_ANISO_16_TO_1                    (4  <<  5) +#       define RADEON_MAX_ANISO_MASK                       (7  <<  5) +#       define RADEON_LOD_BIAS_MASK                        (0xff <<  8) +#       define RADEON_LOD_BIAS_SHIFT                       8 +#       define RADEON_MAX_MIP_LEVEL_MASK                   (0x0f << 16) +#       define RADEON_MAX_MIP_LEVEL_SHIFT                  16 +#       define RADEON_YUV_TO_RGB                           (1  << 20) +#       define RADEON_YUV_TEMPERATURE_COOL                 (0  << 21) +#       define RADEON_YUV_TEMPERATURE_HOT                  (1  << 21) +#       define RADEON_YUV_TEMPERATURE_MASK                 (1  << 21) +#       define RADEON_WRAPEN_S                             (1  << 22) +#       define RADEON_CLAMP_S_WRAP                         (0  << 23) +#       define RADEON_CLAMP_S_MIRROR                       (1  << 23) +#       define RADEON_CLAMP_S_CLAMP_LAST                   (2  << 23) +#       define RADEON_CLAMP_S_MIRROR_CLAMP_LAST            (3  << 23) +#       define RADEON_CLAMP_S_CLAMP_BORDER                 (4  << 23) +#       define RADEON_CLAMP_S_MIRROR_CLAMP_BORDER          (5  << 23) +#       define RADEON_CLAMP_S_CLAMP_GL                     (6  << 23) +#       define RADEON_CLAMP_S_MIRROR_CLAMP_GL              (7  << 23) +#       define RADEON_CLAMP_S_MASK                         (7  << 23) +#       define RADEON_WRAPEN_T                             (1  << 26) +#       define RADEON_CLAMP_T_WRAP                         (0  << 27) +#       define RADEON_CLAMP_T_MIRROR                       (1  << 27) +#       define RADEON_CLAMP_T_CLAMP_LAST                   (2  << 27) +#       define RADEON_CLAMP_T_MIRROR_CLAMP_LAST            (3  << 27) +#       define RADEON_CLAMP_T_CLAMP_BORDER                 (4  << 27) +#       define RADEON_CLAMP_T_MIRROR_CLAMP_BORDER          (5  << 27) +#       define RADEON_CLAMP_T_CLAMP_GL                     (6  << 27) +#       define RADEON_CLAMP_T_MIRROR_CLAMP_GL              (7  << 27) +#       define RADEON_CLAMP_T_MASK                         (7  << 27) +#       define RADEON_BORDER_MODE_OGL                      (0  << 31) +#       define RADEON_BORDER_MODE_D3D                      (1  << 31) +#define RADEON_PP_TXFORMAT_0                0x1c58 +#define RADEON_PP_TXFORMAT_1                0x1c70 +#define RADEON_PP_TXFORMAT_2                0x1c88 +#       define RADEON_TXFORMAT_I8                 (0  <<  0) +#       define RADEON_TXFORMAT_AI88               (1  <<  0) +#       define RADEON_TXFORMAT_RGB332             (2  <<  0) +#       define RADEON_TXFORMAT_ARGB1555           (3  <<  0) +#       define RADEON_TXFORMAT_RGB565             (4  <<  0) +#       define RADEON_TXFORMAT_ARGB4444           (5  <<  0) +#       define RADEON_TXFORMAT_ARGB8888           (6  <<  0) +#       define RADEON_TXFORMAT_RGBA8888           (7  <<  0) +#       define RADEON_TXFORMAT_Y8                 (8  <<  0) +#       define RADEON_TXFORMAT_VYUY422            (10 <<  0) +#       define RADEON_TXFORMAT_YVYU422            (11 <<  0) +#       define RADEON_TXFORMAT_DXT1               (12 <<  0) +#       define RADEON_TXFORMAT_DXT23              (14 <<  0) +#       define RADEON_TXFORMAT_DXT45              (15 <<  0) +#       define RADEON_TXFORMAT_FORMAT_MASK        (31 <<  0) +#       define RADEON_TXFORMAT_FORMAT_SHIFT       0 +#       define RADEON_TXFORMAT_APPLE_YUV_MODE     (1  <<  5) +#       define RADEON_TXFORMAT_ALPHA_IN_MAP       (1  <<  6) +#       define RADEON_TXFORMAT_NON_POWER2         (1  <<  7) +#       define RADEON_TXFORMAT_WIDTH_MASK         (15 <<  8) +#       define RADEON_TXFORMAT_WIDTH_SHIFT        8 +#       define RADEON_TXFORMAT_HEIGHT_MASK        (15 << 12) +#       define RADEON_TXFORMAT_HEIGHT_SHIFT       12 +#       define RADEON_TXFORMAT_F5_WIDTH_MASK      (15 << 16) +#       define RADEON_TXFORMAT_F5_WIDTH_SHIFT     16 +#       define RADEON_TXFORMAT_F5_HEIGHT_MASK     (15 << 20) +#       define RADEON_TXFORMAT_F5_HEIGHT_SHIFT    20 +#       define RADEON_TXFORMAT_ST_ROUTE_STQ0      (0  << 24) +#       define RADEON_TXFORMAT_ST_ROUTE_MASK      (3  << 24) +#       define RADEON_TXFORMAT_ST_ROUTE_STQ1      (1  << 24) +#       define RADEON_TXFORMAT_ST_ROUTE_STQ2      (2  << 24) +#       define RADEON_TXFORMAT_ENDIAN_NO_SWAP     (0  << 26) +#       define RADEON_TXFORMAT_ENDIAN_16BPP_SWAP  (1  << 26) +#       define RADEON_TXFORMAT_ENDIAN_32BPP_SWAP  (2  << 26) +#       define RADEON_TXFORMAT_ENDIAN_HALFDW_SWAP (3  << 26) +#       define RADEON_TXFORMAT_ALPHA_MASK_ENABLE  (1  << 28) +#       define RADEON_TXFORMAT_CHROMA_KEY_ENABLE  (1  << 29) +#       define RADEON_TXFORMAT_CUBIC_MAP_ENABLE   (1  << 30) +#       define RADEON_TXFORMAT_PERSPECTIVE_ENABLE (1  << 31) +#define RADEON_PP_CUBIC_FACES_0             0x1d24 +#define RADEON_PP_CUBIC_FACES_1             0x1d28 +#define RADEON_PP_CUBIC_FACES_2             0x1d2c +#       define RADEON_FACE_WIDTH_1_SHIFT          0 +#       define RADEON_FACE_HEIGHT_1_SHIFT         4 +#       define RADEON_FACE_WIDTH_1_MASK           (0xf << 0) +#       define RADEON_FACE_HEIGHT_1_MASK          (0xf << 4) +#       define RADEON_FACE_WIDTH_2_SHIFT          8 +#       define RADEON_FACE_HEIGHT_2_SHIFT         12 +#       define RADEON_FACE_WIDTH_2_MASK           (0xf << 8) +#       define RADEON_FACE_HEIGHT_2_MASK          (0xf << 12) +#       define RADEON_FACE_WIDTH_3_SHIFT          16 +#       define RADEON_FACE_HEIGHT_3_SHIFT         20 +#       define RADEON_FACE_WIDTH_3_MASK           (0xf << 16) +#       define RADEON_FACE_HEIGHT_3_MASK          (0xf << 20) +#       define RADEON_FACE_WIDTH_4_SHIFT          24 +#       define RADEON_FACE_HEIGHT_4_SHIFT         28 +#       define RADEON_FACE_WIDTH_4_MASK           (0xf << 24) +#       define RADEON_FACE_HEIGHT_4_MASK          (0xf << 28) + +#define RADEON_PP_TXOFFSET_0                0x1c5c +#define RADEON_PP_TXOFFSET_1                0x1c74 +#define RADEON_PP_TXOFFSET_2                0x1c8c +#       define RADEON_TXO_ENDIAN_NO_SWAP     (0 << 0) +#       define RADEON_TXO_ENDIAN_BYTE_SWAP   (1 << 0) +#       define RADEON_TXO_ENDIAN_WORD_SWAP   (2 << 0) +#       define RADEON_TXO_ENDIAN_HALFDW_SWAP (3 << 0) +#       define RADEON_TXO_MACRO_LINEAR       (0 << 2) +#       define RADEON_TXO_MACRO_TILE         (1 << 2) +#       define RADEON_TXO_MICRO_LINEAR       (0 << 3) +#       define RADEON_TXO_MICRO_TILE_X2      (1 << 3) +#       define RADEON_TXO_MICRO_TILE_OPT     (2 << 3) +#       define RADEON_TXO_OFFSET_MASK        0xffffffe0 +#       define RADEON_TXO_OFFSET_SHIFT       5 + +#define RADEON_PP_CUBIC_OFFSET_T0_0         0x1dd0  /* bits [31:5] */ +#define RADEON_PP_CUBIC_OFFSET_T0_1         0x1dd4 +#define RADEON_PP_CUBIC_OFFSET_T0_2         0x1dd8 +#define RADEON_PP_CUBIC_OFFSET_T0_3         0x1ddc +#define RADEON_PP_CUBIC_OFFSET_T0_4         0x1de0 +#define RADEON_PP_CUBIC_OFFSET_T1_0         0x1e00 +#define RADEON_PP_CUBIC_OFFSET_T1_1         0x1e04 +#define RADEON_PP_CUBIC_OFFSET_T1_2         0x1e08 +#define RADEON_PP_CUBIC_OFFSET_T1_3         0x1e0c +#define RADEON_PP_CUBIC_OFFSET_T1_4         0x1e10 +#define RADEON_PP_CUBIC_OFFSET_T2_0         0x1e14 +#define RADEON_PP_CUBIC_OFFSET_T2_1         0x1e18 +#define RADEON_PP_CUBIC_OFFSET_T2_2         0x1e1c +#define RADEON_PP_CUBIC_OFFSET_T2_3         0x1e20 +#define RADEON_PP_CUBIC_OFFSET_T2_4         0x1e24 + +#define RADEON_PP_TEX_SIZE_0                0x1d04  /* NPOT */ +#define RADEON_PP_TEX_SIZE_1                0x1d0c +#define RADEON_PP_TEX_SIZE_2                0x1d14 +#       define RADEON_TEX_USIZE_MASK        (0x7ff << 0) +#       define RADEON_TEX_USIZE_SHIFT       0 +#       define RADEON_TEX_VSIZE_MASK        (0x7ff << 16) +#       define RADEON_TEX_VSIZE_SHIFT       16 +#       define RADEON_SIGNED_RGB_MASK       (1 << 30) +#       define RADEON_SIGNED_RGB_SHIFT      30 +#       define RADEON_SIGNED_ALPHA_MASK     (1 << 31) +#       define RADEON_SIGNED_ALPHA_SHIFT    31 +#define RADEON_PP_TEX_PITCH_0               0x1d08  /* NPOT */ +#define RADEON_PP_TEX_PITCH_1               0x1d10  /* NPOT */ +#define RADEON_PP_TEX_PITCH_2               0x1d18  /* NPOT */ +/* note: bits 13-5: 32 byte aligned stride of texture map */ + +#define RADEON_PP_TXCBLEND_0                0x1c60 +#define RADEON_PP_TXCBLEND_1                0x1c78 +#define RADEON_PP_TXCBLEND_2                0x1c90 +#       define RADEON_COLOR_ARG_A_SHIFT          0 +#       define RADEON_COLOR_ARG_A_MASK           (0x1f << 0) +#       define RADEON_COLOR_ARG_A_ZERO           (0    << 0) +#       define RADEON_COLOR_ARG_A_CURRENT_COLOR  (2    << 0) +#       define RADEON_COLOR_ARG_A_CURRENT_ALPHA  (3    << 0) +#       define RADEON_COLOR_ARG_A_DIFFUSE_COLOR  (4    << 0) +#       define RADEON_COLOR_ARG_A_DIFFUSE_ALPHA  (5    << 0) +#       define RADEON_COLOR_ARG_A_SPECULAR_COLOR (6    << 0) +#       define RADEON_COLOR_ARG_A_SPECULAR_ALPHA (7    << 0) +#       define RADEON_COLOR_ARG_A_TFACTOR_COLOR  (8    << 0) +#       define RADEON_COLOR_ARG_A_TFACTOR_ALPHA  (9    << 0) +#       define RADEON_COLOR_ARG_A_T0_COLOR       (10   << 0) +#       define RADEON_COLOR_ARG_A_T0_ALPHA       (11   << 0) +#       define RADEON_COLOR_ARG_A_T1_COLOR       (12   << 0) +#       define RADEON_COLOR_ARG_A_T1_ALPHA       (13   << 0) +#       define RADEON_COLOR_ARG_A_T2_COLOR       (14   << 0) +#       define RADEON_COLOR_ARG_A_T2_ALPHA       (15   << 0) +#       define RADEON_COLOR_ARG_A_T3_COLOR       (16   << 0) +#       define RADEON_COLOR_ARG_A_T3_ALPHA       (17   << 0) +#       define RADEON_COLOR_ARG_B_SHIFT          5 +#       define RADEON_COLOR_ARG_B_MASK           (0x1f << 5) +#       define RADEON_COLOR_ARG_B_ZERO           (0    << 5) +#       define RADEON_COLOR_ARG_B_CURRENT_COLOR  (2    << 5) +#       define RADEON_COLOR_ARG_B_CURRENT_ALPHA  (3    << 5) +#       define RADEON_COLOR_ARG_B_DIFFUSE_COLOR  (4    << 5) +#       define RADEON_COLOR_ARG_B_DIFFUSE_ALPHA  (5    << 5) +#       define RADEON_COLOR_ARG_B_SPECULAR_COLOR (6    << 5) +#       define RADEON_COLOR_ARG_B_SPECULAR_ALPHA (7    << 5) +#       define RADEON_COLOR_ARG_B_TFACTOR_COLOR  (8    << 5) +#       define RADEON_COLOR_ARG_B_TFACTOR_ALPHA  (9    << 5) +#       define RADEON_COLOR_ARG_B_T0_COLOR       (10   << 5) +#       define RADEON_COLOR_ARG_B_T0_ALPHA       (11   << 5) +#       define RADEON_COLOR_ARG_B_T1_COLOR       (12   << 5) +#       define RADEON_COLOR_ARG_B_T1_ALPHA       (13   << 5) +#       define RADEON_COLOR_ARG_B_T2_COLOR       (14   << 5) +#       define RADEON_COLOR_ARG_B_T2_ALPHA       (15   << 5) +#       define RADEON_COLOR_ARG_B_T3_COLOR       (16   << 5) +#       define RADEON_COLOR_ARG_B_T3_ALPHA       (17   << 5) +#       define RADEON_COLOR_ARG_C_SHIFT          10 +#       define RADEON_COLOR_ARG_C_MASK           (0x1f << 10) +#       define RADEON_COLOR_ARG_C_ZERO           (0    << 10) +#       define RADEON_COLOR_ARG_C_CURRENT_COLOR  (2    << 10) +#       define RADEON_COLOR_ARG_C_CURRENT_ALPHA  (3    << 10) +#       define RADEON_COLOR_ARG_C_DIFFUSE_COLOR  (4    << 10) +#       define RADEON_COLOR_ARG_C_DIFFUSE_ALPHA  (5    << 10) +#       define RADEON_COLOR_ARG_C_SPECULAR_COLOR (6    << 10) +#       define RADEON_COLOR_ARG_C_SPECULAR_ALPHA (7    << 10) +#       define RADEON_COLOR_ARG_C_TFACTOR_COLOR  (8    << 10) +#       define RADEON_COLOR_ARG_C_TFACTOR_ALPHA  (9    << 10) +#       define RADEON_COLOR_ARG_C_T0_COLOR       (10   << 10) +#       define RADEON_COLOR_ARG_C_T0_ALPHA       (11   << 10) +#       define RADEON_COLOR_ARG_C_T1_COLOR       (12   << 10) +#       define RADEON_COLOR_ARG_C_T1_ALPHA       (13   << 10) +#       define RADEON_COLOR_ARG_C_T2_COLOR       (14   << 10) +#       define RADEON_COLOR_ARG_C_T2_ALPHA       (15   << 10) +#       define RADEON_COLOR_ARG_C_T3_COLOR       (16   << 10) +#       define RADEON_COLOR_ARG_C_T3_ALPHA       (17   << 10) +#       define RADEON_COMP_ARG_A                 (1 << 15) +#       define RADEON_COMP_ARG_A_SHIFT           15 +#       define RADEON_COMP_ARG_B                 (1 << 16) +#       define RADEON_COMP_ARG_B_SHIFT           16 +#       define RADEON_COMP_ARG_C                 (1 << 17) +#       define RADEON_COMP_ARG_C_SHIFT           17 +#       define RADEON_BLEND_CTL_MASK             (7 << 18) +#       define RADEON_BLEND_CTL_ADD              (0 << 18) +#       define RADEON_BLEND_CTL_SUBTRACT         (1 << 18) +#       define RADEON_BLEND_CTL_ADDSIGNED        (2 << 18) +#       define RADEON_BLEND_CTL_BLEND            (3 << 18) +#       define RADEON_BLEND_CTL_DOT3             (4 << 18) +#       define RADEON_SCALE_SHIFT                21 +#       define RADEON_SCALE_MASK                 (3 << 21) +#       define RADEON_SCALE_1X                   (0 << 21) +#       define RADEON_SCALE_2X                   (1 << 21) +#       define RADEON_SCALE_4X                   (2 << 21) +#       define RADEON_CLAMP_TX                   (1 << 23) +#       define RADEON_T0_EQ_TCUR                 (1 << 24) +#       define RADEON_T1_EQ_TCUR                 (1 << 25) +#       define RADEON_T2_EQ_TCUR                 (1 << 26) +#       define RADEON_T3_EQ_TCUR                 (1 << 27) +#       define RADEON_COLOR_ARG_MASK             0x1f +#       define RADEON_COMP_ARG_SHIFT             15 +#define RADEON_PP_TXABLEND_0                0x1c64 +#define RADEON_PP_TXABLEND_1                0x1c7c +#define RADEON_PP_TXABLEND_2                0x1c94 +#       define RADEON_ALPHA_ARG_A_SHIFT          0 +#       define RADEON_ALPHA_ARG_A_MASK           (0xf << 0) +#       define RADEON_ALPHA_ARG_A_ZERO           (0   << 0) +#       define RADEON_ALPHA_ARG_A_CURRENT_ALPHA  (1   << 0) +#       define RADEON_ALPHA_ARG_A_DIFFUSE_ALPHA  (2   << 0) +#       define RADEON_ALPHA_ARG_A_SPECULAR_ALPHA (3   << 0) +#       define RADEON_ALPHA_ARG_A_TFACTOR_ALPHA  (4   << 0) +#       define RADEON_ALPHA_ARG_A_T0_ALPHA       (5   << 0) +#       define RADEON_ALPHA_ARG_A_T1_ALPHA       (6   << 0) +#       define RADEON_ALPHA_ARG_A_T2_ALPHA       (7   << 0) +#       define RADEON_ALPHA_ARG_A_T3_ALPHA       (8   << 0) +#       define RADEON_ALPHA_ARG_B_SHIFT          4 +#       define RADEON_ALPHA_ARG_B_MASK           (0xf << 4) +#       define RADEON_ALPHA_ARG_B_ZERO           (0   << 4) +#       define RADEON_ALPHA_ARG_B_CURRENT_ALPHA  (1   << 4) +#       define RADEON_ALPHA_ARG_B_DIFFUSE_ALPHA  (2   << 4) +#       define RADEON_ALPHA_ARG_B_SPECULAR_ALPHA (3   << 4) +#       define RADEON_ALPHA_ARG_B_TFACTOR_ALPHA  (4   << 4) +#       define RADEON_ALPHA_ARG_B_T0_ALPHA       (5   << 4) +#       define RADEON_ALPHA_ARG_B_T1_ALPHA       (6   << 4) +#       define RADEON_ALPHA_ARG_B_T2_ALPHA       (7   << 4) +#       define RADEON_ALPHA_ARG_B_T3_ALPHA       (8   << 4) +#       define RADEON_ALPHA_ARG_C_SHIFT          8 +#       define RADEON_ALPHA_ARG_C_MASK           (0xf << 8) +#       define RADEON_ALPHA_ARG_C_ZERO           (0   << 8) +#       define RADEON_ALPHA_ARG_C_CURRENT_ALPHA  (1   << 8) +#       define RADEON_ALPHA_ARG_C_DIFFUSE_ALPHA  (2   << 8) +#       define RADEON_ALPHA_ARG_C_SPECULAR_ALPHA (3   << 8) +#       define RADEON_ALPHA_ARG_C_TFACTOR_ALPHA  (4   << 8) +#       define RADEON_ALPHA_ARG_C_T0_ALPHA       (5   << 8) +#       define RADEON_ALPHA_ARG_C_T1_ALPHA       (6   << 8) +#       define RADEON_ALPHA_ARG_C_T2_ALPHA       (7   << 8) +#       define RADEON_ALPHA_ARG_C_T3_ALPHA       (8   << 8) +#       define RADEON_DOT_ALPHA_DONT_REPLICATE   (1   << 9) +#       define RADEON_ALPHA_ARG_MASK             0xf + +#define RADEON_PP_TFACTOR_0                 0x1c68 +#define RADEON_PP_TFACTOR_1                 0x1c80 +#define RADEON_PP_TFACTOR_2                 0x1c98 + +#define RADEON_RB3D_BLENDCNTL               0x1c20 +#       define RADEON_COMB_FCN_MASK                    (3  << 12) +#       define RADEON_COMB_FCN_ADD_CLAMP               (0  << 12) +#       define RADEON_COMB_FCN_ADD_NOCLAMP             (1  << 12) +#       define RADEON_COMB_FCN_SUB_CLAMP               (2  << 12) +#       define RADEON_COMB_FCN_SUB_NOCLAMP             (3  << 12) +#       define RADEON_SRC_BLEND_GL_ZERO                (32 << 16) +#       define RADEON_SRC_BLEND_GL_ONE                 (33 << 16) +#       define RADEON_SRC_BLEND_GL_SRC_COLOR           (34 << 16) +#       define RADEON_SRC_BLEND_GL_ONE_MINUS_SRC_COLOR (35 << 16) +#       define RADEON_SRC_BLEND_GL_DST_COLOR           (36 << 16) +#       define RADEON_SRC_BLEND_GL_ONE_MINUS_DST_COLOR (37 << 16) +#       define RADEON_SRC_BLEND_GL_SRC_ALPHA           (38 << 16) +#       define RADEON_SRC_BLEND_GL_ONE_MINUS_SRC_ALPHA (39 << 16) +#       define RADEON_SRC_BLEND_GL_DST_ALPHA           (40 << 16) +#       define RADEON_SRC_BLEND_GL_ONE_MINUS_DST_ALPHA (41 << 16) +#       define RADEON_SRC_BLEND_GL_SRC_ALPHA_SATURATE  (42 << 16) +#       define RADEON_SRC_BLEND_MASK                   (63 << 16) +#       define RADEON_DST_BLEND_GL_ZERO                (32 << 24) +#       define RADEON_DST_BLEND_GL_ONE                 (33 << 24) +#       define RADEON_DST_BLEND_GL_SRC_COLOR           (34 << 24) +#       define RADEON_DST_BLEND_GL_ONE_MINUS_SRC_COLOR (35 << 24) +#       define RADEON_DST_BLEND_GL_DST_COLOR           (36 << 24) +#       define RADEON_DST_BLEND_GL_ONE_MINUS_DST_COLOR (37 << 24) +#       define RADEON_DST_BLEND_GL_SRC_ALPHA           (38 << 24) +#       define RADEON_DST_BLEND_GL_ONE_MINUS_SRC_ALPHA (39 << 24) +#       define RADEON_DST_BLEND_GL_DST_ALPHA           (40 << 24) +#       define RADEON_DST_BLEND_GL_ONE_MINUS_DST_ALPHA (41 << 24) +#       define RADEON_DST_BLEND_MASK                   (63 << 24) +#define RADEON_RB3D_CNTL                    0x1c3c +#       define RADEON_ALPHA_BLEND_ENABLE       (1  <<  0) +#       define RADEON_PLANE_MASK_ENABLE        (1  <<  1) +#       define RADEON_DITHER_ENABLE            (1  <<  2) +#       define RADEON_ROUND_ENABLE             (1  <<  3) +#       define RADEON_SCALE_DITHER_ENABLE      (1  <<  4) +#       define RADEON_DITHER_INIT              (1  <<  5) +#       define RADEON_ROP_ENABLE               (1  <<  6) +#       define RADEON_STENCIL_ENABLE           (1  <<  7) +#       define RADEON_Z_ENABLE                 (1  <<  8) +#       define RADEON_DEPTH_XZ_OFFEST_ENABLE   (1  <<  9) +#       define RADEON_COLOR_FORMAT_ARGB1555    (3  << 10) +#       define RADEON_COLOR_FORMAT_RGB565      (4  << 10) +#       define RADEON_COLOR_FORMAT_ARGB8888    (6  << 10) +#       define RADEON_COLOR_FORMAT_RGB332      (7  << 10) +#       define RADEON_COLOR_FORMAT_Y8          (8  << 10) +#       define RADEON_COLOR_FORMAT_RGB8        (9  << 10) +#       define RADEON_COLOR_FORMAT_YUV422_VYUY (11 << 10) +#       define RADEON_COLOR_FORMAT_YUV422_YVYU (12 << 10) +#       define RADEON_COLOR_FORMAT_aYUV444     (14 << 10) +#       define RADEON_COLOR_FORMAT_ARGB4444    (15 << 10) +#       define RADEON_CLRCMP_FLIP_ENABLE       (1  << 14) +#define RADEON_RB3D_COLOROFFSET             0x1c40 +#       define RADEON_COLOROFFSET_MASK      0xfffffff0 +#define RADEON_RB3D_COLORPITCH              0x1c48 +#       define RADEON_COLORPITCH_MASK         0x000001ff8 +#       define RADEON_COLOR_TILE_ENABLE       (1 << 16) +#       define RADEON_COLOR_MICROTILE_ENABLE  (1 << 17) +#       define RADEON_COLOR_ENDIAN_NO_SWAP    (0 << 18) +#       define RADEON_COLOR_ENDIAN_WORD_SWAP  (1 << 18) +#       define RADEON_COLOR_ENDIAN_DWORD_SWAP (2 << 18) +#define RADEON_RB3D_DEPTHOFFSET             0x1c24 +#define RADEON_RB3D_DEPTHPITCH              0x1c28 +#       define RADEON_DEPTHPITCH_MASK         0x00001ff8 +#       define RADEON_DEPTH_ENDIAN_NO_SWAP    (0 << 18) +#       define RADEON_DEPTH_ENDIAN_WORD_SWAP  (1 << 18) +#       define RADEON_DEPTH_ENDIAN_DWORD_SWAP (2 << 18) +#define RADEON_RB3D_PLANEMASK               0x1d84 +#define RADEON_RB3D_ROPCNTL                 0x1d80 +#       define RADEON_ROP_MASK              (15 << 8) +#       define RADEON_ROP_CLEAR             (0  << 8) +#       define RADEON_ROP_NOR               (1  << 8) +#       define RADEON_ROP_AND_INVERTED      (2  << 8) +#       define RADEON_ROP_COPY_INVERTED     (3  << 8) +#       define RADEON_ROP_AND_REVERSE       (4  << 8) +#       define RADEON_ROP_INVERT            (5  << 8) +#       define RADEON_ROP_XOR               (6  << 8) +#       define RADEON_ROP_NAND              (7  << 8) +#       define RADEON_ROP_AND               (8  << 8) +#       define RADEON_ROP_EQUIV             (9  << 8) +#       define RADEON_ROP_NOOP              (10 << 8) +#       define RADEON_ROP_OR_INVERTED       (11 << 8) +#       define RADEON_ROP_COPY              (12 << 8) +#       define RADEON_ROP_OR_REVERSE        (13 << 8) +#       define RADEON_ROP_OR                (14 << 8) +#       define RADEON_ROP_SET               (15 << 8) +#define RADEON_RB3D_STENCILREFMASK          0x1d7c +#       define RADEON_STENCIL_REF_SHIFT       0 +#       define RADEON_STENCIL_REF_MASK        (0xff << 0) +#       define RADEON_STENCIL_MASK_SHIFT      16 +#       define RADEON_STENCIL_VALUE_MASK      (0xff << 16) +#       define RADEON_STENCIL_WRITEMASK_SHIFT 24 +#       define RADEON_STENCIL_WRITE_MASK      (0xff << 24) +#define RADEON_RB3D_ZSTENCILCNTL            0x1c2c +#       define RADEON_DEPTH_FORMAT_MASK          (0xf << 0) +#       define RADEON_DEPTH_FORMAT_16BIT_INT_Z   (0  <<  0) +#       define RADEON_DEPTH_FORMAT_24BIT_INT_Z   (2  <<  0) +#       define RADEON_DEPTH_FORMAT_24BIT_FLOAT_Z (3  <<  0) +#       define RADEON_DEPTH_FORMAT_32BIT_INT_Z   (4  <<  0) +#       define RADEON_DEPTH_FORMAT_32BIT_FLOAT_Z (5  <<  0) +#       define RADEON_DEPTH_FORMAT_16BIT_FLOAT_W (7  <<  0) +#       define RADEON_DEPTH_FORMAT_24BIT_FLOAT_W (9  <<  0) +#       define RADEON_DEPTH_FORMAT_32BIT_FLOAT_W (11 <<  0) +#       define RADEON_Z_TEST_NEVER               (0  <<  4) +#       define RADEON_Z_TEST_LESS                (1  <<  4) +#       define RADEON_Z_TEST_LEQUAL              (2  <<  4) +#       define RADEON_Z_TEST_EQUAL               (3  <<  4) +#       define RADEON_Z_TEST_GEQUAL              (4  <<  4) +#       define RADEON_Z_TEST_GREATER             (5  <<  4) +#       define RADEON_Z_TEST_NEQUAL              (6  <<  4) +#       define RADEON_Z_TEST_ALWAYS              (7  <<  4) +#       define RADEON_Z_TEST_MASK                (7  <<  4) +#       define RADEON_STENCIL_TEST_NEVER         (0  << 12) +#       define RADEON_STENCIL_TEST_LESS          (1  << 12) +#       define RADEON_STENCIL_TEST_LEQUAL        (2  << 12) +#       define RADEON_STENCIL_TEST_EQUAL         (3  << 12) +#       define RADEON_STENCIL_TEST_GEQUAL        (4  << 12) +#       define RADEON_STENCIL_TEST_GREATER       (5  << 12) +#       define RADEON_STENCIL_TEST_NEQUAL        (6  << 12) +#       define RADEON_STENCIL_TEST_ALWAYS        (7  << 12) +#       define RADEON_STENCIL_TEST_MASK          (0x7 << 12) +#       define RADEON_STENCIL_FAIL_KEEP          (0  << 16) +#       define RADEON_STENCIL_FAIL_ZERO          (1  << 16) +#       define RADEON_STENCIL_FAIL_REPLACE       (2  << 16) +#       define RADEON_STENCIL_FAIL_INC           (3  << 16) +#       define RADEON_STENCIL_FAIL_DEC           (4  << 16) +#       define RADEON_STENCIL_FAIL_INVERT        (5  << 16) +#       define RADEON_STENCIL_FAIL_MASK          (0x7 << 16) +#       define RADEON_STENCIL_ZPASS_KEEP         (0  << 20) +#       define RADEON_STENCIL_ZPASS_ZERO         (1  << 20) +#       define RADEON_STENCIL_ZPASS_REPLACE      (2  << 20) +#       define RADEON_STENCIL_ZPASS_INC          (3  << 20) +#       define RADEON_STENCIL_ZPASS_DEC          (4  << 20) +#       define RADEON_STENCIL_ZPASS_INVERT       (5  << 20) +#       define RADEON_STENCIL_ZPASS_MASK         (0x7 << 20) +#       define RADEON_STENCIL_ZFAIL_KEEP         (0  << 24) +#       define RADEON_STENCIL_ZFAIL_ZERO         (1  << 24) +#       define RADEON_STENCIL_ZFAIL_REPLACE      (2  << 24) +#       define RADEON_STENCIL_ZFAIL_INC          (3  << 24) +#       define RADEON_STENCIL_ZFAIL_DEC          (4  << 24) +#       define RADEON_STENCIL_ZFAIL_INVERT       (5  << 24) +#       define RADEON_STENCIL_ZFAIL_MASK         (0x7 << 24) +#       define RADEON_Z_COMPRESSION_ENABLE       (1  << 28) +#       define RADEON_FORCE_Z_DIRTY              (1  << 29) +#       define RADEON_Z_WRITE_ENABLE             (1  << 30) +#define RADEON_RE_LINE_PATTERN              0x1cd0 +#       define RADEON_LINE_PATTERN_MASK             0x0000ffff +#       define RADEON_LINE_REPEAT_COUNT_SHIFT       16 +#       define RADEON_LINE_PATTERN_START_SHIFT      24 +#       define RADEON_LINE_PATTERN_LITTLE_BIT_ORDER (0 << 28) +#       define RADEON_LINE_PATTERN_BIG_BIT_ORDER    (1 << 28) +#       define RADEON_LINE_PATTERN_AUTO_RESET       (1 << 29) +#define RADEON_RE_LINE_STATE                0x1cd4 +#       define RADEON_LINE_CURRENT_PTR_SHIFT   0 +#       define RADEON_LINE_CURRENT_COUNT_SHIFT 8 +#define RADEON_RE_MISC                      0x26c4 +#       define RADEON_STIPPLE_COORD_MASK       0x1f +#       define RADEON_STIPPLE_X_OFFSET_SHIFT   0 +#       define RADEON_STIPPLE_X_OFFSET_MASK    (0x1f << 0) +#       define RADEON_STIPPLE_Y_OFFSET_SHIFT   8 +#       define RADEON_STIPPLE_Y_OFFSET_MASK    (0x1f << 8) +#       define RADEON_STIPPLE_LITTLE_BIT_ORDER (0 << 16) +#       define RADEON_STIPPLE_BIG_BIT_ORDER    (1 << 16) +#define RADEON_RE_SOLID_COLOR               0x1c1c +#define RADEON_RE_TOP_LEFT                  0x26c0 +#       define RADEON_RE_LEFT_SHIFT         0 +#       define RADEON_RE_TOP_SHIFT          16 +#define RADEON_RE_WIDTH_HEIGHT              0x1c44 +#       define RADEON_RE_WIDTH_SHIFT        0 +#       define RADEON_RE_HEIGHT_SHIFT       16 + +#define RADEON_SE_CNTL                      0x1c4c +#       define RADEON_FFACE_CULL_CW          (0 <<  0) +#       define RADEON_FFACE_CULL_CCW         (1 <<  0) +#       define RADEON_FFACE_CULL_DIR_MASK    (1 <<  0) +#       define RADEON_BFACE_CULL             (0 <<  1) +#       define RADEON_BFACE_SOLID            (3 <<  1) +#       define RADEON_FFACE_CULL             (0 <<  3) +#       define RADEON_FFACE_SOLID            (3 <<  3) +#       define RADEON_FFACE_CULL_MASK        (3 <<  3) +#       define RADEON_BADVTX_CULL_DISABLE    (1 <<  5) +#       define RADEON_FLAT_SHADE_VTX_0       (0 <<  6) +#       define RADEON_FLAT_SHADE_VTX_1       (1 <<  6) +#       define RADEON_FLAT_SHADE_VTX_2       (2 <<  6) +#       define RADEON_FLAT_SHADE_VTX_LAST    (3 <<  6) +#       define RADEON_DIFFUSE_SHADE_SOLID    (0 <<  8) +#       define RADEON_DIFFUSE_SHADE_FLAT     (1 <<  8) +#       define RADEON_DIFFUSE_SHADE_GOURAUD  (2 <<  8) +#       define RADEON_DIFFUSE_SHADE_MASK     (3 <<  8) +#       define RADEON_ALPHA_SHADE_SOLID      (0 << 10) +#       define RADEON_ALPHA_SHADE_FLAT       (1 << 10) +#       define RADEON_ALPHA_SHADE_GOURAUD    (2 << 10) +#       define RADEON_ALPHA_SHADE_MASK       (3 << 10) +#       define RADEON_SPECULAR_SHADE_SOLID   (0 << 12) +#       define RADEON_SPECULAR_SHADE_FLAT    (1 << 12) +#       define RADEON_SPECULAR_SHADE_GOURAUD (2 << 12) +#       define RADEON_SPECULAR_SHADE_MASK    (3 << 12) +#       define RADEON_FOG_SHADE_SOLID        (0 << 14) +#       define RADEON_FOG_SHADE_FLAT         (1 << 14) +#       define RADEON_FOG_SHADE_GOURAUD      (2 << 14) +#       define RADEON_FOG_SHADE_MASK         (3 << 14) +#       define RADEON_ZBIAS_ENABLE_POINT     (1 << 16) +#       define RADEON_ZBIAS_ENABLE_LINE      (1 << 17) +#       define RADEON_ZBIAS_ENABLE_TRI       (1 << 18) +#       define RADEON_WIDELINE_ENABLE        (1 << 20) +#       define RADEON_VPORT_XY_XFORM_ENABLE  (1 << 24) +#       define RADEON_VPORT_Z_XFORM_ENABLE   (1 << 25) +#       define RADEON_VTX_PIX_CENTER_D3D     (0 << 27) +#       define RADEON_VTX_PIX_CENTER_OGL     (1 << 27) +#       define RADEON_ROUND_MODE_TRUNC       (0 << 28) +#       define RADEON_ROUND_MODE_ROUND       (1 << 28) +#       define RADEON_ROUND_MODE_ROUND_EVEN  (2 << 28) +#       define RADEON_ROUND_MODE_ROUND_ODD   (3 << 28) +#       define RADEON_ROUND_PREC_16TH_PIX    (0 << 30) +#       define RADEON_ROUND_PREC_8TH_PIX     (1 << 30) +#       define RADEON_ROUND_PREC_4TH_PIX     (2 << 30) +#       define RADEON_ROUND_PREC_HALF_PIX    (3 << 30) +#define R200_RE_CNTL				0x1c50  +#       define R200_STIPPLE_ENABLE		0x1 +#       define R200_SCISSOR_ENABLE		0x2 +#       define R200_PATTERN_ENABLE		0x4 +#       define R200_PERSPECTIVE_ENABLE		0x8 +#       define R200_POINT_SMOOTH		0x20 +#       define R200_VTX_STQ0_D3D		0x00010000 +#       define R200_VTX_STQ1_D3D		0x00040000 +#       define R200_VTX_STQ2_D3D		0x00100000 +#       define R200_VTX_STQ3_D3D		0x00400000 +#       define R200_VTX_STQ4_D3D		0x01000000 +#       define R200_VTX_STQ5_D3D		0x04000000 +#define RADEON_SE_CNTL_STATUS               0x2140 +#       define RADEON_VC_NO_SWAP            (0 << 0) +#       define RADEON_VC_16BIT_SWAP         (1 << 0) +#       define RADEON_VC_32BIT_SWAP         (2 << 0) +#       define RADEON_VC_HALF_DWORD_SWAP    (3 << 0) +#       define RADEON_TCL_BYPASS            (1 << 8) +#define RADEON_SE_COORD_FMT                 0x1c50 +#       define RADEON_VTX_XY_PRE_MULT_1_OVER_W0  (1 <<  0) +#       define RADEON_VTX_Z_PRE_MULT_1_OVER_W0   (1 <<  1) +#       define RADEON_VTX_ST0_NONPARAMETRIC      (1 <<  8) +#       define RADEON_VTX_ST1_NONPARAMETRIC      (1 <<  9) +#       define RADEON_VTX_ST2_NONPARAMETRIC      (1 << 10) +#       define RADEON_VTX_ST3_NONPARAMETRIC      (1 << 11) +#       define RADEON_VTX_W0_NORMALIZE           (1 << 12) +#       define RADEON_VTX_W0_IS_NOT_1_OVER_W0    (1 << 16) +#       define RADEON_VTX_ST0_PRE_MULT_1_OVER_W0 (1 << 17) +#       define RADEON_VTX_ST1_PRE_MULT_1_OVER_W0 (1 << 19) +#       define RADEON_VTX_ST2_PRE_MULT_1_OVER_W0 (1 << 21) +#       define RADEON_VTX_ST3_PRE_MULT_1_OVER_W0 (1 << 23) +#       define RADEON_TEX1_W_ROUTING_USE_W0      (0 << 26) +#       define RADEON_TEX1_W_ROUTING_USE_Q1      (1 << 26) +#define RADEON_SE_LINE_WIDTH                0x1db8 +#define RADEON_SE_TCL_LIGHT_MODEL_CTL       0x226c +#       define RADEON_LIGHTING_ENABLE              (1 << 0) +#       define RADEON_LIGHT_IN_MODELSPACE          (1 << 1) +#       define RADEON_LOCAL_VIEWER                 (1 << 2) +#       define RADEON_NORMALIZE_NORMALS            (1 << 3) +#       define RADEON_RESCALE_NORMALS              (1 << 4) +#       define RADEON_SPECULAR_LIGHTS              (1 << 5) +#       define RADEON_DIFFUSE_SPECULAR_COMBINE     (1 << 6) +#       define RADEON_LIGHT_ALPHA                  (1 << 7) +#       define RADEON_LOCAL_LIGHT_VEC_GL           (1 << 8) +#       define RADEON_LIGHT_NO_NORMAL_AMBIENT_ONLY (1 << 9) +#       define RADEON_LM_SOURCE_STATE_PREMULT      0 +#       define RADEON_LM_SOURCE_STATE_MULT         1 +#       define RADEON_LM_SOURCE_VERTEX_DIFFUSE     2 +#       define RADEON_LM_SOURCE_VERTEX_SPECULAR    3 +#       define RADEON_EMISSIVE_SOURCE_SHIFT        16 +#       define RADEON_AMBIENT_SOURCE_SHIFT         18 +#       define RADEON_DIFFUSE_SOURCE_SHIFT         20 +#       define RADEON_SPECULAR_SOURCE_SHIFT        22 +#define RADEON_SE_TCL_MATERIAL_AMBIENT_RED     0x2220 +#define RADEON_SE_TCL_MATERIAL_AMBIENT_GREEN   0x2224 +#define RADEON_SE_TCL_MATERIAL_AMBIENT_BLUE    0x2228 +#define RADEON_SE_TCL_MATERIAL_AMBIENT_ALPHA   0x222c +#define RADEON_SE_TCL_MATERIAL_DIFFUSE_RED     0x2230 +#define RADEON_SE_TCL_MATERIAL_DIFFUSE_GREEN   0x2234 +#define RADEON_SE_TCL_MATERIAL_DIFFUSE_BLUE    0x2238 +#define RADEON_SE_TCL_MATERIAL_DIFFUSE_ALPHA   0x223c +#define RADEON_SE_TCL_MATERIAL_EMMISSIVE_RED   0x2210 +#define RADEON_SE_TCL_MATERIAL_EMMISSIVE_GREEN 0x2214 +#define RADEON_SE_TCL_MATERIAL_EMMISSIVE_BLUE  0x2218 +#define RADEON_SE_TCL_MATERIAL_EMMISSIVE_ALPHA 0x221c +#define RADEON_SE_TCL_MATERIAL_SPECULAR_RED    0x2240 +#define RADEON_SE_TCL_MATERIAL_SPECULAR_GREEN  0x2244 +#define RADEON_SE_TCL_MATERIAL_SPECULAR_BLUE   0x2248 +#define RADEON_SE_TCL_MATERIAL_SPECULAR_ALPHA  0x224c +#define RADEON_SE_TCL_MATRIX_SELECT_0       0x225c +#       define RADEON_MODELVIEW_0_SHIFT        0 +#       define RADEON_MODELVIEW_1_SHIFT        4 +#       define RADEON_MODELVIEW_2_SHIFT        8 +#       define RADEON_MODELVIEW_3_SHIFT        12 +#       define RADEON_IT_MODELVIEW_0_SHIFT     16 +#       define RADEON_IT_MODELVIEW_1_SHIFT     20 +#       define RADEON_IT_MODELVIEW_2_SHIFT     24 +#       define RADEON_IT_MODELVIEW_3_SHIFT     28 +#define RADEON_SE_TCL_MATRIX_SELECT_1       0x2260 +#       define RADEON_MODELPROJECT_0_SHIFT     0 +#       define RADEON_MODELPROJECT_1_SHIFT     4 +#       define RADEON_MODELPROJECT_2_SHIFT     8 +#       define RADEON_MODELPROJECT_3_SHIFT     12 +#       define RADEON_TEXMAT_0_SHIFT           16 +#       define RADEON_TEXMAT_1_SHIFT           20 +#       define RADEON_TEXMAT_2_SHIFT           24 +#       define RADEON_TEXMAT_3_SHIFT           28 + + +#define RADEON_SE_TCL_OUTPUT_VTX_FMT        0x2254 +#       define RADEON_TCL_VTX_W0                 (1 <<  0) +#       define RADEON_TCL_VTX_FP_DIFFUSE         (1 <<  1) +#       define RADEON_TCL_VTX_FP_ALPHA           (1 <<  2) +#       define RADEON_TCL_VTX_PK_DIFFUSE         (1 <<  3) +#       define RADEON_TCL_VTX_FP_SPEC            (1 <<  4) +#       define RADEON_TCL_VTX_FP_FOG             (1 <<  5) +#       define RADEON_TCL_VTX_PK_SPEC            (1 <<  6) +#       define RADEON_TCL_VTX_ST0                (1 <<  7) +#       define RADEON_TCL_VTX_ST1                (1 <<  8) +#       define RADEON_TCL_VTX_Q1                 (1 <<  9) +#       define RADEON_TCL_VTX_ST2                (1 << 10) +#       define RADEON_TCL_VTX_Q2                 (1 << 11) +#       define RADEON_TCL_VTX_ST3                (1 << 12) +#       define RADEON_TCL_VTX_Q3                 (1 << 13) +#       define RADEON_TCL_VTX_Q0                 (1 << 14) +#       define RADEON_TCL_VTX_WEIGHT_COUNT_SHIFT 15 +#       define RADEON_TCL_VTX_NORM0              (1 << 18) +#       define RADEON_TCL_VTX_XY1                (1 << 27) +#       define RADEON_TCL_VTX_Z1                 (1 << 28) +#       define RADEON_TCL_VTX_W1                 (1 << 29) +#       define RADEON_TCL_VTX_NORM1              (1 << 30) +#       define RADEON_TCL_VTX_Z0                 (1 << 31) + +#define RADEON_SE_TCL_OUTPUT_VTX_SEL        0x2258 +#       define RADEON_TCL_COMPUTE_XYZW           (1 << 0) +#       define RADEON_TCL_COMPUTE_DIFFUSE        (1 << 1) +#       define RADEON_TCL_COMPUTE_SPECULAR       (1 << 2) +#       define RADEON_TCL_FORCE_NAN_IF_COLOR_NAN (1 << 3) +#       define RADEON_TCL_FORCE_INORDER_PROC     (1 << 4) +#       define RADEON_TCL_TEX_INPUT_TEX_0        0 +#       define RADEON_TCL_TEX_INPUT_TEX_1        1 +#       define RADEON_TCL_TEX_INPUT_TEX_2        2 +#       define RADEON_TCL_TEX_INPUT_TEX_3        3 +#       define RADEON_TCL_TEX_COMPUTED_TEX_0     8 +#       define RADEON_TCL_TEX_COMPUTED_TEX_1     9 +#       define RADEON_TCL_TEX_COMPUTED_TEX_2     10 +#       define RADEON_TCL_TEX_COMPUTED_TEX_3     11 +#       define RADEON_TCL_TEX_0_OUTPUT_SHIFT     16 +#       define RADEON_TCL_TEX_1_OUTPUT_SHIFT     20 +#       define RADEON_TCL_TEX_2_OUTPUT_SHIFT     24 +#       define RADEON_TCL_TEX_3_OUTPUT_SHIFT     28 + +#define RADEON_SE_TCL_PER_LIGHT_CTL_0       0x2270 +#       define RADEON_LIGHT_0_ENABLE               (1 <<  0) +#       define RADEON_LIGHT_0_ENABLE_AMBIENT       (1 <<  1) +#       define RADEON_LIGHT_0_ENABLE_SPECULAR      (1 <<  2) +#       define RADEON_LIGHT_0_IS_LOCAL             (1 <<  3) +#       define RADEON_LIGHT_0_IS_SPOT              (1 <<  4) +#       define RADEON_LIGHT_0_DUAL_CONE            (1 <<  5) +#       define RADEON_LIGHT_0_ENABLE_RANGE_ATTEN   (1 <<  6) +#       define RADEON_LIGHT_0_CONSTANT_RANGE_ATTEN (1 <<  7) +#       define RADEON_LIGHT_0_SHIFT                0 +#       define RADEON_LIGHT_1_ENABLE               (1 << 16) +#       define RADEON_LIGHT_1_ENABLE_AMBIENT       (1 << 17) +#       define RADEON_LIGHT_1_ENABLE_SPECULAR      (1 << 18) +#       define RADEON_LIGHT_1_IS_LOCAL             (1 << 19) +#       define RADEON_LIGHT_1_IS_SPOT              (1 << 20) +#       define RADEON_LIGHT_1_DUAL_CONE            (1 << 21) +#       define RADEON_LIGHT_1_ENABLE_RANGE_ATTEN   (1 << 22) +#       define RADEON_LIGHT_1_CONSTANT_RANGE_ATTEN (1 << 23) +#       define RADEON_LIGHT_1_SHIFT                16 +#define RADEON_SE_TCL_PER_LIGHT_CTL_1       0x2274 +#       define RADEON_LIGHT_2_SHIFT            0 +#       define RADEON_LIGHT_3_SHIFT            16 +#define RADEON_SE_TCL_PER_LIGHT_CTL_2       0x2278 +#       define RADEON_LIGHT_4_SHIFT            0 +#       define RADEON_LIGHT_5_SHIFT            16 +#define RADEON_SE_TCL_PER_LIGHT_CTL_3       0x227c +#       define RADEON_LIGHT_6_SHIFT            0 +#       define RADEON_LIGHT_7_SHIFT            16 + +#define RADEON_SE_TCL_SHININESS             0x2250 + +#define RADEON_SE_TCL_TEXTURE_PROC_CTL      0x2268 +#       define RADEON_TEXGEN_TEXMAT_0_ENABLE      (1 << 0) +#       define RADEON_TEXGEN_TEXMAT_1_ENABLE      (1 << 1) +#       define RADEON_TEXGEN_TEXMAT_2_ENABLE      (1 << 2) +#       define RADEON_TEXGEN_TEXMAT_3_ENABLE      (1 << 3) +#       define RADEON_TEXMAT_0_ENABLE             (1 << 4) +#       define RADEON_TEXMAT_1_ENABLE             (1 << 5) +#       define RADEON_TEXMAT_2_ENABLE             (1 << 6) +#       define RADEON_TEXMAT_3_ENABLE             (1 << 7) +#       define RADEON_TEXGEN_INPUT_MASK           0xf +#       define RADEON_TEXGEN_INPUT_TEXCOORD_0     0 +#       define RADEON_TEXGEN_INPUT_TEXCOORD_1     1 +#       define RADEON_TEXGEN_INPUT_TEXCOORD_2     2 +#       define RADEON_TEXGEN_INPUT_TEXCOORD_3     3 +#       define RADEON_TEXGEN_INPUT_OBJ            4 +#       define RADEON_TEXGEN_INPUT_EYE            5 +#       define RADEON_TEXGEN_INPUT_EYE_NORMAL     6 +#       define RADEON_TEXGEN_INPUT_EYE_REFLECT    7 +#       define RADEON_TEXGEN_INPUT_EYE_NORMALIZED 8 +#       define RADEON_TEXGEN_0_INPUT_SHIFT        16 +#       define RADEON_TEXGEN_1_INPUT_SHIFT        20 +#       define RADEON_TEXGEN_2_INPUT_SHIFT        24 +#       define RADEON_TEXGEN_3_INPUT_SHIFT        28 + +#define RADEON_SE_TCL_UCP_VERT_BLEND_CTL    0x2264 +#       define RADEON_UCP_IN_CLIP_SPACE            (1 <<  0) +#       define RADEON_UCP_IN_MODEL_SPACE           (1 <<  1) +#       define RADEON_UCP_ENABLE_0                 (1 <<  2) +#       define RADEON_UCP_ENABLE_1                 (1 <<  3) +#       define RADEON_UCP_ENABLE_2                 (1 <<  4) +#       define RADEON_UCP_ENABLE_3                 (1 <<  5) +#       define RADEON_UCP_ENABLE_4                 (1 <<  6) +#       define RADEON_UCP_ENABLE_5                 (1 <<  7) +#       define RADEON_TCL_FOG_MASK                 (3 <<  8) +#       define RADEON_TCL_FOG_DISABLE              (0 <<  8) +#       define RADEON_TCL_FOG_EXP                  (1 <<  8) +#       define RADEON_TCL_FOG_EXP2                 (2 <<  8) +#       define RADEON_TCL_FOG_LINEAR               (3 <<  8) +#       define RADEON_RNG_BASED_FOG                (1 << 10) +#       define RADEON_LIGHT_TWOSIDE                (1 << 11) +#       define RADEON_BLEND_OP_COUNT_MASK          (7 << 12) +#       define RADEON_BLEND_OP_COUNT_SHIFT         12 +#       define RADEON_POSITION_BLEND_OP_ENABLE     (1 << 16) +#       define RADEON_NORMAL_BLEND_OP_ENABLE       (1 << 17) +#       define RADEON_VERTEX_BLEND_SRC_0_PRIMARY   (1 << 18) +#       define RADEON_VERTEX_BLEND_SRC_0_SECONDARY (1 << 18) +#       define RADEON_VERTEX_BLEND_SRC_1_PRIMARY   (1 << 19) +#       define RADEON_VERTEX_BLEND_SRC_1_SECONDARY (1 << 19) +#       define RADEON_VERTEX_BLEND_SRC_2_PRIMARY   (1 << 20) +#       define RADEON_VERTEX_BLEND_SRC_2_SECONDARY (1 << 20) +#       define RADEON_VERTEX_BLEND_SRC_3_PRIMARY   (1 << 21) +#       define RADEON_VERTEX_BLEND_SRC_3_SECONDARY (1 << 21) +#       define RADEON_VERTEX_BLEND_WGT_MINUS_ONE   (1 << 22) +#       define RADEON_CULL_FRONT_IS_CW             (0 << 28) +#       define RADEON_CULL_FRONT_IS_CCW            (1 << 28) +#       define RADEON_CULL_FRONT                   (1 << 29) +#       define RADEON_CULL_BACK                    (1 << 30) +#       define RADEON_FORCE_W_TO_ONE               (1 << 31) + +#define RADEON_SE_VPORT_XSCALE              0x1d98 +#define RADEON_SE_VPORT_XOFFSET             0x1d9c +#define RADEON_SE_VPORT_YSCALE              0x1da0 +#define RADEON_SE_VPORT_YOFFSET             0x1da4 +#define RADEON_SE_VPORT_ZSCALE              0x1da8 +#define RADEON_SE_VPORT_ZOFFSET             0x1dac +#define RADEON_SE_ZBIAS_FACTOR              0x1db0 +#define RADEON_SE_ZBIAS_CONSTANT            0x1db4 + +#define RADEON_SE_VTX_FMT                   0x2080 +#       define RADEON_SE_VTX_FMT_XY         0x00000000 +#       define RADEON_SE_VTX_FMT_W0         0x00000001 +#       define RADEON_SE_VTX_FMT_FPCOLOR    0x00000002 +#       define RADEON_SE_VTX_FMT_FPALPHA    0x00000004 +#       define RADEON_SE_VTX_FMT_PKCOLOR    0x00000008 +#       define RADEON_SE_VTX_FMT_FPSPEC     0x00000010 +#       define RADEON_SE_VTX_FMT_FPFOG      0x00000020 +#       define RADEON_SE_VTX_FMT_PKSPEC     0x00000040 +#       define RADEON_SE_VTX_FMT_ST0        0x00000080 +#       define RADEON_SE_VTX_FMT_ST1        0x00000100 +#       define RADEON_SE_VTX_FMT_Q1         0x00000200 +#       define RADEON_SE_VTX_FMT_ST2        0x00000400 +#       define RADEON_SE_VTX_FMT_Q2         0x00000800 +#       define RADEON_SE_VTX_FMT_ST3        0x00001000 +#       define RADEON_SE_VTX_FMT_Q3         0x00002000 +#       define RADEON_SE_VTX_FMT_Q0         0x00004000 +#       define RADEON_SE_VTX_FMT_BLND_WEIGHT_CNT_MASK  0x00038000 +#       define RADEON_SE_VTX_FMT_N0         0x00040000 +#       define RADEON_SE_VTX_FMT_XY1        0x08000000 +#       define RADEON_SE_VTX_FMT_Z1         0x10000000 +#       define RADEON_SE_VTX_FMT_W1         0x20000000 +#       define RADEON_SE_VTX_FMT_N1         0x40000000 +#       define RADEON_SE_VTX_FMT_Z          0x80000000 + +#define RADEON_SE_VF_CNTL                             0x2084 +#       define RADEON_VF_PRIM_TYPE_POINT_LIST         1 +#       define RADEON_VF_PRIM_TYPE_LINE_LIST          2 +#       define RADEON_VF_PRIM_TYPE_LINE_STRIP         3 +#       define RADEON_VF_PRIM_TYPE_TRIANGLE_LIST      4 +#       define RADEON_VF_PRIM_TYPE_TRIANGLE_FAN       5 +#       define RADEON_VF_PRIM_TYPE_TRIANGLE_STRIP     6 +#       define RADEON_VF_PRIM_TYPE_TRIANGLE_FLAG      7 +#       define RADEON_VF_PRIM_TYPE_RECTANGLE_LIST     8 +#       define RADEON_VF_PRIM_TYPE_POINT_LIST_3       9 +#       define RADEON_VF_PRIM_TYPE_LINE_LIST_3        10 +#       define RADEON_VF_PRIM_TYPE_SPIRIT_LIST        11 +#       define RADEON_VF_PRIM_TYPE_LINE_LOOP          12 +#       define RADEON_VF_PRIM_TYPE_QUAD_LIST          13 +#       define RADEON_VF_PRIM_TYPE_QUAD_STRIP         14 +#       define RADEON_VF_PRIM_TYPE_POLYGON            15 +#       define RADEON_VF_PRIM_WALK_STATE              (0<<4) +#       define RADEON_VF_PRIM_WALK_INDEX              (1<<4) +#       define RADEON_VF_PRIM_WALK_LIST               (2<<4) +#       define RADEON_VF_PRIM_WALK_DATA               (3<<4) +#       define RADEON_VF_COLOR_ORDER_RGBA             (1<<6) +#       define RADEON_VF_RADEON_MODE                  (1<<8) +#       define RADEON_VF_TCL_OUTPUT_CTL_ENA           (1<<9) +#       define RADEON_VF_PROG_STREAM_ENA              (1<<10) +#       define RADEON_VF_INDEX_SIZE_SHIFT             11 +#       define RADEON_VF_NUM_VERTICES_SHIFT           16 + +#define RADEON_SE_PORT_DATA0			0x2000 +  +#define R200_SE_VAP_CNTL			0x2080 +#       define R200_VAP_TCL_ENABLE		0x00000001 +#       define R200_VAP_SINGLE_BUF_STATE_ENABLE	0x00000010 +#       define R200_VAP_FORCE_W_TO_ONE		0x00010000 +#       define R200_VAP_D3D_TEX_DEFAULT		0x00020000 +#       define R200_VAP_VF_MAX_VTX_NUM__SHIFT	18 +#       define R200_VAP_VF_MAX_VTX_NUM		(9 << 18) +#       define R200_VAP_DX_CLIP_SPACE_DEF	0x00400000 +#define R200_VF_MAX_VTX_INDX			0x210c +#define R200_VF_MIN_VTX_INDX			0x2110 +#define R200_SE_VTE_CNTL			0x20b0 +#       define R200_VPORT_X_SCALE_ENA			0x00000001 +#       define R200_VPORT_X_OFFSET_ENA			0x00000002 +#       define R200_VPORT_Y_SCALE_ENA			0x00000004 +#       define R200_VPORT_Y_OFFSET_ENA			0x00000008 +#       define R200_VPORT_Z_SCALE_ENA			0x00000010 +#       define R200_VPORT_Z_OFFSET_ENA			0x00000020 +#       define R200_VTX_XY_FMT				0x00000100 +#       define R200_VTX_Z_FMT				0x00000200 +#       define R200_VTX_W0_FMT				0x00000400 +#       define R200_VTX_W0_NORMALIZE			0x00000800 +#       define R200_VTX_ST_DENORMALIZED		0x00001000 +#define R200_SE_VAP_CNTL_STATUS			0x2140 +#       define R200_VC_NO_SWAP			(0 << 0) +#       define R200_VC_16BIT_SWAP		(1 << 0) +#       define R200_VC_32BIT_SWAP		(2 << 0) +#define R200_PP_TXFILTER_0			0x2c00  +#define R200_PP_TXFILTER_1			0x2c20 +#define R200_PP_TXFILTER_2			0x2c40 +#define R200_PP_TXFILTER_3			0x2c60 +#define R200_PP_TXFILTER_4			0x2c80 +#define R200_PP_TXFILTER_5			0x2ca0 +#       define R200_MAG_FILTER_NEAREST		(0  <<  0) +#       define R200_MAG_FILTER_LINEAR		(1  <<  0) +#       define R200_MAG_FILTER_MASK		(1  <<  0) +#       define R200_MIN_FILTER_NEAREST		(0  <<  1) +#       define R200_MIN_FILTER_LINEAR		(1  <<  1) +#       define R200_MIN_FILTER_NEAREST_MIP_NEAREST (2  <<  1) +#       define R200_MIN_FILTER_NEAREST_MIP_LINEAR (3  <<  1) +#       define R200_MIN_FILTER_LINEAR_MIP_NEAREST (6  <<  1) +#       define R200_MIN_FILTER_LINEAR_MIP_LINEAR (7  <<  1) +#       define R200_MIN_FILTER_ANISO_NEAREST	(8  <<  1) +#       define R200_MIN_FILTER_ANISO_LINEAR	(9  <<  1) +#       define R200_MIN_FILTER_ANISO_NEAREST_MIP_NEAREST (10 <<  1) +#       define R200_MIN_FILTER_ANISO_NEAREST_MIP_LINEAR (11 <<  1) +#       define R200_MIN_FILTER_MASK		(15 <<  1) +#       define R200_MAX_ANISO_1_TO_1		(0  <<  5) +#       define R200_MAX_ANISO_2_TO_1		(1  <<  5) +#       define R200_MAX_ANISO_4_TO_1		(2  <<  5) +#       define R200_MAX_ANISO_8_TO_1		(3  <<  5) +#       define R200_MAX_ANISO_16_TO_1		(4  <<  5) +#       define R200_MAX_ANISO_MASK		(7  <<  5) +#       define R200_MAX_MIP_LEVEL_MASK		(0x0f << 16) +#       define R200_MAX_MIP_LEVEL_SHIFT		16 +#       define R200_YUV_TO_RGB			(1  << 20) +#       define R200_YUV_TEMPERATURE_COOL	(0  << 21) +#       define R200_YUV_TEMPERATURE_HOT		(1  << 21) +#       define R200_YUV_TEMPERATURE_MASK	(1  << 21) +#       define R200_WRAPEN_S			(1  << 22) +#       define R200_CLAMP_S_WRAP		(0  << 23) +#       define R200_CLAMP_S_MIRROR		(1  << 23) +#       define R200_CLAMP_S_CLAMP_LAST		(2  << 23) +#       define R200_CLAMP_S_MIRROR_CLAMP_LAST	(3  << 23) +#       define R200_CLAMP_S_CLAMP_BORDER	(4  << 23) +#       define R200_CLAMP_S_MIRROR_CLAMP_BORDER	(5  << 23) +#       define R200_CLAMP_S_CLAMP_GL		(6  << 23) +#       define R200_CLAMP_S_MIRROR_CLAMP_GL	(7  << 23) +#       define R200_CLAMP_S_MASK		(7  << 23) +#       define R200_WRAPEN_T			(1  << 26) +#       define R200_CLAMP_T_WRAP		(0  << 27) +#       define R200_CLAMP_T_MIRROR		(1  << 27) +#       define R200_CLAMP_T_CLAMP_LAST		(2  << 27) +#       define R200_CLAMP_T_MIRROR_CLAMP_LAST	(3  << 27) +#       define R200_CLAMP_T_CLAMP_BORDER	(4  << 27) +#       define R200_CLAMP_T_MIRROR_CLAMP_BORDER	(5  << 27) +#       define R200_CLAMP_T_CLAMP_GL		(6  << 27) +#       define R200_CLAMP_T_MIRROR_CLAMP_GL	(7  << 27) +#       define R200_CLAMP_T_MASK		(7  << 27) +#       define R200_KILL_LT_ZERO		(1  << 30) +#       define R200_BORDER_MODE_OGL		(0  << 31) +#       define R200_BORDER_MODE_D3D		(1  << 31) +#define R200_PP_TXFORMAT_0			0x2c04 +#define R200_PP_TXFORMAT_1			0x2c24 +#define R200_PP_TXFORMAT_2			0x2c44 +#define R200_PP_TXFORMAT_3			0x2c64 +#define R200_PP_TXFORMAT_4			0x2c84 +#define R200_PP_TXFORMAT_5			0x2ca4 +#       define R200_TXFORMAT_I8			(0 << 0) +#       define R200_TXFORMAT_AI88		(1 << 0) +#       define R200_TXFORMAT_RGB332		(2 << 0) +#       define R200_TXFORMAT_ARGB1555		(3 << 0) +#       define R200_TXFORMAT_RGB565		(4 << 0) +#       define R200_TXFORMAT_ARGB4444		(5 << 0) +#       define R200_TXFORMAT_ARGB8888		(6 << 0) +#       define R200_TXFORMAT_RGBA8888		(7 << 0) +#       define R200_TXFORMAT_Y8			(8 << 0) +#       define R200_TXFORMAT_AVYU4444		(9 << 0) +#       define R200_TXFORMAT_VYUY422		(10 << 0) +#       define R200_TXFORMAT_YVYU422		(11 << 0) +#       define R200_TXFORMAT_DXT1		(12 << 0) +#       define R200_TXFORMAT_DXT23		(14 << 0) +#       define R200_TXFORMAT_DXT45		(15 << 0) +#       define R200_TXFORMAT_ABGR8888		(22 << 0) +#       define R200_TXFORMAT_FORMAT_MASK	(31 <<	0) +#       define R200_TXFORMAT_FORMAT_SHIFT	0 +#       define R200_TXFORMAT_ALPHA_IN_MAP	(1 << 6) +#       define R200_TXFORMAT_NON_POWER2		(1 << 7) +#       define R200_TXFORMAT_WIDTH_MASK		(15 <<	8) +#       define R200_TXFORMAT_WIDTH_SHIFT	8 +#       define R200_TXFORMAT_HEIGHT_MASK	(15 << 12) +#       define R200_TXFORMAT_HEIGHT_SHIFT	12 +#       define R200_TXFORMAT_F5_WIDTH_MASK	(15 << 16)	/* cube face 5 */ +#       define R200_TXFORMAT_F5_WIDTH_SHIFT	16 +#       define R200_TXFORMAT_F5_HEIGHT_MASK	(15 << 20) +#       define R200_TXFORMAT_F5_HEIGHT_SHIFT	20 +#       define R200_TXFORMAT_ST_ROUTE_STQ0	(0 << 24) +#       define R200_TXFORMAT_ST_ROUTE_STQ1	(1 << 24) +#       define R200_TXFORMAT_ST_ROUTE_STQ2	(2 << 24) +#       define R200_TXFORMAT_ST_ROUTE_STQ3	(3 << 24) +#       define R200_TXFORMAT_ST_ROUTE_STQ4	(4 << 24) +#       define R200_TXFORMAT_ST_ROUTE_STQ5	(5 << 24) +#       define R200_TXFORMAT_ST_ROUTE_MASK	(7 << 24) +#       define R200_TXFORMAT_ST_ROUTE_SHIFT	24 +#       define R200_TXFORMAT_ALPHA_MASK_ENABLE	(1 << 28) +#       define R200_TXFORMAT_CHROMA_KEY_ENABLE	(1 << 29) +#       define R200_TXFORMAT_CUBIC_MAP_ENABLE		(1 << 30) +#define R200_PP_TXFORMAT_X_0                    0x2c08 +#define R200_PP_TXFORMAT_X_1                    0x2c28 +#define R200_PP_TXFORMAT_X_2                    0x2c48 +#define R200_PP_TXFORMAT_X_3                    0x2c68 +#define R200_PP_TXFORMAT_X_4                    0x2c88 +#define R200_PP_TXFORMAT_X_5                    0x2ca8 + +#define R200_PP_TXSIZE_0			0x2c0c /* NPOT only */ +#define R200_PP_TXSIZE_1			0x2c2c /* NPOT only */ +#define R200_PP_TXSIZE_2			0x2c4c /* NPOT only */ +#define R200_PP_TXSIZE_3			0x2c6c /* NPOT only */ +#define R200_PP_TXSIZE_4			0x2c8c /* NPOT only */ +#define R200_PP_TXSIZE_5			0x2cac /* NPOT only */ + +#define R200_PP_TXPITCH_0                       0x2c10 /* NPOT only */ +#define R200_PP_TXPITCH_1			0x2c30 /* NPOT only */ +#define R200_PP_TXPITCH_2			0x2c50 /* NPOT only */ +#define R200_PP_TXPITCH_3			0x2c70 /* NPOT only */ +#define R200_PP_TXPITCH_4			0x2c90 /* NPOT only */ +#define R200_PP_TXPITCH_5			0x2cb0 /* NPOT only */ + +#define R200_PP_TXOFFSET_0			0x2d00 +#       define R200_TXO_ENDIAN_NO_SWAP		(0 << 0) +#       define R200_TXO_ENDIAN_BYTE_SWAP	(1 << 0) +#       define R200_TXO_ENDIAN_WORD_SWAP	(2 << 0) +#       define R200_TXO_ENDIAN_HALFDW_SWAP	(3 << 0) +#       define R200_TXO_MACRO_LINEAR		(0 << 2) +#       define R200_TXO_MACRO_TILE		(1 << 2) +#       define R200_TXO_MICRO_LINEAR		(0 << 3) +#       define R200_TXO_MICRO_TILE		(1 << 3) +#       define R200_TXO_OFFSET_MASK		0xffffffe0 +#       define R200_TXO_OFFSET_SHIFT		5 +#define R200_PP_TXOFFSET_1			0x2d18 +#define R200_PP_TXOFFSET_2			0x2d30 +#define R200_PP_TXOFFSET_3			0x2d48 +#define R200_PP_TXOFFSET_4			0x2d60 +#define R200_PP_TXOFFSET_5			0x2d78 + +#define R200_PP_TFACTOR_0			0x2ee0 +#define R200_PP_TFACTOR_1			0x2ee4 +#define R200_PP_TFACTOR_2			0x2ee8 +#define R200_PP_TFACTOR_3			0x2eec +#define R200_PP_TFACTOR_4			0x2ef0 +#define R200_PP_TFACTOR_5			0x2ef4 + +#define R200_PP_TXCBLEND_0			0x2f00 +#       define R200_TXC_ARG_A_ZERO		(0) +#       define R200_TXC_ARG_A_CURRENT_COLOR	(2) +#       define R200_TXC_ARG_A_CURRENT_ALPHA	(3) +#       define R200_TXC_ARG_A_DIFFUSE_COLOR	(4) +#       define R200_TXC_ARG_A_DIFFUSE_ALPHA	(5) +#       define R200_TXC_ARG_A_SPECULAR_COLOR	(6) +#       define R200_TXC_ARG_A_SPECULAR_ALPHA	(7) +#       define R200_TXC_ARG_A_TFACTOR_COLOR	(8) +#       define R200_TXC_ARG_A_TFACTOR_ALPHA	(9) +#       define R200_TXC_ARG_A_R0_COLOR		(10) +#       define R200_TXC_ARG_A_R0_ALPHA		(11) +#       define R200_TXC_ARG_A_R1_COLOR		(12) +#       define R200_TXC_ARG_A_R1_ALPHA		(13) +#       define R200_TXC_ARG_A_R2_COLOR		(14) +#       define R200_TXC_ARG_A_R2_ALPHA		(15) +#       define R200_TXC_ARG_A_R3_COLOR		(16) +#       define R200_TXC_ARG_A_R3_ALPHA		(17) +#       define R200_TXC_ARG_A_R4_COLOR		(18) +#       define R200_TXC_ARG_A_R4_ALPHA		(19) +#       define R200_TXC_ARG_A_R5_COLOR		(20) +#       define R200_TXC_ARG_A_R5_ALPHA		(21) +#       define R200_TXC_ARG_A_TFACTOR1_COLOR	(26) +#       define R200_TXC_ARG_A_TFACTOR1_ALPHA	(27) +#       define R200_TXC_ARG_A_MASK		(31 << 0) +#       define R200_TXC_ARG_A_SHIFT		0 +#       define R200_TXC_ARG_B_ZERO		(0 << 5) +#       define R200_TXC_ARG_B_CURRENT_COLOR	(2 << 5) +#       define R200_TXC_ARG_B_CURRENT_ALPHA	(3 << 5) +#       define R200_TXC_ARG_B_DIFFUSE_COLOR	(4 << 5) +#       define R200_TXC_ARG_B_DIFFUSE_ALPHA	(5 << 5) +#       define R200_TXC_ARG_B_SPECULAR_COLOR	(6 << 5) +#       define R200_TXC_ARG_B_SPECULAR_ALPHA	(7 << 5) +#       define R200_TXC_ARG_B_TFACTOR_COLOR	(8 << 5) +#       define R200_TXC_ARG_B_TFACTOR_ALPHA	(9 << 5) +#       define R200_TXC_ARG_B_R0_COLOR		(10 << 5) +#       define R200_TXC_ARG_B_R0_ALPHA		(11 << 5) +#       define R200_TXC_ARG_B_R1_COLOR		(12 << 5) +#       define R200_TXC_ARG_B_R1_ALPHA		(13 << 5) +#       define R200_TXC_ARG_B_R2_COLOR		(14 << 5) +#       define R200_TXC_ARG_B_R2_ALPHA		(15 << 5) +#       define R200_TXC_ARG_B_R3_COLOR		(16 << 5) +#       define R200_TXC_ARG_B_R3_ALPHA		(17 << 5) +#       define R200_TXC_ARG_B_R4_COLOR		(18 << 5) +#       define R200_TXC_ARG_B_R4_ALPHA		(19 << 5) +#       define R200_TXC_ARG_B_R5_COLOR		(20 << 5) +#       define R200_TXC_ARG_B_R5_ALPHA		(21 << 5) +#       define R200_TXC_ARG_B_TFACTOR1_COLOR	(26 << 5) +#       define R200_TXC_ARG_B_TFACTOR1_ALPHA	(27 << 5) +#       define R200_TXC_ARG_B_MASK		(31 << 5) +#       define R200_TXC_ARG_B_SHIFT		5 +#       define R200_TXC_ARG_C_ZERO		(0 << 10) +#       define R200_TXC_ARG_C_CURRENT_COLOR	(2 << 10) +#       define R200_TXC_ARG_C_CURRENT_ALPHA	(3 << 10) +#       define R200_TXC_ARG_C_DIFFUSE_COLOR	(4 << 10) +#       define R200_TXC_ARG_C_DIFFUSE_ALPHA	(5 << 10) +#       define R200_TXC_ARG_C_SPECULAR_COLOR	(6 << 10) +#       define R200_TXC_ARG_C_SPECULAR_ALPHA	(7 << 10) +#       define R200_TXC_ARG_C_TFACTOR_COLOR	(8 << 10) +#       define R200_TXC_ARG_C_TFACTOR_ALPHA	(9 << 10) +#       define R200_TXC_ARG_C_R0_COLOR		(10 << 10) +#       define R200_TXC_ARG_C_R0_ALPHA		(11 << 10) +#       define R200_TXC_ARG_C_R1_COLOR		(12 << 10) +#       define R200_TXC_ARG_C_R1_ALPHA		(13 << 10) +#       define R200_TXC_ARG_C_R2_COLOR		(14 << 10) +#       define R200_TXC_ARG_C_R2_ALPHA		(15 << 10) +#       define R200_TXC_ARG_C_R3_COLOR		(16 << 10) +#       define R200_TXC_ARG_C_R3_ALPHA		(17 << 10) +#       define R200_TXC_ARG_C_R4_COLOR		(18 << 10) +#       define R200_TXC_ARG_C_R4_ALPHA		(19 << 10) +#       define R200_TXC_ARG_C_R5_COLOR		(20 << 10) +#       define R200_TXC_ARG_C_R5_ALPHA		(21 << 10) +#       define R200_TXC_ARG_C_TFACTOR1_COLOR	(26 << 10) +#       define R200_TXC_ARG_C_TFACTOR1_ALPHA	(27 << 10) +#       define R200_TXC_ARG_C_MASK		(31 << 10) +#       define R200_TXC_ARG_C_SHIFT		10 +#       define R200_TXC_COMP_ARG_A		(1 << 16) +#       define R200_TXC_COMP_ARG_A_SHIFT	(16) +#       define R200_TXC_BIAS_ARG_A		(1 << 17) +#       define R200_TXC_SCALE_ARG_A		(1 << 18) +#       define R200_TXC_NEG_ARG_A		(1 << 19) +#       define R200_TXC_COMP_ARG_B		(1 << 20) +#       define R200_TXC_COMP_ARG_B_SHIFT	(20) +#       define R200_TXC_BIAS_ARG_B		(1 << 21) +#       define R200_TXC_SCALE_ARG_B		(1 << 22) +#       define R200_TXC_NEG_ARG_B		(1 << 23) +#       define R200_TXC_COMP_ARG_C		(1 << 24) +#       define R200_TXC_COMP_ARG_C_SHIFT	(24) +#       define R200_TXC_BIAS_ARG_C		(1 << 25) +#       define R200_TXC_SCALE_ARG_C		(1 << 26) +#       define R200_TXC_NEG_ARG_C		(1 << 27) +#       define R200_TXC_OP_MADD			(0 << 28) +#       define R200_TXC_OP_CND0			(2 << 28) +#       define R200_TXC_OP_LERP			(3 << 28) +#       define R200_TXC_OP_DOT3			(4 << 28) +#       define R200_TXC_OP_DOT4			(5 << 28) +#       define R200_TXC_OP_CONDITIONAL		(6 << 28) +#       define R200_TXC_OP_DOT2_ADD		(7 << 28) +#       define R200_TXC_OP_MASK			(7 << 28) +#define R200_PP_TXCBLEND2_0		0x2f04 +#       define R200_TXC_TFACTOR_SEL_SHIFT	0 +#       define R200_TXC_TFACTOR_SEL_MASK	0x7 +#       define R200_TXC_TFACTOR1_SEL_SHIFT	4 +#       define R200_TXC_TFACTOR1_SEL_MASK	(0x7 << 4) +#       define R200_TXC_SCALE_SHIFT		8 +#       define R200_TXC_SCALE_MASK		(7 << 8) +#       define R200_TXC_SCALE_1X		(0 << 8) +#       define R200_TXC_SCALE_2X		(1 << 8) +#       define R200_TXC_SCALE_4X		(2 << 8) +#       define R200_TXC_SCALE_8X		(3 << 8) +#       define R200_TXC_SCALE_INV2		(5 << 8) +#       define R200_TXC_SCALE_INV4		(6 << 8) +#       define R200_TXC_SCALE_INV8		(7 << 8) +#       define R200_TXC_CLAMP_SHIFT		12 +#       define R200_TXC_CLAMP_MASK		(3 << 12) +#       define R200_TXC_CLAMP_WRAP		(0 << 12) +#       define R200_TXC_CLAMP_0_1		(1 << 12) +#       define R200_TXC_CLAMP_8_8		(2 << 12) +#       define R200_TXC_OUTPUT_REG_MASK		(7 << 16) +#       define R200_TXC_OUTPUT_REG_NONE		(0 << 16) +#       define R200_TXC_OUTPUT_REG_R0		(1 << 16) +#       define R200_TXC_OUTPUT_REG_R1		(2 << 16) +#       define R200_TXC_OUTPUT_REG_R2		(3 << 16) +#       define R200_TXC_OUTPUT_REG_R3		(4 << 16) +#       define R200_TXC_OUTPUT_REG_R4		(5 << 16) +#       define R200_TXC_OUTPUT_REG_R5		(6 << 16) +#       define R200_TXC_OUTPUT_MASK_MASK	(7 << 20) +#       define R200_TXC_OUTPUT_MASK_RGB		(0 << 20) +#       define R200_TXC_OUTPUT_MASK_RG		(1 << 20) +#       define R200_TXC_OUTPUT_MASK_RB		(2 << 20) +#       define R200_TXC_OUTPUT_MASK_R		(3 << 20) +#       define R200_TXC_OUTPUT_MASK_GB		(4 << 20) +#       define R200_TXC_OUTPUT_MASK_G		(5 << 20) +#       define R200_TXC_OUTPUT_MASK_B		(6 << 20) +#       define R200_TXC_OUTPUT_MASK_NONE	(7 << 20) +#       define R200_TXC_REPL_NORMAL		0 +#       define R200_TXC_REPL_RED		1 +#       define R200_TXC_REPL_GREEN		2 +#       define R200_TXC_REPL_BLUE		3 +#       define R200_TXC_REPL_ARG_A_SHIFT	26 +#       define R200_TXC_REPL_ARG_A_MASK		(3 << 26) +#       define R200_TXC_REPL_ARG_B_SHIFT	28 +#       define R200_TXC_REPL_ARG_B_MASK		(3 << 28) +#       define R200_TXC_REPL_ARG_C_SHIFT	30 +#       define R200_TXC_REPL_ARG_C_MASK		(3 << 30) +#define R200_PP_TXABLEND_0			0x2f08 +#       define R200_TXA_ARG_A_ZERO		(0) +#       define R200_TXA_ARG_A_CURRENT_ALPHA	(2) /* guess */ +#       define R200_TXA_ARG_A_CURRENT_BLUE	(3) /* guess */ +#       define R200_TXA_ARG_A_DIFFUSE_ALPHA	(4) +#       define R200_TXA_ARG_A_DIFFUSE_BLUE	(5) +#       define R200_TXA_ARG_A_SPECULAR_ALPHA	(6) +#       define R200_TXA_ARG_A_SPECULAR_BLUE	(7) +#       define R200_TXA_ARG_A_TFACTOR_ALPHA	(8) +#       define R200_TXA_ARG_A_TFACTOR_BLUE	(9) +#       define R200_TXA_ARG_A_R0_ALPHA		(10) +#       define R200_TXA_ARG_A_R0_BLUE		(11) +#       define R200_TXA_ARG_A_R1_ALPHA		(12) +#       define R200_TXA_ARG_A_R1_BLUE		(13) +#       define R200_TXA_ARG_A_R2_ALPHA		(14) +#       define R200_TXA_ARG_A_R2_BLUE		(15) +#       define R200_TXA_ARG_A_R3_ALPHA		(16) +#       define R200_TXA_ARG_A_R3_BLUE		(17) +#       define R200_TXA_ARG_A_R4_ALPHA		(18) +#       define R200_TXA_ARG_A_R4_BLUE		(19) +#       define R200_TXA_ARG_A_R5_ALPHA		(20) +#       define R200_TXA_ARG_A_R5_BLUE		(21) +#       define R200_TXA_ARG_A_TFACTOR1_ALPHA	(26) +#       define R200_TXA_ARG_A_TFACTOR1_BLUE	(27) +#       define R200_TXA_ARG_A_MASK		(31 << 0) +#       define R200_TXA_ARG_A_SHIFT		0 +#       define R200_TXA_ARG_B_ZERO		(0 << 5) +#       define R200_TXA_ARG_B_CURRENT_ALPHA	(2 << 5) /* guess */ +#       define R200_TXA_ARG_B_CURRENT_BLUE	(3 << 5) /* guess */ +#       define R200_TXA_ARG_B_DIFFUSE_ALPHA	(4 << 5) +#       define R200_TXA_ARG_B_DIFFUSE_BLUE	(5 << 5) +#       define R200_TXA_ARG_B_SPECULAR_ALPHA	(6 << 5) +#       define R200_TXA_ARG_B_SPECULAR_BLUE	(7 << 5) +#       define R200_TXA_ARG_B_TFACTOR_ALPHA	(8 << 5) +#       define R200_TXA_ARG_B_TFACTOR_BLUE	(9 << 5) +#       define R200_TXA_ARG_B_R0_ALPHA		(10 << 5) +#       define R200_TXA_ARG_B_R0_BLUE		(11 << 5) +#       define R200_TXA_ARG_B_R1_ALPHA		(12 << 5) +#       define R200_TXA_ARG_B_R1_BLUE		(13 << 5) +#       define R200_TXA_ARG_B_R2_ALPHA		(14 << 5) +#       define R200_TXA_ARG_B_R2_BLUE		(15 << 5) +#       define R200_TXA_ARG_B_R3_ALPHA		(16 << 5) +#       define R200_TXA_ARG_B_R3_BLUE		(17 << 5) +#       define R200_TXA_ARG_B_R4_ALPHA		(18 << 5) +#       define R200_TXA_ARG_B_R4_BLUE		(19 << 5) +#       define R200_TXA_ARG_B_R5_ALPHA		(20 << 5) +#       define R200_TXA_ARG_B_R5_BLUE		(21 << 5) +#       define R200_TXA_ARG_B_TFACTOR1_ALPHA	(26 << 5) +#       define R200_TXA_ARG_B_TFACTOR1_BLUE	(27 << 5) +#       define R200_TXA_ARG_B_MASK		(31 << 5) +#       define R200_TXA_ARG_B_SHIFT			5 +#       define R200_TXA_ARG_C_ZERO		(0 << 10) +#       define R200_TXA_ARG_C_CURRENT_ALPHA	(2 << 10) /* guess */ +#       define R200_TXA_ARG_C_CURRENT_BLUE	(3 << 10) /* guess */ +#       define R200_TXA_ARG_C_DIFFUSE_ALPHA	(4 << 10) +#       define R200_TXA_ARG_C_DIFFUSE_BLUE	(5 << 10) +#       define R200_TXA_ARG_C_SPECULAR_ALPHA	(6 << 10) +#       define R200_TXA_ARG_C_SPECULAR_BLUE	(7 << 10) +#       define R200_TXA_ARG_C_TFACTOR_ALPHA	(8 << 10) +#       define R200_TXA_ARG_C_TFACTOR_BLUE	(9 << 10) +#       define R200_TXA_ARG_C_R0_ALPHA		(10 << 10) +#       define R200_TXA_ARG_C_R0_BLUE		(11 << 10) +#       define R200_TXA_ARG_C_R1_ALPHA		(12 << 10) +#       define R200_TXA_ARG_C_R1_BLUE		(13 << 10) +#       define R200_TXA_ARG_C_R2_ALPHA		(14 << 10) +#       define R200_TXA_ARG_C_R2_BLUE		(15 << 10) +#       define R200_TXA_ARG_C_R3_ALPHA		(16 << 10) +#       define R200_TXA_ARG_C_R3_BLUE		(17 << 10) +#       define R200_TXA_ARG_C_R4_ALPHA		(18 << 10) +#       define R200_TXA_ARG_C_R4_BLUE		(19 << 10) +#       define R200_TXA_ARG_C_R5_ALPHA		(20 << 10) +#       define R200_TXA_ARG_C_R5_BLUE		(21 << 10) +#       define R200_TXA_ARG_C_TFACTOR1_ALPHA	(26 << 10) +#       define R200_TXA_ARG_C_TFACTOR1_BLUE	(27 << 10) +#       define R200_TXA_ARG_C_MASK		(31 << 10) +#       define R200_TXA_ARG_C_SHIFT		10 +#       define R200_TXA_COMP_ARG_A		(1 << 16) +#       define R200_TXA_COMP_ARG_A_SHIFT	(16) +#       define R200_TXA_BIAS_ARG_A		(1 << 17) +#       define R200_TXA_SCALE_ARG_A		(1 << 18) +#       define R200_TXA_NEG_ARG_A		(1 << 19) +#       define R200_TXA_COMP_ARG_B		(1 << 20) +#       define R200_TXA_COMP_ARG_B_SHIFT	(20) +#       define R200_TXA_BIAS_ARG_B		(1 << 21) +#       define R200_TXA_SCALE_ARG_B		(1 << 22) +#       define R200_TXA_NEG_ARG_B		(1 << 23) +#       define R200_TXA_COMP_ARG_C		(1 << 24) +#       define R200_TXA_COMP_ARG_C_SHIFT	(24) +#       define R200_TXA_BIAS_ARG_C		(1 << 25) +#       define R200_TXA_SCALE_ARG_C		(1 << 26) +#       define R200_TXA_NEG_ARG_C		(1 << 27) +#       define R200_TXA_OP_MADD			(0 << 28) +#       define R200_TXA_OP_CND0			(2 << 28) +#       define R200_TXA_OP_LERP			(3 << 28) +#       define R200_TXA_OP_CONDITIONAL		(6 << 28) +#       define R200_TXA_OP_MASK			(7 << 28) +#define R200_PP_TXABLEND2_0			0x2f0c +#       define R200_TXA_TFACTOR_SEL_SHIFT	0 +#       define R200_TXA_TFACTOR_SEL_MASK	0x7 +#       define R200_TXA_TFACTOR1_SEL_SHIFT	4 +#       define R200_TXA_TFACTOR1_SEL_MASK	(0x7 << 4) +#       define R200_TXA_SCALE_SHIFT		8 +#       define R200_TXA_SCALE_MASK		(7 << 8) +#       define R200_TXA_SCALE_1X		(0 << 8) +#       define R200_TXA_SCALE_2X		(1 << 8) +#       define R200_TXA_SCALE_4X		(2 << 8) +#       define R200_TXA_SCALE_8X		(3 << 8) +#       define R200_TXA_SCALE_INV2		(5 << 8) +#       define R200_TXA_SCALE_INV4		(6 << 8) +#       define R200_TXA_SCALE_INV8		(7 << 8) +#       define R200_TXA_CLAMP_SHIFT		12 +#       define R200_TXA_CLAMP_MASK		(3 << 12) +#       define R200_TXA_CLAMP_WRAP		(0 << 12) +#       define R200_TXA_CLAMP_0_1		(1 << 12) +#       define R200_TXA_CLAMP_8_8		(2 << 12) +#       define R200_TXA_OUTPUT_REG_MASK		(7 << 16) +#       define R200_TXA_OUTPUT_REG_NONE		(0 << 16) +#       define R200_TXA_OUTPUT_REG_R0		(1 << 16) +#       define R200_TXA_OUTPUT_REG_R1		(2 << 16) +#       define R200_TXA_OUTPUT_REG_R2		(3 << 16) +#       define R200_TXA_OUTPUT_REG_R3		(4 << 16) +#       define R200_TXA_OUTPUT_REG_R4		(5 << 16) +#       define R200_TXA_OUTPUT_REG_R5		(6 << 16) +#       define R200_TXA_DOT_ALPHA		(1 << 20) +#       define R200_TXA_REPL_NORMAL		0 +#       define R200_TXA_REPL_RED		1 +#       define R200_TXA_REPL_GREEN		2 +#       define R200_TXA_REPL_ARG_A_SHIFT	26 +#       define R200_TXA_REPL_ARG_A_MASK		(3 << 26) +#       define R200_TXA_REPL_ARG_B_SHIFT	28 +#       define R200_TXA_REPL_ARG_B_MASK		(3 << 28) +#       define R200_TXA_REPL_ARG_C_SHIFT	30 +#       define R200_TXA_REPL_ARG_C_MASK		(3 << 30) + +#define R200_SE_VTX_FMT_0			0x2088 +#       define R200_VTX_XY			0 /* always have xy */ +#       define R200_VTX_Z0			(1<<0) +#       define R200_VTX_W0			(1<<1) +#       define R200_VTX_WEIGHT_COUNT_SHIFT	(2) +#       define R200_VTX_PV_MATRIX_SEL		(1<<5) +#       define R200_VTX_N0			(1<<6) +#       define R200_VTX_POINT_SIZE		(1<<7) +#       define R200_VTX_DISCRETE_FOG		(1<<8) +#       define R200_VTX_SHININESS_0		(1<<9) +#       define R200_VTX_SHININESS_1		(1<<10) +#       define   R200_VTX_COLOR_NOT_PRESENT	0 +#       define   R200_VTX_PK_RGBA		1 +#       define   R200_VTX_FP_RGB		2 +#       define   R200_VTX_FP_RGBA		3 +#       define   R200_VTX_COLOR_MASK		3 +#       define R200_VTX_COLOR_0_SHIFT		11 +#       define R200_VTX_COLOR_1_SHIFT		13 +#       define R200_VTX_COLOR_2_SHIFT		15 +#       define R200_VTX_COLOR_3_SHIFT		17 +#       define R200_VTX_COLOR_4_SHIFT		19 +#       define R200_VTX_COLOR_5_SHIFT		21 +#       define R200_VTX_COLOR_6_SHIFT		23 +#       define R200_VTX_COLOR_7_SHIFT		25 +#       define R200_VTX_XY1			(1<<28) +#       define R200_VTX_Z1			(1<<29) +#       define R200_VTX_W1			(1<<30) +#       define R200_VTX_N1			(1<<31) +#define R200_SE_VTX_FMT_1			0x208c +#       define R200_VTX_TEX0_COMP_CNT_SHIFT	0 +#       define R200_VTX_TEX1_COMP_CNT_SHIFT	3 +#       define R200_VTX_TEX2_COMP_CNT_SHIFT	6 +#       define R200_VTX_TEX3_COMP_CNT_SHIFT	9 +#       define R200_VTX_TEX4_COMP_CNT_SHIFT	12 +#       define R200_VTX_TEX5_COMP_CNT_SHIFT	15 + +#define R200_SE_TCL_OUTPUT_VTX_FMT_0		0x2090 +#define R200_SE_TCL_OUTPUT_VTX_FMT_1		0x2094 +#define R200_SE_TCL_OUTPUT_VTX_COMP_SEL		0x2250 +#       define R200_OUTPUT_XYZW			(1<<0) +#       define R200_OUTPUT_COLOR_0		(1<<8) +#       define R200_OUTPUT_COLOR_1		(1<<9) +#       define R200_OUTPUT_TEX_0		(1<<16) +#       define R200_OUTPUT_TEX_1		(1<<17) +#       define R200_OUTPUT_TEX_2		(1<<18) +#       define R200_OUTPUT_TEX_3		(1<<19) +#       define R200_OUTPUT_TEX_4		(1<<20) +#       define R200_OUTPUT_TEX_5		(1<<21) +#       define R200_OUTPUT_TEX_MASK		(0x3f<<16) +#       define R200_OUTPUT_DISCRETE_FOG		(1<<24) +#       define R200_OUTPUT_PT_SIZE		(1<<25) +#       define R200_FORCE_INORDER_PROC		(1<<31) +#define R200_PP_CNTL_X				0x2cc4 +#define R200_PP_TXMULTI_CTL_0			0x2c1c +#define R200_SE_VTX_STATE_CNTL			0x2180 +#       define R200_UPDATE_USER_COLOR_0_ENA_MASK (1<<16) + +				/* Registers for CP and Microcode Engine */ +#define RADEON_CP_ME_RAM_ADDR               0x07d4 +#define RADEON_CP_ME_RAM_RADDR              0x07d8 +#define RADEON_CP_ME_RAM_DATAH              0x07dc +#define RADEON_CP_ME_RAM_DATAL              0x07e0 + +#define RADEON_CP_RB_BASE                   0x0700 +#define RADEON_CP_RB_CNTL                   0x0704 +#define RADEON_CP_RB_RPTR_ADDR              0x070c +#define RADEON_CP_RB_RPTR                   0x0710 +#define RADEON_CP_RB_WPTR                   0x0714 + +#define RADEON_CP_IB_BASE                   0x0738 +#define RADEON_CP_IB_BUFSZ                  0x073c + +#define RADEON_CP_CSQ_CNTL                  0x0740 +#       define RADEON_CSQ_CNT_PRIMARY_MASK     (0xff << 0) +#       define RADEON_CSQ_PRIDIS_INDDIS        (0    << 28) +#       define RADEON_CSQ_PRIPIO_INDDIS        (1    << 28) +#       define RADEON_CSQ_PRIBM_INDDIS         (2    << 28) +#       define RADEON_CSQ_PRIPIO_INDBM         (3    << 28) +#       define RADEON_CSQ_PRIBM_INDBM          (4    << 28) +#       define RADEON_CSQ_PRIPIO_INDPIO        (15   << 28) +#define RADEON_CP_CSQ_STAT                  0x07f8 +#       define RADEON_CSQ_RPTR_PRIMARY_MASK    (0xff <<  0) +#       define RADEON_CSQ_WPTR_PRIMARY_MASK    (0xff <<  8) +#       define RADEON_CSQ_RPTR_INDIRECT_MASK   (0xff << 16) +#       define RADEON_CSQ_WPTR_INDIRECT_MASK   (0xff << 24) +#define RADEON_CP_CSQ_ADDR                  0x07f0 +#define RADEON_CP_CSQ_DATA                  0x07f4 +#define RADEON_CP_CSQ_APER_PRIMARY          0x1000 +#define RADEON_CP_CSQ_APER_INDIRECT         0x1300 + +#define RADEON_CP_RB_WPTR_DELAY             0x0718 +#       define RADEON_PRE_WRITE_TIMER_SHIFT    0 +#       define RADEON_PRE_WRITE_LIMIT_SHIFT    23 + +#define RADEON_AIC_CNTL                     0x01d0 +#       define RADEON_PCIGART_TRANSLATE_EN     (1 << 0) +#define RADEON_AIC_LO_ADDR                  0x01dc + + + +				/* Constants */ +//#define RADEON_LAST_FRAME_REG               RADEON_GUI_SCRATCH_REG0 +//efine RADEON_LAST_CLEAR_REG               RADEON_GUI_SCRATCH_REG2 + + + +				/* CP packet types */ +#define RADEON_CP_PACKET0                           0x00000000 +#define RADEON_CP_PACKET1                           0x40000000 +#define RADEON_CP_PACKET2                           0x80000000 +#define RADEON_CP_PACKET3                           0xC0000000 +#       define RADEON_CP_PACKET_MASK                0xC0000000 +#       define RADEON_CP_PACKET_COUNT_MASK          0x3fff0000 +#       define RADEON_CP_PACKET_MAX_DWORDS          (1 << 12) +#       define RADEON_CP_PACKET0_REG_MASK           0x000007ff +#       define RADEON_CP_PACKET1_REG0_MASK          0x000007ff +#       define RADEON_CP_PACKET1_REG1_MASK          0x003ff800 + +#define RADEON_CP_PACKET0_ONE_REG_WR                0x00008000 + +#define RADEON_CP_PACKET3_NOP                       0xC0001000 +#define RADEON_CP_PACKET3_NEXT_CHAR                 0xC0001900 +#define RADEON_CP_PACKET3_PLY_NEXTSCAN              0xC0001D00 +#define RADEON_CP_PACKET3_SET_SCISSORS              0xC0001E00 +#define RADEON_CP_PACKET3_3D_RNDR_GEN_INDX_PRIM     0xC0002300 +#define RADEON_CP_PACKET3_LOAD_MICROCODE            0xC0002400 +#define RADEON_CP_PACKET3_WAIT_FOR_IDLE             0xC0002600 +#define RADEON_CP_PACKET3_3D_DRAW_VBUF              0xC0002800 +#define RADEON_CP_PACKET3_3D_DRAW_IMMD              0xC0002900 +#define RADEON_CP_PACKET3_3D_DRAW_INDX              0xC0002A00 +#define RADEON_CP_PACKET3_LOAD_PALETTE              0xC0002C00 +#define R200_CP_PACKET3_3D_DRAW_IMMD_2              0xc0003500 +#define RADEON_CP_PACKET3_3D_LOAD_VBPNTR            0xC0002F00 +#define RADEON_CP_PACKET3_CNTL_PAINT                0xC0009100 +#define RADEON_CP_PACKET3_CNTL_BITBLT               0xC0009200 +#define RADEON_CP_PACKET3_CNTL_SMALLTEXT            0xC0009300 +#define RADEON_CP_PACKET3_CNTL_HOSTDATA_BLT         0xC0009400 +#define RADEON_CP_PACKET3_CNTL_POLYLINE             0xC0009500 +#define RADEON_CP_PACKET3_CNTL_POLYSCANLINES        0xC0009800 +#define RADEON_CP_PACKET3_CNTL_PAINT_MULTI          0xC0009A00 +#define RADEON_CP_PACKET3_CNTL_BITBLT_MULTI         0xC0009B00 +#define RADEON_CP_PACKET3_CNTL_TRANS_BITBLT         0xC0009C00 + + +#define RADEON_CP_VC_FRMT_XY                        0x00000000 +#define RADEON_CP_VC_FRMT_W0                        0x00000001 +#define RADEON_CP_VC_FRMT_FPCOLOR                   0x00000002 +#define RADEON_CP_VC_FRMT_FPALPHA                   0x00000004 +#define RADEON_CP_VC_FRMT_PKCOLOR                   0x00000008 +#define RADEON_CP_VC_FRMT_FPSPEC                    0x00000010 +#define RADEON_CP_VC_FRMT_FPFOG                     0x00000020 +#define RADEON_CP_VC_FRMT_PKSPEC                    0x00000040 +#define RADEON_CP_VC_FRMT_ST0                       0x00000080 +#define RADEON_CP_VC_FRMT_ST1                       0x00000100 +#define RADEON_CP_VC_FRMT_Q1                        0x00000200 +#define RADEON_CP_VC_FRMT_ST2                       0x00000400 +#define RADEON_CP_VC_FRMT_Q2                        0x00000800 +#define RADEON_CP_VC_FRMT_ST3                       0x00001000 +#define RADEON_CP_VC_FRMT_Q3                        0x00002000 +#define RADEON_CP_VC_FRMT_Q0                        0x00004000 +#define RADEON_CP_VC_FRMT_BLND_WEIGHT_CNT_MASK      0x00038000 +#define RADEON_CP_VC_FRMT_N0                        0x00040000 +#define RADEON_CP_VC_FRMT_XY1                       0x08000000 +#define RADEON_CP_VC_FRMT_Z1                        0x10000000 +#define RADEON_CP_VC_FRMT_W1                        0x20000000 +#define RADEON_CP_VC_FRMT_N1                        0x40000000 +#define RADEON_CP_VC_FRMT_Z                         0x80000000 + +#define RADEON_CP_VC_CNTL_PRIM_TYPE_NONE            0x00000000 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_POINT           0x00000001 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_LINE            0x00000002 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_LINE_STRIP      0x00000003 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_TRI_LIST        0x00000004 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_TRI_FAN         0x00000005 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_TRI_STRIP       0x00000006 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_TRI_TYPE_2      0x00000007 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_RECT_LIST       0x00000008 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_3VRT_POINT_LIST 0x00000009 +#define RADEON_CP_VC_CNTL_PRIM_TYPE_3VRT_LINE_LIST  0x0000000a +#define RADEON_CP_VC_CNTL_PRIM_WALK_IND             0x00000010 +#define RADEON_CP_VC_CNTL_PRIM_WALK_LIST            0x00000020 +#define RADEON_CP_VC_CNTL_PRIM_WALK_RING            0x00000030 +#define RADEON_CP_VC_CNTL_COLOR_ORDER_BGRA          0x00000000 +#define RADEON_CP_VC_CNTL_COLOR_ORDER_RGBA          0x00000040 +#define RADEON_CP_VC_CNTL_MAOS_ENABLE               0x00000080 +#define RADEON_CP_VC_CNTL_VTX_FMT_NON_RADEON_MODE   0x00000000 +#define RADEON_CP_VC_CNTL_VTX_FMT_RADEON_MODE       0x00000100 +#define RADEON_CP_VC_CNTL_TCL_DISABLE               0x00000000 +#define RADEON_CP_VC_CNTL_TCL_ENABLE                0x00000200 +#define RADEON_CP_VC_CNTL_NUM_SHIFT                 16 + +#define RADEON_VS_MATRIX_0_ADDR                   0 +#define RADEON_VS_MATRIX_1_ADDR                   4 +#define RADEON_VS_MATRIX_2_ADDR                   8 +#define RADEON_VS_MATRIX_3_ADDR                  12 +#define RADEON_VS_MATRIX_4_ADDR                  16 +#define RADEON_VS_MATRIX_5_ADDR                  20 +#define RADEON_VS_MATRIX_6_ADDR                  24 +#define RADEON_VS_MATRIX_7_ADDR                  28 +#define RADEON_VS_MATRIX_8_ADDR                  32 +#define RADEON_VS_MATRIX_9_ADDR                  36 +#define RADEON_VS_MATRIX_10_ADDR                 40 +#define RADEON_VS_MATRIX_11_ADDR                 44 +#define RADEON_VS_MATRIX_12_ADDR                 48 +#define RADEON_VS_MATRIX_13_ADDR                 52 +#define RADEON_VS_MATRIX_14_ADDR                 56 +#define RADEON_VS_MATRIX_15_ADDR                 60 +#define RADEON_VS_LIGHT_AMBIENT_ADDR             64 +#define RADEON_VS_LIGHT_DIFFUSE_ADDR             72 +#define RADEON_VS_LIGHT_SPECULAR_ADDR            80 +#define RADEON_VS_LIGHT_DIRPOS_ADDR              88 +#define RADEON_VS_LIGHT_HWVSPOT_ADDR             96 +#define RADEON_VS_LIGHT_ATTENUATION_ADDR        104 +#define RADEON_VS_MATRIX_EYE2CLIP_ADDR          112 +#define RADEON_VS_UCP_ADDR                      116 +#define RADEON_VS_GLOBAL_AMBIENT_ADDR           122 +#define RADEON_VS_FOG_PARAM_ADDR                123 +#define RADEON_VS_EYE_VECTOR_ADDR               124 + +#define RADEON_SS_LIGHT_DCD_ADDR                  0 +#define RADEON_SS_LIGHT_SPOT_EXPONENT_ADDR        8 +#define RADEON_SS_LIGHT_SPOT_CUTOFF_ADDR         16 +#define RADEON_SS_LIGHT_SPECULAR_THRESH_ADDR     24 +#define RADEON_SS_LIGHT_RANGE_CUTOFF_ADDR        32 +#define RADEON_SS_VERT_GUARD_CLIP_ADJ_ADDR       48 +#define RADEON_SS_VERT_GUARD_DISCARD_ADJ_ADDR    49 +#define RADEON_SS_HORZ_GUARD_CLIP_ADJ_ADDR       50 +#define RADEON_SS_HORZ_GUARD_DISCARD_ADJ_ADDR    51 +#define RADEON_SS_SHININESS                      60 + +#define RADEON_TV_MASTER_CNTL                    0x0800 +#       define RADEON_TV_ASYNC_RST               (1 <<  0) +#       define RADEON_CRT_ASYNC_RST              (1 <<  1) +#       define RADEON_RESTART_PHASE_FIX          (1 <<  3) +#	define RADEON_TV_FIFO_ASYNC_RST		 (1 <<  4) +#	define RADEON_VIN_ASYNC_RST		 (1 <<  5) +#	define RADEON_AUD_ASYNC_RST		 (1 <<  6) +#	define RADEON_DVS_ASYNC_RST		 (1 <<  7) +#       define RADEON_CRT_FIFO_CE_EN             (1 <<  9) +#       define RADEON_TV_FIFO_CE_EN              (1 << 10) +#       define RADEON_RE_SYNC_NOW_SEL_MASK       (3 << 14) +#       define RADEON_TVCLK_ALWAYS_ONb           (1 << 30) +#	define RADEON_TV_ON			 (1 << 31) +#define RADEON_TV_PRE_DAC_MUX_CNTL               0x0888 +#       define RADEON_Y_RED_EN                   (1 << 0) +#       define RADEON_C_GRN_EN                   (1 << 1) +#       define RADEON_CMP_BLU_EN                 (1 << 2) +#       define RADEON_DAC_DITHER_EN              (1 << 3) +#       define RADEON_RED_MX_FORCE_DAC_DATA      (6 << 4) +#       define RADEON_GRN_MX_FORCE_DAC_DATA      (6 << 8) +#       define RADEON_BLU_MX_FORCE_DAC_DATA      (6 << 12) +#       define RADEON_TV_FORCE_DAC_DATA_SHIFT    16 +#define RADEON_TV_RGB_CNTL                           0x0804 +#       define RADEON_SWITCH_TO_BLUE		  (1 <<  4) +#       define RADEON_RGB_DITHER_EN		  (1 <<  5) +#       define RADEON_RGB_SRC_SEL_MASK		  (3 <<  8) +#       define RADEON_RGB_SRC_SEL_CRTC1		  (0 <<  8) +#       define RADEON_RGB_SRC_SEL_RMX		  (1 <<  8) +#       define RADEON_RGB_SRC_SEL_CRTC2		  (2 <<  8) +#       define RADEON_RGB_CONVERT_BY_PASS	  (1 << 10) +#       define RADEON_UVRAM_READ_MARGIN_SHIFT	  16 +#       define RADEON_FIFORAM_FFMACRO_READ_MARGIN_SHIFT	  20 +#	define RADEON_TVOUT_SCALE_EN 		  (1 << 26) +#define RADEON_TV_SYNC_CNTL                          0x0808 +#       define RADEON_SYNC_OE                     (1 <<  0) +#       define RADEON_SYNC_OUT                    (1 <<  1) +#       define RADEON_SYNC_IN                     (1 <<  2) +#       define RADEON_SYNC_PUB                    (1 <<  3) +#       define RADEON_SYNC_PD                     (1 <<  4) +#       define RADEON_TV_SYNC_IO_DRIVE            (1 <<  5) +#define RADEON_TV_HTOTAL                             0x080c +#define RADEON_TV_HDISP                              0x0810 +#define RADEON_TV_HSTART                             0x0818 +#define RADEON_TV_HCOUNT                             0x081C +#define RADEON_TV_VTOTAL                             0x0820 +#define RADEON_TV_VDISP                              0x0824 +#define RADEON_TV_VCOUNT                             0x0828 +#define RADEON_TV_FTOTAL                             0x082c +#define RADEON_TV_FCOUNT                             0x0830 +#define RADEON_TV_FRESTART                           0x0834 +#define RADEON_TV_HRESTART                           0x0838 +#define RADEON_TV_VRESTART                           0x083c +#define RADEON_TV_HOST_READ_DATA                     0x0840 +#define RADEON_TV_HOST_WRITE_DATA                    0x0844 +#define RADEON_TV_HOST_RD_WT_CNTL                    0x0848 +#	define RADEON_HOST_FIFO_RD		 (1 << 12) +#	define RADEON_HOST_FIFO_RD_ACK		 (1 << 13) +#	define RADEON_HOST_FIFO_WT		 (1 << 14) +#	define RADEON_HOST_FIFO_WT_ACK		 (1 << 15) +#define RADEON_TV_VSCALER_CNTL1                      0x084c +#       define RADEON_UV_INC_MASK                0xffff +#       define RADEON_UV_INC_SHIFT               0 +#       define RADEON_Y_W_EN			 (1 << 24) +#       define RADEON_RESTART_FIELD              (1 << 29) /* restart on field 0 */ +#       define RADEON_Y_DEL_W_SIG_SHIFT          26 +#define RADEON_TV_TIMING_CNTL                        0x0850 +#       define RADEON_H_INC_MASK                 0xfff +#       define RADEON_H_INC_SHIFT                0 +#       define RADEON_REQ_Y_FIRST                (1 << 19) +#       define RADEON_FORCE_BURST_ALWAYS         (1 << 21) +#       define RADEON_UV_POST_SCALE_BYPASS       (1 << 23) +#       define RADEON_UV_OUTPUT_POST_SCALE_SHIFT 24 +#define RADEON_TV_VSCALER_CNTL2                      0x0854 +#       define RADEON_DITHER_MODE                (1 <<  0) +#       define RADEON_Y_OUTPUT_DITHER_EN         (1 <<  1) +#       define RADEON_UV_OUTPUT_DITHER_EN        (1 <<  2) +#       define RADEON_UV_TO_BUF_DITHER_EN        (1 <<  3) +#define RADEON_TV_Y_FALL_CNTL                        0x0858 +#       define RADEON_Y_FALL_PING_PONG           (1 << 16) +#       define RADEON_Y_COEF_EN                  (1 << 17) +#define RADEON_TV_Y_RISE_CNTL                        0x085c +#       define RADEON_Y_RISE_PING_PONG           (1 << 16) +#define RADEON_TV_Y_SAW_TOOTH_CNTL                   0x0860 +#define RADEON_TV_UPSAMP_AND_GAIN_CNTL               0x0864 +#	define RADEON_YUPSAMP_EN		 (1 <<  0) +#	define RADEON_UVUPSAMP_EN		 (1 <<  2) +#define RADEON_TV_GAIN_LIMIT_SETTINGS                0x0868 +#       define RADEON_Y_GAIN_LIMIT_SHIFT         0 +#       define RADEON_UV_GAIN_LIMIT_SHIFT        16 +#define RADEON_TV_LINEAR_GAIN_SETTINGS               0x086c +#       define RADEON_Y_GAIN_SHIFT               0 +#       define RADEON_UV_GAIN_SHIFT              16 +#define RADEON_TV_MODULATOR_CNTL1                    0x0870 +#	define RADEON_YFLT_EN			 (1 <<  2) +#	define RADEON_UVFLT_EN			 (1 <<  3) +#       define RADEON_ALT_PHASE_EN               (1 <<  6) +#       define RADEON_SYNC_TIP_LEVEL             (1 <<  7) +#       define RADEON_BLANK_LEVEL_SHIFT          8 +#       define RADEON_SET_UP_LEVEL_SHIFT         16 +#	define RADEON_SLEW_RATE_LIMIT		 (1 << 23) +#       define RADEON_CY_FILT_BLEND_SHIFT        28 +#define RADEON_TV_MODULATOR_CNTL2                    0x0874 +#       define RADEON_TV_U_BURST_LEVEL_MASK     0x1ff +#       define RADEON_TV_V_BURST_LEVEL_MASK     0x1ff +#       define RADEON_TV_V_BURST_LEVEL_SHIFT    16 +#define RADEON_TV_CRC_CNTL                           0x0890 +#define RADEON_TV_UV_ADR                             0x08ac +#	define RADEON_MAX_UV_ADR_MASK		 0x000000ff +#	define RADEON_MAX_UV_ADR_SHIFT		 0 +#	define RADEON_TABLE1_BOT_ADR_MASK	 0x0000ff00 +#	define RADEON_TABLE1_BOT_ADR_SHIFT	 8 +#	define RADEON_TABLE3_TOP_ADR_MASK	 0x00ff0000 +#	define RADEON_TABLE3_TOP_ADR_SHIFT	 16 +#	define RADEON_HCODE_TABLE_SEL_MASK	 0x06000000 +#	define RADEON_HCODE_TABLE_SEL_SHIFT	 25 +#	define RADEON_VCODE_TABLE_SEL_MASK	 0x18000000 +#	define RADEON_VCODE_TABLE_SEL_SHIFT	 27 +#	define RADEON_TV_MAX_FIFO_ADDR		 0x1a7 +#	define RADEON_TV_MAX_FIFO_ADDR_INTERNAL	 0x1ff +#define RADEON_TV_PLL_FINE_CNTL			     0x0020	/* PLL */ +#define RADEON_TV_PLL_CNTL                           0x0021	/* PLL */ +#       define RADEON_TV_M0LO_MASK               0xff +#       define RADEON_TV_M0HI_MASK               0x7 +#       define RADEON_TV_M0HI_SHIFT              18 +#       define RADEON_TV_N0LO_MASK               0x1ff +#       define RADEON_TV_N0LO_SHIFT              8 +#       define RADEON_TV_N0HI_MASK               0x3 +#       define RADEON_TV_N0HI_SHIFT              21 +#       define RADEON_TV_P_MASK                  0xf +#       define RADEON_TV_P_SHIFT                 24 +#       define RADEON_TV_SLIP_EN                 (1 << 23) +#       define RADEON_TV_DTO_EN                  (1 << 28) +#define RADEON_TV_PLL_CNTL1                          0x0022	/* PLL */ +#       define RADEON_TVPLL_RESET                (1 <<  1) +#       define RADEON_TVPLL_SLEEP                (1 <<  3) +#       define RADEON_TVPLL_REFCLK_SEL           (1 <<  4) +#       define RADEON_TVPCP_SHIFT                8 +#       define RADEON_TVPCP_MASK                 (7 << 8) +#       define RADEON_TVPVG_SHIFT                11 +#       define RADEON_TVPVG_MASK                 (7 << 11) +#       define RADEON_TVPDC_SHIFT                14 +#       define RADEON_TVPDC_MASK                 (3 << 14) +#       define RADEON_TVPLL_TEST_DIS             (1 << 31) +#       define RADEON_TVCLK_SRC_SEL_TVPLL        (1 << 30) + +#define RS400_DISP2_REQ_CNTL1			0xe30 +#       define RS400_DISP2_START_REQ_LEVEL_SHIFT   0 +#       define RS400_DISP2_START_REQ_LEVEL_MASK    0x3ff +#       define RS400_DISP2_STOP_REQ_LEVEL_SHIFT    12 +#       define RS400_DISP2_STOP_REQ_LEVEL_MASK     0x3ff +#       define RS400_DISP2_ALLOW_FID_LEVEL_SHIFT   22 +#       define RS400_DISP2_ALLOW_FID_LEVEL_MASK    0x3ff +#define RS400_DISP2_REQ_CNTL2			0xe34 +#       define RS400_DISP2_CRITICAL_POINT_START_SHIFT    12 +#       define RS400_DISP2_CRITICAL_POINT_START_MASK     0x3ff +#       define RS400_DISP2_CRITICAL_POINT_STOP_SHIFT     22 +#       define RS400_DISP2_CRITICAL_POINT_STOP_MASK      0x3ff +#define RS400_DMIF_MEM_CNTL1			0xe38 +#       define RS400_DISP2_START_ADR_SHIFT      0 +#       define RS400_DISP2_START_ADR_MASK       0x3ff +#       define RS400_DISP1_CRITICAL_POINT_START_SHIFT    12 +#       define RS400_DISP1_CRITICAL_POINT_START_MASK     0x3ff +#       define RS400_DISP1_CRITICAL_POINT_STOP_SHIFT     22 +#       define RS400_DISP1_CRITICAL_POINT_STOP_MASK      0x3ff +#define RS400_DISP1_REQ_CNTL1			0xe3c +#       define RS400_DISP1_START_REQ_LEVEL_SHIFT   0 +#       define RS400_DISP1_START_REQ_LEVEL_MASK    0x3ff +#       define RS400_DISP1_STOP_REQ_LEVEL_SHIFT    12 +#       define RS400_DISP1_STOP_REQ_LEVEL_MASK     0x3ff +#       define RS400_DISP1_ALLOW_FID_LEVEL_SHIFT   22 +#       define RS400_DISP1_ALLOW_FID_LEVEL_MASK    0x3ff + +#define RS690_MC_INDEX				0x78 +#	define RS690_MC_INDEX_MASK		0x1ff +#	define RS690_MC_INDEX_WR_EN		(1 << 9) +#	define RS690_MC_INDEX_WR_ACK		0x7f +#define RS690_MC_DATA				0x7c + +#define RS690_MC_FB_LOCATION			0x100 +#define RS690_MC_AGP_LOCATION			0x101 +#define RS690_MC_AGP_BASE			0x102 +#define RS690_MC_AGP_BASE_2                     0x103 +#define RS690_MC_STATUS                         0x90 +#define RS690_MC_STATUS_IDLE                    (1 << 0) + +#define RS600_MC_INDEX				0x78 +#	define RS600_MC_INDEX_MASK		0xff +#	define RS600_MC_INDEX_WR_EN		(1 << 8) +#	define RS600_MC_INDEX_WR_ACK		0xff +#define RS600_MC_DATA				0x7c + +#define RS600_MC_FB_LOCATION			0xA +#define RS600_MC_STATUS                         0x0 +#define RS600_MC_STATUS_IDLE                    (1 << 0) + +#define AVIVO_MC_INDEX						0x0070 +#define R520_MC_STATUS 0x00 +#define R520_MC_STATUS_IDLE (1<<1) +#define RV515_MC_STATUS 0x08 +#define RV515_MC_STATUS_IDLE (1<<4) +#define AVIVO_MC_DATA						0x0074 + +#define RV515_MC_FB_LOCATION   0x1 +#define RV515_MC_AGP_LOCATION  0x2 +#define RV515_MC_AGP_BASE      0x3 +#define RV515_MC_AGP_BASE_2    0x4 +#define RV515_MC_CNTL          0x5 +#	define RV515_MEM_NUM_CHANNELS_MASK  0x3 +#define R520_MC_FB_LOCATION    0x4 +#define R520_MC_AGP_LOCATION   0x5 +#define R520_MC_AGP_BASE       0x6 +#define R520_MC_AGP_BASE_2     0x7 +#define R520_MC_CNTL0          0x8 +#	define R520_MEM_NUM_CHANNELS_MASK  (0x3 << 24) +#	define R520_MEM_NUM_CHANNELS_SHIFT  24 +#	define R520_MC_CHANNEL_SIZE  (1 << 23) + +#define R600_RAMCFG				       0x2408 +#       define R600_CHANSIZE                           (1 << 7) +#       define R600_CHANSIZE_OVERRIDE                  (1 << 10) + +#define AVIVO_HDP_FB_LOCATION 0x134 + +#define AVIVO_VGA_RENDER_CONTROL				0x0300 +#       define AVIVO_VGA_VSTATUS_CNTL_MASK                      (3 << 16) +#define AVIVO_D1VGA_CONTROL					0x0330 +#       define AVIVO_DVGA_CONTROL_MODE_ENABLE (1<<0) +#       define AVIVO_DVGA_CONTROL_TIMING_SELECT (1<<8) +#       define AVIVO_DVGA_CONTROL_SYNC_POLARITY_SELECT (1<<9) +#       define AVIVO_DVGA_CONTROL_OVERSCAN_TIMING_SELECT (1<<10) +#       define AVIVO_DVGA_CONTROL_OVERSCAN_COLOR_EN (1<<16) +#       define AVIVO_DVGA_CONTROL_ROTATE (1<<24) +#define AVIVO_D2VGA_CONTROL					0x0338 + +#define AVIVO_EXT1_PPLL_REF_DIV_SRC                             0x400 +#define AVIVO_EXT1_PPLL_REF_DIV                                 0x404 +#define AVIVO_EXT1_PPLL_UPDATE_LOCK                             0x408 +#define AVIVO_EXT1_PPLL_UPDATE_CNTL                             0x40c + +#define AVIVO_EXT2_PPLL_REF_DIV_SRC                             0x410 +#define AVIVO_EXT2_PPLL_REF_DIV                                 0x414 +#define AVIVO_EXT2_PPLL_UPDATE_LOCK                             0x418 +#define AVIVO_EXT2_PPLL_UPDATE_CNTL                             0x41c + +#define AVIVO_EXT1_PPLL_FB_DIV                                   0x430 +#define AVIVO_EXT2_PPLL_FB_DIV                                   0x434 + +#define AVIVO_EXT1_PPLL_POST_DIV_SRC                                 0x438 +#define AVIVO_EXT1_PPLL_POST_DIV                                     0x43c + +#define AVIVO_EXT2_PPLL_POST_DIV_SRC                                 0x440 +#define AVIVO_EXT2_PPLL_POST_DIV                                     0x444 + +#define AVIVO_EXT1_PPLL_CNTL                                    0x448 +#define AVIVO_EXT2_PPLL_CNTL                                    0x44c + +#define AVIVO_P1PLL_CNTL                                        0x450 +#define AVIVO_P2PLL_CNTL                                        0x454 +#define AVIVO_P1PLL_INT_SS_CNTL                                 0x458 +#define AVIVO_P2PLL_INT_SS_CNTL                                 0x45c +#define AVIVO_P1PLL_TMDSA_CNTL                                  0x460 +#define AVIVO_P2PLL_LVTMA_CNTL                                  0x464 + +#define AVIVO_PCLK_CRTC1_CNTL                                   0x480 +#define AVIVO_PCLK_CRTC2_CNTL                                   0x484 + +#define AVIVO_D1CRTC_H_TOTAL					0x6000 +#define AVIVO_D1CRTC_H_BLANK_START_END                          0x6004 +#define AVIVO_D1CRTC_H_SYNC_A                                   0x6008 +#define AVIVO_D1CRTC_H_SYNC_A_CNTL                              0x600c +#define AVIVO_D1CRTC_H_SYNC_B                                   0x6010 +#define AVIVO_D1CRTC_H_SYNC_B_CNTL                              0x6014 + +#define AVIVO_D1CRTC_V_TOTAL					0x6020 +#define AVIVO_D1CRTC_V_BLANK_START_END                          0x6024 +#define AVIVO_D1CRTC_V_SYNC_A                                   0x6028 +#define AVIVO_D1CRTC_V_SYNC_A_CNTL                              0x602c +#define AVIVO_D1CRTC_V_SYNC_B                                   0x6030 +#define AVIVO_D1CRTC_V_SYNC_B_CNTL                              0x6034 + +#define AVIVO_D1CRTC_CONTROL                                    0x6080 +#       define AVIVO_CRTC_EN                            (1<<0) +#define AVIVO_D1CRTC_BLANK_CONTROL                              0x6084 +#define AVIVO_D1CRTC_INTERLACE_CONTROL                          0x6088 +#define AVIVO_D1CRTC_INTERLACE_STATUS                           0x608c +#define AVIVO_D1CRTC_STEREO_CONTROL                             0x60c4 + +/* master controls */ +#define AVIVO_DC_CRTC_MASTER_EN                                 0x60f8 +#define AVIVO_DC_CRTC_TV_CONTROL                                0x60fc + +#define AVIVO_D1GRPH_ENABLE                                     0x6100 +#define AVIVO_D1GRPH_CONTROL                                    0x6104 +#       define AVIVO_D1GRPH_CONTROL_DEPTH_8BPP          (0<<0) +#       define AVIVO_D1GRPH_CONTROL_DEPTH_16BPP         (1<<0) +#       define AVIVO_D1GRPH_CONTROL_DEPTH_32BPP         (2<<0) +#       define AVIVO_D1GRPH_CONTROL_DEPTH_64BPP         (3<<0) + +#       define AVIVO_D1GRPH_CONTROL_8BPP_INDEXED        (0<<8) + +#       define AVIVO_D1GRPH_CONTROL_16BPP_ARGB1555      (0<<8) +#       define AVIVO_D1GRPH_CONTROL_16BPP_RGB565        (1<<8) +#       define AVIVO_D1GRPH_CONTROL_16BPP_ARGB4444      (2<<8) +#       define AVIVO_D1GRPH_CONTROL_16BPP_AI88          (3<<8) +#       define AVIVO_D1GRPH_CONTROL_16BPP_MONO16        (4<<8) + +#       define AVIVO_D1GRPH_CONTROL_32BPP_ARGB8888      (0<<8) +#       define AVIVO_D1GRPH_CONTROL_32BPP_ARGB2101010   (1<<8) +#       define AVIVO_D1GRPH_CONTROL_32BPP_DIGITAL       (2<<8) +#       define AVIVO_D1GRPH_CONTROL_32BPP_8B_ARGB2101010 (3<<8) + + +#       define AVIVO_D1GRPH_CONTROL_64BPP_ARGB16161616  (0<<8) + +#       define AVIVO_D1GRPH_SWAP_RB                     (1<<16) +#       define AVIVO_D1GRPH_TILED                       (1<<20) +#       define AVIVO_D1GRPH_MACRO_ADDRESS_MODE          (1<<21) + +#define AVIVO_D1GRPH_LUT_SEL                                    0x6108 +#define AVIVO_D1GRPH_PRIMARY_SURFACE_ADDRESS                    0x6110 +#define AVIVO_D1GRPH_SECONDARY_SURFACE_ADDRESS                  0x6118 +#define AVIVO_D1GRPH_PITCH                                      0x6120 +#define AVIVO_D1GRPH_SURFACE_OFFSET_X                           0x6124 +#define AVIVO_D1GRPH_SURFACE_OFFSET_Y                           0x6128 +#define AVIVO_D1GRPH_X_START                                    0x612c +#define AVIVO_D1GRPH_Y_START                                    0x6130 +#define AVIVO_D1GRPH_X_END                                      0x6134 +#define AVIVO_D1GRPH_Y_END                                      0x6138 +#define AVIVO_D1GRPH_UPDATE                                     0x6144 +#       define AVIVO_D1GRPH_UPDATE_LOCK                 (1<<16) +#define AVIVO_D1GRPH_FLIP_CONTROL                               0x6148 + +#define AVIVO_D1CUR_CONTROL                     0x6400 +#       define AVIVO_D1CURSOR_EN           (1<<0) +#       define AVIVO_D1CURSOR_MODE_SHIFT  8 +#       define AVIVO_D1CURSOR_MODE_MASK   (0x3<<8) +#       define AVIVO_D1CURSOR_MODE_24BPP  (0x2) +#define AVIVO_D1CUR_SURFACE_ADDRESS             0x6408 +#define AVIVO_D1CUR_SIZE                        0x6410 +#define AVIVO_D1CUR_POSITION                    0x6414 +#define AVIVO_D1CUR_HOT_SPOT                    0x6418 +#define AVIVO_D1CUR_UPDATE                      0x6424 +#       define AVIVO_D1CURSOR_UPDATE_LOCK (1 << 16) + +#define AVIVO_DC_LUT_RW_SELECT                  0x6480 +#define AVIVO_DC_LUT_RW_MODE                    0x6484 +#define AVIVO_DC_LUT_RW_INDEX                   0x6488 +#define AVIVO_DC_LUT_SEQ_COLOR                  0x648c +#define AVIVO_DC_LUT_PWL_DATA                   0x6490 +#define AVIVO_DC_LUT_30_COLOR                   0x6494 +#define AVIVO_DC_LUT_READ_PIPE_SELECT           0x6498 +#define AVIVO_DC_LUT_WRITE_EN_MASK              0x649c +#define AVIVO_DC_LUT_AUTOFILL                   0x64a0 + +#define AVIVO_DC_LUTA_CONTROL                   0x64c0 +#define AVIVO_DC_LUTA_BLACK_OFFSET_BLUE         0x64c4 +#define AVIVO_DC_LUTA_BLACK_OFFSET_GREEN        0x64c8 +#define AVIVO_DC_LUTA_BLACK_OFFSET_RED          0x64cc +#define AVIVO_DC_LUTA_WHITE_OFFSET_BLUE         0x64d0 +#define AVIVO_DC_LUTA_WHITE_OFFSET_GREEN        0x64d4 +#define AVIVO_DC_LUTA_WHITE_OFFSET_RED          0x64d8 + + +#define AVIVO_D1MODE_DESKTOP_HEIGHT             0x652C +#define AVIVO_D1MODE_VIEWPORT_START             0x6580 +#define AVIVO_D1MODE_VIEWPORT_SIZE              0x6584 +#define AVIVO_D1MODE_EXT_OVERSCAN_LEFT_RIGHT    0x6588 +#define AVIVO_D1MODE_EXT_OVERSCAN_TOP_BOTTOM    0x658c + +#define AVIVO_D1SCL_SCALER_ENABLE               0x6590 +#define AVIVO_D1SCL_SCALER_TAP_CONTROL	 	0x6594 +#define AVIVO_D1SCL_UPDATE                      0x65cc +#       define AVIVO_D1SCL_UPDATE_LOCK         (1<<16) + +/* second crtc */ +#define AVIVO_D2CRTC_H_TOTAL					0x6800 +#define AVIVO_D2CRTC_H_BLANK_START_END                          0x6804 +#define AVIVO_D2CRTC_H_SYNC_A                                   0x6808 +#define AVIVO_D2CRTC_H_SYNC_A_CNTL                              0x680c +#define AVIVO_D2CRTC_H_SYNC_B                                   0x6810 +#define AVIVO_D2CRTC_H_SYNC_B_CNTL                              0x6814 + +#define AVIVO_D2CRTC_V_TOTAL					0x6820 +#define AVIVO_D2CRTC_V_BLANK_START_END                          0x6824 +#define AVIVO_D2CRTC_V_SYNC_A                                   0x6828 +#define AVIVO_D2CRTC_V_SYNC_A_CNTL                              0x682c +#define AVIVO_D2CRTC_V_SYNC_B                                   0x6830 +#define AVIVO_D2CRTC_V_SYNC_B_CNTL                              0x6834 + +#define AVIVO_D2CRTC_CONTROL                                    0x6880 +#define AVIVO_D2CRTC_BLANK_CONTROL                              0x6884 +#define AVIVO_D2CRTC_INTERLACE_CONTROL                          0x6888 +#define AVIVO_D2CRTC_INTERLACE_STATUS                           0x688c +#define AVIVO_D2CRTC_STEREO_CONTROL                             0x68c4 + +#define AVIVO_D2GRPH_ENABLE                                     0x6900 +#define AVIVO_D2GRPH_CONTROL                                    0x6904 +#define AVIVO_D2GRPH_LUT_SEL                                    0x6908 +#define AVIVO_D2GRPH_PRIMARY_SURFACE_ADDRESS                    0x6910 +#define AVIVO_D2GRPH_SECONDARY_SURFACE_ADDRESS                  0x6918 +#define AVIVO_D2GRPH_PITCH                                      0x6920 +#define AVIVO_D2GRPH_SURFACE_OFFSET_X                           0x6924 +#define AVIVO_D2GRPH_SURFACE_OFFSET_Y                           0x6928 +#define AVIVO_D2GRPH_X_START                                    0x692c +#define AVIVO_D2GRPH_Y_START                                    0x6930 +#define AVIVO_D2GRPH_X_END                                      0x6934 +#define AVIVO_D2GRPH_Y_END                                      0x6938 +#define AVIVO_D2GRPH_UPDATE                                     0x6944 +#define AVIVO_D2GRPH_FLIP_CONTROL                               0x6948 + +#define AVIVO_D2CUR_CONTROL                     0x6c00 +#define AVIVO_D2CUR_SURFACE_ADDRESS             0x6c08 +#define AVIVO_D2CUR_SIZE                        0x6c10 +#define AVIVO_D2CUR_POSITION                    0x6c14 + +#define AVIVO_D2MODE_VIEWPORT_START             0x6d80 +#define AVIVO_D2MODE_VIEWPORT_SIZE              0x6d84 +#define AVIVO_D2MODE_EXT_OVERSCAN_LEFT_RIGHT    0x6d88 +#define AVIVO_D2MODE_EXT_OVERSCAN_TOP_BOTTOM    0x6d8c + +#define AVIVO_D2SCL_SCALER_ENABLE               0x6d90 +#define AVIVO_D2SCL_SCALER_TAP_CONTROL	 	0x6d94 + +#define AVIVO_DDIA_BIT_DEPTH_CONTROL				0x7214 + +#define AVIVO_DACA_ENABLE					0x7800 +#	define AVIVO_DAC_ENABLE				(1 << 0) +#define AVIVO_DACA_SOURCE_SELECT				0x7804 +#       define AVIVO_DAC_SOURCE_CRTC1                   (0 << 0) +#       define AVIVO_DAC_SOURCE_CRTC2                   (1 << 0) +#       define AVIVO_DAC_SOURCE_TV                      (2 << 0) + +#define AVIVO_DACA_FORCE_OUTPUT_CNTL				0x783c +# define AVIVO_DACA_FORCE_OUTPUT_CNTL_FORCE_DATA_EN             (1 << 0) +# define AVIVO_DACA_FORCE_OUTPUT_CNTL_DATA_SEL_SHIFT            (8) +# define AVIVO_DACA_FORCE_OUTPUT_CNTL_DATA_SEL_BLUE             (1 << 0) +# define AVIVO_DACA_FORCE_OUTPUT_CNTL_DATA_SEL_GREEN            (1 << 1) +# define AVIVO_DACA_FORCE_OUTPUT_CNTL_DATA_SEL_RED              (1 << 2) +# define AVIVO_DACA_FORCE_OUTPUT_CNTL_DATA_ON_BLANKB_ONLY       (1 << 24) +#define AVIVO_DACA_POWERDOWN					0x7850 +# define AVIVO_DACA_POWERDOWN_POWERDOWN                         (1 << 0) +# define AVIVO_DACA_POWERDOWN_BLUE                              (1 << 8) +# define AVIVO_DACA_POWERDOWN_GREEN                             (1 << 16) +# define AVIVO_DACA_POWERDOWN_RED                               (1 << 24) + +#define AVIVO_DACB_ENABLE					0x7a00 +#define AVIVO_DACB_SOURCE_SELECT				0x7a04 +#define AVIVO_DACB_FORCE_OUTPUT_CNTL				0x7a3c +# define AVIVO_DACB_FORCE_OUTPUT_CNTL_FORCE_DATA_EN             (1 << 0) +# define AVIVO_DACB_FORCE_OUTPUT_CNTL_DATA_SEL_SHIFT            (8) +# define AVIVO_DACB_FORCE_OUTPUT_CNTL_DATA_SEL_BLUE             (1 << 0) +# define AVIVO_DACB_FORCE_OUTPUT_CNTL_DATA_SEL_GREEN            (1 << 1) +# define AVIVO_DACB_FORCE_OUTPUT_CNTL_DATA_SEL_RED              (1 << 2) +# define AVIVO_DACB_FORCE_OUTPUT_CNTL_DATA_ON_BLANKB_ONLY       (1 << 24) +#define AVIVO_DACB_POWERDOWN					0x7a50 +# define AVIVO_DACB_POWERDOWN_POWERDOWN                         (1 << 0) +# define AVIVO_DACB_POWERDOWN_BLUE                              (1 << 8) +# define AVIVO_DACB_POWERDOWN_GREEN                             (1 << 16) +# define AVIVO_DACB_POWERDOWN_RED  + +#define AVIVO_TMDSA_CNTL                    0x7880 +#   define AVIVO_TMDSA_CNTL_ENABLE               (1 << 0) +#   define AVIVO_TMDSA_CNTL_HPD_MASK             (1 << 4) +#   define AVIVO_TMDSA_CNTL_HPD_SELECT           (1 << 8) +#   define AVIVO_TMDSA_CNTL_SYNC_PHASE           (1 << 12) +#   define AVIVO_TMDSA_CNTL_PIXEL_ENCODING       (1 << 16) +#   define AVIVO_TMDSA_CNTL_DUAL_LINK_ENABLE     (1 << 24) +#   define AVIVO_TMDSA_CNTL_SWAP                 (1 << 28) +#define AVIVO_TMDSA_SOURCE_SELECT				0x7884 +/* 78a8 appears to be some kind of (reasonably tolerant) clock? + * 78d0 definitely hits the transmitter, definitely clock. */ +/* MYSTERY1 This appears to control dithering? */ +#define AVIVO_TMDSA_BIT_DEPTH_CONTROL		0x7894 +#   define AVIVO_TMDS_BIT_DEPTH_CONTROL_TRUNCATE_EN           (1 << 0) +#   define AVIVO_TMDS_BIT_DEPTH_CONTROL_TRUNCATE_DEPTH        (1 << 4) +#   define AVIVO_TMDS_BIT_DEPTH_CONTROL_SPATIAL_DITHER_EN     (1 << 8) +#   define AVIVO_TMDS_BIT_DEPTH_CONTROL_SPATIAL_DITHER_DEPTH  (1 << 12) +#   define AVIVO_TMDS_BIT_DEPTH_CONTROL_TEMPORAL_DITHER_EN    (1 << 16) +#   define AVIVO_TMDS_BIT_DEPTH_CONTROL_TEMPORAL_DITHER_DEPTH (1 << 20) +#   define AVIVO_TMDS_BIT_DEPTH_CONTROL_TEMPORAL_LEVEL        (1 << 24) +#   define AVIVO_TMDS_BIT_DEPTH_CONTROL_TEMPORAL_DITHER_RESET (1 << 26) +#define AVIVO_TMDSA_DCBALANCER_CONTROL                  0x78d0 +#   define AVIVO_TMDSA_DCBALANCER_CONTROL_EN                  (1 << 0) +#   define AVIVO_TMDSA_DCBALANCER_CONTROL_TEST_EN             (1 << 8) +#   define AVIVO_TMDSA_DCBALANCER_CONTROL_TEST_IN_SHIFT       (16) +#   define AVIVO_TMDSA_DCBALANCER_CONTROL_FORCE               (1 << 24) +#define AVIVO_TMDSA_DATA_SYNCHRONIZATION                0x78d8 +#   define AVIVO_TMDSA_DATA_SYNCHRONIZATION_DSYNSEL           (1 << 0) +#   define AVIVO_TMDSA_DATA_SYNCHRONIZATION_PFREQCHG          (1 << 8) +#define AVIVO_TMDSA_CLOCK_ENABLE            0x7900 +#define AVIVO_TMDSA_TRANSMITTER_ENABLE              0x7904 +#   define AVIVO_TMDSA_TRANSMITTER_ENABLE_TX0_ENABLE          (1 << 0) +#   define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKC0EN             (1 << 1) +#   define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKD00EN            (1 << 2) +#   define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKD01EN            (1 << 3) +#   define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKD02EN            (1 << 4) +#   define AVIVO_TMDSA_TRANSMITTER_ENABLE_TX1_ENABLE          (1 << 8) +#   define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKD10EN            (1 << 10) +#   define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKD11EN            (1 << 11) +#   define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKD12EN            (1 << 12) +#   define AVIVO_TMDSA_TRANSMITTER_ENABLE_TX_ENABLE_HPD_MASK  (1 << 16) +#   define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKCEN_HPD_MASK     (1 << 17) +#   define AVIVO_TMDSA_TRANSMITTER_ENABLE_LNKDEN_HPD_MASK     (1 << 18) + +#define AVIVO_TMDSA_TRANSMITTER_CONTROL				0x7910 +#	define AVIVO_TMDSA_TRANSMITTER_CONTROL_PLL_ENABLE	(1 << 0) +#	define AVIVO_TMDSA_TRANSMITTER_CONTROL_PLL_RESET  	(1 << 1) +#	define AVIVO_TMDSA_TRANSMITTER_CONTROL_PLL_HPD_MASK_SHIFT	(2) +#	define AVIVO_TMDSA_TRANSMITTER_CONTROL_IDSCKSEL	        (1 << 4) +#       define AVIVO_TMDSA_TRANSMITTER_CONTROL_BGSLEEP          (1 << 5) +#	define AVIVO_TMDSA_TRANSMITTER_CONTROL_PLL_PWRUP_SEQ_EN	(1 << 6) +#	define AVIVO_TMDSA_TRANSMITTER_CONTROL_TMCLK	        (1 << 8) +#	define AVIVO_TMDSA_TRANSMITTER_CONTROL_TMCLK_FROM_PADS	(1 << 13) +#	define AVIVO_TMDSA_TRANSMITTER_CONTROL_TDCLK	        (1 << 14) +#	define AVIVO_TMDSA_TRANSMITTER_CONTROL_TDCLK_FROM_PADS	(1 << 15) +#       define AVIVO_TMDSA_TRANSMITTER_CONTROL_CLK_PATTERN_SHIFT (16) +#	define AVIVO_TMDSA_TRANSMITTER_CONTROL_BYPASS_PLL	(1 << 28) +#       define AVIVO_TMDSA_TRANSMITTER_CONTROL_USE_CLK_DATA     (1 << 29) +#	define AVIVO_TMDSA_TRANSMITTER_CONTROL_INPUT_TEST_CLK_SEL	(1 << 31) + +#define AVIVO_LVTMA_CNTL					0x7a80 +#   define AVIVO_LVTMA_CNTL_ENABLE               (1 << 0) +#   define AVIVO_LVTMA_CNTL_HPD_MASK             (1 << 4) +#   define AVIVO_LVTMA_CNTL_HPD_SELECT           (1 << 8) +#   define AVIVO_LVTMA_CNTL_SYNC_PHASE           (1 << 12) +#   define AVIVO_LVTMA_CNTL_PIXEL_ENCODING       (1 << 16) +#   define AVIVO_LVTMA_CNTL_DUAL_LINK_ENABLE     (1 << 24) +#   define AVIVO_LVTMA_CNTL_SWAP                 (1 << 28) +#define AVIVO_LVTMA_SOURCE_SELECT                               0x7a84 +#define AVIVO_LVTMA_COLOR_FORMAT                                0x7a88 +#define AVIVO_LVTMA_BIT_DEPTH_CONTROL                           0x7a94 +#   define AVIVO_LVTMA_BIT_DEPTH_CONTROL_TRUNCATE_EN           (1 << 0) +#   define AVIVO_LVTMA_BIT_DEPTH_CONTROL_TRUNCATE_DEPTH        (1 << 4) +#   define AVIVO_LVTMA_BIT_DEPTH_CONTROL_SPATIAL_DITHER_EN     (1 << 8) +#   define AVIVO_LVTMA_BIT_DEPTH_CONTROL_SPATIAL_DITHER_DEPTH  (1 << 12) +#   define AVIVO_LVTMA_BIT_DEPTH_CONTROL_TEMPORAL_DITHER_EN    (1 << 16) +#   define AVIVO_LVTMA_BIT_DEPTH_CONTROL_TEMPORAL_DITHER_DEPTH (1 << 20) +#   define AVIVO_LVTMA_BIT_DEPTH_CONTROL_TEMPORAL_LEVEL        (1 << 24) +#   define AVIVO_LVTMA_BIT_DEPTH_CONTROL_TEMPORAL_DITHER_RESET (1 << 26) + + + +#define AVIVO_LVTMA_DCBALANCER_CONTROL                  0x7ad0 +#   define AVIVO_LVTMA_DCBALANCER_CONTROL_EN                  (1 << 0) +#   define AVIVO_LVTMA_DCBALANCER_CONTROL_TEST_EN             (1 << 8) +#   define AVIVO_LVTMA_DCBALANCER_CONTROL_TEST_IN_SHIFT       (16) +#   define AVIVO_LVTMA_DCBALANCER_CONTROL_FORCE               (1 << 24) + +#define AVIVO_LVTMA_DATA_SYNCHRONIZATION                0x78d8 +#   define AVIVO_LVTMA_DATA_SYNCHRONIZATION_DSYNSEL           (1 << 0) +#   define AVIVO_LVTMA_DATA_SYNCHRONIZATION_PFREQCHG          (1 << 8) +#define R500_LVTMA_CLOCK_ENABLE			0x7b00 +#define R600_LVTMA_CLOCK_ENABLE			0x7b04 + +#define R500_LVTMA_TRANSMITTER_ENABLE              0x7b04 +#define R600_LVTMA_TRANSMITTER_ENABLE              0x7b08 +#   define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKC0EN             (1 << 1) +#   define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKD00EN            (1 << 2) +#   define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKD01EN            (1 << 3) +#   define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKD02EN            (1 << 4) +#   define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKD03EN            (1 << 5) +#   define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKC1EN             (1 << 9) +#   define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKD10EN            (1 << 10) +#   define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKD11EN            (1 << 11) +#   define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKD12EN            (1 << 12) +#   define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKCEN_HPD_MASK     (1 << 17) +#   define AVIVO_LVTMA_TRANSMITTER_ENABLE_LNKDEN_HPD_MASK     (1 << 18) + +#define R500_LVTMA_TRANSMITTER_CONTROL			        0x7b10 +#define R600_LVTMA_TRANSMITTER_CONTROL			        0x7b14 +#	define AVIVO_LVTMA_TRANSMITTER_CONTROL_PLL_ENABLE	  (1 << 0) +#	define AVIVO_LVTMA_TRANSMITTER_CONTROL_PLL_RESET  	  (1 << 1) +#	define AVIVO_LVTMA_TRANSMITTER_CONTROL_PLL_HPD_MASK_SHIFT (2) +#	define AVIVO_LVTMA_TRANSMITTER_CONTROL_IDSCKSEL	          (1 << 4) +#       define AVIVO_LVTMA_TRANSMITTER_CONTROL_BGSLEEP            (1 << 5) +#	define AVIVO_LVTMA_TRANSMITTER_CONTROL_PLL_PWRUP_SEQ_EN	  (1 << 6) +#	define AVIVO_LVTMA_TRANSMITTER_CONTROL_TMCLK	          (1 << 8) +#	define AVIVO_LVTMA_TRANSMITTER_CONTROL_TMCLK_FROM_PADS	  (1 << 13) +#	define AVIVO_LVTMA_TRANSMITTER_CONTROL_TDCLK	          (1 << 14) +#	define AVIVO_LVTMA_TRANSMITTER_CONTROL_TDCLK_FROM_PADS	  (1 << 15) +#       define AVIVO_LVTMA_TRANSMITTER_CONTROL_CLK_PATTERN_SHIFT  (16) +#	define AVIVO_LVTMA_TRANSMITTER_CONTROL_BYPASS_PLL	  (1 << 28) +#       define AVIVO_LVTMA_TRANSMITTER_CONTROL_USE_CLK_DATA       (1 << 29) +#	define AVIVO_LVTMA_TRANSMITTER_CONTROL_INPUT_TEST_CLK_SEL (1 << 31) + +#define R500_LVTMA_PWRSEQ_CNTL						0x7af0 +#define R600_LVTMA_PWRSEQ_CNTL						0x7af4 +#	define AVIVO_LVTMA_PWRSEQ_EN					    (1 << 0) +#	define AVIVO_LVTMA_PWRSEQ_PLL_ENABLE_MASK			    (1 << 2) +#	define AVIVO_LVTMA_PWRSEQ_PLL_RESET_MASK			    (1 << 3) +#	define AVIVO_LVTMA_PWRSEQ_TARGET_STATE				    (1 << 4) +#	define AVIVO_LVTMA_SYNCEN					    (1 << 8) +#	define AVIVO_LVTMA_SYNCEN_OVRD					    (1 << 9) +#	define AVIVO_LVTMA_SYNCEN_POL					    (1 << 10) +#	define AVIVO_LVTMA_DIGON					    (1 << 16) +#	define AVIVO_LVTMA_DIGON_OVRD					    (1 << 17) +#	define AVIVO_LVTMA_DIGON_POL					    (1 << 18) +#	define AVIVO_LVTMA_BLON						    (1 << 24) +#	define AVIVO_LVTMA_BLON_OVRD					    (1 << 25) +#	define AVIVO_LVTMA_BLON_POL					    (1 << 26) + +#define R500_LVTMA_PWRSEQ_STATE                        0x7af4 +#define R600_LVTMA_PWRSEQ_STATE                        0x7af8 +#       define AVIVO_LVTMA_PWRSEQ_STATE_TARGET_STATE_R          (1 << 0) +#       define AVIVO_LVTMA_PWRSEQ_STATE_DIGON                   (1 << 1) +#       define AVIVO_LVTMA_PWRSEQ_STATE_SYNCEN                  (1 << 2) +#       define AVIVO_LVTMA_PWRSEQ_STATE_BLON                    (1 << 3) +#       define AVIVO_LVTMA_PWRSEQ_STATE_DONE                    (1 << 4) +#       define AVIVO_LVTMA_PWRSEQ_STATE_STATUS_SHIFT            (8) + +#define AVIVO_LVDS_BACKLIGHT_CNTL			0x7af8 +#	define AVIVO_LVDS_BACKLIGHT_CNTL_EN			(1 << 0) +#	define AVIVO_LVDS_BACKLIGHT_LEVEL_MASK		0x0000ff00 +#	define AVIVO_LVDS_BACKLIGHT_LEVEL_SHIFT		8 + +#define AVIVO_DVOA_BIT_DEPTH_CONTROL			0x7988 + +#define AVIVO_GPIO_0                        0x7e30 +#define AVIVO_GPIO_1                        0x7e40 +#define AVIVO_GPIO_2                        0x7e50 +#define AVIVO_GPIO_3                        0x7e60 + +#define AVIVO_DC_GPIO_HPD_Y                 0x7e9c + +#define AVIVO_I2C_STATUS					0x7d30 +#	define AVIVO_I2C_STATUS_DONE				(1 << 0) +#	define AVIVO_I2C_STATUS_NACK				(1 << 1) +#	define AVIVO_I2C_STATUS_HALT				(1 << 2) +#	define AVIVO_I2C_STATUS_GO				(1 << 3) +#	define AVIVO_I2C_STATUS_MASK				0x7 +/* If radeon_mm_i2c is to be believed, this is HALT, NACK, and maybe + * DONE? */ +#	define AVIVO_I2C_STATUS_CMD_RESET			0x7 +#	define AVIVO_I2C_STATUS_CMD_WAIT			(1 << 3) +#define AVIVO_I2C_STOP						0x7d34 +#define AVIVO_I2C_START_CNTL				0x7d38 +#	define AVIVO_I2C_START						(1 << 8) +#	define AVIVO_I2C_CONNECTOR0					(0 << 16) +#	define AVIVO_I2C_CONNECTOR1					(1 << 16) +#define R520_I2C_START (1<<0) +#define R520_I2C_STOP (1<<1) +#define R520_I2C_RX (1<<2) +#define R520_I2C_EN (1<<8) +#define R520_I2C_DDC1 (0<<16) +#define R520_I2C_DDC2 (1<<16) +#define R520_I2C_DDC3 (2<<16) +#define R520_I2C_DDC_MASK (3<<16) +#define AVIVO_I2C_CONTROL2					0x7d3c +#	define AVIVO_I2C_7D3C_SIZE_SHIFT			8 +#	define AVIVO_I2C_7D3C_SIZE_MASK				(0xf << 8) +#define AVIVO_I2C_CONTROL3						0x7d40 +/* Reading is done 4 bytes at a time: read the bottom 8 bits from + * 7d44, four times in a row. + * Writing is a little more complex.  First write DATA with + * 0xnnnnnnzz, then 0xnnnnnnyy, where nnnnnn is some non-deterministic + * magic number, zz is, I think, the slave address, and yy is the byte + * you want to write. */ +#define AVIVO_I2C_DATA						0x7d44 +#define R520_I2C_ADDR_COUNT_MASK (0x7) +#define R520_I2C_DATA_COUNT_SHIFT (8) +#define R520_I2C_DATA_COUNT_MASK (0xF00) +#define AVIVO_I2C_CNTL						0x7d50 +#	define AVIVO_I2C_EN							(1 << 0) +#	define AVIVO_I2C_RESET						(1 << 8) + +#define R600_GENERAL_PWRMGT                                        0x618 +#	define R600_OPEN_DRAIN_PADS				   (1 << 11) + +#define R600_LOWER_GPIO_ENABLE                                     0x710 +#define R600_CTXSW_VID_LOWER_GPIO_CNTL                             0x718 +#define R600_HIGH_VID_LOWER_GPIO_CNTL                              0x71c +#define R600_MEDIUM_VID_LOWER_GPIO_CNTL                            0x720 +#define R600_LOW_VID_LOWER_GPIO_CNTL                               0x724 + +#define R600_MC_VM_FB_LOCATION                                     0x2180 +#define R600_MC_VM_AGP_TOP                                         0x2184 +#define R600_MC_VM_AGP_BOT                                         0x2188 +#define R600_MC_VM_AGP_BASE                                        0x218c +#define R600_MC_VM_SYSTEM_APERTURE_LOW_ADDR                        0x2190 +#define R600_MC_VM_SYSTEM_APERTURE_HIGH_ADDR                       0x2194 +#define R600_MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR                    0x2198 + +#define R700_MC_VM_FB_LOCATION                                     0x2024 + +#define R600_HDP_NONSURFACE_BASE                                0x2c04 + +#define R600_BUS_CNTL                                           0x5420 +#define R600_CONFIG_CNTL                                        0x5424 +#define R600_CONFIG_MEMSIZE                                     0x5428 +#define R600_CONFIG_F0_BASE                                     0x542C +#define R600_CONFIG_APER_SIZE                                   0x5430 + +#define R600_ROM_CNTL                              0x1600 +#       define R600_SCK_OVERWRITE                  (1 << 1) +#       define R600_SCK_PRESCALE_CRYSTAL_CLK_SHIFT 28 +#       define R600_SCK_PRESCALE_CRYSTAL_CLK_MASK  (0xf << 28) + +#define R600_BIOS_0_SCRATCH               0x1724 +#define R600_BIOS_1_SCRATCH               0x1728 +#define R600_BIOS_2_SCRATCH               0x172c +#define R600_BIOS_3_SCRATCH               0x1730 +#define R600_BIOS_4_SCRATCH               0x1734 +#define R600_BIOS_5_SCRATCH               0x1738 +#define R600_BIOS_6_SCRATCH               0x173c +#define R600_BIOS_7_SCRATCH               0x1740 + +#define R300_GB_TILE_CONFIG				0x4018 +#       define R300_ENABLE_TILING                       (1 << 0) +#       define R300_PIPE_COUNT_RV350                    (0 << 1) +#       define R300_PIPE_COUNT_R300                     (3 << 1) +#       define R300_PIPE_COUNT_R420_3P                  (6 << 1) +#       define R300_PIPE_COUNT_R420                     (7 << 1) +#       define R300_TILE_SIZE_8                         (0 << 4) +#       define R300_TILE_SIZE_16                        (1 << 4) +#       define R300_TILE_SIZE_32                        (2 << 4) +#       define R300_SUBPIXEL_1_12                       (0 << 16) +#       define R300_SUBPIXEL_1_16                       (1 << 16) +#define R300_GB_ENABLE				        0x4008 +#define R300_GB_AA_CONFIG				0x4020 +#define R400_GB_PIPE_SELECT                             0x402c +#define R300_GB_MSPOS0				        0x4010 +#       define R300_MS_X0_SHIFT                         0 +#       define R300_MS_Y0_SHIFT                         4 +#       define R300_MS_X1_SHIFT                         8 +#       define R300_MS_Y1_SHIFT                         12 +#       define R300_MS_X2_SHIFT                         16 +#       define R300_MS_Y2_SHIFT                         20 +#       define R300_MSBD0_Y_SHIFT                       24 +#       define R300_MSBD0_X_SHIFT                       28 +#define R300_GB_MSPOS1				        0x4014 +#       define R300_MS_X3_SHIFT                         0 +#       define R300_MS_Y3_SHIFT                         4 +#       define R300_MS_X4_SHIFT                         8 +#       define R300_MS_Y4_SHIFT                         12 +#       define R300_MS_X5_SHIFT                         16 +#       define R300_MS_Y5_SHIFT                         20 +#       define R300_MSBD1_SHIFT                         24 + +#define R300_GA_ENHANCE				        0x4274 +#       define R300_GA_DEADLOCK_CNTL                    (1 << 0) +#       define R300_GA_FASTSYNC_CNTL                    (1 << 1) + +#define R300_GA_POLY_MODE				0x4288 +#       define R300_FRONT_PTYPE_POINT                   (0 << 4) +#       define R300_FRONT_PTYPE_LINE                    (1 << 4) +#       define R300_FRONT_PTYPE_TRIANGE                 (2 << 4) +#       define R300_BACK_PTYPE_POINT                    (0 << 7) +#       define R300_BACK_PTYPE_LINE                     (1 << 7) +#       define R300_BACK_PTYPE_TRIANGE                  (2 << 7) +#define R300_GA_ROUND_MODE				0x428c +#       define R300_GEOMETRY_ROUND_TRUNC                (0 << 0) +#       define R300_GEOMETRY_ROUND_NEAREST              (1 << 0) +#       define R300_COLOR_ROUND_TRUNC                   (0 << 2) +#       define R300_COLOR_ROUND_NEAREST                 (1 << 2) +#define R300_GA_COLOR_CONTROL			        0x4278 +#       define R300_RGB0_SHADING_SOLID                  (0 << 0) +#       define R300_RGB0_SHADING_FLAT                   (1 << 0) +#       define R300_RGB0_SHADING_GOURAUD                (2 << 0) +#       define R300_ALPHA0_SHADING_SOLID                (0 << 2) +#       define R300_ALPHA0_SHADING_FLAT                 (1 << 2) +#       define R300_ALPHA0_SHADING_GOURAUD              (2 << 2) +#       define R300_RGB1_SHADING_SOLID                  (0 << 4) +#       define R300_RGB1_SHADING_FLAT                   (1 << 4) +#       define R300_RGB1_SHADING_GOURAUD                (2 << 4) +#       define R300_ALPHA1_SHADING_SOLID                (0 << 6) +#       define R300_ALPHA1_SHADING_FLAT                 (1 << 6) +#       define R300_ALPHA1_SHADING_GOURAUD              (2 << 6) +#       define R300_RGB2_SHADING_SOLID                  (0 << 8) +#       define R300_RGB2_SHADING_FLAT                   (1 << 8) +#       define R300_RGB2_SHADING_GOURAUD                (2 << 8) +#       define R300_ALPHA2_SHADING_SOLID                (0 << 10) +#       define R300_ALPHA2_SHADING_FLAT                 (1 << 10) +#       define R300_ALPHA2_SHADING_GOURAUD              (2 << 10) +#       define R300_RGB3_SHADING_SOLID                  (0 << 12) +#       define R300_RGB3_SHADING_FLAT                   (1 << 12) +#       define R300_RGB3_SHADING_GOURAUD                (2 << 12) +#       define R300_ALPHA3_SHADING_SOLID                (0 << 14) +#       define R300_ALPHA3_SHADING_FLAT                 (1 << 14) +#       define R300_ALPHA3_SHADING_GOURAUD              (2 << 14) +#define R300_GA_OFFSET				        0x4290 + +#define R500_SU_REG_DEST                                0x42c8 + +#define R300_VAP_CNTL_STATUS				0x2140 +#       define R300_PVS_BYPASS                          (1 << 8) +#define R300_VAP_PVS_STATE_FLUSH_REG		        0x2284 +#define R300_VAP_CNTL				        0x2080 +#       define R300_PVS_NUM_SLOTS_SHIFT                 0 +#       define R300_PVS_NUM_CNTLRS_SHIFT                4 +#       define R300_PVS_NUM_FPUS_SHIFT                  8 +#       define R300_VF_MAX_VTX_NUM_SHIFT                18 +#       define R300_GL_CLIP_SPACE_DEF                   (0 << 22) +#       define R300_DX_CLIP_SPACE_DEF                   (1 << 22) +#       define R500_TCL_STATE_OPTIMIZATION              (1 << 23) +#define R300_VAP_PSC_SGN_NORM_CNTL		        0x21DC +#define R300_VAP_PROG_STREAM_CNTL_0		        0x2150 +#       define R300_DATA_TYPE_0_SHIFT                   0 +#       define R300_DATA_TYPE_FLOAT_1                   0 +#       define R300_DATA_TYPE_FLOAT_2                   1 +#       define R300_DATA_TYPE_FLOAT_3                   2 +#       define R300_DATA_TYPE_FLOAT_4                   3 +#       define R300_DATA_TYPE_BYTE                      4 +#       define R300_DATA_TYPE_D3DCOLOR                  5 +#       define R300_DATA_TYPE_SHORT_2                   6 +#       define R300_DATA_TYPE_SHORT_4                   7 +#       define R300_DATA_TYPE_VECTOR_3_TTT              8 +#       define R300_DATA_TYPE_VECTOR_3_EET              9 +#       define R300_SKIP_DWORDS_0_SHIFT                 4 +#       define R300_DST_VEC_LOC_0_SHIFT                 8 +#       define R300_LAST_VEC_0                          (1 << 13) +#       define R300_SIGNED_0                            (1 << 14) +#       define R300_NORMALIZE_0                         (1 << 15) +#       define R300_DATA_TYPE_1_SHIFT                   16 +#       define R300_SKIP_DWORDS_1_SHIFT                 20 +#       define R300_DST_VEC_LOC_1_SHIFT                 24 +#       define R300_LAST_VEC_1                          (1 << 29) +#       define R300_SIGNED_1                            (1 << 30) +#       define R300_NORMALIZE_1                         (1 << 31) +#define R300_VAP_PROG_STREAM_CNTL_1		        0x2154 +#       define R300_DATA_TYPE_2_SHIFT                   0 +#       define R300_SKIP_DWORDS_2_SHIFT                 4 +#       define R300_DST_VEC_LOC_2_SHIFT                 8 +#       define R300_LAST_VEC_2                          (1 << 13) +#       define R300_SIGNED_2                            (1 << 14) +#       define R300_NORMALIZE_2                         (1 << 15) +#       define R300_DATA_TYPE_3_SHIFT                   16 +#       define R300_SKIP_DWORDS_3_SHIFT                 20 +#       define R300_DST_VEC_LOC_3_SHIFT                 24 +#       define R300_LAST_VEC_3                          (1 << 29) +#       define R300_SIGNED_3                            (1 << 30) +#       define R300_NORMALIZE_3                         (1 << 31) +#define R300_VAP_PROG_STREAM_CNTL_EXT_0	                0x21e0 +#       define R300_SWIZZLE_SELECT_X_0_SHIFT            0 +#       define R300_SWIZZLE_SELECT_Y_0_SHIFT            3 +#       define R300_SWIZZLE_SELECT_Z_0_SHIFT            6 +#       define R300_SWIZZLE_SELECT_W_0_SHIFT            9 +#       define R300_SWIZZLE_SELECT_X                    0 +#       define R300_SWIZZLE_SELECT_Y                    1 +#       define R300_SWIZZLE_SELECT_Z                    2 +#       define R300_SWIZZLE_SELECT_W                    3 +#       define R300_SWIZZLE_SELECT_FP_ZERO              4 +#       define R300_SWIZZLE_SELECT_FP_ONE               5 +#       define R300_WRITE_ENA_0_SHIFT                   12 +#       define R300_WRITE_ENA_X                         1 +#       define R300_WRITE_ENA_Y                         2 +#       define R300_WRITE_ENA_Z                         4 +#       define R300_WRITE_ENA_W                         8 +#       define R300_SWIZZLE_SELECT_X_1_SHIFT            16 +#       define R300_SWIZZLE_SELECT_Y_1_SHIFT            19 +#       define R300_SWIZZLE_SELECT_Z_1_SHIFT            22 +#       define R300_SWIZZLE_SELECT_W_1_SHIFT            25 +#       define R300_WRITE_ENA_1_SHIFT                   28 +#define R300_VAP_PROG_STREAM_CNTL_EXT_1	                0x21e4 +#       define R300_SWIZZLE_SELECT_X_2_SHIFT            0 +#       define R300_SWIZZLE_SELECT_Y_2_SHIFT            3 +#       define R300_SWIZZLE_SELECT_Z_2_SHIFT            6 +#       define R300_SWIZZLE_SELECT_W_2_SHIFT            9 +#       define R300_WRITE_ENA_2_SHIFT                   12 +#       define R300_SWIZZLE_SELECT_X_3_SHIFT            16 +#       define R300_SWIZZLE_SELECT_Y_3_SHIFT            19 +#       define R300_SWIZZLE_SELECT_Z_3_SHIFT            22 +#       define R300_SWIZZLE_SELECT_W_3_SHIFT            25 +#       define R300_WRITE_ENA_3_SHIFT                   28 +#define R300_VAP_PVS_CODE_CNTL_0			0x22D0 +#       define R300_PVS_FIRST_INST_SHIFT                0 +#       define R300_PVS_XYZW_VALID_INST_SHIFT           10 +#       define R300_PVS_LAST_INST_SHIFT                 20 +#define R300_VAP_PVS_CODE_CNTL_1			0x22D8 +#       define R300_PVS_LAST_VTX_SRC_INST_SHIFT         0 +#define R300_VAP_PVS_VECTOR_INDX_REG		        0x2200 +#define R300_VAP_PVS_VECTOR_DATA_REG		        0x2204 +/* PVS instructions */ +/* Opcode and dst instruction */ +#define R300_PVS_DST_OPCODE(x)                          (x << 0) +/* Vector ops */ +#       define R300_VECTOR_NO_OP                        0 +#       define R300_VE_DOT_PRODUCT                      1 +#       define R300_VE_MULTIPLY                         2 +#       define R300_VE_ADD                              3 +#       define R300_VE_MULTIPLY_ADD                     4 +#       define R300_VE_DISTANCE_VECTOR                  5 +#       define R300_VE_FRACTION                         6 +#       define R300_VE_MAXIMUM                          7 +#       define R300_VE_MINIMUM                          8 +#       define R300_VE_SET_GREATER_THAN_EQUAL           9 +#       define R300_VE_SET_LESS_THAN                    10 +#       define R300_VE_MULTIPLYX2_ADD                   11 +#       define R300_VE_MULTIPLY_CLAMP                   12 +#       define R300_VE_FLT2FIX_DX                       13 +#       define R300_VE_FLT2FIX_DX_RND                   14 +/* R500 additions */ +#       define R500_VE_PRED_SET_EQ_PUSH                 15 +#       define R500_VE_PRED_SET_GT_PUSH                 16 +#       define R500_VE_PRED_SET_GTE_PUSH                17 +#       define R500_VE_PRED_SET_NEQ_PUSH                18 +#       define R500_VE_COND_WRITE_EQ                    19 +#       define R500_VE_COND_WRITE_GT                    20 +#       define R500_VE_COND_WRITE_GTE                   21 +#       define R500_VE_COND_WRITE_NEQ                   22 +#       define R500_VE_COND_MUX_EQ                      23 +#       define R500_VE_COND_MUX_GT                      24 +#       define R500_VE_COND_MUX_GTE                     25 +#       define R500_VE_SET_GREATER_THAN                 26 +#       define R500_VE_SET_EQUAL                        27 +#       define R500_VE_SET_NOT_EQUAL                    28 +/* Math ops */ +#       define R300_MATH_NO_OP                          0 +#       define R300_ME_EXP_BASE2_DX                     1 +#       define R300_ME_LOG_BASE2_DX                     2 +#       define R300_ME_EXP_BASEE_FF                     3 +#       define R300_ME_LIGHT_COEFF_DX                   4 +#       define R300_ME_POWER_FUNC_FF                    5 +#       define R300_ME_RECIP_DX                         6 +#       define R300_ME_RECIP_FF                         7 +#       define R300_ME_RECIP_SQRT_DX                    8 +#       define R300_ME_RECIP_SQRT_FF                    9 +#       define R300_ME_MULTIPLY                         10 +#       define R300_ME_EXP_BASE2_FULL_DX                11 +#       define R300_ME_LOG_BASE2_FULL_DX                12 +#       define R300_ME_POWER_FUNC_FF_CLAMP_B            13 +#       define R300_ME_POWER_FUNC_FF_CLAMP_B1           14 +#       define R300_ME_POWER_FUNC_FF_CLAMP_01           15 +#       define R300_ME_SIN                              16 +#       define R300_ME_COS                              17 +/* R500 additions */ +#       define R500_ME_LOG_BASE2_IEEE                   18 +#       define R500_ME_RECIP_IEEE                       19 +#       define R500_ME_RECIP_SQRT_IEEE                  20 +#       define R500_ME_PRED_SET_EQ                      21 +#       define R500_ME_PRED_SET_GT                      22 +#       define R500_ME_PRED_SET_GTE                     23 +#       define R500_ME_PRED_SET_NEQ                     24 +#       define R500_ME_PRED_SET_CLR                     25 +#       define R500_ME_PRED_SET_INV                     26 +#       define R500_ME_PRED_SET_POP                     27 +#       define R500_ME_PRED_SET_RESTORE                 28 +/* macro */ +#       define R300_PVS_MACRO_OP_2CLK_MADD              0 +#       define R300_PVS_MACRO_OP_2CLK_M2X_ADD           1 +#define R300_PVS_DST_MATH_INST                          (1 << 6) +#define R300_PVS_DST_MACRO_INST                         (1 << 7) +#define R300_PVS_DST_REG_TYPE(x)                        (x << 8) +#       define R300_PVS_DST_REG_TEMPORARY               0 +#       define R300_PVS_DST_REG_A0                      1 +#       define R300_PVS_DST_REG_OUT                     2 +#       define R500_PVS_DST_REG_OUT_REPL_X              3 +#       define R300_PVS_DST_REG_ALT_TEMPORARY           4 +#       define R300_PVS_DST_REG_INPUT                   5 +#define R300_PVS_DST_ADDR_MODE_1                        (1 << 12) +#define R300_PVS_DST_OFFSET(x)                          (x << 13) +#define R300_PVS_DST_WE_X                               (1 << 20) +#define R300_PVS_DST_WE_Y                               (1 << 21) +#define R300_PVS_DST_WE_Z                               (1 << 22) +#define R300_PVS_DST_WE_W                               (1 << 23) +#define R300_PVS_DST_VE_SAT                             (1 << 24) +#define R300_PVS_DST_ME_SAT                             (1 << 25) +#define R300_PVS_DST_PRED_ENABLE                        (1 << 26) +#define R300_PVS_DST_PRED_SENSE                         (1 << 27) +#define R300_PVS_DST_DUAL_MATH_OP                       (1 << 28) +#define R300_PVS_DST_ADDR_SEL(x)                        (x << 29) +#define R300_PVS_DST_ADDR_MODE_0                        (1 << 31) +/* src operand instruction */ +#define R300_PVS_SRC_REG_TYPE(x)                        (x << 0) +#       define R300_PVS_SRC_REG_TEMPORARY               0 +#       define R300_PVS_SRC_REG_INPUT                   1 +#       define R300_PVS_SRC_REG_CONSTANT                2 +#       define R300_PVS_SRC_REG_ALT_TEMPORARY           3 +#define R300_SPARE_0                                    (1 << 2) +#define R300_PVS_SRC_ABS_XYZW                           (1 << 3) +#define R300_PVS_SRC_ADDR_MODE_0                        (1 << 4) +#define R300_PVS_SRC_OFFSET(x)                          (x << 5) +#define R300_PVS_SRC_SWIZZLE_X(x)                       (x << 13) +#define R300_PVS_SRC_SWIZZLE_Y(x)                       (x << 16) +#define R300_PVS_SRC_SWIZZLE_Z(x)                       (x << 19) +#define R300_PVS_SRC_SWIZZLE_W(x)                       (x << 22) +#       define R300_PVS_SRC_SELECT_X                    0 +#       define R300_PVS_SRC_SELECT_Y                    1 +#       define R300_PVS_SRC_SELECT_Z                    2 +#       define R300_PVS_SRC_SELECT_W                    3 +#       define R300_PVS_SRC_SELECT_FORCE_0              4 +#       define R300_PVS_SRC_SELECT_FORCE_1              5 +#define R300_PVS_SRC_NEG_X                              (1 << 25) +#define R300_PVS_SRC_NEG_Y                              (1 << 26) +#define R300_PVS_SRC_NEG_Z                              (1 << 27) +#define R300_PVS_SRC_NEG_W                              (1 << 28) +#define R300_PVS_SRC_ADDR_SEL(x)                        (x << 29) +#define R300_PVS_SRC_ADDR_MODE_1                        (1 << 31) + +#define R300_VAP_PVS_FLOW_CNTL_OPC		        0x22DC +#define R300_VAP_OUT_VTX_FMT_0			        0x2090 +#       define R300_VTX_POS_PRESENT                     (1 << 0) +#       define R300_VTX_COLOR_0_PRESENT                 (1 << 1) +#       define R300_VTX_COLOR_1_PRESENT                 (1 << 2) +#       define R300_VTX_COLOR_2_PRESENT                 (1 << 3) +#       define R300_VTX_COLOR_3_PRESENT                 (1 << 4) +#       define R300_VTX_PT_SIZE_PRESENT                 (1 << 16) +#define R300_VAP_OUT_VTX_FMT_1			        0x2094 +#       define R300_TEX_0_COMP_CNT_SHIFT                0 +#       define R300_TEX_1_COMP_CNT_SHIFT                3 +#       define R300_TEX_2_COMP_CNT_SHIFT                6 +#       define R300_TEX_3_COMP_CNT_SHIFT                9 +#       define R300_TEX_4_COMP_CNT_SHIFT                12 +#       define R300_TEX_5_COMP_CNT_SHIFT                15 +#       define R300_TEX_6_COMP_CNT_SHIFT                18 +#       define R300_TEX_7_COMP_CNT_SHIFT                21 +#define R300_VAP_VTX_SIZE				0x20b4 +#define R300_VAP_GB_VERT_CLIP_ADJ		        0x2220 +#define R300_VAP_GB_VERT_DISC_ADJ		        0x2224 +#define R300_VAP_GB_HORZ_CLIP_ADJ		        0x2228 +#define R300_VAP_GB_HORZ_DISC_ADJ		        0x222c +#define R300_VAP_CLIP_CNTL				0x221c +#       define R300_UCP_ENA_0                           (1 << 0) +#       define R300_UCP_ENA_1                           (1 << 1) +#       define R300_UCP_ENA_2                           (1 << 2) +#       define R300_UCP_ENA_3                           (1 << 3) +#       define R300_UCP_ENA_4                           (1 << 4) +#       define R300_UCP_ENA_5                           (1 << 5) +#       define R300_PS_UCP_MODE_SHIFT                   14 +#       define R300_CLIP_DISABLE                        (1 << 16) +#       define R300_UCP_CULL_ONLY_ENA                   (1 << 17) +#       define R300_BOUNDARY_EDGE_FLAG_ENA              (1 << 18) +#define R300_VAP_PVS_STATE_FLUSH_REG			0x2284 + +#define R500_VAP_INDEX_OFFSET			        0x208c + +#define R300_SU_TEX_WRAP				0x42a0 +#define R300_SU_POLY_OFFSET_ENABLE		        0x42b4 +#define R300_SU_CULL_MODE				0x42b8 +#       define R300_CULL_FRONT                          (1 << 0) +#       define R300_CULL_BACK                           (1 << 1) +#       define R300_FACE_POS                            (0 << 2) +#       define R300_FACE_NEG                            (1 << 2) +#define R300_SU_DEPTH_SCALE				0x42c0 +#define R300_SU_DEPTH_OFFSET			        0x42c4 + +#define R300_RS_COUNT				        0x4300 +#	define R300_RS_COUNT_IT_COUNT_SHIFT		0 +#	define R300_RS_COUNT_IC_COUNT_SHIFT		7 +#	define R300_RS_COUNT_HIRES_EN			(1 << 18) + +#define R300_RS_IP_0				        0x4310 +#define R300_RS_IP_1				        0x4314 +#	define R300_RS_TEX_PTR(x)		        (x << 0) +#	define R300_RS_COL_PTR(x)		        (x << 6) +#	define R300_RS_COL_FMT(x)		        (x << 9) +#	define R300_RS_COL_FMT_RGBA		        0 +#	define R300_RS_COL_FMT_RGB0		        2 +#	define R300_RS_COL_FMT_RGB1		        3 +#	define R300_RS_COL_FMT_000A		        4 +#	define R300_RS_COL_FMT_0000		        5 +#	define R300_RS_COL_FMT_0001		        6 +#	define R300_RS_COL_FMT_111A		        8 +#	define R300_RS_COL_FMT_1110		        9 +#	define R300_RS_COL_FMT_1111		        10 +#	define R300_RS_SEL_S(x)		                (x << 13) +#	define R300_RS_SEL_T(x)		                (x << 16) +#	define R300_RS_SEL_R(x)		                (x << 19) +#	define R300_RS_SEL_Q(x)		                (x << 22) +#	define R300_RS_SEL_C0		                0 +#	define R300_RS_SEL_C1		                1 +#	define R300_RS_SEL_C2		                2 +#	define R300_RS_SEL_C3		                3 +#	define R300_RS_SEL_K0		                4 +#	define R300_RS_SEL_K1		                5 +#define R300_RS_INST_COUNT				0x4304 +#	define R300_INST_COUNT_RS(x)		        (x << 0) +#	define R300_RS_W_EN			        (1 << 4) +#	define R300_TX_OFFSET_RS(x)		        (x << 5) +#define R300_RS_INST_0				        0x4330 +#define R300_RS_INST_1				        0x4334 +#	define R300_INST_TEX_ID(x)		        (x << 0) +#       define R300_RS_INST_TEX_CN_WRITE		(1 << 3) +#	define R300_INST_TEX_ADDR(x)		        (x << 6) + +#define R300_TX_INVALTAGS				0x4100 +#define R300_TX_FILTER0_0				0x4400 +#       define R300_TX_CLAMP_S(x)                       (x << 0) +#       define R300_TX_CLAMP_T(x)                       (x << 3) +#       define R300_TX_CLAMP_R(x)                       (x << 6) +#       define R300_TX_CLAMP_WRAP                       0 +#       define R300_TX_CLAMP_MIRROR                     1 +#       define R300_TX_CLAMP_CLAMP_LAST                 2 +#       define R300_TX_CLAMP_MIRROR_CLAMP_LAST          3 +#       define R300_TX_CLAMP_CLAMP_BORDER               4 +#       define R300_TX_CLAMP_MIRROR_CLAMP_BORDER        5 +#       define R300_TX_CLAMP_CLAMP_GL                   6 +#       define R300_TX_CLAMP_MIRROR_CLAMP_GL            7 +#       define R300_TX_MAG_FILTER_NEAREST               (1 << 9) +#       define R300_TX_MIN_FILTER_NEAREST               (1 << 11) +#       define R300_TX_MAG_FILTER_LINEAR                (2 << 9) +#       define R300_TX_MIN_FILTER_LINEAR                (2 << 11) +#       define R300_TX_ID_SHIFT                         28 +#define R300_TX_FILTER1_0				0x4440 +#define R300_TX_FORMAT0_0				0x4480 +#       define R300_TXWIDTH_SHIFT                       0 +#       define R300_TXHEIGHT_SHIFT                      11 +#       define R300_NUM_LEVELS_SHIFT                    26 +#       define R300_NUM_LEVELS_MASK                     0x +#       define R300_TXPROJECTED                         (1 << 30) +#       define R300_TXPITCH_EN                          (1 << 31) +#define R300_TX_FORMAT1_0				0x44c0 +#	define R300_TX_FORMAT_X8		    0x0 +#	define R300_TX_FORMAT_X16		    0x1 +#	define R300_TX_FORMAT_Y4X4		    0x2 +#	define R300_TX_FORMAT_Y8X8		    0x3 +#	define R300_TX_FORMAT_Y16X16		    0x4 +#	define R300_TX_FORMAT_Z3Y3X2		    0x5 +#	define R300_TX_FORMAT_Z5Y6X5		    0x6 +#	define R300_TX_FORMAT_Z6Y5X5		    0x7 +#	define R300_TX_FORMAT_Z11Y11X10		    0x8 +#	define R300_TX_FORMAT_Z10Y11X11		    0x9 +#	define R300_TX_FORMAT_W4Z4Y4X4		    0xA +#	define R300_TX_FORMAT_W1Z5Y5X5		    0xB +#	define R300_TX_FORMAT_W8Z8Y8X8		    0xC +#	define R300_TX_FORMAT_W2Z10Y10X10	    0xD +#	define R300_TX_FORMAT_W16Z16Y16X16	    0xE +#	define R300_TX_FORMAT_DXT1	    	    0xF +#	define R300_TX_FORMAT_DXT3	    	    0x10 +#	define R300_TX_FORMAT_DXT5	    	    0x11 +#	define R300_TX_FORMAT_D3DMFT_CxV8U8	    0x12     /* no swizzle */ +#	define R300_TX_FORMAT_A8R8G8B8	    	    0x13     /* no swizzle */ +#	define R300_TX_FORMAT_B8G8_B8G8	    	    0x14     /* no swizzle */ +#	define R300_TX_FORMAT_G8R8_G8B8	    	    0x15     /* no swizzle */ +#	define R300_TX_FORMAT_VYUY422	    	    0x14     /* no swizzle */ +#	define R300_TX_FORMAT_YVYU422	    	    0x15     /* no swizzle */ +#	define R300_TX_FORMAT_X24_Y8	    	    0x1e +#	define R300_TX_FORMAT_X32	    	    0x1e +	/* Floating point formats */ +	/* Note - hardware supports both 16 and 32 bit floating point */ +#	define R300_TX_FORMAT_FL_I16	    	    0x18 +#	define R300_TX_FORMAT_FL_I16A16	    	    0x19 +#	define R300_TX_FORMAT_FL_R16G16B16A16	    0x1A +#	define R300_TX_FORMAT_FL_I32	    	    0x1B +#	define R300_TX_FORMAT_FL_I32A32	    	    0x1C +#	define R300_TX_FORMAT_FL_R32G32B32A32	    0x1D +	/* alpha modes, convenience mostly */ +	/* if you have alpha, pick constant appropriate to the +	   number of channels (1 for I8, 2 for I8A8, 4 for R8G8B8A8, etc */ +# 	define R300_TX_FORMAT_ALPHA_1CH		    0x000 +# 	define R300_TX_FORMAT_ALPHA_2CH		    0x200 +# 	define R300_TX_FORMAT_ALPHA_4CH		    0x600 +# 	define R300_TX_FORMAT_ALPHA_NONE	    0xA00 +	/* Swizzling */ +	/* constants */ +#	define R300_TX_FORMAT_X		0 +#	define R300_TX_FORMAT_Y		1 +#	define R300_TX_FORMAT_Z		2 +#	define R300_TX_FORMAT_W		3 +#	define R300_TX_FORMAT_ZERO	4 +#	define R300_TX_FORMAT_ONE	5 +	/* 2.0*Z, everything above 1.0 is set to 0.0 */ +#	define R300_TX_FORMAT_CUT_Z	6 +	/* 2.0*W, everything above 1.0 is set to 0.0 */ +#	define R300_TX_FORMAT_CUT_W	7 + +#	define R300_TX_FORMAT_B_SHIFT	18 +#	define R300_TX_FORMAT_G_SHIFT	15 +#	define R300_TX_FORMAT_R_SHIFT	12 +#	define R300_TX_FORMAT_A_SHIFT	9 + +	/* Convenience macro to take care of layout and swizzling */ +#	define R300_EASY_TX_FORMAT(B, G, R, A, FMT)	(		\ +		((R300_TX_FORMAT_##B)<<R300_TX_FORMAT_B_SHIFT)		\ +		| ((R300_TX_FORMAT_##G)<<R300_TX_FORMAT_G_SHIFT)	\ +		| ((R300_TX_FORMAT_##R)<<R300_TX_FORMAT_R_SHIFT)	\ +		| ((R300_TX_FORMAT_##A)<<R300_TX_FORMAT_A_SHIFT)	\ +		| (R300_TX_FORMAT_##FMT)				\ +		) + +#       define R300_TX_FORMAT_YUV_TO_RGB_CLAMP         (1 << 22) +#       define R300_TX_FORMAT_YUV_TO_RGB_NO_CLAMP      (2 << 22) +#       define R300_TX_FORMAT_SWAP_YUV                 (1 << 24) + +#define R300_TX_FORMAT2_0				0x4500 +#       define R500_TXWIDTH_11                          (1 << 15) +#       define R500_TXHEIGHT_11                         (1 << 16) + +#define R300_TX_OFFSET_0				0x4540 +#       define R300_ENDIAN_SWAP_16_BIT                  (1 << 0) +#       define R300_ENDIAN_SWAP_32_BIT                  (2 << 0) +#       define R300_ENDIAN_SWAP_HALF_DWORD              (3 << 0) +#       define R300_MACRO_TILE                          (1 << 2) + +#define R300_TX_ENABLE				        0x4104 +#       define R300_TEX_0_ENABLE                        (1 << 0) +#       define R300_TEX_1_ENABLE                        (1 << 1) + +#define R300_US_W_FMT				        0x46b4 +#define R300_US_OUT_FMT_1				0x46a8 +#define R300_US_OUT_FMT_2				0x46ac +#define R300_US_OUT_FMT_3				0x46b0 +#define R300_US_OUT_FMT_0				0x46a4 +#       define R300_OUT_FMT_C4_8                        (0 << 0) +#       define R300_OUT_FMT_C4_10                       (1 << 0) +#       define R300_OUT_FMT_C4_10_GAMMA                 (2 << 0) +#       define R300_OUT_FMT_C_16                        (3 << 0) +#       define R300_OUT_FMT_C2_16                       (4 << 0) +#       define R300_OUT_FMT_C4_16                       (5 << 0) +#       define R300_OUT_FMT_C_16_MPEG                   (6 << 0) +#       define R300_OUT_FMT_C2_16_MPEG                  (7 << 0) +#       define R300_OUT_FMT_C2_4                        (8 << 0) +#       define R300_OUT_FMT_C_3_3_2                     (9 << 0) +#       define R300_OUT_FMT_C_5_6_5                     (10 << 0) +#       define R300_OUT_FMT_C_11_11_10                  (11 << 0) +#       define R300_OUT_FMT_C_10_11_11                  (12 << 0) +#       define R300_OUT_FMT_C_2_10_10_10                (13 << 0) +#       define R300_OUT_FMT_UNUSED                      (15 << 0) +#       define R300_OUT_FMT_C_16_FP                     (16 << 0) +#       define R300_OUT_FMT_C2_16_FP                    (17 << 0) +#       define R300_OUT_FMT_C4_16_FP                    (18 << 0) +#       define R300_OUT_FMT_C_32_FP                     (19 << 0) +#       define R300_OUT_FMT_C2_32_FP                    (20 << 0) +#       define R300_OUT_FMT_C4_32_FP                    (21 << 0) +#       define R300_OUT_FMT_C0_SEL_ALPHA                (0 << 8) +#       define R300_OUT_FMT_C0_SEL_RED                  (1 << 8) +#       define R300_OUT_FMT_C0_SEL_GREEN                (2 << 8) +#       define R300_OUT_FMT_C0_SEL_BLUE                 (3 << 8) +#       define R300_OUT_FMT_C1_SEL_ALPHA                (0 << 10) +#       define R300_OUT_FMT_C1_SEL_RED                  (1 << 10) +#       define R300_OUT_FMT_C1_SEL_GREEN                (2 << 10) +#       define R300_OUT_FMT_C1_SEL_BLUE                 (3 << 10) +#       define R300_OUT_FMT_C2_SEL_ALPHA                (0 << 12) +#       define R300_OUT_FMT_C2_SEL_RED                  (1 << 12) +#       define R300_OUT_FMT_C2_SEL_GREEN                (2 << 12) +#       define R300_OUT_FMT_C2_SEL_BLUE                 (3 << 12) +#       define R300_OUT_FMT_C3_SEL_ALPHA                (0 << 14) +#       define R300_OUT_FMT_C3_SEL_RED                  (1 << 14) +#       define R300_OUT_FMT_C3_SEL_GREEN                (2 << 14) +#       define R300_OUT_FMT_C3_SEL_BLUE                 (3 << 14) +#define R300_US_CONFIG				        0x4600 +#       define R300_NLEVEL_SHIFT                        0 +#       define R300_FIRST_TEX                           (1 << 3) +#       define R500_ZERO_TIMES_ANYTHING_EQUALS_ZERO     (1 << 1) +#define R300_US_PIXSIZE				        0x4604 +#define R300_US_CODE_OFFSET				0x4608 +#       define R300_ALU_CODE_OFFSET(x)                  (x << 0) +#       define R300_ALU_CODE_SIZE(x)                    (x << 6) +#       define R300_TEX_CODE_OFFSET(x)                  (x << 13) +#       define R300_TEX_CODE_SIZE(x)                    (x << 18) +#define R300_US_CODE_ADDR_0				0x4610 +#       define R300_ALU_START(x)                        (x << 0) +#       define R300_ALU_SIZE(x)                         (x << 6) +#       define R300_TEX_START(x)                        (x << 12) +#       define R300_TEX_SIZE(x)                         (x << 17) +#       define R300_RGBA_OUT                            (1 << 22) +#       define R300_W_OUT                               (1 << 23) +#define R300_US_CODE_ADDR_1				0x4614 +#define R300_US_CODE_ADDR_2				0x4618 +#define R300_US_CODE_ADDR_3				0x461c +#define R300_US_TEX_INST_0				0x4620 +#define R300_US_TEX_INST_1				0x4624 +#define R300_US_TEX_INST_2				0x4628 +#       define R300_TEX_SRC_ADDR(x)                     (x << 0) +#       define R300_TEX_DST_ADDR(x)                     (x << 6) +#       define R300_TEX_ID(x)                           (x << 11) +#       define R300_TEX_INST(x)                         (x << 15) +#       define R300_TEX_INST_NOP                        0 +#       define R300_TEX_INST_LD                         1 +#       define R300_TEX_INST_TEXKILL                    2 +#       define R300_TEX_INST_PROJ                       3 +#       define R300_TEX_INST_LODBIAS                    4 +#define R300_US_ALU_RGB_ADDR_0			        0x46c0 +#define R300_US_ALU_RGB_ADDR_1			        0x46c4 +#define R300_US_ALU_RGB_ADDR_2			        0x46c8 +/* for ADDR0-2, values 0-31 specify a location in the pixel stack, +   values 32-63 specify a constant */ +#       define R300_ALU_RGB_ADDR0(x)                    (x << 0) +#       define R300_ALU_RGB_ADDR1(x)                    (x << 6) +#       define R300_ALU_RGB_ADDR2(x)                    (x << 12) +/* ADDRD - where on the pixel stack the result of this instruction +   will be written */ +#       define R300_ALU_RGB_ADDRD(x)                    (x << 18) +#       define R300_ALU_RGB_WMASK(x)                    (x << 23) +#       define R300_ALU_RGB_OMASK(x)                    (x << 26) +#       define R300_ALU_RGB_MASK_NONE                   0 +#       define R300_ALU_RGB_MASK_R                      1 +#       define R300_ALU_RGB_MASK_G                      2 +#       define R300_ALU_RGB_MASK_B                      4 +#       define R300_ALU_RGB_TARGET_A                    (0 << 29) +#       define R300_ALU_RGB_TARGET_B                    (1 << 29) +#       define R300_ALU_RGB_TARGET_C                    (2 << 29) +#       define R300_ALU_RGB_TARGET_D                    (3 << 29) +#define R300_US_ALU_RGB_INST_0			        0x48c0 +#define R300_US_ALU_RGB_INST_1			        0x48c4 +#define R300_US_ALU_RGB_INST_2			        0x48c8 +#       define R300_ALU_RGB_SEL_A(x)                    (x << 0) +#       define R300_ALU_RGB_SRC0_RGB                    0 +#       define R300_ALU_RGB_SRC0_RRR                    1 +#       define R300_ALU_RGB_SRC0_GGG                    2 +#       define R300_ALU_RGB_SRC0_BBB                    3 +#       define R300_ALU_RGB_SRC1_RGB                    4 +#       define R300_ALU_RGB_SRC1_RRR                    5 +#       define R300_ALU_RGB_SRC1_GGG                    6 +#       define R300_ALU_RGB_SRC1_BBB                    7 +#       define R300_ALU_RGB_SRC2_RGB                    8 +#       define R300_ALU_RGB_SRC2_RRR                    9 +#       define R300_ALU_RGB_SRC2_GGG                    10 +#       define R300_ALU_RGB_SRC2_BBB                    11 +#       define R300_ALU_RGB_SRC0_AAA                    12 +#       define R300_ALU_RGB_SRC1_AAA                    13 +#       define R300_ALU_RGB_SRC2_AAA                    14 +#       define R300_ALU_RGB_SRCP_RGB                    15 +#       define R300_ALU_RGB_SRCP_RRR                    16 +#       define R300_ALU_RGB_SRCP_GGG                    17 +#       define R300_ALU_RGB_SRCP_BBB                    18 +#       define R300_ALU_RGB_SRCP_AAA                    19 +#       define R300_ALU_RGB_0_0                         20 +#       define R300_ALU_RGB_1_0                         21 +#       define R300_ALU_RGB_0_5                         22 +#       define R300_ALU_RGB_SRC0_GBR                    23 +#       define R300_ALU_RGB_SRC1_GBR                    24 +#       define R300_ALU_RGB_SRC2_GBR                    25 +#       define R300_ALU_RGB_SRC0_BRG                    26 +#       define R300_ALU_RGB_SRC1_BRG                    27 +#       define R300_ALU_RGB_SRC2_BRG                    28 +#       define R300_ALU_RGB_SRC0_ABG                    29 +#       define R300_ALU_RGB_SRC1_ABG                    30 +#       define R300_ALU_RGB_SRC2_ABG                    31 +#       define R300_ALU_RGB_MOD_A(x)                    (x << 5) +#       define R300_ALU_RGB_MOD_NOP                     0 +#       define R300_ALU_RGB_MOD_NEG                     1 +#       define R300_ALU_RGB_MOD_ABS                     2 +#       define R300_ALU_RGB_MOD_NAB                     3 +#       define R300_ALU_RGB_SEL_B(x)                    (x << 7) +#       define R300_ALU_RGB_MOD_B(x)                    (x << 12) +#       define R300_ALU_RGB_SEL_C(x)                    (x << 14) +#       define R300_ALU_RGB_MOD_C(x)                    (x << 19) +#       define R300_ALU_RGB_SRCP_OP(x)                  (x << 21) +#       define R300_ALU_RGB_SRCP_OP_1_MINUS_2RGB0	0 +#       define R300_ALU_RGB_SRCP_OP_RGB1_MINUS_RGB0	1 +#       define R300_ALU_RGB_SRCP_OP_RGB1_PLUS_RGB0	2 +#       define R300_ALU_RGB_SRCP_OP_1_MINUS_RGB0	3 +#       define R300_ALU_RGB_OP(x)                       (x << 23) +#       define R300_ALU_RGB_OP_MAD                      0 +#       define R300_ALU_RGB_OP_DP3                      1 +#       define R300_ALU_RGB_OP_DP4                      2 +#       define R300_ALU_RGB_OP_D2A                      3 +#       define R300_ALU_RGB_OP_MIN                      4 +#       define R300_ALU_RGB_OP_MAX                      5 +#       define R300_ALU_RGB_OP_CND                      7 +#       define R300_ALU_RGB_OP_CMP                      8 +#       define R300_ALU_RGB_OP_FRC                      9 +#       define R300_ALU_RGB_OP_SOP                      10 +#       define R300_ALU_RGB_OMOD(x)                     (x << 27) +#       define R300_ALU_RGB_OMOD_NONE                   0 +#       define R300_ALU_RGB_OMOD_MUL_2                  1 +#       define R300_ALU_RGB_OMOD_MUL_4                  2 +#       define R300_ALU_RGB_OMOD_MUL_8                  3 +#       define R300_ALU_RGB_OMOD_DIV_2                  4 +#       define R300_ALU_RGB_OMOD_DIV_4                  5 +#       define R300_ALU_RGB_OMOD_DIV_8                  6 +#       define R300_ALU_RGB_CLAMP                       (1 << 30) +#       define R300_ALU_RGB_INSERT_NOP                  (1 << 31) +#define R300_US_ALU_ALPHA_ADDR_0		        0x47c0 +#define R300_US_ALU_ALPHA_ADDR_1		        0x47c4 +#define R300_US_ALU_ALPHA_ADDR_2		        0x47c8 +/* for ADDR0-2, values 0-31 specify a location in the pixel stack, +   values 32-63 specify a constant */ +#       define R300_ALU_ALPHA_ADDR0(x)                  (x << 0) +#       define R300_ALU_ALPHA_ADDR1(x)                  (x << 6) +#       define R300_ALU_ALPHA_ADDR2(x)                  (x << 12) +/* ADDRD - where on the pixel stack the result of this instruction +   will be written */ +#       define R300_ALU_ALPHA_ADDRD(x)                  (x << 18) +#       define R300_ALU_ALPHA_WMASK(x)                  (x << 23) +#       define R300_ALU_ALPHA_OMASK(x)                  (x << 24) +#       define R300_ALU_ALPHA_OMASK_W(x)                (x << 27) +#       define R300_ALU_ALPHA_MASK_NONE                 0 +#       define R300_ALU_ALPHA_MASK_A                    1 +#       define R300_ALU_ALPHA_TARGET_A                  (0 << 25) +#       define R300_ALU_ALPHA_TARGET_B                  (1 << 25) +#       define R300_ALU_ALPHA_TARGET_C                  (2 << 25) +#       define R300_ALU_ALPHA_TARGET_D                  (3 << 25) +#define R300_US_ALU_ALPHA_INST_0		        0x49c0 +#define R300_US_ALU_ALPHA_INST_1		        0x49c4 +#define R300_US_ALU_ALPHA_INST_2		        0x49c8 +#       define R300_ALU_ALPHA_SEL_A(x)                  (x << 0) +#       define R300_ALU_ALPHA_SRC0_R                    0 +#       define R300_ALU_ALPHA_SRC0_G                    1 +#       define R300_ALU_ALPHA_SRC0_B                    2 +#       define R300_ALU_ALPHA_SRC1_R                    3 +#       define R300_ALU_ALPHA_SRC1_G                    4 +#       define R300_ALU_ALPHA_SRC1_B                    5 +#       define R300_ALU_ALPHA_SRC2_R                    6 +#       define R300_ALU_ALPHA_SRC2_G                    7 +#       define R300_ALU_ALPHA_SRC2_B                    8 +#       define R300_ALU_ALPHA_SRC0_A                    9 +#       define R300_ALU_ALPHA_SRC1_A                    10 +#       define R300_ALU_ALPHA_SRC2_A                    11 +#       define R300_ALU_ALPHA_SRCP_R                    12 +#       define R300_ALU_ALPHA_SRCP_G                    13 +#       define R300_ALU_ALPHA_SRCP_B                    14 +#       define R300_ALU_ALPHA_SRCP_A                    15 +#       define R300_ALU_ALPHA_0_0                       16 +#       define R300_ALU_ALPHA_1_0                       17 +#       define R300_ALU_ALPHA_0_5                       18 +#       define R300_ALU_ALPHA_MOD_A(x)                  (x << 5) +#       define R300_ALU_ALPHA_MOD_NOP                   0 +#       define R300_ALU_ALPHA_MOD_NEG                   1 +#       define R300_ALU_ALPHA_MOD_ABS                   2 +#       define R300_ALU_ALPHA_MOD_NAB                   3 +#       define R300_ALU_ALPHA_SEL_B(x)                  (x << 7) +#       define R300_ALU_ALPHA_MOD_B(x)                  (x << 12) +#       define R300_ALU_ALPHA_SEL_C(x)                  (x << 14) +#       define R300_ALU_ALPHA_MOD_C(x)                  (x << 19) +#       define R300_ALU_ALPHA_SRCP_OP(x)                (x << 21) +#       define R300_ALU_ALPHA_SRCP_OP_1_MINUS_2RGB0	0 +#       define R300_ALU_ALPHA_SRCP_OP_RGB1_MINUS_RGB0	1 +#       define R300_ALU_ALPHA_SRCP_OP_RGB1_PLUS_RGB0	2 +#       define R300_ALU_ALPHA_SRCP_OP_1_MINUS_RGB0	3 +#       define R300_ALU_ALPHA_OP(x)                     (x << 23) +#       define R300_ALU_ALPHA_OP_MAD                    0 +#       define R300_ALU_ALPHA_OP_DP                     1 +#       define R300_ALU_ALPHA_OP_MIN                    2 +#       define R300_ALU_ALPHA_OP_MAX                    3 +#       define R300_ALU_ALPHA_OP_CND                    5 +#       define R300_ALU_ALPHA_OP_CMP                    6 +#       define R300_ALU_ALPHA_OP_FRC                    7 +#       define R300_ALU_ALPHA_OP_EX2                    8 +#       define R300_ALU_ALPHA_OP_LN2                    9 +#       define R300_ALU_ALPHA_OP_RCP                    10 +#       define R300_ALU_ALPHA_OP_RSQ                    11 +#       define R300_ALU_ALPHA_OMOD(x)                   (x << 27) +#       define R300_ALU_ALPHA_OMOD_NONE                 0 +#       define R300_ALU_ALPHA_OMOD_MUL_2                1 +#       define R300_ALU_ALPHA_OMOD_MUL_4                2 +#       define R300_ALU_ALPHA_OMOD_MUL_8                3 +#       define R300_ALU_ALPHA_OMOD_DIV_2                4 +#       define R300_ALU_ALPHA_OMOD_DIV_4                5 +#       define R300_ALU_ALPHA_OMOD_DIV_8                6 +#       define R300_ALU_ALPHA_CLAMP                     (1 << 30) + +#define R300_FG_DEPTH_SRC				0x4bd8 +#define R300_FG_FOG_BLEND				0x4bc0 +#define R300_FG_ALPHA_FUNC				0x4bd4 + +#define R300_DST_PIPE_CONFIG		                0x170c +#       define R300_PIPE_AUTO_CONFIG                    (1 << 31) +#define R300_RB2D_DSTCACHE_MODE		                0x3428 +#define R300_RB2D_DSTCACHE_MODE		                0x3428 +#       define R300_DC_AUTOFLUSH_ENABLE                 (1 << 8) +#       define R300_DC_DC_DISABLE_IGNORE_PE             (1 << 17) +#define R300_RB2D_DSTCACHE_CTLSTAT		        0x342c /* use DSTCACHE_CTLSTAT instead */ +#define R300_DSTCACHE_CTLSTAT		                0x1714 +#       define R300_DC_FLUSH_2D                         (1 << 0) +#       define R300_DC_FREE_2D                          (1 << 2) +#       define R300_RB2D_DC_FLUSH_ALL                   (R300_DC_FLUSH_2D | R300_DC_FREE_2D) +#       define R300_RB2D_DC_BUSY                        (1 << 31) +#define R300_RB3D_DSTCACHE_CTLSTAT		        0x4e4c +#       define R300_DC_FLUSH_3D                         (2 << 0) +#       define R300_DC_FREE_3D                          (2 << 2) +#       define R300_RB3D_DC_FLUSH_ALL                   (R300_DC_FLUSH_3D | R300_DC_FREE_3D) +#       define R300_DC_FINISH_3D                        (1 << 4) +#define R300_RB3D_ZCACHE_CTLSTAT			0x4f18 +#       define R300_ZC_FLUSH                            (1 << 0) +#       define R300_ZC_FREE                             (1 << 1) +#       define R300_ZC_FLUSH_ALL                        0x3 +#define R300_RB3D_ZSTENCILCNTL			        0x4f04 +#define R300_RB3D_ZCACHE_CTLSTAT		        0x4f18 +#define R300_RB3D_BW_CNTL				0x4f1c +#define R300_RB3D_ZCNTL				        0x4f00 +#define R300_RB3D_ZTOP				        0x4f14 +#define R300_RB3D_ROPCNTL				0x4e18 +#define R300_RB3D_BLENDCNTL				0x4e04 +#       define R300_ALPHA_BLEND_ENABLE                  (1 << 0) +#       define R300_SEPARATE_ALPHA_ENABLE               (1 << 1) +#       define R300_READ_ENABLE                         (1 << 2) +#define R300_RB3D_ABLENDCNTL			        0x4e08 +#define R300_RB3D_DSTCACHE_CTLSTAT		        0x4e4c + +#define R300_RB3D_COLOR_CHANNEL_MASK	                0x4e0c +#       define R300_BLUE_MASK_EN                        (1 << 0) +#       define R300_GREEN_MASK_EN                       (1 << 1) +#       define R300_RED_MASK_EN                         (1 << 2) +#       define R300_ALPHA_MASK_EN                       (1 << 3) +#define R300_RB3D_COLOR_CLEAR_VALUE                     0x4e14 +#define R300_RB3D_DSTCACHE_CTLSTAT		        0x4e4c +#define R300_RB3D_CCTL				        0x4e00 +#define R300_RB3D_DITHER_CTL			        0x4e50 + +#define R300_SC_EDGERULE				0x43a8 +#define R300_SC_SCISSOR0				0x43e0 +#define R300_SC_SCISSOR1				0x43e4 +#       define R300_SCISSOR_X_SHIFT                     0 +#       define R300_SCISSOR_Y_SHIFT                     13 +#define R300_SC_CLIP_0_A				0x43b0 +#define R300_SC_CLIP_0_B				0x43b4 +#       define R300_CLIP_X_SHIFT                        0 +#       define R300_CLIP_Y_SHIFT                        13 +#define R300_SC_CLIP_RULE				0x43d0 +#define R300_SC_SCREENDOOR				0x43e8 + +/* R500 US has to be loaded through an index/data pair */ +#define R500_GA_US_VECTOR_INDEX				0x4250 +#   define R500_US_VECTOR_INDEX(x)			(x << 0) +#   define R500_US_VECTOR_TYPE_INST			(0 << 16) +#   define R500_US_VECTOR_TYPE_CONST			(1 << 16) +#   define R500_US_VECTOR_CLAMP				(1 << 17) +#define R500_GA_US_VECTOR_DATA				0x4254 + +/* + * The R500 unified shader (US) registers come in banks of 512 each, one + * for each instruction slot in the shader.  You can't touch them directly. + * R500_US_VECTOR_INDEX() sets the base instruction to modify; successive + * writes to R500_GA_US_VECTOR_DATA autoincrement the index after the + * instruction is fully specified. + */ +#define R500_US_ALU_ALPHA_INST_0			0xa800 +#   define R500_ALPHA_OP_MAD				0 +#   define R500_ALPHA_OP_DP				1 +#   define R500_ALPHA_OP_MIN				2 +#   define R500_ALPHA_OP_MAX				3 +/* #define R500_ALPHA_OP_RESERVED			4 */ +#   define R500_ALPHA_OP_CND				5 +#   define R500_ALPHA_OP_CMP				6 +#   define R500_ALPHA_OP_FRC				7 +#   define R500_ALPHA_OP_EX2				8 +#   define R500_ALPHA_OP_LN2				9 +#   define R500_ALPHA_OP_RCP				10 +#   define R500_ALPHA_OP_RSQ				11 +#   define R500_ALPHA_OP_SIN				12 +#   define R500_ALPHA_OP_COS				13 +#   define R500_ALPHA_OP_MDH				14 +#   define R500_ALPHA_OP_MDV				15 +#   define R500_ALPHA_ADDRD(x)				(x << 4) +#   define R500_ALPHA_ADDRD_REL				(1 << 11) +#   define R500_ALPHA_SEL_A_SRC0			(0 << 12) +#   define R500_ALPHA_SEL_A_SRC1			(1 << 12) +#   define R500_ALPHA_SEL_A_SRC2			(2 << 12) +#   define R500_ALPHA_SEL_A_SRCP			(3 << 12) +#   define R500_ALPHA_SWIZ_A_R				(0 << 14) +#   define R500_ALPHA_SWIZ_A_G				(1 << 14) +#   define R500_ALPHA_SWIZ_A_B				(2 << 14) +#   define R500_ALPHA_SWIZ_A_A				(3 << 14) +#   define R500_ALPHA_SWIZ_A_0				(4 << 14) +#   define R500_ALPHA_SWIZ_A_HALF			(5 << 14) +#   define R500_ALPHA_SWIZ_A_1				(6 << 14) +/* #define R500_ALPHA_SWIZ_A_UNUSED			(7 << 14) */ +#   define R500_ALPHA_MOD_A_NOP				(0 << 17) +#   define R500_ALPHA_MOD_A_NEG				(1 << 17) +#   define R500_ALPHA_MOD_A_ABS				(2 << 17) +#   define R500_ALPHA_MOD_A_NAB				(3 << 17) +#   define R500_ALPHA_SEL_B_SRC0			(0 << 19) +#   define R500_ALPHA_SEL_B_SRC1			(1 << 19) +#   define R500_ALPHA_SEL_B_SRC2			(2 << 19) +#   define R500_ALPHA_SEL_B_SRCP			(3 << 19) +#   define R500_ALPHA_SWIZ_B_R				(0 << 21) +#   define R500_ALPHA_SWIZ_B_G				(1 << 21) +#   define R500_ALPHA_SWIZ_B_B				(2 << 21) +#   define R500_ALPHA_SWIZ_B_A				(3 << 21) +#   define R500_ALPHA_SWIZ_B_0				(4 << 21) +#   define R500_ALPHA_SWIZ_B_HALF			(5 << 21) +#   define R500_ALPHA_SWIZ_B_1				(6 << 21) +/* #define R500_ALPHA_SWIZ_B_UNUSED			(7 << 21) */ +#   define R500_ALPHA_MOD_B_NOP				(0 << 24) +#   define R500_ALPHA_MOD_B_NEG				(1 << 24) +#   define R500_ALPHA_MOD_B_ABS				(2 << 24) +#   define R500_ALPHA_MOD_B_NAB				(3 << 24) +#   define R500_ALPHA_OMOD_IDENTITY			(0 << 26) +#   define R500_ALPHA_OMOD_MUL_2			(1 << 26) +#   define R500_ALPHA_OMOD_MUL_4			(2 << 26) +#   define R500_ALPHA_OMOD_MUL_8			(3 << 26) +#   define R500_ALPHA_OMOD_DIV_2			(4 << 26) +#   define R500_ALPHA_OMOD_DIV_4			(5 << 26) +#   define R500_ALPHA_OMOD_DIV_8			(6 << 26) +#   define R500_ALPHA_OMOD_DISABLE			(7 << 26) +#   define R500_ALPHA_TARGET(x)				(x << 29) +#   define R500_ALPHA_W_OMASK				(1 << 31) +#define R500_US_ALU_ALPHA_ADDR_0			0x9800 +#   define R500_ALPHA_ADDR0(x)				(x << 0) +#   define R500_ALPHA_ADDR0_CONST			(1 << 8) +#   define R500_ALPHA_ADDR0_REL				(1 << 9) +#   define R500_ALPHA_ADDR1(x)				(x << 10) +#   define R500_ALPHA_ADDR1_CONST			(1 << 18) +#   define R500_ALPHA_ADDR1_REL				(1 << 19) +#   define R500_ALPHA_ADDR2(x)				(x << 20) +#   define R500_ALPHA_ADDR2_CONST			(1 << 28) +#   define R500_ALPHA_ADDR2_REL				(1 << 29) +#   define R500_ALPHA_SRCP_OP_1_MINUS_2A0		(0 << 30) +#   define R500_ALPHA_SRCP_OP_A1_MINUS_A0		(1 << 30) +#   define R500_ALPHA_SRCP_OP_A1_PLUS_A0		(2 << 30) +#   define R500_ALPHA_SRCP_OP_1_MINUS_A0		(3 << 30) +#define R500_US_ALU_RGBA_INST_0				0xb000 +#   define R500_ALU_RGBA_OP_MAD				(0 << 0) +#   define R500_ALU_RGBA_OP_DP3				(1 << 0) +#   define R500_ALU_RGBA_OP_DP4				(2 << 0) +#   define R500_ALU_RGBA_OP_D2A				(3 << 0) +#   define R500_ALU_RGBA_OP_MIN				(4 << 0) +#   define R500_ALU_RGBA_OP_MAX				(5 << 0) +/* #define R500_ALU_RGBA_OP_RESERVED			(6 << 0) */ +#   define R500_ALU_RGBA_OP_CND				(7 << 0) +#   define R500_ALU_RGBA_OP_CMP				(8 << 0) +#   define R500_ALU_RGBA_OP_FRC				(9 << 0) +#   define R500_ALU_RGBA_OP_SOP				(10 << 0) +#   define R500_ALU_RGBA_OP_MDH				(11 << 0) +#   define R500_ALU_RGBA_OP_MDV				(12 << 0) +#   define R500_ALU_RGBA_ADDRD(x)			(x << 4) +#   define R500_ALU_RGBA_ADDRD_REL			(1 << 11) +#   define R500_ALU_RGBA_SEL_C_SRC0			(0 << 12) +#   define R500_ALU_RGBA_SEL_C_SRC1			(1 << 12) +#   define R500_ALU_RGBA_SEL_C_SRC2			(2 << 12) +#   define R500_ALU_RGBA_SEL_C_SRCP			(3 << 12) +#   define R500_ALU_RGBA_R_SWIZ_R			(0 << 14) +#   define R500_ALU_RGBA_R_SWIZ_G			(1 << 14) +#   define R500_ALU_RGBA_R_SWIZ_B			(2 << 14) +#   define R500_ALU_RGBA_R_SWIZ_A			(3 << 14) +#   define R500_ALU_RGBA_R_SWIZ_0			(4 << 14) +#   define R500_ALU_RGBA_R_SWIZ_HALF			(5 << 14) +#   define R500_ALU_RGBA_R_SWIZ_1			(6 << 14) +/* #define R500_ALU_RGBA_R_SWIZ_UNUSED			(7 << 14) */ +#   define R500_ALU_RGBA_G_SWIZ_R			(0 << 17) +#   define R500_ALU_RGBA_G_SWIZ_G			(1 << 17) +#   define R500_ALU_RGBA_G_SWIZ_B			(2 << 17) +#   define R500_ALU_RGBA_G_SWIZ_A			(3 << 17) +#   define R500_ALU_RGBA_G_SWIZ_0			(4 << 17) +#   define R500_ALU_RGBA_G_SWIZ_HALF			(5 << 17) +#   define R500_ALU_RGBA_G_SWIZ_1			(6 << 17) +/* #define R500_ALU_RGBA_G_SWIZ_UNUSED			(7 << 17) */ +#   define R500_ALU_RGBA_B_SWIZ_R			(0 << 20) +#   define R500_ALU_RGBA_B_SWIZ_G			(1 << 20) +#   define R500_ALU_RGBA_B_SWIZ_B			(2 << 20) +#   define R500_ALU_RGBA_B_SWIZ_A			(3 << 20) +#   define R500_ALU_RGBA_B_SWIZ_0			(4 << 20) +#   define R500_ALU_RGBA_B_SWIZ_HALF			(5 << 20) +#   define R500_ALU_RGBA_B_SWIZ_1			(6 << 20) +/* #define R500_ALU_RGBA_B_SWIZ_UNUSED			(7 << 20) */ +#   define R500_ALU_RGBA_MOD_C_NOP			(0 << 23) +#   define R500_ALU_RGBA_MOD_C_NEG			(1 << 23) +#   define R500_ALU_RGBA_MOD_C_ABS			(2 << 23) +#   define R500_ALU_RGBA_MOD_C_NAB			(3 << 23) +#   define R500_ALU_RGBA_ALPHA_SEL_C_SRC0		(0 << 25) +#   define R500_ALU_RGBA_ALPHA_SEL_C_SRC1		(1 << 25) +#   define R500_ALU_RGBA_ALPHA_SEL_C_SRC2		(2 << 25) +#   define R500_ALU_RGBA_ALPHA_SEL_C_SRCP		(3 << 25) +#   define R500_ALU_RGBA_A_SWIZ_R			(0 << 27) +#   define R500_ALU_RGBA_A_SWIZ_G			(1 << 27) +#   define R500_ALU_RGBA_A_SWIZ_B			(2 << 27) +#   define R500_ALU_RGBA_A_SWIZ_A			(3 << 27) +#   define R500_ALU_RGBA_A_SWIZ_0			(4 << 27) +#   define R500_ALU_RGBA_A_SWIZ_HALF			(5 << 27) +#   define R500_ALU_RGBA_A_SWIZ_1			(6 << 27) +/* #define R500_ALU_RGBA_A_SWIZ_UNUSED			(7 << 27) */ +#   define R500_ALU_RGBA_ALPHA_MOD_C_NOP		(0 << 30) +#   define R500_ALU_RGBA_ALPHA_MOD_C_NEG		(1 << 30) +#   define R500_ALU_RGBA_ALPHA_MOD_C_ABS		(2 << 30) +#   define R500_ALU_RGBA_ALPHA_MOD_C_NAB		(3 << 30) +#define R500_US_ALU_RGB_INST_0				0xa000 +#   define R500_ALU_RGB_SEL_A_SRC0			(0 << 0) +#   define R500_ALU_RGB_SEL_A_SRC1			(1 << 0) +#   define R500_ALU_RGB_SEL_A_SRC2			(2 << 0) +#   define R500_ALU_RGB_SEL_A_SRCP			(3 << 0) +#   define R500_ALU_RGB_R_SWIZ_A_R			(0 << 2) +#   define R500_ALU_RGB_R_SWIZ_A_G			(1 << 2) +#   define R500_ALU_RGB_R_SWIZ_A_B			(2 << 2) +#   define R500_ALU_RGB_R_SWIZ_A_A			(3 << 2) +#   define R500_ALU_RGB_R_SWIZ_A_0			(4 << 2) +#   define R500_ALU_RGB_R_SWIZ_A_HALF			(5 << 2) +#   define R500_ALU_RGB_R_SWIZ_A_1			(6 << 2) +/* #define R500_ALU_RGB_R_SWIZ_A_UNUSED			(7 << 2) */ +#   define R500_ALU_RGB_G_SWIZ_A_R			(0 << 5) +#   define R500_ALU_RGB_G_SWIZ_A_G			(1 << 5) +#   define R500_ALU_RGB_G_SWIZ_A_B			(2 << 5) +#   define R500_ALU_RGB_G_SWIZ_A_A			(3 << 5) +#   define R500_ALU_RGB_G_SWIZ_A_0			(4 << 5) +#   define R500_ALU_RGB_G_SWIZ_A_HALF			(5 << 5) +#   define R500_ALU_RGB_G_SWIZ_A_1			(6 << 5) +/* #define R500_ALU_RGB_G_SWIZ_A_UNUSED			(7 << 5) */ +#   define R500_ALU_RGB_B_SWIZ_A_R			(0 << 8) +#   define R500_ALU_RGB_B_SWIZ_A_G			(1 << 8) +#   define R500_ALU_RGB_B_SWIZ_A_B			(2 << 8) +#   define R500_ALU_RGB_B_SWIZ_A_A			(3 << 8) +#   define R500_ALU_RGB_B_SWIZ_A_0			(4 << 8) +#   define R500_ALU_RGB_B_SWIZ_A_HALF			(5 << 8) +#   define R500_ALU_RGB_B_SWIZ_A_1			(6 << 8) +/* #define R500_ALU_RGB_B_SWIZ_A_UNUSED			(7 << 8) */ +#   define R500_ALU_RGB_MOD_A_NOP			(0 << 11) +#   define R500_ALU_RGB_MOD_A_NEG			(1 << 11) +#   define R500_ALU_RGB_MOD_A_ABS			(2 << 11) +#   define R500_ALU_RGB_MOD_A_NAB			(3 << 11) +#   define R500_ALU_RGB_SEL_B_SRC0			(0 << 13) +#   define R500_ALU_RGB_SEL_B_SRC1			(1 << 13) +#   define R500_ALU_RGB_SEL_B_SRC2			(2 << 13) +#   define R500_ALU_RGB_SEL_B_SRCP			(3 << 13) +#   define R500_ALU_RGB_R_SWIZ_B_R			(0 << 15) +#   define R500_ALU_RGB_R_SWIZ_B_G			(1 << 15) +#   define R500_ALU_RGB_R_SWIZ_B_B			(2 << 15) +#   define R500_ALU_RGB_R_SWIZ_B_A			(3 << 15) +#   define R500_ALU_RGB_R_SWIZ_B_0			(4 << 15) +#   define R500_ALU_RGB_R_SWIZ_B_HALF			(5 << 15) +#   define R500_ALU_RGB_R_SWIZ_B_1			(6 << 15) +/* #define R500_ALU_RGB_R_SWIZ_B_UNUSED			(7 << 15) */ +#   define R500_ALU_RGB_G_SWIZ_B_R			(0 << 18) +#   define R500_ALU_RGB_G_SWIZ_B_G			(1 << 18) +#   define R500_ALU_RGB_G_SWIZ_B_B			(2 << 18) +#   define R500_ALU_RGB_G_SWIZ_B_A			(3 << 18) +#   define R500_ALU_RGB_G_SWIZ_B_0			(4 << 18) +#   define R500_ALU_RGB_G_SWIZ_B_HALF			(5 << 18) +#   define R500_ALU_RGB_G_SWIZ_B_1			(6 << 18) +/* #define R500_ALU_RGB_G_SWIZ_B_UNUSED			(7 << 18) */ +#   define R500_ALU_RGB_B_SWIZ_B_R			(0 << 21) +#   define R500_ALU_RGB_B_SWIZ_B_G			(1 << 21) +#   define R500_ALU_RGB_B_SWIZ_B_B			(2 << 21) +#   define R500_ALU_RGB_B_SWIZ_B_A			(3 << 21) +#   define R500_ALU_RGB_B_SWIZ_B_0			(4 << 21) +#   define R500_ALU_RGB_B_SWIZ_B_HALF			(5 << 21) +#   define R500_ALU_RGB_B_SWIZ_B_1			(6 << 21) +/* #define R500_ALU_RGB_B_SWIZ_B_UNUSED			(7 << 21) */ +#   define R500_ALU_RGB_MOD_B_NOP			(0 << 24) +#   define R500_ALU_RGB_MOD_B_NEG			(1 << 24) +#   define R500_ALU_RGB_MOD_B_ABS			(2 << 24) +#   define R500_ALU_RGB_MOD_B_NAB			(3 << 24) +#   define R500_ALU_RGB_OMOD_IDENTITY			(0 << 26) +#   define R500_ALU_RGB_OMOD_MUL_2			(1 << 26) +#   define R500_ALU_RGB_OMOD_MUL_4			(2 << 26) +#   define R500_ALU_RGB_OMOD_MUL_8			(3 << 26) +#   define R500_ALU_RGB_OMOD_DIV_2			(4 << 26) +#   define R500_ALU_RGB_OMOD_DIV_4			(5 << 26) +#   define R500_ALU_RGB_OMOD_DIV_8			(6 << 26) +#   define R500_ALU_RGB_OMOD_DISABLE			(7 << 26) +#   define R500_ALU_RGB_TARGET(x)			(x << 29) +#   define R500_ALU_RGB_WMASK				(1 << 31) +#define R500_US_ALU_RGB_ADDR_0				0x9000 +#   define R500_RGB_ADDR0(x)				(x << 0) +#   define R500_RGB_ADDR0_CONST				(1 << 8) +#   define R500_RGB_ADDR0_REL				(1 << 9) +#   define R500_RGB_ADDR1(x)				(x << 10) +#   define R500_RGB_ADDR1_CONST				(1 << 18) +#   define R500_RGB_ADDR1_REL				(1 << 19) +#   define R500_RGB_ADDR2(x)				(x << 20) +#   define R500_RGB_ADDR2_CONST				(1 << 28) +#   define R500_RGB_ADDR2_REL				(1 << 29) +#   define R500_RGB_SRCP_OP_1_MINUS_2RGB0		(0 << 30) +#   define R500_RGB_SRCP_OP_RGB1_MINUS_RGB0		(1 << 30) +#   define R500_RGB_SRCP_OP_RGB1_PLUS_RGB0		(2 << 30) +#   define R500_RGB_SRCP_OP_1_MINUS_RGB0		(3 << 30) +#define R500_US_CMN_INST_0				0xb800 +#   define R500_INST_TYPE_ALU				(0 << 0) +#   define R500_INST_TYPE_OUT				(1 << 0) +#   define R500_INST_TYPE_FC				(2 << 0) +#   define R500_INST_TYPE_TEX				(3 << 0) +#   define R500_INST_TEX_SEM_WAIT			(1 << 2) +#   define R500_INST_RGB_PRED_SEL_NONE			(0 << 3) +#   define R500_INST_RGB_PRED_SEL_RGBA			(1 << 3) +#   define R500_INST_RGB_PRED_SEL_RRRR			(2 << 3) +#   define R500_INST_RGB_PRED_SEL_GGGG			(3 << 3) +#   define R500_INST_RGB_PRED_SEL_BBBB			(4 << 3) +#   define R500_INST_RGB_PRED_SEL_AAAA			(5 << 3) +#   define R500_INST_RGB_PRED_INV			(1 << 6) +#   define R500_INST_WRITE_INACTIVE			(1 << 7) +#   define R500_INST_LAST				(1 << 8) +#   define R500_INST_NOP				(1 << 9) +#   define R500_INST_ALU_WAIT				(1 << 10) +#   define R500_INST_RGB_WMASK_R			(1 << 11) +#   define R500_INST_RGB_WMASK_G			(1 << 12) +#   define R500_INST_RGB_WMASK_B			(1 << 13) +#   define R500_INST_ALPHA_WMASK			(1 << 14) +#   define R500_INST_RGB_OMASK_R			(1 << 15) +#   define R500_INST_RGB_OMASK_G			(1 << 16) +#   define R500_INST_RGB_OMASK_B			(1 << 17) +#   define R500_INST_ALPHA_OMASK			(1 << 18) +#   define R500_INST_RGB_CLAMP				(1 << 19) +#   define R500_INST_ALPHA_CLAMP			(1 << 20) +#   define R500_INST_ALU_RESULT_SEL			(1 << 21) +#   define R500_INST_ALPHA_PRED_INV			(1 << 22) +#   define R500_INST_ALU_RESULT_OP_EQ			(0 << 23) +#   define R500_INST_ALU_RESULT_OP_LT			(1 << 23) +#   define R500_INST_ALU_RESULT_OP_GE			(2 << 23) +#   define R500_INST_ALU_RESULT_OP_NE			(3 << 23) +#   define R500_INST_ALPHA_PRED_SEL_NONE		(0 << 25) +#   define R500_INST_ALPHA_PRED_SEL_RGBA		(1 << 25) +#   define R500_INST_ALPHA_PRED_SEL_RRRR		(2 << 25) +#   define R500_INST_ALPHA_PRED_SEL_GGGG		(3 << 25) +#   define R500_INST_ALPHA_PRED_SEL_BBBB		(4 << 25) +#   define R500_INST_ALPHA_PRED_SEL_AAAA		(5 << 25) +/* XXX next four are kind of guessed */ +#   define R500_INST_STAT_WE_R				(1 << 28) +#   define R500_INST_STAT_WE_G				(1 << 29) +#   define R500_INST_STAT_WE_B				(1 << 30) +#   define R500_INST_STAT_WE_A				(1 << 31) +/* note that these are 8 bit lengths, despite the offsets, at least for R500 */ +#define R500_US_CODE_ADDR				0x4630 +#   define R500_US_CODE_START_ADDR(x)			(x << 0) +#   define R500_US_CODE_END_ADDR(x)			(x << 16) +#define R500_US_CODE_OFFSET				0x4638 +#   define R500_US_CODE_OFFSET_ADDR(x)			(x << 0) +#define R500_US_CODE_RANGE				0x4634 +#   define R500_US_CODE_RANGE_ADDR(x)			(x << 0) +#   define R500_US_CODE_RANGE_SIZE(x)			(x << 16) +#define R500_US_CONFIG					0x4600 +#   define R500_ZERO_TIMES_ANYTHING_EQUALS_ZERO		(1 << 1) +#define R500_US_FC_ADDR_0				0xa000 +#   define R500_FC_BOOL_ADDR(x)				(x << 0) +#   define R500_FC_INT_ADDR(x)				(x << 8) +#   define R500_FC_JUMP_ADDR(x)				(x << 16) +#   define R500_FC_JUMP_GLOBAL				(1 << 31) +#define R500_US_FC_BOOL_CONST				0x4620 +#   define R500_FC_KBOOL(x)				(x) +#define R500_US_FC_CTRL					0x4624 +#   define R500_FC_TEST_EN				(1 << 30) +#   define R500_FC_FULL_FC_EN				(1 << 31) +#define R500_US_FC_INST_0				0x9800 +#   define R500_FC_OP_JUMP				(0 << 0) +#   define R500_FC_OP_LOOP				(1 << 0) +#   define R500_FC_OP_ENDLOOP				(2 << 0) +#   define R500_FC_OP_REP				(3 << 0) +#   define R500_FC_OP_ENDREP				(4 << 0) +#   define R500_FC_OP_BREAKLOOP				(5 << 0) +#   define R500_FC_OP_BREAKREP				(6 << 0) +#   define R500_FC_OP_CONTINUE				(7 << 0) +#   define R500_FC_B_ELSE				(1 << 4) +#   define R500_FC_JUMP_ANY				(1 << 5) +#   define R500_FC_A_OP_NONE				(0 << 6) +#   define R500_FC_A_OP_POP				(1 << 6) +#   define R500_FC_A_OP_PUSH				(2 << 6) +#   define R500_FC_JUMP_FUNC(x)				(x << 8) +#   define R500_FC_B_POP_CNT(x)				(x << 16) +#   define R500_FC_B_OP0_NONE				(0 << 24) +#   define R500_FC_B_OP0_DECR				(1 << 24) +#   define R500_FC_B_OP0_INCR				(2 << 24) +#   define R500_FC_B_OP1_DECR				(0 << 26) +#   define R500_FC_B_OP1_NONE				(1 << 26) +#   define R500_FC_B_OP1_INCR				(2 << 26) +#   define R500_FC_IGNORE_UNCOVERED			(1 << 28) +#define R500_US_FC_INT_CONST_0				0x4c00 +#   define R500_FC_INT_CONST_KR(x)			(x << 0) +#   define R500_FC_INT_CONST_KG(x)			(x << 8) +#   define R500_FC_INT_CONST_KB(x)			(x << 16) +/* _0 through _15 */ +#define R500_US_FORMAT0_0				0x4640 +#   define R500_FORMAT_TXWIDTH(x)			(x << 0) +#   define R500_FORMAT_TXHEIGHT(x)			(x << 11) +#   define R500_FORMAT_TXDEPTH(x)			(x << 22) +/* _0 through _3 */ +#define R500_US_OUT_FMT_0				0x46a4 +#   define R500_OUT_FMT_C4_8				(0 << 0) +#   define R500_OUT_FMT_C4_10				(1 << 0) +#   define R500_OUT_FMT_C4_10_GAMMA			(2 << 0) +#   define R500_OUT_FMT_C_16				(3 << 0) +#   define R500_OUT_FMT_C2_16				(4 << 0) +#   define R500_OUT_FMT_C4_16				(5 << 0) +#   define R500_OUT_FMT_C_16_MPEG			(6 << 0) +#   define R500_OUT_FMT_C2_16_MPEG			(7 << 0) +#   define R500_OUT_FMT_C2_4				(8 << 0) +#   define R500_OUT_FMT_C_3_3_2				(9 << 0) +#   define R500_OUT_FMT_C_6_5_6				(10 << 0) +#   define R500_OUT_FMT_C_11_11_10			(11 << 0) +#   define R500_OUT_FMT_C_10_11_11			(12 << 0) +#   define R500_OUT_FMT_C_2_10_10_10			(13 << 0) +/* #define R500_OUT_FMT_RESERVED			(14 << 0) */ +#   define R500_OUT_FMT_UNUSED				(15 << 0) +#   define R500_OUT_FMT_C_16_FP				(16 << 0) +#   define R500_OUT_FMT_C2_16_FP			(17 << 0) +#   define R500_OUT_FMT_C4_16_FP			(18 << 0) +#   define R500_OUT_FMT_C_32_FP				(19 << 0) +#   define R500_OUT_FMT_C2_32_FP			(20 << 0) +#   define R500_OUT_FMT_C4_32_FP			(21 << 0) +#   define R500_C0_SEL_A				(0 << 8) +#   define R500_C0_SEL_R				(1 << 8) +#   define R500_C0_SEL_G				(2 << 8) +#   define R500_C0_SEL_B				(3 << 8) +#   define R500_C1_SEL_A				(0 << 10) +#   define R500_C1_SEL_R				(1 << 10) +#   define R500_C1_SEL_G				(2 << 10) +#   define R500_C1_SEL_B				(3 << 10) +#   define R500_C2_SEL_A				(0 << 12) +#   define R500_C2_SEL_R				(1 << 12) +#   define R500_C2_SEL_G				(2 << 12) +#   define R500_C2_SEL_B				(3 << 12) +#   define R500_C3_SEL_A				(0 << 14) +#   define R500_C3_SEL_R				(1 << 14) +#   define R500_C3_SEL_G				(2 << 14) +#   define R500_C3_SEL_B				(3 << 14) +#   define R500_OUT_SIGN(x)				(x << 16) +#   define R500_ROUND_ADJ				(1 << 20) +#define R500_US_PIXSIZE					0x4604 +#   define R500_PIX_SIZE(x)				(x) +#define R500_US_TEX_ADDR_0				0x9800 +#   define R500_TEX_SRC_ADDR(x)				(x << 0) +#   define R500_TEX_SRC_ADDR_REL			(1 << 7) +#   define R500_TEX_SRC_S_SWIZ_R			(0 << 8) +#   define R500_TEX_SRC_S_SWIZ_G			(1 << 8) +#   define R500_TEX_SRC_S_SWIZ_B			(2 << 8) +#   define R500_TEX_SRC_S_SWIZ_A			(3 << 8) +#   define R500_TEX_SRC_T_SWIZ_R			(0 << 10) +#   define R500_TEX_SRC_T_SWIZ_G			(1 << 10) +#   define R500_TEX_SRC_T_SWIZ_B			(2 << 10) +#   define R500_TEX_SRC_T_SWIZ_A			(3 << 10) +#   define R500_TEX_SRC_R_SWIZ_R			(0 << 12) +#   define R500_TEX_SRC_R_SWIZ_G			(1 << 12) +#   define R500_TEX_SRC_R_SWIZ_B			(2 << 12) +#   define R500_TEX_SRC_R_SWIZ_A			(3 << 12) +#   define R500_TEX_SRC_Q_SWIZ_R			(0 << 14) +#   define R500_TEX_SRC_Q_SWIZ_G			(1 << 14) +#   define R500_TEX_SRC_Q_SWIZ_B			(2 << 14) +#   define R500_TEX_SRC_Q_SWIZ_A			(3 << 14) +#   define R500_TEX_DST_ADDR(x)				(x << 16) +#   define R500_TEX_DST_ADDR_REL			(1 << 23) +#   define R500_TEX_DST_R_SWIZ_R			(0 << 24) +#   define R500_TEX_DST_R_SWIZ_G			(1 << 24) +#   define R500_TEX_DST_R_SWIZ_B			(2 << 24) +#   define R500_TEX_DST_R_SWIZ_A			(3 << 24) +#   define R500_TEX_DST_G_SWIZ_R			(0 << 26) +#   define R500_TEX_DST_G_SWIZ_G			(1 << 26) +#   define R500_TEX_DST_G_SWIZ_B			(2 << 26) +#   define R500_TEX_DST_G_SWIZ_A			(3 << 26) +#   define R500_TEX_DST_B_SWIZ_R			(0 << 28) +#   define R500_TEX_DST_B_SWIZ_G			(1 << 28) +#   define R500_TEX_DST_B_SWIZ_B			(2 << 28) +#   define R500_TEX_DST_B_SWIZ_A			(3 << 28) +#   define R500_TEX_DST_A_SWIZ_R			(0 << 30) +#   define R500_TEX_DST_A_SWIZ_G			(1 << 30) +#   define R500_TEX_DST_A_SWIZ_B			(2 << 30) +#   define R500_TEX_DST_A_SWIZ_A			(3 << 30) +#define R500_US_TEX_ADDR_DXDY_0				0xa000 +#   define R500_DX_ADDR(x)				(x << 0) +#   define R500_DX_ADDR_REL				(1 << 7) +#   define R500_DX_S_SWIZ_R				(0 << 8) +#   define R500_DX_S_SWIZ_G				(1 << 8) +#   define R500_DX_S_SWIZ_B				(2 << 8) +#   define R500_DX_S_SWIZ_A				(3 << 8) +#   define R500_DX_T_SWIZ_R				(0 << 10) +#   define R500_DX_T_SWIZ_G				(1 << 10) +#   define R500_DX_T_SWIZ_B				(2 << 10) +#   define R500_DX_T_SWIZ_A				(3 << 10) +#   define R500_DX_R_SWIZ_R				(0 << 12) +#   define R500_DX_R_SWIZ_G				(1 << 12) +#   define R500_DX_R_SWIZ_B				(2 << 12) +#   define R500_DX_R_SWIZ_A				(3 << 12) +#   define R500_DX_Q_SWIZ_R				(0 << 14) +#   define R500_DX_Q_SWIZ_G				(1 << 14) +#   define R500_DX_Q_SWIZ_B				(2 << 14) +#   define R500_DX_Q_SWIZ_A				(3 << 14) +#   define R500_DY_ADDR(x)				(x << 16) +#   define R500_DY_ADDR_REL				(1 << 17) +#   define R500_DY_S_SWIZ_R				(0 << 24) +#   define R500_DY_S_SWIZ_G				(1 << 24) +#   define R500_DY_S_SWIZ_B				(2 << 24) +#   define R500_DY_S_SWIZ_A				(3 << 24) +#   define R500_DY_T_SWIZ_R				(0 << 26) +#   define R500_DY_T_SWIZ_G				(1 << 26) +#   define R500_DY_T_SWIZ_B				(2 << 26) +#   define R500_DY_T_SWIZ_A				(3 << 26) +#   define R500_DY_R_SWIZ_R				(0 << 28) +#   define R500_DY_R_SWIZ_G				(1 << 28) +#   define R500_DY_R_SWIZ_B				(2 << 28) +#   define R500_DY_R_SWIZ_A				(3 << 28) +#   define R500_DY_Q_SWIZ_R				(0 << 30) +#   define R500_DY_Q_SWIZ_G				(1 << 30) +#   define R500_DY_Q_SWIZ_B				(2 << 30) +#   define R500_DY_Q_SWIZ_A				(3 << 30) +#define R500_US_TEX_INST_0				0x9000 +#   define R500_TEX_ID(x)				(x << 16) +#   define R500_TEX_INST_NOP				(0 << 22) +#   define R500_TEX_INST_LD				(1 << 22) +#   define R500_TEX_INST_TEXKILL			(2 << 22) +#   define R500_TEX_INST_PROJ				(3 << 22) +#   define R500_TEX_INST_LODBIAS			(4 << 22) +#   define R500_TEX_INST_LOD				(5 << 22) +#   define R500_TEX_INST_DXDY				(6 << 22) +#   define R500_TEX_SEM_ACQUIRE				(1 << 25) +#   define R500_TEX_IGNORE_UNCOVERED			(1 << 26) +#   define R500_TEX_UNSCALED				(1 << 27) +#define R500_US_W_FMT					0x46b4 +#   define R500_W_FMT_W0				(0 << 0) +#   define R500_W_FMT_W24				(1 << 0) +#   define R500_W_FMT_W24FP				(2 << 0) +#   define R500_W_SRC_US				(0 << 2) +#   define R500_W_SRC_RAS				(1 << 2) + +#define R500_GA_US_VECTOR_INDEX 0x4250 +#define R500_GA_US_VECTOR_DATA 0x4254 + +#define R500_RS_INST_0					0x4320 +#define R500_RS_INST_1					0x4324 +#   define R500_RS_INST_TEX_ID_SHIFT			0 +#   define R500_RS_INST_TEX_CN_WRITE			(1 << 4) +#   define R500_RS_INST_TEX_ADDR_SHIFT			5 +#   define R500_RS_INST_COL_ID_SHIFT			12 +#   define R500_RS_INST_COL_CN_NO_WRITE			(0 << 16) +#   define R500_RS_INST_COL_CN_WRITE			(1 << 16) +#   define R500_RS_INST_COL_CN_WRITE_FBUFFER		(2 << 16) +#   define R500_RS_INST_COL_CN_WRITE_BACKFACE		(3 << 16) +#   define R500_RS_INST_COL_COL_ADDR_SHIFT		18 +#   define R500_RS_INST_TEX_ADJ				(1 << 25) +#   define R500_RS_INST_W_CN				(1 << 26) + +#define R500_US_FC_CTRL					0x4624 +#define R500_US_CODE_ADDR				0x4630 +#define R500_US_CODE_RANGE 				0x4634 +#define R500_US_CODE_OFFSET 				0x4638 + +#define R500_RS_IP_0					0x4074 +#define R500_RS_IP_1					0x4078 +#   define R500_RS_IP_PTR_K0				62 +#   define R500_RS_IP_PTR_K1 				63 +#   define R500_RS_IP_TEX_PTR_S_SHIFT 			0 +#   define R500_RS_IP_TEX_PTR_T_SHIFT 			6 +#   define R500_RS_IP_TEX_PTR_R_SHIFT 			12 +#   define R500_RS_IP_TEX_PTR_Q_SHIFT 			18 +#   define R500_RS_IP_COL_PTR_SHIFT 			24 +#   define R500_RS_IP_COL_FMT_SHIFT 			27 +#   define R500_RS_IP_COL_FMT_RGBA			(0 << 27) +#   define R500_RS_IP_OFFSET_EN 			(1 << 31) + +#define R500_DYN_SCLK_PWMEM_PIPE                        0x000d /* PLL */ + +#endif diff --git a/linux-core/via_fence.c b/linux-core/via_fence.c index 3a680a32..6473e701 100644 --- a/linux-core/via_fence.c +++ b/linux-core/via_fence.c @@ -63,7 +63,7 @@ static void via_fence_poll(struct drm_device *dev, uint32_t class,  		if (!dev_priv->have_idlelock) { -			drm_idlelock_take(&dev->lock); +			drm_idlelock_take(&dev->primary->master->lock);  			dev_priv->have_idlelock = 1;  		} @@ -89,7 +89,7 @@ static void via_fence_poll(struct drm_device *dev, uint32_t class,  		if (signaled_flush_types) {  			waiting_types &= ~signaled_flush_types;  			if (!waiting_types && dev_priv->have_idlelock) { -				drm_idlelock_release(&dev->lock); +				drm_idlelock_release(&dev->primary->master->lock);  				dev_priv->have_idlelock = 0;  			}  			drm_fence_handler(dev, 0, dev_priv->emit_0_sequence,  | 
