/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Purpose: Definitions for the SB Live/Audigy driver
 */
/*
 * Copyright (C) 4Front Technologies 1996-2009.
 */
#ifndef	EMU10K_H
#define	EMU10K_H

#define	PCI_VENDOR_ID_CREATIVE		0x1102
#define	PCI_DEVICE_ID_SBLIVE		0x0002
#define	PCI_DEVICE_ID_AUDIGY		0x0004
#define	PCI_DEVICE_ID_AUDIGYVALUE	0x0008

#define	SAMPLE_RATE		48000

#define	EMU10K_NAME		"audioemu10k"

#define	EMU10K_NUM_PORTC	2
#define	EMU10K_PLAY		0
#define	EMU10K_REC		1

#define	EMU10K_NUM_FRAGS	(2*4)	/* Must be multiple of 2 */

#define	EMU10K_MAX_INTRS	512
#define	EMU10K_MIN_INTRS	10
#define	EMU10K_INTRS		100

#define	FRAGMENT_FRAMES		512

#define	EMU10K1_MAGIC		0xe10001
#define	EMU10K2_MAGIC		0xe10002

/* Audio */

#define	DMABUF_SIZE		(256 * 1024)

#define	AUDIO_MAXVOICE		(2*EMU10K_NUM_PORTC)
/* Audio buffer + silent page */
#define	AUDIO_MEMSIZE		(EMU10K_NUM_PORTC*DMABUF_SIZE+4096)

/* Wall clock register */
#define	WC			0x10

/* Hardware config register */
#define	HCFG			0x14
#define	HCFG_CODECFORMAT_MASK	0x00070000	/* CODEC format */
#define	HCFG_CODECFORMAT_AC97	0x00000000	/* AC97 CODEC format */
#define	HCFG_CODECFORMAT_I2S	0x00010000	/* I2S CODEC format */
#define	HCFG_GPINPUT0		0x00004000	/* External pin112 */
#define	HCFG_GPINPUT1		0x00002000	/* External pin110 */
#define	HCFG_GPOUTPUT_MASK	0x00001c00	/* Controllable pins */
#define	HCFG_GPOUT0		0x00001000	/* enable dig out on 5.1 */
#define	HCFG_GPOUT1		0x00000800	/* IR */
#define	HCFG_GPOUT2		0x00000400	/* IR */
#define	HCFG_JOYENABLE		0x00000200	/* Internal joystick enable */
#define	HCFG_PHASETRACKENABLE	0x00000100	/* Phase tracking enable */
#define	HCFG_AC3ENABLE_MASK	0x0x0000e0	/* AC3 async input control */
#define	HCFG_AC3ENABLE_ZVIDEO	0x00000080	/* Chan 0/1 replace ZVIDEO  */
#define	HCFG_AC3ENABLE_CDSPDIF	0x00000040	/* Chan 0/1 replace CDSPDIF */
#define	HCFG_AC3ENABLE_GPSPDIF	0x00000020	/* Chan 0/1 replace GPSPDIF */
#define	HCFG_AUTOMUTE		0x00000010
#define	HCFG_LOCKSOUNDCACHE	0x00000008
#define	HCFG_LOCKTANKCACHE_MASK	0x00000004
#define	HCFG_LOCKTANKCACHE	0x01020014
#define	HCFG_MUTEBUTTONENABLE	0x00000002	/* Mute can clear audioenable */
#define	HCFG_AUDIOENABLE	0x00000001	/* Codecs can send data */
#define	A_HCFG_VMUTE		0x00004000
#define	A_HCFG_AUTOMUTE		0x00008000
#define	A_HCFG_XM		0x00040000	/* Xtended address mode */

/*
 * GPIO bit definitions (global register 0x18) for Audigy.
 */

#define	A_IOCFG_GPOUT0		0x0044	/* analog/digital? */
#define	A_IOCFG_GPOUT1		0x0002	/* IR */
#define	A_IOCFG_GPOUT2		0x0001	/* IR */

/* Status bits (read only) */
#define	GPIO_VERSAPLUGGED	0x2000	/* Center/LFE/digital */
#define	GPIO_FRONTPLUGGED	0x4000
#define	GPIO_REARPLUGGED	0x8000
#define	GPIO_HEADPHPLUGGED	0x0100
#define	GPIO_ANALOG_MUTE	0x0040
#define	GPIO_DIGITAL_ENABLE	0x0004	/* Cen/lfe (0) or digital (1) switch */

