Revert "Merge commit 'main-jb-2012.08.03-B4' into t114-0806"
[linux-2.6.git] / sound / pci / hda / hda_codec.c
index 6b611d5..6f3a857 100644 (file)
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
+#include <linux/mm.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/pci.h>
 #include <linux/mutex.h>
+#include <linux/module.h>
 #include <sound/core.h>
 #include "hda_codec.h"
 #include <sound/asoundef.h>
@@ -32,6 +34,7 @@
 #include <sound/jack.h>
 #include "hda_local.h"
 #include "hda_beep.h"
+#include "hda_jack.h"
 #include <sound/hda_hwdep.h>
 
 #define CREATE_TRACE_POINTS
@@ -773,7 +776,7 @@ int /*__devinit*/ 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);
@@ -1189,6 +1192,7 @@ static void snd_hda_codec_free(struct hda_codec *codec)
 {
        if (!codec)
                return;
+       snd_hda_jack_tbl_clear(codec);
        restore_init_pincfgs(codec);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        cancel_delayed_work(&codec->power_work);
@@ -1197,6 +1201,7 @@ static void snd_hda_codec_free(struct hda_codec *codec)
        list_del(&codec->list);
        snd_array_free(&codec->mixers);
        snd_array_free(&codec->nids);
+       snd_array_free(&codec->cvt_setups);
        snd_array_free(&codec->conn_lists);
        snd_array_free(&codec->spdif_out);
        codec->bus->caddr_tbl[codec->addr] = NULL;
@@ -1445,7 +1450,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
                for (i = 0; i < c->cvt_setups.used; i++) {
                        p = snd_array_elem(&c->cvt_setups, i);
                        if (!p->active && p->stream_tag == stream_tag &&
-                           get_wcaps_type(get_wcaps(codec, p->nid)) == type)
+                           get_wcaps_type(get_wcaps(c, p->nid)) == type)
                                p->dirty = 1;
                }
        }
@@ -1491,8 +1496,11 @@ static void really_cleanup_stream(struct hda_codec *codec,
                                  struct hda_cvt_setup *q)
 {
        hda_nid_t nid = q->nid;
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
+       if (q->stream_tag || q->channel_id)
+               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+       if (q->format_id)
+               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0
+);
        memset(q, 0, sizeof(*q));
        q->nid = nid;
 }
@@ -1719,43 +1727,6 @@ int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
 }
 EXPORT_SYMBOL_HDA(snd_hda_override_pin_caps);
 
-/**
- * snd_hda_pin_sense - execute pin sense measurement
- * @codec: the CODEC to sense
- * @nid: the pin NID to sense
- *
- * Execute necessary pin sense measurement and return its Presence Detect,
- * Impedance, ELD Valid etc. status bits.
- */
-u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
-{
-       u32 pincap;
-
-       if (!codec->no_trigger_sense) {
-               pincap = snd_hda_query_pin_caps(codec, nid);
-               if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
-                       snd_hda_codec_read(codec, nid, 0,
-                                       AC_VERB_SET_PIN_SENSE, 0);
-       }
-       return snd_hda_codec_read(codec, nid, 0,
-                                 AC_VERB_GET_PIN_SENSE, 0);
-}
-EXPORT_SYMBOL_HDA(snd_hda_pin_sense);
-
-/**
- * snd_hda_jack_detect - query pin Presence Detect status
- * @codec: the CODEC to sense
- * @nid: the pin NID to sense
- *
- * Query and return the pin's Presence Detect status.
- */
-int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
-{
-       u32 sense = snd_hda_pin_sense(codec, nid);
-       return !!(sense & AC_PINSENSE_PRESENCE);
-}
-EXPORT_SYMBOL_HDA(snd_hda_jack_detect);
-
 /*
  * read the current volume to info
  * if the cache exists, read the cache value.
@@ -1791,7 +1762,11 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
        parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT;
        parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT;
        parm |= index << AC_AMP_SET_INDEX_SHIFT;
-       parm |= val;
+       if ((val & HDA_AMP_MUTE) && !(info->amp_caps & AC_AMPCAP_MUTE) &&
+           (info->amp_caps & AC_AMPCAP_MIN_MUTE))
+               ; /* set the zero value as a fake mute */
+       else
+               parm |= val;
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm);
        info->vol[ch] = val;
 }
