Merge branch 'timers-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[safe/jmp/linux-2.6] / sound / pci / hda / hda_codec.c
index 2fff0fb..0b70813 100644 (file)
@@ -46,6 +46,7 @@ static struct hda_vendor_id hda_vendor_ids[] = {
        { 0x1002, "ATI" },
        { 0x1057, "Motorola" },
        { 0x1095, "Silicon Image" },
+       { 0x10de, "Nvidia" },
        { 0x10ec, "Realtek" },
        { 0x1106, "VIA" },
        { 0x111d, "IDT" },
@@ -57,6 +58,7 @@ static struct hda_vendor_id hda_vendor_ids[] = {
        { 0x1854, "LG" },
        { 0x1aec, "Wolfson Microelectronics" },
        { 0x434d, "C-Media" },
+       { 0x8086, "Intel" },
        { 0x8384, "SigmaTel" },
        {} /* terminator */
 };
@@ -71,7 +73,7 @@ int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset)
        mutex_unlock(&preset_mutex);
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_add_codec_preset);
+EXPORT_SYMBOL_HDA(snd_hda_add_codec_preset);
 
 int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset)
 {
@@ -80,7 +82,7 @@ int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset)
        mutex_unlock(&preset_mutex);
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_delete_codec_preset);
+EXPORT_SYMBOL_HDA(snd_hda_delete_codec_preset);
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 static void hda_power_work(struct work_struct *work);
@@ -114,7 +116,7 @@ const char *snd_hda_get_jack_location(u32 cfg)
        }
        return "UNKNOWN";
 }
-EXPORT_SYMBOL_GPL(snd_hda_get_jack_location);
+EXPORT_SYMBOL_HDA(snd_hda_get_jack_location);
 
 const char *snd_hda_get_jack_connectivity(u32 cfg)
 {
@@ -122,7 +124,7 @@ const char *snd_hda_get_jack_connectivity(u32 cfg)
 
        return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3];
 }
-EXPORT_SYMBOL_GPL(snd_hda_get_jack_connectivity);
+EXPORT_SYMBOL_HDA(snd_hda_get_jack_connectivity);
 
 const char *snd_hda_get_jack_type(u32 cfg)
 {
@@ -136,7 +138,7 @@ const char *snd_hda_get_jack_type(u32 cfg)
        return jack_types[(cfg & AC_DEFCFG_DEVICE)
                                >> AC_DEFCFG_DEVICE_SHIFT];
 }
