/*
   Intel i830 DirectFB graphics driver

   (c) Copyright 2005       Servision Ltd.
                            http://www.servision.net/

   All rights reserved.

   Based on i810 driver written by Antonino Daplas <adaplas@pol.net>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the
   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

#ifndef __I830_H__
#define __I830_H__

#include <dfb_types.h>

#include <sys/types.h>
#include <linux/agpgart.h>

#include <core/gfxcard.h>
#include <core/layers.h>


#define RINGBUFFER_SIZE             (128 * 1024)



/* Ring buffer registers, p277, overview p19
 */
#define LP_RING     0x2030
#define HP_RING     0x2040

#define RING_TAIL      0x00
#define TAIL_ADDR           0x000FFFF8
#define I830_TAIL_MASK	    0x001FFFF8

#define RING_HEAD      0x04
#define HEAD_WRAP_COUNT     0xFFE00000
#define HEAD_WRAP_ONE       0x00200000
#define HEAD_ADDR           0x001FFFFC
#define I830_HEAD_MASK      0x001FFFFC

#define RING_START     0x08
#define START_ADDR          0x00FFFFF8
#define I830_RING_START_MASK	0xFFFFF000

#define RING_LEN       0x0C
#define RING_NR_PAGES       0x000FF000
#define I830_RING_NR_PAGES	0x001FF000
#define RING_REPORT_MASK    0x00000006
#define RING_REPORT_64K     0x00000002
#define RING_REPORT_128K    0x00000004
#define RING_NO_REPORT      0x00000000
#define RING_VALID_MASK     0x00000001
#define RING_VALID          0x00000001
#define RING_INVALID        0x00000000


/* Overlay Flip */
#define MI_OVERLAY_FLIP			(0x11<<23)
#define MI_OVERLAY_FLIP_CONTINUE	(0<<21)
#define MI_OVERLAY_FLIP_ON		(1<<21)
#define MI_OVERLAY_FLIP_OFF		(2<<21)

/* Wait for Events */
#define MI_WAIT_FOR_EVENT		(0x03<<23)
#define MI_WAIT_FOR_OVERLAY_FLIP	(1<<16)

/* Flush */
#define MI_FLUSH			(0x04<<23)
#define MI_WRITE_DIRTY_STATE		(1<<4)
#define MI_END_SCENE			(1<<3)
#define MI_INHIBIT_RENDER_CACHE_FLUSH	(1<<2)
#define MI_INVALIDATE_MAP_CACHE		(1<<0)

/* Noop */
#define MI_NOOP				0x00
#define MI_NOOP_WRITE_ID		(1<<22)
#define MI_NOOP_ID_MASK			(1<<22 - 1)


/* Instruction Parser Mode Register
 *    - p281
 *    - 2 new bits.
 */
#define INST_PM                  0x20c0
#define AGP_SYNC_PACKET_FLUSH_ENABLE 0x20 /* reserved */
#define SYNC_PACKET_FLUSH_ENABLE     0x10
#define TWO_D_INST_DISABLE           0x08
#define THREE_D_INST_DISABLE         0x04
#define STATE_VAR_UPDATE_DISABLE     0x02
#define PAL_STIP_DISABLE             0x01

#define INST_DONE                0x2090
#define INST_PS                  0x20c4

#define MEMMODE                  0x20dc



#define I830RES_GART                1
#define I830RES_LRING_ACQ           2
#define I830RES_LRING_BIND          4
#define I830RES_OVL_ACQ             8
#define I830RES_OVL_BIND           16
#define I830RES_GART_ACQ           32
#define I830RES_MMAP               64
#define I830RES_STATE_SAVE        128

#ifndef AGP_NORMAL_MEMORY
#define AGP_NORMAL_MEMORY 0
#endif

#ifndef AGP_PHYSICAL_MEMORY
#define AGP_PHYSICAL_MEMORY 2
#endif




/*
 * OCMD - Overlay Command Register
 */
