/*
* ALSA driver for VIA VT82xx (South Bridge)
*
* VT82C686A/B/C, VT8233A/C, VT8235
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
* Tjeerd.Mulder <Tjeerd.Mulder@fujitsu-siemens.com>
* 2002 Takashi Iwai <tiwai@suse.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/*
* Changes:
*
* Dec. 19, 2002 Takashi Iwai <tiwai@suse.de>
* - use the DSX channels for the first pcm playback.
* (on VIA8233, 8233C and 8235 only)
* this will allow you play simultaneously up to 4 streams.
* multi-channel playback is assigned to the second device
* on these chips.
* - support the secondary capture (on VIA8233/C,8235)
* - SPDIF support
* the DSX3 channel can be used for SPDIF output.
* on VIA8233A, this channel is assigned to the second pcm
* playback.
* the card config of alsa-lib will assign the correct
* device for applications.
* - clean up the code, separate low-level initialization
* routines for each chipset.
*
* Sep. 26, 2005 Karsten Wiese <annabellesgarden@yahoo.de>
* - Optimize position calculation for the 823x chips.
*/
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/gameport.h>
#include <linux/moduleparam.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/info.h>
#include <sound/tlv.h>
#include <sound/ac97_codec.h>
#include <sound/mpu401.h>
#include <sound/initval.h>
#if 0
#define POINTER_DEBUG
#endif
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("VIA VT82xx audio");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{VIA,VT82C686A/B/C,pci},{VIA,VT8233A/C,8235}}");
#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
#define SUPPORT_JOYSTICK 1
#endif
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
static long mpu_port;
#ifdef SUPPORT_JOYSTICK
static int joystick;
#endif
static int ac97_clock = 48000;
static char *ac97_quirk;
static int dxs_support;
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for VIA 82xx bridge.");
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for VIA 82xx bridge.");
module_param(mpu_port, long, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port. (VT82C686x only)");
#ifdef SUPPORT_JOYSTICK
module_param(joystick, bool, 0444);
MODULE_PARM_DESC(joystick, "Enable joystick. (VT82C686x only)");
#endif
module_param(ac97_clock, int, 0444);
MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz).");
module_param(ac97_quirk, charp, 0444);
MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware.");
module_param(dxs_support, int, 0444);
MODULE_PARM_DESC(dxs_support, "Support for DXS channels (0 = auto, 1 = enable, 2 = disable, 3 = 48k only, 4 = no VRA, 5 = enable any sample rate)");
/* just for backward compatibility */
static int enable;
module_param(enable, bool, 0444);
/* revision numbers for via686 */
#define VIA_REV_686_A 0x10
#define VIA_REV_686_B 0x11
#define VIA_REV_686_C 0x12
#define VIA_REV_686_D 0x13
#define VIA_REV_686_E 0x14
#define VIA_REV_686_H 0x20
/* revision numbers for via8233 */
#define VIA_REV_PRE_8233 0x10 /* not in market */
#define VIA_REV_8233C 0x20 /* 2 rec, 4 pb, 1 multi-pb */
#define VIA_REV_8233 0x30 /* 2 rec, 4 pb, 1 multi-pb, spdif */
#define VIA_REV_8233A 0x40 /* 1 rec, 1 multi-pb, spdf */
#define VIA_REV_8235 0x50 /* 2 rec, 4 pb, 1 multi-pb, spdif */
#define VIA_REV_8237 0x60
#define VIA_REV_8251 0x70
/*
* Direct registers
*/
#define VIAREG(via, x) ((via)->port + VIA_REG_##x)
#define VIADEV_REG(viadev, x) ((viadev)->port + VIA_REG_##x)
/* common offsets */
#define VIA_REG_OFFSET_STATUS 0x00 /* byte - channel status */
#define VIA_REG_STAT_ACTIVE 0x80 /* RO */
#define VIA8233_SHADOW_STAT_ACTIVE 0x08 /* RO */
#define VIA_REG_STAT_PAUSED 0x40 /* RO */
#define VIA_REG_STAT_TRIGGER_QUEUED 0x08 /* RO */
#define VIA_REG_STAT_STOPPED 0x04 /* RWC */
#define VIA_REG_STAT_EOL 0x02 /* RWC */
#define VIA_REG_STAT_FLAG 0x01 /* RWC */
#define VIA_REG_OFFSET_CONTROL 0x01 /* byte - channel control */
#define VIA_REG_CTRL_START 0x80 /* WO */
#define VIA_REG_CTRL_TERMINATE 0x40 /* WO */
#define VIA_REG_CTRL_AUTOSTART 0x20
#define VIA_REG_CTRL_PAUSE 0x08 /* RW */
#define VIA_REG_CTRL_INT_STOP 0x04
#define VIA_REG_CTRL_INT_EOL 0x02
#define VIA_REG_CTRL_INT_FLAG 0x01
#define VIA_REG_CTRL_RESET 0x01 /* RW - probably reset? undocumented */
#define VIA_REG_CTRL_INT (VIA_REG_CTRL_INT_FLAG | VIA_REG_CTRL_INT_EOL | VIA_REG_CTRL_AUTOSTART)
#define VIA_REG_OFFSET_TYPE 0x02 /* byte - channel type (686 only) */
#define VIA_REG_TYPE_AUTOSTART 0x80 /* RW - autostart at EOL */
#define VIA_REG_TYPE_16BIT 0x20 /* RW */
#define VIA_REG_TYPE_STEREO 0x10 /* RW */
#define VIA_REG_TYPE_INT_LLINE 0x00
#define VIA_REG_TYPE_INT_LSAMPLE 0x04
#define VIA_REG_TYPE_INT_LESSONE 0x08
#define VIA_REG_TYPE_INT_MASK 0x0c
#define VIA_REG_TYPE_INT_EOL 0x02
#define VIA_REG_TYPE_INT_FLAG 0x01
#define VIA_REG_OFFSET_TABLE_PTR 0x04 /* dword - channel table pointer */
#define VIA_REG_OFFSET_CURR_PTR 0x04 /* dword - channel current pointer */
#define VIA_REG_OFFSET_STOP_IDX 0x08 /* dword - stop index, channel type, sample rate */
#define VIA8233_REG_TYPE_16BIT 0x00200000 /* RW */
#define VIA8233_REG_TYPE_STEREO 0x00100000 /* RW */
#define VIA_REG_OFFSET_CURR_COUNT 0x0c /* dword - channel current count (24 bit) */
#define VIA_REG_OFFSET_CURR_INDEX 0x0f /* byte - channel current index (for via8233 only) */
#define DEFINE_VIA_REGSET(name,val) \
enum {\
VIA_REG_##name##_STATUS = (val),\
VIA_REG_##name##_CONTROL = (val) + 0x01,\
VIA_REG_##name##_TYPE = (val) + 0x02,\
VIA_REG_##name##_TABLE_PTR = (val) + 0x04,\
VIA_REG_##name##_CURR_PTR = (val) + 0x04,\
VIA_REG_##name##_STOP_IDX = (val) + 0x08,\
VIA_REG_##name##_CURR_COUNT = (val) + 0x0c,\
}
/* playback block */
DEFINE_VIA_REGSET(PLAYBACK, 0x00);
DEFINE_VIA_REGSET(CAPTURE, 0x10);
DEFINE_VIA_REGSET(FM, 0x20);
/* AC'97 */
#define VIA_REG_AC97 0x80 /* dword */
#define VIA_REG_AC97_CODEC_ID_MASK (3<<30)
#define VIA_REG_AC97_CODEC_ID_SHIFT 30
#define VIA_REG_AC97_CODEC_ID_PRIMARY 0x00
#define VIA_REG_AC97_CODEC_ID_SECONDARY 0x01
#define VIA_REG_AC97_SECONDARY_VALID (1<<27)
#define VIA_REG_AC97_PRIMARY_VALID (1<<25)
#define VIA_REG_AC97_BUSY (1<<24)
#define VIA_REG_AC97_READ (1<<23)
#define VIA_REG_AC97_CMD_SHIFT 16
#define VIA_REG_AC97_CMD_MASK 0x7e
#define VIA_REG_AC97_DATA_SHIFT 0
#define VIA_REG_AC97_DATA_MASK 0xffff
#define VIA_REG_SGD_SHADOW 0x84 /* dword */
/* via686 */
#define VIA_REG_SGD_STAT_PB_FLAG (1<<0)
#define VIA_REG_SGD_STAT_CP_FLAG (1<<1)
#define VIA_REG_SGD_STAT_FM_FLAG (1<<2)
#define VIA_REG_SGD_STAT_PB_EOL (1<<4)
#define VIA_REG_SGD_STAT_CP_EOL (1<<5)
#define VIA_REG_SGD_STAT_FM_EOL (1<<6)
#define VIA_REG_SGD_STAT_PB_STOP (1<<8)
#define VIA_REG_SGD_STAT_CP_STOP (1<<9)
#define VIA_REG_SGD_STAT_FM_STOP (1<<10)
#define VIA_REG_SGD_STAT_PB_ACTIVE (1<<12)
#define VIA_REG_SGD_STAT_CP_ACTIVE (1<<13)
#define VIA_REG_SGD_STAT_FM_ACTIVE (1<<14)
/* via8233 */
#define VIA8233_REG_SGD_STAT_FLAG (1<<0)
#define VIA8233_REG_SGD_STAT_EOL (1<<1)
#define VIA8233_REG_SGD_STAT_STOP (1<<2)
#define VIA8233_REG_SGD_STAT_ACTIVE (1<<3)
#define VIA8233_INTR_MASK(chan) ((VIA8233_REG_SGD_STAT_FLAG|VIA8233_REG_SGD_STAT_EOL) << ((chan) * 4))
#define VIA8233_REG_SGD_CHAN_SDX 0
#define VIA8233_REG_SGD_CHAN_MULTI 4
#define VIA8233_REG_SGD_CHAN_REC 6
#define VIA8233_REG_SGD_CHAN_REC1 7
#define VIA_REG_GPI_STATUS 0x88
#define VIA_REG_GPI_INTR 0x8c
/* multi-channel and capture registers for via8233 */
DEFINE_VIA_REGSET(MULTPLAY, 0x40);
DEFINE_VIA_REGSET(CAPTURE_8233, 0x60);
/* via8233-specific registers */
#define VIA_REG_OFS_PLAYBACK_VOLUME_L 0x02 /* byte */
#define VIA_REG_OFS_PLAYBACK_VOLUME_R 0x03 /* byte */
#define VIA_REG_OFS_MULTPLAY_FORMAT 0x02 /* byte - format and channels */
#define VIA_REG_MULTPLAY_FMT_8BIT 0x00
#define VIA_REG_MULTPLAY_FMT_16BIT 0x80
#define VIA_REG_MULTPLAY_FMT_CH_MASK 0x70 /* # channels << 4 (valid = 1,2,4,6) */
#define VIA_REG_OFS_CAPTURE_FIFO 0x02 /* byte - bit 6 = fifo enable */
#define VIA_REG_CAPTURE_FIFO_ENABLE 0x40
#define VIA_DXS_MAX_VOLUME 31 /* max. volume (attenuation) of reg 0x32/33 */
#define VIA_REG_CAPTURE_CHANNEL 0x63 /* byte - input select */
#define VIA_REG_CAPTURE_CHANNEL_MIC 0x4
#define VIA_REG_CAPTURE_CHANNEL_LINE 0
#define VIA_REG_CAPTURE_SELECT_CODEC 0x03 /* recording source codec (0 = primary) */
#define VIA_TBL_BIT_FLAG 0x40000000
#define VIA_TBL_BIT_EOL 0x80000000
/* pci space */
#define VIA_ACLINK_STAT 0x40
#define VIA_ACLINK_C11_READY 0x20
#define VIA_ACLINK_C10_READY 0x10
#define VIA_ACLINK_C01_READY 0x04 /* secondary codec ready */
#define VIA_ACLINK_LOWPOWER 0x02 /* low-power state */
#define VIA_ACLINK_C00_READY 0x01 /* primary codec ready */
#define VIA_ACLINK_CTRL 0x41
#define VIA_ACLINK_CTRL_ENABLE 0x80 /* 0: disable, 1: enable */
#define VIA_ACLINK_CTRL_RESET 0x40 /* 0: assert, 1: de-assert */
#define VIA_ACLINK_CTRL_SYNC 0x20 /* 0: release SYNC, 1: force SYNC hi */
#define VIA_ACLINK_CTRL_SDO 0x10 /* 0: release SDO, 1: force SDO hi */
#define VIA_ACLINK_CTRL_VRA 0x08 /* 0: disable VRA, 1: enable VRA */
#define VIA_ACLINK_CTRL_PCM 0x04 /* 0: disable PCM, 1: enable PCM */
#define VIA_ACLINK_CTRL_FM 0x02 /* via686 only */
#define VIA_ACLINK_CTRL_SB 0x01 /* via686 only */
#define VIA_ACLINK_CTRL_INIT (VIA_ACLINK_CTRL_ENABLE|\
VIA_ACLINK_CTRL_RESET|\
VIA_ACLINK_CTRL_PCM|\
VIA_ACLINK_CTRL_VRA)
#define VIA_FUNC_ENABLE 0x42
#define VIA_FUNC_MIDI_PNP 0x80 /* FIXME: it's 0x40 in the datasheet! */
#define VIA_FUNC_MIDI_IRQMASK 0x40 /* FIXME: not documented! */
#define VIA_FUNC_RX2C_WRITE 0x20
#define VIA_FUNC_SB_FIFO_EMPTY 0x10
#define VIA_FUNC_ENABLE_GAME 0x08
#define VIA_FUNC_ENABLE_FM 0x04
#define VIA_FUNC_ENABLE_MIDI 0x02
#define VIA_FUNC_ENABLE_SB 0x01
#define VIA_PNP_CONTROL 0x43
#define VIA_FM_NMI_CTRL 0x48
#define VIA8233_VOLCHG_CTRL 0x48
#define VIA8233_SPDIF_CTRL 0x49
#define VIA8233_SPDIF_DX3 0x08
#define VIA8233_SPDIF_SLOT_MASK 0x03
#define VIA8233_SPDIF_SLOT_1011 0x00
#define VIA8233_SPDIF_SLOT_34 0x01
#define VIA8233_SPDIF_SLOT_78 0x02
#define VIA8233_SPDIF_SLOT_69 0x03
/*
*/
#define VIA_DXS_AUTO 0
#define VIA_DXS_ENABLE 1
#define VIA_DXS_DISABLE 2
#define VIA_DXS_48K 3
#define VIA_DXS_NO_VRA 4
#define VIA_DXS_SRC 5
/*
* pcm stream
*/
struct snd_via_sg_table {
unsigned int offset;
unsigned int size;
} ;
#define VIA_TABLE_SIZE 255
struct viadev {
unsigned int reg_offset;
unsigned long port;
int direction; /* playback = 0, capture = 1 */
struct snd_pcm_substream *substream;
int running;
unsigned int tbl_entries; /* # descriptors */
struct snd_dma_buffer table;
struct snd_via_sg_table *idx_table;
/* for recovery from the unexpected pointer */
unsigned int lastpos;
unsigned int fragsize;
unsigned int bufsize;
unsigned int bufsize2;
int hwptr_done; /* processed frame position in the buffer */
int in_interrupt;
int shadow_shift;
};
enum { TYPE_CARD_VIA686 = 1, TYPE_CARD_VIA8233 };
enum { TYPE_VIA686, TYPE_VIA8233, TYPE_VIA8233A };
#define VIA_MAX_DEVS 7 /* 4 playback, 1 multi, 2 capture */
struct via_rate_lock {
spinlock_t lock;
int rate;
int used;
};
struct via82xx {
int irq;
unsigned long port;
struct resource *mpu_res;
int chip_type;
unsigned char revision;
unsigned char old_legacy;
unsigned char old_legacy_cfg;
#ifdef CONFIG_PM
unsigned char legacy_saved;
unsigned char legacy_cfg_saved;
unsigned char spdif_ctrl_saved;
unsigned char capture_src_saved[2];
unsigned int mpu_port_saved;
#endif
unsigned char playback_volume[4][2]; /* for VIA8233/C/8235; default = 0 */
unsigned char playback_volume_c[2]; /* for VIA8233/C/8235; default = 0 */
unsigned int intr_mask; /* SGD_SHADOW mask to check interrupts */
struct pci_dev *pci;
struct snd_card *card;
unsigned int num_devs;
unsigned int playback_devno, multi_devno, capture_devno;
struct viadev devs[VIA_MAX_DEVS];
struct via_rate_lock rates[2]; /* playback and capture */
unsigned int dxs_fixed: 1; /* DXS channel accepts only 48kHz */
unsigned int no_vra: 1; /* no need to set VRA on DXS channels */
unsigned int dxs_src: 1; /* use full SRC capabilities of DXS */
unsigned int spdif_on: 1; /* only spdif rates work to external DACs */
struct snd_pcm *pcms[2];
struct snd_rawmidi *rmidi;
struct snd_ac97_bus *ac97_bus;
struct snd_ac97 *ac97;
unsigned int ac97_clock;
unsigned int ac97_secondary; /* secondary AC'97 codec is present */
spinlock_t reg_lock;
struct snd_info_entry *proc_entry;