[ALSA] azt3328.c: add suspend/resume support

- add suspend/resume handlers
- fix problem (private_data members not set)
Playing a file while suspending will resume correctly with this patch,
so I assume the hardware to get fully correctly reinitialized with
this patch.

Signed-off-by: Andreas Mohr <andi@lisas.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Andreas Mohr 2006-05-17 11:02:24 +02:00 committed by Jaroslav Kysela
parent 746df94898
commit ca54bde363
2 changed files with 133 additions and 6 deletions

View file

@ -90,9 +90,11 @@
*
* TODO
* - test MPU401 MIDI playback etc.
* - power management. See e.g. intel8x0 or cs4281.
* This would be nice since the chip runs a bit hot, and it's *required*
* anyway for proper ACPI power management.
* - add some power micro-management (disable various units of the card
* as long as they're unused). However this requires I/O ports which I
* haven't figured out yet and which thus might not even exist...
* The standard suspend/resume functionality could probably make use of
* some improvement, too...
* - figure out what all unknown port bits are responsible for
*/
@ -214,6 +216,16 @@ struct snd_azf3328 {
struct pci_dev *pci;
int irq;
#ifdef CONFIG_PM
/* register value containers for power management
* Note: not always full I/O range preserved (just like Win driver!) */
u16 saved_regs_codec [AZF_IO_SIZE_CODEC_PM / 2];
u16 saved_regs_io2 [AZF_IO_SIZE_IO2_PM / 2];
u16 saved_regs_mpu [AZF_IO_SIZE_MPU_PM / 2];
u16 saved_regs_synth[AZF_IO_SIZE_SYNTH_PM / 2];
u16 saved_regs_mixer[AZF_IO_SIZE_MIXER_PM / 2];
#endif
};
static const struct pci_device_id snd_azf3328_ids[] __devinitdata = {
@ -961,6 +973,13 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
chip->is_playing = 1;
snd_azf3328_dbgplay("STARTED PLAYBACK\n");
break;
case SNDRV_PCM_TRIGGER_RESUME:
snd_azf3328_dbgplay("RESUME PLAYBACK\n");
/* resume playback if we were active */
if (chip->is_playing)
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) | DMA_RESUME);
break;
case SNDRV_PCM_TRIGGER_STOP:
snd_azf3328_dbgplay("STOP PLAYBACK\n");
@ -988,6 +1007,12 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
chip->is_playing = 0;
snd_azf3328_dbgplay("STOPPED PLAYBACK\n");
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
snd_azf3328_dbgplay("SUSPEND PLAYBACK\n");
/* make sure playback is stopped */
snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) & ~DMA_RESUME);
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n");
break;
@ -995,6 +1020,7 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
break;
default:
printk(KERN_ERR "FIXME: unknown trigger mode!\n");
return -EINVAL;
}
@ -1068,6 +1094,13 @@ snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd)
chip->is_recording = 1;
snd_azf3328_dbgplay("STARTED CAPTURE\n");
break;
case SNDRV_PCM_TRIGGER_RESUME:
snd_azf3328_dbgplay("RESUME CAPTURE\n");
/* resume recording if we were active */
if (chip->is_recording)
snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS) | DMA_RESUME);
break;
case SNDRV_PCM_TRIGGER_STOP:
snd_azf3328_dbgplay("STOP CAPTURE\n");
@ -1088,6 +1121,12 @@ snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd)
chip->is_recording = 0;
snd_azf3328_dbgplay("STOPPED CAPTURE\n");
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
snd_azf3328_dbgplay("SUSPEND CAPTURE\n");
/* make sure recording is stopped */
snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS) & ~DMA_RESUME);
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n");
break;
@ -1095,6 +1134,7 @@ snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd)
snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
break;
default:
printk(KERN_ERR "FIXME: unknown trigger mode!\n");
return -EINVAL;
}
@ -1766,6 +1806,8 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
goto out_err;
}
card->private_data = chip;
if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_MPU401,
chip->mpu_port, 1, pci->irq, 0,
&chip->rmidi)) < 0) {
@ -1791,6 +1833,8 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
}
}
opl3->private_data = chip;
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, chip->codec_port, chip->irq);
@ -1834,11 +1878,80 @@ snd_azf3328_remove(struct pci_dev *pci)
snd_azf3328_dbgcallleave();
}
#ifdef CONFIG_PM
static int
snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
{
struct snd_card *card = pci_get_drvdata(pci);
struct snd_azf3328 *chip = card->private_data;
int reg;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
snd_pcm_suspend_all(chip->pcm);
for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; reg++)
chip->saved_regs_mixer[reg] = inw(chip->mixer_port + reg * 2);
/* make sure to disable master volume etc. to prevent looping sound */
snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1);
snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; reg++)
chip->saved_regs_codec[reg] = inw(chip->codec_port + reg * 2);
for (reg = 0; reg < AZF_IO_SIZE_IO2_PM / 2; reg++)
chip->saved_regs_io2[reg] = inw(chip->io2_port + reg * 2);
for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; reg++)
chip->saved_regs_mpu[reg] = inw(chip->mpu_port + reg * 2);
for (reg = 0; reg < AZF_IO_SIZE_SYNTH_PM / 2; reg++)
chip->saved_regs_synth[reg] = inw(chip->synth_port + reg * 2);
pci_set_power_state(pci, PCI_D3hot);
pci_disable_device(pci);
pci_save_state(pci);
return 0;
}
static int
snd_azf3328_resume(struct pci_dev *pci)
{
struct snd_card *card = pci_get_drvdata(pci);
struct snd_azf3328 *chip = card->private_data;
int reg;
pci_restore_state(pci);
pci_enable_device(pci);
pci_set_power_state(pci, PCI_D0);
pci_set_master(pci);
for (reg = 0; reg < AZF_IO_SIZE_IO2_PM / 2; reg++)
outw(chip->saved_regs_io2[reg], chip->io2_port + reg * 2);
for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; reg++)
outw(chip->saved_regs_mpu[reg], chip->mpu_port + reg * 2);
for (reg = 0; reg < AZF_IO_SIZE_SYNTH_PM / 2; reg++)
outw(chip->saved_regs_synth[reg], chip->synth_port + reg * 2);
for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; reg++)
outw(chip->saved_regs_mixer[reg], chip->mixer_port + reg * 2);
for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; reg++)
outw(chip->saved_regs_codec[reg], chip->codec_port + reg * 2);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
#endif
static struct pci_driver driver = {
.name = "AZF3328",
.id_table = snd_azf3328_ids,
.probe = snd_azf3328_probe,
.remove = __devexit_p(snd_azf3328_remove),
#ifdef CONFIG_PM
.suspend = snd_azf3328_suspend,
.resume = snd_azf3328_resume,
#endif
};
static int __init

View file

@ -5,6 +5,9 @@
/*** main I/O area port indices ***/
/* (only 0x70 of 0x80 bytes saved/restored by Windows driver) */
#define AZF_IO_SIZE_CODEC 0x80
#define AZF_IO_SIZE_CODEC_PM 0x70
/* the driver initialisation suggests a layout of 4 main areas:
* from 0x00 (playback), from 0x20 (recording) and from 0x40 (maybe MPU401??).
* And another area from 0x60 to 0x6f (DirectX timer, IRQ management,
@ -107,7 +110,8 @@
#define IRQ_UNKNOWN2 0x0080 /* probably unused */
#define IDX_IO_66H 0x66 /* writing 0xffff returns 0x0000 */
#define IDX_IO_SOME_VALUE 0x68 /* this is set to e.g. 0x3ff or 0x300, and writable; maybe some buffer limit, but I couldn't find out more, PU:0x00ff */
#define IDX_IO_6AH 0x6A /* this WORD can be set to have bits 0x0028 activated; actually inhibits PCM playback!!! maybe power management?? */
#define IDX_IO_6AH 0x6A /* this WORD can be set to have bits 0x0028 activated (FIXME: correct??); actually inhibits PCM playback!!! maybe power management?? */
#define IO_6A_PAUSE_PLAYBACK 0x0200 /* bit 9; sure, this pauses playback, but what the heck is this really about?? */
#define IDX_IO_6CH 0x6C
#define IDX_IO_6EH 0x6E /* writing 0xffff returns 0x83fe */
/* further I/O indices not saved/restored, so probably not used */
@ -115,15 +119,25 @@
/*** I/O 2 area port indices ***/
/* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */
#define AZF_IO_SIZE_IO2 0x08
#define AZF_IO_SIZE_IO2_PM 0x06
#define IDX_IO2_LEGACY_ADDR 0x04
#define LEGACY_SOMETHING 0x01 /* OPL3?? */
#define LEGACY_JOY 0x08
#define AZF_IO_SIZE_MPU 0x04
#define AZF_IO_SIZE_MPU_PM 0x04
#define AZF_IO_SIZE_SYNTH 0x08
#define AZF_IO_SIZE_SYNTH_PM 0x06
/*** mixer I/O area port indices ***/
/* (only 0x22 of 0x40 bytes saved/restored by Windows driver)
* generally spoken: AC97 register index = AZF3328 mixer reg index + 2
* (in other words: AZF3328 NOT fully AC97 compliant) */
* UNFORTUNATELY azf3328 is NOT truly AC97 compliant: see main file intro */
#define AZF_IO_SIZE_MIXER 0x40
#define AZF_IO_SIZE_MIXER_PM 0x22
#define MIXER_VOLUME_RIGHT_MASK 0x001f
#define MIXER_VOLUME_LEFT_MASK 0x1f00
#define MIXER_MUTE_MASK 0x8000