#define	FILL_PAGE_MAP_ENTRY(e, v)					\
	ddi_put32(devc->pt_acch, devc->page_map + e, ((v) << 1) | (e));

/*
 * Audio block registers
 */

#define	CPF		0x000	/* DW:cnl   Current pitch and fraction */
#define	CPF_CURRENTPITCH_MASK		0xffff0000
#define	CPF_CURRENTPITCH		0x10100000
#define	CPF_STEREO_MASK			0x00008000
#define	CPF_STOP_MASK			0x00004000
#define	CPF_FRACADDRESS_MASK		0x00003fff


#define	PTAB		0x001	/* DW:cnl   Pitch target and sends A and B */
#define	PTRX_PITCHTARGET_MASK		0xffff0000
#define	PTRX_PITCHTARGET		0x10100001
#define	PTRX_FXSENDAMOUNT_A_MASK	0x0000ff00
#define	PTRX_FXSENDAMOUNT_A		0x08080001
#define	PTRX_FXSENDAMOUNT_B_MASK	0x000000ff
#define	PTRX_FXSENDAMOUNT_B		0x08000001


#define	CVCF		0x002	/* DW:cnl   Curr vol and curr filter cutoff */
#define	VTFT		0x003	/* DW:cnl   Volume tgt and filter cutoff tgt */
#define	Z2		0x004	/* DW:cnl   Filter delay memory 2 */
#define	Z1		0x005	/* DW:cnl   Filter delay memory 1 */
#define	SCSA		0x006	/* DW:cnl   Send C and Start addr */
#define	SDL		0x007	/* DW:cnl   Send D and Loop addr */
#define	QKBCA		0x008	/* DW:cnl   Filter Q, ROM, etc */
#define	CCR		0x009
#define	CCR_CACHEINVALIDSIZE		0x07190009
#define	CCR_CACHEINVALIDSIZE_MASK	0xfe000000
#define	CCR_CACHELOOPFLAG		0x01000000
#define	CCR_INTERLEAVEDSAMPLES		0x00800000
#define	CCR_WORDSIZEDSAMPLES		0x00400000
#define	CCR_READADDRESS			0x06100009
#define	CCR_READADDRESS_MASK		0x003f0000
#define	CCR_LOOPINVALSIZE		0x0000fe00
#define	CCR_LOOPFLAG			0x00000100
#define	CCR_CACHELOOPADDRHI		0x000000ff

#define	CLP		0x00a
#define	SRHE		0x07c
#define	STHE		0x07d
#define	SRDA		0x07e
#define	STDA		0x07f
#define	L_FXRT		0x00b
#define	FXRT		0x00b	/* W:cnl */
#define	MAPA		0x00c
#define	MAPB		0x00d
#define	VEV		0x010	/* W:cnl */
#define	VEHA		0x011	/* W:cnl */
#define	VEDS		0x012	/* W:cnl */
#define	MLV		0x013	/* W:cnl */
#define	MEV		0x014	/* W:cnl */
#define	MEHA		0x015	/* W:cnl */
#define	MEDS		0x016	/* W:cnl */
#define	VLV		0x017	/* W:cnl */
#define	IP		0x018	/* W:cnl */
#define	IFA		0x019	/* W:cnl */
#define	PEFE		0x01a	/* W:cnl */
#define	PEFE_PITCHAMOUNT_MASK	0x0000ff00	/* Pitch envlope amount */
#define	PEFE_PITCHAMOUNT	0x0808001a
#define	PEFE_FILTERAMOUNT_MASK	0x000000ff	/* Filter envlope amount */
#define	PEFE_FILTERAMOUNT	0x0800001a

#define	VFM		0x01b	/* W:cnl */
#define	TMFQ		0x01c	/* W:cnl */
#define	VVFQ		0x01d	/* W:cnl */
#define	TMPE		0x01e	/* W:cnl */
#define	CD0		0x020	/* DW:cnl (16 registers) */
#define	PTBA		0x040	/* DW:nocnl */
#define	TCBA		0x041	/* DW:nocnl */
#define	ADCSR		0x042	/* B:nocnl */
#define	FXWC		0x043	/* DW:nocnl */
#define	TCBS		0x044	/* B:nocnl */
#define	MBA		0x045	/* DW:nocnl */
#define	ADCBA		0x046	/* DW:nocnl */
#define	FXBA		0x047	/* DW:nocnl */

#define	MBS		0x049	/* B:nocnl */
#define	ADCBS		0x04a	/* B:nocnl */
#define	FXBS		0x04b	/* B:nocnl */
#define	CSBA	0x4c
#define	CSDC	0x4d
#define	CSFE	0x4e
#define	CSHG	0x4f
#define	CDCS		0x050	/* DW:nocnl */
#define	GPSCS		0x051	/* DW:nocnl */
#define	DBG		0x052	/* DW:nocnl */
#define	AUDIGY_DBG	0x053	/* DW:nocnl */
#define	SCS0		0x054	/* DW:nocnl */
#define	SCS1		0x055	/* DW:nocnl */
#define	SCS2		0x056	/* DW:nocnl */
#define	CLIEL		0x058	/* DW:nocnl */
#define	CLIEH		0x059	/* DW:nocnl */
#define	CLIPL		0x05a	/* DW:nocnl */
#define	CLIPH		0x05b	/* DW:nocnl */
#define	SOLL		0x05c	/* DW:nocnl */
#define	SOLH		0x05d	/* DW:nocnl */
#define	SOC		0x05e	/* DW:nocnl */
#define	AC97SLOT	0x05f
#define	AC97SLOT_REAR_RIGHT	0x01
#define	AC97SLOT_REAR_LEFT	0x02
#define	AC97SLOT_CENTER		0x10
#define	AC97SLOT_LFE		0x20
#define	CDSRCS		0x060	/* DW:nocnl */
#define	GPSRCS		0x061	/* DW:nocnl */
#define	ZVSRCS		0x062	/* DW:nocnl */
#define	ADCIDX		0x063	/* W:nocnl */
#define	MIDX		0x064	/* W:nocnl */
#define	FXIDX		0x065	/* W:nocnl */

/* Half loop interrupt registers (audigy only) */
#define	HLIEL		0x066	/* DW:nocnl */
#define	HLIEH		0x067	/* DW:nocnl */
#define	HLIPL		0x068	/* DW:nocnl */
#define	HLIPH		0x069	/* DW:nocnl */
#define	GPR0	((devc->feature_mask&SB_LIVE)? 0x100:0x400)	/* DW:nocnl */
#define	TMA0		0x300	/* Tank memory */
#define	UC0	((devc->feature_mask&SB_LIVE) ? 0x400:0x600)	/* DSM ucode */

/* Interrupt pending register */
#define	INTPEND	0x08
#define		INT_VI		0x00100000
#define		INT_VD		0x00080000
#define		INT_MU		0x00040000
#define		INT_MF		0x00020000
#define		INT_MH		0x00010000
#define		INT_AF		0x00008000
#define		INT_AH		0x00004000
#define		INT_IT		0x00000200
#define		INT_TX		0x00000100
#define		INT_RX		0x00000080
#define		INT_CL		0x00000040
/* Interrupt enable register */
#define	IE	0x0c
#define		IE_VI		0x00000400
#define		IE_VD		0x00000200
#define		IE_MU		0x00000100
#define		IE_MB		0x00000080
#define		IE_AB		0x00000040
#define		IE_IT		0x00000004
#define		IE_TX		0x00000002
#define		IE_RX		0x00000001

/* Interval timer register */
#define	TIMR		0x1a

/* EMU10K2 MIDI UART */
#define	MUADAT		0x070
#define	MUACMD		0x071
#define	MUASTAT		MUACMD

/* EMU10K2 S/PDIF recording buffer */
#define	SPRI		0x6a
#define	SPRA		0x6b
#define	SPRC		0x6c

#define	EHC		0x76	/* Audigy 2 */

#define	SRHE	0x07c
#define	STHE	0x07d
#define	SRDA	0x07e

#define	ROM0		0x00000000	/* interpolation ROM 0 */
#define	ROM1		0x02000000	/* interpolation ROM 1 */
#define	ROM2		0x04000000	/* interpolation ROM 2 */
#define	ROM3		0x06000000	/* interpolation ROM 3 */
#define	ROM4		0x08000000	/* interpolation ROM 4 */
#define	ROM5		0x0A000000	/* interpolation ROM 5 */
#define	ROM6		0x0C000000	/* interpolation ROM 6 */
#define	ROM7		0x0E000000	/* interpolation ROM 7 */
#define	BYTESIZE	0x01000000	/* byte sound memory */

#define	MAX_GPR	256

/* See feature_mask below */
#define	SB_LIVE		1
#define	SB_AUDIGY	2
#define	SB_AUDIGY2	4
#define	SB_AUDIGY2VAL	8
#define	SB_51		0x10
#define	SB_71		0x20
#define	SB_INVSP	0x40	/* invert shared spdif switch */
#define	SB_NOEXP	0x80	/* no support for Live! Drive or expansion */

