📄 maestro.c
字号:
maestro_ac97_set(iobase, 0x1C, 0x0000); maestro_ac97_set(iobase, 0x02, 0x0404); maestro_ac97_set(iobase, 0x04, 0x0808); maestro_ac97_set(iobase, 0x0C, 0x801F); maestro_ac97_set(iobase, 0x0E, 0x801F); return 0;}#endif/* this is very magic, and very slow.. */static void maestro_ac97_reset(int ioaddr, struct pci_dev *pcidev){ u16 save_68; u16 w; u32 vend; outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); outw( inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); /* reset the first codec */ outw(0x0000, ioaddr+0x36); save_68 = inw(ioaddr+0x68); pci_read_config_word(pcidev, 0x58, &w); /* something magical with gpio and bus arb. */ pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &vend); if( w & 0x1) save_68 |= 0x10; outw(0xfffe, ioaddr + 0x64); /* tickly gpio 0.. */ outw(0x0001, ioaddr + 0x68); outw(0x0000, ioaddr + 0x60); udelay(20); outw(0x0001, ioaddr + 0x60); mdelay(20); outw(save_68 | 0x1, ioaddr + 0x68); /* now restore .. */ outw( (inw(ioaddr + 0x38) & 0xfffc)|0x1, ioaddr + 0x38); outw( (inw(ioaddr + 0x3a) & 0xfffc)|0x1, ioaddr + 0x3a); outw( (inw(ioaddr + 0x3c) & 0xfffc)|0x1, ioaddr + 0x3c); /* now the second codec */ outw(0x0000, ioaddr+0x36); outw(0xfff7, ioaddr + 0x64); save_68 = inw(ioaddr+0x68); outw(0x0009, ioaddr + 0x68); outw(0x0001, ioaddr + 0x60); udelay(20); outw(0x0009, ioaddr + 0x60); mdelay(500); /* .. ouch.. */ outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); outw( inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c);#if 0 /* the loop here needs to be much better if we want it.. */ M_printk("trying software reset\n"); /* try and do a software reset */ outb(0x80|0x7c, ioaddr + 0x30); for (w=0; ; w++) { if ((inw(ioaddr+ 0x30) & 1) == 0) { if(inb(ioaddr + 0x32) !=0) break; outb(0x80|0x7d, ioaddr + 0x30); if (((inw(ioaddr+ 0x30) & 1) == 0) && (inb(ioaddr + 0x32) !=0)) break; outb(0x80|0x7f, ioaddr + 0x30); if (((inw(ioaddr+ 0x30) & 1) == 0) && (inb(ioaddr + 0x32) !=0)) break; } if( w > 10000) { outb( inb(ioaddr + 0x37) | 0x08, ioaddr + 0x37); /* do a software reset */ mdelay(500); /* oh my.. */ outb( inb(ioaddr + 0x37) & ~0x08, ioaddr + 0x37); udelay(1); outw( 0x80, ioaddr+0x30); for(w = 0 ; w < 10000; w++) { if((inw(ioaddr + 0x30) & 1) ==0) break; } } }#endif if ( vend == NEC_VERSA_SUBID1 || vend == NEC_VERSA_SUBID2) { /* turn on external amp? */ outw(0xf9ff, ioaddr + 0x64); outw(inw(ioaddr+0x68) | 0x600, ioaddr + 0x68); outw(0x0209, ioaddr + 0x60); } /* Turn on the 978 docking chip. First frob the "master output enable" bit, then set most of the playback volume control registers to max. */ outb(inb(ioaddr+0xc0)|(1<<5), ioaddr+0xc0); outb(0xff, ioaddr+0xc3); outb(0xff, ioaddr+0xc4); outb(0xff, ioaddr+0xc6); outb(0xff, ioaddr+0xc8); outb(0x3f, ioaddr+0xcf); outb(0x3f, ioaddr+0xd0);}/* * Indirect register access. Not all registers are readable so we * need to keep register state ourselves */ #define WRITEABLE_MAP 0xEFFFFF#define READABLE_MAP 0x64003F/* * The Maestro engineers were a little indirection happy. These indirected * registers themselves include indirect registers at another layer */static void __maestro_write(struct ess_card *card, u16 reg, u16 data){ long ioaddr = card->iobase; outw(reg, ioaddr+0x02); outw(data, ioaddr+0x00); if( reg >= NR_IDRS) printk("maestro: IDR %d out of bounds!\n",reg); else card->maestro_map[reg]=data;} static void maestro_write(struct ess_state *s, u16 reg, u16 data){ unsigned long flags; check_suspend(s->card); spin_lock_irqsave(&s->card->lock,flags); __maestro_write(s->card,reg,data); spin_unlock_irqrestore(&s->card->lock,flags);}static u16 __maestro_read(struct ess_card *card, u16 reg){ long ioaddr = card->iobase; outw(reg, ioaddr+0x02); return card->maestro_map[reg]=inw(ioaddr+0x00);}static u16 maestro_read(struct ess_state *s, u16 reg){ if(READABLE_MAP & (1<<reg)) { unsigned long flags; check_suspend(s->card); spin_lock_irqsave(&s->card->lock,flags); __maestro_read(s->card,reg); spin_unlock_irqrestore(&s->card->lock,flags); } return s->card->maestro_map[reg];}/* * These routines handle accessing the second level indirections to the * wave ram. *//* * The register names are the ones ESS uses (see 104T31.ZIP) */ #define IDR0_DATA_PORT 0x00#define IDR1_CRAM_POINTER 0x01#define IDR2_CRAM_DATA 0x02#define IDR3_WAVE_DATA 0x03#define IDR4_WAVE_PTR_LOW 0x04#define IDR5_WAVE_PTR_HI 0x05#define IDR6_TIMER_CTRL 0x06#define IDR7_WAVE_ROMRAM 0x07static void apu_index_set(struct ess_card *card, u16 index){ int i; __maestro_write(card, IDR1_CRAM_POINTER, index); for(i=0;i<1000;i++) if(__maestro_read(card, IDR1_CRAM_POINTER)==index) return; printk(KERN_WARNING "maestro: APU register select failed.\n");}static void apu_data_set(struct ess_card *card, u16 data){ int i; for(i=0;i<1000;i++) { if(__maestro_read(card, IDR0_DATA_PORT)==data) return; __maestro_write(card, IDR0_DATA_PORT, data); }}/* * This is the public interface for APU manipulation. It handles the * interlock to avoid two APU writes in parallel etc. Don't diddle * directly with the stuff above. */static void apu_set_register(struct ess_state *s, u16 channel, u8 reg, u16 data){ unsigned long flags; check_suspend(s->card); if(channel&ESS_CHAN_HARD) channel&=~ESS_CHAN_HARD; else { if(channel>5) printk("BAD CHANNEL %d.\n",channel); else channel = s->apu[channel]; /* store based on real hardware apu/reg */ s->card->apu_map[channel][reg]=data; } reg|=(channel<<4); /* hooray for double indirection!! */ spin_lock_irqsave(&s->card->lock,flags); apu_index_set(s->card, reg); apu_data_set(s->card, data); spin_unlock_irqrestore(&s->card->lock,flags);}static u16 apu_get_register(struct ess_state *s, u16 channel, u8 reg){ unsigned long flags; u16 v; check_suspend(s->card); if(channel&ESS_CHAN_HARD) channel&=~ESS_CHAN_HARD; else channel = s->apu[channel]; reg|=(channel<<4); spin_lock_irqsave(&s->card->lock,flags); apu_index_set(s->card, reg); v=__maestro_read(s->card, IDR0_DATA_PORT); spin_unlock_irqrestore(&s->card->lock,flags); return v;}/* * The wavecache buffers between the APUs and * pci bus mastering */ static void wave_set_register(struct ess_state *s, u16 reg, u16 value){ long ioaddr = s->card->iobase; unsigned long flags; check_suspend(s->card); spin_lock_irqsave(&s->card->lock,flags); outw(reg, ioaddr+0x10); outw(value, ioaddr+0x12); spin_unlock_irqrestore(&s->card->lock,flags);}static u16 wave_get_register(struct ess_state *s, u16 reg){ long ioaddr = s->card->iobase; unsigned long flags; u16 value; check_suspend(s->card); spin_lock_irqsave(&s->card->lock,flags); outw(reg, ioaddr+0x10); value=inw(ioaddr+0x12); spin_unlock_irqrestore(&s->card->lock,flags); return value;}static void sound_reset(int ioaddr){ outw(0x2000, 0x18+ioaddr); udelay(1); outw(0x0000, 0x18+ioaddr); udelay(1);}/* sets the play formats of these apus, should be passed the already shifted format */static void set_apu_fmt(struct ess_state *s, int apu, int mode){ int apu_fmt = 0x10; if(!(mode&ESS_FMT_16BIT)) apu_fmt+=0x20; if((mode&ESS_FMT_STEREO)) apu_fmt+=0x10; s->apu_mode[apu] = apu_fmt; s->apu_mode[apu+1] = apu_fmt;}/* this only fixes the output apu mode to be later set by start_dac and company. output apu modes are set in ess_rec_setup */static void set_fmt(struct ess_state *s, unsigned char mask, unsigned char data){ s->fmt = (s->fmt & mask) | data; set_apu_fmt(s, 0, (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK);}/* this is off by a little bit.. */static u32 compute_rate(struct ess_state *s, u32 freq){ u32 clock = clock_freq[s->card->card_type]; freq = (freq * clocking)/48000; if (freq == 48000) return 0x10000; return ((freq / clock) <<16 )+ (((freq % clock) << 16) / clock);}static void set_dac_rate(struct ess_state *s, unsigned int rate){ u32 freq; int fmt = (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK; if (rate > 48000) rate = 48000; if (rate < 4000) rate = 4000; s->ratedac = rate; if(! (fmt & ESS_FMT_16BIT) && !(fmt & ESS_FMT_STEREO)) rate >>= 1;/* M_printk("computing dac rate %d with mode %d\n",rate,s->fmt);*/ freq = compute_rate(s, rate); /* Load the frequency, turn on 6dB */ apu_set_register(s, 0, 2,(apu_get_register(s, 0, 2)&0x00FF)| ( ((freq&0xFF)<<8)|0x10 )); apu_set_register(s, 0, 3, freq>>8); apu_set_register(s, 1, 2,(apu_get_register(s, 1, 2)&0x00FF)| ( ((freq&0xFF)<<8)|0x10 )); apu_set_register(s, 1, 3, freq>>8);}static void set_adc_rate(struct ess_state *s, unsigned rate){ u32 freq; /* Sample Rate conversion APUs don't like 0x10000 for their rate */ if (rate > 47999) rate = 47999; if (rate < 4000) rate = 4000; s->rateadc = rate; freq = compute_rate(s, rate); /* Load the frequency, turn on 6dB */ apu_set_register(s, 2, 2,(apu_get_register(s, 2, 2)&0x00FF)| ( ((freq&0xFF)<<8)|0x10 )); apu_set_register(s, 2, 3, freq>>8); apu_set_register(s, 3, 2,(apu_get_register(s, 3, 2)&0x00FF)| ( ((freq&0xFF)<<8)|0x10 )); apu_set_register(s, 3, 3, freq>>8); /* fix mixer rate at 48khz. and its _must_ be 0x10000. */ freq = 0x10000; apu_set_register(s, 4, 2,(apu_get_register(s, 4, 2)&0x00FF)| ( ((freq&0xFF)<<8)|0x10 )); apu_set_register(s, 4, 3, freq>>8); apu_set_register(s, 5, 2,(apu_get_register(s, 5, 2)&0x00FF)| ( ((freq&0xFF)<<8)|0x10 )); apu_set_register(s, 5, 3, freq>>8);}/* Stop our host of recording apus */static inline void stop_adc(struct ess_state *s){ /* XXX lets hope we don't have to lock around this */ if (! (s->enable & ADC_RUNNING)) return; s->enable &= ~ADC_RUNNING; apu_set_register(s, 2, 0, apu_get_register(s, 2, 0)&0xFF0F); apu_set_register(s, 3, 0, apu_get_register(s, 3, 0)&0xFF0F); apu_set_register(s, 4, 0, apu_get_register(s, 2, 0)&0xFF0F); apu_set_register(s, 5, 0, apu_get_register(s, 3, 0)&0xFF0F);} /* stop output apus */static void stop_dac(struct ess_state *s){ /* XXX have to lock around this? */ if (! (s->enable & DAC_RUNNING)) return; s->enable &= ~DAC_RUNNING; apu_set_register(s, 0, 0, apu_get_register(s, 0, 0)&0xFF0F); apu_set_register(s, 1, 0, apu_get_register(s, 1, 0)&0xFF0F);} static void start_dac(struct ess_state *s){ /* XXX locks? */ if ( (s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready && (! (s->enable & DAC_RUNNING)) ) { s->enable |= DAC_RUNNING; apu_set_register(s, 0, 0, (apu_get_register(s, 0, 0)&0xFF0F)|s->apu_mode[0]); if((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_STEREO) apu_set_register(s, 1, 0, (apu_get_register(s, 1, 0)&0xFF0F)|s->apu_mode[1]); }} static void start_adc(struct ess_state *s){ /* XXX locks? */ if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) && s->dma_adc.ready && (! (s->enable & ADC_RUNNING)) ) { s->enable |= ADC_RUNNING; apu_set_register(s, 2, 0, (apu_get_register(s, 2, 0)&0xFF0F)|s->apu_mode[2]); apu_set_register(s, 4, 0, (apu_get_register(s, 4, 0)&0xFF0F)|s->apu_mode[4]); if( s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { apu_set_register(s, 3, 0, (apu_get_register(s, 3, 0)&0xFF0F)|s->apu_mode[3]); apu_set_register(s, 5, 0, (apu_get_register(s, 5, 0)&0xFF0F)|s->apu_mode[5]); } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -