📄 ice1724.c
字号:
/* * ALSA driver for VT1724 ICEnsemble ICE1724 / VIA VT1724 (Envy24HT) * VIA VT1720 (Envy24PT) * * Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz> * 2002 James Stafford <jstafford@ampltd.com> * 2003 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 * */ #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/moduleparam.h>#include <sound/core.h>#include <sound/info.h>#include <sound/mpu401.h>#include <sound/initval.h>#include <sound/asoundef.h>#include "ice1712.h"#include "envy24ht.h"/* lowlevel routines */#include "amp.h"#include "revo.h"#include "aureon.h"#include "vt1720_mobo.h"#include "pontis.h"#include "prodigy192.h"MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");MODULE_DESCRIPTION("VIA ICEnsemble ICE1724/1720 (Envy24HT/PT)");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("{" REVO_DEVICE_DESC AMP_AUDIO2000_DEVICE_DESC AUREON_DEVICE_DESC VT1720_MOBO_DEVICE_DESC PONTIS_DEVICE_DESC PRODIGY192_DEVICE_DESC "{VIA,VT1720}," "{VIA,VT1724}," "{ICEnsemble,Generic ICE1724}," "{ICEnsemble,Generic Envy24HT}" "{ICEnsemble,Generic Envy24PT}}");static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */static char *model[SNDRV_CARDS];module_param_array(index, int, NULL, 0444);MODULE_PARM_DESC(index, "Index value for ICE1724 soundcard.");module_param_array(id, charp, NULL, 0444);MODULE_PARM_DESC(id, "ID string for ICE1724 soundcard.");module_param_array(enable, bool, NULL, 0444);MODULE_PARM_DESC(enable, "Enable ICE1724 soundcard.");module_param_array(model, charp, NULL, 0444);MODULE_PARM_DESC(model, "Use the given board model.");#ifndef PCI_VENDOR_ID_ICE#define PCI_VENDOR_ID_ICE 0x1412#endif#ifndef PCI_DEVICE_ID_VT1724#define PCI_DEVICE_ID_VT1724 0x1724#endif/* Both VT1720 and VT1724 have the same PCI IDs */static struct pci_device_id snd_vt1724_ids[] = { { PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_VT1724, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { 0, }};MODULE_DEVICE_TABLE(pci, snd_vt1724_ids);static int PRO_RATE_LOCKED;static int PRO_RATE_RESET = 1;static unsigned int PRO_RATE_DEFAULT = 44100;/* * Basic I/O */ /* check whether the clock mode is spdif-in */static inline int is_spdif_master(ice1712_t *ice){ return (inb(ICEMT1724(ice, RATE)) & VT1724_SPDIF_MASTER) ? 1 : 0;}static inline int is_pro_rate_locked(ice1712_t *ice){ return is_spdif_master(ice) || PRO_RATE_LOCKED;}/* * ac97 section */static unsigned char snd_vt1724_ac97_ready(ice1712_t *ice){ unsigned char old_cmd; int tm; for (tm = 0; tm < 0x10000; tm++) { old_cmd = inb(ICEMT1724(ice, AC97_CMD)); if (old_cmd & (VT1724_AC97_WRITE | VT1724_AC97_READ)) continue; if (!(old_cmd & VT1724_AC97_READY)) continue; return old_cmd; } snd_printd(KERN_ERR "snd_vt1724_ac97_ready: timeout\n"); return old_cmd;}static int snd_vt1724_ac97_wait_bit(ice1712_t *ice, unsigned char bit){ int tm; for (tm = 0; tm < 0x10000; tm++) if ((inb(ICEMT1724(ice, AC97_CMD)) & bit) == 0) return 0; snd_printd(KERN_ERR "snd_vt1724_ac97_wait_bit: timeout\n"); return -EIO;}static void snd_vt1724_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val){ ice1712_t *ice = (ice1712_t *)ac97->private_data; unsigned char old_cmd; old_cmd = snd_vt1724_ac97_ready(ice); old_cmd &= ~VT1724_AC97_ID_MASK; old_cmd |= ac97->num; outb(reg, ICEMT1724(ice, AC97_INDEX)); outw(val, ICEMT1724(ice, AC97_DATA)); outb(old_cmd | VT1724_AC97_WRITE, ICEMT1724(ice, AC97_CMD)); snd_vt1724_ac97_wait_bit(ice, VT1724_AC97_WRITE);}static unsigned short snd_vt1724_ac97_read(ac97_t *ac97, unsigned short reg){ ice1712_t *ice = (ice1712_t *)ac97->private_data; unsigned char old_cmd; old_cmd = snd_vt1724_ac97_ready(ice); old_cmd &= ~VT1724_AC97_ID_MASK; old_cmd |= ac97->num; outb(reg, ICEMT1724(ice, AC97_INDEX)); outb(old_cmd | VT1724_AC97_READ, ICEMT1724(ice, AC97_CMD)); if (snd_vt1724_ac97_wait_bit(ice, VT1724_AC97_READ) < 0) return ~0; return inw(ICEMT1724(ice, AC97_DATA));}/* * GPIO operations *//* set gpio direction 0 = read, 1 = write */static void snd_vt1724_set_gpio_dir(ice1712_t *ice, unsigned int data){ outl(data, ICEREG1724(ice, GPIO_DIRECTION)); inw(ICEREG1724(ice, GPIO_DIRECTION)); /* dummy read for pci-posting */}/* set the gpio mask (0 = writable) */static void snd_vt1724_set_gpio_mask(ice1712_t *ice, unsigned int data){ outw(data, ICEREG1724(ice, GPIO_WRITE_MASK)); if (! ice->vt1720) /* VT1720 supports only 16 GPIO bits */ outb((data >> 16) & 0xff, ICEREG1724(ice, GPIO_WRITE_MASK_22)); inw(ICEREG1724(ice, GPIO_WRITE_MASK)); /* dummy read for pci-posting */}static void snd_vt1724_set_gpio_data(ice1712_t *ice, unsigned int data){ outw(data, ICEREG1724(ice, GPIO_DATA)); if (! ice->vt1720) outb(data >> 16, ICEREG1724(ice, GPIO_DATA_22)); inw(ICEREG1724(ice, GPIO_DATA)); /* dummy read for pci-posting */}static unsigned int snd_vt1724_get_gpio_data(ice1712_t *ice){ unsigned int data; if (! ice->vt1720) data = (unsigned int)inb(ICEREG1724(ice, GPIO_DATA_22)); else data = 0; data = (data << 16) | inw(ICEREG1724(ice, GPIO_DATA)); return data;}/* * Interrupt handler */static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id, struct pt_regs *regs){ ice1712_t *ice = dev_id; unsigned char status; int handled = 0; while (1) { status = inb(ICEREG1724(ice, IRQSTAT)); if (status == 0) break; handled = 1; /* these should probably be separated at some point, but as we don't currently have MPU support on the board I will leave it */ if ((status & VT1724_IRQ_MPU_RX)||(status & VT1724_IRQ_MPU_TX)) { if (ice->rmidi[0]) snd_mpu401_uart_interrupt(irq, ice->rmidi[0]->private_data, regs); outb(status & (VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX), ICEREG1724(ice, IRQSTAT)); status &= ~(VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX); } if (status & VT1724_IRQ_MTPCM) { /* * Multi-track PCM * PCM assignment are: * Playback DMA0 (M/C) = playback_pro_substream * Playback DMA1 = playback_con_substream_ds[0] * Playback DMA2 = playback_con_substream_ds[1] * Playback DMA3 = playback_con_substream_ds[2] * Playback DMA4 (SPDIF) = playback_con_substream * Record DMA0 = capture_pro_substream * Record DMA1 = capture_con_substream */ unsigned char mtstat = inb(ICEMT1724(ice, IRQ)); if (mtstat & VT1724_MULTI_PDMA0) { if (ice->playback_pro_substream) snd_pcm_period_elapsed(ice->playback_pro_substream); } if (mtstat & VT1724_MULTI_RDMA0) { if (ice->capture_pro_substream) snd_pcm_period_elapsed(ice->capture_pro_substream); } if (mtstat & VT1724_MULTI_PDMA1) { if (ice->playback_con_substream_ds[0]) snd_pcm_period_elapsed(ice->playback_con_substream_ds[0]); } if (mtstat & VT1724_MULTI_PDMA2) { if (ice->playback_con_substream_ds[1]) snd_pcm_period_elapsed(ice->playback_con_substream_ds[1]); } if (mtstat & VT1724_MULTI_PDMA3) { if (ice->playback_con_substream_ds[2]) snd_pcm_period_elapsed(ice->playback_con_substream_ds[2]); } if (mtstat & VT1724_MULTI_PDMA4) { if (ice->playback_con_substream) snd_pcm_period_elapsed(ice->playback_con_substream); } if (mtstat & VT1724_MULTI_RDMA1) { if (ice->capture_con_substream) snd_pcm_period_elapsed(ice->capture_con_substream); } /* ack anyway to avoid freeze */ outb(mtstat, ICEMT1724(ice, IRQ)); /* ought to really handle this properly */ if (mtstat & VT1724_MULTI_FIFO_ERR) { unsigned char fstat = inb(ICEMT1724(ice, DMA_FIFO_ERR)); outb(fstat, ICEMT1724(ice, DMA_FIFO_ERR)); outb(VT1724_MULTI_FIFO_ERR | inb(ICEMT1724(ice, DMA_INT_MASK)), ICEMT1724(ice, DMA_INT_MASK)); /* If I don't do this, I get machine lockup due to continual interrupts */ } } } return IRQ_RETVAL(handled);}/* * PCM code - professional part (multitrack) */static unsigned int rates[] = { 8000, 9600, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000,};static snd_pcm_hw_constraint_list_t hw_constraints_rates_96 = { .count = ARRAY_SIZE(rates) - 2, /* up to 96000 */ .list = rates, .mask = 0,};static snd_pcm_hw_constraint_list_t hw_constraints_rates_48 = { .count = ARRAY_SIZE(rates) - 5, /* up to 48000 */ .list = rates, .mask = 0,};static snd_pcm_hw_constraint_list_t hw_constraints_rates_192 = { .count = ARRAY_SIZE(rates), .list = rates, .mask = 0,};struct vt1724_pcm_reg { unsigned int addr; /* ADDR register offset */ unsigned int size; /* SIZE register offset */ unsigned int count; /* COUNT register offset */ unsigned int start; /* start & pause bit */};static int snd_vt1724_pcm_trigger(snd_pcm_substream_t *substream, int cmd){ ice1712_t *ice = snd_pcm_substream_chip(substream); unsigned char what; unsigned char old; struct list_head *pos; snd_pcm_substream_t *s; what = 0; snd_pcm_group_for_each(pos, substream) { struct vt1724_pcm_reg *reg; s = snd_pcm_group_substream_entry(pos); reg = s->runtime->private_data; what |= reg->start; snd_pcm_trigger_done(s, substream); } switch (cmd) { case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: spin_lock(&ice->reg_lock); old = inb(ICEMT1724(ice, DMA_PAUSE)); if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) old |= what; else old &= ~what; outb(old, ICEMT1724(ice, DMA_PAUSE)); spin_unlock(&ice->reg_lock); break; case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_STOP: spin_lock(&ice->reg_lock); old = inb(ICEMT1724(ice, DMA_CONTROL)); if (cmd == SNDRV_PCM_TRIGGER_START) old |= what; else old &= ~what; outb(old, ICEMT1724(ice, DMA_CONTROL)); spin_unlock(&ice->reg_lock); break; default: return -EINVAL; } return 0;}/* */#define DMA_STARTS (VT1724_RDMA0_START|VT1724_PDMA0_START|VT1724_RDMA1_START|\ VT1724_PDMA1_START|VT1724_PDMA2_START|VT1724_PDMA3_START|VT1724_PDMA4_START)#define DMA_PAUSES (VT1724_RDMA0_PAUSE|VT1724_PDMA0_PAUSE|VT1724_RDMA1_PAUSE|\ VT1724_PDMA1_PAUSE|VT1724_PDMA2_PAUSE|VT1724_PDMA3_PAUSE|VT1724_PDMA4_PAUSE)static int get_max_rate(ice1712_t *ice){ if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) { if ((ice->eeprom.data[ICE_EEP2_I2S] & 0x08) && !ice->vt1720) return 192000; else return 96000; } else return 48000;}static void snd_vt1724_set_pro_rate(ice1712_t *ice, unsigned int rate, int force){ unsigned long flags; unsigned char val, old; unsigned int i; if (rate > get_max_rate(ice)) return; spin_lock_irqsave(&ice->reg_lock, flags); if ((inb(ICEMT1724(ice, DMA_CONTROL)) & DMA_STARTS) || (inb(ICEMT1724(ice, DMA_PAUSE)) & DMA_PAUSES)) { /* running? we cannot change the rate now... */ spin_unlock_irqrestore(&ice->reg_lock, flags); return; } if (!force && is_pro_rate_locked(ice)) { spin_unlock_irqrestore(&ice->reg_lock, flags); return; } switch (rate) { case 8000: val = 6; break; case 9600: val = 3; break; case 11025: val = 10; break; case 12000: val = 2; break; case 16000: val = 5; break; case 22050: val = 9; break; case 24000: val = 1; break; case 32000: val = 4; break; case 44100: val = 8; break; case 48000: val = 0; break; case 64000: val = 15; break; case 88200: val = 11; break; case 96000: val = 7; break; case 176400: val = 12; break; case 192000: val = 14; break; default: snd_BUG(); val = 0; break; } old = inb(ICEMT1724(ice, RATE)); if (old != val) outb(val, ICEMT1724(ice, RATE)); else if (rate == ice->cur_rate) { spin_unlock_irqrestore(&ice->reg_lock, flags); return; } ice->cur_rate = rate; /* check MT02 */ if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) { val = old = inb(ICEMT1724(ice, I2S_FORMAT)); if (rate > 96000) val |= VT1724_MT_I2S_MCLK_128X; /* 128x MCLK */ else val &= ~VT1724_MT_I2S_MCLK_128X; /* 256x MCLK */ if (val != old) { outb(val, ICEMT1724(ice, I2S_FORMAT)); if (ice->eeprom.subvendor == VT1724_SUBDEVICE_REVOLUTION71) { /* FIXME: is this revo only? */ /* assert PRST# to converters; MT05 bit 7 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -