/*-7
 * Copyright (c) 2006 Stephane E. Potvin <sepotvin@videotron.ca>
 * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org>
 * Copyright (c) 2008-2012 Alexander Motin <mav@FreeBSD.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $FreeBSD: release/9.1.0/sys/dev/sound/pci/hda/hdaa.h 232798 2012-03-10 21:58:08Z mav $
 */

/*
 * Intel High Definition Audio (Audio function quirks) driver for FreeBSD.
 */

#ifndef _HDAA_QUIRKS_H_
#define _HDAA_QUIRKS_H_

#define HDAA_GPIO_SHIFT(n)	(n * 3)
#define HDAA_GPIO_MASK(n)	(0x7 << (n * 3))
#define HDAA_GPIO_KEEP(n)	(0x0 << (n * 3))
#define HDAA_GPIO_SET(n)	(0x1 << (n * 3))
#define HDAA_GPIO_CLEAR(n)	(0x2 << (n * 3))
#define HDAA_GPIO_DISABLE(n)	(0x3 << (n * 3))
#define HDAA_GPIO_INPUT(n)	(0x4 << (n * 3))

/* 9 - 25 = anything else */
#define HDAA_QUIRK_SOFTPCMVOL	(1 << 9)
#define HDAA_QUIRK_FIXEDRATE	(1 << 10)
#define HDAA_QUIRK_FORCESTEREO	(1 << 11)
#define HDAA_QUIRK_EAPDINV	(1 << 12)
#define HDAA_QUIRK_SENSEINV	(1 << 14)

/* 26 - 31 = vrefs */
#define HDAA_QUIRK_IVREF50	(1 << 26)
#define HDAA_QUIRK_IVREF80	(1 << 27)
#define HDAA_QUIRK_IVREF100	(1 << 28)
#define HDAA_QUIRK_OVREF50	(1 << 29)
#define HDAA_QUIRK_OVREF80	(1 << 30)
#define HDAA_QUIRK_OVREF100	(1 << 31)

#define HDAA_QUIRK_IVREF	(HDAA_QUIRK_IVREF50 | HDAA_QUIRK_IVREF80 | \
						HDAA_QUIRK_IVREF100)
#define HDAA_QUIRK_OVREF	(HDAA_QUIRK_OVREF50 | HDAA_QUIRK_OVREF80 | \
						HDAA_QUIRK_OVREF100)
#define HDAA_QUIRK_VREF		(HDAA_QUIRK_IVREF | HDAA_QUIRK_OVREF)

#define HDAA_AMP_VOL_DEFAULT	(-1)
#define HDAA_AMP_MUTE_DEFAULT	(0xffffffff)
#define HDAA_AMP_MUTE_NONE	(0)
#define HDAA_AMP_MUTE_LEFT	(1 << 0)
#define HDAA_AMP_MUTE_RIGHT	(1 << 1)
#define HDAA_AMP_MUTE_ALL	(HDAA_AMP_MUTE_LEFT | HDAA_AMP_MUTE_RIGHT)

#define HDAA_AMP_LEFT_MUTED(v)	((v) & (HDAA_AMP_MUTE_LEFT))
#define HDAA_AMP_RIGHT_MUTED(v)	(((v) & HDAA_AMP_MUTE_RIGHT) >> 1)

/* Widget in playback receiving signal from recording. */
#define HDAA_ADC_MONITOR		(1 << 0)
/* Input mixer widget needs volume control as destination. */
#define HDAA_IMIX_AS_DST		(2 << 0)

#define HDAA_CTL_OUT		1
#define HDAA_CTL_IN		2

#define HDA_MAX_CONNS	32
#define HDA_MAX_NAMELEN	32

struct hdaa_audio_as;
struct hdaa_audio_ctl;
struct hdaa_chan;
struct hdaa_devinfo;
struct hdaa_pcm_devinfo;
struct hdaa_widget;

struct hdaa_widget {
	nid_t nid;
	int type;
	int enable;
	int nconns, selconn;
	int waspin;
	uint32_t pflags;
	int bindas;
	int bindseqmask;
	int ossdev;
	uint32_t ossmask;
	int unsol;
	nid_t conns[HDA_MAX_CONNS];
	u_char connsenable[HDA_MAX_CONNS];
	char name[HDA_MAX_NAMELEN];
	uint8_t	*eld;
	int	eld_len;
	struct hdaa_devinfo *devinfo;
	struct {
		uint32_t widget_cap;
		uint32_t outamp_cap;
		uint32_t inamp_cap;
		uint32_t supp_stream_formats;
		uint32_t supp_pcm_size_rate;
		uint32_t eapdbtl;
	} param;
	union {
		struct {
			uint32_t config;
			uint32_t original;
			uint32_t newconf;
			uint32_t cap;
			uint32_t ctrl;
			int	connected;
		} pin;
		struct {
			uint8_t	stripecap;
		} conv;
	} wclass;
};

