/*
* The driver for the ForteMedia FM801 based soundcards
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
+ * Support FM only card by Andy Shevchenko <andy@smile.org.ua>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/moduleparam.h>
#include <sound/core.h>
#include <sound/pcm.h>
+#include <sound/tlv.h>
#include <sound/ac97_codec.h>
#include <sound/mpu401.h>
#include <sound/opl3.h>
#include <asm/io.h>
-#if (defined(CONFIG_SND_FM801_TEA575X) || defined(CONFIG_SND_FM801_TEA575X_MODULE)) && (defined(CONFIG_VIDEO_DEV) || defined(CONFIG_VIDEO_DEV_MODULE))
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
#include <sound/tea575x-tuner.h>
#define TEA575X_RADIO 1
#endif
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("ForteMedia FM801");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{ForteMedia,FM801},"
* 1 = MediaForte 256-PCS
* 2 = MediaForte 256-PCPR
* 3 = MediaForte 64-PCR
+ * 16 = setup tuner only (this is additional bit), i.e. SF-64-PCR FM card
* High 16-bits are video (radio) device number + 1
*/
-static int tea575x_tuner[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 };
+static int tea575x_tuner[SNDRV_CARDS];
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the FM801 soundcard.");
#define FM801_OPL3_DATA1 0x6b /* OPL3 Bank 1 Write */
#define FM801_POWERDOWN 0x70 /* Blocks Power Down Control */
-#define FM801_AC97_ADDR_SHIFT 10
+/* codec access */
+#define FM801_AC97_READ (1<<7) /* read=1, write=0 */
+#define FM801_AC97_VALID (1<<8) /* port valid=1 */
+#define FM801_AC97_BUSY (1<<9) /* busy=1 */
+#define FM801_AC97_ADDR_SHIFT 10 /* codec id (2bit) */
/* playback and record control register bits */
#define FM801_BUF1_LAST (1<<1)
unsigned int multichannel: 1, /* multichannel support */
secondary: 1; /* secondary codec */
unsigned char secondary_addr; /* address of the secondary codec */
+ unsigned int tea575x_tuner; /* tuner flags */
unsigned short ply_ctrl; /* playback control */
unsigned short cap_ctrl; /* capture control */
#ifdef TEA575X_RADIO
struct snd_tea575x tea;
#endif
+
+#ifdef CONFIG_PM
+ u16 saved_regs[0x20];
+#endif
};
static struct pci_device_id snd_fm801_ids[] = {
* Wait until the codec interface is not ready..
*/
for (idx = 0; idx < 100; idx++) {
- if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9)))
+ if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
goto ok1;
udelay(10);
}
* Wait until the write command is not completed..
*/
for (idx = 0; idx < 1000; idx++) {
- if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9)))
+ if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
return;
udelay(10);
}
* Wait until the codec interface is not ready..
*/
for (idx = 0; idx < 100; idx++) {
- if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9)))
+ if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
goto ok1;
udelay(10);
}
ok1:
/* read command */
- outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | (1<<7), FM801_REG(chip, AC97_CMD));
+ outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ,
+ FM801_REG(chip, AC97_CMD));
for (idx = 0; idx < 100; idx++) {
- if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9)))
+ if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
goto ok2;
udelay(10);
}
ok2:
for (idx = 0; idx < 1000; idx++) {
- if (inw(FM801_REG(chip, AC97_CMD)) & (1<<8))
+ if (inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_VALID)
goto ok3;
udelay(10);
}
2, 4, 6
};
-#define CHANNELS sizeof(channels) / sizeof(channels[0])
-
static struct snd_pcm_hw_constraint_list hw_constraints_channels = {
- .count = CHANNELS,
+ .count = ARRAY_SIZE(channels),
.list = channels,
.mask = 0,
};
chip->ply_ctrl &= ~(FM801_START | FM801_PAUSE);
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
chip->ply_ctrl |= FM801_PAUSE;
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
chip->ply_ctrl &= ~FM801_PAUSE;
break;
default:
chip->cap_ctrl &= ~(FM801_START | FM801_PAUSE);
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
chip->cap_ctrl |= FM801_PAUSE;
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
chip->cap_ctrl &= ~FM801_PAUSE;
break;
default:
return bytes_to_frames(substream->runtime, ptr);
}
-static irqreturn_t snd_fm801_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t snd_fm801_interrupt(int irq, void *dev_id)
{
struct fm801 *chip = dev_id;
unsigned short status;
snd_pcm_period_elapsed(chip->capture_substream);
}
if (chip->rmidi && (status & FM801_IRQ_MPU))
- snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+ snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
if (status & FM801_IRQ_VOLUME)
;/* TODO */
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
return val;
}
+static void snd_fm801_tea575x_64pcr_mute(struct snd_tea575x *tea,
+ unsigned int mute)
+{
+ struct fm801 *chip = tea->private_data;
+ unsigned short reg;
+
+ spin_lock_irq(&chip->reg_lock);
+
+ reg = inw(FM801_REG(chip, GPIO_CTRL));
+ if (mute)
+ /* 0xf800 (mute) */
+ reg &= ~FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
+ else
+ /* 0xf802 (unmute) */
+ reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
+ outw(reg, FM801_REG(chip, GPIO_CTRL));
+ udelay(1);
+
+ spin_unlock_irq(&chip->reg_lock);
+}
+
static struct snd_tea575x_ops snd_fm801_tea_ops[3] = {
{
/* 1 = MediaForte 256-PCS */
/* 3 = MediaForte 64-PCR */
.write = snd_fm801_tea575x_64pcr_write,
.read = snd_fm801_tea575x_64pcr_read,
+ .mute = snd_fm801_tea575x_64pcr_mute,
}
};
#endif
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_double, \
.get = snd_fm801_get_double, .put = snd_fm801_put_double, \
.private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) }
+#define FM801_DOUBLE_TLV(xname, reg, shift_left, shift_right, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+ .name = xname, .info = snd_fm801_info_double, \
+ .get = snd_fm801_get_double, .put = snd_fm801_put_double, \
+ .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24), \
+ .tlv = { .p = (xtlv) } }
static int snd_fm801_info_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
return snd_fm801_update_bits(chip, FM801_REC_SRC, 7, val);
}
+static const DECLARE_TLV_DB_SCALE(db_scale_dsp, -3450, 150, 0);
+
#define FM801_CONTROLS ARRAY_SIZE(snd_fm801_controls)
static struct snd_kcontrol_new snd_fm801_controls[] __devinitdata = {
-FM801_DOUBLE("Wave Playback Volume", FM801_PCM_VOL, 0, 8, 31, 1),
+FM801_DOUBLE_TLV("Wave Playback Volume", FM801_PCM_VOL, 0, 8, 31, 1,
+ db_scale_dsp),
FM801_SINGLE("Wave Playback Switch", FM801_PCM_VOL, 15, 1, 1),
-FM801_DOUBLE("I2S Playback Volume", FM801_I2S_VOL, 0, 8, 31, 1),
+FM801_DOUBLE_TLV("I2S Playback Volume", FM801_I2S_VOL, 0, 8, 31, 1,
+ db_scale_dsp),
FM801_SINGLE("I2S Playback Switch", FM801_I2S_VOL, 15, 1, 1),
-FM801_DOUBLE("FM Playback Volume", FM801_FM_VOL, 0, 8, 31, 1),
+FM801_DOUBLE_TLV("FM Playback Volume", FM801_FM_VOL, 0, 8, 31, 1,
+ db_scale_dsp),
FM801_SINGLE("FM Playback Switch", FM801_FM_VOL, 15, 1, 1),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
* initialization routines
*/
+static int wait_for_codec(struct fm801 *chip, unsigned int codec_id,
+ unsigned short reg, unsigned long waits)
+{
+ unsigned long timeout = jiffies + waits;
+
+ outw(FM801_AC97_READ | (codec_id << FM801_AC97_ADDR_SHIFT) | reg,
+ FM801_REG(chip, AC97_CMD));
+ udelay(5);
+ do {
+ if ((inw(FM801_REG(chip, AC97_CMD)) & (FM801_AC97_VALID|FM801_AC97_BUSY))
+ == FM801_AC97_VALID)
+ return 0;
+ schedule_timeout_uninterruptible(1);
+ } while (time_after(timeout, jiffies));
+ return -EIO;
+}
+
+static int snd_fm801_chip_init(struct fm801 *chip, int resume)
+{
+ unsigned short cmdw;
+
+ if (chip->tea575x_tuner & 0x0010)
+ goto __ac97_ok;
+
+ /* codec cold reset + AC'97 warm reset */
+ outw((1<<5) | (1<<6), FM801_REG(chip, CODEC_CTRL));
+ inw(FM801_REG(chip, CODEC_CTRL)); /* flush posting data */
+ udelay(100);
+ outw(0, FM801_REG(chip, CODEC_CTRL));
+
+ if (wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)) < 0) {
+ snd_printk(KERN_ERR "Primary AC'97 codec not found\n");
+ if (! resume)
+ return -EIO;
+ }
+
+ if (chip->multichannel) {
+ if (chip->secondary_addr) {
+ wait_for_codec(chip, chip->secondary_addr,
+ AC97_VENDOR_ID1, msecs_to_jiffies(50));
+ } else {
+ /* my card has the secondary codec */
+ /* at address #3, so the loop is inverted */
+ int i;
+ for (i = 3; i > 0; i--) {
+ if (!wait_for_codec(chip, i, AC97_VENDOR_ID1,
+ msecs_to_jiffies(50))) {
+ cmdw = inw(FM801_REG(chip, AC97_DATA));
+ if (cmdw != 0xffff && cmdw != 0) {
+ chip->secondary = 1;
+ chip->secondary_addr = i;
+ break;
+ }
+ }
+ }
+ }
+
+ /* the recovery phase, it seems that probing for non-existing codec might */
+ /* cause timeout problems */
+ wait_for_codec(chip, 0, AC97_VENDOR_ID1, msecs_to_jiffies(750));
+ }
+
+ __ac97_ok:
+
+ /* init volume */
+ outw(0x0808, FM801_REG(chip, PCM_VOL));
+ outw(0x9f1f, FM801_REG(chip, FM_VOL));
+ outw(0x8808, FM801_REG(chip, I2S_VOL));
+
+ /* I2S control - I2S mode */
+ outw(0x0003, FM801_REG(chip, I2S_MODE));
+
+ /* interrupt setup */
+ cmdw = inw(FM801_REG(chip, IRQ_MASK));
+ if (chip->irq < 0)
+ cmdw |= 0x00c3; /* mask everything, no PCM nor MPU */
+ else
+ cmdw &= ~0x0083; /* unmask MPU, PLAYBACK & CAPTURE */
+ outw(cmdw, FM801_REG(chip, IRQ_MASK));
+
+ /* interrupt clear */
+ outw(FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU, FM801_REG(chip, IRQ_STATUS));
+
+ return 0;
+}
+
+
static int snd_fm801_free(struct fm801 *chip)
{
unsigned short cmdw;
struct fm801 ** rchip)
{
struct fm801 *chip;
- unsigned char rev, id;
- unsigned short cmdw;
- unsigned long timeout;
int err;
static struct snd_device_ops ops = {
.dev_free = snd_fm801_dev_free,
chip->card = card;
chip->pci = pci;
chip->irq = -1;
+ chip->tea575x_tuner = tea575x_tuner;
if ((err = pci_request_regions(pci, "FM801")) < 0) {
kfree(chip);
pci_disable_device(pci);
return err;
}
chip->port = pci_resource_start(pci, 0);
- if (request_irq(pci->irq, snd_fm801_interrupt, SA_INTERRUPT|SA_SHIRQ,
- "FM801", chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->irq);
- snd_fm801_free(chip);
- return -EBUSY;
+ if ((tea575x_tuner & 0x0010) == 0) {
+ if (request_irq(pci->irq, snd_fm801_interrupt, IRQF_SHARED,
+ "FM801", chip)) {
+ snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->irq);
+ snd_fm801_free(chip);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+ pci_set_master(pci);
}
- chip->irq = pci->irq;
- pci_set_master(pci);
- pci_read_config_byte(pci, PCI_REVISION_ID, &rev);
- if (rev >= 0xb1) /* FM801-AU */
+ if (pci->revision >= 0xb1) /* FM801-AU */
chip->multichannel = 1;
- /* codec cold reset + AC'97 warm reset */
- outw((1<<5)|(1<<6), FM801_REG(chip, CODEC_CTRL));
- inw(FM801_REG(chip, CODEC_CTRL)); /* flush posting data */
- udelay(100);
- outw(0, FM801_REG(chip, CODEC_CTRL));
-
- timeout = (jiffies + (3 * HZ) / 4) + 1; /* min 750ms */
-
- outw((1<<7) | (0 << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD));
- udelay(5);
- do {
- if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8))
- goto __ac97_secondary;
- schedule_timeout_uninterruptible(1);
- } while (time_after(timeout, jiffies));
- snd_printk(KERN_ERR "Primary AC'97 codec not found\n");
- snd_fm801_free(chip);
- return -EIO;
-
- __ac97_secondary:
- if (!chip->multichannel) /* lookup is not required */
- goto __ac97_ok;
- for (id = 3; id > 0; id--) { /* my card has the secondary codec */
- /* at address #3, so the loop is inverted */
-
- timeout = jiffies + HZ / 20;
-
- outw((1<<7) | (id << FM801_AC97_ADDR_SHIFT) | AC97_VENDOR_ID1,
- FM801_REG(chip, AC97_CMD));
- udelay(5);
- do {
- if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8)) {
- cmdw = inw(FM801_REG(chip, AC97_DATA));
- if (cmdw != 0xffff && cmdw != 0) {
- chip->secondary = 1;
- chip->secondary_addr = id;
- goto __ac97_ok;
- }
- }
- schedule_timeout_uninterruptible(1);
- } while (time_after(timeout, jiffies));
- }
-
- /* the recovery phase, it seems that probing for non-existing codec might */
- /* cause timeout problems */
- timeout = (jiffies + (3 * HZ) / 4) + 1; /* min 750ms */
-
- outw((1<<7) | (0 << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD));
- udelay(5);
- do {
- if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8))
- goto __ac97_ok;
- schedule_timeout_uninterruptible(1);
- } while (time_after(timeout, jiffies));
- snd_printk(KERN_ERR "Primary AC'97 codec not responding\n");
- snd_fm801_free(chip);
- return -EIO;
-
- __ac97_ok:
-
- /* init volume */
- outw(0x0808, FM801_REG(chip, PCM_VOL));
- outw(0x9f1f, FM801_REG(chip, FM_VOL));
- outw(0x8808, FM801_REG(chip, I2S_VOL));
-
- /* I2S control - I2S mode */
- outw(0x0003, FM801_REG(chip, I2S_MODE));
-
- /* interrupt setup - unmask MPU, PLAYBACK & CAPTURE */
- cmdw = inw(FM801_REG(chip, IRQ_MASK));
- cmdw &= ~0x0083;
- outw(cmdw, FM801_REG(chip, IRQ_MASK));
-
- /* interrupt clear */
- outw(FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU, FM801_REG(chip, IRQ_STATUS));
+ snd_fm801_chip_init(chip, 0);
if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
snd_fm801_free(chip);
snd_card_set_dev(card, &pci->dev);
#ifdef TEA575X_RADIO
- if (tea575x_tuner > 0 && (tea575x_tuner & 0xffff) < 4) {
+ if (tea575x_tuner > 0 && (tea575x_tuner & 0x000f) < 4) {
chip->tea.dev_nr = tea575x_tuner >> 16;
chip->tea.card = card;
chip->tea.freq_fixup = 10700;
chip->tea.private_data = chip;
- chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & 0xffff) - 1];
+ chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & 0x000f) - 1];
snd_tea575x_init(&chip->tea);
}
#endif
snd_card_free(card);
return err;
}
+ card->private_data = chip;
strcpy(card->driver, "FM801");
strcpy(card->shortname, "ForteMedia FM801-");
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, chip->port, chip->irq);
+ if (tea575x_tuner[dev] & 0x0010)
+ goto __fm801_tuner_only;
+
if ((err = snd_fm801_pcm(chip, 0, NULL)) < 0) {
snd_card_free(card);
return err;
return err;
}
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801,
- FM801_REG(chip, MPU401_DATA), 1,
+ FM801_REG(chip, MPU401_DATA),
+ MPU401_INFO_INTEGRATED,
chip->irq, 0, &chip->rmidi)) < 0) {
snd_card_free(card);
return err;
return err;
}
+ __fm801_tuner_only:
if ((err = snd_card_register(card)) < 0) {
snd_card_free(card);
return err;
pci_set_drvdata(pci, NULL);
}
+#ifdef CONFIG_PM
+static unsigned char saved_regs[] = {
+ FM801_PCM_VOL, FM801_I2S_VOL, FM801_FM_VOL, FM801_REC_SRC,
+ FM801_PLY_CTRL, FM801_PLY_COUNT, FM801_PLY_BUF1, FM801_PLY_BUF2,
+ FM801_CAP_CTRL, FM801_CAP_COUNT, FM801_CAP_BUF1, FM801_CAP_BUF2,
+ FM801_CODEC_CTRL, FM801_I2S_MODE, FM801_VOLUME, FM801_GEN_CTRL,
+};
+
+static int snd_fm801_suspend(struct pci_dev *pci, pm_message_t state)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct fm801 *chip = card->private_data;
+ int i;
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ snd_pcm_suspend_all(chip->pcm);
+ snd_ac97_suspend(chip->ac97);
+ snd_ac97_suspend(chip->ac97_sec);
+ for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
+ chip->saved_regs[i] = inw(chip->port + saved_regs[i]);
+ /* FIXME: tea575x suspend */
+
+ pci_disable_device(pci);
+ pci_save_state(pci);
+ pci_set_power_state(pci, pci_choose_state(pci, state));
+ return 0;
+}
+
+static int snd_fm801_resume(struct pci_dev *pci)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct fm801 *chip = card->private_data;
+ int i;
+
+ pci_set_power_state(pci, PCI_D0);
+ pci_restore_state(pci);
+ if (pci_enable_device(pci) < 0) {
+ printk(KERN_ERR "fm801: pci_enable_device failed, "
+ "disabling device\n");
+ snd_card_disconnect(card);
+ return -EIO;
+ }
+ pci_set_master(pci);
+
+ snd_fm801_chip_init(chip, 1);
+ snd_ac97_resume(chip->ac97);
+ snd_ac97_resume(chip->ac97_sec);
+ for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
+ outw(chip->saved_regs[i], chip->port + saved_regs[i]);
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ return 0;
+}
+#endif
+
static struct pci_driver driver = {
.name = "FM801",
.id_table = snd_fm801_ids,
.probe = snd_card_fm801_probe,
.remove = __devexit_p(snd_card_fm801_remove),
+#ifdef CONFIG_PM
+ .suspend = snd_fm801_suspend,
+ .resume = snd_fm801_resume,
+#endif
};
static int __init alsa_card_fm801_init(void)