/*
* Universal Interface for Intel High Definition Audio Codec
*
* Generic widget tree parser
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This driver is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
/* widget node for parsing */
struct hda_gnode {
hda_nid_t nid; /* NID of this widget */
unsigned short nconns; /* number of input connections */
hda_nid_t *conn_list;
hda_nid_t slist[2]; /* temporay list */
unsigned int wid_caps; /* widget capabilities */
unsigned char type; /* widget type */
unsigned char pin_ctl; /* pin controls */
unsigned char checked; /* the flag indicates that the node is already parsed */
unsigned int pin_caps; /* pin widget capabilities */
unsigned int def_cfg; /* default configuration */
unsigned int amp_out_caps; /* AMP out capabilities */
unsigned int amp_in_caps; /* AMP in capabilities */
struct list_head list;
};
/* patch-specific record */
#define MAX_PCM_VOLS 2
struct pcm_vol {
struct hda_gnode *node; /* Node for PCM volume */
unsigned int index; /* connection of PCM volume */
};
struct hda_gspec {
struct hda_gnode *dac_node[2]; /* DAC node */
struct hda_gnode *out_pin_node[2]; /* Output pin (Line-Out) node */
struct pcm_vol pcm_vol[MAX_PCM_VOLS]; /* PCM volumes */
unsigned int pcm_vol_nodes; /* number of PCM volumes */
struct hda_gnode *adc_node; /* ADC node */
struct hda_gnode *cap_vol_node; /* Node for capture volume */
unsigned int cur_cap_src; /* current capture source */
struct hda_input_mux input_mux;
unsigned int def_amp_in_caps;
unsigned int def_amp_out_caps;
struct hda_pcm pcm_rec; /* PCM information */
struct list_head nid_list; /* list of widgets */
#ifdef CONFIG_SND_HDA_POWER_SAVE
#define MAX_LOOPBACK_AMPS 7
struct hda_loopback_check loopback;
int num_loopbacks;
struct hda_amp_list loopback_list[MAX_LOOPBACK_AMPS + 1];
#endif
};
/*
* retrieve the default device type from the default config value
*/
#define defcfg_type(node) (((node)->def_cfg & AC_DEFCFG_DEVICE) >> \
AC_DEFCFG_DEVICE_SHIFT)
#define defcfg_location(node) (((node)->def_cfg & AC_DEFCFG_LOCATION) >> \
AC_DEFCFG_LOCATION_SHIFT)
#define defcfg_port_conn(node) (((node)->def_cfg & AC_DEFCFG_PORT_CONN) >> \
AC_DEFCFG_PORT_CONN_SHIFT)
/*
* destructor
*/
static void snd_hda_generic_free(struct hda_codec *codec)
{
struct hda_gspec *spec = codec->spec;
struct hda_gnode *node, *n;
if (! spec)
return;
/* free all widgets */
list_for_each_entry_safe(node, n, &spec->nid_list, list) {
if (node->conn_list != node->slist)
kfree(node->conn_list);
kfree(node);
}
kfree(spec);
}
/*
* add a new widget node and read its attributes
*/
static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid_t nid)
{
struct hda_gnode *node;
int nconns;
hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
node = kzalloc(sizeof(*node), GFP_KERNEL);
if (node == NULL)
return -ENOMEM;
node->nid = nid;
node->wid_caps = get_wcaps(codec, nid);
node->type = get_wcaps_type(node->wid_caps);
if (node->wid_caps & AC_WCAP_CONN_LIST) {
nconns = snd_hda_get_connections(codec, nid, conn_list,
HDA_MAX_CONNECTIONS);
if (nconns < 0) {
kfree(node);
return nconns;
}
} else {
nconns = 0;
}
if (nconns <= ARRAY_SIZE(node->slist))
node->conn_list = node->slist;
else {
node->conn_list = kmalloc(sizeof(