📄 via82xx.c
字号:
/* * 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 <sound/driver.h>#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#endifMODULE_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#endifstatic int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */static long mpu_port;#ifdef SUPPORT_JOYSTICKstatic int joystick;#endifstatic 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_JOYSTICKmodule_param(joystick, bool, 0444);MODULE_PARM_DESC(joystick, "Enable joystick. (VT82C686x only)");#endifmodule_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 255struct 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;#ifdef SUPPORT_JOYSTICK struct gameport *gameport;#endif};static struct pci_device_id snd_via82xx_ids[] = { /* 0x1106, 0x3058 */ { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA686, }, /* 686A */ /* 0x1106, 0x3059 */ { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8233_5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA8233, }, /* VT8233 */ { 0, }};MODULE_DEVICE_TABLE(pci, snd_via82xx_ids);/* *//* * allocate and initialize the descriptor buffers * periods = number of periods * fragsize = period size in bytes */static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substream, struct pci_dev *pci, unsigned int periods, unsigned int fragsize){ unsigned int i, idx, ofs, rest; struct via82xx *chip = snd_pcm_substream_chip(substream); struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream); if (dev->table.area == NULL) { /* the start of each lists must be aligned to 8 bytes, * but the kernel pages are much bigger, so we don't care */ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8), &dev->table) < 0) return -ENOMEM; } if (! dev->idx_table) { dev->idx_table = kmalloc(sizeof(*dev->idx_table) * VIA_TABLE_SIZE, GFP_KERNEL); if (! dev->idx_table) return -ENOMEM;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -