Merge linux-3.10.67 into dev-kernel-3.10
[linux-3.10.git] / sound / pci / hda / hda_codec.c
index 83a0f9b..7461cdb 100644 (file)
@@ -845,7 +845,7 @@ int snd_hda_bus_new(struct snd_card *card,
 
        snprintf(bus->workq_name, sizeof(bus->workq_name),
                 "hd-audio%d", card->number);
-       bus->workq = create_singlethread_workqueue(bus->workq_name);
+       bus->workq = create_freezable_workqueue(bus->workq_name);
        if (!bus->workq) {
                snd_printk(KERN_ERR "cannot create workqueue %s\n",
                           bus->workq_name);
@@ -1426,9 +1426,11 @@ int snd_hda_codec_new(struct hda_bus *bus,
 #ifdef CONFIG_PM
        codec->d3_stop_clk = snd_hda_codec_get_supported_ps(codec, fg,
                                        AC_PWRST_CLKSTOP);
+#ifndef CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA
        if (!codec->d3_stop_clk)
                bus->power_keep_link_on = 1;
 #endif
+#endif
        codec->epss = snd_hda_codec_get_supported_ps(codec, fg,
                                        AC_PWRST_EPSS);
 
@@ -3269,6 +3271,44 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
        return change;
 }
 
+int snd_hda_max_pcm_ch_info(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 0xFFFFFFFF;
+       return 0;
+}
+
+int snd_hda_hdmi_decode_info(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 0xFFFFFFFF;
+       return 0;
+}
+
+static int snd_hda_max_pcm_ch_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = codec->max_pcm_channels;
+       return 0;
+}
+
+static int snd_hda_hdmi_decode_get(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = codec->recv_dec_cap;
+       return 0;
+}
+
 static struct snd_kcontrol_new dig_mixes[] = {
        {
                .access = SNDRV_CTL_ELEM_ACCESS_READ,
@@ -3298,6 +3338,20 @@ static struct snd_kcontrol_new dig_mixes[] = {
                .get = snd_hda_spdif_out_switch_get,
                .put = snd_hda_spdif_out_switch_put,
        },
+       {
+               .access = SNDRV_CTL_ELEM_ACCESS_READ,
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "HDA Decode Capability",
+               .info = snd_hda_hdmi_decode_info,
+               .get = snd_hda_hdmi_decode_get,
+       },
+       {
+               .access = SNDRV_CTL_ELEM_ACCESS_READ,
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "HDA Maximum PCM Channels",
+               .info = snd_hda_max_pcm_ch_info,
+               .get = snd_hda_max_pcm_ch_get,
+       },
        { } /* end */
 };
 
@@ -3774,7 +3828,7 @@ static unsigned int hda_sync_power_state(struct hda_codec *codec,
                if (time_after_eq(jiffies, end_time))
                        break;
                /* wait until the codec reachs to the target state */
-               msleep(1);
+               mdelay(1);
        }
        return state;
 }
@@ -3894,6 +3948,9 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq)
        codec->power_jiffies = jiffies;
        spin_unlock(&codec->power_lock);
        codec->in_pm = 0;
+#ifdef CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA
+       state |= AC_PWRST_CLK_STOP_OK;
+#endif
        return state;
 }
 
@@ -5514,11 +5571,18 @@ EXPORT_SYMBOL_HDA(snd_hda_add_imux_item);
 int snd_hda_suspend(struct hda_bus *bus)
 {
        struct hda_codec *codec;
+       unsigned int state;
 
        list_for_each_entry(codec, &bus->codec_list, list) {
                cancel_delayed_work_sync(&codec->jackpoll_work);
-               if (hda_codec_is_power_on(codec))
-                       hda_call_codec_suspend(codec, false);
+               if (hda_codec_is_power_on(codec)) {
+                       state = hda_call_codec_suspend(codec, false);
+                       codec->pm_down_notified = 0;
+                       if (!bus->power_keep_link_on && (state & AC_PWRST_CLK_STOP_OK)) {
+                               codec->pm_down_notified = 1;
+                               hda_call_pm_notify(bus, false);
+                       }
+               }
        }
        return 0;
 }