patch-2.4.14 linux/drivers/sound/i810_audio.c
Next file: linux/drivers/sound/ite8172.c
Previous file: linux/drivers/sound/emu10k1/hwaccess.h
Back to the patch index
Back to the overall index
- Lines: 619
- Date:
Thu Oct 25 13:53:52 2001
- Orig file:
v2.4.13/linux/drivers/sound/i810_audio.c
- Orig date:
Tue Oct 9 17:06:52 2001
diff -u --recursive --new-file v2.4.13/linux/drivers/sound/i810_audio.c linux/drivers/sound/i810_audio.c
@@ -247,6 +247,12 @@
MODULE_DEVICE_TABLE (pci, i810_pci_tbl);
+#ifdef CONFIG_PM
+#define PM_SUSPENDED(card) (card->pm_suspended)
+#else
+#define PM_SUSPENDED(card) (0)
+#endif
+
/* "software" or virtual channel, an instance of opened /dev/dsp */
struct i810_state {
unsigned int magic;
@@ -262,6 +268,9 @@
/* virtual channel number */
int virt;
+#ifdef CONFIG_PM
+ unsigned int pm_saved_dac_rate,pm_saved_adc_rate;
+#endif
struct dmabuf {
/* wave sample stuff */
unsigned int rate;
@@ -322,7 +331,11 @@
/* PCI device stuff */
struct pci_dev * pci_dev;
u16 pci_id;
-
+#ifdef CONFIG_PM
+ u16 pm_suspended;
+ u32 pm_save_state[64/sizeof(u32)];
+ int pm_saved_mixer_settings[SOUND_MIXER_NRDEVICES][NR_AC97];
+#endif
/* soundcore stuff */
int dev_audio;
@@ -451,7 +464,7 @@
if(!(state->card->ac97_features & 4)) {
#ifdef DEBUG
- printk(KERN_WARNING "i810_audio: S/PDIF transmitter not avalible.\n");
+ printk(KERN_WARNING "i810_audio: S/PDIF transmitter not available.\n");
#endif
state->card->ac97_status &= ~SPDIF_ON;
} else {
@@ -572,6 +585,10 @@
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;
}
@@ -594,11 +611,11 @@
if(new_rate != rate) {
dmabuf->rate = (new_rate * 48000)/clocking;
- rate = new_rate;
}
#ifdef DEBUG
- printk("i810_audio: called i810_set_dac_rate : rate = %d/%d\n", dmabuf->rate, rate);
+ printk("i810_audio: called i810_set_dac_rate : asked for %d, got %d\n", rate, dmabuf->rate);
#endif
+ rate = new_rate;
return dmabuf->rate;
}
@@ -1066,7 +1083,7 @@
for (;;) {
/* It seems that we have to set the current state to TASK_INTERRUPTIBLE
every time to make the process really go to sleep */
- current->state = TASK_INTERRUPTIBLE;
+ set_current_state(TASK_INTERRUPTIBLE);
spin_lock_irqsave(&state->card->lock, flags);
i810_update_ptr(state);
@@ -1085,7 +1102,7 @@
if (nonblock) {
remove_wait_queue(&dmabuf->wait, &wait);
- current->state = TASK_RUNNING;
+ set_current_state(TASK_RUNNING);
return -EBUSY;
}
@@ -1099,7 +1116,7 @@
stop_dac(state);
synchronize_irq();
remove_wait_queue(&dmabuf->wait, &wait);
- current->state = TASK_RUNNING;
+ set_current_state(TASK_RUNNING);
if (signal_pending(current))
return -ERESTARTSYS;
@@ -1201,16 +1218,20 @@
spin_unlock(&card->lock);
}
-/* in this loop, dmabuf.count signifies the amount of data that is waiting to be copied to
- the user's buffer. it is filled by the dma machine and drained by this loop. */
+/* in this loop, dmabuf.count signifies the amount of data that is
+ waiting to be copied to the user's buffer. It is filled by the dma
+ machine and drained by this loop. */
+
static ssize_t i810_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
{
struct i810_state *state = (struct i810_state *)file->private_data;
+ struct i810_card *card=state ? state->card : 0;
struct dmabuf *dmabuf = &state->dmabuf;
ssize_t ret;
unsigned long flags;
unsigned int swptr;
int cnt;
+ DECLARE_WAITQUEUE(waita, current);
#ifdef DEBUG2
printk("i810_audio: i810_read called, count = %d\n", count);
@@ -1224,7 +1245,7 @@
return -ENODEV;
if (!dmabuf->read_channel) {
dmabuf->ready = 0;
- dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card);
+ dmabuf->read_channel = card->alloc_rec_pcm_channel(card);
if (!dmabuf->read_channel) {
return -EBUSY;
}
@@ -1236,8 +1257,19 @@
dmabuf->trigger &= ~PCM_ENABLE_OUTPUT;
ret = 0;
+ add_wait_queue(&dmabuf->wait, &waita);
while (count > 0) {
- spin_lock_irqsave(&state->card->lock, flags);
+ spin_lock_irqsave(&card->lock, flags);
+ if (PM_SUSPENDED(card)) {
+ spin_unlock_irqrestore(&card->lock, flags);
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
+ if (signal_pending(current)) {
+ if (!ret) ret = -EAGAIN;
+ break;
+ }
+ continue;
+ }
swptr = dmabuf->swptr;
if (dmabuf->count > dmabuf->dmasize) {
dmabuf->count = dmabuf->dmasize;
@@ -1246,7 +1278,7 @@
// this is to make the copy_to_user simpler below
if(cnt > (dmabuf->dmasize - swptr))
cnt = dmabuf->dmasize - swptr;
- spin_unlock_irqrestore(&state->card->lock, flags);
+ spin_unlock_irqrestore(&card->lock, flags);
if (cnt > count)
cnt = count;
@@ -1290,15 +1322,20 @@
if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) {
if (!ret) ret = -EFAULT;
- return ret;
+ goto done;
}
swptr = (swptr + cnt) % dmabuf->dmasize;
- spin_lock_irqsave(&state->card->lock, flags);
+ spin_lock_irqsave(&card->lock, flags);
+
+ if (PM_SUSPENDED(card)) {
+ spin_unlock_irqrestore(&card->lock, flags);
+ continue;
+ }
dmabuf->swptr = swptr;
dmabuf->count -= cnt;
- spin_unlock_irqrestore(&state->card->lock, flags);
+ spin_unlock_irqrestore(&card->lock, flags);
count -= cnt;
buffer += cnt;
@@ -1307,6 +1344,10 @@
i810_update_lvi(state,1);
if(!(dmabuf->enable & ADC_RUNNING))
start_adc(state);
+ done:
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&dmabuf->wait, &waita);
+
return ret;
}
@@ -1315,11 +1356,13 @@
static ssize_t i810_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
{
struct i810_state *state = (struct i810_state *)file->private_data;
+ struct i810_card *card=state ? state->card : 0;
struct dmabuf *dmabuf = &state->dmabuf;
ssize_t ret;
unsigned long flags;
unsigned int swptr = 0;
int cnt, x;
+ DECLARE_WAITQUEUE(waita, current);
#ifdef DEBUG2
printk("i810_audio: i810_write called, count = %d\n", count);
@@ -1333,7 +1376,7 @@
return -ENODEV;
if (!dmabuf->write_channel) {
dmabuf->ready = 0;
- dmabuf->write_channel = state->card->alloc_pcm_channel(state->card);
+ dmabuf->write_channel = card->alloc_pcm_channel(card);
if(!dmabuf->write_channel)
return -EBUSY;
}
@@ -1344,8 +1387,20 @@
dmabuf->trigger &= ~PCM_ENABLE_INPUT;
ret = 0;
+ add_wait_queue(&dmabuf->wait, &waita);
while (count > 0) {
spin_lock_irqsave(&state->card->lock, flags);
+ if (PM_SUSPENDED(card)) {
+ spin_unlock_irqrestore(&card->lock, flags);
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
+ if (signal_pending(current)) {
+ if (!ret) ret = -EAGAIN;
+ break;
+ }
+ continue;
+ }
+
swptr = dmabuf->swptr;
if (dmabuf->count < 0) {
dmabuf->count = 0;
@@ -1376,7 +1431,7 @@
i810_update_lvi(state,0);
if (file->f_flags & O_NONBLOCK) {
if (!ret) ret = -EAGAIN;
- return ret;
+ goto ret;
}
/* Not strictly correct but works */
tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 4);
@@ -1400,25 +1455,30 @@
}
if (signal_pending(current)) {
if (!ret) ret = -ERESTARTSYS;
- return ret;
+ goto ret;
}
continue;
}
if (copy_from_user(dmabuf->rawbuf+swptr,buffer,cnt)) {
if (!ret) ret = -EFAULT;
- return ret;
+ goto ret;
}
swptr = (swptr + cnt) % dmabuf->dmasize;
spin_lock_irqsave(&state->card->lock, flags);
+ if (PM_SUSPENDED(card)) {
+ spin_unlock_irqrestore(&card->lock, flags);
+ continue;
+ }
+
dmabuf->swptr = swptr;
dmabuf->count += cnt;
- spin_unlock_irqrestore(&state->card->lock, flags);
count -= cnt;
buffer += cnt;
ret += cnt;
+ spin_unlock_irqrestore(&state->card->lock, flags);
}
if (swptr % dmabuf->fragsize) {
x = dmabuf->fragsize - (swptr % dmabuf->fragsize);
@@ -1427,6 +1487,9 @@
i810_update_lvi(state,0);
if (!dmabuf->enable && dmabuf->count >= dmabuf->userfragsize)
start_dac(state);
+ ret:
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&dmabuf->wait, &waita);
return ret;
}
@@ -2310,18 +2373,52 @@
open: i810_open_mixdev,
};
-/* AC97 codec initialisation. */
-static int __init i810_ac97_init(struct i810_card *card)
+/* AC97 codec initialisation. These small functions exist so we don't
+ duplicate code between module init and apm resume */
+
+static inline int i810_ac97_exists(struct i810_card *card,int ac97_number)
{
- int num_ac97 = 0;
- int total_channels = 0;
- struct ac97_codec *codec;
- u16 eid;
- int i=0;
- u32 reg;
+ u32 reg = inl(card->iobase + GLOB_STA);
+ return (reg & (0x100 << ac97_number));
+}
- reg = inl(card->iobase + GLOB_CNT);
+static inline int i810_ac97_enable_variable_rate(struct ac97_codec *codec)
+{
+ i810_ac97_set(codec, AC97_EXTENDED_STATUS, 9);
+ i810_ac97_set(codec,AC97_EXTENDED_STATUS,
+ i810_ac97_get(codec, AC97_EXTENDED_STATUS)|0xE800);
+ return (i810_ac97_get(codec, AC97_EXTENDED_STATUS)&1);
+}
+
+
+static int i810_ac97_probe_and_powerup(struct i810_card *card,struct ac97_codec *codec)
+{
+ /* Returns 0 on failure */
+ int i;
+
+ if (ac97_probe_codec(codec) == 0) return 0;
+
+ /* power it all up */
+ i810_ac97_set(codec, AC97_POWER_CONTROL,
+ i810_ac97_get(codec, AC97_POWER_CONTROL) & ~0x7f00);
+ /* wait for analog ready */
+ for (i=10;
+ i && ((i810_ac97_get(codec, AC97_POWER_CONTROL) & 0xf) != 0xf);
+ i--)
+ {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/20);
+ }
+ return i;
+}
+
+/* if I knew what this did, I'd give it a better name */
+static int i810_ac97_random_init_stuff(struct i810_card *card)
+{
+ u32 reg = inl(card->iobase + GLOB_CNT);
+ int i;
+
if((reg&2)==0) /* Cold required */
reg|=2;
else
@@ -2330,13 +2427,13 @@
reg&=~8; /* ACLink on */
outl(reg , card->iobase + GLOB_CNT);
- while(i<10)
+ for(i=0;i<10;i++)
{
if((inl(card->iobase+GLOB_CNT)&4)==0)
break;
- current->state = TASK_UNINTERRUPTIBLE;
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/20);
- i++;
}
if(i==10)
{
@@ -2344,8 +2441,22 @@
return 0;
}
- current->state = TASK_UNINTERRUPTIBLE;
- schedule_timeout(HZ/5);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/2);
+ reg = inl(card->iobase + GLOB_STA);
+ inw(card->ac97base);
+ return 1;
+}
+
+static int __init i810_ac97_init(struct i810_card *card)
+{
+ int num_ac97 = 0;
+ int total_channels = 0;
+ struct ac97_codec *codec;
+ u16 eid;
+ u32 reg;
+
+ if(!i810_ac97_random_init_stuff(card)) return 0;
/* Number of channels supported */
/* What about the codec? Just because the ICH supports */
@@ -2371,10 +2482,10 @@
/* check the ready status before probing. So we chk */
/* What do we do if it's not ready? Wait and try */
/* again, or abort? */
- reg = inl(card->iobase + GLOB_STA);
- if (!(reg & (0x100 << num_ac97))) {
+ if (!i810_ac97_exists(card,num_ac97)) {
if(num_ac97 == 0)
printk(KERN_ERR "i810_audio: Primary codec not ready.\n");
+ card->ac97_codec[num_ac97] = 0;
break; /* I think this works, if not ready stop */
}
@@ -2390,24 +2501,13 @@
codec->codec_read = i810_ac97_get;
codec->codec_write = i810_ac97_set;
- if (ac97_probe_codec(codec) == 0)
- break;
-
- /* power up everything, modify this when implementing power saving */
- i810_ac97_set(codec, AC97_POWER_CONTROL,
- i810_ac97_get(codec, AC97_POWER_CONTROL) & ~0x7f00);
- /* wait for analog ready */
- for (i=10;
- i && ((i810_ac97_get(codec, AC97_POWER_CONTROL) & 0xf) != 0xf);
- i--)
- {
- current->state = TASK_UNINTERRUPTIBLE;
- schedule_timeout(HZ/20);
+ if(!i810_ac97_probe_and_powerup(card,codec)) {
+ printk("i810_audio: timed out waiting for codec %d analog ready", num_ac97);
+ break; /* it didn't work */
}
-
/* Store state information about S/PDIF transmitter */
card->ac97_status = 0;
-
+
/* Don't attempt to get eid until powerup is complete */
eid = i810_ac97_get(codec, AC97_EXTENDED_ID);
@@ -2427,16 +2527,10 @@
printk(KERN_WARNING "i810_audio: only 48Khz playback available.\n");
else
{
- /* Enable variable rate mode */
- i810_ac97_set(codec, AC97_EXTENDED_STATUS, 9);
- i810_ac97_set(codec,AC97_EXTENDED_STATUS,
- i810_ac97_get(codec, AC97_EXTENDED_STATUS)|0xE800);
-
- if(!(i810_ac97_get(codec, AC97_EXTENDED_STATUS)&1))
- {
+ if(!i810_ac97_enable_variable_rate(codec)) {
printk(KERN_WARNING "i810_audio: Codec refused to allow VRA, using 48Khz only.\n");
card->ac97_features&=~1;
- }
+ }
}
/* Determine how many channels the codec(s) support */
@@ -2609,6 +2703,9 @@
card->irq = pci_dev->irq;
card->next = devs;
card->magic = I810_CARD_MAGIC;
+#ifdef CONFIG_PM
+ card->pm_suspended=0;
+#endif
spin_lock_init(&card->lock);
devs = card;
@@ -2697,6 +2794,131 @@
kfree(card);
}
+#ifdef CONFIG_PM
+static int i810_pm_suspend(struct pci_dev *dev, u32 pm_state)
+{
+ struct i810_card *card = dev->driver_data;
+ struct i810_state *state;
+ unsigned long flags;
+ struct dmabuf *dmabuf;
+ int i,num_ac97;
+#ifdef DEBUG
+ printk("i810_audio: i810_pm_suspend called\n");
+#endif
+ if(!card) return 0;
+ spin_lock_irqsave(&card->lock, flags);
+ card->pm_suspended=1;
+ for(i=0;i<NR_HW_CH;i++) {
+ state = card->states[i];
+ if(!state) continue;
+ /* this happens only if there are open files */
+ dmabuf = &state->dmabuf;
+ if(dmabuf->enable & DAC_RUNNING ||
+ (dmabuf->count && (dmabuf->trigger & PCM_ENABLE_OUTPUT))) {
+ state->pm_saved_dac_rate=dmabuf->rate;
+ stop_dac(state);
+ } else {
+ state->pm_saved_dac_rate=0;
+ }
+ if(dmabuf->enable & ADC_RUNNING) {
+ state->pm_saved_adc_rate=dmabuf->rate;
+ stop_adc(state);
+ } else {
+ state->pm_saved_adc_rate=0;
+ }
+ dmabuf->ready = 0;
+ dmabuf->swptr = dmabuf->hwptr = 0;
+ dmabuf->count = dmabuf->total_bytes = 0;
+ }
+
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ /* save mixer settings */
+ for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) {
+ struct ac97_codec *codec = card->ac97_codec[num_ac97];
+ if(!codec) continue;
+ for(i=0;i< SOUND_MIXER_NRDEVICES ;i++) {
+ if((supported_mixer(codec,i)) &&
+ (codec->read_mixer)) {
+ card->pm_saved_mixer_settings[i][num_ac97]=
+ codec->read_mixer(codec,i);
+ }
+ }
+ }
+ pci_save_state(dev,card->pm_save_state); /* XXX do we need this? */
+ pci_disable_device(dev); /* disable busmastering */
+ pci_set_power_state(dev,3); /* Zzz. */
+
+ return 0;
+}
+
+
+static int i810_pm_resume(struct pci_dev *dev)
+{
+ int num_ac97,i=0;
+ struct i810_card *card=(struct i810_card *)dev->driver_data;
+ pci_enable_device(dev);
+ pci_restore_state (dev,card->pm_save_state);
+
+ /* observation of a toshiba portege 3440ct suggests that the
+ hardware has to be more or less completely reinitialized from
+ scratch after an apm suspend. Works For Me. -dan */
+
+ i810_ac97_random_init_stuff(card);
+
+ for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) {
+ struct ac97_codec *codec = card->ac97_codec[num_ac97];
+ /* check they haven't stolen the hardware while we were
+ away */
+ if(!i810_ac97_exists(card,num_ac97)) {
+ if(num_ac97) continue;
+ else BUG();
+ }
+ if(!i810_ac97_probe_and_powerup(card,codec)) BUG();
+
+ if((card->ac97_features&0x0001)) {
+ /* at probe time we found we could do variable
+ rates, but APM suspend has made it forget
+ its magical powers */
+ if(!i810_ac97_enable_variable_rate(codec)) BUG();
+ }
+ /* we lost our mixer settings, so restore them */
+ for(i=0;i< SOUND_MIXER_NRDEVICES ;i++) {
+ if(supported_mixer(codec,i)){
+ int val=card->
+ pm_saved_mixer_settings[i][num_ac97];
+ codec->mixer_state[i]=val;
+ codec->write_mixer(codec,i,
+ (val & 0xff) ,
+ ((val >> 8) & 0xff) );
+ }
+ }
+ }
+
+ /* we need to restore the sample rate from whatever it was */
+ for(i=0;i<NR_HW_CH;i++) {
+ struct i810_state * state=card->states[i];
+ if(state) {
+ if(state->pm_saved_adc_rate)
+ i810_set_adc_rate(state,state->pm_saved_adc_rate);
+ if(state->pm_saved_dac_rate)
+ i810_set_dac_rate(state,state->pm_saved_dac_rate);
+ }
+ }
+
+
+ card->pm_suspended = 0;
+
+ /* any processes that were reading/writing during the suspend
+ probably ended up here */
+ for(i=0;i<NR_HW_CH;i++) {
+ struct i810_state *state = card->states[i];
+ if(state) wake_up(&state->dmabuf.wait);
+ }
+
+ return 0;
+}
+#endif /* CONFIG_PM */
MODULE_AUTHOR("");
MODULE_DESCRIPTION("Intel 810 audio support");
@@ -2713,6 +2935,10 @@
id_table: i810_pci_tbl,
probe: i810_probe,
remove: i810_remove,
+#ifdef CONFIG_PM
+ suspend: i810_pm_suspend,
+ resume: i810_pm_resume,
+#endif /* CONFIG_PM */
};
@@ -2753,3 +2979,9 @@
module_init(i810_init_module);
module_exit(i810_cleanup_module);
+
+/*
+Local Variables:
+c-basic-offset: 8
+End:
+*/
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)