/*
 * Copyright 2000-2001 VA Linux Systems, Inc.
 * 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, 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
 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Authors:
 *    Keith Whitwell <keith@tungstengraphics.com>
 */

#ifndef MGALIB_INC
#define MGALIB_INC

#include <stdint.h>
#include "drm.h"
#include "mga_drm.h"
#include "dri_util.h"
#include "xf86drm.h"
#include "main/mtypes.h"
#include "main/mm.h"
#include "main/colormac.h"
#include "main/macros.h"
#include "texmem.h"
#include "xmlconfig.h"

#define MGA_SET_FIELD(reg,mask,val)  reg = ((reg) & (mask)) | ((val) & ~(mask))
#define MGA_FIELD(field,val) (((val) << (field ## _SHIFT)) & ~(field ## _MASK))
#define MGA_GET_FIELD(field, val) ((val & ~(field ## _MASK)) >> (field ## _SHIFT))

#define MGA_IS_G200(mmesa) (mmesa->mgaScreen->chipset == MGA_CARD_TYPE_G200)
#define MGA_IS_G400(mmesa) (mmesa->mgaScreen->chipset == MGA_CARD_TYPE_G400)


/* SoftwareFallback
 *    - texture env GL_BLEND -- can be fixed
 *    - 1D and 3D textures
 *    - incomplete textures
 *    - GL_DEPTH_FUNC == GL_NEVER not in h/w
 */
#define MGA_FALLBACK_TEXTURE        0x1
#define MGA_FALLBACK_DRAW_BUFFER    0x2
#define MGA_FALLBACK_READ_BUFFER    0x4
#define MGA_FALLBACK_BLEND          0x8
#define MGA_FALLBACK_RENDERMODE     0x10
#define MGA_FALLBACK_STENCIL        0x20
#define MGA_FALLBACK_DEPTH          0x40
#define MGA_FALLBACK_BORDER_MODE    0x80
#define MGA_FALLBACK_DISABLE        0x100


/* Use the templated vertex formats:
 */
#define TAG(x) mga##x
#include "tnl_dd/t_dd_vertex.h"
#undef TAG

typedef struct mga_context_t mgaContext;
typedef struct mga_context_t *mgaContextPtr;

typedef void (*mga_tri_func)( mgaContextPtr, mgaVertex *, mgaVertex *,
			       mgaVertex * );
typedef void (*mga_line_func)( mgaContextPtr, mgaVertex *, mgaVertex * );
typedef void (*mga_point_func)( mgaContextPtr, mgaVertex * );



/* Texture environment color
 */
#define RGB_ZERO(c)   (((c) & 0xffffff) == 0x000000)
#define RGB_ONE(c)    (((c) & 0xffffff) == 0xffffff)
#define ALPHA_ZERO(c) (((c) >> 24) == 0x00)
#define ALPHA_ONE(c)  (((c) >> 24) == 0xff)
#define RGBA_EQUAL(c) ((c) == PACK_COLOR_8888( (c) & 0xff, (c) & 0xff, \
                                               (c) & 0xff, (c) & 0xff ))

struct mga_texture_object_s;
struct mga_screen_private_s;

#define G200_TEX_MAXLEVELS 5
#define G400_TEX_MAXLEVELS 11

