📄 maestro.c
字号:
/* * Native play back driver *//* the mode passed should be already shifted and masked */static void ess_play_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size){ u32 pa; u32 tmpval; int high_apu = 0; int channel; M_printk("mode=%d rate=%d buf=%p len=%d.\n", mode, rate, buffer, size); /* all maestro sizes are in 16bit words */ size >>=1; if(mode&ESS_FMT_STEREO) { high_apu++; /* only 16/stereo gets size divided */ if(mode&ESS_FMT_16BIT) size>>=1; } for(channel=0; channel <= high_apu; channel++) { pa = virt_to_bus(buffer); /* set the wavecache control reg */ tmpval = (pa - 0x10) & 0xFFF8; if(!(mode & ESS_FMT_16BIT)) tmpval |= 4; if(mode & ESS_FMT_STEREO) tmpval |= 2; ess->apu_base[channel]=tmpval; wave_set_register(ess, ess->apu[channel]<<3, tmpval); pa -= virt_to_bus(ess->card->dmapages); pa>>=1; /* words */ /* base offset of dma calcs when reading the pointer on the left one */ if(!channel) ess->dma_dac.base = pa&0xFFFF; pa|=0x00400000; /* System RAM */ /* XXX the 16bit here might not be needed.. */ if((mode & ESS_FMT_STEREO) && (mode & ESS_FMT_16BIT)) { if(channel) pa|=0x00800000; /* Stereo */ pa>>=1; } /* XXX think about endianess when writing these registers */ M_printk("maestro: ess_play_setup: APU[%d] pa = 0x%x\n", ess->apu[channel], pa); /* start of sample */ apu_set_register(ess, channel, 4, ((pa>>16)&0xFF)<<8); apu_set_register(ess, channel, 5, pa&0xFFFF); /* sample end */ apu_set_register(ess, channel, 6, (pa+size)&0xFFFF); /* setting loop len == sample len */ apu_set_register(ess, channel, 7, size); /* clear effects/env.. */ apu_set_register(ess, channel, 8, 0x0000); /* set amp now to 0xd0 (?), low byte is 'amplitude dest'? */ apu_set_register(ess, channel, 9, 0xD000); /* clear routing stuff */ apu_set_register(ess, channel, 11, 0x0000); /* dma on, no envelopes, filter to all 1s) */ apu_set_register(ess, channel, 0, 0x400F); if(mode&ESS_FMT_16BIT) ess->apu_mode[channel]=0x10; else ess->apu_mode[channel]=0x30; if(mode&ESS_FMT_STEREO) { /* set panning: left or right */ apu_set_register(ess, channel, 10, 0x8F00 | (channel ? 0 : 0x10)); ess->apu_mode[channel] += 0x10; } else apu_set_register(ess, channel, 10, 0x8F08); } /* clear WP interrupts */ outw(1, ess->card->iobase+0x04); /* enable WP ints */ outw(inw(ess->card->iobase+0x18)|4, ess->card->iobase+0x18); /* go team! */ set_dac_rate(ess,rate); start_dac(ess);}/* * Native record driver *//* again, passed mode is alrady shifted/masked */static void ess_rec_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size){ int apu_step = 2; int channel; M_printk("maestro: ess_rec_setup: mode=%d rate=%d buf=0x%p len=%d.\n", mode, rate, buffer, size); /* all maestro sizes are in 16bit words */ size >>=1; /* we're given the full size of the buffer, but in stereo each channel will only use its half */ if(mode&ESS_FMT_STEREO) { size >>=1; apu_step = 1; } /* APU assignments: 2 = mono/left SRC 3 = right SRC 4 = mono/left Input Mixer 5 = right Input Mixer */ for(channel=2;channel<6;channel+=apu_step) { int i; int bsize, route; u32 pa; u32 tmpval; /* data seems to flow from the codec, through an apu into the 'mixbuf' bit of page, then through the SRC apu and out to the real 'buffer'. ok. sure. */ if(channel & 0x04) { /* ok, we're an input mixer going from adc through the mixbuf to the other apus */ if(!(channel & 0x01)) { pa = virt_to_bus(ess->mixbuf); } else { pa = virt_to_bus(ess->mixbuf + (PAGE_SIZE >> 4)); } /* we source from a 'magic' apu */ bsize = PAGE_SIZE >> 5; /* half of this channels alloc, in words */ route = 0x14 + (channel - 4); /* parallel in crap, see maestro reg 0xC [8-11] */ ess->apu_mode[channel] = 0x90; /* Input Mixer */ } else { /* we're a rate converter taking input from the input apus and outputing it to system memory */ if(!(channel & 0x01)) { pa = virt_to_bus(buffer); } else { /* right channel records its split half. *2 accomodates for rampant shifting earlier */ pa = virt_to_bus(buffer + size*2); } ess->apu_mode[channel] = 0xB0; /* Sample Rate Converter */ bsize = size; /* get input from inputing apu */ route = channel + 2; } M_printk("maestro: ess_rec_setup: getting pa 0x%x from %d\n",pa,channel); /* set the wavecache control reg */ tmpval = (pa - 0x10) & 0xFFF8; ess->apu_base[channel]=tmpval; wave_set_register(ess, ess->apu[channel]<<3, tmpval); pa -= virt_to_bus(ess->card->dmapages); pa>>=1; /* words */ /* base offset of dma calcs when reading the pointer on this left one */ if(channel==2) ess->dma_adc.base = pa&0xFFFF; pa|=0x00400000; /* bit 22 -> System RAM */ M_printk("maestro: ess_rec_setup: APU[%d] pa = 0x%x size = 0x%x route = 0x%x\n", ess->apu[channel], pa, bsize, route); /* Begin loading the APU */ for(i=0;i<15;i++) /* clear all PBRs */ apu_set_register(ess, channel, i, 0x0000); apu_set_register(ess, channel, 0, 0x400F); /* need to enable subgroups.. and we should probably have different groups for different /dev/dsps.. */ apu_set_register(ess, channel, 2, 0x8); /* Load the buffer into the wave engine */ apu_set_register(ess, channel, 4, ((pa>>16)&0xFF)<<8); /* XXX reg is little endian.. */ apu_set_register(ess, channel, 5, pa&0xFFFF); apu_set_register(ess, channel, 6, (pa+bsize)&0xFFFF); apu_set_register(ess, channel, 7, bsize); /* clear effects/env.. */ apu_set_register(ess, channel, 8, 0x00F0); /* amplitude now? sure. why not. */ apu_set_register(ess, channel, 9, 0x0000); /* set filter tune, radius, polar pan */ apu_set_register(ess, channel, 10, 0x8F08); /* route input */ apu_set_register(ess, channel, 11, route); } /* clear WP interrupts */ outw(1, ess->card->iobase+0x04); /* enable WP ints */ outw(inw(ess->card->iobase+0x18)|4, ess->card->iobase+0x18); /* let 'er rip */ set_adc_rate(ess,rate); start_adc(ess);}/* --------------------------------------------------------------------- */static void set_dmaa(struct ess_state *s, unsigned int addr, unsigned int count){ M_printk("set_dmaa??\n");}static void set_dmac(struct ess_state *s, unsigned int addr, unsigned int count){ M_printk("set_dmac??\n");}/* Playback pointer */static inline unsigned get_dmaa(struct ess_state *s){ int offset; offset = apu_get_register(s,0,5);/* M_printk("dmaa: offset: %d, base: %d\n",offset,s->dma_dac.base); */ offset-=s->dma_dac.base; return (offset&0xFFFE)<<1; /* hardware is in words */}/* Record pointer */static inline unsigned get_dmac(struct ess_state *s){ int offset; offset = apu_get_register(s,2,5);/* M_printk("dmac: offset: %d, base: %d\n",offset,s->dma_adc.base); */ /* The offset is an address not a position relative to base */ offset-=s->dma_adc.base; return (offset&0xFFFE)<<1; /* hardware is in words */}/* * Meet Bob, the timer... */static void ess_interrupt(int irq, void *dev_id, struct pt_regs *regs);static void stop_bob(struct ess_state *s){ /* Mask IDR 11,17 */ maestro_write(s, 0x11, maestro_read(s, 0x11)&~1); maestro_write(s, 0x17, maestro_read(s, 0x17)&~1);}/* eventually we could be clever and limit bob ints to the frequency at which our smallest duration chunks may expire */#define ESS_SYSCLK 50000000static void start_bob(struct ess_state *s){ int prescale; int divide; /* XXX make freq selector much smarter, see calc_bob_rate */ int freq = 200; /* compute ideal interrupt frequency for buffer size & play rate */ /* first, find best prescaler value to match freq */ for(prescale=5;prescale<12;prescale++) if(freq > (ESS_SYSCLK>>(prescale+9))) break; /* next, back off prescaler whilst getting divider into optimum range */ divide=1; while((prescale > 5) && (divide<32)) { prescale--; divide <<=1; } divide>>=1; /* now fine-tune the divider for best match */ for(;divide<31;divide++) if(freq >= ((ESS_SYSCLK>>(prescale+9))/(divide+1))) break; /* divide = 0 is illegal, but don't let prescale = 4! */ if(divide == 0) { divide++; if(prescale>5) prescale--; } maestro_write(s, 6, 0x9000 | (prescale<<5) | divide); /* set reg */ /* Now set IDR 11/17 */ maestro_write(s, 0x11, maestro_read(s, 0x11)|1); maestro_write(s, 0x17, maestro_read(s, 0x17)|1);}/* --------------------------------------------------------------------- *//* this quickly calculates the frequency needed for bob and sets it if its different than what bob is currently running at. its called often so needs to be fairly quick. */#define BOB_MIN 50#define BOB_MAX 400static void calc_bob_rate(struct ess_state *s) {#if 0 /* this thing tries to set the frequency of bob such that there are 2 interrupts / buffer walked by the dac/adc. That is probably very wrong for people who actually care about mid buffer positioning. it should be calculated as bytes/interrupt and that needs to be decided :) so for now just use the static 150 in start_bob.*/ unsigned int dac_rate=2,adc_rate=1,newrate; static int israte=-1; if (s->dma_dac.fragsize == 0) dac_rate = BOB_MIN; else { dac_rate = (2 * s->ratedac * sample_size[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]) / (s->dma_dac.fragsize) ; } if (s->dma_adc.fragsize == 0) adc_rate = BOB_MIN; else { adc_rate = (2 * s->rateadc * sample_size[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]) / (s->dma_adc.fragsize) ; } if(dac_rate > adc_rate) newrate = adc_rate; else newrate=dac_rate; if(newrate > BOB_MAX) newrate = BOB_MAX; else { if(newrate < BOB_MIN) newrate = BOB_MIN; } if( israte != newrate) { printk("dac: %d adc: %d rate: %d\n",dac_rate,adc_rate,israte); israte=newrate; }#endif}static int prog_dmabuf(struct ess_state *s, unsigned rec){ struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; unsigned rate = rec ? s->rateadc : s->ratedac; unsigned bytepersec; unsigned bufs; unsigned char fmt; unsigned long flags; spin_lock_irqsave(&s->lock, flags); fmt = s->fmt; if (rec) { stop_adc(s); fmt >>= ESS_ADC_SHIFT; } else { stop_dac(s); fmt >>= ESS_DAC_SHIFT; } spin_unlock_irqrestore(&s->lock, flags); fmt &= ESS_FMT_MASK; db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; /* this algorithm is a little nuts.. where did /1000 come from? */ bytepersec = rate << sample_shift[fmt]; bufs = PAGE_SIZE << db->buforder; if (db->ossfragshift) { if ((1000 << db->ossfragshift) < bytepersec) db->fragshift = ld2(bytepersec/1000); else db->fragshift = db->ossfragshift; } else { db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); if (db->fragshift < 3) db->fragshift = 3; } db->numfrag = bufs >> db->fragshift; while (db->numfrag < 4 && db->fragshift > 3) { db->fragshift--; db->numfrag = bufs >> db->fragshift; } db->fragsize = 1 << db->fragshift; if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) db->numfrag = db->ossmaxfrags; db->fragsamples = db->fragsize >> sample_shift[fmt]; db->dmasize = db->numfrag << db->fragshift; M_printk("maestro: setup oss: numfrag: %d fragsize: %d dmasize: %d\n",db->numfrag,db->fragsize,db->dmasize); memset(db->rawbuf, (fmt & ESS_FMT_16BIT) ? 0 : 0x80, db->dmasize); spin_lock_irqsave(&s->lock, flags); if (rec) ess_rec_setup(s, fmt, s->rateadc, db->rawbuf, db->dmasize); else ess_play_setup(s, fmt, s->ratedac, db->rawbuf, db->dmasize); spin_unlock_irqrestore(&s->lock, flags); db->ready = 1; return 0;}static __inline__ void clear_advance(struct ess_state *s){ unsigned char c = ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_16BIT) ? 0 : 0x80; unsigned char *buf = s->dma_dac.rawbuf; unsigned bsize = s->dma_dac.dmasize; unsigned bptr = s->dma_dac.swptr; unsigned len = s->dma_dac.fragsize; if (bptr + len > bsize) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -