patch-2.3.99-pre6 linux/drivers/sound/maestro.c
Next file: linux/drivers/sound/midibuf.c
Previous file: linux/drivers/sound/i810_audio.c
Back to the patch index
Back to the overall index
- Lines: 1300
- Date:
Fri Apr 21 16:14:14 2000
- Orig file:
v2.3.99-pre5/linux/drivers/sound/maestro.c
- Orig date:
Thu Feb 10 17:11:14 2000
diff -u --recursive --new-file v2.3.99-pre5/linux/drivers/sound/maestro.c linux/drivers/sound/maestro.c
@@ -21,7 +21,7 @@
* Based heavily on SonicVibes.c:
* Copyright (C) 1998-1999 Thomas Sailer (sailer@ife.ee.ethz.ch)
*
- * Heavily modified by Zach Brown <zab@redhat.com> based on lunch
+ * Heavily modified by Zach Brown <zab@zabbo.net> based on lunch
* with ESS engineers. Many thanks to Howard Kim for providing
* contacts and hardware. Honorable mention goes to Eric
* Brombaugh for all sorts of things. Best regards to the
@@ -105,8 +105,28 @@
* being used now is quite dirty and assumes we're on a uni-processor
* machine. Much of it will need to be cleaned up for SMP ACPI or
* similar.
+ *
+ * We also pay attention to PCI power management now. The driver
+ * will power down units of the chip that it knows aren't needed.
+ * The WaveProcessor and company are only powered on when people
+ * have /dev/dsp*s open. On removal the driver will
+ * power down the maestro entirely. There could still be
+ * trouble with BIOSen that magically change power states
+ * themselves, but we'll see.
*
* History
+ * (still based on v0.14) Mar 29 2000 - Zach Brown <zab@redhat.com>
+ * move to 2.3 power management interface, which
+ * required hacking some suspend/resume/check paths
+ * make static compilation work
+ * v0.14 - Jan 28 2000 - Zach Brown <zab@redhat.com>
+ * add PCI power management through ACPI regs.
+ * we now shut down on machine reboot/halt
+ * leave scary PCI config items alone (isa stuff, mostly)
+ * enable 1921s, it seems only mine was broke.
+ * fix swapped left/right pcm dac. har har.
+ * up bob freq, increase buffers, fix pointers at underflow
+ * silly compilation problems
* v0.13 - Nov 18 1999 - Zach Brown <zab@redhat.com>
* fix nec Versas? man would that be cool.
* v0.12 - Nov 12 1999 - Zach Brown <zab@redhat.com>
@@ -162,13 +182,11 @@
* bob freq code, region sanity, jitter sync fix; all from Eric
*
* TODO
- * some people get indir reg timeouts?
* fix bob frequency
* endianness
* do smart things with ac97 2.0 bits.
* docking and dual codecs and 978?
* leave 54->61 open
- * resolve 2.3/2.2 stuff
*
* it also would be fun to have a mode that would not use pci dma at all
* but would copy into the wavecache on board memory and use that
@@ -190,6 +208,7 @@
#define SILLY_MAKE_INIT(FUNC) __initfunc(FUNC)
#define SILLY_OFFSET(VMA) ((VMA)->vm_offset)
+
#else
#define SILLY_PCI_BASE_ADDRESS(PCIDEV) (PCIDEV->resource[0].start)
@@ -197,6 +216,7 @@
#define SILLY_MAKE_INIT(FUNC) __init FUNC
#define SILLY_OFFSET(VMA) ((VMA)->vm_pgoff)
+
#endif
#include <linux/string.h>
@@ -213,15 +233,12 @@
#include <asm/dma.h>
#include <linux/init.h>
#include <linux/poll.h>
+#include <linux/reboot.h>
#include <asm/uaccess.h>
#include <asm/hardirq.h>
#include <linux/pm.h>
static int maestro_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *d);
-static int in_suspend=0;
-wait_queue_head_t suspend_queue;
-static void check_suspend(void);
-#define CHECK_SUSPEND check_suspend();
#include "maestro.h"
@@ -231,14 +248,18 @@
#ifdef M_DEBUG
static int debug=0;
-static int dsps_order=0;
#define M_printk(args...) {if (debug) printk(args);}
#else
#define M_printk(x)
#endif
+/* we try to setup 2^(dsps_order) /dev/dsp devices */
+static int dsps_order=0;
+/* wether or not we mess around with power management */
+static int use_pm=2; /* set to 1 for force */
+
/* --------------------------------------------------------------------- */
-#define DRIVER_VERSION "0.13"
+#define DRIVER_VERSION "0.14"
#ifndef PCI_VENDOR_ESS
#define PCI_VENDOR_ESS 0x125D
@@ -282,6 +303,46 @@
#define NR_APUS 64
#define NR_APU_REGS 16
+/* acpi states */
+enum {
+ ACPI_D0=0,
+ ACPI_D1,
+ ACPI_D2,
+ ACPI_D3
+};
+
+/* bits in the acpi masks */
+#define ACPI_12MHZ ( 1 << 15)
+#define ACPI_24MHZ ( 1 << 14)
+#define ACPI_978 ( 1 << 13)
+#define ACPI_SPDIF ( 1 << 12)
+#define ACPI_GLUE ( 1 << 11)
+#define ACPI__10 ( 1 << 10) /* reserved */
+#define ACPI_PCIINT ( 1 << 9)
+#define ACPI_HV ( 1 << 8) /* hardware volume */
+#define ACPI_GPIO ( 1 << 7)
+#define ACPI_ASSP ( 1 << 6)
+#define ACPI_SB ( 1 << 5) /* sb emul */
+#define ACPI_FM ( 1 << 4) /* fm emul */
+#define ACPI_RB ( 1 << 3) /* ringbus / aclink */
+#define ACPI_MIDI ( 1 << 2)
+#define ACPI_GP ( 1 << 1) /* game port */
+#define ACPI_WP ( 1 << 0) /* wave processor */
+
+#define ACPI_ALL (0xffff)
+#define ACPI_SLEEP (~(ACPI_SPDIF|ACPI_ASSP|ACPI_SB|ACPI_FM| \
+ ACPI_MIDI|ACPI_GP|ACPI_WP))
+#define ACPI_NONE (ACPI__10)
+
+/* these masks indicate which units we care about at
+ which states */
+u16 acpi_state_mask[] = {
+ [ACPI_D0] = ACPI_ALL,
+ [ACPI_D1] = ACPI_SLEEP,
+ [ACPI_D2] = ACPI_SLEEP,
+ [ACPI_D3] = ACPI_NONE
+};
+
static const unsigned sample_size[] = { 1, 2, 2, 4 };
static const unsigned sample_shift[] = { 0, 1, 1, 2 };
@@ -303,6 +364,10 @@
[TYPE_MAESTRO2E] = (50000000L / 1024L)
};
+static int maestro_notifier(struct notifier_block *nb, unsigned long event, void *buf);
+
+static struct notifier_block maestro_nb = {maestro_notifier, NULL, 0};
+
/* --------------------------------------------------------------------- */
struct ess_state {
@@ -357,6 +422,7 @@
/* pointer to each dsp?s piece of the apu->src buffer page */
void *mixbuf;
+
};
struct ess_card {
@@ -383,6 +449,11 @@
unsigned int mixer_state[SOUND_MIXER_NRDEVICES];
} mix;
+ int power_regs;
+
+ int in_suspend;
+ wait_queue_head_t suspend_queue;
+
struct ess_state channels[MAX_DSPS];
u16 maestro_map[NR_IDRS]; /* Register map */
/* we have to store this junk so that we can come back from a
@@ -397,7 +468,7 @@
int dmaorder;
/* hardware resources */
- struct pci_dev pcidev; /* uck.. */
+ struct pci_dev *pcidev;
u32 iobase;
u32 irq;
@@ -434,6 +505,8 @@
/* --------------------------------------------------------------------- */
+static void check_suspend(struct ess_card *card);
+
static struct ess_card *devs = NULL;
/* --------------------------------------------------------------------- */
@@ -443,14 +516,15 @@
* ESS Maestro AC97 codec programming interface.
*/
-static void maestro_ac97_set(int io, u8 cmd, u16 val)
+static void maestro_ac97_set(struct ess_card *card, u8 cmd, u16 val)
{
+ int io = card->iobase;
int i;
/*
* Wait for the codec bus to be free
*/
- CHECK_SUSPEND;
+ check_suspend(card);
for(i=0;i<10000;i++)
{
@@ -466,13 +540,14 @@
mdelay(1);
}
-static u16 maestro_ac97_get(int io, u8 cmd)
+static u16 maestro_ac97_get(struct ess_card *card, u8 cmd)
{
+ int io = card->iobase;
int sanity=10000;
u16 data;
int i;
- CHECK_SUSPEND;
+ check_suspend(card);
/*
* Wait for the codec bus to be free
*/
@@ -564,7 +639,7 @@
int ret=0;
struct ac97_mixer_hw *mh = &ac97_hw[mixer];
- val = maestro_ac97_get(card->iobase , mh->offset);
+ val = maestro_ac97_get(card, mh->offset);
if(AC97_STEREO_MASK & (1<<mixer)) {
/* nice stereo mixers .. */
@@ -605,7 +680,7 @@
call with spinlock held */
/* linear scale -> log */
-unsigned char lin2log[101] =
+static unsigned char lin2log[101] =
{
0, 0 , 15 , 23 , 30 , 34 , 38 , 42 , 45 , 47 ,
50 , 52 , 53 , 55 , 57 , 58 , 60 , 61 , 62 ,
@@ -649,19 +724,19 @@
} else if (mixer == SOUND_MIXER_SPEAKER) {
val = (((100 - left) * mh->scale) / 100) << 1;
} else if (mixer == SOUND_MIXER_MIC) {
- val = maestro_ac97_get(card->iobase , mh->offset) & ~0x801f;
+ val = maestro_ac97_get(card, mh->offset) & ~0x801f;
val |= (((100 - left) * mh->scale) / 100);
/* the low bit is optional in the tone sliders and masking
it lets is avoid the 0xf 'bypass'.. */
} else if (mixer == SOUND_MIXER_BASS) {
- val = maestro_ac97_get(card->iobase , mh->offset) & ~0x0f00;
+ val = maestro_ac97_get(card , mh->offset) & ~0x0f00;
val |= ((((100 - left) * mh->scale) / 100) << 8) & 0x0e00;
} else if (mixer == SOUND_MIXER_TREBLE) {
- val = maestro_ac97_get(card->iobase , mh->offset) & ~0x000f;
+ val = maestro_ac97_get(card , mh->offset) & ~0x000f;
val |= (((100 - left) * mh->scale) / 100) & 0x000e;
}
- maestro_ac97_set(card->iobase , mh->offset, val);
+ maestro_ac97_set(card , mh->offset, val);
M_printk(" -> %x\n",val);
}
@@ -708,7 +783,7 @@
static int
ac97_recmask_io(struct ess_card *card, int read, int mask)
{
- unsigned int val = ac97_oss_mask[ maestro_ac97_get(card->iobase, 0x1a) & 0x7 ];
+ unsigned int val = ac97_oss_mask[ maestro_ac97_get(card, 0x1a) & 0x7 ];
if (read) return val;
@@ -723,7 +798,7 @@
M_printk("maestro: setting ac97 recmask to 0x%x\n",val);
- maestro_ac97_set(card->iobase,0x1a,val);
+ maestro_ac97_set(card,0x1a,val);
return 0;
};
@@ -736,7 +811,7 @@
* The PT101 setup is untested.
*/
-static u16 maestro_ac97_init(struct ess_card *card, int iobase)
+static u16 maestro_ac97_init(struct ess_card *card)
{
u16 vend1, vend2, caps;
@@ -747,13 +822,13 @@
card->mix.write_mixer = ac97_write_mixer;
card->mix.recmask_io = ac97_recmask_io;
- vend1 = maestro_ac97_get(iobase, 0x7c);
- vend2 = maestro_ac97_get(iobase, 0x7e);
+ vend1 = maestro_ac97_get(card, 0x7c);
+ vend2 = maestro_ac97_get(card, 0x7e);
- caps = maestro_ac97_get(iobase, 0x00);
+ caps = maestro_ac97_get(card, 0x00);
printk(KERN_INFO "maestro: AC97 Codec detected: v: 0x%2x%2x caps: 0x%x pwr: 0x%x\n",
- vend1,vend2,caps,maestro_ac97_get(iobase,0x26) & 0xf);
+ vend1,vend2,caps,maestro_ac97_get(card,0x26) & 0xf);
if (! (caps & 0x4) ) {
/* no bass/treble nobs */
@@ -765,10 +840,14 @@
switch ((long)(vend1 << 16) | vend2) {
case 0x545200ff: /* TriTech */
/* no idea what this does */
- maestro_ac97_set(iobase,0x2a,0x0001);
- maestro_ac97_set(iobase,0x2c,0x0000);
- maestro_ac97_set(iobase,0x2c,0xffff);
+ maestro_ac97_set(card,0x2a,0x0001);
+ maestro_ac97_set(card,0x2c,0x0000);
+ maestro_ac97_set(card,0x2c,0xffff);
break;
+#if 0 /* i thought the problems I was seeing were with
+ the 1921, but apparently they were with the pci board
+ it was on, so this code is commented out.
+ lets see if this holds true. */
case 0x83847609: /* ESS 1921 */
/* writing to 0xe (mic) or 0x1a (recmask) seems
to hang this codec */
@@ -776,20 +855,21 @@
card->mix.record_sources = 0;
card->mix.recmask_io = NULL;
#if 0 /* don't ask. I have yet to see what these actually do. */
- maestro_ac97_set(iobase,0x76,0xABBA); /* o/~ Take a chance on me o/~ */
+ maestro_ac97_set(card,0x76,0xABBA); /* o/~ Take a chance on me o/~ */
udelay(20);
- maestro_ac97_set(iobase,0x78,0x3002);
+ maestro_ac97_set(card,0x78,0x3002);
udelay(20);
- maestro_ac97_set(iobase,0x78,0x3802);
+ maestro_ac97_set(card,0x78,0x3802);
udelay(20);
#endif
break;
+#endif
default: break;
}
- maestro_ac97_set(iobase, 0x1E, 0x0404);
+ maestro_ac97_set(card, 0x1E, 0x0404);
/* null misc stuff */
- maestro_ac97_set(iobase, 0x20, 0x0000);
+ maestro_ac97_set(card, 0x20, 0x0000);
return 0;
}
@@ -923,7 +1003,7 @@
{
unsigned long flags;
- CHECK_SUSPEND;
+ check_suspend(s->card);
spin_lock_irqsave(&s->card->lock,flags);
__maestro_write(s->card,reg,data);
@@ -944,7 +1024,7 @@
if(READABLE_MAP & (1<<reg))
{
unsigned long flags;
- CHECK_SUSPEND;
+ check_suspend(s->card);
spin_lock_irqsave(&s->card->lock,flags);
__maestro_read(s->card,reg);
@@ -1003,7 +1083,7 @@
{
unsigned long flags;
- CHECK_SUSPEND;
+ check_suspend(s->card);
if(channel&ESS_CHAN_HARD)
channel&=~ESS_CHAN_HARD;
@@ -1032,7 +1112,7 @@
unsigned long flags;
u16 v;
- CHECK_SUSPEND;
+ check_suspend(s->card);
if(channel&ESS_CHAN_HARD)
channel&=~ESS_CHAN_HARD;
@@ -1060,7 +1140,7 @@
{
long ioaddr = s->card->iobase;
unsigned long flags;
- CHECK_SUSPEND;
+ check_suspend(s->card);
spin_lock_irqsave(&s->card->lock,flags);
@@ -1075,7 +1155,7 @@
long ioaddr = s->card->iobase;
unsigned long flags;
u16 value;
- CHECK_SUSPEND;
+ check_suspend(s->card);
spin_lock_irqsave(&s->card->lock,flags);
outw(reg, ioaddr+0x10);
@@ -1198,7 +1278,7 @@
}
/* stop output apus */
-extern inline void stop_dac(struct ess_state *s)
+static void stop_dac(struct ess_state *s)
{
/* XXX have to lock around this? */
if (! (s->enable & DAC_RUNNING)) return;
@@ -1304,11 +1384,12 @@
/* XXX think about endianess when writing these registers */
M_printk("maestro: ess_play_setup: APU[%d] pa = 0x%x\n", ess->apu[channel], pa);
- /* Load the buffer into the wave engine */
+ /* 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 == sample len */
+ /* setting loop len == sample len */
apu_set_register(ess, channel, 7, size);
/* clear effects/env.. */
@@ -1328,7 +1409,7 @@
if(mode&ESS_FMT_STEREO) {
/* set panning: left or right */
- apu_set_register(ess, channel, 10, 0x8F00 | (channel ? 0x10 : 0));
+ apu_set_register(ess, channel, 10, 0x8F00 | (channel ? 0 : 0x10));
ess->apu_mode[channel] += 0x10;
} else
apu_set_register(ess, channel, 10, 0x8F08);
@@ -1539,7 +1620,7 @@
int divide;
/* XXX make freq selector much smarter, see calc_bob_rate */
- int freq = 150; /* requested frequency - calculate what we want here. */
+ int freq = 200;
/* compute ideal interrupt frequency for buffer size & play rate */
/* first, find best prescaler value to match freq */
@@ -1647,6 +1728,7 @@
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) {
@@ -1675,13 +1757,11 @@
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->numfrag << db->fragshift);
- } else {
- ess_play_setup(s, fmt, s->ratedac,
- db->rawbuf, db->numfrag << db->fragshift);
- }
+ 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;
@@ -1720,8 +1800,7 @@
/* oh boy should this all be re-written. everything in the current code paths think
that the various counters/pointers are expressed in bytes to the user but we have
two apus doing stereo stuff so we fix it up here.. it propogates to all the various
- counters from here. Notice that this means that mono recording is very very
- broken right now. */
+ counters from here. */
if ( s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) {
hwptr = (get_dmac(s)*2) % s->dma_adc.dmasize;
} else {
@@ -1751,7 +1830,7 @@
hwptr = get_dmaa(s) % s->dma_dac.dmasize;
/* the apu only reports the length it has seen, not the
length of the memory that has been used (the WP
- knows that */
+ knows that) */
if ( ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK) == (ESS_FMT_STEREO|ESS_FMT_16BIT))
hwptr<<=1;
@@ -1768,14 +1847,15 @@
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) {
+ M_printk("underflow! diff: %d count: %d hw: %d sw: %d\n", diff, s->dma_dac.count,
+ hwptr, s->dma_dac.swptr);
/* 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.swptr = hwptr;
s->dma_dac.error++;
} else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) {
clear_advance(s);
@@ -1783,6 +1863,8 @@
}
if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) {
wake_up(&s->dma_dac.wait);
+/* printk("waking up DAC count: %d sw: %d hw: %d\n",s->dma_dac.count, s->dma_dac.swptr,
+ hwptr);*/
}
}
}
@@ -2023,10 +2105,10 @@
}
static /*const*/ struct file_operations ess_mixer_fops = {
- llseek: ess_llseek,
- ioctl: ess_ioctl_mixdev,
- open: ess_open_mixdev,
- release: ess_release_mixdev,
+ llseek: ess_llseek,
+ ioctl: ess_ioctl_mixdev,
+ open: ess_open_mixdev,
+ release: ess_release_mixdev,
};
/* --------------------------------------------------------------------- */
@@ -2153,8 +2235,7 @@
goto rec_return_free;
}
if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) {
- if(! in_suspend)
- printk(KERN_DEBUG "maestro: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+ if(! s->card->in_suspend) printk(KERN_DEBUG "maestro: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count,
s->dma_adc.hwptr, s->dma_adc.swptr);
stop_adc(s);
@@ -2253,8 +2334,7 @@
goto return_free;
}
if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ)) {
- if(! in_suspend)
- printk(KERN_DEBUG "maestro: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
+ if(! s->card->in_suspend) printk(KERN_DEBUG "maestro: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count,
s->dma_dac.hwptr, s->dma_dac.swptr);
stop_dac(s);
@@ -2277,6 +2357,7 @@
if (!ret) ret = -EFAULT;
goto return_free;
}
+/* printk("wrote %d bytes at sw: %d cnt: %d while hw: %d\n",cnt, swptr, s->dma_dac.count, s->dma_dac.hwptr);*/
swptr = (swptr + cnt) % s->dma_dac.dmasize;
@@ -2294,6 +2375,7 @@
return ret;
}
+/* No kernel lock - we have our own spinlock */
static unsigned int ess_poll(struct file *file, struct poll_table_struct *wait)
{
struct ess_state *s = (struct ess_state *)file->private_data;
@@ -2613,6 +2695,7 @@
case SNDCTL_DSP_SETFRAGMENT:
get_user_ret(val, (int *)arg, -EFAULT);
+ M_printk("maestro: SETFRAGMENT: %0x\n",val);
if (file->f_mode & FMODE_READ) {
s->dma_adc.ossfragshift = val & 0xffff;
s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
@@ -2678,6 +2761,36 @@
wave_set_register(s, 0x01FF , packed_phys);
}
+/*
+ * this guy makes sure we're in the right power
+ * state for what we want to be doing
+ */
+static void maestro_power(struct ess_card *card, int tostate)
+{
+ u16 active_mask = acpi_state_mask[tostate];
+ u8 state;
+
+ if(!use_pm) return;
+
+ pci_read_config_byte(card->pcidev, card->power_regs+0x4, &state);
+ state&=3;
+
+ /* make sure we're in the right state */
+ if(state != tostate) {
+ M_printk(KERN_WARNING "maestro: dev %02x:%02x.%x switching from D%d to D%d\n",
+ card->pcidev->bus->number,
+ PCI_SLOT(card->pcidev->devfn),
+ PCI_FUNC(card->pcidev->devfn),
+ state,tostate);
+ pci_write_config_byte(card->pcidev, card->power_regs+0x4, tostate);
+ }
+
+ /* and make sure the units we care about are on
+ XXX we might want to do this before state flipping? */
+ pci_write_config_word(card->pcidev, 0x54, ~ active_mask);
+ pci_write_config_word(card->pcidev, 0x56, ~ active_mask);
+}
+
/* we allocate a large power of two for all our memory.
this is cut up into (not to scale :):
|silly fifo word | 512byte mixbuf per adc | dac/adc * channels |
@@ -2690,7 +2803,7 @@
unsigned long mapend,map;
/* alloc as big a chunk as we can */
- for (order = (dsps_order + (15-PAGE_SHIFT) + 1); order >= (dsps_order + 2 + 1); order--)
+ for (order = (dsps_order + (16-PAGE_SHIFT) + 1); order >= (dsps_order + 2 + 1); order--)
if((rawbuf = (void *)__get_free_pages(GFP_KERNEL|GFP_DMA, order)))
break;
@@ -2709,12 +2822,6 @@
s->card->dmapages = rawbuf;
s->card->dmaorder = order;
- /* play bufs are in the same first region as record bufs */
- set_base_registers(s,rawbuf);
-
- M_printk("maestro: writing %lx (%lx) to the wp\n",virt_to_bus(rawbuf),
- ((virt_to_bus(rawbuf))&0xFFE00000)>>12);
-
for(i=0;i<NR_DSPS;i++) {
struct ess_state *ess = &s->card->channels[i];
@@ -2733,7 +2840,7 @@
happily scribble away.. */
ess->mixbuf = rawbuf + (512 * (i+1));
- M_printk("maestro: setup apu %d: %p %p %p\n",i,ess->dma_dac.rawbuf,
+ M_printk("maestro: setup apu %d: dac: %p adc: %p mix: %p\n",i,ess->dma_dac.rawbuf,
ess->dma_adc.rawbuf, ess->mixbuf);
}
@@ -2819,6 +2926,20 @@
return -ENOMEM;
}
+ /* we're covered by the open_sem */
+ if( ! s->card->dsps_open ) {
+ maestro_power(s->card,ACPI_D0);
+ start_bob(s);
+ }
+ s->card->dsps_open++;
+ M_printk("maestro: open, %d bobs now\n",s->card->dsps_open);
+
+ /* ok, lets write WC base regs now that we've
+ powered up the chip */
+ M_printk("maestro: writing 0x%lx (bus 0x%lx) to the wp\n",virt_to_bus(s->card->dmapages),
+ ((virt_to_bus(s->card->dmapages))&0xFFE00000)>>12);
+ set_base_registers(s,s->card->dmapages);
+
if (file->f_mode & FMODE_READ) {
/*
fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_ADC_SHIFT);
@@ -2842,13 +2963,6 @@
set_fmt(s, fmtm, fmts);
s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
- /* we're covered by the open_sem */
- if( ! s->card->dsps_open ) {
- start_bob(s);
- }
- s->card->dsps_open++;
- M_printk("maestro: open, %d bobs now\n",s->card->dsps_open);
-
up(&s->open_sem);
MOD_INC_USE_COUNT;
return 0;
@@ -2874,8 +2988,10 @@
/* we're covered by the open_sem */
M_printk("maestro: %d dsps now alive\n",s->card->dsps_open-1);
if( --s->card->dsps_open <= 0) {
+ s->card->dsps_open = 0;
stop_bob(s);
free_buffers(s);
+ maestro_power(s->card,ACPI_D2);
}
up(&s->open_sem);
wake_up(&s->open_wait);
@@ -2884,56 +3000,42 @@
}
static struct file_operations ess_audio_fops = {
- llseek: ess_llseek,
- read: ess_read,
- write: ess_write,
- poll: ess_poll,
- ioctl: ess_ioctl,
- mmap: ess_mmap,
- open: ess_open,
- release: ess_release,
+ llseek: ess_llseek,
+ read: ess_read,
+ write: ess_write,
+ poll: ess_poll,
+ ioctl: ess_ioctl,
+ mmap: ess_mmap,
+ open: ess_open,
+ release: ess_release,
};
static int
maestro_config(struct ess_card *card)
{
- struct pci_dev *pcidev = &card->pcidev;
+ struct pci_dev *pcidev = card->pcidev;
struct ess_state *ess = &card->channels[0];
int apu,iobase = card->iobase;
u16 w;
u32 n;
- /*
- * Disable ACPI
+ /* We used to muck around with pci config space that
+ * we had no business messing with. We don't know enough
+ * about the machine to know which DMA mode is appropriate,
+ * etc. We were guessing wrong on some machines and making
+ * them unhappy. We now trust in the BIOS to do things right,
+ * which almost certainly means a new host of problems will
+ * arise with broken BIOS implementations. screw 'em.
+ * We're already intolerant of machines that don't assign
+ * IRQs.
*/
-
- pci_write_config_dword(pcidev, 0x54, 0x00000000);
- pci_write_config_dword(pcidev, 0x56, 0x00000000);
- /*
- * Use TDMA for now. TDMA works on all boards, so while its
- * not the most efficient its the simplest.
- */
+ /* do config work at full power */
+ maestro_power(card,ACPI_D0);
pci_read_config_word(pcidev, 0x50, &w);
- /* Clear DMA bits */
- w&=~(1<<10|1<<9|1<<8);
-
- /* TDMA on */
- w|= (1<<8);
-
- /*
- * Some of these are undocumented bits
- */
-
- w&=~(1<<13)|(1<<14); /* PIC Snoop mode bits */
- w&=~(1<<11); /* Safeguard off */
- w|= (1<<7); /* Posted write */
- w|= (1<<6); /* ISA timing on */
- /* XXX huh? claims to be reserved.. */
- w&=~(1<<5); /* Don't swap left/right */
- w&=~(1<<1); /* Subtractive decode off */
+ w&=~(1<<5); /* Don't swap left/right (undoc)*/
pci_write_config_word(pcidev, 0x50, w);
@@ -2946,19 +3048,9 @@
w&=~(1<<6); /* Debounce off */
w&=~(1<<5); /* GPIO 4:5 */
w|= (1<<4); /* Disconnect from the CHI. Enabling this made a dell 7500 work. */
- w&=~(1<<3); /* IDMA off (undocumented) */
w&=~(1<<2); /* MIDI fix off (undoc) */
w&=~(1<<1); /* reserved, always write 0 */
- w&=~(1<<0); /* IRQ to ISA off (undoc) */
pci_write_config_word(pcidev, 0x52, w);
-
- /*
- * DDMA off
- */
-
- pci_read_config_word(pcidev, 0x60, &w);
- w&=~(1<<0);
- pci_write_config_word(pcidev, 0x60, w);
/*
* Legacy mode
@@ -2971,8 +3063,6 @@
pci_write_config_word(pcidev, 0x40, w);
- /* stake our claim on the iospace */
- request_region(iobase, 256, card_names[card->card_type]);
sound_reset(iobase);
@@ -3140,6 +3230,39 @@
}
+/* this guy tries to find the pci power management
+ * register bank. this should really be in core
+ * code somewhere. 1 on success. */
+int
+parse_power(struct ess_card *card, struct pci_dev *pcidev)
+{
+ u32 n;
+ u16 w;
+ u8 next;
+ int max = 64; /* an a 8bit guy pointing to 32bit guys
+ can only express so much. */
+
+ card->power_regs = 0;
+
+ /* check to see if we have a capabilities list in
+ the config register */
+ pci_read_config_word(pcidev, PCI_STATUS, &w);
+ if(! w & PCI_STATUS_CAP_LIST) return 0;
+
+ /* walk the list, starting at the head. */
+ pci_read_config_byte(pcidev,PCI_CAPABILITY_LIST,&next);
+
+ while(next && max--) {
+ pci_read_config_dword(pcidev, next & ~3, &n);
+ if((n & 0xff) == PCI_CAP_ID_PM) {
+ card->power_regs = next;
+ break;
+ }
+ next = ((n>>8) & 0xff);
+ }
+
+ return card->power_regs ? 1 : 0;
+}
static int
maestro_install(struct pci_dev *pcidev, int card_type)
@@ -3149,7 +3272,7 @@
int i;
struct ess_card *card;
struct ess_state *ess;
- struct pm_dev *pmdev;
+ struct pm_dev *pmdev;
int num = 0;
/* don't pick up weird modem maestros */
@@ -3158,15 +3281,15 @@
iobase = SILLY_PCI_BASE_ADDRESS(pcidev);
- if(check_region(iobase, 256))
+ /* stake our claim on the iospace */
+ if( request_region(iobase, 256, card_names[card_type]) == NULL )
{
printk(KERN_WARNING "maestro: can't allocate 256 bytes I/O at 0x%4.4x\n", iobase);
return 0;
}
/* this was tripping up some machines */
- if(pcidev->irq == 0)
- {
+ if(pcidev->irq == 0) {
printk(KERN_WARNING "maestro: pci subsystem reports irq 0, this might not be correct.\n");
}
@@ -3181,13 +3304,16 @@
}
memset(card, 0, sizeof(*card));
- memcpy(&card->pcidev,pcidev,sizeof(card->pcidev));
+ card->pcidev = pcidev;
- pmdev = pm_register(PM_PCI_DEV,
- PM_PCI_ID(pcidev),
- maestro_pm_callback);
- if (pmdev)
- pmdev->data = card;
+ pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(pcidev),
+ maestro_pm_callback);
+ if (pmdev)
+ pmdev->data = card;
+
+ if (register_reboot_notifier(&maestro_nb)) {
+ printk(KERN_WARNING "maestro: reboot notifier registration failed; may not reboot properly.\n");
+ }
card->iobase = iobase;
card->card_type = card_type;
@@ -3195,6 +3321,7 @@
card->next = devs;
card->magic = ESS_CARD_MAGIC;
spin_lock_init(&card->lock);
+ init_waitqueue_head(&card->suspend_queue);
devs = card;
/* init our groups of 6 apus */
@@ -3246,13 +3373,37 @@
pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &n);
printk(KERN_INFO "maestro: subvendor id: 0x%08x\n",n);
+ /* turn off power management unless:
+ * - the user explicitly asks for it
+ * or
+ * - we're not a 2e, lesser chipps seem to have problems.
+ * - we're not on our _very_ small whitelist. some implemenetations
+ * really dont' like the pm code, others require it.
+ * feel free to expand this as required.
+ */
+#define SUBSYSTEM_VENDOR(x) (x&0xffff)
+ if( (use_pm != 1) &&
+ ((card_type != TYPE_MAESTRO2E) || (SUBSYSTEM_VENDOR(n) != 0x1028)))
+ use_pm = 0;
+
+ if(!use_pm)
+ printk(KERN_INFO "maestro: not attempting power management.\n");
+ else {
+ if(!parse_power(card,pcidev))
+ printk(KERN_INFO "maestro: no PCI power managment interface found.\n");
+ else {
+ pci_read_config_dword(pcidev, card->power_regs, &n);
+ printk(KERN_INFO "maestro: PCI power managment capability: 0x%x\n",n>>16);
+ }
+ }
+
maestro_config(card);
- if(maestro_ac97_get(iobase, 0x00)==0x0080) {
+ if(maestro_ac97_get(card, 0x00)==0x0080) {
printk(KERN_ERR "maestro: my goodness! you seem to have a pt101 codec, which is quite rare.\n"
"\tyou should tell someone about this.\n");
} else {
- maestro_ac97_init(card,iobase);
+ maestro_ac97_init(card);
}
if ((card->dev_mixer = register_sound_mixer(&ess_mixer_fops, -1)) < 0) {
@@ -3273,9 +3424,12 @@
unregister_sound_dsp(s->dev_audio);
}
release_region(card->iobase, 256);
+ unregister_reboot_notifier(&maestro_nb);
kfree(card);
return 0;
}
+ /* now go to sleep 'till something interesting happens */
+ maestro_power(card,ACPI_D2);
printk(KERN_INFO "maestro: %d channels configured.\n", num);
return 1;
@@ -3305,8 +3459,6 @@
printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order);
}
- init_waitqueue_head(&suspend_queue);
-
/*
* Find the ESS Maestro 2.
*/
@@ -3340,56 +3492,78 @@
return 0;
}
-/* --------------------------------------------------------------------- */
-
-#ifdef MODULE
-MODULE_AUTHOR("Zach Brown <zab@redhat.com>, Alan Cox <alan@redhat.com>");
-MODULE_DESCRIPTION("ESS Maestro Driver");
-#ifdef M_DEBUG
-MODULE_PARM(debug,"i");
-#endif
-MODULE_PARM(dsps_order,"i");
-
-void cleanup_module(void)
+static void nuke_maestros(void)
{
- struct ess_card *s;
+ struct ess_card *card;
+ /* we do these unconditionally, which is probably wrong */
pm_unregister_all(maestro_pm_callback);
+ unregister_reboot_notifier(&maestro_nb);
- while ((s = devs)) {
+ while ((card = devs)) {
int i;
devs = devs->next;
/* XXX maybe should force stop bob, but should be all
stopped by _release by now */
- free_irq(s->irq, s);
- unregister_sound_mixer(s->dev_mixer);
+ free_irq(card->irq, card);
+ unregister_sound_mixer(card->dev_mixer);
for(i=0;i<NR_DSPS;i++)
{
- struct ess_state *ess = &s->channels[i];
+ struct ess_state *ess = &card->channels[i];
if(ess->dev_audio != -1)
unregister_sound_dsp(ess->dev_audio);
}
- release_region(s->iobase, 256);
- kfree(s);
+ /* Goodbye, Mr. Bond. */
+ maestro_power(card,ACPI_D3);
+ release_region(card->iobase, 256);
+ kfree(card);
}
+ devs = NULL;
+}
+
+static int maestro_notifier(struct notifier_block *nb, unsigned long event, void *buf)
+{
+ /* this notifier is called when the kernel is really shut down. */
+ M_printk("maestro: shutting down\n");
+ nuke_maestros();
+ return NOTIFY_OK;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+MODULE_AUTHOR("Zach Brown <zab@zabbo.net>, Alan Cox <alan@redhat.com>");
+MODULE_DESCRIPTION("ESS Maestro Driver");
+#ifdef M_DEBUG
+MODULE_PARM(debug,"i");
+#endif
+MODULE_PARM(dsps_order,"i");
+MODULE_PARM(use_pm,"i");
+
+void cleanup_module(void) {
M_printk("maestro: unloading\n");
+ nuke_maestros();
}
-#endif /* MODULE */
+#else /* MODULE */
+__initcall(init_maestro);
+#endif
+
+/* --------------------------------------------------------------------- */
void
-check_suspend(void)
+check_suspend(struct ess_card *card)
{
DECLARE_WAITQUEUE(wait, current);
- if(!in_suspend) return;
+ if(!card->in_suspend) return;
- in_suspend++;
- add_wait_queue(&suspend_queue, &wait);
+ card->in_suspend++;
+ add_wait_queue(&(card->suspend_queue), &wait);
current->state = TASK_UNINTERRUPTIBLE;
schedule();
- remove_wait_queue(&suspend_queue, &wait);
+ remove_wait_queue(&(card->suspend_queue), &wait);
current->state = TASK_RUNNING;
}
@@ -3397,113 +3571,138 @@
maestro_suspend(struct ess_card *card)
{
unsigned long flags;
- int i,j;
+ int i,j;
- save_flags(flags);
- cli();
+ save_flags(flags);
+ cli(); /* over-kill */
- M_printk("maestro: pm in dev %p\n",card);
+ M_printk("maestro: apm in dev %p\n",card);
- for(i=0;i<NR_DSPS;i++) {
- struct ess_state *s = &card->channels[i];
+ /* we have to read from the apu regs, need
+ to power it up */
+ maestro_power(card,ACPI_D0);
- if(s->dev_audio == -1)
- continue;
-
- M_printk("maestro: stopping apus for device %d\n",i);
- stop_dac(s);
- stop_adc(s);
- for(j=0;j<6;j++)
- card->apu_map[s->apu[j]][5]=apu_get_register(s,j,5);
-
- }
+ for(i=0;i<NR_DSPS;i++) {
+ struct ess_state *s = &card->channels[i];
- /* get rid of interrupts? */
- if( card->dsps_open > 0)
- stop_bob(&card->channels[0]);
+ if(s->dev_audio == -1)
+ continue;
- in_suspend=1;
+ M_printk("maestro: stopping apus for device %d\n",i);
+ stop_dac(s);
+ stop_adc(s);
+ for(j=0;j<6;j++)
+ card->apu_map[s->apu[j]][5]=apu_get_register(s,j,5);
- restore_flags(flags);
+ }
+
+ /* get rid of interrupts? */
+ if( card->dsps_open > 0)
+ stop_bob(&card->channels[0]);
- /* we'll let the bios do the rest of the power down.. */
+ card->in_suspend++;
+ restore_flags(flags);
+
+ /* we trust in the bios to power down the chip on suspend.
+ * XXX I'm also not sure that in_suspend will protect
+ * against all reg accesses from here on out.
+ */
return 0;
}
static int
maestro_resume(struct ess_card *card)
{
unsigned long flags;
- int i;
+ int i;
save_flags(flags);
- cli();
- in_suspend=0;
- M_printk("maestro: resuming\n");
-
- /* first lets just bring everything back. .*/
-
- M_printk("maestro: pm in dev %p\n",card);
-
- maestro_config(card);
- /* need to restore the base pointers.. */
- if(card->dmapages)
- set_base_registers(&card->channels[0],card->dmapages);
-
- mixer_push_state(card);
-
- for(i=0;i<NR_DSPS;i++) {
- struct ess_state *s = &card->channels[i];
- int chan,reg;
-
- if(s->dev_audio == -1)
- continue;
-
- for(chan = 0 ; chan < 6 ; chan++) {
- wave_set_register(s,s->apu[chan]<<3,s->apu_base[chan]);
- for(reg = 1 ; reg < NR_APU_REGS ; reg++)
- apu_set_register(s,chan,reg,s->card->apu_map[s->apu[chan]][reg]);
- }
- for(chan = 0 ; chan < 6 ; chan++)
- apu_set_register(s,chan,0,s->card->apu_map[s->apu[chan]][0] & 0xFF0F);
- }
+ cli(); /* over-kill */
+
+ card->in_suspend = 0;
+
+ M_printk("maestro: resuming card at %p\n",card);
+
+ /* restore all our config */
+ maestro_config(card);
+ /* need to restore the base pointers.. */
+ if(card->dmapages)
+ set_base_registers(&card->channels[0],card->dmapages);
+
+ mixer_push_state(card);
+
+ /* set each channels' apu control registers before
+ * restoring audio
+ */
+ for(i=0;i<NR_DSPS;i++) {
+ struct ess_state *s = &card->channels[i];
+ int chan,reg;
+
+ if(s->dev_audio == -1)
+ continue;
+
+ for(chan = 0 ; chan < 6 ; chan++) {
+ wave_set_register(s,s->apu[chan]<<3,s->apu_base[chan]);
+ for(reg = 1 ; reg < NR_APU_REGS ; reg++)
+ apu_set_register(s,chan,reg,s->card->apu_map[s->apu[chan]][reg]);
+ }
+ for(chan = 0 ; chan < 6 ; chan++)
+ apu_set_register(s,chan,0,s->card->apu_map[s->apu[chan]][0] & 0xFF0F);
+ }
/* now we flip on the music */
- M_printk("maestro: pm in dev %p\n",card);
- for(i=0;i<NR_DSPS;i++) {
- struct ess_state *s = &card->channels[i];
-
- /* these use the apu_mode, and can handle
- spurious calls */
- start_dac(s);
- start_adc(s);
- }
- if( card->dsps_open > 0)
- start_bob(&card->channels[0]);
+ if( card->dsps_open <= 0) {
+ /* this card's idle */
+ maestro_power(card,ACPI_D2);
+ } else {
+ /* ok, we're actually playing things on
+ this card */
+ maestro_power(card,ACPI_D0);
+ start_bob(&card->channels[0]);
+ for(i=0;i<NR_DSPS;i++) {
+ struct ess_state *s = &card->channels[i];
+
+ /* these use the apu_mode, and can handle
+ spurious calls */
+ start_dac(s);
+ start_adc(s);
+ }
+ }
restore_flags(flags);
- wake_up(&suspend_queue);
+ /* all right, we think things are ready,
+ wake up people who were using the device
+ when we suspended */
+ wake_up(&(card->suspend_queue));
return 0;
}
int
-maestro_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) {
- struct ess_card *card = (struct ess_card*) dev->data;
- if (card) {
- M_printk("maestro: pm event received: 0x%x\n", rqst);
-
- switch (rqst) {
- case PM_SUSPEND:
- maestro_suspend(card);
- break;
- case PM_RESUME:
- maestro_resume(card);
- break;
- }
- }
+maestro_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+ struct ess_card *card = (struct ess_card*) dev->data;
+
+ if ( ! card ) goto out;
+ M_printk("maestro: pm event 0x%x received for card %p\n", rqst, card);
+
+ switch (rqst) {
+ case PM_SUSPEND:
+ maestro_suspend(card);
+ break;
+ case PM_RESUME:
+ maestro_resume(card);
+ break;
+ /*
+ * we'd also like to find out about
+ * power level changes because some biosen
+ * do mean things to the maestro when they
+ * change their power state.
+ */
+ }
+out:
return 0;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)