typedef struct mga_texture_object_s
{
   driTextureObject   base;

   /* The G200 only has the ability to use 5 mipmap levels (including the
    * base level).  The G400 does not have this restriction, but it still
    * only has 5 offset pointers in the hardware.  The trick on the G400 is
    * upto the first 4 offset pointers point to mipmap levels.  The last
    * offset pointer tells how large the preceeding mipmap is.  This value is
    * then used to determine where the remaining mipmaps are.
    * 
    * For example, if the first offsets[0] through offsets[2] are used as
    * pointers, then offset[3] will be the size of the mipmap pointed to by
    * offsets[2].  So mipmap level 3 will be at (offsets[2]+offsets[3]).  For
    * each successive mipmap level, offsets[3] is divided by 4 and added to
    * the previous address.  So mipmap level 4 will be at 
    * (offsets[2]+offsets[3]+(offsets[3] / 4)).
    * 
    * The last pointer is selected by setting TO_texorgoffsetsel in its
    * pointer.  In the previous example, offset[2] would have
    * TO_texorgoffsetsel or'ed in before writing it to the hardware.
    * 
    * In the current driver all of the mipmaps are packed together linearly
    * with mipmap level 0.  Therefore offsets[0] points to the base of the
    * texture (and has TO_texorgoffsetsel or'ed in), and offsets[1] is the
    * size of the base texture.
    *
    * There is a possible optimization available here.  At times the driver
    * may not be able to allocate a single block of memory for the complete
    * texture without ejecting some other textures from memory.  It may be
    * possible to put some of the lower mipmap levels (i.e., the larger
    * mipmaps) in memory separate from the higher levels.
    *
    * The implementation should be fairly obvious, but getting "right" would
    * likely be non-trivial.  A first allocation for the entire texture would
    * be attempted with a flag that says "don't eject other textures."  If
    * that failed, an additional allocation would be attmpted for just the
    * base map.  The process would repeat with the block of lower maps.  The
    * tricky parts would be in detecting when some of the levels had been
    * ejected from texture memory by other textures and preventing the
    * 4th allocation (for all the smallest mipmap levels) from kicking out
    * any of the first three.
    * 
    * This array holds G400_TEX_MAXLEVELS pointers to remove an if-statement
    * in a loop in mgaSetTexImages.  Values past G200_TEX_MAXLEVELS are not
    * used.
    */
   GLuint             offsets[G400_TEX_MAXLEVELS];

   int                texelBytes;
   GLuint             age;

   drm_mga_texture_regs_t setup;

   /* If one texture dimension wraps with GL_CLAMP and the other with
    * GL_CLAMP_TO_EDGE, we have to fallback to software.  We would also have
    * to fallback for GL_CLAMP_TO_BORDER.
    */
   GLboolean          border_fallback;
   /* Depending on multitxturing and environment color
    * GL_BLEND may have to be a software fallback.
    */
   GLboolean texenv_fallback;
} mgaTextureObject_t;

struct mga_hw_state {
   GLuint   specen;
   GLuint   cull;
   GLuint   cull_dualtex;
   GLuint   stencil;
   GLuint   stencilctl;
   GLuint   stencil_enable;
   GLuint   zmode;
   GLuint   rop;
   GLuint   alpha_func;
   GLuint   alpha_func_enable;
   GLuint   blend_func;
   GLuint   blend_func_enable;
   GLuint   alpha_sel;
};

struct mga_context_t {

   GLcontext *glCtx;
   unsigned int lastStamp;		/* fullscreen breaks dpriv->laststamp,
					 * need to shadow it here. */

   /* Hardware state management
    */
   struct mga_hw_state hw;

   /* Bookkeeping for texturing
    */
   unsigned           nr_heaps;
   driTexHeap       * texture_heaps[ MGA_NR_TEX_HEAPS ];
   driTextureObject   swapped;

   struct mga_texture_object_s *CurrentTexObj[2];


   /* Map GL texture units onto hardware.
    */
   GLuint tmu_source[2];
   
   int texture_depth;

   /* Manage fallbacks
    */
   GLuint Fallback;  

   /* Texture environment color.
    */
   unsigned int envcolor[2];
   GLboolean fcol_used;
   GLboolean force_dualtex;

   /* Rasterization state 
    */
   GLuint SetupNewInputs;
   GLuint SetupIndex;
   GLuint RenderIndex;
   
   GLuint hw_primitive;
   GLenum raster_primitive;
   GLenum render_primitive;

   GLubyte *verts;
   GLint vertex_stride_shift;
   GLuint vertex_format;		
   GLuint vertex_size;

