These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / sound / pci / hda / hda_generic.c
index 5bc7f2e..5c4fa8e 100644 (file)
@@ -771,9 +771,6 @@ static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir,
        unsigned int caps;
        unsigned int mask, val;
 
-       if (!enable && is_active_nid(codec, nid, dir, idx_to_check))
-               return;
-
        caps = query_amp_caps(codec, nid, dir);
        val = get_amp_val_to_activate(codec, nid, dir, caps, enable);
        mask = get_amp_mask_to_modify(codec, nid, dir, idx_to_check, caps);
@@ -784,12 +781,22 @@ static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir,
        update_amp(codec, nid, dir, idx, mask, val);
 }
 
+static void check_and_activate_amp(struct hda_codec *codec, hda_nid_t nid,
+                                  int dir, int idx, int idx_to_check,
+                                  bool enable)
+{
+       /* check whether the given amp is still used by others */
+       if (!enable && is_active_nid(codec, nid, dir, idx_to_check))
+               return;
+       activate_amp(codec, nid, dir, idx, idx_to_check, enable);
+}
+
 static void activate_amp_out(struct hda_codec *codec, struct nid_path *path,
                             int i, bool enable)
 {
        hda_nid_t nid = path->path[i];
        init_amp(codec, nid, HDA_OUTPUT, 0);
-       activate_amp(codec, nid, HDA_OUTPUT, 0, 0, enable);
+       check_and_activate_amp(codec, nid, HDA_OUTPUT, 0, 0, enable);
 }
 
 static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
@@ -817,9 +824,16 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
         * when aa-mixer is available, we need to enable the path as well
         */
        for (n = 0; n < nums; n++) {
-               if (n != idx && (!add_aamix || conn[n] != spec->mixer_merge_nid))
-                       continue;
-               activate_amp(codec, nid, HDA_INPUT, n, idx, enable);
+               if (n != idx) {
+                       if (conn[n] != spec->mixer_merge_nid)
+                               continue;
+                       /* when aamix is disabled, force to off */
+                       if (!add_aamix) {
+                               activate_amp(codec, nid, HDA_INPUT, n, n, false);
+                               continue;
+                       }
+               }
+               check_and_activate_amp(codec, nid, HDA_INPUT, n, idx, enable);
        }
 }
 
@@ -1580,6 +1594,12 @@ static bool map_singles(struct hda_codec *codec, int outs,
        return found;
 }
 
+static inline bool has_aamix_out_paths(struct hda_gen_spec *spec)
+{
+       return spec->aamix_out_paths[0] || spec->aamix_out_paths[1] ||
+               spec->aamix_out_paths[2];
+}
+
 /* create a new path including aamix if available, and return its index */
 static int check_aamix_out_path(struct hda_codec *codec, int path_idx)
 {
@@ -2422,25 +2442,51 @@ static void update_aamix_paths(struct hda_codec *codec, bool do_mix,
        }
 }
 
+/* re-initialize the output paths; only called from loopback_mixing_put() */
+static void update_output_paths(struct hda_codec *codec, int num_outs,
+                               const int *paths)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       struct nid_path *path;
+       int i;
+
+       for (i = 0; i < num_outs; i++) {
+               path = snd_hda_get_path_from_idx(codec, paths[i]);
+               if (path)
+                       snd_hda_activate_path(codec, path, path->active,
+                                             spec->aamix_mode);
+       }
+}
+
 static int loopback_mixing_put(struct snd_kcontrol *kcontrol,
                               struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct hda_gen_spec *spec = codec->spec;
+       const struct auto_pin_cfg *cfg = &spec->autocfg;
        unsigned int val = ucontrol->value.enumerated.item[0];
 
        if (val == spec->aamix_mode)
                return 0;
        spec->aamix_mode = val;
-       update_aamix_paths(codec, val, spec->out_paths[0],
-                          spec->aamix_out_paths[0],
-                          spec->autocfg.line_out_type);
-       update_aamix_paths(codec, val, spec->hp_paths[0],
-                          spec->aamix_out_paths[1],
-                          AUTO_PIN_HP_OUT);
-       update_aamix_paths(codec, val, spec->speaker_paths[0],
-                          spec->aamix_out_paths[2],
-                          AUTO_PIN_SPEAKER_OUT);
+       if (has_aamix_out_paths(spec)) {
+               update_aamix_paths(codec, val, spec->out_paths[0],
+                                  spec->aamix_out_paths[0],
+                                  cfg->line_out_type);
+               update_aamix_paths(codec, val, spec->hp_paths[0],
+                                  spec->aamix_out_paths[1],
+                                  AUTO_PIN_HP_OUT);
+               update_aamix_paths(codec, val, spec->speaker_paths[0],
+                                  spec->aamix_out_paths[2],
+                                  AUTO_PIN_SPEAKER_OUT);
+       } else {
+               update_output_paths(codec, cfg->line_outs, spec->out_paths);
+               if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+                       update_output_paths(codec, cfg->hp_outs, spec->hp_paths);
+               if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+                       update_output_paths(codec, cfg->speaker_outs,
+                                           spec->speaker_paths);
+       }
        return 1;
 }
 
@@ -2458,12 +2504,13 @@ static int create_loopback_mixing_ctl(struct hda_codec *codec)
 
        if (!spec->mixer_nid)
                return 0;
-       if (!(spec->aamix_out_paths[0] || spec->aamix_out_paths[1] ||
-             spec->aamix_out_paths[2]))
-               return 0;
        if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum))
                return -ENOMEM;
        spec->have_aamix_ctl = 1;
+       /* if no explicit aamix path is present (e.g. for Realtek codecs),
+        * enable aamix as default -- just for compatibility
+        */
+       spec->aamix_mode = !has_aamix_out_paths(spec);
        return 0;
 }
 
@@ -3998,9 +4045,9 @@ static void pin_power_callback(struct hda_codec *codec,
                               struct hda_jack_callback *jack,
                               bool on)
 {
-       if (jack && jack->tbl->nid)
+       if (jack && jack->nid)
                sync_power_state_change(codec,
-                                       set_pin_power_jack(codec, jack->tbl->nid, on));
+                                       set_pin_power_jack(codec, jack->nid, on));
 }
 
 /* callback only doing power up -- called at first */
@@ -5172,7 +5219,7 @@ static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo,
        int err = 0;
 
        mutex_lock(&spec->pcm_mutex);
-       if (!spec->indep_hp_enabled)
+       if (spec->indep_hp && !spec->indep_hp_enabled)
                err = -EBUSY;
        else
                spec->active_streams |= 1 << STREAM_INDEP_HP;
@@ -5664,6 +5711,8 @@ static void init_aamix_paths(struct hda_codec *codec)
 
        if (!spec->have_aamix_ctl)
                return;
+       if (!has_aamix_out_paths(spec))
+               return;
        update_aamix_paths(codec, spec->aamix_mode, spec->out_paths[0],
                           spec->aamix_out_paths[0],
                           spec->autocfg.line_out_type);
@@ -5877,13 +5926,14 @@ error:
        return err;
 }
 
-static const struct hda_codec_preset snd_hda_preset_generic[] = {
-       { .id = HDA_CODEC_ID_GENERIC, .patch = snd_hda_parse_generic_codec },
+static const struct hda_device_id snd_hda_id_generic[] = {
+       HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC, "Generic", snd_hda_parse_generic_codec),
        {} /* terminator */
 };
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_generic);
 
 static struct hda_codec_driver generic_driver = {
-       .preset = snd_hda_preset_generic,
+       .id = snd_hda_id_generic,
 };
 
 module_hda_codec_driver(generic_driver);