struct hdaa_audio_ctl {
	struct hdaa_widget *widget, *childwidget;
	int enable;
	int index, dir, ndir;
	int mute, step, size, offset;
	int left, right, forcemute;
	uint32_t muted;
	uint32_t ossmask;	/* OSS devices that may affect control. */
	int	devleft[SOUND_MIXER_NRDEVICES]; /* Left ampl in 1/4dB. */
	int	devright[SOUND_MIXER_NRDEVICES]; /* Right ampl in 1/4dB. */
	int	devmute[SOUND_MIXER_NRDEVICES]; /* Mutes per OSS device. */
};

/* Association is a group of pins bound for some special function. */
struct hdaa_audio_as {
	u_char enable;
	u_char index;
	u_char dir;
	u_char pincnt;
	u_char fakeredir;
	u_char digital;
	uint16_t pinset;
	nid_t hpredir;
	nid_t pins[16];
	nid_t dacs[2][16];
	int num_chans;
	int chans[2];
	int location;	/* Pins location, if all have the same */
	int mixed;	/* Mixed/multiplexed recording, not multichannel. */
	struct hdaa_pcm_devinfo *pdevinfo;
};

struct hdaa_pcm_devinfo {
	device_t dev;
	struct hdaa_devinfo *devinfo;
	struct	snd_mixer *mixer;
	int	index;
	int	registered;
	int	playas, recas;
	u_char	left[SOUND_MIXER_NRDEVICES];
	u_char	right[SOUND_MIXER_NRDEVICES];
	int	minamp[SOUND_MIXER_NRDEVICES]; /* Minimal amps in 1/4dB. */
	int	maxamp[SOUND_MIXER_NRDEVICES]; /* Maximal amps in 1/4dB. */
	int	chan_size;
	int	chan_blkcnt;
	u_char	digital;
	uint32_t	ossmask;	/* Mask of supported OSS devices. */
	uint32_t	recsrc;		/* Mask of supported OSS sources. */
	int		autorecsrc;
};

struct hdaa_devinfo {
	device_t		dev;
	struct mtx		*lock;
	nid_t			nid;
	nid_t			startnode, endnode;
	uint32_t		outamp_cap;
	uint32_t		inamp_cap;
	uint32_t		supp_stream_formats;
	uint32_t		supp_pcm_size_rate;
	uint32_t		gpio_cap;
	uint32_t		quirks;
	uint32_t		newquirks;
	uint32_t		gpio;
	uint32_t		newgpio;
	uint32_t		gpo;
	uint32_t		newgpo;
	int			nodecnt;
	int			ctlcnt;
	int			ascnt;
	int			num_devs;
	int			num_chans;
	struct hdaa_widget	*widget;
	struct hdaa_audio_ctl	*ctl;
	struct hdaa_audio_as	*as;
	struct hdaa_pcm_devinfo	*devs;
	struct hdaa_chan	*chans;
	struct callout		poll_jack;
	int			poll_ival;
};

#define HDAA_CHN_RUNNING	0x00000001
#define HDAA_CHN_SUSPEND	0x00000002

struct hdaa_chan {
	struct snd_dbuf *b;
	struct pcm_channel *c;
	struct pcmchan_caps caps;
	struct hdaa_devinfo *devinfo;
	struct hdaa_pcm_devinfo *pdevinfo;
	uint32_t spd, fmt, fmtlist[32], pcmrates[16];
	uint32_t supp_stream_formats, supp_pcm_size_rate;
	uint32_t blkcnt, blksz;
	uint32_t *dmapos;
	uint32_t flags;
	int dir;
	int off;
	int sid;
	int bit16, bit32;
	int channels;	/* Number of audio channels. */
	int as;		/* Number of association. */
	int asindex;	/* Index within association. */
	nid_t io[16];
	uint8_t	stripecap;	/* AND of stripecap of all ios. */
	uint8_t	stripectl;	/* stripe to use to all ios. */
};

#define MINQDB(ctl)							\
	((0 - (ctl)->offset) * ((ctl)->size + 1))

#define MAXQDB(ctl)							\
	(((ctl)->step - (ctl)->offset) * ((ctl)->size + 1))

#define RANGEQDB(ctl)							\
	((ctl)->step * ((ctl)->size + 1))

#define VAL2QDB(ctl, val) 						\
	(((ctl)->size + 1) * ((int)(val) - (ctl)->offset))

#define QDB2VAL(ctl, qdb) 						\
	imax(imin((((qdb) + (ctl)->size / 2 * ((qdb) > 0 ? 1 : -1)) /	\
	 ((ctl)->size + 1) + (ctl)->offset), (ctl)->step), 0)

#define hdaa_codec_id(devinfo)						\
		(((uint32_t)hda_get_vendor_id(devinfo->dev) << 16) +	\
		hda_get_device_id(devinfo->dev))

#define hdaa_subvendor_id(devinfo)					\
		(((uint32_t)hda_get_subdevice_id(devinfo->dev) << 16) +	\
		hda_get_subvendor_id(devinfo->dev))

struct hdaa_widget	*hdaa_widget_get(struct hdaa_devinfo *, nid_t);
uint32_t		hdaa_widget_pin_patch(uint32_t config, const char *str);
uint32_t		hdaa_gpio_patch(uint32_t gpio, const char *str);

void			hdaa_patch(struct hdaa_devinfo *devinfo);
void			hdaa_patch_direct(struct hdaa_devinfo *devinfo);

#endif