   /* Fallback rasterization functions 
    */
   mga_point_func draw_point;
   mga_line_func draw_line;
   mga_tri_func draw_tri;


   /* Manage driver and hardware state
    */
   GLuint        NewGLState; 
   GLuint        dirty;

   drm_mga_context_regs_t setup;

   GLuint        ClearColor;
   GLuint        ClearDepth;
   GLuint        poly_stipple;
   GLfloat       depth_scale;

   GLuint        depth_clear_mask;
   GLuint        stencil_clear_mask;
   GLuint        hw_stencil;
   GLuint        haveHwStipple;
   GLfloat       hw_viewport[16];

   /* Dma buffers
    */
   drmBufPtr  vertex_dma_buffer;
   drmBufPtr  iload_buffer;

   int64_t swap_ust;
   int64_t swap_missed_ust;

   GLuint swap_count;
   GLuint swap_missed_count;

   uint32_t last_frame_fence;

   /* Drawable, cliprect and scissor information
    */
   int dirty_cliprects;		/* which sets of cliprects are uptodate? */
   int draw_buffer;		/* which buffer are we rendering to */
   unsigned int drawOffset;		/* draw buffer address in  space */
   int readOffset;
   int drawX, drawY;		/* origin of drawable in draw buffer */
   int lastX, lastY;		/* detect DSTORG bug */
   GLuint numClipRects;		/* cliprects for the draw buffer */
   drm_clip_rect_t *pClipRects;
   drm_clip_rect_t draw_rect;
   drm_clip_rect_t scissor_rect;
   int scissor;

   drm_clip_rect_t tmp_boxes[2][MGA_NR_SAREA_CLIPRECTS];


   /* Texture aging and DMA based aging.
    */
   unsigned int texAge[MGA_NR_TEX_HEAPS];/* texture LRU age  */
   unsigned int dirtyAge;		/* buffer age for synchronization */

   GLuint primary_offset;

   /* Mirrors of some DRI state.
    */
   drm_context_t hHWContext;
   drm_hw_lock_t *driHwLock;
   int driFd;
   __DRIdrawablePrivate *driDrawable;
   __DRIdrawablePrivate *driReadable;

   __DRIscreenPrivate *driScreen;
   struct mga_screen_private_s *mgaScreen;
   drm_mga_sarea_t *sarea;

   /* Configuration cache
    */
   driOptionCache optionCache;
};

#define MGA_CONTEXT(ctx) ((mgaContextPtr)(ctx->DriverCtx))




/* ================================================================
 * Debugging:
 */
#define DO_DEBUG		1

#if DO_DEBUG
extern int MGA_DEBUG;
#else
#define MGA_DEBUG		0
#endif

#define DEBUG_VERBOSE_MSG	0x01
#define DEBUG_VERBOSE_DRI	0x02
#define DEBUG_VERBOSE_IOCTL	0x04
#define DEBUG_VERBOSE_TEXTURE   0x08
#define DEBUG_VERBOSE_FALLBACK	0x10

static INLINE GLuint mgaPackColor(GLuint cpp,
                                  GLubyte r, GLubyte g,
                                  GLubyte b, GLubyte a)
{
   switch (cpp) {
   case 2:
      return PACK_COLOR_565( r, g, b );
   case 4:
      return PACK_COLOR_8888( a, r, g, b );
   default:
      return 0;
   }
}


/*
 * Subpixel offsets for window coordinates:
 */
#define SUBPIXEL_X (-0.5F)
#define SUBPIXEL_Y (-0.5F + 0.125)


#define MGA_WA_TRIANGLES     0x18000000
#define MGA_WA_TRISTRIP_T0   0x02010200
#define MGA_WA_TRIFAN_T0     0x01000408
#define MGA_WA_TRISTRIP_T0T1 0x02010400
#define MGA_WA_TRIFAN_T0T1   0x01000810

#endif