@@ -2058,7 +2033,7 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
        val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT);
        val1 += ofs;
        val1 = ((int)val1) * ((int)val2);
-       if (min_mute)
+       if (min_mute || (caps & AC_AMPCAP_MIN_MUTE))
                val2 |= TLV_DB_SCALE_MUTE;
        if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv))
                return -EFAULT;
@@ -2304,6 +2279,7 @@ int snd_hda_codec_reset(struct hda_codec *codec)
        }
        if (codec->patch_ops.free)
                codec->patch_ops.free(codec);
+       snd_hda_jack_tbl_clear(codec);
        codec->proc_widget_hook = NULL;
        codec->spec = NULL;
        free_hda_cache(&codec->amp_cache);
@@ -2327,12 +2303,105 @@ int snd_hda_codec_reset(struct hda_codec *codec)
        return 0;
 }
 
+typedef int (*map_slave_func_t)(void *, struct snd_kcontrol *);
+
+/* apply the function to all matching slave ctls in the mixer list */
+static int map_slaves(struct hda_codec *codec, const char * const *slaves,
+                     const char *suffix, map_slave_func_t func, void *data) 
+{
+       struct hda_nid_item *items;
+       const char * const *s;
+       int i, err;
+
+       items = codec->mixers.list;
+       for (i = 0; i < codec->mixers.used; i++) {
+               struct snd_kcontrol *sctl = items[i].kctl;
+               if (!sctl || !sctl->id.name ||
+                   sctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER)
+                       continue;
+               for (s = slaves; *s; s++) {
+                       char tmpname[sizeof(sctl->id.name)];
+                       const char *name = *s;
+                       if (suffix) {
+                               snprintf(tmpname, sizeof(tmpname), "%s %s",
+                                        name, suffix);
+                               name = tmpname;
+                       }
+                       if (!strcmp(sctl->id.name, name)) {
+                               err = func(data, sctl);
+                               if (err)
+                                       return err;
+                               break;
+                       }
+               }
+       }
+       return 0;
+}
+
+static int check_slave_present(void *data, struct snd_kcontrol *sctl)
+{
+       return 1;
+}
+
+/* guess the value corresponding to 0dB */
+static int get_kctl_0dB_offset(struct snd_kcontrol *kctl)
+{
+       int _tlv[4];
+       const int *tlv = NULL;
+       int val = -1;
+
+       if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
+               /* FIXME: set_fs() hack for obtaining user-space TLV data */
+               mm_segment_t fs = get_fs();
+               set_fs(get_ds());
+               if (!kctl->tlv.c(kctl, 0, sizeof(_tlv), _tlv))
+                       tlv = _tlv;
+               set_fs(fs);
+       } else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ)
+               tlv = kctl->tlv.p;
+       if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE)
+               val = -tlv[2] / tlv[3];
+       return val;
+}
+
+/* call kctl->put with the given value(s) */
+static int put_kctl_with_value(struct snd_kcontrol *kctl, int val)
+{
+       struct snd_ctl_elem_value *ucontrol;
+       ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL);
+       if (!ucontrol)
+               return -ENOMEM;
+       ucontrol->value.integer.value[0] = val;
+       ucontrol->value.integer.value[1] = val;
+       kctl->put(kctl, ucontrol);
+       kfree(ucontrol);
+       return 0;
+}
+
+/* initialize the slave volume with 0dB */
+static int init_slave_0dB(void *data, struct snd_kcontrol *slave)
+{
+       int offset = get_kctl_0dB_offset(slave);
+       if (offset > 0)
+               put_kctl_with_value(slave, offset);
+       return 0;
+}
+
+/* unmute the slave */
+static int init_slave_unmute(void *data, struct snd_kcontrol *slave)
+{
+       return put_kctl_with_value(slave, 1);
+}
+
 /**
  * snd_hda_add_vmaster - create a virtual master control and add slaves
  * @codec: HD-audio codec
  * @name: vmaster control name
  * @tlv: TLV data (optional)
  * @slaves: slave control names (optional)
+ * @suffix: suffix string to each slave name (optional)
+ * @init_slave_vol: initialize slaves to unmute/0dB
+ * @ctl_ret: store the vmaster kcontrol in return
  *
  * Create a virtual master control with the given name.  The TLV data
  * must be either NULL or a valid data.
@@ -2343,16 +2412,19 @@ int snd_hda_codec_reset(struct hda_codec *codec)
  *
  * This function returns zero if successful or a negative error code.
  */
