Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/sound/hda/codecs/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 164 kB image not shown  

Quelle  generic.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Universal Interface for Intel High Definition Audio Codec
 *
 * Generic widget tree parser
 *
 * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
 */


#include <linux/init.h>
#include <linux/slab.h>
#include <linux/export.h>
#include <linux/sort.h>
#include <linux/delay.h>
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/leds.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/tlv.h>
#include <sound/hda_codec.h>
#include "hda_local.h"
#include "hda_auto_parser.h"
#include "hda_jack.h"
#include "hda_beep.h"
#include "generic.h"


/**
 * snd_hda_gen_spec_init - initialize hda_gen_spec struct
 * @spec: hda_gen_spec object to initialize
 *
 * Initialize the given hda_gen_spec object.
 */

int snd_hda_gen_spec_init(struct hda_gen_spec *spec)
{
 snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
 snd_array_init(&spec->paths, sizeof(struct nid_path), 8);
 snd_array_init(&spec->loopback_list, sizeof(struct hda_amp_list), 8);
 mutex_init(&spec->pcm_mutex);
 return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_gen_spec_init);

/**
 * snd_hda_gen_add_kctl - Add a new kctl_new struct from the template
 * @spec: hda_gen_spec object
 * @name: name string to override the template, NULL if unchanged
 * @temp: template for the new kctl
 *
 * Add a new kctl (actually snd_kcontrol_new to be instantiated later)
 * element based on the given snd_kcontrol_new template @temp and the
 * name string @name to the list in @spec.
 * Returns the newly created object or NULL as error.
 */

struct snd_kcontrol_new *
snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name,
       const struct snd_kcontrol_new *temp)
{
 struct snd_kcontrol_new *knew = snd_array_new(&spec->kctls);
 if (!knew)
  return NULL;
 *knew = *temp;
 if (name)
  knew->name = kstrdup(name, GFP_KERNEL);
 else if (knew->name)
  knew->name = kstrdup(knew->name, GFP_KERNEL);
 if (!knew->name)
  return NULL;
 return knew;
}
EXPORT_SYMBOL_GPL(snd_hda_gen_add_kctl);

static void free_kctls(struct hda_gen_spec *spec)
{
 if (spec->kctls.list) {
  struct snd_kcontrol_new *kctl = spec->kctls.list;
  int i;
  for (i = 0; i < spec->kctls.used; i++)
   kfree(kctl[i].name);
 }
 snd_array_free(&spec->kctls);
}

static void snd_hda_gen_spec_free(struct hda_gen_spec *spec)
{
 if (!spec)
  return;
 free_kctls(spec);
 snd_array_free(&spec->paths);
 snd_array_free(&spec->loopback_list);
#ifdef CONFIG_SND_HDA_GENERIC_LEDS
 if (spec->led_cdevs[LED_AUDIO_MUTE])
  led_classdev_unregister(spec->led_cdevs[LED_AUDIO_MUTE]);
 if (spec->led_cdevs[LED_AUDIO_MICMUTE])
  led_classdev_unregister(spec->led_cdevs[LED_AUDIO_MICMUTE]);
#endif
}

/*
 * store user hints
 */

static void parse_user_hints(struct hda_codec *codec)
{
 struct hda_gen_spec *spec = codec->spec;
 int val;

 val = snd_hda_get_bool_hint(codec, "jack_detect");
 if (val >= 0)
  codec->no_jack_detect = !val;
 val = snd_hda_get_bool_hint(codec, "inv_jack_detect");
 if (val >= 0)
  codec->inv_jack_detect = !!val;
 val = snd_hda_get_bool_hint(codec, "trigger_sense");
 if (val >= 0)
  codec->no_trigger_sense = !val;
 val = snd_hda_get_bool_hint(codec, "inv_eapd");
 if (val >= 0)
  codec->inv_eapd = !!val;
 val = snd_hda_get_bool_hint(codec, "pcm_format_first");
 if (val >= 0)
  codec->pcm_format_first = !!val;
 val = snd_hda_get_bool_hint(codec, "sticky_stream");
 if (val >= 0)
  codec->no_sticky_stream = !val;
 val = snd_hda_get_bool_hint(codec, "spdif_status_reset");
 if (val >= 0)
  codec->spdif_status_reset = !!val;
 val = snd_hda_get_bool_hint(codec, "pin_amp_workaround");
 if (val >= 0)
  codec->pin_amp_workaround = !!val;
 val = snd_hda_get_bool_hint(codec, "single_adc_amp");
 if (val >= 0)
  codec->single_adc_amp = !!val;
 val = snd_hda_get_bool_hint(codec, "power_save_node");
 if (val >= 0)
  codec->power_save_node = !!val;

 val = snd_hda_get_bool_hint(codec, "auto_mute");
 if (val >= 0)
  spec->suppress_auto_mute = !val;
 val = snd_hda_get_bool_hint(codec, "auto_mic");
 if (val >= 0)
  spec->suppress_auto_mic = !val;
 val = snd_hda_get_bool_hint(codec, "line_in_auto_switch");
 if (val >= 0)
  spec->line_in_auto_switch = !!val;
 val = snd_hda_get_bool_hint(codec, "auto_mute_via_amp");
 if (val >= 0)
  spec->auto_mute_via_amp = !!val;
 val = snd_hda_get_bool_hint(codec, "need_dac_fix");
 if (val >= 0)
  spec->need_dac_fix = !!val;
 val = snd_hda_get_bool_hint(codec, "primary_hp");
 if (val >= 0)
  spec->no_primary_hp = !val;
 val = snd_hda_get_bool_hint(codec, "multi_io");
 if (val >= 0)
  spec->no_multi_io = !val;
 val = snd_hda_get_bool_hint(codec, "multi_cap_vol");
 if (val >= 0)
  spec->multi_cap_vol = !!val;
 val = snd_hda_get_bool_hint(codec, "inv_dmic_split");
 if (val >= 0)
  spec->inv_dmic_split = !!val;
 val = snd_hda_get_bool_hint(codec, "indep_hp");
 if (val >= 0)
  spec->indep_hp = !!val;
 val = snd_hda_get_bool_hint(codec, "add_stereo_mix_input");
 if (val >= 0)
  spec->add_stereo_mix_input = !!val;
 /* the following two are just for compatibility */
 val = snd_hda_get_bool_hint(codec, "add_out_jack_modes");
 if (val >= 0)
  spec->add_jack_modes = !!val;
 val = snd_hda_get_bool_hint(codec, "add_in_jack_modes");
 if (val >= 0)
  spec->add_jack_modes = !!val;
 val = snd_hda_get_bool_hint(codec, "add_jack_modes");
 if (val >= 0)
  spec->add_jack_modes = !!val;
 val = snd_hda_get_bool_hint(codec, "power_down_unused");
 if (val >= 0)
  spec->power_down_unused = !!val;
 val = snd_hda_get_bool_hint(codec, "add_hp_mic");
 if (val >= 0)
  spec->hp_mic = !!val;
 val = snd_hda_get_bool_hint(codec, "hp_mic_detect");
 if (val >= 0)
  spec->suppress_hp_mic_detect = !val;
 val = snd_hda_get_bool_hint(codec, "vmaster");
 if (val >= 0)
  spec->suppress_vmaster = !val;

 if (!snd_hda_get_int_hint(codec, "mixer_nid", &val))
  spec->mixer_nid = val;
}

/*
 * pin control value accesses
 */


#define update_pin_ctl(codec, pin, val) \
 snd_hda_codec_write_cache(codec, pin, 0, \
       AC_VERB_SET_PIN_WIDGET_CONTROL, val)

/* restore the pinctl based on the cached value */
static inline void restore_pin_ctl(struct hda_codec *codec, hda_nid_t pin)
{
 update_pin_ctl(codec, pin, snd_hda_codec_get_pin_target(codec, pin));
}

/* set the pinctl target value and write it if requested */
static void set_pin_target(struct hda_codec *codec, hda_nid_t pin,
      unsigned int val, bool do_write)
{
 if (!pin)
  return;
 val = snd_hda_correct_pin_ctl(codec, pin, val);
 snd_hda_codec_set_pin_target(codec, pin, val);
 if (do_write)
  update_pin_ctl(codec, pin, val);
}

/* set pinctl target values for all given pins */
static void set_pin_targets(struct hda_codec *codec, int num_pins,
       hda_nid_t *pins, unsigned int val)
{
 int i;
 for (i = 0; i < num_pins; i++)
  set_pin_target(codec, pins[i], val, false);
}

/*
 * parsing paths
 */


/* 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;
}

/* return true if the given NID is contained in the path */
static bool is_nid_contained(struct nid_path *path, hda_nid_t nid)
{
 return find_idx_in_nid_list(nid, path->path, path->depth) >= 0;
}

static struct nid_path *get_nid_path(struct hda_codec *codec,
         hda_nid_t from_nid, hda_nid_t to_nid,
         int anchor_nid)
{
 struct hda_gen_spec *spec = codec->spec;
 struct nid_path *path;
 int i;

 snd_array_for_each(&spec->paths, i, path) {
  if (path->depth <= 0)
   continue;
  if ((!from_nid || path->path[0] == from_nid) &&
      (!to_nid || path->path[path->depth - 1] == to_nid)) {
   if (!anchor_nid ||
       (anchor_nid > 0 && is_nid_contained(path, anchor_nid)) ||
       (anchor_nid < 0 && !is_nid_contained(path, anchor_nid)))
    return path;
  }
 }
 return NULL;
}

/**
 * snd_hda_get_path_idx - get the index number corresponding to the path
 * instance
 * @codec: the HDA codec
 * @path: nid_path object
 *
 * The returned index starts from 1, i.e. the actual array index with offset 1,
 * and zero is handled as an invalid path
 */

int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path)
{
 struct hda_gen_spec *spec = codec->spec;
 struct nid_path *array = spec->paths.list;
 ssize_t idx;

 if (!spec->paths.used)
  return 0;
 idx = path - array;
 if (idx < 0 || idx >= spec->paths.used)
  return 0;
 return idx + 1;
}
EXPORT_SYMBOL_GPL(snd_hda_get_path_idx);

/**
 * snd_hda_get_path_from_idx - get the path instance corresponding to the
 * given index number
 * @codec: the HDA codec
 * @idx: the path index
 */

struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx)
{
 struct hda_gen_spec *spec = codec->spec;

 if (idx <= 0 || idx > spec->paths.used)
  return NULL;
 return snd_array_elem(&spec->paths, idx - 1);
}
EXPORT_SYMBOL_GPL(snd_hda_get_path_from_idx);

/* check whether the given DAC is already found in any existing paths */
static bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid)
{
 struct hda_gen_spec *spec = codec->spec;
 const struct nid_path *path;
 int i;

 snd_array_for_each(&spec->paths, i, path) {
  if (path->path[0] == nid)
   return true;
 }
 return false;
}

/* check whether the given two widgets can be connected */
static bool is_reachable_path(struct hda_codec *codec,
         hda_nid_t from_nid, hda_nid_t to_nid)
{
 if (!from_nid || !to_nid)
  return false;
 return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0;
}

/* nid, dir and idx */
#define AMP_VAL_COMPARE_MASK (0xffff | (1U << 18) | (0x0f << 19))

/* check whether the given ctl is already assigned in any path elements */
static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type)
{
 struct hda_gen_spec *spec = codec->spec;
 const struct nid_path *path;
 int i;

 val &= AMP_VAL_COMPARE_MASK;
 snd_array_for_each(&spec->paths, i, path) {
  if ((path->ctls[type] & AMP_VAL_COMPARE_MASK) == val)
   return true;
 }
 return false;
}

/* check whether a control with the given (nid, dir, idx) was assigned */
static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid,
         int dir, int idx, int type)
{
 unsigned int val = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir);
 return is_ctl_used(codec, val, type);
}

static void print_nid_path(struct hda_codec *codec,
      const char *pfx, struct nid_path *path)
{
 char buf[40];
 char *pos = buf;
 int i;

 *pos = 0;
 for (i = 0; i < path->depth; i++)
  pos += scnprintf(pos, sizeof(buf) - (pos - buf), "%s%02x",
     pos != buf ? ":" : "",
     path->path[i]);

 codec_dbg(codec, "%s path: depth=%d '%s'\n", pfx, path->depth, buf);
}

/* called recursively */
static bool __parse_nid_path(struct hda_codec *codec,
        hda_nid_t from_nid, hda_nid_t to_nid,
        int anchor_nid, struct nid_path *path,
        int depth)
{
 const hda_nid_t *conn;
 int i, nums;

 if (to_nid == anchor_nid)
  anchor_nid = 0; /* anchor passed */
 else if (to_nid == (hda_nid_t)(-anchor_nid))
  return false/* hit the exclusive nid */

 nums = snd_hda_get_conn_list(codec, to_nid, &conn);
 for (i = 0; i < nums; i++) {
  if (conn[i] != from_nid) {
   /* special case: when from_nid is 0,
 * try to find an empty DAC
 */

   if (from_nid ||
       get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT ||
       is_dac_already_used(codec, conn[i]))
    continue;
  }
  /* anchor is not requested or already passed? */
  if (anchor_nid <= 0)
   goto found;
 }
 if (depth >= MAX_NID_PATH_DEPTH)
  return false;
 for (i = 0; i < nums; i++) {
  unsigned int type;
  type = get_wcaps_type(get_wcaps(codec, conn[i]));
  if (type == AC_WID_AUD_OUT || type == AC_WID_AUD_IN ||
      type == AC_WID_PIN)
   continue;
  if (__parse_nid_path(codec, from_nid, conn[i],
         anchor_nid, path, depth + 1))
   goto found;
 }
 return false;

 found:
 path->path[path->depth] = conn[i];
 path->idx[path->depth + 1] = i;
 if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX)
  path->multi[path->depth + 1] = 1;
 path->depth++;
 return true;
}

/*
 * snd_hda_parse_nid_path - parse the widget path from the given nid to
 * the target nid
 * @codec: the HDA codec
 * @from_nid: the NID where the path start from
 * @to_nid: the NID where the path ends at
 * @anchor_nid: the anchor indication
 * @path: the path object to store the result
 *
 * Returns true if a matching path is found.
 *
 * The parsing behavior depends on parameters:
 * when @from_nid is 0, try to find an empty DAC;
 * when @anchor_nid is set to a positive value, only paths through the widget
 * with the given value are evaluated.
 * when @anchor_nid is set to a negative value, paths through the widget
 * with the negative of given value are excluded, only other paths are chosen.
 * when @anchor_nid is zero, no special handling about path selection.
 */

static bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
       hda_nid_t to_nid, int anchor_nid,
       struct nid_path *path)
{
 if (__parse_nid_path(codec, from_nid, to_nid, anchor_nid, path, 1)) {
  path->path[path->depth] = to_nid;
  path->depth++;
  return true;
 }
 return false;
}

/**
 * snd_hda_add_new_path - parse the path between the given NIDs and
 * add to the path list
 * @codec: the HDA codec
 * @from_nid: the NID where the path start from
 * @to_nid: the NID where the path ends at
 * @anchor_nid: the anchor indication, see snd_hda_parse_nid_path()
 *
 * If no valid path is found, returns NULL.
 */

struct nid_path *
snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid,
       hda_nid_t to_nid, int anchor_nid)
{
 struct hda_gen_spec *spec = codec->spec;
 struct nid_path *path;

 if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid))
  return NULL;

 /* check whether the path has been already added */
 path = get_nid_path(codec, from_nid, to_nid, anchor_nid);
 if (path)
  return path;

 path = snd_array_new(&spec->paths);
 if (!path)
  return NULL;
 memset(path, 0, sizeof(*path));
 if (snd_hda_parse_nid_path(codec, from_nid, to_nid, anchor_nid, path))
  return path;
 /* push back */
 spec->paths.used--;
 return NULL;
}
EXPORT_SYMBOL_GPL(snd_hda_add_new_path);

/* clear the given path as invalid so that it won't be picked up later */
static void invalidate_nid_path(struct hda_codec *codec, int idx)
{
 struct nid_path *path = snd_hda_get_path_from_idx(codec, idx);
 if (!path)
  return;
 memset(path, 0, sizeof(*path));
}

/* return a DAC if paired to the given pin by codec driver */
static hda_nid_t get_preferred_dac(struct hda_codec *codec, hda_nid_t pin)
{
 struct hda_gen_spec *spec = codec->spec;
 const hda_nid_t *list = spec->preferred_dacs;

 if (!list)
  return 0;
 for (; *list; list += 2)
  if (*list == pin)
   return list[1];
 return 0;
}

/* look for an empty DAC slot */
static hda_nid_t look_for_dac(struct hda_codec *codec, hda_nid_t pin,
         bool is_digital)
{
 struct hda_gen_spec *spec = codec->spec;
 bool cap_digital;
 int i;

 for (i = 0; i < spec->num_all_dacs; i++) {
  hda_nid_t nid = spec->all_dacs[i];
  if (!nid || is_dac_already_used(codec, nid))
   continue;
  cap_digital = !!(get_wcaps(codec, nid) & AC_WCAP_DIGITAL);
  if (is_digital != cap_digital)
   continue;
  if (is_reachable_path(codec, nid, pin))
   return nid;
 }
 return 0;
}

/* replace the channels in the composed amp value with the given number */
static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs)
{
 val &= ~(0x3U << 16);
 val |= chs << 16;
 return val;
}

static bool same_amp_caps(struct hda_codec *codec, hda_nid_t nid1,
     hda_nid_t nid2, int dir)
{
 if (!(get_wcaps(codec, nid1) & (1 << (dir + 1))))
  return !(get_wcaps(codec, nid2) & (1 << (dir + 1)));
 return (query_amp_caps(codec, nid1, dir) ==
  query_amp_caps(codec, nid2, dir));
}

/* look for a widget suitable for assigning a mute switch in the path */
static hda_nid_t look_for_out_mute_nid(struct hda_codec *codec,
           struct nid_path *path)
{
 int i;

 for (i = path->depth - 1; i >= 0; i--) {
  if (nid_has_mute(codec, path->path[i], HDA_OUTPUT))
   return path->path[i];
  if (i != path->depth - 1 && i != 0 &&
      nid_has_mute(codec, path->path[i], HDA_INPUT))
   return path->path[i];
 }
 return 0;
}

/* look for a widget suitable for assigning a volume ctl in the path */
static hda_nid_t look_for_out_vol_nid(struct hda_codec *codec,
          struct nid_path *path)
{
 struct hda_gen_spec *spec = codec->spec;
 int i;

 for (i = path->depth - 1; i >= 0; i--) {
  hda_nid_t nid = path->path[i];
  if ((spec->out_vol_mask >> nid) & 1)
   continue;
  if (nid_has_volume(codec, nid, HDA_OUTPUT))
   return nid;
 }
 return 0;
}

/*
 * path activation / deactivation
 */


/* can have the amp-in capability? */
static bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx)
{
 hda_nid_t nid = path->path[idx];
 unsigned int caps = get_wcaps(codec, nid);
 unsigned int type = get_wcaps_type(caps);

 if (!(caps & AC_WCAP_IN_AMP))
  return false;
 if (type == AC_WID_PIN && idx > 0) /* only for input pins */
  return false;
 return true;
}

/* can have the amp-out capability? */
static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx)
{
 hda_nid_t nid = path->path[idx];
 unsigned int caps = get_wcaps(codec, nid);
 unsigned int type = get_wcaps_type(caps);

 if (!(caps & AC_WCAP_OUT_AMP))
  return false;
 if (type == AC_WID_PIN && !idx) /* only for output pins */
  return false;
 return true;
}

/* check whether the given (nid,dir,idx) is active */
static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid,
     unsigned int dir, unsigned int idx)
{
 struct hda_gen_spec *spec = codec->spec;
 int type = get_wcaps_type(get_wcaps(codec, nid));
 const struct nid_path *path;
 int i, n;

 if (nid == codec->core.afg)
  return true;

 snd_array_for_each(&spec->paths, n, path) {
  if (!path->active)
   continue;
  if (codec->power_save_node) {
   if (!path->stream_enabled)
    continue;
   /* ignore unplugged paths except for DAC/ADC */
   if (!(path->pin_enabled || path->pin_fixed) &&
       type != AC_WID_AUD_OUT && type != AC_WID_AUD_IN)
    continue;
  }
  for (i = 0; i < path->depth; i++) {
   if (path->path[i] == nid) {
    if (dir == HDA_OUTPUT || idx == -1 ||
        path->idx[i] == idx)
     return true;
    break;
   }
  }
 }
 return false;
}

/* check whether the NID is referred by any active paths */
#define is_active_nid_for_any(codec, nid) \
 is_active_nid(codec, nid, HDA_OUTPUT, -1)

/* get the default amp value for the target state */
static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid,
       int dir, unsigned int caps, bool enable)
{
 unsigned int val = 0;

 if (caps & AC_AMPCAP_NUM_STEPS) {
  /* set to 0dB */
  if (enable)
   val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
 }
 if (caps & (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) {
  if (!enable)
   val |= HDA_AMP_MUTE;
 }
 return val;
}

/* is this a stereo widget or a stereo-to-mono mix? */
static bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid, int dir)
{
 unsigned int wcaps = get_wcaps(codec, nid);
 hda_nid_t conn;

 if (wcaps & AC_WCAP_STEREO)
  return true;
 if (dir != HDA_INPUT || get_wcaps_type(wcaps) != AC_WID_AUD_MIX)
  return false;
 if (snd_hda_get_num_conns(codec, nid) != 1)
  return false;
 if (snd_hda_get_connections(codec, nid, &conn, 1) < 0)
  return false;
 return !!(get_wcaps(codec, conn) & AC_WCAP_STEREO);
}

/* initialize the amp value (only at the first time) */
static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx)
{
 unsigned int caps = query_amp_caps(codec, nid, dir);
 int val = get_amp_val_to_activate(codec, nid, dir, caps, false);

 if (is_stereo_amps(codec, nid, dir))
  snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val);
 else
  snd_hda_codec_amp_init(codec, nid, 0, dir, idx, 0xff, val);
}

/* update the amp, doing in stereo or mono depending on NID */
static int update_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx,
        unsigned int mask, unsigned int val)
{
 if (is_stereo_amps(codec, nid, dir))
  return snd_hda_codec_amp_stereo(codec, nid, dir, idx,
      mask, val);
 else
  return snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
      mask, val);
}

/* calculate amp value mask we can modify;
 * if the given amp is controlled by mixers, don't touch it
 */

static unsigned int get_amp_mask_to_modify(struct hda_codec *codec,
        hda_nid_t nid, int dir, int idx,
        unsigned int caps)
{
 unsigned int mask = 0xff;

 if (caps & (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) {
  if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_MUTE_CTL))
   mask &= ~0x80;
 }
 if (caps & AC_AMPCAP_NUM_STEPS) {
  if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) ||
      is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL))
   mask &= ~0x7f;
 }
 return mask;
}

static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir,
    int idx, int idx_to_check, bool enable)
{
 unsigned int caps;
 unsigned int mask, val;

 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);
 if (!mask)
  return;

 val &= mask;
 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);
 check_and_activate_amp(codec, nid, HDA_OUTPUT, 0, 0, enable);
}

static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
       int i, bool enable, bool add_aamix)
{
 struct hda_gen_spec *spec = codec->spec;
 const hda_nid_t *conn;
 int n, nums, idx;
 int type;
 hda_nid_t nid = path->path[i];

 nums = snd_hda_get_conn_list(codec, nid, &conn);
 if (nums < 0)
  return;
 type = get_wcaps_type(get_wcaps(codec, nid));
 if (type == AC_WID_PIN ||
     (type == AC_WID_AUD_IN && codec->single_adc_amp)) {
  nums = 1;
  idx = 0;
 } else
  idx = path->idx[i];

 for (n = 0; n < nums; n++)
  init_amp(codec, nid, HDA_INPUT, n);

 /* here is a little bit tricky in comparison with activate_amp_out();
 * when aa-mixer is available, we need to enable the path as well
 */

 for (n = 0; n < nums; n++) {
  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);
 }
}

/* sync power of each widget in the given path */
static hda_nid_t path_power_update(struct hda_codec *codec,
       struct nid_path *path,
       bool allow_powerdown)
{
 hda_nid_t nid, changed = 0;
 int i, state, power;

 for (i = 0; i < path->depth; i++) {
  nid = path->path[i];
  if (!(get_wcaps(codec, nid) & AC_WCAP_POWER))
   continue;
  if (nid == codec->core.afg)
   continue;
  if (!allow_powerdown || is_active_nid_for_any(codec, nid))
   state = AC_PWRST_D0;
  else
   state = AC_PWRST_D3;
  power = snd_hda_codec_read(codec, nid, 0,
        AC_VERB_GET_POWER_STATE, 0);
  if (power != (state | (state << 4))) {
   snd_hda_codec_write(codec, nid, 0,
         AC_VERB_SET_POWER_STATE, state);
   changed = nid;
   /* all known codecs seem to be capable to handl
 * widgets state even in D3, so far.
 * if any new codecs need to restore the widget
 * states after D0 transition, call the function
 * below.
 */

#if 0 /* disabled */
   if (state == AC_PWRST_D0)
    snd_hdac_regmap_sync_node(&codec->core, nid);
#endif
  }
 }
 return changed;
}

/* do sync with the last power state change */
static void sync_power_state_change(struct hda_codec *codec, hda_nid_t nid)
{
 if (nid) {
  msleep(10);
  snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0);
 }
}

/**
 * snd_hda_activate_path - activate or deactivate the given path
 * @codec: the HDA codec
 * @path: the path to activate/deactivate
 * @enable: flag to activate or not
 * @add_aamix: enable the input from aamix NID
 *
 * If @add_aamix is set, enable the input from aa-mix NID as well (if any).
 */

void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
      bool enable, bool add_aamix)
{
 struct hda_gen_spec *spec = codec->spec;
 int i;

 path->active = enable;

 /* make sure the widget is powered up */
 if (enable && (spec->power_down_unused || codec->power_save_node))
  path_power_update(codec, path, codec->power_save_node);

 for (i = path->depth - 1; i >= 0; i--) {
  hda_nid_t nid = path->path[i];

  if (enable && path->multi[i])
   snd_hda_codec_write_cache(codec, nid, 0,
         AC_VERB_SET_CONNECT_SEL,
         path->idx[i]);
  if (has_amp_in(codec, path, i))
   activate_amp_in(codec, path, i, enable, add_aamix);
  if (has_amp_out(codec, path, i))
   activate_amp_out(codec, path, i, enable);
 }
}
EXPORT_SYMBOL_GPL(snd_hda_activate_path);

/* if the given path is inactive, put widgets into D3 (only if suitable) */
static void path_power_down_sync(struct hda_codec *codec, struct nid_path *path)
{
 struct hda_gen_spec *spec = codec->spec;

 if (!(spec->power_down_unused || codec->power_save_node) || path->active)
  return;
 sync_power_state_change(codec, path_power_update(codec, path, true));
}

/* turn on/off EAPD on the given pin */
static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable)
{
 struct hda_gen_spec *spec = codec->spec;
 if (spec->own_eapd_ctl ||
     !(snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD))
  return;
 if (spec->keep_eapd_on && !enable)
  return;
 if (codec->inv_eapd)
  enable = !enable;
 snd_hda_codec_write_cache(codec, pin, 0,
       AC_VERB_SET_EAPD_BTLENABLE,
       enable ? 0x02 : 0x00);
}

/* re-initialize the path specified by the given path index */
static void resume_path_from_idx(struct hda_codec *codec, int path_idx)
{
 struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx);
 if (path)
  snd_hda_activate_path(codec, path, path->active, false);
}


/*
 * Helper functions for creating mixer ctl elements
 */


static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
      struct snd_ctl_elem_value *ucontrol);
static int hda_gen_bind_mute_get(struct snd_kcontrol *kcontrol,
     struct snd_ctl_elem_value *ucontrol);
static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol,
     struct snd_ctl_elem_value *ucontrol);

enum {
 HDA_CTL_WIDGET_VOL,
 HDA_CTL_WIDGET_MUTE,
 HDA_CTL_BIND_MUTE,
};
static const struct snd_kcontrol_new control_templates[] = {
 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
 /* only the put callback is replaced for handling the special mute */
 {
  .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  .subdevice = HDA_SUBDEV_AMP_FLAG,
  .info = snd_hda_mixer_amp_switch_info,
  .get = snd_hda_mixer_amp_switch_get,
  .put = hda_gen_mixer_mute_put, /* replaced */
  .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0),
 },
 {
  .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  .info = snd_hda_mixer_amp_switch_info,
  .get = hda_gen_bind_mute_get,
  .put = hda_gen_bind_mute_put, /* replaced */
  .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0),
 },
};

/* add dynamic controls from template */
static struct snd_kcontrol_new *
add_control(struct hda_gen_spec *spec, int type, const char *name,
         int cidx, unsigned long val)
{
 struct snd_kcontrol_new *knew;

 knew = snd_hda_gen_add_kctl(spec, name, &control_templates[type]);
 if (!knew)
  return NULL;
 knew->index = cidx;
 if (get_amp_nid_(val))
  knew->subdevice = HDA_SUBDEV_AMP_FLAG;
 if (knew->access == 0)
  knew->access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
 knew->private_value = val;
 return knew;
}

static int add_control_with_pfx(struct hda_gen_spec *spec, int type,
    const char *pfx, const char *dir,
    const char *sfx, int cidx, unsigned long val)
{
 char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
 int len;

 len = snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
 if (snd_BUG_ON(len >= sizeof(name)))
  return -EINVAL;
 if (!add_control(spec, type, name, cidx, val))
  return -ENOMEM;
 return 0;
}

#define add_pb_vol_ctrl(spec, type, pfx, val)   \
 add_control_with_pfx(spec, type, pfx, "Playback""Volume", 0, val)
#define add_pb_sw_ctrl(spec, type, pfx, val)   \
 add_control_with_pfx(spec, type, pfx, "Playback""Switch", 0, val)
#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val)   \
 add_control_with_pfx(spec, type, pfx, "Playback""Volume", cidx, val)
#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val)   \
 add_control_with_pfx(spec, type, pfx, "Playback""Switch", cidx, val)

static int add_vol_ctl(struct hda_codec *codec, const char *pfx, int cidx,
         unsigned int chs, struct nid_path *path)
{
 unsigned int val;
 if (!path)
  return 0;
 val = path->ctls[NID_PATH_VOL_CTL];
 if (!val)
  return 0;
 val = amp_val_replace_channels(val, chs);
 return __add_pb_vol_ctrl(codec->spec, HDA_CTL_WIDGET_VOL, pfx, cidx, val);
}

/* return the channel bits suitable for the given path->ctls[] */
static int get_default_ch_nums(struct hda_codec *codec, struct nid_path *path,
          int type)
{
 int chs = 1; /* mono (left only) */
 if (path) {
  hda_nid_t nid = get_amp_nid_(path->ctls[type]);
  if (nid && (get_wcaps(codec, nid) & AC_WCAP_STEREO))
   chs = 3; /* stereo */
 }
 return chs;
}

static int add_stereo_vol(struct hda_codec *codec, const char *pfx, int cidx,
     struct nid_path *path)
{
 int chs = get_default_ch_nums(codec, path, NID_PATH_VOL_CTL);
 return add_vol_ctl(codec, pfx, cidx, chs, path);
}

/* create a mute-switch for the given mixer widget;
 * if it has multiple sources (e.g. DAC and loopback), create a bind-mute
 */

static int add_sw_ctl(struct hda_codec *codec, const char *pfx, int cidx,
        unsigned int chs, struct nid_path *path)
{
 unsigned int val;
 int type = HDA_CTL_WIDGET_MUTE;

 if (!path)
  return 0;
 val = path->ctls[NID_PATH_MUTE_CTL];
 if (!val)
  return 0;
 val = amp_val_replace_channels(val, chs);
 if (get_amp_direction_(val) == HDA_INPUT) {
  hda_nid_t nid = get_amp_nid_(val);
  int nums = snd_hda_get_num_conns(codec, nid);
  if (nums > 1) {
   type = HDA_CTL_BIND_MUTE;
   val |= nums << 19;
  }
 }
 return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val);
}

static int add_stereo_sw(struct hda_codec *codec, const char *pfx,
      int cidx, struct nid_path *path)
{
 int chs = get_default_ch_nums(codec, path, NID_PATH_MUTE_CTL);
 return add_sw_ctl(codec, pfx, cidx, chs, path);
}

/* playback mute control with the software mute bit check */
static void sync_auto_mute_bits(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;

 if (spec->auto_mute_via_amp) {
  hda_nid_t nid = get_amp_nid(kcontrol);
  bool enabled = !((spec->mute_bits >> nid) & 1);
  ucontrol->value.integer.value[0] &= enabled;
  ucontrol->value.integer.value[1] &= enabled;
 }
}

static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
      struct snd_ctl_elem_value *ucontrol)
{
 sync_auto_mute_bits(kcontrol, ucontrol);
 return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
}

/*
 * Bound mute controls
 */

#define AMP_VAL_IDX_SHIFT 19
#define AMP_VAL_IDX_MASK (0x0f<<19)

static int hda_gen_bind_mute_get(struct snd_kcontrol *kcontrol,
     struct snd_ctl_elem_value *ucontrol)
{
 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 unsigned long pval;
 int err;

 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->control_mutex);
 return err;
}

static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol,
     struct snd_ctl_elem_value *ucontrol)
{
 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 unsigned long pval;
 int i, indices, err = 0, change = 0;

 sync_auto_mute_bits(kcontrol, ucontrol);

 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++) {
  kcontrol->private_value = (pval & ~AMP_VAL_IDX_MASK) |
   (i << AMP_VAL_IDX_SHIFT);
  err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
  if (err < 0)
   break;
  change |= err;
 }
 kcontrol->private_value = pval;
 mutex_unlock(&codec->control_mutex);
 return err < 0 ? err : change;
}

/* any ctl assigned to the path with the given index? */
static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type)
{
 struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx);
 return path && path->ctls[ctl_type];
}

static const char * const channel_name[] = {
 "Front""Surround""CLFE""Side""Back",
};

/* give some appropriate ctl name prefix for the given line out channel */
static const char *get_line_out_pfx(struct hda_codec *codec, int ch,
        int *index, int ctl_type)
{
 struct hda_gen_spec *spec = codec->spec;
 struct auto_pin_cfg *cfg = &spec->autocfg;

 *index = 0;
 if (cfg->line_outs == 1 && !spec->multi_ios &&
     !codec->force_pin_prefix &&
     !cfg->hp_outs && !cfg->speaker_outs)
  return spec->vmaster_mute.hook ? "PCM" : "Master";

 /* if there is really a single DAC used in the whole output paths,
 * use it master (or "PCM" if a vmaster hook is present)
 */

 if (spec->multiout.num_dacs == 1 && !spec->mixer_nid &&
     !codec->force_pin_prefix &&
     !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0])
  return spec->vmaster_mute.hook ? "PCM" : "Master";

 /* multi-io channels */
 if (ch >= cfg->line_outs)
  goto fixed_name;

 switch (cfg->line_out_type) {
 case AUTO_PIN_SPEAKER_OUT:
  /* if the primary channel vol/mute is shared with HP volume,
 * don't name it as Speaker
 */

  if (!ch && cfg->hp_outs &&
      !path_has_mixer(codec, spec->hp_paths[0], ctl_type))
   break;
  if (cfg->line_outs == 1)
   return "Speaker";
  if (cfg->line_outs == 2)
   return ch ? "Bass Speaker" : "Speaker";
  break;
 case AUTO_PIN_HP_OUT:
  /* if the primary channel vol/mute is shared with spk volume,
 * don't name it as Headphone
 */

  if (!ch && cfg->speaker_outs &&
      !path_has_mixer(codec, spec->speaker_paths[0], ctl_type))
   break;
  /* for multi-io case, only the primary out */
  if (ch && spec->multi_ios)
   break;
  *index = ch;
  return "Headphone";
 case AUTO_PIN_LINE_OUT:
  /* This deals with the case where one HP or one Speaker or
 * one HP + one Speaker need to share the DAC with LO
 */

  if (!ch) {
   bool hp_lo_shared = false, spk_lo_shared = false;

   if (cfg->speaker_outs)
    spk_lo_shared = !path_has_mixer(codec,
        spec->speaker_paths[0], ctl_type);
   if (cfg->hp_outs)
    hp_lo_shared = !path_has_mixer(codec, spec->hp_paths[0], ctl_type);
   if (hp_lo_shared && spk_lo_shared)
    return spec->vmaster_mute.hook ? "PCM" : "Master";
   if (hp_lo_shared)
    return "Headphone+LO";
   if (spk_lo_shared)
    return "Speaker+LO";
  }
 }

 /* for a single channel output, we don't have to name the channel */
 if (cfg->line_outs == 1 && !spec->multi_ios)
  return "Line Out";

 fixed_name:
 if (ch >= ARRAY_SIZE(channel_name)) {
  snd_BUG();
  return "PCM";
 }

 return channel_name[ch];
}

/*
 * Parse output paths
 */


/* badness definition */
enum {
 /* No primary DAC is found for the main output */
 BAD_NO_PRIMARY_DAC = 0x10000,
 /* No DAC is found for the extra output */
 BAD_NO_DAC = 0x4000,
 /* No possible multi-ios */
 BAD_MULTI_IO = 0x120,
 /* No individual DAC for extra output */
 BAD_NO_EXTRA_DAC = 0x102,
 /* No individual DAC for extra surrounds */
 BAD_NO_EXTRA_SURR_DAC = 0x101,
 /* Primary DAC shared with main surrounds */
 BAD_SHARED_SURROUND = 0x100,
 /* No independent HP possible */
 BAD_NO_INDEP_HP = 0x10,
 /* Primary DAC shared with main CLFE */
 BAD_SHARED_CLFE = 0x10,
 /* Primary DAC shared with extra surrounds */
 BAD_SHARED_EXTRA_SURROUND = 0x10,
 /* Volume widget is shared */
 BAD_SHARED_VOL = 0x10,
};

/* look for widgets in the given path which are appropriate for
 * volume and mute controls, and assign the values to ctls[].
 *
 * When no appropriate widget is found in the path, the badness value
 * is incremented depending on the situation.  The function returns the
 * total badness for both volume and mute controls.
 */

static int assign_out_path_ctls(struct hda_codec *codec, struct nid_path *path)
{
 struct hda_gen_spec *spec = codec->spec;
 hda_nid_t nid;
 unsigned int val;
 int badness = 0;

 if (!path)
  return BAD_SHARED_VOL * 2;

 if (path->ctls[NID_PATH_VOL_CTL] ||
     path->ctls[NID_PATH_MUTE_CTL])
  return 0; /* already evaluated */

 nid = look_for_out_vol_nid(codec, path);
 if (nid) {
  val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
  if (spec->dac_min_mute)
   val |= HDA_AMP_VAL_MIN_MUTE;
  if (is_ctl_used(codec, val, NID_PATH_VOL_CTL))
   badness += BAD_SHARED_VOL;
  else
   path->ctls[NID_PATH_VOL_CTL] = val;
 } else
  badness += BAD_SHARED_VOL;
 nid = look_for_out_mute_nid(codec, path);
 if (nid) {
  unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid));
  if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT ||
      nid_has_mute(codec, nid, HDA_OUTPUT))
   val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
  else
   val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT);
  if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL))
   badness += BAD_SHARED_VOL;
  else
   path->ctls[NID_PATH_MUTE_CTL] = val;
 } else
  badness += BAD_SHARED_VOL;
 return badness;
}

const struct badness_table hda_main_out_badness = {
 .no_primary_dac = BAD_NO_PRIMARY_DAC,
 .no_dac = BAD_NO_DAC,
 .shared_primary = BAD_NO_PRIMARY_DAC,
 .shared_surr = BAD_SHARED_SURROUND,
 .shared_clfe = BAD_SHARED_CLFE,
 .shared_surr_main = BAD_SHARED_SURROUND,
};
EXPORT_SYMBOL_GPL(hda_main_out_badness);

const struct badness_table hda_extra_out_badness = {
 .no_primary_dac = BAD_NO_DAC,
 .no_dac = BAD_NO_DAC,
 .shared_primary = BAD_NO_EXTRA_DAC,
 .shared_surr = BAD_SHARED_EXTRA_SURROUND,
 .shared_clfe = BAD_SHARED_EXTRA_SURROUND,
 .shared_surr_main = BAD_NO_EXTRA_SURR_DAC,
};
EXPORT_SYMBOL_GPL(hda_extra_out_badness);

/* get the DAC of the primary output corresponding to the given array index */
static hda_nid_t get_primary_out(struct hda_codec *codec, int idx)
{
 struct hda_gen_spec *spec = codec->spec;
 struct auto_pin_cfg *cfg = &spec->autocfg;

 if (cfg->line_outs > idx)
  return spec->private_dac_nids[idx];
 idx -= cfg->line_outs;
 if (spec->multi_ios > idx)
  return spec->multi_io[idx].dac;
 return 0;
}

/* return the DAC if it's reachable, otherwise zero */
static inline hda_nid_t try_dac(struct hda_codec *codec,
    hda_nid_t dac, hda_nid_t pin)
{
 return is_reachable_path(codec, dac, pin) ? dac : 0;
}

/* try to assign DACs to pins and return the resultant badness */
static int try_assign_dacs(struct hda_codec *codec, int num_outs,
      const hda_nid_t *pins, hda_nid_t *dacs,
      int *path_idx,
      const struct badness_table *bad)
{
 struct hda_gen_spec *spec = codec->spec;
 int i, j;
 int badness = 0;
 hda_nid_t dac;

 if (!num_outs)
  return 0;

 for (i = 0; i < num_outs; i++) {
  struct nid_path *path;
  hda_nid_t pin = pins[i];

  if (!spec->preferred_dacs) {
   path = snd_hda_get_path_from_idx(codec, path_idx[i]);
   if (path) {
    badness += assign_out_path_ctls(codec, path);
    continue;
   }
  }

  dacs[i] = get_preferred_dac(codec, pin);
  if (dacs[i]) {
   if (is_dac_already_used(codec, dacs[i]))
    badness += bad->shared_primary;
  } else if (spec->preferred_dacs) {
   badness += BAD_NO_PRIMARY_DAC;
  }

  if (!dacs[i])
   dacs[i] = look_for_dac(codec, pin, false);
  if (!dacs[i] && !i) {
   /* try to steal the DAC of surrounds for the front */
   for (j = 1; j < num_outs; j++) {
    if (is_reachable_path(codec, dacs[j], pin)) {
     dacs[0] = dacs[j];
     dacs[j] = 0;
     invalidate_nid_path(codec, path_idx[j]);
     path_idx[j] = 0;
     break;
    }
   }
  }
  dac = dacs[i];
  if (!dac) {
   if (num_outs > 2)
    dac = try_dac(codec, get_primary_out(codec, i), pin);
   if (!dac)
    dac = try_dac(codec, dacs[0], pin);
   if (!dac)
    dac = try_dac(codec, get_primary_out(codec, i), pin);
   if (dac) {
    if (!i)
     badness += bad->shared_primary;
    else if (i == 1)
     badness += bad->shared_surr;
    else
     badness += bad->shared_clfe;
   } else if (is_reachable_path(codec, spec->private_dac_nids[0], pin)) {
    dac = spec->private_dac_nids[0];
    badness += bad->shared_surr_main;
   } else if (!i)
    badness += bad->no_primary_dac;
   else
    badness += bad->no_dac;
  }
  if (!dac)
   continue;
  path = snd_hda_add_new_path(codec, dac, pin, -spec->mixer_nid);
  if (!path && !i && spec->mixer_nid) {
   /* try with aamix */
   path = snd_hda_add_new_path(codec, dac, pin, 0);
  }
  if (!path) {
   dacs[i] = 0;
   badness += bad->no_dac;
  } else {
   /* print_nid_path(codec, "output", path); */
   path->active = true;
   path_idx[i] = snd_hda_get_path_idx(codec, path);
   badness += assign_out_path_ctls(codec, path);
  }
 }

 return badness;
}

/* return NID if the given pin has only a single connection to a certain DAC */
static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
{
 struct hda_gen_spec *spec = codec->spec;
 int i;
 hda_nid_t nid_found = 0;

 for (i = 0; i < spec->num_all_dacs; i++) {
  hda_nid_t nid = spec->all_dacs[i];
  if (!nid || is_dac_already_used(codec, nid))
   continue;
  if (is_reachable_path(codec, nid, pin)) {
   if (nid_found)
    return 0;
   nid_found = nid;
  }
 }
 return nid_found;
}

/* check whether the given pin can be a multi-io pin */
static bool can_be_multiio_pin(struct hda_codec *codec,
          unsigned int location, hda_nid_t nid)
{
 unsigned int defcfg, caps;

 defcfg = snd_hda_codec_get_pincfg(codec, nid);
 if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX)
  return false;
 if (location && get_defcfg_location(defcfg) != location)
  return false;
 caps = snd_hda_query_pin_caps(codec, nid);
 if (!(caps & AC_PINCAP_OUT))
  return false;
 return true;
}

/* count the number of input pins that are capable to be multi-io */
static int count_multiio_pins(struct hda_codec *codec, hda_nid_t reference_pin)
{
 struct hda_gen_spec *spec = codec->spec;
 struct auto_pin_cfg *cfg = &spec->autocfg;
 unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin);
 unsigned int location = get_defcfg_location(defcfg);
 int type, i;
 int num_pins = 0;

 for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
  for (i = 0; i < cfg->num_inputs; i++) {
   if (cfg->inputs[i].type != type)
    continue;
   if (can_be_multiio_pin(codec, location,
            cfg->inputs[i].pin))
    num_pins++;
  }
 }
 return num_pins;
}

/*
 * multi-io helper
 *
 * When hardwired is set, try to fill ony hardwired pins, and returns
 * zero if any pins are filled, non-zero if nothing found.
 * When hardwired is off, try to fill possible input pins, and returns
 * the badness value.
 */

static int fill_multi_ios(struct hda_codec *codec,
     hda_nid_t reference_pin,
     bool hardwired)
{
 struct hda_gen_spec *spec = codec->spec;
 struct auto_pin_cfg *cfg = &spec->autocfg;
 int type, i, j, num_pins, old_pins;
 unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin);
 unsigned int location = get_defcfg_location(defcfg);
 int badness = 0;
 struct nid_path *path;

 old_pins = spec->multi_ios;
 if (old_pins >= 2)
  goto end_fill;

 num_pins = count_multiio_pins(codec, reference_pin);
 if (num_pins < 2)
  goto end_fill;

 for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
  for (i = 0; i < cfg->num_inputs; i++) {
   hda_nid_t nid = cfg->inputs[i].pin;
   hda_nid_t dac = 0;

   if (cfg->inputs[i].type != type)
    continue;
   if (!can_be_multiio_pin(codec, location, nid))
    continue;
   for (j = 0; j < spec->multi_ios; j++) {
    if (nid == spec->multi_io[j].pin)
     break;
   }
   if (j < spec->multi_ios)
    continue;

   if (hardwired)
    dac = get_dac_if_single(codec, nid);
   else if (!dac)
    dac = look_for_dac(codec, nid, false);
   if (!dac) {
    badness++;
    continue;
   }
   path = snd_hda_add_new_path(codec, dac, nid,
          -spec->mixer_nid);
   if (!path) {
    badness++;
    continue;
   }
   /* print_nid_path(codec, "multiio", path); */
   spec->multi_io[spec->multi_ios].pin = nid;
   spec->multi_io[spec->multi_ios].dac = dac;
   spec->out_paths[cfg->line_outs + spec->multi_ios] =
    snd_hda_get_path_idx(codec, path);
   spec->multi_ios++;
   if (spec->multi_ios >= 2)
    break;
  }
 }
 end_fill:
 if (badness)
  badness = BAD_MULTI_IO;
 if (old_pins == spec->multi_ios) {
  if (hardwired)
   return 1; /* nothing found */
  else
   return badness; /* no badness if nothing found */
 }
 if (!hardwired && spec->multi_ios < 2) {
  /* cancel newly assigned paths */
  spec->paths.used -= spec->multi_ios - old_pins;
  spec->multi_ios = old_pins;
  return badness;
 }

 /* assign volume and mute controls */
 for (i = old_pins; i < spec->multi_ios; i++) {
  path = snd_hda_get_path_from_idx(codec, spec->out_paths[cfg->line_outs + i]);
  badness += assign_out_path_ctls(codec, path);
 }

 return badness;
}

/* map DACs for all pins in the list if they are single connections */
static bool map_singles(struct hda_codec *codec, int outs,
   const hda_nid_t *pins, hda_nid_t *dacs, int *path_idx)
{
 struct hda_gen_spec *spec = codec->spec;
 int i;
 bool found = false;
 for (i = 0; i < outs; i++) {
  struct nid_path *path;
  hda_nid_t dac;
  if (dacs[i])
   continue;
  dac = get_dac_if_single(codec, pins[i]);
  if (!dac)
   continue;
  path = snd_hda_add_new_path(codec, dac, pins[i],
         -spec->mixer_nid);
  if (!path && !i && spec->mixer_nid)
   path = snd_hda_add_new_path(codec, dac, pins[i], 0);
  if (path) {
   dacs[i] = dac;
   found = true;
   /* print_nid_path(codec, "output", path); */
   path->active = true;
   path_idx[i] = snd_hda_get_path_idx(codec, path);
  }
 }
 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)
{
 struct hda_gen_spec *spec = codec->spec;
 struct nid_path *path;
 hda_nid_t path_dac, dac, pin;

 path = snd_hda_get_path_from_idx(codec, path_idx);
 if (!path || !path->depth ||
     is_nid_contained(path, spec->mixer_nid))
  return 0;
 path_dac = path->path[0];
 dac = spec->private_dac_nids[0];
 pin = path->path[path->depth - 1];
 path = snd_hda_add_new_path(codec, dac, pin, spec->mixer_nid);
 if (!path) {
  if (dac != path_dac)
   dac = path_dac;
  else if (spec->multiout.hp_out_nid[0])
   dac = spec->multiout.hp_out_nid[0];
  else if (spec->multiout.extra_out_nid[0])
   dac = spec->multiout.extra_out_nid[0];
  else
   dac = 0;
  if (dac)
   path = snd_hda_add_new_path(codec, dac, pin,
          spec->mixer_nid);
 }
 if (!path)
  return 0;
 /* print_nid_path(codec, "output-aamix", path); */
 path->active = false/* unused as default */
 path->pin_fixed = true/* static route */
 return snd_hda_get_path_idx(codec, path);
}

/* check whether the independent HP is available with the current config */
static bool indep_hp_possible(struct hda_codec *codec)
{
 struct hda_gen_spec *spec = codec->spec;
 struct auto_pin_cfg *cfg = &spec->autocfg;
 struct nid_path *path;
 int i, idx;

 if (cfg->line_out_type == AUTO_PIN_HP_OUT)
  idx = spec->out_paths[0];
 else
  idx = spec->hp_paths[0];
 path = snd_hda_get_path_from_idx(codec, idx);
 if (!path)
  return false;

 /* assume no path conflicts unless aamix is involved */
 if (!spec->mixer_nid || !is_nid_contained(path, spec->mixer_nid))
  return true;

 /* check whether output paths contain aamix */
 for (i = 0; i < cfg->line_outs; i++) {
  if (spec->out_paths[i] == idx)
   break;
  path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]);
  if (path && is_nid_contained(path, spec->mixer_nid))
   return false;
 }
 for (i = 0; i < cfg->speaker_outs; i++) {
  path = snd_hda_get_path_from_idx(codec, spec->speaker_paths[i]);
  if (path && is_nid_contained(path, spec->mixer_nid))
   return false;
 }

 return true;
}

/* fill the empty entries in the dac array for speaker/hp with the
 * shared dac pointed by the paths
 */

static void refill_shared_dacs(struct hda_codec *codec, int num_outs,
          hda_nid_t *dacs, int *path_idx)
{
 struct nid_path *path;
 int i;

 for (i = 0; i < num_outs; i++) {
  if (dacs[i])
   continue;
  path = snd_hda_get_path_from_idx(codec, path_idx[i]);
  if (!path)
   continue;
  dacs[i] = path->path[0];
 }
}

/* fill in the dac_nids table from the parsed pin configuration */
static int fill_and_eval_dacs(struct hda_codec *codec,
         bool fill_hardwired,
         bool fill_mio_first)
{
 struct hda_gen_spec *spec = codec->spec;
 struct auto_pin_cfg *cfg = &spec->autocfg;
 int i, err, badness;

 /* set num_dacs once to full for look_for_dac() */
 spec->multiout.num_dacs = cfg->line_outs;
 spec->multiout.dac_nids = spec->private_dac_nids;
 memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids));
 memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid));
 memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid));
 spec->multi_ios = 0;
 snd_array_free(&spec->paths);

 /* clear path indices */
 memset(spec->out_paths, 0, sizeof(spec->out_paths));
 memset(spec->hp_paths, 0, sizeof(spec->hp_paths));
 memset(spec->speaker_paths, 0, sizeof(spec->speaker_paths));
 memset(spec->aamix_out_paths, 0, sizeof(spec->aamix_out_paths));
 memset(spec->digout_paths, 0, sizeof(spec->digout_paths));
 memset(spec->input_paths, 0, sizeof(spec->input_paths));
 memset(spec->loopback_paths, 0, sizeof(spec->loopback_paths));
 memset(&spec->digin_path, 0, sizeof(spec->digin_path));

 badness = 0;

 /* fill hard-wired DACs first */
 if (fill_hardwired) {
  bool mapped;
  do {
   mapped = map_singles(codec, cfg->line_outs,
          cfg->line_out_pins,
          spec->private_dac_nids,
          spec->out_paths);
   mapped |= map_singles(codec, cfg->hp_outs,
           cfg->hp_pins,
           spec->multiout.hp_out_nid,
           spec->hp_paths);
   mapped |= map_singles(codec, cfg->speaker_outs,
           cfg->speaker_pins,
           spec->multiout.extra_out_nid,
           spec->speaker_paths);
   if (!spec->no_multi_io &&
       fill_mio_first && cfg->line_outs == 1 &&
       cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
    err = fill_multi_ios(codec, cfg->line_out_pins[0], true);
    if (!err)
     mapped = true;
   }
  } while (mapped);
 }

 badness += try_assign_dacs(codec, cfg->line_outs, cfg->line_out_pins,
       spec->private_dac_nids, spec->out_paths,
       spec->main_out_badness);

 if (!spec->no_multi_io && fill_mio_first &&
     cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
  /* try to fill multi-io first */
  err = fill_multi_ios(codec, cfg->line_out_pins[0], false);
  if (err < 0)
   return err;
  /* we don't count badness at this stage yet */
 }

 if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
  err = try_assign_dacs(codec, cfg->hp_outs, cfg->hp_pins,
          spec->multiout.hp_out_nid,
          spec->hp_paths,
          spec->extra_out_badness);
  if (err < 0)
   return err;
  badness += err;
 }
 if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
  err = try_assign_dacs(codec, cfg->speaker_outs,
          cfg->speaker_pins,
          spec->multiout.extra_out_nid,
          spec->speaker_paths,
          spec->extra_out_badness);
  if (err < 0)
   return err;
  badness += err;
 }
 if (!spec->no_multi_io &&
     cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
  err = fill_multi_ios(codec, cfg->line_out_pins[0], false);
  if (err < 0)
   return err;
  badness += err;
 }

 if (spec->mixer_nid) {
  spec->aamix_out_paths[0] =
   check_aamix_out_path(codec, spec->out_paths[0]);
  if (cfg->line_out_type != AUTO_PIN_HP_OUT)
   spec->aamix_out_paths[1] =
    check_aamix_out_path(codec, spec->hp_paths[0]);
  if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
   spec->aamix_out_paths[2] =
    check_aamix_out_path(codec, spec->speaker_paths[0]);
 }

 if (!spec->no_multi_io &&
     cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
  if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2)
   spec->multi_ios = 1; /* give badness */

 /* re-count num_dacs and squash invalid entries */
 spec->multiout.num_dacs = 0;
 for (i = 0; i < cfg->line_outs; i++) {
  if (spec->private_dac_nids[i])
   spec->multiout.num_dacs++;
  else {
   memmove(spec->private_dac_nids + i,
    spec->private_dac_nids + i + 1,
    sizeof(hda_nid_t) * (cfg->line_outs - i - 1));
   spec->private_dac_nids[cfg->line_outs - 1] = 0;
  }
 }

 spec->ext_channel_count = spec->min_channel_count =
  spec->multiout.num_dacs * 2;

 if (spec->multi_ios == 2) {
  for (i = 0; i < 2; i++)
   spec->private_dac_nids[spec->multiout.num_dacs++] =
    spec->multi_io[i].dac;
 } else if (spec->multi_ios) {
  spec->multi_ios = 0;
  badness += BAD_MULTI_IO;
 }

 if (spec->indep_hp && !indep_hp_possible(codec))
  badness += BAD_NO_INDEP_HP;

 /* re-fill the shared DAC for speaker / headphone */
 if (cfg->line_out_type != AUTO_PIN_HP_OUT)
  refill_shared_dacs(codec, cfg->hp_outs,
       spec->multiout.hp_out_nid,
       spec->hp_paths);
 if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
  refill_shared_dacs(codec, cfg->speaker_outs,
       spec->multiout.extra_out_nid,
       spec->speaker_paths);

 return badness;
}

#define DEBUG_BADNESS

#ifdef DEBUG_BADNESS
#define debug_badness(fmt, ...)      \
 codec_dbg(codec, fmt, ##__VA_ARGS__)
#else
#define debug_badness(fmt, ...)      \
 do { if (0) codec_dbg(codec, fmt, ##__VA_ARGS__); } while (0)
#endif

#ifdef DEBUG_BADNESS
static inline void print_nid_path_idx(struct hda_codec *codec,
          const char *pfx, int idx)
{
 struct nid_path *path;

 path = snd_hda_get_path_from_idx(codec, idx);
 if (path)
  print_nid_path(codec, pfx, path);
}

static void debug_show_configs(struct hda_codec *codec,
          struct auto_pin_cfg *cfg)
{
 struct hda_gen_spec *spec = codec->spec;
 static const char * const lo_type[3] = { "LO""SP""HP" };
 int i;

 debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x (type %s)\n",
        cfg->line_out_pins[0], cfg->line_out_pins[1],
        cfg->line_out_pins[2], cfg->line_out_pins[3],
        spec->multiout.dac_nids[0],
        spec->multiout.dac_nids[1],
        spec->multiout.dac_nids[2],
        spec->multiout.dac_nids[3],
        lo_type[cfg->line_out_type]);
 for (i = 0; i < cfg->line_outs; i++)
  print_nid_path_idx(codec, " out", spec->out_paths[i]);
 if (spec->multi_ios > 0)
  debug_badness("multi_ios(%d) = %x/%x : %x/%x\n",
         spec->multi_ios,
         spec->multi_io[0].pin, spec->multi_io[1].pin,
         spec->multi_io[0].dac, spec->multi_io[1].dac);
 for (i = 0; i < spec->multi_ios; i++)
  print_nid_path_idx(codec, " mio",
       spec->out_paths[cfg->line_outs + i]);
 if (cfg->hp_outs)
  debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
        cfg->hp_pins[0], cfg->hp_pins[1],
        cfg->hp_pins[2], cfg->hp_pins[3],
        spec->multiout.hp_out_nid[0],
        spec->multiout.hp_out_nid[1],
        spec->multiout.hp_out_nid[2],
        spec->multiout.hp_out_nid[3]);
 for (i = 0; i < cfg->hp_outs; i++)
  print_nid_path_idx(codec, " hp ", spec->hp_paths[i]);
 if (cfg->speaker_outs)
  debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
        cfg->speaker_pins[0], cfg->speaker_pins[1],
        cfg->speaker_pins[2], cfg->speaker_pins[3],
        spec->multiout.extra_out_nid[0],
        spec->multiout.extra_out_nid[1],
        spec->multiout.extra_out_nid[2],
        spec->multiout.extra_out_nid[3]);
 for (i = 0; i < cfg->speaker_outs; i++)
  print_nid_path_idx(codec, " spk", spec->speaker_paths[i]);
 for (i = 0; i < 3; i++)
  print_nid_path_idx(codec, " mix", spec->aamix_out_paths[i]);
}
#else
#define debug_show_configs(codec, cfg) /* NOP */
#endif

/* find all available DACs of the codec */
static void fill_all_dac_nids(struct hda_codec *codec)
{
 struct hda_gen_spec *spec = codec->spec;
 hda_nid_t nid;

 spec->num_all_dacs = 0;
 memset(spec->all_dacs, 0, sizeof(spec->all_dacs));
 for_each_hda_codec_node(nid, codec) {
  if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT)
   continue;
  if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) {
   codec_err(codec, "Too many DACs!\n");
   break;
  }
  spec->all_dacs[spec->num_all_dacs++] = nid;
 }
}

static int parse_output_paths(struct hda_codec *codec)
{
 struct hda_gen_spec *spec = codec->spec;
 struct auto_pin_cfg *cfg = &spec->autocfg;
 struct auto_pin_cfg *best_cfg;
 unsigned int val;
 int best_badness = INT_MAX;
 int badness;
 bool fill_hardwired = true, fill_mio_first = true;
 bool best_wired = true, best_mio = true;
 bool hp_spk_swapped = false;

 best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL);
 if (!best_cfg)
  return -ENOMEM;
 *best_cfg = *cfg;

 for (;;) {
  badness = fill_and_eval_dacs(codec, fill_hardwired,
          fill_mio_first);
  if (badness < 0) {
   kfree(best_cfg);
   return badness;
  }
  debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n",
         cfg->line_out_type, fill_hardwired, fill_mio_first,
         badness);
  debug_show_configs(codec, cfg);
  if (badness < best_badness) {
   best_badness = badness;
   *best_cfg = *cfg;
   best_wired = fill_hardwired;
   best_mio = fill_mio_first;
  }
  if (!badness)
   break;
  fill_mio_first = !fill_mio_first;
  if (!fill_mio_first)
   continue;
  fill_hardwired = !fill_hardwired;
  if (!fill_hardwired)
   continue;
  if (hp_spk_swapped)
   break;
  hp_spk_swapped = true;
  if (cfg->speaker_outs > 0 &&
      cfg->line_out_type == AUTO_PIN_HP_OUT) {
   cfg->hp_outs = cfg->line_outs;
   memcpy(cfg->hp_pins, cfg->line_out_pins,
          sizeof(cfg->hp_pins));
   cfg->line_outs = cfg->speaker_outs;
   memcpy(cfg->line_out_pins, cfg->speaker_pins,
          sizeof(cfg->speaker_pins));
   cfg->speaker_outs = 0;
   memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
   cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
   fill_hardwired = true;
   continue;
  }
  if (cfg->hp_outs > 0 &&
      cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
   cfg->speaker_outs = cfg->line_outs;
   memcpy(cfg->speaker_pins, cfg->line_out_pins,
          sizeof(cfg->speaker_pins));
   cfg->line_outs = cfg->hp_outs;
   memcpy(cfg->line_out_pins, cfg->hp_pins,
          sizeof(cfg->hp_pins));
   cfg->hp_outs = 0;
   memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
   cfg->line_out_type = AUTO_PIN_HP_OUT;
   fill_hardwired = true;
   continue;
  }
  break;
 }

 if (badness) {
  debug_badness("==> restoring best_cfg\n");
  *cfg = *best_cfg;
  fill_and_eval_dacs(codec, best_wired, best_mio);
 }
 debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n",
        cfg->line_out_type, best_wired, best_mio);
 debug_show_configs(codec, cfg);

 if (cfg->line_out_pins[0]) {
  struct nid_path *path;
  path = snd_hda_get_path_from_idx(codec, spec->out_paths[0]);
  if (path)
   spec->vmaster_nid = look_for_out_vol_nid(codec, path);
  if (spec->vmaster_nid) {
   snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
      HDA_OUTPUT, spec->vmaster_tlv);
   if (spec->dac_min_mute)
    spec->vmaster_tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] |= TLV_DB_SCALE_MUTE;
  }
 }

 /* set initial pinctl targets */
 if (spec->prefer_hp_amp || cfg->line_out_type == AUTO_PIN_HP_OUT)
  val = PIN_HP;
 else
  val = PIN_OUT;
 set_pin_targets(codec, cfg->line_outs, cfg->line_out_pins, val);
 if (cfg->line_out_type != AUTO_PIN_HP_OUT)
  set_pin_targets(codec, cfg->hp_outs, cfg->hp_pins, PIN_HP);
 if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
  val = spec->prefer_hp_amp ? PIN_HP : PIN_OUT;
  set_pin_targets(codec, cfg->speaker_outs,
    cfg->speaker_pins, val);
 }

 /* clear indep_hp flag if not available */
 if (spec->indep_hp && !indep_hp_possible(codec))
  spec->indep_hp = 0;

 kfree(best_cfg);
 return 0;
}

/* add playback controls from the parsed DAC table */
static int create_multi_out_ctls(struct hda_codec *codec,
     const struct auto_pin_cfg *cfg)
{
 struct hda_gen_spec *spec = codec->spec;
 int i, err, noutputs;

 noutputs = cfg->line_outs;
 if (spec->multi_ios > 0 && cfg->line_outs < 3)
  noutputs += spec->multi_ios;

 for (i = 0; i < noutputs; i++) {
  const char *name;
  int index;
  struct nid_path *path;

  path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]);
  if (!path)
   continue;

  name = get_line_out_pfx(codec, i, &index, NID_PATH_VOL_CTL);
  if (!name || !strcmp(name, "CLFE")) {
   /* Center/LFE */
   err = add_vol_ctl(codec, "Center", 0, 1, path);
   if (err < 0)
    return err;
   err = add_vol_ctl(codec, "LFE", 0, 2, path);
   if (err < 0)
    return err;
  } else {
   err = add_stereo_vol(codec, name, index, path);
   if (err < 0)
    return err;
  }

  name = get_line_out_pfx(codec, i, &index, NID_PATH_MUTE_CTL);
  if (!name || !strcmp(name, "CLFE")) {
   err = add_sw_ctl(codec, "Center", 0, 1, path);
   if (err < 0)
    return err;
   err = add_sw_ctl(codec, "LFE", 0, 2, path);
   if (err < 0)
    return err;
  } else {
   err = add_stereo_sw(codec, name, index, path);
   if (err < 0)
    return err;
  }
 }
 return 0;
}

static int create_extra_out(struct hda_codec *codec, int path_idx,
       const char *pfx, int cidx)
{
 struct nid_path *path;
 int err;

 path = snd_hda_get_path_from_idx(codec, path_idx);
 if (!path)
  return 0;
 err = add_stereo_vol(codec, pfx, cidx, path);
 if (err < 0)
  return err;
 err = add_stereo_sw(codec, pfx, cidx, path);
 if (err < 0)
  return err;
 return 0;
}

/* add playback controls for speaker and HP outputs */
static int create_extra_outs(struct hda_codec *codec, int num_pins,
        const int *paths, const char *pfx)
{
 int i;

 for (i = 0; i < num_pins; i++) {
  const char *name;
  char tmp[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
  int err, idx = 0;

  if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker"))
   name = "Bass Speaker";
  else if (num_pins >= 3) {
   snprintf(tmp, sizeof(tmp), "%s %s",
     pfx, channel_name[i]);
   name = tmp;
  } else {
   name = pfx;
   idx = i;
  }
  err = create_extra_out(codec, paths[i], name, idx);
  if (err < 0)
   return err;
 }
 return 0;
}

static int create_hp_out_ctls(struct hda_codec *codec)
{
 struct hda_gen_spec *spec = codec->spec;
 return create_extra_outs(codec, spec->autocfg.hp_outs,
     spec->hp_paths,
     "Headphone");
}

static int create_speaker_out_ctls(struct hda_codec *codec)
{
 struct hda_gen_spec *spec = codec->spec;
 return create_extra_outs(codec, spec->autocfg.speaker_outs,
     spec->speaker_paths,
     "Speaker");
}

/*
 * independent HP controls
 */


static void call_hp_automute(struct hda_codec *codec,
        struct hda_jack_callback *jack);
static int indep_hp_info(struct snd_kcontrol *kcontrol,
    struct snd_ctl_elem_info *uinfo)
{
 return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
}

static int indep_hp_get(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;
 ucontrol->value.enumerated.item[0] = spec->indep_hp_enabled;
 return 0;
}

static void update_aamix_paths(struct hda_codec *codec, bool do_mix,
          int nomix_path_idx, int mix_path_idx,
          int out_type);

static int indep_hp_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;
 unsigned int select = ucontrol->value.enumerated.item[0];
 int ret = 0;

 mutex_lock(&spec->pcm_mutex);
 if (spec->active_streams) {
  ret = -EBUSY;
  goto unlock;
 }

 if (spec->indep_hp_enabled != select) {
  hda_nid_t *dacp;
  if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
   dacp = &spec->private_dac_nids[0];
  else
   dacp = &spec->multiout.hp_out_nid[0];

  /* update HP aamix paths in case it conflicts with indep HP */
  if (spec->have_aamix_ctl) {
   if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
    update_aamix_paths(codec, spec->aamix_mode,
         spec->out_paths[0],
         spec->aamix_out_paths[0],
         spec->autocfg.line_out_type);
   else
    update_aamix_paths(codec, spec->aamix_mode,
         spec->hp_paths[0],
         spec->aamix_out_paths[1],
         AUTO_PIN_HP_OUT);
  }

  spec->indep_hp_enabled = select;
  if (spec->indep_hp_enabled)
   *dacp = 0;
  else
   *dacp = spec->alt_dac_nid;

  call_hp_automute(codec, NULL);
  ret = 1;
 }
 unlock:
 mutex_unlock(&spec->pcm_mutex);
 return ret;
}

static const struct snd_kcontrol_new indep_hp_ctl = {
 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 .name = "Independent HP",
 .info = indep_hp_info,
 .get = indep_hp_get,
 .put = indep_hp_put,
};


static int create_indep_hp_ctls(struct hda_codec *codec)
{
 struct hda_gen_spec *spec = codec->spec;
 hda_nid_t dac;

 if (!spec->indep_hp)
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=95 G=94

¤ Dauer der Verarbeitung: 0.21 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.