#define MIRROR_MODE             (0x3<<17)
#define MIRROR_HORIZONTAL       (0x1<<17)
#define MIRROR_VERTICAL         (0x2<<17)
#define MIRROR_BOTH             (0x3<<17)
#define OV_BYTE_ORDER           (0x3<<14)
#define UV_SWAP                 (0x1<<14)
#define Y_SWAP                  (0x2<<14)
#define Y_AND_UV_SWAP           (0x3<<14)
#define SOURCE_FORMAT           (0xf<<10)
#define RGB_888                 (0x1<<10)
#define RGB_555                 (0x2<<10)
#define RGB_565                 (0x3<<10)
#define YUV_422                 (0x8<<10)
#define YUV_411                 (0x9<<10)
#define YUV_420                 (0xc<<10)
#define YUV_422_PLANAR          (0xd<<10)
#define YUV_410                 (0xe<<10)
#define TVSYNC_FLIP_PARITY      (0x1<<9)
#define TVSYNC_FLIP_ENABLE      (0x1<<7)
#define BUF_TYPE                (0x1<<5)
#define BUF_TYPE_FRAME          (0x0<<5)
#define BUF_TYPE_FIELD          (0x1<<5)
#define TEST_MODE               (0x1<<4)
#define BUFFER_SELECT           (0x3<<2)
#define BUFFER0                 (0x0<<2)
#define BUFFER1                 (0x1<<2)
#define FIELD_SELECT            (0x1<<1)
#define FIELD0                  (0x0<<1)
#define FIELD1                  (0x1<<1)
#define OVERLAY_ENABLE          0x1

/* OCONFIG register */
#define CC_OUT_8BIT             (0x1<<3)
#define OVERLAY_PIPE_MASK       (0x1<<18)
#define OVERLAY_PIPE_A          (0x0<<18)
#define OVERLAY_PIPE_B          (0x1<<18)

/* DCLRKM register */
#define DEST_KEY_ENABLE         (0x1<<31)

/* Polyphase filter coefficients */
#define N_HORIZ_Y_TAPS          5
#define N_VERT_Y_TAPS           3
#define N_HORIZ_UV_TAPS         3
#define N_VERT_UV_TAPS          3
#define N_PHASES                17
#define MAX_TAPS                5

/* Filter cutoff frequency limits. */
#define MIN_CUTOFF_FREQ         1.0
#define MAX_CUTOFF_FREQ         3.0

typedef volatile struct {
     u32 OBUF_0Y;
     u32 OBUF_1Y;
     u32 OBUF_0U;
     u32 OBUF_0V;
     u32 OBUF_1U;
     u32 OBUF_1V;
     u32 OSTRIDE;
     u32 YRGB_VPH;
     u32 UV_VPH;
     u32 HORZ_PH;
     u32 INIT_PHS;
     u32 DWINPOS;
     u32 DWINSZ;
     u32 SWIDTH;
     u32 SWIDTHSW;
     u32 SHEIGHT;
     u32 YRGBSCALE;
     u32 UVSCALE;
     u32 OCLRC0;
     u32 OCLRC1;
     u32 DCLRKV;
     u32 DCLRKM;
     u32 SCLRKVH;
     u32 SCLRKVL;
     u32 SCLRKEN;
     u32 OCONFIG;
     u32 OCMD;
     u32 RESERVED1;           /* 0x6C */
     u32 AWINPOS;
     u32 AWINSZ;
     u32 RESERVED2;           /* 0x78 */
     u32 RESERVED3;           /* 0x7C */
     u32 RESERVED4;           /* 0x80 */
     u32 RESERVED5;           /* 0x84 */
     u32 RESERVED6;           /* 0x88 */
     u32 RESERVED7;           /* 0x8C */
     u32 RESERVED8;           /* 0x90 */
     u32 RESERVED9;           /* 0x94 */
     u32 RESERVEDA;           /* 0x98 */
     u32 RESERVEDB;           /* 0x9C */
     u32 FASTHSCALE;               /* 0xA0 */
     u32 UVSCALEV;            /* 0xA4 */

     u32 RESERVEDC[(0x200 - 0xA8) / 4];         /* 0xA8 - 0x1FC */
     u16 Y_VCOEFS[N_VERT_Y_TAPS * N_PHASES];         /* 0x200 */
     u16 RESERVEDD[0x100 / 2 - N_VERT_Y_TAPS * N_PHASES];
     u16 Y_HCOEFS[N_HORIZ_Y_TAPS * N_PHASES];             /* 0x300 */
     u16 RESERVEDE[0x200 / 2 - N_HORIZ_Y_TAPS * N_PHASES];
     u16 UV_VCOEFS[N_VERT_UV_TAPS * N_PHASES];            /* 0x500 */
     u16 RESERVEDF[0x100 / 2 - N_VERT_UV_TAPS * N_PHASES];
     u16 UV_HCOEFS[N_HORIZ_UV_TAPS * N_PHASES];      /* 0x600 */
     u16 RESERVEDG[0x100 / 2 - N_HORIZ_UV_TAPS * N_PHASES];
} I830OverlayRegs;

typedef struct {
     CoreLayerRegionConfig  config;
} I830OverlayLayerData;


typedef struct {
     unsigned int   tail_mask;

     int            size;
     int            head;
     int            tail;
     int            space;
} I830RingBuffer;

typedef struct {
     volatile void *virt;
     unsigned int   tail_mask;
     unsigned int   outring;
} I830RingBlock;


typedef struct {
     bool                  initialized;

     I830RingBuffer        lp_ring;

     bool                  overlayOn;
     I830OverlayLayerData *iovl;

     agp_info              info;
     agp_allocate          lring_mem;
     agp_allocate          ovl_mem;
     agp_bind              lring_bind;
     agp_bind              ovl_bind;

     u32                   pattern;
     u32                   lring1;
     u32                   lring2;
     u32                   lring3;
     u32                   lring4;

     /* benchmarking */
     u32                   waitfifo_sum;
     u32                   waitfifo_calls;
     u32                   idle_calls;
     u32                   fifo_waitcycles;
     u32                   idle_waitcycles;
     u32                   fifo_cache_hits;
     u32                   fifo_timeoutsum;
     u32                   idle_timeoutsum;
} I830DeviceData;

typedef struct {
     I830DeviceData     *idev;

     I830OverlayRegs    *oregs;

     u32                 flags;
     int                 agpgart;
     agp_info            info;
     volatile u8        *aper_base;
     volatile u8        *lring_base;
     volatile u8        *ovl_base;
     volatile u8        *mmio_base;
     volatile u8        *pattern_base;
} I830DriverData;

extern DisplayLayerFuncs i830OverlayFuncs;

void i830ovlOnOff( I830DriverData       *idrv,
                   I830DeviceData       *idev,
                   bool                  on );



#define i830_readb(mmio_base, where)                     \
        *((volatile u8 *) (mmio_base + where))           \

#define i830_readw(mmio_base, where)                     \
       *((volatile u16 *) (mmio_base + where))           \

#define i830_readl(mmio_base, where)                     \
       *((volatile u32 *) (mmio_base + where))           \

#define i830_writeb(mmio_base, where, val)                              \
        *((volatile u8 *) (mmio_base + where)) = (volatile u8) val      \

#define i830_writew(mmio_base, where, val)                              \
        *((volatile u16 *) (mmio_base + where)) = (volatile u16) val    \

#define i830_writel(mmio_base, where, val)                              \
        *((volatile u32 *) (mmio_base + where)) = (volatile u32) val    \



DFBResult i830_wait_lp_ring( I830DriverData *idrv,
                             I830DeviceData *idev,
                             int             space );

D_DEBUG_DOMAIN( I830_Ring, "I830/Ring", "I830 Ring Buffer" );


static inline DFBResult
i830_begin_lp_ring( I830DriverData *idrv,
                    I830DeviceData *idev,
                    int             needed,
                    I830RingBlock  *ret_block )
{
     DFBResult       ret;
     I830RingBuffer *buf = &idev->lp_ring;

     D_DEBUG_AT( I830_Ring, "begin_lp_ring( %d ) <- head 0x%08x\n", needed, i830_readl( idrv->mmio_base, LP_RING + RING_HEAD ) );

     if (needed & 1)
          D_ERROR( "i830_begin_ring called with odd argument: %d\n", needed);

     needed *= 4;

     if (buf->space < needed) {
          ret = i830_wait_lp_ring( idrv, idev, needed );
          if (ret)
               return ret;
     }

     buf->space -= needed;

     ret_block->virt      = idrv->lring_base;
     ret_block->tail_mask = buf->tail_mask;
     ret_block->outring   = buf->tail;

     return DFB_OK;
}

static inline void
i830_out_ring( I830RingBlock *block,
               u32            value )
{
     D_DEBUG_AT( I830_Ring, "out_ring( 0x%08x, 0x%08x )\n", block->outring, value );

     *(volatile u32*)(block->virt + block->outring) = value;

     block->outring = (block->outring + 4) & block->tail_mask;
}

static inline void
i830_advance_lp_ring( I830DriverData      *idrv,
                      I830DeviceData      *idev,
                      const I830RingBlock *block )
{
     D_DEBUG_AT( I830_Ring, "advance_lp_ring( 0x%08x )\n", block->outring );

     idev->lp_ring.tail = block->outring;

     if (block->outring & 0x07)
          D_ERROR( "i830_advance_lp_ring: "
                   "outring (0x%x) isn't on a QWord boundary", block->outring );

     i830_writel( idrv->mmio_base, LP_RING + RING_TAIL, block->outring );
}

#endif /* __I830_H__ */