#define	LEFT_CH		0
#define	RIGHT_CH	1

#ifdef	_KERNEL

typedef struct _emu10k_devc_t emu10k_devc_t;
typedef struct _emu10k_portc_t emu10k_portc_t;


typedef enum {
	CTL_VOLUME = 0,
	CTL_FRONT,
	CTL_SURROUND,
	CTL_CENTER,
	CTL_LFE,
	CTL_SIDE,
	CTL_HEADPH,

	CTL_RECGAIN,
	CTL_RECSRC,
	CTL_AC97SRC,

	/* monitor source values */
	CTL_AC97,
	CTL_DIGCD,
	CTL_SPD1,
	CTL_SPD2,
	CTL_LINE2,
	CTL_AUX2,

	CTL_JACK3,

	/* this one must be last */
	CTL_MAX,
} emu10k_ctrl_id_t;

typedef struct _emu10k_ctrl {
	emu10k_devc_t	*devc;
	audio_ctrl_t	*ctrl;
	int		gpr_num;
	uint64_t	val;
} emu10k_ctrl_t;

typedef struct _emu10k_gpr {
	boolean_t	valid;
	uint32_t	value;
} emu10k_gpr_t;

struct _emu10k_portc_t {
	emu10k_devc_t		*devc;
	audio_engine_t		*engine;

	/* Helper functions */
	void			(*update_port)(emu10k_portc_t *);
	void			(*reset_port)(emu10k_portc_t *);
	void			(*stop_port)(emu10k_portc_t *);
	void			(*start_port)(emu10k_portc_t *);

	int			channels;

	boolean_t		started;
	boolean_t		active;
	unsigned		nframes;
	unsigned		nfrags;
	unsigned		fragsz;

	ddi_dma_handle_t	buf_dmah;	/* dma for buffers */
	ddi_acc_handle_t	buf_acch;
	uint32_t		buf_paddr;
	caddr_t			buf_kaddr;
	size_t			buf_size;
	/* Start of loop within the internal memory space */
	uint32_t		memptr;
	int			syncdir;
	/* Position & timing */
	uint64_t		count;
	uint32_t		pos;
	int		dopos;
};

struct _emu10k_devc_t {
	dev_info_t		*dip;
	audio_dev_t		*adev;
	ddi_acc_handle_t	pcih;
	ddi_acc_handle_t	regsh;
	caddr_t			regs;
	kmutex_t		mutex;

	/*
	 * Page table
	 */
	ddi_dma_handle_t	pt_dmah;	/* dma for page_tablefers */
	ddi_acc_handle_t	pt_acch;
	uint32_t		pt_paddr;
	caddr_t			pt_kaddr;
	uint32_t		*page_map;	/* up to 8k ptrs to 4k pages */


	/*
	 * Silent page used by voices that don't play anything.
	 */
	ddi_dma_handle_t	silence_dmah;	/* dma for silencefers */
	ddi_acc_handle_t	silence_acch;
	uint32_t		silence_paddr;
	caddr_t			silence_kaddr;

	/*
	 * Device feature mask tells which kind of features are
	 * supported by the hardware. Audigy2/2val have multiple bits
	 * set while Live! has just the SB_LIVE bits. So Features of
	 * Audigy will be reported by Audigy2/val too.
	 */
	int			feature_mask;
	int			max_mem, max_pages, nr_pages;
	/*
	 * Mixer
	 */
	ac97_t			*ac97;
	ac97_ctrl_t		*ac97_recsrc;
	uint32_t		ac97_stereomix;
	emu10k_gpr_t		gpr_shadow[MAX_GPR];
	emu10k_ctrl_t		ctrls[CTL_MAX];

	/*
	 * Audio
	 */

	int			audio_memptr;
	int			*silent_page;

	emu10k_portc_t		*portc[EMU10K_NUM_PORTC];
};

#define	INB(devc, reg)		ddi_get8(devc->regsh, (void *)(reg))
#define	OUTB(devc, val, reg)	ddi_put8(devc->regsh, (void *)(reg), (val))

#define	INW(devc, reg)		ddi_get16(devc->regsh, (void *)(reg))
#define	OUTW(devc, val, reg)	ddi_put16(devc->regsh, (void *)(reg), (val))

#define	INL(devc, reg)		ddi_get32(devc->regsh, (void *)(reg))
#define	OUTL(devc, val, reg)	ddi_put32(devc->regsh, (void *)(reg), (val))

#endif	/* _KERNEL */

#endif /* EMU10K_H */