patch-2.3.17 linux/drivers/sound/maestro.c
Next file: linux/drivers/sound/sonicvibes.c
Previous file: linux/drivers/sound/esssolo1.c
Back to the patch index
Back to the overall index
- Lines: 654
- Date:
Tue Sep 7 10:14:37 1999
- Orig file:
v2.3.16/linux/drivers/sound/maestro.c
- Orig date:
Tue Aug 31 17:29:14 1999
diff -u --recursive --new-file v2.3.16/linux/drivers/sound/maestro.c linux/drivers/sound/maestro.c
@@ -69,15 +69,29 @@
* Once input is actually written, it will be worth pointing out
* that only 44/16 input actually works.
*
+ * History
+ * v0.04 - Sep 01 1999 - Zach Brown <zab@redhat.com>
+ * copied memory leak fix from sonicvibes driver
+ * different ac97 reset, play with 2.0 ac97, simplify ring bus setup
+ * bob freq code, region sanity, jitter sync fix; all from eric
+ *
* TODO
- * Leaks memory?
* recording is horribly broken
- * apus or dmas get out sync
- * bob can be started twice
+ * codec timeouts (we're way under the example source's 20ms(!?))
+ * some people get indir reg timeouts?
+ * mixer interface broken?
* anyone have a pt101 codec?
* ess's ac97 codec (es1921) doesn't work
- * generally test across codecs..
* mmap(), but beware stereo encoding nastiness.
+ * actually post pci writes
+ * check for bogon bios set irq/io windows
+ * compare our pci setup to the dos one, explains register timeouts?
+ * look really hard at the apu/bob/dma buffer code paths.
+ *
+ * the entire issue of smp safety needs to be looked at. cli() needs
+ * to be replaced with spinlock_irqsave, being very careful of call
+ * paths avoiding deadlock. if lock hold times are quick just
+ * use one big ass per device spinlock..
*/
/*****************************************************************************/
@@ -124,7 +138,7 @@
/* --------------------------------------------------------------------- */
-#define DRIVER_VERSION "0.03"
+#define DRIVER_VERSION "0.04"
#ifndef PCI_VENDOR_ESS
#define PCI_VENDOR_ESS 0x125D
@@ -293,22 +307,21 @@
for(i=0;i<10000;i++)
{
- if(!(inb(io+ESS_AC97_INDEX)&1))
+ if(!(inb(io+ESS_AC97_INDEX)&1))
break;
}
/*
* Write the bus
*/
outw(val, io+ESS_AC97_DATA);
- udelay(1);
- /* should actually be delaying 10 milliseconds? */
+ mdelay(1);
outb(cmd, io+ESS_AC97_INDEX);
- udelay(1);
+ mdelay(1);
}
static u16 maestro_ac97_get(int io, u8 cmd)
{
- int sanity=100000;
+ int sanity=10000;
u16 data;
int i;
@@ -323,7 +336,7 @@
}
outb(cmd|0x80, io+ESS_AC97_INDEX);
- udelay(1);
+ mdelay(1);
while(inb(io+ESS_AC97_INDEX)&1)
{
@@ -335,7 +348,7 @@
}
}
data=inw(io+ESS_AC97_DATA);
- udelay(1);
+ mdelay(1);
return data;
}
@@ -349,28 +362,36 @@
static u16 maestro_ac97_init(int iobase)
{
-
int val, seid, caps;
u16 vend1, vend2;
-#if 0 /* an experiment for another time */
- /* aim at the second codec */
- outw(0x21, iobase+0x38);
- outw(0x5555, iobase+0x3a);
- outw(0x5555, iobase+0x3c);
- udelay(1);
- vend1 = maestro_ac97_get(iobase, 0x7c);
- vend2 = maestro_ac97_get(iobase, 0x7e);
- if(vend1 != 0xffff || vend2 != 0xffff) {
- printk("maestro: It seems you have a second codec: %x %x, please report this.\n",
- vend1,vend2);
- }
- /* back to the first */
+#if 0 /* this needs to be thought about harder */
+ /* aim at the second codec */
+ outw(0x21, iobase+0x38);
+ outw(0x5555, iobase+0x3a);
+ outw(0x5555, iobase+0x3c);
+ udelay(1);
+ vend1 = maestro_ac97_get(iobase, 0x7c);
+ vend2 = maestro_ac97_get(iobase, 0x7e);
+ if(vend1 != 0xffff || vend2 != 0xffff) {
+ printk("maestro: second codec 0x%4x%4x found, enabling both. please report this.\n",
+ vend1,vend2);
+ /* enable them both */
+ outw(0x00, iobase+0x38);
+ outw(0xFFFC, iobase+0x3a);
+ outw(0x000C, iobase+0x3c);
+ } else {
+ /* back to the first only */
outw(0x0, iobase+0x38);
outw(0x0, iobase+0x3a);
outw(0x0, iobase+0x3c);
+ }
+ udelay(1);
#endif
+ /* perform codec reset */
+ maestro_ac97_set(iobase, 0x00, 0x0000);
+
/* should make sure we're ac97 2.1? */
vend1 = maestro_ac97_get(iobase, 0x7c);
vend2 = maestro_ac97_get(iobase, 0x7e);
@@ -382,9 +403,15 @@
printk(KERN_INFO "maestro: AC97 Codec detected: v: 0x%2x%2x 3d: 0x%x caps: 0x%x\n",
vend1,vend2,seid, caps);
+ /* XXX endianness, dork head. */
+ /* magic vendor specifc init code, _no_ idea what these do */
switch ((long)(vend1 << 16) | vend2) {
- /* magic vendor specifc init code, _no_ idea what these do */
-#if 0
+ case 0x545200ff: /* TriTech */
+
+ maestro_ac97_set(iobase,0x2a,0x0001);
+ maestro_ac97_set(iobase,0x2c,0x0000);
+ maestro_ac97_set(iobase,0x2c,0xffff);
+ break;
case 0x83847609: /* ESS 1921 */
maestro_ac97_set(iobase,0x76,0xABBA); /* o/~ Take a chance on me o/~ */
udelay(20);
@@ -393,7 +420,6 @@
maestro_ac97_set(iobase,0x78,0x3802);
udelay(20);
break;
-#endif
default: break;
}
@@ -424,6 +450,15 @@
/* power up various units? */
maestro_ac97_set(iobase, 0x26, 0x000F);
+ /* lets see if they actually default to the spec :) */
+ if(maestro_ac97_get(iobase,0x36) ==0x8080) {
+ int reg;
+ printk("maestro: your ac97 might be 2.0, see if this makes sense:\n");
+ for(reg = 0x28; reg <= 0x58 ; reg += 2) {
+ printk(" 0x%2x: %4x\n",reg,maestro_ac97_get(iobase,reg));
+ }
+ }
+
return 0;
}
@@ -450,10 +485,43 @@
static void maestro_ac97_reset(int ioaddr)
{
+/* outw(0x2000, ioaddr+0x36);
+ inb(ioaddr);
+ mdelay(1);
+ outw(0x0000, ioaddr+0x36);
+ inb(ioaddr);
+ mdelay(1);*/
+
+ /* well this seems to work a little
+ better on the 2e */
+ /* this screws around with the gpio
+ mask/input/direction.. */
+ outw(0x0000, ioaddr+0x36);
+ udelay(20);
+ outw(0xFFFE, ioaddr+0x64);
+ outw(0x1, ioaddr+0x68);
+ outw(0x0, ioaddr+0x60);
+ udelay(20);
+ outw(0x1, ioaddr+0x60);
+ udelay(20); /* other source says 500ms.. INSANE */
outw(0x2000, ioaddr+0x36);
udelay(20);
- outw(0x0000, ioaddr+0x36);
+ outw(0x3000, ioaddr+0x36);
udelay(200);
+ outw(0x0001, ioaddr+0x68);
+ outw(0xFFFF, ioaddr+0x64);
+
+ /* strange strange reset tickling the ring bus */
+ outw(0x0, ioaddr+0x36);
+ udelay(20);
+ outw(0x200, ioaddr+0x36); /* first codec only */
+ udelay(20);
+ outw(0x0, ioaddr+0x36);
+ udelay(20);
+ outw(0x2000, ioaddr+0x36);
+ udelay(20);
+ outw(0x3000, ioaddr+0x36);
+ udelay(20);
}
/*
@@ -621,9 +689,9 @@
static void sound_reset(int ioaddr)
{
outw(0x2000, 0x18+ioaddr);
- udelay(10);
+ udelay(1);
outw(0x0000, 0x18+ioaddr);
- udelay(10);
+ udelay(1);
}
static void set_apu_fmt(struct ess_state *s, int apu, int mode)
@@ -790,9 +858,9 @@
/* apu_set_register(ess, channel, 2, (rate&0xFF)<<8|0x10);
apu_set_register(ess, channel, 3, rate>>8);*/
+/* XXX think about endianess when writing these registers */
/* 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+size)&0xFFFF);
/* setting loop == sample len */
@@ -949,7 +1017,7 @@
#else
-/* nice HW BOB implementation. cheers, eric. */
+/* nice HW BOB implementation. */
static void stop_bob(struct ess_state *s)
{
@@ -961,17 +1029,51 @@
/* eventually we could be clever and limit bob ints
to the frequency at which our smallest duration
chunks may expire */
+#define ESS_SYSCLK 50000000
static void start_bob(struct ess_state *s)
{
- stop_bob(s); // make sure bob's not already running
+ int prescale;
+ int divide;
+
+ int freq = 200; /* requested frequency - calculate what we want here. */
+
+ stop_bob(s); /* make sure bob's not already running */
- maestro_write(s, 6, 0x8000 |(1<<12) | (5<<5) | 11); // (50MHz/2^14)/12 = 254 Hz = 40 mS
+ /* 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);
}
-#endif // ESS_HW_TIMER
+#endif /* ESS_HW_TIMER */
/* --------------------------------------------------------------------- */
static int adc_active = 0;
@@ -986,8 +1088,8 @@
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);
adc_active&=~1;
-// if(!adc_active)
-// stop_bob(s);
+/* if(!adc_active)
+ stop_bob(s); */
spin_unlock_irqrestore(&s->lock, flags);
}
@@ -1000,8 +1102,8 @@
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);
adc_active&=~2;
-// if(!adc_active)
-// stop_bob(s);
+/* if(!adc_active)
+ stop_bob(s); */
spin_unlock_irqrestore(&s->lock, flags);
}
@@ -1020,8 +1122,8 @@
apu_set_register(s, 1, 0,
(apu_get_register(s, 1, 0)&0xFF0F)|s->apu_mode[1]);
}
-// if(!adc_active)
-// start_bob(s);
+/* if(!adc_active)
+ start_bob(s);*/
adc_active|=2;
spin_unlock_irqrestore(&s->lock, flags);
}
@@ -1039,8 +1141,8 @@
apu_set_register(s, 3, 0,
(apu_get_register(s, 3, 0)&0xFF0F)|s->apu_mode[3]);
}
-// if(!adc_active)
-// start_bob(s);
+/* if(!adc_active)
+ start_bob(s); */
adc_active|=1;
spin_unlock_irqrestore(&s->lock, flags);
}
@@ -1094,8 +1196,9 @@
db->ready = db->mapped = 0;
/* alloc as big a chunk as we can */
- for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--)
- db->rawbuf = (void *)__get_free_pages(GFP_KERNEL|GFP_DMA, order);
+ for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)
+ if((db->rawbuf = (void *)__get_free_pages(GFP_KERNEL|GFP_DMA, order)))
+ break;
if (!db->rawbuf)
return -ENOMEM;
@@ -1144,10 +1247,10 @@
/* program enhanced mode registers */
/* FILL */
} else {
- //set_dmaa(s, virt_to_bus(db->rawbuf), db->numfrag << db->fragshift);
+ /* set_dmaa(s, virt_to_bus(db->rawbuf), db->numfrag << db->fragshift); */
/* program enhanced mode registers */
/* FILL */
- //set_dac_rate(s, s->ratedac); // redundant
+ /*set_dac_rate(s, s->ratedac); redundant */
ess_play_setup(s, fmt, s->ratedac,
db->rawbuf, db->numfrag << db->fragshift);
}
@@ -1156,6 +1259,7 @@
return 0;
}
+/* XXX haha, way broken with our split stereo setup. giggle. */
extern __inline__ void clear_advance(struct ess_state *s)
{
unsigned char c = (s->fmt & (ESS_CFMT_16BIT << ESS_CFMT_ASHIFT)) ? 0 : 0x80;
@@ -1180,9 +1284,9 @@
unsigned hwptr;
int diff;
+/* ADC is way broken. compare to DAC.. */
/* update ADC pointer */
if (s->dma_adc.ready) {
- M_printk("adc ready.. \n");
hwptr = (/*s->dma_adc.dmasize - */get_dmac(s)) % s->dma_adc.dmasize;
diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize;
s->dma_adc.hwptr = hwptr;
@@ -1193,16 +1297,17 @@
if (!s->dma_adc.mapped) {
if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) {
s->enable &= ~ESS_ENABLE_RE;
- /* FILL ME */
-// wrindir(s, SV_CIENABLE, s->enable);
- stop_adc(s);
+ /* FILL ME
+ wrindir(s, SV_CIENABLE, s->enable); */
+ stop_adc(s);
s->dma_adc.error++;
}
}
}
/* update DAC pointer */
if (s->dma_dac.ready) {
- hwptr = (/*s->dma_dac.dmasize -*/ get_dmaa(s)) % s->dma_dac.dmasize;
+ /* this is so gross. */
+ hwptr = (/*s->dma_dac.dmasize -*/ get_dmaa(s)) % s->dma_dac.dmasize;
diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize;
/* M_printk("updating dac: hwptr: %d diff: %d\n",hwptr,diff);*/
s->dma_dac.hwptr = hwptr;
@@ -1213,12 +1318,17 @@
wake_up(&s->dma_dac.wait);
} else {
s->dma_dac.count -= diff;
+/* M_printk("maestro: ess_update_ptr: diff: %d, count: %d\n", diff, s->dma_dac.count); */
if (s->dma_dac.count <= 0) {
s->enable &= ~ESS_ENABLE_PE;
-/* FILL ME */
-// wrindir(s, SV_CIENABLE, s->enable);
-
+ /* FILL ME
+ wrindir(s, SV_CIENABLE, s->enable); */
+ /* XXX how on earth can calling this with the lock held work.. */
stop_dac(s);
+ /* brute force everyone back in sync, sigh */
+ s->dma_dac.count = 0;
+ s->dma_dac.swptr = 0;
+ s->dma_dac.hwptr = 0;
s->dma_dac.error++;
} else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) {
clear_advance(s);
@@ -1708,8 +1818,8 @@
set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift);
/* program enhanced mode registers */
/* FILL ME */
-// wrindir(s, SV_CIDMACBASECOUNT1, (s->dma_adc.fragsamples-1) >> 8);
-// wrindir(s, SV_CIDMACBASECOUNT0, s->dma_adc.fragsamples-1);
+/* wrindir(s, SV_CIDMACBASECOUNT1, (s->dma_adc.fragsamples-1) >> 8);
+ wrindir(s, SV_CIDMACBASECOUNT0, s->dma_adc.fragsamples-1); */
s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0;
spin_unlock_irqrestore(&s->lock, flags);
}
@@ -1739,9 +1849,7 @@
{
/* oh, bother. stereo decoding APU's don't work in 16bit so we
use dual linear decoders. which means we have to hack up stereo
- buffer's we're given. yuck.
-
- and we have to be able to work a byte at a time..*/
+ buffer's we're given. yuck. */
unsigned char *so,*left,*right;
int i;
@@ -1780,6 +1888,8 @@
unsigned char *splitbuf = NULL;
int cnt;
+/* printk("maestro: ess_write: count %d\n", count);*/
+
VALIDATE_STATE(s);
if (ppos != &file->f_pos)
return -ESPIPE;
@@ -1789,7 +1899,7 @@
return ret;
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
- /* I wish we could be more clever than this */
+ /* XXX be more clever than this.. */
if (!(splitbuf = kmalloc(count,GFP_KERNEL)))
return -ENOMEM;
ret = 0;
@@ -1840,8 +1950,8 @@
spin_lock_irqsave(&s->lock, flags);
set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift);
/* program enhanced mode registers */
-// wrindir(s, SV_CIDMAABASECOUNT1, (s->dma_dac.fragsamples-1) >> 8);
-// wrindir(s, SV_CIDMAABASECOUNT0, s->dma_dac.fragsamples-1);
+/* wrindir(s, SV_CIDMAABASECOUNT1, (s->dma_dac.fragsamples-1) >> 8);
+ wrindir(s, SV_CIDMAABASECOUNT0, s->dma_dac.fragsamples-1); */
/* FILL ME */
s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0;
spin_unlock_irqrestore(&s->lock, flags);
@@ -1957,6 +2067,8 @@
int val, mapped, ret;
unsigned char fmtm, fmtd;
+/* printk("maestro: ess_ioctl: cmd %d\n", cmd);*/
+
VALIDATE_STATE(s);
mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
@@ -2248,7 +2360,6 @@
return -EINVAL;
}
-// return mixer_ioctl(s, cmd, arg);
return -EINVAL;
}
@@ -2374,7 +2485,7 @@
static int maestro_install(struct pci_dev *pcidev, int card_type, int index)
{
u16 w;
- u32 n;
+/* u32 n;*/
int iobase;
int i;
struct ess_card *card;
@@ -2384,6 +2495,12 @@
iobase = pcidev->resource[0].start;
+ if(check_region(iobase, 256))
+ {
+ printk(KERN_WARNING "maestro: can't allocate 256 bytes I/O at 0x%4.4x\n", iobase);
+ return 0;
+ }
+
card = kmalloc(sizeof(struct ess_card), GFP_KERNEL);
if(card == NULL)
{
@@ -2531,14 +2648,37 @@
pci_write_config_word(pcidev, 0x40, w);
+ /* stake our claim on the iospace */
+ request_region(iobase, 256, card_names[card_type]);
+
sound_reset(iobase);
+#if 0
+
+ /* reset the ring bus */
+
+ outw(inw(iobase + 0x36) & 0xdfff, iobase+0x36); /* disable */
+ outw(0xC090, iobase+0x34);
+ udelay(20);
+ outw(inw(iobase + 0x36) |0x2000, iobase+0x36); /* enable */
+#endif
+ /*
+ * Ring Bus Setup
+ */
+
+ /* setup usual 0x34 stuff.. 0x36 may be chip specific */
+ outw(0xC090, iobase+0x34); /* direct sound, stereo */
+ udelay(20);
+ outw(0x3000, iobase+0x36); /* direct sound, stereo */
+ udelay(20);
+
/*
* Reset the CODEC
*/
maestro_ac97_reset(iobase);
-
+
+#if 0
/*
* Ring Bus Setup
*/
@@ -2546,6 +2686,7 @@
n=inl(iobase+0x34);
n&=~0xF000;
n|=12<<12; /* Direct Sound, Stereo */
+ outl(n, iobase+0x34);
n=inl(iobase+0x34);
n&=~0x0F00; /* Modem off */
@@ -2605,11 +2746,15 @@
w=inw(iobase+0x18);
w|=(1<<0); /* SB IRQ on */
outw(w, iobase+0x18);
+#endif
- outb(0, iobase+0xA4);
- outb(3, iobase+0xA2);
+#if 0
+ /* asp crap */
+ outb(0, iobase+0xA4);
+ outb(3, iobase+0xA2);
outb(0, iobase+0xA6);
+#endif
for(apu=0;apu<16;apu++)
{
@@ -2698,10 +2843,18 @@
if(request_irq(card->irq, ess_interrupt, SA_SHIRQ, card_names[card_type], card))
{
printk(KERN_ERR "maestro: unable to allocate irq %d,\n", card->irq);
+ unregister_sound_mixer(card->dev_mixer);
+ for(i=0;i<8;i++)
+ {
+ struct ess_state *s = &card->channels[i];
+ if(s->dev_audio != -1)
+ unregister_sound_dsp(s->dev_audio);
+ }
+ release_region(card->iobase, 256);
+ kfree(card);
return 0;
}
-// ess_play_test(ess);
printk("maestro: %d channels configured.\n", num);
return 1;
}
@@ -2776,18 +2929,11 @@
while ((s = devs)) {
int i;
devs = devs->next;
-// ess_play_test(&s->channels[0]);
#ifndef ESS_HW_TIMER
kill_bob(&s->channels[0]);
#else
stop_bob(&s->channels[0]);
#endif
-// outb(~0, s->ioenh + SV_CODEC_INTMASK); /* disable ints */
-// synchronize_irq();
-// inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */
-// wrindir(s, SV_CIENABLE, 0); /* disable DMAA and DMAC */
- //outb(0, s->iodmaa + SV_DMA_RESET);
- //outb(0, s->iodmac + SV_DMA_RESET);
free_irq(s->irq, s);
unregister_sound_mixer(s->dev_mixer);
for(i=0;i<8;i++)
@@ -2796,6 +2942,7 @@
if(ess->dev_audio != -1)
unregister_sound_dsp(ess->dev_audio);
}
+ release_region(s->iobase, 256);
kfree(s);
}
M_printk("maestro: unloading\n");
@@ -2808,6 +2955,7 @@
* ex-code that we're not using anymore..
*============================================================================
*/
+
/*
* The ASSP is fortunately not double indexed
*/
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)