[ALSA] ac97_codec: increase timeout for analog subsections
[safe/jmp/linux-2.6] / sound / pci / ac97 / ac97_codec.c
index 91d8ceb..78288db 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
  *  Universal interface for Audio Codec '97
  *
  *  For more details look to AC '97 component specification revision 2.2
@@ -22,7 +22,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <sound/ac97_codec.h>
 #include <sound/asoundef.h>
 #include <sound/initval.h>
-#include "ac97_local.h"
 #include "ac97_id.h"
-#include "ac97_patch.h"
 
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+#include "ac97_patch.c"
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
 MODULE_DESCRIPTION("Universal interface for Audio Codec '97");
 MODULE_LICENSE("GPL");
 
@@ -49,9 +48,10 @@ module_param(enable_loopback, bool, 0444);
 MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control");
 
 #ifdef CONFIG_SND_AC97_POWER_SAVE
-static int power_save;
-module_param(power_save, bool, 0644);
-MODULE_PARM_DESC(power_save, "Enable AC97 power-saving control");
+static int power_save = CONFIG_SND_AC97_POWER_SAVE_DEFAULT;
+module_param(power_save, int, 0644);
+MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
+                "(in second, 0 = disable).");
 #endif
 /*
 
@@ -67,8 +67,8 @@ struct ac97_codec_id {
 };
 
 static const struct ac97_codec_id snd_ac97_codec_id_vendors[] = {
-{ 0x414b4d00, 0xffffff00, "Asahi Kasei",       NULL,   NULL },
 { 0x41445300, 0xffffff00, "Analog Devices",    NULL,   NULL },
+{ 0x414b4d00, 0xffffff00, "Asahi Kasei",       NULL,   NULL },
 { 0x414c4300, 0xffffff00, "Realtek",           NULL,   NULL },
 { 0x414c4700, 0xffffff00, "Realtek",           NULL,   NULL },
 { 0x434d4900, 0xffffff00, "C-Media Electronics", NULL, NULL },
@@ -94,11 +94,6 @@ static const struct ac97_codec_id snd_ac97_codec_id_vendors[] = {
 };
 
 static const struct ac97_codec_id snd_ac97_codec_ids[] = {
-{ 0x414b4d00, 0xffffffff, "AK4540",            NULL,           NULL },
-{ 0x414b4d01, 0xffffffff, "AK4542",            NULL,           NULL },
-{ 0x414b4d02, 0xffffffff, "AK4543",            NULL,           NULL },
-{ 0x414b4d06, 0xffffffff, "AK4544A",           NULL,           NULL },
-{ 0x414b4d07, 0xffffffff, "AK4545",            NULL,           NULL },
 { 0x41445303, 0xffffffff, "AD1819",            patch_ad1819,   NULL },
 { 0x41445340, 0xffffffff, "AD1881",            patch_ad1881,   NULL },
 { 0x41445348, 0xffffffff, "AD1881A",           patch_ad1881,   NULL },
@@ -111,27 +106,32 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
 { 0x41445372, 0xffffffff, "AD1981A",           patch_ad1981a,  NULL },
 { 0x41445374, 0xffffffff, "AD1981B",           patch_ad1981b,  NULL },
 { 0x41445375, 0xffffffff, "AD1985",            patch_ad1985,   NULL },
-{ 0x41445378, 0xffffffff, "AD1986",            patch_ad1985,   NULL },
+{ 0x41445378, 0xffffffff, "AD1986",            patch_ad1986,   NULL },
+{ 0x414b4d00, 0xffffffff, "AK4540",            NULL,           NULL },
+{ 0x414b4d01, 0xffffffff, "AK4542",            NULL,           NULL },
+{ 0x414b4d02, 0xffffffff, "AK4543",            NULL,           NULL },
+{ 0x414b4d06, 0xffffffff, "AK4544A",           NULL,           NULL },
+{ 0x414b4d07, 0xffffffff, "AK4545",            NULL,           NULL },
 { 0x414c4300, 0xffffff00, "ALC100,100P",       NULL,           NULL },
 { 0x414c4710, 0xfffffff0, "ALC200,200P",       NULL,           NULL },
 { 0x414c4721, 0xffffffff, "ALC650D",           NULL,   NULL }, /* already patched */
 { 0x414c4722, 0xffffffff, "ALC650E",           NULL,   NULL }, /* already patched */
 { 0x414c4723, 0xffffffff, "ALC650F",           NULL,   NULL }, /* already patched */
 { 0x414c4720, 0xfffffff0, "ALC650",            patch_alc650,   NULL },
+{ 0x414c4730, 0xffffffff, "ALC101",            NULL,           NULL },
+{ 0x414c4740, 0xfffffff0, "ALC202",            NULL,           NULL },
+{ 0x414c4750, 0xfffffff0, "ALC250",            NULL,           NULL },
 { 0x414c4760, 0xfffffff0, "ALC655",            patch_alc655,   NULL },
+{ 0x414c4770, 0xfffffff0, "ALC203",            patch_alc203,   NULL },
 { 0x414c4781, 0xffffffff, "ALC658D",           NULL,   NULL }, /* already patched */
 { 0x414c4780, 0xfffffff0, "ALC658",            patch_alc655,   NULL },
 { 0x414c4790, 0xfffffff0, "ALC850",            patch_alc850,   NULL },
-{ 0x414c4730, 0xffffffff, "ALC101",            NULL,           NULL },
-{ 0x414c4740, 0xfffffff0, "ALC202",            NULL,           NULL },
-{ 0x414c4750, 0xfffffff0, "ALC250",            NULL,           NULL },
-{ 0x414c4770, 0xfffffff0, "ALC203",            NULL,           NULL },
 { 0x434d4941, 0xffffffff, "CMI9738",           patch_cm9738,   NULL },
 { 0x434d4961, 0xffffffff, "CMI9739",           patch_cm9739,   NULL },
 { 0x434d4969, 0xffffffff, "CMI9780",           patch_cm9780,   NULL },
-{ 0x434d4978, 0xffffffff, "CMI9761",           patch_cm9761,   NULL },
-{ 0x434d4982, 0xffffffff, "CMI9761",           patch_cm9761,   NULL },
-{ 0x434d4983, 0xffffffff, "CMI9761",           patch_cm9761,   NULL },
+{ 0x434d4978, 0xffffffff, "CMI9761A",          patch_cm9761,   NULL },
+{ 0x434d4982, 0xffffffff, "CMI9761B",          patch_cm9761,   NULL },
+{ 0x434d4983, 0xffffffff, "CMI9761A+",         patch_cm9761,   NULL },
 { 0x43525900, 0xfffffff8, "CS4297",            NULL,           NULL },
 { 0x43525910, 0xfffffff8, "CS4297A",           patch_cirrus_spdif,     NULL },
 { 0x43525920, 0xfffffff8, "CS4298",            patch_cirrus_spdif,             NULL },
@@ -143,6 +143,8 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
 { 0x43525970, 0xfffffff8, "CS4202",            NULL,           NULL },
 { 0x43585421, 0xffffffff, "HSD11246",          NULL,           NULL }, // SmartMC II
 { 0x43585428, 0xfffffff8, "Cx20468",           patch_conexant, NULL }, // SmartAMC fixme: the mask might be different
+{ 0x43585430, 0xffffffff, "Cx20468-31",                patch_conexant, NULL },
+{ 0x43585431, 0xffffffff, "Cx20551",           patch_cx20551,  NULL },
 { 0x44543031, 0xfffffff0, "DT0398",            NULL,           NULL },
 { 0x454d4328, 0xffffffff, "EM28028",           NULL,           NULL },  // same as TR28028?
 { 0x45838308, 0xffffffff, "ESS1988",           NULL,           NULL },
@@ -160,22 +162,23 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
 { 0x50534304, 0xffffffff, "UCB1400",           patch_ucb1400,  NULL },
 { 0x53494c20, 0xffffffe0, "Si3036,8",          mpatch_si3036,  mpatch_si3036, AC97_MODEM_PATCH },
 { 0x54524102, 0xffffffff, "TR28022",           NULL,           NULL },
+{ 0x54524103, 0xffffffff, "TR28023",           NULL,           NULL },
 { 0x54524106, 0xffffffff, "TR28026",           NULL,           NULL },
 { 0x54524108, 0xffffffff, "TR28028",           patch_tritech_tr28028,  NULL }, // added by xin jin [07/09/99]
 { 0x54524123, 0xffffffff, "TR28602",           NULL,           NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)]
 { 0x54584e20, 0xffffffff, "TLC320AD9xC",       NULL,           NULL },
 { 0x56494161, 0xffffffff, "VIA1612A",          NULL,           NULL }, // modified ICE1232 with S/PDIF
 { 0x56494170, 0xffffffff, "VIA1617A",          patch_vt1617a,  NULL }, // modified VT1616 with S/PDIF
-{ 0x56494182, 0xffffffff, "VIA1618",           NULL,           NULL },
+{ 0x56494182, 0xffffffff, "VIA1618",           patch_vt1618,   NULL },
 { 0x57454301, 0xffffffff, "W83971D",           NULL,           NULL },
-{ 0x574d4c00, 0xffffffff, "WM9701A",           NULL,           NULL },
+{ 0x574d4c00, 0xffffffff, "WM9701,WM9701A",    NULL,           NULL },
 { 0x574d4C03, 0xffffffff, "WM9703,WM9707,WM9708,WM9717", patch_wolfson03, NULL},
 { 0x574d4C04, 0xffffffff, "WM9704M,WM9704Q",   patch_wolfson04, NULL},
 { 0x574d4C05, 0xffffffff, "WM9705,WM9710",     patch_wolfson05, NULL},
 { 0x574d4C09, 0xffffffff, "WM9709",            NULL,           NULL},
-{ 0x574d4C12, 0xffffffff, "WM9711,WM9712",     patch_wolfson11, NULL},
+{ 0x574d4C12, 0xffffffff, "WM9711,WM9712,WM9715",      patch_wolfson11, NULL},
 { 0x574d4c13, 0xffffffff, "WM9713,WM9714",     patch_wolfson13, NULL, AC97_DEFAULT_POWER_OFF},
-{ 0x594d4800, 0xffffffff, "YMF743",            NULL,           NULL },
+{ 0x594d4800, 0xffffffff, "YMF743",            patch_yamaha_ymf743,    NULL },
 { 0x594d4802, 0xffffffff, "YMF752",            NULL,           NULL },
 { 0x594d4803, 0xffffffff, "YMF753",            patch_yamaha_ymf753,    NULL },
 { 0x83847600, 0xffffffff, "STAC9700,83,84",    patch_sigmatel_stac9700,        NULL },
@@ -194,6 +197,13 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
 
 
 static void update_power_regs(struct snd_ac97 *ac97);
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+#define ac97_is_power_save_mode(ac97) \
+       ((ac97->scaps & AC97_SCAP_POWER_SAVE) && power_save)
+#else
+#define ac97_is_power_save_mode(ac97) 0
+#endif
+
 
 /*
  *  I/O routines
@@ -374,7 +384,7 @@ int snd_ac97_update_bits(struct snd_ac97 *ac97, unsigned short reg, unsigned sho
 
 EXPORT_SYMBOL(snd_ac97_update_bits);
 
-/* no lock version - see snd_ac97_updat_bits() */
+/* no lock version - see snd_ac97_update_bits() */
 int snd_ac97_update_bits_nolock(struct snd_ac97 *ac97, unsigned short reg,
                                unsigned short mask, unsigned short value)
 {
@@ -424,7 +434,8 @@ static int snd_ac97_ad18xx_update_pcm_bits(struct snd_ac97 *ac97, int codec, uns
  * Controls
  */
 
-int snd_ac97_info_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int snd_ac97_info_enum_double(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_info *uinfo)
 {
        struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
        
@@ -438,7 +449,8 @@ int snd_ac97_info_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
        return 0;
 }
 
-int snd_ac97_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int snd_ac97_get_enum_double(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
        struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
@@ -454,7 +466,8 @@ int snd_ac97_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
        return 0;
 }
 
-int snd_ac97_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int snd_ac97_put_enum_double(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
        struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
@@ -500,7 +513,8 @@ static void snd_ac97_page_restore(struct snd_ac97 *ac97, int page_save)
 }
 
 /* volume and switch controls */
-int snd_ac97_info_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int snd_ac97_info_volsw(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_info *uinfo)
 {
        int mask = (kcontrol->private_value >> 16) & 0xff;
        int shift = (kcontrol->private_value >> 8) & 0x0f;
@@ -513,7 +527,8 @@ int snd_ac97_info_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info
        return 0;
 }
 
-int snd_ac97_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int snd_ac97_get_volsw(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
        int reg = kcontrol->private_value & 0xff;
@@ -536,7 +551,8 @@ int snd_ac97_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value
        return 0;
 }
 
-int snd_ac97_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int snd_ac97_put_volsw(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
        int reg = kcontrol->private_value & 0xff;
@@ -638,7 +654,7 @@ AC97_ENUM("Mic Select", std_enum[3]),
 AC97_SINGLE("ADC/DAC Loopback", AC97_GENERAL_PURPOSE, 7, 1, 0)
 };
 
-const struct snd_kcontrol_new snd_ac97_controls_3d[2] = {
+static const struct snd_kcontrol_new snd_ac97_controls_3d[2] = {
 AC97_SINGLE("3D Control - Center", AC97_3D_CONTROL, 8, 15, 0),
 AC97_SINGLE("3D Control - Depth", AC97_3D_CONTROL, 0, 15, 0)
 };
@@ -765,6 +781,12 @@ static int snd_ac97_spdif_default_put(struct snd_kcontrol *kcontrol, struct snd_
                change |= snd_ac97_update_bits_nolock(ac97, AC97_CXR_AUDIO_MISC, 
                                                      AC97_CXR_SPDIF_MASK | AC97_CXR_COPYRGT,
                                                      v);
+       } else if (ac97->id == AC97_ID_YMF743) {
+               change |= snd_ac97_update_bits_nolock(ac97,
+                                                     AC97_YMF7X3_DIT_CTRL,
+                                                     0xff38,
+                                                     ((val << 4) & 0xff00) |
+                                                     ((val << 2) & 0x0038));
        } else {
                unsigned short extst = snd_ac97_read_cache(ac97, AC97_EXTENDED_STATUS);
                snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); /* turn off */
@@ -809,7 +831,7 @@ static int snd_ac97_put_spsa(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
        return change;
 }
 
-const struct snd_kcontrol_new snd_ac97_controls_spdif[5] = {
+static const struct snd_kcontrol_new snd_ac97_controls_spdif[5] = {
        {
                .access = SNDRV_CTL_ELEM_ACCESS_READ,
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -982,8 +1004,8 @@ static int snd_ac97_free(struct snd_ac97 *ac97)
 {
        if (ac97) {
 #ifdef CONFIG_SND_AC97_POWER_SAVE
-               if (ac97->power_workq)
-                       destroy_workqueue(ac97->power_workq);
+               cancel_delayed_work(&ac97->power_work);
+               flush_scheduled_work();
 #endif
                snd_ac97_proc_done(ac97);
                if (ac97->bus)
@@ -1075,7 +1097,7 @@ static void check_volume_resolution(struct snd_ac97 *ac97, int reg, unsigned cha
                unsigned short val;
                snd_ac97_write(ac97, reg, 0x8080 | cbit[i] | (cbit[i] << 8));
                /* Do the read twice due to buffers on some ac97 codecs.
-                * e.g. The STAC9704 returns exactly what you wrote the the register
+                * e.g. The STAC9704 returns exactly what you wrote to the register
                 * if you read it immediately. This causes the detect routine to fail.
                 */
                val = snd_ac97_read(ac97, reg);
@@ -1089,7 +1111,7 @@ static void check_volume_resolution(struct snd_ac97 *ac97, int reg, unsigned cha
        }
 }
 
-int snd_ac97_try_bit(struct snd_ac97 * ac97, int reg, int bit)
+static int snd_ac97_try_bit(struct snd_ac97 * ac97, int reg, int bit)
 {
        unsigned short mask, val, orig, res;
 
@@ -1129,7 +1151,8 @@ static inline int printable(unsigned int x)
        return x;
 }
 
-struct snd_kcontrol *snd_ac97_cnew(const struct snd_kcontrol_new *_template, struct snd_ac97 * ac97)
+static struct snd_kcontrol *snd_ac97_cnew(const struct snd_kcontrol_new *_template,
+                                         struct snd_ac97 * ac97)
 {
        struct snd_kcontrol_new template;
        memcpy(&template, _template, sizeof(template));
@@ -1184,13 +1207,13 @@ static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg,
 /*
  * set dB information
  */
-static DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
-static DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0);
-static DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
-static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
-static DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
 
-static unsigned int *find_db_scale(unsigned int maxval)
+static const unsigned int *find_db_scale(unsigned int maxval)
 {
        switch (maxval) {
        case 0x0f: return db_scale_4bit;
@@ -1200,8 +1223,8 @@ static unsigned int *find_db_scale(unsigned int maxval)
        return NULL;
 }
 
-static void set_tlv_db_scale(struct snd_kcontrol *kctl, unsigned int *tlv)
-{      
+static void set_tlv_db_scale(struct snd_kcontrol *kctl, const unsigned int *tlv)
+{
        kctl->tlv.p = tlv;
        if (tlv)
                kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
@@ -1360,7 +1383,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
                        for (idx = 0; idx < 2; idx++) {
                                if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_tone[idx], ac97))) < 0)
                                        return err;
-                               if (ac97->id == AC97_ID_YMF753) {
+                               if (ac97->id == AC97_ID_YMF743 ||
+                                   ac97->id == AC97_ID_YMF753) {
                                        kctl->private_value &= ~(0xff << 16);
                                        kctl->private_value |= 7 << 16;
                                }
@@ -1620,7 +1644,10 @@ static int snd_ac97_modem_build(struct snd_card *card, struct snd_ac97 * ac97)
 {
        int err, idx;
 
-       //printk("AC97_GPIO_CFG = %x\n",snd_ac97_read(ac97,AC97_GPIO_CFG));
+       /*
+       printk(KERN_DEBUG "AC97_GPIO_CFG = %x\n",
+              snd_ac97_read(ac97,AC97_GPIO_CFG));
+       */
        snd_ac97_write(ac97, AC97_GPIO_CFG, 0xffff & ~(AC97_GPIO_LINE1_OH));
        snd_ac97_write(ac97, AC97_GPIO_POLARITY, 0xffff & ~(AC97_GPIO_LINE1_OH));
        snd_ac97_write(ac97, AC97_GPIO_STICKY, 0xffff);
@@ -1867,8 +1894,8 @@ int snd_ac97_bus(struct snd_card *card, int num, struct snd_ac97_bus_ops *ops,
                .dev_free =     snd_ac97_bus_dev_free,
        };
 
-       snd_assert(card != NULL, return -EINVAL);
-       snd_assert(rbus != NULL, return -EINVAL);
+       if (snd_BUG_ON(!card))
+               return -EINVAL;
        bus = kzalloc(sizeof(*bus), GFP_KERNEL);
        if (bus == NULL)
                return -ENOMEM;
@@ -1883,7 +1910,8 @@ int snd_ac97_bus(struct snd_card *card, int num, struct snd_ac97_bus_ops *ops,
                snd_ac97_bus_free(bus);
                return err;
        }
-       *rbus = bus;
+       if (rbus)
+               *rbus = bus;
        return 0;
 }
 
@@ -1903,9 +1931,9 @@ static int snd_ac97_dev_register(struct snd_device *device)
        ac97->dev.bus = &ac97_bus_type;
        ac97->dev.parent = ac97->bus->card->dev;
        ac97->dev.release = ac97_device_release;
-       snprintf(ac97->dev.bus_id, BUS_ID_SIZE, "%d-%d:%s",
-                ac97->bus->card->number, ac97->num,
-                snd_ac97_get_short_name(ac97));
+       dev_set_name(&ac97->dev, "%d-%d:%s",
+                    ac97->bus->card->number, ac97->num,
+                    snd_ac97_get_short_name(ac97));
        if ((err = device_register(&ac97->dev)) < 0) {
                snd_printk(KERN_ERR "Can't register ac97 bus\n");
                ac97->dev.bus = NULL;
@@ -1968,10 +1996,14 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
                .dev_disconnect =       snd_ac97_dev_disconnect,
        };
 
-       snd_assert(rac97 != NULL, return -EINVAL);
-       *rac97 = NULL;
-       snd_assert(bus != NULL && template != NULL, return -EINVAL);
-       snd_assert(template->num < 4 && bus->codec[template->num] == NULL, return -EINVAL);
+       if (rac97)
+               *rac97 = NULL;
+       if (snd_BUG_ON(!bus || !template))
+               return -EINVAL;
+       if (snd_BUG_ON(template->num >= 4))
+               return -EINVAL;
+       if (bus->codec[template->num])
+               return -EBUSY;
 
        card = bus->card;
        ac97 = kzalloc(sizeof(*ac97), GFP_KERNEL);
@@ -1989,7 +2021,6 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
        mutex_init(&ac97->reg_mutex);
        mutex_init(&ac97->page_mutex);
 #ifdef CONFIG_SND_AC97_POWER_SAVE
-       ac97->power_workq = create_workqueue("ac97");
        INIT_DELAYED_WORK(&ac97->power_work, do_update_power);
 #endif
 
@@ -2022,11 +2053,12 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
        else {
                udelay(50);
                if (ac97->scaps & AC97_SCAP_SKIP_AUDIO)
-                       err = ac97_reset_wait(ac97, HZ/2, 1);
+                       err = ac97_reset_wait(ac97, msecs_to_jiffies(500), 1);
                else {
-                       err = ac97_reset_wait(ac97, HZ/2, 0);
+                       err = ac97_reset_wait(ac97, msecs_to_jiffies(500), 0);
                        if (err < 0)
-                               err = ac97_reset_wait(ac97, HZ/2, 1);
+                               err = ac97_reset_wait(ac97,
+                                                     msecs_to_jiffies(500), 1);
                }
                if (err < 0) {
                        snd_printk(KERN_WARNING "AC'97 %d does not respond - RESET\n", ac97->num);
@@ -2090,7 +2122,7 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
                }
                /* nothing should be in powerdown mode */
                snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0);
-               end_time = jiffies + (HZ / 10);
+               end_time = jiffies + msecs_to_jiffies(120);
                do {
                        if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0f) == 0x0f)
                                goto __ready_ok;
@@ -2122,7 +2154,7 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
                udelay(100);
                /* nothing should be in powerdown mode */
                snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0);
-               end_time = jiffies + (HZ / 10);
+               end_time = jiffies + msecs_to_jiffies(100);
                do {
                        if ((snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS) & tmp) == tmp)
                                goto __ready_ok;
@@ -2273,17 +2305,17 @@ static void snd_ac97_powerdown(struct snd_ac97 *ac97)
        power |= AC97_PD_PR0 | AC97_PD_PR1;     /* ADC & DAC powerdown */
        snd_ac97_write(ac97, AC97_POWERDOWN, power);
        udelay(100);
-       power |= AC97_PD_PR2 | AC97_PD_PR3;     /* Analog Mixer powerdown */
+       power |= AC97_PD_PR2;   /* Analog Mixer powerdown (Vref on) */
        snd_ac97_write(ac97, AC97_POWERDOWN, power);
-#ifdef CONFIG_SND_AC97_POWER_SAVE
-       if (power_save) {
+       if (ac97_is_power_save_mode(ac97)) {
+               power |= AC97_PD_PR3;   /* Analog Mixer powerdown */
+               snd_ac97_write(ac97, AC97_POWERDOWN, power);
                udelay(100);
                /* AC-link powerdown, internal Clk disable */
                /* FIXME: this may cause click noises on some boards */
                power |= AC97_PD_PR4 | AC97_PD_PR5;
                snd_ac97_write(ac97, AC97_POWERDOWN, power);
        }
-#endif
 }
 
 
@@ -2337,14 +2369,17 @@ int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup)
                }
        }
 
-       if (power_save && !powerup && ac97->power_workq)
+       if (ac97_is_power_save_mode(ac97) && !powerup)
                /* adjust power-down bits after two seconds delay
                 * (for avoiding loud click noises for many (OSS) apps
                 *  that open/close frequently)
                 */
-               queue_delayed_work(ac97->power_workq, &ac97->power_work, HZ*2);
-       else
+               schedule_delayed_work(&ac97->power_work,
+                                     msecs_to_jiffies(power_save * 1000));
+       else {
+               cancel_delayed_work(&ac97->power_work);
                update_power_regs(ac97);
+       }
 
        return 0;
 }
@@ -2357,19 +2392,15 @@ static void update_power_regs(struct snd_ac97 *ac97)
        unsigned int power_up, bits;
        int i;
 
+       power_up = (1 << PWIDX_FRONT) | (1 << PWIDX_ADC);
+       power_up |= (1 << PWIDX_MIC);
+       if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
+               power_up |= (1 << PWIDX_SURR);
+       if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
+               power_up |= (1 << PWIDX_CLFE);
 #ifdef CONFIG_SND_AC97_POWER_SAVE
-       if (power_save)
+       if (ac97_is_power_save_mode(ac97))
                power_up = ac97->power_up;
-       else {
-#endif
-               power_up = (1 << PWIDX_FRONT) | (1 << PWIDX_ADC);
-               power_up |= (1 << PWIDX_MIC);
-               if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
-                       power_up |= (1 << PWIDX_SURR);
-               if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
-                       power_up |= (1 << PWIDX_CLFE);
-#ifdef CONFIG_SND_AC97_POWER_SAVE
-       }
 #endif
        if (power_up) {
                if (ac97->regs[AC97_POWERDOWN] & AC97_PD_PR2) {
@@ -2414,6 +2445,10 @@ void snd_ac97_suspend(struct snd_ac97 *ac97)
                return;
        if (ac97->build_ops->suspend)
                ac97->build_ops->suspend(ac97);
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+       cancel_delayed_work(&ac97->power_work);
+       flush_scheduled_work();
+#endif
        snd_ac97_powerdown(ac97);
 }
 
@@ -2422,7 +2457,7 @@ EXPORT_SYMBOL(snd_ac97_suspend);
 /*
  * restore ac97 status
  */
-void snd_ac97_restore_status(struct snd_ac97 *ac97)
+static void snd_ac97_restore_status(struct snd_ac97 *ac97)
 {
        int i;
 
@@ -2443,7 +2478,7 @@ void snd_ac97_restore_status(struct snd_ac97 *ac97)
 /*
  * restore IEC958 status
  */
-void snd_ac97_restore_iec958(struct snd_ac97 *ac97)
+static void snd_ac97_restore_iec958(struct snd_ac97 *ac97)
 {
        if (ac97->ext_id & AC97_EI_SPDIF) {
                if (ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_SPDIF) {
@@ -2480,7 +2515,10 @@ void snd_ac97_resume(struct snd_ac97 *ac97)
 
        snd_ac97_write(ac97, AC97_POWERDOWN, 0);
        if (! (ac97->flags & AC97_DEFAULT_POWER_OFF)) {
-               snd_ac97_write(ac97, AC97_RESET, 0);
+               if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO))
+                       snd_ac97_write(ac97, AC97_RESET, 0);
+               else if (!(ac97->scaps & AC97_SCAP_SKIP_MODEM))
+                       snd_ac97_write(ac97, AC97_EXTENDED_MID, 0);
                udelay(100);
                snd_ac97_write(ac97, AC97_POWERDOWN, 0);
        }
@@ -2537,7 +2575,8 @@ static void set_ctl_name(char *dst, const char *src, const char *suffix)
 }      
 
 /* remove the control with the given name and optional suffix */
-int snd_ac97_remove_ctl(struct snd_ac97 *ac97, const char *name, const char *suffix)
+static int snd_ac97_remove_ctl(struct snd_ac97 *ac97, const char *name,
+                              const char *suffix)
 {
        struct snd_ctl_elem_id id;
        memset(&id, 0, sizeof(id));
@@ -2556,7 +2595,8 @@ static struct snd_kcontrol *ctl_find(struct snd_ac97 *ac97, const char *name, co
 }
 
 /* rename the control with the given name and optional suffix */
-int snd_ac97_rename_ctl(struct snd_ac97 *ac97, const char *src, const char *dst, const char *suffix)
+static int snd_ac97_rename_ctl(struct snd_ac97 *ac97, const char *src,
+                              const char *dst, const char *suffix)
 {
        struct snd_kcontrol *kctl = ctl_find(ac97, src, suffix);
        if (kctl) {
@@ -2567,14 +2607,16 @@ int snd_ac97_rename_ctl(struct snd_ac97 *ac97, const char *src, const char *dst,
 }
 
 /* rename both Volume and Switch controls - don't check the return value */
-void snd_ac97_rename_vol_ctl(struct snd_ac97 *ac97, const char *src, const char *dst)
+static void snd_ac97_rename_vol_ctl(struct snd_ac97 *ac97, const char *src,
+                                   const char *dst)
 {
        snd_ac97_rename_ctl(ac97, src, dst, "Switch");
        snd_ac97_rename_ctl(ac97, src, dst, "Volume");
 }
 
 /* swap controls */
-int snd_ac97_swap_ctl(struct snd_ac97 *ac97, const char *s1, const char *s2, const char *suffix)
+static int snd_ac97_swap_ctl(struct snd_ac97 *ac97, const char *s1,
+                            const char *s2, const char *suffix)
 {
        struct snd_kcontrol *kctl1, *kctl2;
        kctl1 = ctl_find(ac97, s1, suffix);