-EXPORT_SYMBOL_GPL(snd_hda_get_jack_type);
+EXPORT_SYMBOL_HDA(snd_hda_get_jack_type);
 
 /*
  * Compose a 32bit command word to be sent to the HD-audio controller
@@ -185,7 +187,7 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
        snd_hda_power_down(codec);
        return res;
 }
-EXPORT_SYMBOL_GPL(snd_hda_codec_read);
+EXPORT_SYMBOL_HDA(snd_hda_codec_read);
 
 /**
  * snd_hda_codec_write - send a single command without waiting for response
@@ -214,7 +216,7 @@ int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
        snd_hda_power_down(codec);
        return err;
 }
-EXPORT_SYMBOL_GPL(snd_hda_codec_write);
+EXPORT_SYMBOL_HDA(snd_hda_codec_write);
 
 /**
  * snd_hda_sequence_write - sequence writes
@@ -229,7 +231,7 @@ void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq)
        for (; seq->nid; seq++)
                snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param);
 }
-EXPORT_SYMBOL_GPL(snd_hda_sequence_write);
+EXPORT_SYMBOL_HDA(snd_hda_sequence_write);
 
 /**
  * snd_hda_get_sub_nodes - get the range of sub nodes
@@ -251,7 +253,7 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
        *start_id = (parm >> 16) & 0x7fff;
        return (int)(parm & 0x7fff);
 }
-EXPORT_SYMBOL_GPL(snd_hda_get_sub_nodes);
+EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes);
 
 /**
  * snd_hda_get_connections - get connection list
@@ -340,7 +342,7 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
        }
        return conns;
 }
-EXPORT_SYMBOL_GPL(snd_hda_get_connections);
+EXPORT_SYMBOL_HDA(snd_hda_get_connections);
 
 
 /**
@@ -371,11 +373,11 @@ int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex)
        unsol->queue[wp] = res;
        unsol->queue[wp + 1] = res_ex;
 
-       schedule_work(&unsol->work);
+       queue_work(bus->workq, &unsol->work);
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_queue_unsol_event);
+EXPORT_SYMBOL_HDA(snd_hda_queue_unsol_event);
 
 /*
  * process queued unsolicited events
@@ -435,15 +437,17 @@ static int snd_hda_bus_free(struct hda_bus *bus)
 
        if (!bus)
                return 0;
-       if (bus->unsol) {
-               flush_scheduled_work();
+       if (bus->workq)
+               flush_workqueue(bus->workq);
+       if (bus->unsol)
                kfree(bus->unsol);
-       }
        list_for_each_entry_safe(codec, n, &bus->codec_list, list) {
                snd_hda_codec_free(codec);
        }
        if (bus->ops.private_free)
                bus->ops.private_free(bus);
+       if (bus->workq)
+               destroy_workqueue(bus->workq);
        kfree(bus);
        return 0;
 }
@@ -512,6 +516,16 @@ int /*__devinit*/ snd_hda_bus_new(struct snd_card *card,
        mutex_init(&bus->cmd_mutex);
        INIT_LIST_HEAD(&bus->codec_list);
 
+       snprintf(bus->workq_name, sizeof(bus->workq_name),
+                "hd-audio%d", card->number);
+       bus->workq = create_singlethread_workqueue(bus->workq_name);
+       if (!bus->workq) {
+               snd_printk(KERN_ERR "cannot create workqueue %s\n",
+                          bus->workq_name);
+               kfree(bus);
+               return -ENOMEM;
+       }
+
        err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops);
        if (err < 0) {
                snd_hda_bus_free(bus);
@@ -521,7 +535,7 @@ int /*__devinit*/ snd_hda_bus_new(struct snd_card *card,
                *busp = bus;
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_bus_new);
+EXPORT_SYMBOL_HDA(snd_hda_bus_new);
 
 #ifdef CONFIG_SND_HDA_GENERIC
 #define is_generic_config(codec) \
@@ -682,7 +696,7 @@ static void snd_hda_codec_free(struct hda_codec *codec)
                return;
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        cancel_delayed_work(&codec->power_work);
-       flush_scheduled_work();
+       flush_workqueue(codec->bus->workq);
 #endif
        list_del(&codec->list);
        snd_array_free(&codec->mixers);
@@ -707,7 +721,7 @@ static void snd_hda_codec_free(struct hda_codec *codec)
  * Returns 0 if successful, or a negative error code.
  */
 int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
-                               struct hda_codec **codecp)
+                                   int do_init, struct hda_codec **codecp)
 {
        struct hda_codec *codec;
        char component[31];
@@ -733,6 +747,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
        codec->bus = bus;
        codec->addr = codec_addr;
        mutex_init(&codec->spdif_mutex);
+       mutex_init(&codec->control_mutex);
        init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
        init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
        snd_array_init(&codec->mixers, sizeof(struct snd_kcontrol *), 32);
@@ -791,10 +806,12 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
        if (bus->modelname)
                codec->modelname = kstrdup(bus->modelname, GFP_KERNEL);
 
-       err = snd_hda_codec_configure(codec);
-       if (err < 0) {
-               snd_hda_codec_free(codec);
-               return err;
+       if (do_init) {
+               err = snd_hda_codec_configure(codec);
+               if (err < 0) {
+                       snd_hda_codec_free(codec);
+                       return err;
+               }
        }
        snd_hda_codec_proc_new(codec);
 
@@ -808,7 +825,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
                *codecp = codec;
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_codec_new);
+EXPORT_SYMBOL_HDA(snd_hda_codec_new);
 
 int snd_hda_codec_configure(struct hda_codec *codec)
 {
@@ -868,7 +885,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
        msleep(1);
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format);
 }
-EXPORT_SYMBOL_GPL(snd_hda_codec_setup_stream);
+EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream);
 
 void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
 {
@@ -882,7 +899,7 @@ void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
 #endif
 }
-EXPORT_SYMBOL_GPL(snd_hda_codec_cleanup_stream);
+EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream);
 
 /*
  * amp access functions
@@ -964,7 +981,7 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
        }
        return info->amp_caps;
 }
-EXPORT_SYMBOL_GPL(query_amp_caps);
+EXPORT_SYMBOL_HDA(query_amp_caps);
 
 int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
                              unsigned int caps)
@@ -978,7 +995,7 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
        info->head.val |= INFO_AMP_CAPS;
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps);
+EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps);
 
 /*
  * read the current volume to info
@@ -1032,7 +1049,7 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
                return 0;
        return get_vol_mute(codec, info, nid, ch, direction, index);
 }
-EXPORT_SYMBOL_GPL(snd_hda_codec_amp_read);
+EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
 
 /*
  * update the AMP value, mask = bit mask to set, val = the value
@@ -1052,7 +1069,7 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
        put_vol_mute(codec, info, nid, ch, direction, idx, val);
        return 1;
 }
-EXPORT_SYMBOL_GPL(snd_hda_codec_amp_update);
+EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update);
 
 /*
  * update the AMP stereo with the same mask and value
@@ -1066,7 +1083,7 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
                                                idx, mask, val);
        return ret;
 }
-EXPORT_SYMBOL_GPL(snd_hda_codec_amp_stereo);
+EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
 
 #ifdef SND_HDA_NEEDS_RESUME
 /* resume the all amp commands from the cache */
@@ -1092,7 +1109,7 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
                }
        }
 }
-EXPORT_SYMBOL_GPL(snd_hda_codec_resume_amp);
+EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
 #endif /* SND_HDA_NEEDS_RESUME */
 
 /* volume */
@@ -1120,7 +1137,7 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
        uinfo->value.integer.max = caps;
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_info);
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_info);
 
 int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
@@ -1140,7 +1157,7 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
                        & HDA_AMP_VOLMASK;
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_get);
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get);
 
 int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
@@ -1165,7 +1182,7 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
        snd_hda_power_down(codec);
        return change;
 }
-EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_put);
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_put);
 
 int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
                          unsigned int size, unsigned int __user *_tlv)
@@ -1192,7 +1209,7 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
                return -EFAULT;
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_tlv);
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_tlv);
 
 /*
  * set (static) TLV for virtual master volume; recalculated as max 0dB
@@ -1212,7 +1229,7 @@ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
        tlv[2] = -nums * step;
        tlv[3] = step;
 }
-EXPORT_SYMBOL_GPL(snd_hda_set_vmaster_tlv);
+EXPORT_SYMBOL_HDA(snd_hda_set_vmaster_tlv);
 
 /* find a mixer control element with the given name */
 static struct snd_kcontrol *
@@ -1232,7 +1249,7 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
 {
        return _snd_hda_find_mixer_ctl(codec, name, 0);
 }
-EXPORT_SYMBOL_GPL(snd_hda_find_mixer_ctl);
+EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl);
 
 /* Add a control element and assign to the codec */
 int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl)
@@ -1249,7 +1266,7 @@ int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl)
        *knewp = kctl;
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_ctl_add);
+EXPORT_SYMBOL_HDA(snd_hda_ctl_add);
 
 #ifdef CONFIG_SND_HDA_RECONFIG
 /* Clear all controls assigned to the given codec */
@@ -1268,7 +1285,7 @@ void snd_hda_codec_reset(struct hda_codec *codec)
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        cancel_delayed_work(&codec->power_work);
-       flush_scheduled_work();
+       flush_workqueue(codec->bus->workq);
 #endif
        snd_hda_ctls_clear(codec);
        /* relase PCMs */
@@ -1282,9 +1299,12 @@ void snd_hda_codec_reset(struct hda_codec *codec)
        }
        if (codec->patch_ops.free)
                codec->patch_ops.free(codec);
+       codec->proc_widget_hook = NULL;
        codec->spec = NULL;
        free_hda_cache(&codec->amp_cache);
        free_hda_cache(&codec->cmd_cache);
+       init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
+       init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
        codec->num_pcms = 0;
        codec->pcm_info = NULL;
        codec->preset = NULL;
@@ -1328,7 +1348,7 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
        }
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_add_vmaster);
+EXPORT_SYMBOL_HDA(snd_hda_add_vmaster);
 
 /* switch */
 int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
@@ -1342,7 +1362,7 @@ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
        uinfo->value.integer.max = 1;
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_info);
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_info);
 
 int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
@@ -1362,7 +1382,7 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
                         HDA_AMP_MUTE) ? 0 : 1;
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_get);
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_get);
 
 int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
@@ -1393,7 +1413,7 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
        snd_hda_power_down(codec);
        return change;
 }
-EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put);
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put);
 
 /*
  * bound volume controls
@@ -1411,15 +1431,15 @@ int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol,
        unsigned long pval;
        int err;
 
-       mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+       mutex_lock(&codec->control_mutex);
        pval = kcontrol->private_value;
        kcontrol->private_value = pval & ~AMP_VAL_IDX_MASK; /* index 0 */
        err = snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
        kcontrol->private_value = pval;
-       mutex_unlock(&codec->spdif_mutex);
+       mutex_unlock(&codec->control_mutex);
        return err;
 }
-EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_switch_get);
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_get);
 
 int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol)
@@ -1428,7 +1448,7 @@ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
        unsigned long pval;
        int i, indices, err = 0, change = 0;
 
-       mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+       mutex_lock(&codec->control_mutex);
        pval = kcontrol->private_value;
        indices = (pval & AMP_VAL_IDX_MASK) >> AMP_VAL_IDX_SHIFT;
        for (i = 0; i < indices; i++) {
@@ -1440,10 +1460,10 @@ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
                change |= err;
        }
        kcontrol->private_value = pval;
-       mutex_unlock(&codec->spdif_mutex);
+       mutex_unlock(&codec->control_mutex);
        return err < 0 ? err : change;
 }
-EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_switch_put);
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_put);
 
 /*
  * generic bound volume/swtich controls
@@ -1455,15 +1475,15 @@ int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
        struct hda_bind_ctls *c;
        int err;
 
-       mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+       mutex_lock(&codec->control_mutex);
        c = (struct hda_bind_ctls *)kcontrol->private_value;
        kcontrol->private_value = *c->values;
        err = c->ops->info(kcontrol, uinfo);
        kcontrol->private_value = (long)c;
-       mutex_unlock(&codec->spdif_mutex);
+       mutex_unlock(&codec->control_mutex);
        return err;
 }
-EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_ctls_info);
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_info);
 
 int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
@@ -1472,15 +1492,15 @@ int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
        struct hda_bind_ctls *c;
        int err;
 
-       mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+       mutex_lock(&codec->control_mutex);
        c = (struct hda_bind_ctls *)kcontrol->private_value;
        kcontrol->private_value = *c->values;
        err = c->ops->get(kcontrol, ucontrol);
        kcontrol->private_value = (long)c;
-       mutex_unlock(&codec->spdif_mutex);
+       mutex_unlock(&codec->control_mutex);
        return err;
 }
-EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_ctls_get);
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_get);
 
 int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
@@ -1490,7 +1510,7 @@ int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
        unsigned long *vals;
        int err = 0, change = 0;
 
-       mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+       mutex_lock(&codec->control_mutex);
        c = (struct hda_bind_ctls *)kcontrol->private_value;
        for (vals = c->values; *vals; vals++) {
                kcontrol->private_value = *vals;
@@ -1500,10 +1520,10 @@ int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
                change |= err;
        }
        kcontrol->private_value = (long)c;
-       mutex_unlock(&codec->spdif_mutex);
+       mutex_unlock(&codec->control_mutex);
        return err < 0 ? err : change;
 }
-EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_ctls_put);
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_put);
 
 int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
                           unsigned int size, unsigned int __user *tlv)
@@ -1512,15 +1532,15 @@ int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
        struct hda_bind_ctls *c;
        int err;
 
-       mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+       mutex_lock(&codec->control_mutex);
        c = (struct hda_bind_ctls *)kcontrol->private_value;
        kcontrol->private_value = *c->values;
        err = c->ops->tlv(kcontrol, op_flag, size, tlv);
        kcontrol->private_value = (long)c;
-       mutex_unlock(&codec->spdif_mutex);
+       mutex_unlock(&codec->control_mutex);
        return err;
 }
-EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_tlv);
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_tlv);
 
 struct hda_ctl_ops snd_hda_bind_vol = {
        .info = snd_hda_mixer_amp_volume_info,
@@ -1528,7 +1548,7 @@ struct hda_ctl_ops snd_hda_bind_vol = {
        .put = snd_hda_mixer_amp_volume_put,
        .tlv = snd_hda_mixer_amp_tlv
 };
-EXPORT_SYMBOL_GPL(snd_hda_bind_vol);
+EXPORT_SYMBOL_HDA(snd_hda_bind_vol);
 
 struct hda_ctl_ops snd_hda_bind_sw = {
        .info = snd_hda_mixer_amp_switch_info,
@@ -1536,7 +1556,7 @@ struct hda_ctl_ops snd_hda_bind_sw = {
        .put = snd_hda_mixer_amp_switch_put,
        .tlv = snd_hda_mixer_amp_tlv
 };
-EXPORT_SYMBOL_GPL(snd_hda_bind_sw);
+EXPORT_SYMBOL_HDA(snd_hda_bind_sw);
 
 /*
  * SPDIF out controls
@@ -1798,7 +1818,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
        codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls);
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_create_spdif_out_ctls);
+EXPORT_SYMBOL_HDA(snd_hda_create_spdif_out_ctls);
 
 /*
  * SPDIF sharing with analog output
@@ -1836,7 +1856,7 @@ int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
        return snd_hda_ctl_add(codec,
                           snd_ctl_new1(&spdif_share_sw, mout));
 }
-EXPORT_SYMBOL_GPL(snd_hda_create_spdif_share_sw);
+EXPORT_SYMBOL_HDA(snd_hda_create_spdif_share_sw);
 
 /*
  * SPDIF input
@@ -1946,7 +1966,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
                AC_DIG1_ENABLE;
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_create_spdif_in_ctls);
+EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
 
 #ifdef SND_HDA_NEEDS_RESUME
 /*
@@ -1992,7 +2012,7 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
        snd_hda_power_down(codec);
        return err;
 }
-EXPORT_SYMBOL_GPL(snd_hda_codec_write_cache);
+EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache);
 
 /* resume the all commands from the cache */
 void snd_hda_codec_resume_cache(struct hda_codec *codec)
@@ -2008,7 +2028,7 @@ void snd_hda_codec_resume_cache(struct hda_codec *codec)
                                    get_cmd_cache_cmd(key), buffer->val);
        }
 }
-EXPORT_SYMBOL_GPL(snd_hda_codec_resume_cache);
+EXPORT_SYMBOL_HDA(snd_hda_codec_resume_cache);
 
 /**
  * snd_hda_sequence_write_cache - sequence writes with caching
@@ -2026,7 +2046,7 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
                snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb,
                                          seq->param);
 }
-EXPORT_SYMBOL_GPL(snd_hda_sequence_write_cache);
+EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache);
 #endif /* SND_HDA_NEEDS_RESUME */
 
 /*
@@ -2156,7 +2176,7 @@ int /*__devinit*/ snd_hda_build_controls(struct hda_bus *bus)
        }
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_build_controls);
+EXPORT_SYMBOL_HDA(snd_hda_build_controls);
 
 int snd_hda_codec_build_controls(struct hda_codec *codec)
 {
@@ -2268,7 +2288,7 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
 
        return val;
 }
-EXPORT_SYMBOL_GPL(snd_hda_calc_stream_format);
+EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format);
 
 /**
  * snd_hda_query_supported_pcm - query the supported PCM rates and formats
@@ -2448,7 +2468,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
 
        return 1;
 }
-EXPORT_SYMBOL_GPL(snd_hda_is_supported_format);
+EXPORT_SYMBOL_HDA(snd_hda_is_supported_format);
 
 /*
  * PCM stuff
@@ -2646,7 +2666,7 @@ int __devinit snd_hda_build_pcms(struct hda_bus *bus)
        }
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_build_pcms);
+EXPORT_SYMBOL_HDA(snd_hda_build_pcms);
 
 /**
  * snd_hda_check_board_config - compare the current codec with the config table
@@ -2702,7 +2722,68 @@ int snd_hda_check_board_config(struct hda_codec *codec,
        }
        return -1;
 }
-EXPORT_SYMBOL_GPL(snd_hda_check_board_config);
+EXPORT_SYMBOL_HDA(snd_hda_check_board_config);
+
+/**
+ * snd_hda_check_board_codec_sid_config - compare the current codec
+                                         subsystem ID with the
+                                         config table
+
+          This is important for Gateway notebooks with SB450 HDA Audio
+          where the vendor ID of the PCI device is:
+               ATI Technologies Inc SB450 HDA Audio [1002:437b]
+          and the vendor/subvendor are found only at the codec.
+
+ * @codec: the HDA codec
+ * @num_configs: number of config enums
+ * @models: array of model name strings
+ * @tbl: configuration table, terminated by null entries
+ *
+ * Compares the modelname or PCI subsystem id of the current codec with the
+ * given configuration table.  If a matching entry is found, returns its
+ * config value (supposed to be 0 or positive).
+ *
+ * If no entries are matching, the function returns a negative value.
+ */
+int snd_hda_check_board_codec_sid_config(struct hda_codec *codec,
+                              int num_configs, const char **models,
+                              const struct snd_pci_quirk *tbl)
+{
+       const struct snd_pci_quirk *q;
+
+       /* Search for codec ID */
+       for (q = tbl; q->subvendor; q++) {
+               unsigned long vendorid = (q->subdevice) | (q->subvendor << 16);
+
+               if (vendorid == codec->subsystem_id)
+                       break;
+       }
+
+       if (!q->subvendor)
+               return -1;
+
+       tbl = q;
+
+       if (tbl->value >= 0 && tbl->value < num_configs) {
+#ifdef CONFIG_SND_DEBUG_DETECT
+               char tmp[10];
+               const char *model = NULL;
+               if (models)
+                       model = models[tbl->value];
+               if (!model) {
+                       sprintf(tmp, "#%d", tbl->value);
+                       model = tmp;
+               }
+               snd_printdd(KERN_INFO "hda_codec: model '%s' is selected "
+                           "for config %x:%x (%s)\n",
+                           model, tbl->subvendor, tbl->subdevice,
+                           (tbl->name ? tbl->name : "Unknown device"));
+#endif
+               return tbl->value;
+       }
+       return -1;
+}
+EXPORT_SYMBOL_HDA(snd_hda_check_board_codec_sid_config);
 
 /**
  * snd_hda_add_new_ctls - create controls from the array
@@ -2738,7 +2819,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
        }
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_add_new_ctls);
+EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls);
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
@@ -2781,7 +2862,7 @@ void snd_hda_power_up(struct hda_codec *codec)
        cancel_delayed_work(&codec->power_work);
        codec->power_transition = 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_power_up);
+EXPORT_SYMBOL_HDA(snd_hda_power_up);
 
 #define power_save(codec)      \
        ((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
@@ -2796,11 +2877,11 @@ void snd_hda_power_down(struct hda_codec *codec)
                return;
        if (power_save(codec)) {
                codec->power_transition = 1; /* avoid reentrance */
-               schedule_delayed_work(&codec->power_work,
+               queue_delayed_work(codec->bus->workq, &codec->power_work,
                                msecs_to_jiffies(power_save(codec) * 1000));
        }
 }
-EXPORT_SYMBOL_GPL(snd_hda_power_down);
+EXPORT_SYMBOL_HDA(snd_hda_power_down);
 
 int snd_hda_check_amp_list_power(struct hda_codec *codec,
                                 struct hda_loopback_check *check,
@@ -2837,7 +2918,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
        }
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_check_amp_list_power);
+EXPORT_SYMBOL_HDA(snd_hda_check_amp_list_power);
 #endif
 
 /*
@@ -2857,7 +2938,7 @@ int snd_hda_ch_mode_info(struct hda_codec *codec,
                chmode[uinfo->value.enumerated.item].channels);
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_ch_mode_info);
+EXPORT_SYMBOL_HDA(snd_hda_ch_mode_info);
 
 int snd_hda_ch_mode_get(struct hda_codec *codec,
                        struct snd_ctl_elem_value *ucontrol,
@@ -2875,7 +2956,7 @@ int snd_hda_ch_mode_get(struct hda_codec *codec,
        }
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_ch_mode_get);
+EXPORT_SYMBOL_HDA(snd_hda_ch_mode_get);
 
 int snd_hda_ch_mode_put(struct hda_codec *codec,
                        struct snd_ctl_elem_value *ucontrol,
@@ -2896,7 +2977,7 @@ int snd_hda_ch_mode_put(struct hda_codec *codec,
                snd_hda_sequence_write_cache(codec, chmode[mode].sequence);
        return 1;
 }
-EXPORT_SYMBOL_GPL(snd_hda_ch_mode_put);
+EXPORT_SYMBOL_HDA(snd_hda_ch_mode_put);
 
 /*
  * input MUX helper
@@ -2917,7 +2998,7 @@ int snd_hda_input_mux_info(const struct hda_input_mux *imux,
        strcpy(uinfo->value.enumerated.name, imux->items[index].label);
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_input_mux_info);
+EXPORT_SYMBOL_HDA(snd_hda_input_mux_info);
 
 int snd_hda_input_mux_put(struct hda_codec *codec,
                          const struct hda_input_mux *imux,
@@ -2939,7 +3020,7 @@ int snd_hda_input_mux_put(struct hda_codec *codec,
        *cur_val = idx;
        return 1;
 }
-EXPORT_SYMBOL_GPL(snd_hda_input_mux_put);
+EXPORT_SYMBOL_HDA(snd_hda_input_mux_put);
 
 
 /*
@@ -2992,7 +3073,7 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec,
        mutex_unlock(&codec->spdif_mutex);
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_open);
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_open);
 
 int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
                                  struct hda_multi_out *mout,
@@ -3005,7 +3086,7 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
        mutex_unlock(&codec->spdif_mutex);
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_prepare);
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_prepare);
 
 /*
  * release the digital out
@@ -3018,7 +3099,7 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec,
        mutex_unlock(&codec->spdif_mutex);
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_close);
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_close);
 
 /*
  * set up more restrictions for analog out
@@ -3058,7 +3139,7 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec,
        return snd_pcm_hw_constraint_step(substream->runtime, 0,
                                          SNDRV_PCM_HW_PARAM_CHANNELS, 2);
 }
-EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_open);
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_open);
 
 /*
  * set up the i/o for analog out
@@ -3117,7 +3198,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
        }
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_prepare);
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare);
 
 /*
  * clean up the setting for analog out
@@ -3144,7 +3225,7 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
        mutex_unlock(&codec->spdif_mutex);
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_cleanup);
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_cleanup);
 
 /*
  * Helper for automatic pin configuration
@@ -3430,13 +3511,13 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_parse_pin_def_config);
+EXPORT_SYMBOL_HDA(snd_hda_parse_pin_def_config);
 
 /* labels for input pins */
 const char *auto_pin_cfg_labels[AUTO_PIN_LAST] = {
        "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux"
 };
-EXPORT_SYMBOL_GPL(auto_pin_cfg_labels);
+EXPORT_SYMBOL_HDA(auto_pin_cfg_labels);
 
 
 #ifdef CONFIG_PM
@@ -3464,7 +3545,7 @@ int snd_hda_suspend(struct hda_bus *bus, pm_message_t state)
        }
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_suspend);
+EXPORT_SYMBOL_HDA(snd_hda_suspend);
 
 /**
  * snd_hda_resume - resume the codecs
@@ -3485,7 +3566,7 @@ int snd_hda_resume(struct hda_bus *bus)
        }
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_hda_resume);
+EXPORT_SYMBOL_HDA(snd_hda_resume);
 #endif /* CONFIG_PM */
 
 /*
@@ -3515,7 +3596,7 @@ void *snd_array_new(struct snd_array *array)
        }
        return snd_array_elem(array, array->used++);
 }
-EXPORT_SYMBOL_GPL(snd_array_new);
+EXPORT_SYMBOL_HDA(snd_array_new);
 
 /* free the given array elements */
 void snd_array_free(struct snd_array *array)
@@ -3525,7 +3606,7 @@ void snd_array_free(struct snd_array *array)
        array->alloced = 0;
        array->list = NULL;
 }
-EXPORT_SYMBOL_GPL(snd_array_free);
+EXPORT_SYMBOL_HDA(snd_array_free);
 
 /*
  * used by hda_proc.c and hda_eld.c
@@ -3544,7 +3625,7 @@ void snd_print_pcm_rates(int pcm, char *buf, int buflen)
 
        buf[j] = '\0'; /* necessary when j == 0 */
 }
-EXPORT_SYMBOL_GPL(snd_print_pcm_rates);
+EXPORT_SYMBOL_HDA(snd_print_pcm_rates);
 
 void snd_print_pcm_bits(int pcm, char *buf, int buflen)
 {
@@ -3557,7 +3638,7 @@ void snd_print_pcm_bits(int pcm, char *buf, int buflen)
 
        buf[j] = '\0'; /* necessary when j == 0 */
 }
-EXPORT_SYMBOL_GPL(snd_print_pcm_bits);
+EXPORT_SYMBOL_HDA(snd_print_pcm_bits);
 
 MODULE_DESCRIPTION("HDA codec core");
 MODULE_LICENSE("GPL");