-int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
-                       unsigned int *tlv, const char * const *slaves)
+int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
+                       unsigned int *tlv, const char * const *slaves,
+                         const char *suffix, bool init_slave_vol,
+                         struct snd_kcontrol **ctl_ret)
 {
        struct snd_kcontrol *kctl;
-       const char * const *s;
        int err;
 
-       for (s = slaves; *s && !snd_hda_find_mixer_ctl(codec, *s); s++)
-               ;
-       if (!*s) {
+       if (ctl_ret)
+               *ctl_ret = NULL;
+
+       err = map_slaves(codec, slaves, suffix, check_slave_present, NULL);
+       if (err != 1) {
                snd_printdd("No slave found for %s\n", name);
                return 0;
        }
@@ -2363,26 +2435,119 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
        if (err < 0)
                return err;
 
-       for (s = slaves; *s; s++) {
-               struct snd_kcontrol *sctl;
-               int i = 0;
-               for (;;) {
-                       sctl = _snd_hda_find_mixer_ctl(codec, *s, i);
-                       if (!sctl) {
-                               if (!i)
-                                       snd_printdd("Cannot find slave %s, "
-                                                   "skipped\n", *s);
-                               break;
-                       }
-                       err = snd_ctl_add_slave(kctl, sctl);
-                       if (err < 0)
-                               return err;
-                       i++;
-               }
-       }
+       err = map_slaves(codec, slaves, suffix,
+                        (map_slave_func_t)snd_ctl_add_slave, kctl);
+       if (err < 0)
+               return err;
+
+       /* init with master mute & zero volume */
+       put_kctl_with_value(kctl, 0);
+       if (init_slave_vol)
+               map_slaves(codec, slaves, suffix,
+                          tlv ? init_slave_0dB : init_slave_unmute, kctl);
+
+       if (ctl_ret)
+               *ctl_ret = kctl;
        return 0;
 }
-EXPORT_SYMBOL_HDA(snd_hda_add_vmaster);
+EXPORT_SYMBOL_HDA(__snd_hda_add_vmaster);
+
+/*
+ * mute-LED control using vmaster
+ */
+static int vmaster_mute_mode_info(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_info *uinfo)
+{
+       static const char * const texts[] = {
+               "Off", "On", "Follow Master"
+       };
+       unsigned int index;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 3;
+       index = uinfo->value.enumerated.item;
+       if (index >= 3)
+               index = 2;
+       strcpy(uinfo->value.enumerated.name, texts[index]);
+       return 0;
+}
+
+static int vmaster_mute_mode_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_vmaster_mute_hook *hook = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.enumerated.item[0] = hook->mute_mode;
+       return 0;
+}
+
+static int vmaster_mute_mode_put(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_vmaster_mute_hook *hook = snd_kcontrol_chip(kcontrol);
+       unsigned int old_mode = hook->mute_mode;
+
+       hook->mute_mode = ucontrol->value.enumerated.item[0];
+       if (hook->mute_mode > HDA_VMUTE_FOLLOW_MASTER)
+               hook->mute_mode = HDA_VMUTE_FOLLOW_MASTER;
+       if (old_mode == hook->mute_mode)
+               return 0;
+       snd_hda_sync_vmaster_hook(hook);
+       return 1;
+}
+
+static struct snd_kcontrol_new vmaster_mute_mode = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Mute-LED Mode",
+       .info = vmaster_mute_mode_info,
+       .get = vmaster_mute_mode_get,
+       .put = vmaster_mute_mode_put,
+};
+
+/*
+ * Add a mute-LED hook with the given vmaster switch kctl
+ * "Mute-LED Mode" control is automatically created and associated with
+ * the given hook.
+ */
+int snd_hda_add_vmaster_hook(struct hda_codec *codec,
+                            struct hda_vmaster_mute_hook *hook,
+                            bool expose_enum_ctl)
+{
+       struct snd_kcontrol *kctl;
+
+       if (!hook->hook || !hook->sw_kctl)
+               return 0;
+       snd_ctl_add_vmaster_hook(hook->sw_kctl, hook->hook, codec);
+       hook->codec = codec;
+       hook->mute_mode = HDA_VMUTE_FOLLOW_MASTER;
+       if (!expose_enum_ctl)
+               return 0;
+       kctl = snd_ctl_new1(&vmaster_mute_mode, hook);
+       if (!kctl)
+               return -ENOMEM;
+       return snd_hda_ctl_add(codec, 0, kctl);
+}
+EXPORT_SYMBOL_HDA(snd_hda_add_vmaster_hook);
+
+/*
+ * Call the hook with the current value for synchronization
+ * Should be called in init callback
+ */
+void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook)
+{
+       if (!hook->hook || !hook->codec)
+               return;
+       switch (hook->mute_mode) {
+       case HDA_VMUTE_FOLLOW_MASTER:
+               snd_ctl_sync_vmaster_hook(hook->sw_kctl);
+               break;
+       default:
+               hook->hook(hook->codec, hook->mute_mode);
+               break;
+       }
+}
+EXPORT_SYMBOL_HDA(snd_hda_sync_vmaster_hook);
+
 
 /**
  * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch
@@ -2853,6 +3018,25 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
        return change;
 }
 
+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_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,
@@ -2882,6 +3066,12 @@ static struct snd_kcontrol_new dig_mixes[] = {
                .get = snd_hda_spdif_out_switch_get,
                .put = snd_hda_spdif_out_switch_put,
        },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "HDA Decode Capability",
+               .info = snd_hda_hdmi_decode_info,
+               .get = snd_hda_hdmi_decode_get,
+       },
        { } /* end */
 };
 
@@ -3274,7 +3464,7 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
                                                   AC_VERB_GET_POWER_STATE, 0);
                        if (state == power_state)
                                break;
-                       msleep(1);
+                       mdelay(1);
                } while (time_after_eq(end_time, jiffies));
        }
 }
@@ -3342,6 +3532,7 @@ static void hda_call_codec_resume(struct hda_codec *codec)
        restore_pincfgs(codec); /* restore all current pin configs */
        restore_shutup_pins(codec);
        hda_exec_init_verbs(codec);
+       snd_hda_jack_set_dirty_all(codec);
        if (codec->patch_ops.resume)
                codec->patch_ops.resume(codec);
        else {
@@ -3828,6 +4019,12 @@ static int get_empty_pcm_device(struct hda_bus *bus, int type)
                if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits))
                        return audio_idx[type][i];
 
+       /* non-fixed slots starting from 10 */
+       for (i = 10; i < 32; i++) {
+               if (!test_and_set_bit(i, bus->pcm_dev_bits))
+                       return i;
+       }
+
        snd_printk(KERN_WARNING "Too many %s devices\n",
                snd_hda_pcm_type_name[type]);
        return -EAGAIN;
@@ -4024,9 +4221,9 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec,
 
        /* Search for codec ID */
        for (q = tbl; q->subvendor; q++) {
-               unsigned long vendorid = (q->subdevice) | (q->subvendor << 16);
-
-               if (vendorid == codec->subsystem_id)
+               unsigned int mask = 0xffff0000 | q->subdevice_mask;
+               unsigned int id = (q->subdevice | (q->subvendor << 16)) & mask;
+               if ((codec->subsystem_id & mask) == id)
                        break;
        }
 
@@ -4691,6 +4888,27 @@ static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg)
        }
 }
 
+/* Reorder the surround channels
+ * ALSA sequence is front/surr/clfe/side
+ * HDA sequence is:
+ *    4-ch: front/surr  =>  OK as it is
+ *    6-ch: front/clfe/surr
+ *    8-ch: front/clfe/rear/side|fc
+ */
+static void reorder_outputs(unsigned int nums, hda_nid_t *pins)
+{
+       hda_nid_t nid;
+
+       switch (nums) {
+       case 3:
+       case 4:
+               nid = pins[1];
+               pins[1] = pins[2];
+               pins[2] = nid;
+               break;
+       }
+}
+
 /*
  * Parse all pin widgets and store the useful pin nids to cfg
  *
@@ -4727,6 +4945,7 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
        memset(sequences_hp, 0, sizeof(sequences_hp));
        assoc_line_out = 0;
 
+       codec->ignore_misc_bit = true;
        end_nid = codec->start_nid + codec->num_nodes;
        for (nid = codec->start_nid; nid < end_nid; nid++) {
                unsigned int wid_caps = get_wcaps(codec, nid);
@@ -4742,6 +4961,9 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
                        continue;
 
                def_conf = snd_hda_codec_get_pincfg(codec, nid);
+               if (!(get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
+                     AC_DEFCFG_MISC_NO_PRESENCE))
+                       codec->ignore_misc_bit = false;
                conn = get_defcfg_connect(def_conf);
                if (conn == AC_JACK_PORT_NONE)
                        continue;
@@ -4886,21 +5108,9 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
                }
        }
 
-       /* Reorder the surround channels
-        * ALSA sequence is front/surr/clfe/side
-        * HDA sequence is:
-        *    4-ch: front/surr  =>  OK as it is
-        *    6-ch: front/clfe/surr
-        *    8-ch: front/clfe/rear/side|fc
-        */
-       switch (cfg->line_outs) {
-       case 3:
-       case 4:
-               nid = cfg->line_out_pins[1];
-               cfg->line_out_pins[1] = cfg->line_out_pins[2];
-               cfg->line_out_pins[2] = nid;
-               break;
-       }
+       reorder_outputs(cfg->line_outs, cfg->line_out_pins);
+       reorder_outputs(cfg->hp_outs, cfg->hp_pins);
+       reorder_outputs(cfg->speaker_outs, cfg->speaker_pins);
 
        sort_autocfg_input_pins(cfg);
 
@@ -4969,8 +5179,8 @@ EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr);
  * "Rear", "Internal".
  */
 
-const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin,
-                                       int check_location)
+static const char *hda_get_input_pin_label(struct hda_codec *codec,
+                                          hda_nid_t pin, bool check_location)
 {
        unsigned int def_conf;
        static const char * const mic_names[] = {
@@ -5009,7 +5219,6 @@ const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin,
                return "Misc";
        }
 }
