📄 i810_audio.c
字号:
type val; \ if (card->use_mmio) \ val=read##size(card->iobase_mmio+off); \ else \ val=in##size(card->iobase+off); \ val; \})#define I810_IOREADL(card, off) I810_IOREAD(l, u32, card, off)#define I810_IOREADW(card, off) I810_IOREAD(w, u16, card, off)#define I810_IOREADB(card, off) I810_IOREAD(b, u8, card, off)#define I810_IOWRITE(size, val, card, off) \({ \ if (card->use_mmio) \ write##size(val, card->iobase_mmio+off); \ else \ out##size(val, card->iobase+off); \})#define I810_IOWRITEL(val, card, off) I810_IOWRITE(l, val, card, off)#define I810_IOWRITEW(val, card, off) I810_IOWRITE(w, val, card, off)#define I810_IOWRITEB(val, card, off) I810_IOWRITE(b, val, card, off)#define GET_CIV(card, port) MODULOP2(I810_IOREADB((card), (port) + OFF_CIV), SG_LEN)#define GET_LVI(card, port) MODULOP2(I810_IOREADB((card), (port) + OFF_LVI), SG_LEN)/* set LVI from CIV */#define CIV_TO_LVI(card, port, off) \ I810_IOWRITEB(MODULOP2(GET_CIV((card), (port)) + (off), SG_LEN), (card), (port) + OFF_LVI)static struct ac97_quirk ac97_quirks[] __devinitdata = { { .vendor = 0x0e11, .device = 0x00b8, .name = "Compaq Evo D510C", .type = AC97_TUNE_HP_ONLY }, { .vendor = 0x1028, .device = 0x00d8, .name = "Dell Precision 530", /* AD1885 */ .type = AC97_TUNE_HP_ONLY }, { .vendor = 0x1028, .device = 0x0126, .name = "Dell Optiplex GX260", /* AD1981A */ .type = AC97_TUNE_HP_ONLY }, { .vendor = 0x1028, .device = 0x012d, .name = "Dell Precision 450", /* AD1981B*/ .type = AC97_TUNE_HP_ONLY }, { /* FIXME: which codec? */ .vendor = 0x103c, .device = 0x00c3, .name = "Hewlett-Packard onboard", .type = AC97_TUNE_HP_ONLY }, { .vendor = 0x103c, .device = 0x12f1, .name = "HP xw8200", /* AD1981B*/ .type = AC97_TUNE_HP_ONLY }, { .vendor = 0x103c, .device = 0x3008, .name = "HP xw4200", /* AD1981B*/ .type = AC97_TUNE_HP_ONLY }, { .vendor = 0x10f1, .device = 0x2665, .name = "Fujitsu-Siemens Celsius", /* AD1981? */ .type = AC97_TUNE_HP_ONLY }, { .vendor = 0x10f1, .device = 0x2885, .name = "AMD64 Mobo", /* ALC650 */ .type = AC97_TUNE_HP_ONLY }, { .vendor = 0x110a, .device = 0x0056, .name = "Fujitsu-Siemens Scenic", /* AD1981? */ .type = AC97_TUNE_HP_ONLY }, { .vendor = 0x11d4, .device = 0x5375, .name = "ADI AD1985 (discrete)", .type = AC97_TUNE_HP_ONLY }, { .vendor = 0x1462, .device = 0x5470, .name = "MSI P4 ATX 645 Ultra", .type = AC97_TUNE_HP_ONLY }, { .vendor = 0x1734, .device = 0x0088, .name = "Fujitsu-Siemens D1522", /* AD1981 */ .type = AC97_TUNE_HP_ONLY }, { .vendor = 0x8086, .device = 0x4856, .name = "Intel D845WN (82801BA)", .type = AC97_TUNE_SWAP_HP }, { .vendor = 0x8086, .device = 0x4d44, .name = "Intel D850EMV2", /* AD1885 */ .type = AC97_TUNE_HP_ONLY }, { .vendor = 0x8086, .device = 0x4d56, .name = "Intel ICH/AD1885", .type = AC97_TUNE_HP_ONLY }, { .vendor = 0x1028, .device = 0x012d, .name = "Dell Precision 450", /* AD1981B*/ .type = AC97_TUNE_HP_ONLY }, { .vendor = 0x103c, .device = 0x3008, .name = "HP xw4200", /* AD1981B*/ .type = AC97_TUNE_HP_ONLY }, { .vendor = 0x103c, .device = 0x12f1, .name = "HP xw8200", /* AD1981B*/ .type = AC97_TUNE_HP_ONLY }, { } /* terminator */};static struct i810_card *devs = NULL;static int i810_open_mixdev(struct inode *inode, struct file *file);static int i810_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg);static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data);static u16 i810_ac97_get_mmio(struct ac97_codec *dev, u8 reg);static void i810_ac97_set_mmio(struct ac97_codec *dev, u8 reg, u16 data);static u16 i810_ac97_get_io(struct ac97_codec *dev, u8 reg);static void i810_ac97_set_io(struct ac97_codec *dev, u8 reg, u16 data);static struct i810_channel *i810_alloc_pcm_channel(struct i810_card *card){ if(card->channel[1].used==1) return NULL; card->channel[1].used=1; return &card->channel[1];}static struct i810_channel *i810_alloc_rec_pcm_channel(struct i810_card *card){ if(card->channel[0].used==1) return NULL; card->channel[0].used=1; return &card->channel[0];}static struct i810_channel *i810_alloc_rec_mic_channel(struct i810_card *card){ if(card->channel[2].used==1) return NULL; card->channel[2].used=1; return &card->channel[2];}static void i810_free_pcm_channel(struct i810_card *card, int channel){ card->channel[channel].used=0;}static int i810_valid_spdif_rate ( struct ac97_codec *codec, int rate ){ unsigned long id = 0L; id = (i810_ac97_get(codec, AC97_VENDOR_ID1) << 16); id |= i810_ac97_get(codec, AC97_VENDOR_ID2) & 0xffff;#ifdef DEBUG printk ( "i810_audio: codec = %s, codec_id = 0x%08lx\n", codec->name, id);#endif switch ( id ) { case 0x41445361: /* AD1886 */ if (rate == 48000) { return 1; } break; default: /* all other codecs, until we know otherwiae */ if (rate == 48000 || rate == 44100 || rate == 32000) { return 1; } break; } return (0);}/* i810_set_spdif_output * * Configure the S/PDIF output transmitter. When we turn on * S/PDIF, we turn off the analog output. This may not be * the right thing to do. * * Assumptions: * The DSP sample rate must already be set to a supported * S/PDIF rate (32kHz, 44.1kHz, or 48kHz) or we abort. */static int i810_set_spdif_output(struct i810_state *state, int slots, int rate){ int vol; int aud_reg; int r = 0; struct ac97_codec *codec = state->card->ac97_codec[0]; if(!codec->codec_ops->digital) { state->card->ac97_status &= ~SPDIF_ON; } else { if ( slots == -1 ) { /* Turn off S/PDIF */ codec->codec_ops->digital(codec, 0, 0, 0); /* If the volume wasn't muted before we turned on S/PDIF, unmute it */ if ( !(state->card->ac97_status & VOL_MUTED) ) { aud_reg = i810_ac97_get(codec, AC97_MASTER_VOL_STEREO); i810_ac97_set(codec, AC97_MASTER_VOL_STEREO, (aud_reg & ~VOL_MUTED)); } state->card->ac97_status &= ~(VOL_MUTED | SPDIF_ON); return 0; } vol = i810_ac97_get(codec, AC97_MASTER_VOL_STEREO); state->card->ac97_status = vol & VOL_MUTED; r = codec->codec_ops->digital(codec, slots, rate, 0); if(r) state->card->ac97_status |= SPDIF_ON; else state->card->ac97_status &= ~SPDIF_ON; /* Mute the analog output */ /* Should this only mute the PCM volume??? */ i810_ac97_set(codec, AC97_MASTER_VOL_STEREO, (vol | VOL_MUTED)); } return r;}/* i810_set_dac_channels * * Configure the codec's multi-channel DACs * * The logic is backwards. Setting the bit to 1 turns off the DAC. * * What about the ICH? We currently configure it using the * SNDCTL_DSP_CHANNELS ioctl. If we're turnning on the DAC, * does that imply that we want the ICH set to support * these channels? * * TODO: * vailidate that the codec really supports these DACs * before turning them on. */static void i810_set_dac_channels(struct i810_state *state, int channel){ int aud_reg; struct ac97_codec *codec = state->card->ac97_codec[0]; /* No codec, no setup */ if(codec == NULL) return; aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); aud_reg |= AC97_EA_PRI | AC97_EA_PRJ | AC97_EA_PRK; state->card->ac97_status &= ~(SURR_ON | CENTER_LFE_ON); switch ( channel ) { case 2: /* always enabled */ break; case 4: aud_reg &= ~AC97_EA_PRJ; state->card->ac97_status |= SURR_ON; break; case 6: aud_reg &= ~(AC97_EA_PRJ | AC97_EA_PRI | AC97_EA_PRK); state->card->ac97_status |= SURR_ON | CENTER_LFE_ON; break; default: break; } i810_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg);}/* set playback sample rate */static unsigned int i810_set_dac_rate(struct i810_state * state, unsigned int rate){ struct dmabuf *dmabuf = &state->dmabuf; u32 new_rate; struct ac97_codec *codec=state->card->ac97_codec[0]; if(!(state->card->ac97_features&0x0001)) { dmabuf->rate = clocking;#ifdef DEBUG printk("Asked for %d Hz, but ac97_features says we only do %dHz. Sorry!\n", rate,clocking);#endif return clocking; } if (rate > 48000) rate = 48000; if (rate < 8000) rate = 8000; dmabuf->rate = rate; /* * Adjust for misclocked crap */ rate = ( rate * clocking)/48000; if(strict_clocking && rate < 8000) { rate = 8000; dmabuf->rate = (rate * 48000)/clocking; } new_rate=ac97_set_dac_rate(codec, rate); if(new_rate != rate) { dmabuf->rate = (new_rate * 48000)/clocking; }#ifdef DEBUG printk("i810_audio: called i810_set_dac_rate : asked for %d, got %d\n", rate, dmabuf->rate);#endif rate = new_rate; return dmabuf->rate;}/* set recording sample rate */static unsigned int i810_set_adc_rate(struct i810_state * state, unsigned int rate){ struct dmabuf *dmabuf = &state->dmabuf; u32 new_rate; struct ac97_codec *codec=state->card->ac97_codec[0]; if(!(state->card->ac97_features&0x0001)) { dmabuf->rate = clocking; return clocking; } if (rate > 48000) rate = 48000; if (rate < 8000) rate = 8000; dmabuf->rate = rate; /* * Adjust for misclocked crap */ rate = ( rate * clocking)/48000; if(strict_clocking && rate < 8000) { rate = 8000; dmabuf->rate = (rate * 48000)/clocking; } new_rate = ac97_set_adc_rate(codec, rate); if(new_rate != rate) { dmabuf->rate = (new_rate * 48000)/clocking; rate = new_rate; }#ifdef DEBUG printk("i810_audio: called i810_set_adc_rate : rate = %d/%d\n", dmabuf->rate, rate);#endif return dmabuf->rate;}/* get current playback/recording dma buffer pointer (byte offset from LBA), called with spinlock held! */ static inline unsigned i810_get_dma_addr(struct i810_state *state, int rec){ struct dmabuf *dmabuf = &state->dmabuf; unsigned int civ, offset, port, port_picb, bytes = 2; if (!dmabuf->enable) return 0; if (rec) port = dmabuf->read_channel->port; else port = dmabuf->write_channel->port; if(state->card->pci_id == PCI_DEVICE_ID_SI_7012) { port_picb = port + OFF_SR; bytes = 1; } else port_picb = port + OFF_PICB; do { civ = GET_CIV(state->card, port); offset = I810_IOREADW(state->card, port_picb); /* Must have a delay here! */ if(offset == 0) udelay(1); /* Reread both registers and make sure that that total * offset from the first reading to the second is 0. * There is an issue with SiS hardware where it will count * picb down to 0, then update civ to the next value, * then set the new picb to fragsize bytes. We can catch * it between the civ update and the picb update, making * it look as though we are 1 fragsize ahead of where we * are. The next to we get the address though, it will * be back in the right place, and we will suddenly think * we just went forward dmasize - fragsize bytes, causing * totally stupid *huge* dma overrun messages. We are * assuming that the 1us delay is more than long enough * that we won't have to worry about the chip still being * out of sync with reality ;-) */ } while (civ != GET_CIV(state->card, port) || offset != I810_IOREADW(state->card, port_picb)); return (((civ + 1) * dmabuf->fragsize - (bytes * offset)) % dmabuf->dmasize);}/* Stop recording (lock held) */static inline void __stop_adc(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; struct i810_card *card = state->card; dmabuf->enable &= ~ADC_RUNNING; I810_IOWRITEB(0, card, PI_CR); // wait for the card to acknowledge shutdown while( I810_IOREADB(card, PI_CR) != 0 ) ; // now clear any latent interrupt bits (like the halt bit)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -