/*
 * Purpose: Definitions for the neomagic driver
 */
/*
 * This file is part of Open Sound System
 *
 * Copyright (C) 4Front Technologies 1996-2007.
 *
 * This software is released under CDDL 1.0 source license for operating systems
 * that have their full source code available under the BSD or CDDL licenses.
 *
 * See the COPYING file included in the main directory of this source
 * distribution. Please contact sales@opensound.com for further info.
 */

/*
 * NOTE! This is not the development version of this file. Please download the
 * sources of the latest Open Sound System version from 
 * http://developer.opensound.com/sources if you plan to make any changes
 * to the software.
 */


typedef unsigned char U8;
typedef unsigned short U16;
typedef unsigned int U32;

/* The revisions that we currently handle.  */
enum neomagicrev
{
  REV_NEOMAGICAV, REV_NEOMAGICZX
};

#define MAX_PORTC 2

/* The two memory ports.  */
typedef struct neomagic_portc
{
  unsigned int physaddr;	/* Physical address of the port. */
  char *ptr;			/* Our mapped-in pointer. */
  unsigned int start_offset;	/* PTR's offset within the physical port.  */
  unsigned int end_offset;	/* And the offset of the end of the buffer.  */
  unsigned int samplerate;
  unsigned char bits;
  unsigned char stereo;
}
neomagic_portc;


typedef struct neomagic_devc
{
  /* OS struct for OSS */
  oss_device_t *osdev;

  /* Our IRQ number. */
  int irq;

  /* Mutex */
  oss_mutex_t mutex;
  oss_mutex_t low_mutex;

  /* Magic number used to verify that this struct is valid. */
#define NM_MAGIC_SIG 0x55aa00ff
  int magsig;

  enum neomagicrev rev;		/* Revision number */

  char *chip_name;		/* Chip name. */

  ac97_devc ac97devc;

  int dev[2];			/* Our audio device numbers. */

  int opencnt[2];		/* The # of times each device has been opened. */

  /* We use two devices, because we can do simultaneous play and record.
     This keeps track of which device is being used for what purpose;
     these are the actual device numbers. */
  int dev_for_play;
  int dev_for_record;

  int mixer_dev;		/* The mixer device. */

  /* 
   * Can only be opened once for each operation.  These aren't set
   * until an actual I/O operation is performed; this allows one
   * device to be open for read/write without inhibiting I/O to
   * the other device.
   */
  int is_open_play;
  int is_open_record;

  int playing;			/* Non-zero if we're currently playing a sample. */
  int recording;		/* Same for recording a sample. */

  neomagic_portc portc[MAX_PORTC];

  /* The following are offsets within memory port 1. */
  unsigned int coeffBuf;
  unsigned int allCoeffBuf;

  /* Record and playback buffers. */
  unsigned int abuf1, abuf2;
  char *abuf1virt, *abuf2virt;

  /* Offset of the mixer status register in memory port 2.  */
  unsigned int mixer_status_offset;

  /* 
   * Status mask bit; (*mixer_status_loc & mixer_status_mask) == 0 means
   * it's ready.  
   */
  unsigned short mixer_status_mask;

  /* The sizes of the playback and record ring buffers. */
  unsigned int playbackBufferSize;
  unsigned int recordBufferSize;

  /* The devc interrupt service routine. */
  int (*introutine) (struct neomagic_devc *);

  int mixer_cache[64];
}
neomagic_devc;

/* The BIOS signature. */
#define NM_SIGNATURE 0x4e4d0000
/* Signature mask. */
#define NM_SIG_MASK 0xffff0000

/* Size of the second memory area. */
#define NM_PORT2_SIZE 4096

/* The base offset of the mixer in the second memory area. */
#define NM_MIXER_OFFSET 0x600

/* The maximum size of a coefficient entry. */
#define NM_MAX_COEFFICIENT 0x5000

/* The interrupt register. */
#define NM_INT_REG 0xa04
/* And its bits. */
#define NM_PLAYBACK_INT 0x40
#define NM_RECORD_INT 0x100
#define NM_MISC_INT_1 0x4000
#define NM_MISC_INT_2 0x1
#define NM_ACK_INT(CARD, X) neomagic_writePort16((CARD), 2, NM_INT_REG, (X) << 1)

/* The AV's "mixer ready" status bit and location. */
#define NM_MIXER_STATUS_OFFSET 0xa06
#define NM_MIXER_READY_MASK 0x0800
#define NM_PRESENCE_MASK 0x0050
#define NM_PRESENCE_VALUE 0x0040

/*
 * For the ZX.  It uses the same interrupt register, but it holds 32
 * bits instead of 16.
 */
#define NMZX_PLAYBACK_INT 0x10000
#define NMZX_RECORD_INT 0x80000
#define NMZX_MISC_INT_1 0x8
#define NMZX_MISC_INT_2 0x2
#define NMZX_ACK_INT(CARD, X) neomagic_writePort32((CARD), 2, NM_INT_REG, (X))

/* The ZX's "mixer ready" status bit and location. */
#define NMZX_MIXER_STATUS_OFFSET 0xa08
#define NMZX_MIXER_READY_MASK 0x0800

/* The playback registers start from here. */
#define NM_PLAYBACK_REG_OFFSET 0x0
/* The record registers start from here. */
#define NM_RECORD_REG_OFFSET 0x200

/* The rate register is located 2 bytes from the start of the register area. */
#define NM_RATE_REG_OFFSET 2

/* Mono/stereo flag, number of bits on playback, and rate mask. */
#define NM_RATE_STEREO 1
#define NM_RATE_BITS_16 2
#define NM_RATE_MASK 0xf0

/* Playback enable register. */
#define NM_PLAYBACK_ENABLE_REG (NM_PLAYBACK_REG_OFFSET + 0x1)
#define NM_PLAYBACK_ENABLE_FLAG 1
#define NM_PLAYBACK_ONESHOT 2
#define NM_PLAYBACK_FREERUN 4

/* Mutes the audio output. */
#define NM_AUDIO_MUTE_REG (NM_PLAYBACK_REG_OFFSET + 0x18)
#define NM_AUDIO_MUTE_LEFT 0x8000
#define NM_AUDIO_MUTE_RIGHT 0x0080

/* Recording enable register. */
#define NM_RECORD_ENABLE_REG (NM_RECORD_REG_OFFSET + 0)
#define NM_RECORD_ENABLE_FLAG 1
#define NM_RECORD_FREERUN 2

#define NM_RBUFFER_START (NM_RECORD_REG_OFFSET + 0x4)
#define NM_RBUFFER_END   (NM_RECORD_REG_OFFSET + 0x10)
#define NM_RBUFFER_WMARK (NM_RECORD_REG_OFFSET + 0xc)
#define NM_RBUFFER_CURRP (NM_RECORD_REG_OFFSET + 0x8)

#define NM_PBUFFER_START (NM_PLAYBACK_REG_OFFSET + 0x4)
#define NM_PBUFFER_END   (NM_PLAYBACK_REG_OFFSET + 0x14)
#define NM_PBUFFER_WMARK (NM_PLAYBACK_REG_OFFSET + 0xc)
#define NM_PBUFFER_CURRP (NM_PLAYBACK_REG_OFFSET + 0x8)

/* A few trivial routines to make it easier to work with the registers
   on the chip. */

/* This is a common code portion used to fix up the port offsets. */
#define NM_FIX_PORT \
  if (port < 1 || port > 2 || devc == NULL) \
      return -1; \
\
    if (offset < devc->portc[port - 1].start_offset \
	|| offset >= devc->portc[port - 1].end_offset) { \
	cmn_err(CE_WARN, "Bad access: port %d, offset 0x%x\n", port, offset); \
	return -1; \
    } \
    offset -= devc->portc[port - 1].start_offset;

#define DEFwritePortX(X, func) \
static inline int neomagic_writePort##X (neomagic_devc *devc,\
				      int port, int offset, int value)\
{\
    U##X *addr;\
\
    DDB (cmn_err(CE_CONT, "Writing 0x%x to %d:0x%x\n", value, port, offset));\
\
    NM_FIX_PORT;\
\
    addr = (U##X *)(devc->portc[port - 1].ptr + offset);\
    func (value, addr);\
    return 0;\
}

static inline void
nm_writeb (unsigned char value, unsigned char *ptr)
{
  *ptr = value;
}

static inline void
nm_writew (unsigned short value, unsigned short *ptr)
{
  *ptr = value;
}

static inline void
nm_writel (unsigned int value, unsigned int *ptr)
{
  *ptr = value;
}


DEFwritePortX (8, nm_writeb)
DEFwritePortX (16, nm_writew) DEFwritePortX (32, nm_writel)
#define DEFreadPortX(X, func) \
static inline U##X neomagic_readPort##X (neomagic_devc *devc,\
					int port, int offset)\
{\
    U##X *addr;\
\
    NM_FIX_PORT\
\
    addr = (U##X *)(devc->portc[port - 1].ptr + offset);\
    return func(addr);\
}
     static inline unsigned char
     nm_readb (unsigned char *ptr)
{
  return *ptr;
}

static inline unsigned short
nm_readw (unsigned short *ptr)
{
  return *ptr;
}

static inline unsigned int
nm_readl (unsigned int *ptr)
{
  return *ptr;
}

DEFreadPortX (8, nm_readb)
DEFreadPortX (16, nm_readw) DEFreadPortX (32, nm_readl)
     static inline int
     neomagic_writeBuffer8 (neomagic_devc * devc, unsigned char *src,
			    int port, int offset, int amt)
{
  NM_FIX_PORT;
  memcpy (devc->portc[port - 1].ptr + offset, src, amt);
  return 0;
}

static inline int
neomagic_readBuffer8 (neomagic_devc * devc, unsigned char *dst, int port,
		      int offset, int amt)
{
  NM_FIX_PORT;
  memcpy (dst, devc->portc[port - 1].ptr + offset, amt);
  return 0;
}