-EXPORT_SYMBOL_HDA(hda_get_input_pin_label);
 
 /* Check whether the location prefix needs to be added to the label.
  * If all mic-jacks are in the same location (e.g. rear panel), we don't
@@ -5066,6 +5275,149 @@ const char *hda_get_autocfg_input_label(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label);
 
+/* return the position of NID in the list, or -1 if not found */
+static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+       int i;
+       for (i = 0; i < nums; i++)
+               if (list[i] == nid)
+                       return i;
+       return -1;
+}
+
+/* get a unique suffix or an index number */
+static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins,
+                                   int num_pins, int *indexp)
+{
+       static const char * const channel_sfx[] = {
+               " Front", " Surround", " CLFE", " Side"
+       };
+       int i;
+
+       i = find_idx_in_nid_list(nid, pins, num_pins);
+       if (i < 0)
+               return NULL;
+       if (num_pins == 1)
+               return "";
+       if (num_pins > ARRAY_SIZE(channel_sfx)) {
+               if (indexp)
+                       *indexp = i;
+               return "";
+       }
+       return channel_sfx[i];
+}
+
+static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid,
+                              const struct auto_pin_cfg *cfg,
+                              const char *name, char *label, int maxlen,
+                              int *indexp)
+{
+       unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+       int attr = snd_hda_get_input_pin_attr(def_conf);
+       const char *pfx = "", *sfx = "";
+
+       /* handle as a speaker if it's a fixed line-out */
+       if (!strcmp(name, "Line Out") && attr == INPUT_PIN_ATTR_INT)
+               name = "Speaker";
+       /* check the location */
+       switch (attr) {
+       case INPUT_PIN_ATTR_DOCK:
+               pfx = "Dock ";
+               break;
+       case INPUT_PIN_ATTR_FRONT:
+               pfx = "Front ";
+               break;
+       }
+       if (cfg) {
+               /* try to give a unique suffix if needed */
+               sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs,
+                                      indexp);
+               if (!sfx)
+                       sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs,
+                                              indexp);
+               if (!sfx) {
+                       /* don't add channel suffix for Headphone controls */
+                       int idx = find_idx_in_nid_list(nid, cfg->hp_pins,
+                                                      cfg->hp_outs);
+                       if (idx >= 0)
+                               *indexp = idx;
+                       sfx = "";
+               }
+       }
+       snprintf(label, maxlen, "%s%s%s", pfx, name, sfx);
+       return 1;
+}
+
+/**
+ * snd_hda_get_pin_label - Get a label for the given I/O pin
+ *
+ * Get a label for the given pin.  This function works for both input and
+ * output pins.  When @cfg is given as non-NULL, the function tries to get
+ * an optimized label using hda_get_autocfg_input_label().
+ *
+ * This function tries to give a unique label string for the pin as much as
+ * possible.  For example, when the multiple line-outs are present, it adds
+ * the channel suffix like "Front", "Surround", etc (only when @cfg is given).
+ * If no unique name with a suffix is available and @indexp is non-NULL, the
+ * index number is stored in the pointer.
+ */
+int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
+                         const struct auto_pin_cfg *cfg,
+                         char *label, int maxlen, int *indexp)
+{
+       unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+       const char *name = NULL;
+       int i;
+
+       if (indexp)
+               *indexp = 0;
+       if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
+               return 0;
+
+       switch (get_defcfg_device(def_conf)) {
+       case AC_JACK_LINE_OUT:
+               return fill_audio_out_name(codec, nid, cfg, "Line Out",
+                                          label, maxlen, indexp);
+       case AC_JACK_SPEAKER:
+               return fill_audio_out_name(codec, nid, cfg, "Speaker",
+                                          label, maxlen, indexp);
+       case AC_JACK_HP_OUT:
+               return fill_audio_out_name(codec, nid, cfg, "Headphone",
+                                          label, maxlen, indexp);
+       case AC_JACK_SPDIF_OUT:
+       case AC_JACK_DIG_OTHER_OUT:
+               if (get_defcfg_location(def_conf) == AC_JACK_LOC_HDMI)
+                       name = "HDMI";
+               else
+                       name = "SPDIF";
+               if (cfg && indexp) {
+                       i = find_idx_in_nid_list(nid, cfg->dig_out_pins,
+                                                cfg->dig_outs);
+                       if (i >= 0)
+                               *indexp = i;
+               }
+               break;
+       default:
+               if (cfg) {
+                       for (i = 0; i < cfg->num_inputs; i++) {
+                               if (cfg->inputs[i].pin != nid)
+                                       continue;
+                               name = hda_get_autocfg_input_label(codec, cfg, i);
+                               if (name)
+                                       break;
+                       }
+               }
+               if (!name)
+                       name = hda_get_input_pin_label(codec, nid, true);
+               break;
+       }
+       if (!name)
+               return 0;
+       strlcpy(label, name, maxlen);
+       return 1;
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_pin_label);
+
 /**
  * snd_hda_add_imux_item - Add an item to input_mux
  *
@@ -5197,30 +5549,6 @@ void snd_array_free(struct snd_array *array)
 EXPORT_SYMBOL_HDA(snd_array_free);
 
 /**
- * snd_print_pcm_rates - Print the supported PCM rates to the string buffer
- * @pcm: PCM caps bits
- * @buf: the string buffer to write
- * @buflen: the max buffer length
- *
- * used by hda_proc.c and hda_eld.c
- */
-void snd_print_pcm_rates(int pcm, char *buf, int buflen)
-{
-       static unsigned int rates[] = {
-               8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
-               96000, 176400, 192000, 384000
-       };
-       int i, j;
-
-       for (i = 0, j = 0; i < ARRAY_SIZE(rates); i++)
-               if (pcm & (1 << i))
-                       j += snprintf(buf + j, buflen - j,  " %d", rates[i]);
-
-       buf[j] = '\0'; /* necessary when j == 0 */
-}
-EXPORT_SYMBOL_HDA(snd_print_pcm_rates);
-
-/**
  * snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer
  * @pcm: PCM caps bits
  * @buf: the string buffer to write
@@ -5241,111 +5569,5 @@ void snd_print_pcm_bits(int pcm, char *buf, int buflen)
 }
 EXPORT_SYMBOL_HDA(snd_print_pcm_bits);
 
-#ifdef CONFIG_SND_HDA_INPUT_JACK
-/*
- * Input-jack notification support
- */
-struct hda_jack_item {
-       hda_nid_t nid;
-       int type;
-       struct snd_jack *jack;
-};
-
-static const char *get_jack_default_name(struct hda_codec *codec, hda_nid_t nid,
-                                        int type)
-{
-       switch (type) {
-       case SND_JACK_HEADPHONE:
-               return "Headphone";
-       case SND_JACK_MICROPHONE:
-               return "Mic";
-       case SND_JACK_LINEOUT:
-               return "Line-out";
-       case SND_JACK_HEADSET:
-               return "Headset";
-       case SND_JACK_VIDEOOUT:
-               return "HDMI/DP";
-       default:
-               return "Misc";
-       }
-}
-
-static void hda_free_jack_priv(struct snd_jack *jack)
-{
-       struct hda_jack_item *jacks = jack->private_data;
-       jacks->nid = 0;
-       jacks->jack = NULL;
-}
-
-int snd_hda_input_jack_add(struct hda_codec *codec, hda_nid_t nid, int type,
-                          const char *name)
-{
-       struct hda_jack_item *jack;
-       int err;
-
-       snd_array_init(&codec->jacks, sizeof(*jack), 32);
-       jack = snd_array_new(&codec->jacks);
-       if (!jack)
-               return -ENOMEM;
-
-       jack->nid = nid;
-       jack->type = type;
-       if (!name)
-               name = get_jack_default_name(codec, nid, type);
-       err = snd_jack_new(codec->bus->card, name, type, &jack->jack);
-       if (err < 0) {
-               jack->nid = 0;
-               return err;
-       }
-       jack->jack->private_data = jack;
-       jack->jack->private_free = hda_free_jack_priv;
-       return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_input_jack_add);
-
-void snd_hda_input_jack_report(struct hda_codec *codec, hda_nid_t nid)
-{
-       struct hda_jack_item *jacks = codec->jacks.list;
-       int i;
-
-       if (!jacks)
-               return;
-
-       for (i = 0; i < codec->jacks.used; i++, jacks++) {
-               unsigned int pin_ctl;
-               unsigned int present;
-               int type;
-
-               if (jacks->nid != nid)
-                       continue;
-               present = snd_hda_jack_detect(codec, nid);
-               type = jacks->type;
-               if (type == (SND_JACK_HEADPHONE | SND_JACK_LINEOUT)) {
-                       pin_ctl = snd_hda_codec_read(codec, nid, 0,
-                                            AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-                       type = (pin_ctl & AC_PINCTL_HP_EN) ?
-                               SND_JACK_HEADPHONE : SND_JACK_LINEOUT;
-               }
-               snd_jack_report(jacks->jack, present ? type : 0);
-       }
-}
-EXPORT_SYMBOL_HDA(snd_hda_input_jack_report);
-
-/* free jack instances manually when clearing/reconfiguring */
-void snd_hda_input_jack_free(struct hda_codec *codec)
-{
-       if (!codec->bus->shutdown && codec->jacks.list) {
-               struct hda_jack_item *jacks = codec->jacks.list;
-               int i;
-               for (i = 0; i < codec->jacks.used; i++, jacks++) {
-                       if (jacks->jack)
-                               snd_device_free(codec->bus->card, jacks->jack);
-               }
-       }
-       snd_array_free(&codec->jacks);
-}
-EXPORT_SYMBOL_HDA(snd_hda_input_jack_free);
-#endif /* CONFIG_SND_HDA_INPUT_JACK */
-
 MODULE_DESCRIPTION("HDA codec core");
 MODULE_LICENSE("GPL");