aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/hda')
-rw-r--r--sound/pci/hda/hda_codec.c341
-rw-r--r--sound/pci/hda/hda_codec.h32
-rw-r--r--sound/pci/hda/hda_generic.c43
-rw-r--r--sound/pci/hda/hda_intel.c220
-rw-r--r--sound/pci/hda/hda_local.h81
-rw-r--r--sound/pci/hda/hda_proc.c13
-rw-r--r--sound/pci/hda/patch_analog.c1338
-rw-r--r--sound/pci/hda/patch_cmedia.c107
-rw-r--r--sound/pci/hda/patch_realtek.c2601
-rw-r--r--sound/pci/hda/patch_si3054.c21
-rw-r--r--sound/pci/hda/patch_sigmatel.c424
11 files changed, 4338 insertions, 883 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 0dbeeaf6113a..4a6dd97deba6 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -155,8 +155,9 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
hda_nid_t *conn_list, int max_conns)
{
unsigned int parm;
- int i, j, conn_len, num_tupples, conns;
+ int i, conn_len, conns;
unsigned int shift, num_elems, mask;
+ hda_nid_t prev_nid;
snd_assert(conn_list && max_conns > 0, return -EINVAL);
@@ -171,7 +172,6 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
num_elems = 4;
}
conn_len = parm & AC_CLIST_LENGTH;
- num_tupples = num_elems / 2;
mask = (1 << (shift-1)) - 1;
if (! conn_len)
@@ -186,40 +186,38 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
/* multi connection */
conns = 0;
- for (i = 0; i < conn_len; i += num_elems) {
- parm = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_LIST, i);
- for (j = 0; j < num_tupples; j++) {
- int range_val;
- hda_nid_t val1, val2, n;
- range_val = parm & (1 << (shift-1)); /* ranges */
- val1 = parm & mask;
- parm >>= shift;
- val2 = parm & mask;
- parm >>= shift;
- if (range_val) {
- /* ranges between val1 and val2 */
- if (val1 > val2) {
- snd_printk(KERN_WARNING "hda_codec: invalid dep_range_val %x:%x\n", val1, val2);
- continue;
- }
- for (n = val1; n <= val2; n++) {
- if (conns >= max_conns)
- return -EINVAL;
- conn_list[conns++] = n;
- }
- } else {
- if (! val1)
- break;
- if (conns >= max_conns)
- return -EINVAL;
- conn_list[conns++] = val1;
- if (! val2)
- break;
- if (conns >= max_conns)
+ prev_nid = 0;
+ for (i = 0; i < conn_len; i++) {
+ int range_val;
+ hda_nid_t val, n;
+
+ if (i % num_elems == 0)
+ parm = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_CONNECT_LIST, i);
+ range_val = !! (parm & (1 << (shift-1))); /* ranges */
+ val = parm & mask;
+ parm >>= shift;
+ if (range_val) {
+ /* ranges between the previous and this one */
+ if (! prev_nid || prev_nid >= val) {
+ snd_printk(KERN_WARNING "hda_codec: invalid dep_range_val %x:%x\n", prev_nid, val);
+ continue;
+ }
+ for (n = prev_nid + 1; n <= val; n++) {
+ if (conns >= max_conns) {
+ snd_printk(KERN_ERR "Too many connections\n");
return -EINVAL;
- conn_list[conns++] = val2;
+ }
+ conn_list[conns++] = n;
+ }
+ } else {
+ if (conns >= max_conns) {
+ snd_printk(KERN_ERR "Too many connections\n");
+ return -EINVAL;
}
+ conn_list[conns++] = val;
}
+ prev_nid = val;
}
return conns;
}
@@ -288,6 +286,9 @@ static int init_unsol_queue(struct hda_bus *bus)
{
struct hda_bus_unsolicited *unsol;
+ if (bus->unsol) /* already initialized */
+ return 0;
+
unsol = kzalloc(sizeof(*unsol), GFP_KERNEL);
if (! unsol) {
snd_printk(KERN_ERR "hda_codec: can't allocate unsolicited queue\n");
@@ -329,7 +330,7 @@ static int snd_hda_bus_free(struct hda_bus *bus)
return 0;
}
-static int snd_hda_bus_dev_free(snd_device_t *device)
+static int snd_hda_bus_dev_free(struct snd_device *device)
{
struct hda_bus *bus = device->device_data;
return snd_hda_bus_free(bus);
@@ -343,12 +344,12 @@ static int snd_hda_bus_dev_free(snd_device_t *device)
*
* Returns 0 if successful, or a negative error code.
*/
-int snd_hda_bus_new(snd_card_t *card, const struct hda_bus_template *temp,
+int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp,
struct hda_bus **busp)
{
struct hda_bus *bus;
int err;
- static snd_device_ops_t dev_ops = {
+ static struct snd_device_ops dev_ops = {
.dev_free = snd_hda_bus_dev_free,
};
@@ -373,8 +374,6 @@ int snd_hda_bus_new(snd_card_t *card, const struct hda_bus_template *temp,
init_MUTEX(&bus->cmd_mutex);
INIT_LIST_HEAD(&bus->codec_list);
- init_unsol_queue(bus);
-
if ((err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops)) < 0) {
snd_hda_bus_free(bus);
return err;
@@ -455,6 +454,27 @@ static void setup_fg_nodes(struct hda_codec *codec)
}
/*
+ * read widget caps for each widget and store in cache
+ */
+static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node)
+{
+ int i;
+ hda_nid_t nid;
+
+ codec->num_nodes = snd_hda_get_sub_nodes(codec, fg_node,
+ &codec->start_nid);
+ codec->wcaps = kmalloc(codec->num_nodes * 4, GFP_KERNEL);
+ if (! codec->wcaps)
+ return -ENOMEM;
+ nid = codec->start_nid;
+ for (i = 0; i < codec->num_nodes; i++, nid++)
+ codec->wcaps[i] = snd_hda_param_read(codec, nid,
+ AC_PAR_AUDIO_WIDGET_CAP);
+ return 0;
+}
+
+
+/*
* codec destructor
*/
static void snd_hda_codec_free(struct hda_codec *codec)
@@ -465,6 +485,8 @@ static void snd_hda_codec_free(struct hda_codec *codec)
codec->bus->caddr_tbl[codec->addr] = NULL;
if (codec->patch_ops.free)
codec->patch_ops.free(codec);
+ kfree(codec->amp_info);
+ kfree(codec->wcaps);
kfree(codec);
}
@@ -518,6 +540,12 @@ int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
return -ENODEV;
}
+ if (read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg) < 0) {
+ snd_printk(KERN_ERR "hda_codec: cannot malloc\n");
+ snd_hda_codec_free(codec);
+ return -ENOMEM;
+ }
+
if (! codec->subsystem_id) {
hda_nid_t nid = codec->afg ? codec->afg : codec->mfg;
codec->subsystem_id = snd_hda_codec_read(codec, nid, 0,
@@ -539,6 +567,9 @@ int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
return err;
}
+ if (codec->patch_ops.unsol_event)
+ init_unsol_queue(bus);
+
snd_hda_codec_proc_new(codec);
sprintf(component, "HDA:%08x", codec->vendor_id);
@@ -586,6 +617,8 @@ static void init_amp_hash(struct hda_codec *codec)
{
memset(codec->amp_hash, 0xff, sizeof(codec->amp_hash));
codec->num_amp_entries = 0;
+ codec->amp_info_size = 0;
+ codec->amp_info = NULL;
}
/* query the hash. allocate an entry if not found. */
@@ -603,9 +636,22 @@ static struct hda_amp_info *get_alloc_amp_hash(struct hda_codec *codec, u32 key)
}
/* add a new hash entry */
- if (codec->num_amp_entries >= ARRAY_SIZE(codec->amp_info)) {
- snd_printk(KERN_ERR "hda_codec: Tooooo many amps!\n");
- return NULL;
+ if (codec->num_amp_entries >= codec->amp_info_size) {
+ /* reallocate the array */
+ int new_size = codec->amp_info_size + 64;
+ struct hda_amp_info *new_info = kcalloc(new_size, sizeof(struct hda_amp_info),
+ GFP_KERNEL);
+ if (! new_info) {
+ snd_printk(KERN_ERR "hda_codec: can't malloc amp_info\n");
+ return NULL;
+ }
+ if (codec->amp_info) {
+ memcpy(new_info, codec->amp_info,
+ codec->amp_info_size * sizeof(struct hda_amp_info));
+ kfree(codec->amp_info);
+ }
+ codec->amp_info_size = new_size;
+ codec->amp_info = new_info;
}
cur = codec->num_amp_entries++;
info = &codec->amp_info[cur];
@@ -627,7 +673,7 @@ static u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
if (! info)
return 0;
if (! (info->status & INFO_AMP_CAPS)) {
- if (!(snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) & AC_WCAP_AMP_OVRD))
+ if (! (get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
nid = codec->afg;
info->amp_caps = snd_hda_param_read(codec, nid, direction == HDA_OUTPUT ?
AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
@@ -712,7 +758,7 @@ static int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int
#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
/* volume */
-int snd_hda_mixer_amp_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
u16 nid = get_amp_nid(kcontrol);
@@ -733,7 +779,7 @@ int snd_hda_mixer_amp_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t
return 0;
}
-int snd_hda_mixer_amp_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = get_amp_nid(kcontrol);
@@ -749,7 +795,7 @@ int snd_hda_mixer_amp_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
return 0;
}
-int snd_hda_mixer_amp_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = get_amp_nid(kcontrol);
@@ -771,7 +817,7 @@ int snd_hda_mixer_amp_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
}
/* switch */
-int snd_hda_mixer_amp_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
int chs = get_amp_channels(kcontrol);
@@ -782,7 +828,7 @@ int snd_hda_mixer_amp_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t
return 0;
}
-int snd_hda_mixer_amp_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = get_amp_nid(kcontrol);
@@ -798,7 +844,7 @@ int snd_hda_mixer_amp_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
return 0;
}
-int snd_hda_mixer_amp_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = get_amp_nid(kcontrol);
@@ -829,7 +875,7 @@ int snd_hda_mixer_amp_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
#define AMP_VAL_IDX_SHIFT 19
#define AMP_VAL_IDX_MASK (0x0f<<19)
-int snd_hda_mixer_bind_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
unsigned long pval;
@@ -844,7 +890,7 @@ int snd_hda_mixer_bind_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
return err;
}
-int snd_hda_mixer_bind_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
unsigned long pval;
@@ -869,14 +915,14 @@ int snd_hda_mixer_bind_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
* SPDIF out controls
*/
-static int snd_hda_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+static int snd_hda_spdif_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
uinfo->count = 1;
return 0;
}
-static int snd_hda_spdif_cmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int snd_hda_spdif_cmask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
IEC958_AES0_NONAUDIO |
@@ -887,7 +933,7 @@ static int snd_hda_spdif_cmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_
return 0;
}
-static int snd_hda_spdif_pmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int snd_hda_spdif_pmask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
IEC958_AES0_NONAUDIO |
@@ -895,7 +941,7 @@ static int snd_hda_spdif_pmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_
return 0;
}
-static int snd_hda_spdif_default_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
@@ -958,7 +1004,7 @@ static unsigned int convert_to_spdif_status(unsigned short val)
return sbits;
}
-static int snd_hda_spdif_default_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = kcontrol->private_value;
@@ -984,7 +1030,7 @@ static int snd_hda_spdif_default_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_valu
return change;
}
-static int snd_hda_spdif_out_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+static int snd_hda_spdif_out_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
@@ -993,7 +1039,7 @@ static int snd_hda_spdif_out_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_
return 0;
}
-static int snd_hda_spdif_out_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
@@ -1001,7 +1047,7 @@ static int snd_hda_spdif_out_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_v
return 0;
}
-static int snd_hda_spdif_out_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = kcontrol->private_value;
@@ -1024,7 +1070,7 @@ static int snd_hda_spdif_out_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_v
return change;
}
-static snd_kcontrol_new_t dig_mixes[] = {
+static struct snd_kcontrol_new dig_mixes[] = {
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1069,8 +1115,8 @@ static snd_kcontrol_new_t dig_mixes[] = {
int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
{
int err;
- snd_kcontrol_t *kctl;
- snd_kcontrol_new_t *dig_mix;
+ struct snd_kcontrol *kctl;
+ struct snd_kcontrol_new *dig_mix;
for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
kctl = snd_ctl_new1(dig_mix, codec);
@@ -1089,7 +1135,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
#define snd_hda_spdif_in_switch_info snd_hda_spdif_out_switch_info
-static int snd_hda_spdif_in_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int snd_hda_spdif_in_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
@@ -1097,7 +1143,7 @@ static int snd_hda_spdif_in_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_va
return 0;
}
-static int snd_hda_spdif_in_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int snd_hda_spdif_in_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = kcontrol->private_value;
@@ -1114,7 +1160,7 @@ static int snd_hda_spdif_in_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_va
return change;
}
-static int snd_hda_spdif_in_status_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = kcontrol->private_value;
@@ -1130,7 +1176,7 @@ static int snd_hda_spdif_in_status_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_va
return 0;
}
-static snd_kcontrol_new_t dig_in_ctls[] = {
+static struct snd_kcontrol_new dig_in_ctls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH),
@@ -1161,8 +1207,8 @@ static snd_kcontrol_new_t dig_in_ctls[] = {
int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
{
int err;
- snd_kcontrol_t *kctl;
- snd_kcontrol_new_t *dig_mix;
+ struct snd_kcontrol *kctl;
+ struct snd_kcontrol_new *dig_mix;
for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
kctl = snd_ctl_new1(dig_mix, codec);
@@ -1175,6 +1221,31 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
}
+/*
+ * set power state of the codec
+ */
+static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
+ unsigned int power_state)
+{
+ hda_nid_t nid, nid_start;
+ int nodes;
+
+ snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE,
+ power_state);
+
+ nodes = snd_hda_get_sub_nodes(codec, fg, &nid_start);
+ for (nid = nid_start; nid < nodes + nid_start; nid++) {
+ if (get_wcaps(codec, nid) & AC_WCAP_POWER)
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_POWER_STATE,
+ power_state);
+ }
+
+ if (power_state == AC_PWRST_D0)
+ msleep(10);
+}
+
+
/**
* snd_hda_build_controls - build mixer controls
* @bus: the BUS
@@ -1202,6 +1273,9 @@ int snd_hda_build_controls(struct hda_bus *bus)
list_for_each(p, &bus->codec_list) {
struct hda_codec *codec = list_entry(p, struct hda_codec, list);
int err;
+ hda_set_power_state(codec,
+ codec->afg ? codec->afg : codec->mfg,
+ AC_PWRST_D0);
if (! codec->patch_ops.init)
continue;
err = codec->patch_ops.init(codec);
@@ -1320,7 +1394,7 @@ int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
val = 0;
if (nid != codec->afg &&
- snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) & AC_WCAP_FORMAT_OVRD) {
+ (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) {
val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
if (val == -1)
return -EIO;
@@ -1342,7 +1416,7 @@ int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
unsigned int bps;
unsigned int wcaps;
- wcaps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
+ wcaps = get_wcaps(codec, nid);
streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
if (streams == -1)
return -EIO;
@@ -1412,7 +1486,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
unsigned int val = 0, rate, stream;
if (nid != codec->afg &&
- snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) & AC_WCAP_FORMAT_OVRD) {
+ (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) {
val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
if (val == -1)
return 0;
@@ -1478,7 +1552,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
*/
static int hda_pcm_default_open_close(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
return 0;
}
@@ -1487,7 +1561,7 @@ static int hda_pcm_default_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
return 0;
@@ -1495,7 +1569,7 @@ static int hda_pcm_default_prepare(struct hda_pcm_stream *hinfo,
static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
snd_hda_codec_setup_stream(codec, hinfo->nid, 0, 0, 0);
return 0;
@@ -1626,30 +1700,90 @@ int snd_hda_check_board_config(struct hda_codec *codec, const struct hda_board_c
/**
* snd_hda_add_new_ctls - create controls from the array
* @codec: the HDA codec
- * @knew: the array of snd_kcontrol_new_t
+ * @knew: the array of struct snd_kcontrol_new
*
* This helper function creates and add new controls in the given array.
* The array must be terminated with an empty entry as terminator.
*
* Returns 0 if successful, or a negative error code.
*/
-int snd_hda_add_new_ctls(struct hda_codec *codec, snd_kcontrol_new_t *knew)
+int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
{
int err;
for (; knew->name; knew++) {
- err = snd_ctl_add(codec->bus->card, snd_ctl_new1(knew, codec));
- if (err < 0)
- return err;
+ struct snd_kcontrol *kctl;
+ kctl = snd_ctl_new1(knew, codec);
+ if (! kctl)
+ return -ENOMEM;
+ err = snd_ctl_add(codec->bus->card, kctl);
+ if (err < 0) {
+ if (! codec->addr)
+ return err;
+ kctl = snd_ctl_new1(knew, codec);
+ if (! kctl)
+ return -ENOMEM;
+ kctl->id.device = codec->addr;
+ if ((err = snd_ctl_add(codec->bus->card, kctl)) < 0)
+ return err;
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * Channel mode helper
+ */
+int snd_hda_ch_mode_info(struct hda_codec *codec, struct snd_ctl_elem_info *uinfo,
+ const struct hda_channel_mode *chmode, int num_chmodes)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = num_chmodes;
+ if (uinfo->value.enumerated.item >= num_chmodes)
+ uinfo->value.enumerated.item = num_chmodes - 1;
+ sprintf(uinfo->value.enumerated.name, "%dch",
+ chmode[uinfo->value.enumerated.item].channels);
+ return 0;
+}
+
+int snd_hda_ch_mode_get(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol,
+ const struct hda_channel_mode *chmode, int num_chmodes,
+ int max_channels)
+{
+ int i;
+
+ for (i = 0; i < num_chmodes; i++) {
+ if (max_channels == chmode[i].channels) {
+ ucontrol->value.enumerated.item[0] = i;
+ break;
+ }
}
return 0;
}
+int snd_hda_ch_mode_put(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol,
+ const struct hda_channel_mode *chmode, int num_chmodes,
+ int *max_channelsp)
+{
+ unsigned int mode;
+
+ mode = ucontrol->value.enumerated.item[0];
+ snd_assert(mode < num_chmodes, return -EINVAL);
+ if (*max_channelsp == chmode[mode].channels && ! codec->in_resume)
+ return 0;
+ /* change the current channel setting */
+ *max_channelsp = chmode[mode].channels;
+ if (chmode[mode].sequence)
+ snd_hda_sequence_write(codec, chmode[mode].sequence);
+ return 1;
+}
/*
* input MUX helper
*/
-int snd_hda_input_mux_info(const struct hda_input_mux *imux, snd_ctl_elem_info_t *uinfo)
+int snd_hda_input_mux_info(const struct hda_input_mux *imux, struct snd_ctl_elem_info *uinfo)
{
unsigned int index;
@@ -1664,7 +1798,7 @@ int snd_hda_input_mux_info(const struct hda_input_mux *imux, snd_ctl_elem_info_t
}
int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *imux,
- snd_ctl_elem_value_t *ucontrol, hda_nid_t nid,
+ struct snd_ctl_elem_value *ucontrol, hda_nid_t nid,
unsigned int *cur_val)
{
unsigned int idx;
@@ -1715,7 +1849,7 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec, struct hda_multi_out *m
* set up more restrictions for analog out
*/
int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out *mout,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
substream->runtime->hw.channels_max = mout->max_channels;
return snd_pcm_hw_constraint_step(substream->runtime, 0,
@@ -1729,7 +1863,7 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out
int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_out *mout,
unsigned int stream_tag,
unsigned int format,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
hda_nid_t *nids = mout->dac_nids;
int chs = substream->runtime->channels;
@@ -1792,8 +1926,18 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_o
/*
* Helper for automatic ping configuration
*/
+
+static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list)
+{
+ for (; *list; list++)
+ if (*list == nid)
+ return 1;
+ return 0;
+}
+
/* parse all pin widgets and store the useful pin nids to cfg */
-int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *cfg)
+int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *cfg,
+ hda_nid_t *ignore_nids)
{
hda_nid_t nid, nid_start;
int i, j, nodes;
@@ -1806,8 +1950,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c
nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid_start);
for (nid = nid_start; nid < nodes + nid_start; nid++) {
- unsigned int wid_caps = snd_hda_param_read(codec, nid,
- AC_PAR_AUDIO_WIDGET_CAP);
+ unsigned int wid_caps = get_wcaps(codec, nid);
unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
unsigned int def_conf;
short assoc, loc;
@@ -1815,13 +1958,16 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c
/* read all default configuration for pin complex */
if (wid_type != AC_WID_PIN)
continue;
+ /* ignore the given nids (e.g. pc-beep returns error) */
+ if (ignore_nids && is_in_nid_list(nid, ignore_nids))
+ continue;
+
def_conf = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
continue;
loc = get_defcfg_location(def_conf);
switch (get_defcfg_device(def_conf)) {
case AC_JACK_LINE_OUT:
- case AC_JACK_SPEAKER:
seq = get_defcfg_sequence(def_conf);
assoc = get_defcfg_association(def_conf);
if (! assoc)
@@ -1836,6 +1982,9 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c
sequences[cfg->line_outs] = seq;
cfg->line_outs++;
break;
+ case AC_JACK_SPEAKER:
+ cfg->speaker_pin = nid;
+ break;
case AC_JACK_HP_OUT:
cfg->hp_pin = nid;
break;
@@ -1902,6 +2051,12 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c
return 0;
}
+/* labels for input pins */
+const char *auto_pin_cfg_labels[AUTO_PIN_LAST] = {
+ "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux"
+};
+
+
#ifdef CONFIG_PM
/*
* power management
@@ -1923,6 +2078,9 @@ int snd_hda_suspend(struct hda_bus *bus, pm_message_t state)
struct hda_codec *codec = list_entry(p, struct hda_codec, list);
if (codec->patch_ops.suspend)
codec->patch_ops.suspend(codec, state);
+ hda_set_power_state(codec,
+ codec->afg ? codec->afg : codec->mfg,
+ AC_PWRST_D3);
}
return 0;
}
@@ -1940,6 +2098,9 @@ int snd_hda_resume(struct hda_bus *bus)
list_for_each(p, &bus->codec_list) {
struct hda_codec *codec = list_entry(p, struct hda_codec, list);
+ hda_set_power_state(codec,
+ codec->afg ? codec->afg : codec->mfg,
+ AC_PWRST_D0);
if (codec->patch_ops.resume)
codec->patch_ops.resume(codec);
}
@@ -1949,15 +2110,15 @@ int snd_hda_resume(struct hda_bus *bus)
/**
* snd_hda_resume_ctls - resume controls in the new control list
* @codec: the HDA codec
- * @knew: the array of snd_kcontrol_new_t
+ * @knew: the array of struct snd_kcontrol_new
*
- * This function resumes the mixer controls in the snd_kcontrol_new_t array,
+ * This function resumes the mixer controls in the struct snd_kcontrol_new array,
* originally for snd_hda_add_new_ctls().
* The array must be terminated with an empty entry as terminator.
*/
-int snd_hda_resume_ctls(struct hda_codec *codec, snd_kcontrol_new_t *knew)
+int snd_hda_resume_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
{
- snd_ctl_elem_value_t *val;
+ struct snd_ctl_elem_value *val;
val = kmalloc(sizeof(*val), GFP_KERNEL);
if (! val)
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 1179d6cfa82a..63e26c7a2b7a 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -98,7 +98,7 @@ enum {
#define AC_VERB_SET_UNSOLICITED_ENABLE 0x708
#define AC_VERB_SET_PIN_SENSE 0x709
#define AC_VERB_SET_BEEP_CONTROL 0x70a
-#define AC_VERB_SET_EAPD_BTLENALBE 0x70c
+#define AC_VERB_SET_EAPD_BTLENABLE 0x70c
#define AC_VERB_SET_DIGI_CONVERT_1 0x70d
#define AC_VERB_SET_DIGI_CONVERT_2 0x70e
#define AC_VERB_SET_VOLUME_KNOB_CONTROL 0x70f
@@ -214,6 +214,12 @@ enum {
#define AC_PWRST_D2SUP (1<<2)
#define AC_PWRST_D3SUP (1<<3)
+/* Power state values */
+#define AC_PWRST_D0 0x00
+#define AC_PWRST_D1 0x01
+#define AC_PWRST_D2 0x02
+#define AC_PWRST_D3 0x03
+
/* Processing capabilies */
#define AC_PCAP_BENIGN (1<<0)
#define AC_PCAP_NUM_COEF (0xff<<8)
@@ -376,7 +382,7 @@ enum {
};
/* max. connections to a widget */
-#define HDA_MAX_CONNECTIONS 16
+#define HDA_MAX_CONNECTIONS 32
/* max. codec address */
#define HDA_MAX_CODEC_ADDRESS 0x0f
@@ -420,7 +426,7 @@ struct hda_bus_template {
* A hda_bus contains several codecs in the list codec_list.
*/
struct hda_bus {
- snd_card_t *card;
+ struct snd_card *card;
/* copied from template */
void *private_data;
@@ -437,7 +443,7 @@ struct hda_bus {
/* unsolicited event queue */
struct hda_bus_unsolicited *unsol;
- snd_info_entry_t *proc;
+ struct snd_info_entry *proc;
};
/*
@@ -481,14 +487,14 @@ struct hda_amp_info {
/* PCM callbacks */
struct hda_pcm_ops {
int (*open)(struct hda_pcm_stream *info, struct hda_codec *codec,
- snd_pcm_substream_t *substream);
+ struct snd_pcm_substream *substream);
int (*close)(struct hda_pcm_stream *info, struct hda_codec *codec,
- snd_pcm_substream_t *substream);
+ struct snd_pcm_substream *substream);
int (*prepare)(struct hda_pcm_stream *info, struct hda_codec *codec,
unsigned int stream_tag, unsigned int format,
- snd_pcm_substream_t *substream);
+ struct snd_pcm_substream *substream);
int (*cleanup)(struct hda_pcm_stream *info, struct hda_codec *codec,
- snd_pcm_substream_t *substream);
+ struct snd_pcm_substream *substream);
};
/* PCM information for each substream */
@@ -542,10 +548,16 @@ struct hda_codec {
/* codec specific info */
void *spec;
+ /* widget capabilities cache */
+ unsigned int num_nodes;
+ hda_nid_t start_nid;
+ u32 *wcaps;
+
/* hash for amp access */
u16 amp_hash[32];
int num_amp_entries;
- struct hda_amp_info amp_info[128]; /* big enough? */
+ int amp_info_size;
+ struct hda_amp_info *amp_info;
struct semaphore spdif_mutex;
unsigned int spdif_status; /* IEC958 status bits */
@@ -562,7 +574,7 @@ enum {
/*
* constructors
*/
-int snd_hda_bus_new(snd_card_t *card, const struct hda_bus_template *temp,
+int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp,
struct hda_bus **busp);
int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
struct hda_codec **codecp);
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index d0eb9f2250aa..39edfcfd3abd 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -32,7 +32,8 @@
struct hda_gnode {
hda_nid_t nid; /* NID of this widget */
unsigned short nconns; /* number of input connections */
- hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; /* 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 */
@@ -84,6 +85,8 @@ static void snd_hda_generic_free(struct hda_codec *codec)
/* free all widgets */
list_for_each_safe(p, n, &spec->nid_list) {
struct hda_gnode *node = list_entry(p, struct hda_gnode, list);
+ if (node->conn_list != node->slist)
+ kfree(node->conn_list);
kfree(node);
}
kfree(spec);
@@ -97,18 +100,32 @@ static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_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;
- nconns = snd_hda_get_connections(codec, nid, node->conn_list, HDA_MAX_CONNECTIONS);
+ nconns = snd_hda_get_connections(codec, nid, conn_list,
+ HDA_MAX_CONNECTIONS);
if (nconns < 0) {
kfree(node);
return nconns;
}
+ if (nconns <= ARRAY_SIZE(node->slist))
+ node->conn_list = node->slist;
+ else {
+ node->conn_list = kmalloc(sizeof(hda_nid_t) * nconns,
+ GFP_KERNEL);
+ if (! node->conn_list) {
+ snd_printk(KERN_ERR "hda-generic: cannot malloc\n");
+ kfree(node);
+ return -ENOMEM;
+ }
+ }
+ memcpy(node->conn_list, conn_list, nconns);
node->nconns = nconns;
- node->wid_caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
+ node->wid_caps = get_wcaps(codec, nid);
node->type = (node->wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
if (node->type == AC_WID_PIN) {
@@ -389,14 +406,14 @@ static int parse_output(struct hda_codec *codec)
*/
/* control callbacks */
-static int capture_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+static int capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct hda_gspec *spec = codec->spec;
return snd_hda_input_mux_info(&spec->input_mux, uinfo);
}
-static int capture_source_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int capture_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct hda_gspec *spec = codec->spec;
@@ -405,7 +422,7 @@ static int capture_source_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *uc
return 0;
}
-static int capture_source_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int capture_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct hda_gspec *spec = codec->spec;
@@ -617,7 +634,7 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
char name[32];
int err;
int created = 0;
- snd_kcontrol_new_t knew;
+ struct snd_kcontrol_new knew;
if (type)
sprintf(name, "%s %s Switch", type, dir_sfx);
@@ -625,14 +642,14 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
sprintf(name, "%s Switch", dir_sfx);
if ((node->wid_caps & AC_WCAP_IN_AMP) &&
(node->amp_in_caps & AC_AMPCAP_MUTE)) {
- knew = (snd_kcontrol_new_t)HDA_CODEC_MUTE(name, node->nid, index, HDA_INPUT);
+ knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, index, HDA_INPUT);
snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
return err;
created = 1;
} else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
(node->amp_out_caps & AC_AMPCAP_MUTE)) {
- knew = (snd_kcontrol_new_t)HDA_CODEC_MUTE(name, node->nid, 0, HDA_OUTPUT);
+ knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, 0, HDA_OUTPUT);
snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
return err;
@@ -645,14 +662,14 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
sprintf(name, "%s Volume", dir_sfx);
if ((node->wid_caps & AC_WCAP_IN_AMP) &&
(node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) {
- knew = (snd_kcontrol_new_t)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT);
+ knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT);
snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
return err;
created = 1;
} else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
(node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) {
- knew = (snd_kcontrol_new_t)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT);
+ knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT);
snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
return err;
@@ -667,7 +684,7 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
*/
static int check_existing_control(struct hda_codec *codec, const char *type, const char *dir)
{
- snd_ctl_elem_id_t id;
+ struct snd_ctl_elem_id id;
memset(&id, 0, sizeof(id));
sprintf(id.name, "%s %s Volume", type, dir);
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
@@ -710,7 +727,7 @@ static int build_input_controls(struct hda_codec *codec)
/* create input MUX if multiple sources are available */
if (spec->input_mux.num_items > 1) {
- static snd_kcontrol_new_t cap_sel = {
+ static struct snd_kcontrol_new cap_sel = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.info = capture_source_info,
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index ed525c03c996..a983deba4025 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -37,6 +37,7 @@
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
@@ -51,6 +52,7 @@ static int index = SNDRV_DEFAULT_IDX1;
static char *id = SNDRV_DEFAULT_STR1;
static char *model;
static int position_fix;
+static int probe_mask = -1;
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
@@ -60,6 +62,9 @@ module_param(model, charp, 0444);
MODULE_PARM_DESC(model, "Use the given board model.");
module_param(position_fix, int, 0444);
MODULE_PARM_DESC(position_fix, "Fix DMA pointer (0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size).");
+module_param(probe_mask, int, 0444);
+MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1).");
+
/* just for backward compatibility */
static int enable;
@@ -237,11 +242,7 @@ enum {
/*
*/
-typedef struct snd_azx azx_t;
-typedef struct snd_azx_rb azx_rb_t;
-typedef struct snd_azx_dev azx_dev_t;
-
-struct snd_azx_dev {
+struct azx_dev {
u32 *bdl; /* virtual address of the BDL */
dma_addr_t bdl_addr; /* physical address of the BDL */
volatile u32 *posbuf; /* position buffer pointer */
@@ -257,7 +258,7 @@ struct snd_azx_dev {
u32 sd_int_sta_mask; /* stream int status mask */
/* pcm support */
- snd_pcm_substream_t *substream; /* assigned substream, set in PCM open */
+ struct snd_pcm_substream *substream; /* assigned substream, set in PCM open */
unsigned int format_val; /* format value to be set in the controller and the codec */
unsigned char stream_tag; /* assigned stream */
unsigned char index; /* stream index */
@@ -268,7 +269,7 @@ struct snd_azx_dev {
};
/* CORB/RIRB */
-struct snd_azx_rb {
+struct azx_rb {
u32 *buf; /* CORB/RIRB buffer
* Each CORB entry is 4byte, RIRB is 8byte
*/
@@ -279,8 +280,8 @@ struct snd_azx_rb {
u32 res; /* last read value */
};
-struct snd_azx {
- snd_card_t *card;
+struct azx {
+ struct snd_card *card;
struct pci_dev *pci;
/* chip type specific */
@@ -301,19 +302,19 @@ struct snd_azx {
struct semaphore open_mutex;
/* streams (x num_streams) */
- azx_dev_t *azx_dev;
+ struct azx_dev *azx_dev;
/* PCM */
unsigned int pcm_devs;
- snd_pcm_t *pcm[AZX_MAX_PCMS];
+ struct snd_pcm *pcm[AZX_MAX_PCMS];
/* HD codec */
unsigned short codec_mask;
struct hda_bus *bus;
/* CORB/RIRB */
- azx_rb_t corb;
- azx_rb_t rirb;
+ struct azx_rb corb;
+ struct azx_rb rirb;
/* BDL, CORB/RIRB and position buffers */
struct snd_dma_buffer bdl;
@@ -374,7 +375,7 @@ static char *driver_short_names[] __devinitdata = {
readb((dev)->sd_addr + ICH6_REG_##reg)
/* for pcm support */
-#define get_azx_dev(substream) (azx_dev_t*)(substream->runtime->private_data)
+#define get_azx_dev(substream) (substream->runtime->private_data)
/* Get the upper 32bit of the given dma_addr_t
* Compiler should optimize and eliminate the code if dma_addr_t is 32bit
@@ -390,7 +391,7 @@ static char *driver_short_names[] __devinitdata = {
/*
* CORB / RIRB interface
*/
-static int azx_alloc_cmd_io(azx_t *chip)
+static int azx_alloc_cmd_io(struct azx *chip)
{
int err;
@@ -404,7 +405,7 @@ static int azx_alloc_cmd_io(azx_t *chip)
return 0;
}
-static void azx_init_cmd_io(azx_t *chip)
+static void azx_init_cmd_io(struct azx *chip)
{
/* CORB set up */
chip->corb.addr = chip->rb.addr;
@@ -442,7 +443,7 @@ static void azx_init_cmd_io(azx_t *chip)
chip->rirb.rp = chip->rirb.cmds = 0;
}
-static void azx_free_cmd_io(azx_t *chip)
+static void azx_free_cmd_io(struct azx *chip)
{
/* disable ringbuffer DMAs */
azx_writeb(chip, RIRBCTL, 0);
@@ -453,7 +454,7 @@ static void azx_free_cmd_io(azx_t *chip)
static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
unsigned int verb, unsigned int para)
{
- azx_t *chip = codec->bus->private_data;
+ struct azx *chip = codec->bus->private_data;
unsigned int wp;
u32 val;
@@ -480,7 +481,7 @@ static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
#define ICH6_RIRB_EX_UNSOL_EV (1<<4)
/* retrieve RIRB entry - called from interrupt handler */
-static void azx_update_rirb(azx_t *chip)
+static void azx_update_rirb(struct azx *chip)
{
unsigned int rp, wp;
u32 res, res_ex;
@@ -509,12 +510,14 @@ static void azx_update_rirb(azx_t *chip)
/* receive a response */
static unsigned int azx_get_response(struct hda_codec *codec)
{
- azx_t *chip = codec->bus->private_data;
+ struct azx *chip = codec->bus->private_data;
int timeout = 50;
while (chip->rirb.cmds) {
if (! --timeout) {
- snd_printk(KERN_ERR "azx_get_response timeout\n");
+ if (printk_ratelimit())
+ snd_printk(KERN_ERR
+ "azx_get_response timeout\n");
chip->rirb.rp = azx_readb(chip, RIRBWP);
chip->rirb.cmds = 0;
return -1;
@@ -543,7 +546,7 @@ static unsigned int azx_get_response(struct hda_codec *codec)
static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
unsigned int verb, unsigned int para)
{
- azx_t *chip = codec->bus->private_data;
+ struct azx *chip = codec->bus->private_data;
u32 val;
int timeout = 50;
@@ -571,7 +574,7 @@ static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
/* receive a response */
static unsigned int azx_get_response(struct hda_codec *codec)
{
- azx_t *chip = codec->bus->private_data;
+ struct azx *chip = codec->bus->private_data;
int timeout = 50;
while (timeout--) {
@@ -589,7 +592,7 @@ static unsigned int azx_get_response(struct hda_codec *codec)
#endif /* USE_CORB_RIRB */
/* reset codec link */
-static int azx_reset(azx_t *chip)
+static int azx_reset(struct azx *chip)
{
int count;
@@ -639,7 +642,7 @@ static int azx_reset(azx_t *chip)
*/
/* enable interrupts */
-static void azx_int_enable(azx_t *chip)
+static void azx_int_enable(struct azx *chip)
{
/* enable controller CIE and GIE */
azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) |
@@ -647,13 +650,13 @@ static void azx_int_enable(azx_t *chip)
}
/* disable interrupts */
-static void azx_int_disable(azx_t *chip)
+static void azx_int_disable(struct azx *chip)
{
int i;
/* disable interrupts in stream descriptor */
for (i = 0; i < chip->num_streams; i++) {
- azx_dev_t *azx_dev = &chip->azx_dev[i];
+ struct azx_dev *azx_dev = &chip->azx_dev[i];
azx_sd_writeb(azx_dev, SD_CTL,
azx_sd_readb(azx_dev, SD_CTL) & ~SD_INT_MASK);
}
@@ -667,13 +670,13 @@ static void azx_int_disable(azx_t *chip)
}
/* clear interrupts */
-static void azx_int_clear(azx_t *chip)
+static void azx_int_clear(struct azx *chip)
{
int i;
/* clear stream status */
for (i = 0; i < chip->num_streams; i++) {
- azx_dev_t *azx_dev = &chip->azx_dev[i];
+ struct azx_dev *azx_dev = &chip->azx_dev[i];
azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
}
@@ -688,7 +691,7 @@ static void azx_int_clear(azx_t *chip)
}
/* start a stream */
-static void azx_stream_start(azx_t *chip, azx_dev_t *azx_dev)
+static void azx_stream_start(struct azx *chip, struct azx_dev *azx_dev)
{
/* enable SIE */
azx_writeb(chip, INTCTL,
@@ -699,7 +702,7 @@ static void azx_stream_start(azx_t *chip, azx_dev_t *azx_dev)
}
/* stop a stream */
-static void azx_stream_stop(azx_t *chip, azx_dev_t *azx_dev)
+static void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev)
{
/* stop DMA */
azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) &
@@ -714,7 +717,7 @@ static void azx_stream_stop(azx_t *chip, azx_dev_t *azx_dev)
/*
* initialize the chip
*/
-static void azx_init_chip(azx_t *chip)
+static void azx_init_chip(struct azx *chip)
{
unsigned char reg;
@@ -762,8 +765,8 @@ static void azx_init_chip(azx_t *chip)
*/
static irqreturn_t azx_interrupt(int irq, void* dev_id, struct pt_regs *regs)
{
- azx_t *chip = dev_id;
- azx_dev_t *azx_dev;
+ struct azx *chip = dev_id;
+ struct azx_dev *azx_dev;
u32 status;
int i;
@@ -811,7 +814,7 @@ static irqreturn_t azx_interrupt(int irq, void* dev_id, struct pt_regs *regs)
/*
* set up BDL entries
*/
-static void azx_setup_periods(azx_dev_t *azx_dev)
+static void azx_setup_periods(struct azx_dev *azx_dev)
{
u32 *bdl = azx_dev->bdl;
dma_addr_t dma_addr = azx_dev->substream->runtime->dma_addr;
@@ -840,7 +843,7 @@ static void azx_setup_periods(azx_dev_t *azx_dev)
/*
* set up the SD for streaming
*/
-static int azx_setup_controller(azx_t *chip, azx_dev_t *azx_dev)
+static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
{
unsigned char val;
int timeout;
@@ -900,7 +903,7 @@ static int azx_setup_controller(azx_t *chip, azx_dev_t *azx_dev)
* Codec initialization
*/
-static int __devinit azx_codec_create(azx_t *chip, const char *model)
+static int __devinit azx_codec_create(struct azx *chip, const char *model)
{
struct hda_bus_template bus_temp;
int c, codecs, err;
@@ -917,7 +920,7 @@ static int __devinit azx_codec_create(azx_t *chip, const char *model)
codecs = 0;
for (c = 0; c < AZX_MAX_CODECS; c++) {
- if (chip->codec_mask & (1 << c)) {
+ if ((chip->codec_mask & (1 << c)) & probe_mask) {
err = snd_hda_codec_new(chip->bus, c, NULL);
if (err < 0)
continue;
@@ -938,7 +941,7 @@ static int __devinit azx_codec_create(azx_t *chip, const char *model)
*/
/* assign a stream for the PCM */
-static inline azx_dev_t *azx_assign_device(azx_t *chip, int stream)
+static inline struct azx_dev *azx_assign_device(struct azx *chip, int stream)
{
int dev, i, nums;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -957,12 +960,12 @@ static inline azx_dev_t *azx_assign_device(azx_t *chip, int stream)
}
/* release the assigned stream */
-static inline void azx_release_device(azx_dev_t *azx_dev)
+static inline void azx_release_device(struct azx_dev *azx_dev)
{
azx_dev->opened = 0;
}
-static snd_pcm_hardware_t azx_pcm_hw = {
+static struct snd_pcm_hardware azx_pcm_hw = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -983,18 +986,18 @@ static snd_pcm_hardware_t azx_pcm_hw = {
};
struct azx_pcm {
- azx_t *chip;
+ struct azx *chip;
struct hda_codec *codec;
struct hda_pcm_stream *hinfo[2];
};
-static int azx_pcm_open(snd_pcm_substream_t *substream)
+static int azx_pcm_open(struct snd_pcm_substream *substream)
{
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
- azx_t *chip = apcm->chip;
- azx_dev_t *azx_dev;
- snd_pcm_runtime_t *runtime = substream->runtime;
+ struct azx *chip = apcm->chip;
+ struct azx_dev *azx_dev;
+ struct snd_pcm_runtime *runtime = substream->runtime;
unsigned long flags;
int err;
@@ -1026,12 +1029,12 @@ static int azx_pcm_open(snd_pcm_substream_t *substream)
return 0;
}
-static int azx_pcm_close(snd_pcm_substream_t *substream)
+static int azx_pcm_close(struct snd_pcm_substream *substream)
{
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
- azx_t *chip = apcm->chip;
- azx_dev_t *azx_dev = get_azx_dev(substream);
+ struct azx *chip = apcm->chip;
+ struct azx_dev *azx_dev = get_azx_dev(substream);
unsigned long flags;
down(&chip->open_mutex);
@@ -1045,15 +1048,15 @@ static int azx_pcm_close(snd_pcm_substream_t *substream)
return 0;
}
-static int azx_pcm_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *hw_params)
+static int azx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params)
{
return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
}
-static int azx_pcm_hw_free(snd_pcm_substream_t *substream)
+static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
{
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- azx_dev_t *azx_dev = get_azx_dev(substream);
+ struct azx_dev *azx_dev = get_azx_dev(substream);
struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
/* reset BDL address */
@@ -1066,13 +1069,13 @@ static int azx_pcm_hw_free(snd_pcm_substream_t *substream)
return snd_pcm_lib_free_pages(substream);
}
-static int azx_pcm_prepare(snd_pcm_substream_t *substream)
+static int azx_pcm_prepare(struct snd_pcm_substream *substream)
{
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- azx_t *chip = apcm->chip;
- azx_dev_t *azx_dev = get_azx_dev(substream);
+ struct azx *chip = apcm->chip;
+ struct azx_dev *azx_dev = get_azx_dev(substream);
struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
- snd_pcm_runtime_t *runtime = substream->runtime;
+ struct snd_pcm_runtime *runtime = substream->runtime;
azx_dev->bufsize = snd_pcm_lib_buffer_bytes(substream);
azx_dev->fragsize = snd_pcm_lib_period_bytes(substream);
@@ -1101,11 +1104,11 @@ static int azx_pcm_prepare(snd_pcm_substream_t *substream)
azx_dev->format_val, substream);
}
-static int azx_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
+static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- azx_dev_t *azx_dev = get_azx_dev(substream);
- azx_t *chip = apcm->chip;
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+ struct azx *chip = apcm->chip;
int err = 0;
spin_lock(&chip->reg_lock);
@@ -1136,11 +1139,11 @@ static int azx_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
return err;
}
-static snd_pcm_uframes_t azx_pcm_pointer(snd_pcm_substream_t *substream)
+static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
{
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- azx_t *chip = apcm->chip;
- azx_dev_t *azx_dev = get_azx_dev(substream);
+ struct azx *chip = apcm->chip;
+ struct azx_dev *azx_dev = get_azx_dev(substream);
unsigned int pos;
if (chip->position_fix == POS_FIX_POSBUF) {
@@ -1151,38 +1154,13 @@ static snd_pcm_uframes_t azx_pcm_pointer(snd_pcm_substream_t *substream)
pos = azx_sd_readl(azx_dev, SD_LPIB);
if (chip->position_fix == POS_FIX_FIFO)
pos += azx_dev->fifo_size;
-#if 0 /* disabled temprarily, auto-correction doesn't work well... */
- else if (chip->position_fix == POS_FIX_AUTO && azx_dev->period_updating) {
- /* check the validity of DMA position */
- unsigned int diff = 0;
- azx_dev->last_pos += azx_dev->fragsize;
- if (azx_dev->last_pos > pos)
- diff = azx_dev->last_pos - pos;
- if (azx_dev->last_pos >= azx_dev->bufsize) {
- if (pos < azx_dev->fragsize)
- diff = 0;
- azx_dev->last_pos = 0;
- }
- if (diff > 0 && diff <= azx_dev->fifo_size)
- pos += azx_dev->fifo_size;
- else {
- snd_printdd(KERN_INFO "hda_intel: DMA position fix %d, switching to posbuf\n", diff);
- chip->position_fix = POS_FIX_POSBUF;
- pos = *azx_dev->posbuf;
- }
- azx_dev->period_updating = 0;
- }
-#else
- else if (chip->position_fix == POS_FIX_AUTO)
- pos += azx_dev->fifo_size;
-#endif
}
if (pos >= azx_dev->bufsize)
pos = 0;
return bytes_to_frames(substream->runtime, pos);
}
-static snd_pcm_ops_t azx_pcm_ops = {
+static struct snd_pcm_ops azx_pcm_ops = {
.open = azx_pcm_open,
.close = azx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1193,16 +1171,16 @@ static snd_pcm_ops_t azx_pcm_ops = {
.pointer = azx_pcm_pointer,
};
-static void azx_pcm_free(snd_pcm_t *pcm)
+static void azx_pcm_free(struct snd_pcm *pcm)
{
kfree(pcm->private_data);
}
-static int __devinit create_codec_pcm(azx_t *chip, struct hda_codec *codec,
+static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,
struct hda_pcm *cpcm, int pcm_dev)
{
int err;
- snd_pcm_t *pcm;
+ struct snd_pcm *pcm;
struct azx_pcm *apcm;
snd_assert(cpcm->stream[0].substreams || cpcm->stream[1].substreams, return -EINVAL);
@@ -1236,7 +1214,7 @@ static int __devinit create_codec_pcm(azx_t *chip, struct hda_codec *codec,
return 0;
}
-static int __devinit azx_pcm_create(azx_t *chip)
+static int __devinit azx_pcm_create(struct azx *chip)
{
struct list_head *p;
struct hda_codec *codec;
@@ -1288,7 +1266,7 @@ static int __devinit azx_pcm_create(azx_t *chip)
/*
* mixer creation - all stuff is implemented in hda module
*/
-static int __devinit azx_mixer_create(azx_t *chip)
+static int __devinit azx_mixer_create(struct azx *chip)
{
return snd_hda_build_controls(chip->bus);
}
@@ -1297,7 +1275,7 @@ static int __devinit azx_mixer_create(azx_t *chip)
/*
* initialize SD streams
*/
-static int __devinit azx_init_stream(azx_t *chip)
+static int __devinit azx_init_stream(struct azx *chip)
{
int i;
@@ -1306,7 +1284,7 @@ static int __devinit azx_init_stream(azx_t *chip)
*/
for (i = 0; i < chip->num_streams; i++) {
unsigned int off = sizeof(u32) * (i * AZX_MAX_FRAG * 4);
- azx_dev_t *azx_dev = &chip->azx_dev[i];
+ struct azx_dev *azx_dev = &chip->azx_dev[i];
azx_dev->bdl = (u32 *)(chip->bdl.area + off);
azx_dev->bdl_addr = chip->bdl.addr + off;
azx_dev->posbuf = (volatile u32 *)(chip->posbuf.area + i * 8);
@@ -1327,28 +1305,33 @@ static int __devinit azx_init_stream(azx_t *chip)
/*
* power management
*/
-static int azx_suspend(snd_card_t *card, pm_message_t state)
+static int azx_suspend(struct pci_dev *pci, pm_message_t state)
{
- azx_t *chip = card->pm_private_data;
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct azx *chip = card->private_data;
int i;
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
for (i = 0; i < chip->pcm_devs; i++)
- if (chip->pcm[i])
- snd_pcm_suspend_all(chip->pcm[i]);
+ snd_pcm_suspend_all(chip->pcm[i]);
snd_hda_suspend(chip->bus, state);
azx_free_cmd_io(chip);
- pci_disable_device(chip->pci);
+ pci_disable_device(pci);
+ pci_save_state(pci);
return 0;
}
-static int azx_resume(snd_card_t *card)
+static int azx_resume(struct pci_dev *pci)
{
- azx_t *chip = card->pm_private_data;
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct azx *chip = card->private_data;
- pci_enable_device(chip->pci);
- pci_set_master(chip->pci);
+ pci_restore_state(pci);
+ pci_enable_device(pci);
+ pci_set_master(pci);
azx_init_chip(chip);
snd_hda_resume(chip->bus);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
#endif /* CONFIG_PM */
@@ -1357,7 +1340,7 @@ static int azx_resume(snd_card_t *card)
/*
* destructor
*/
-static int azx_free(azx_t *chip)
+static int azx_free(struct azx *chip)
{
if (chip->initialized) {
int i;
@@ -1399,7 +1382,7 @@ static int azx_free(azx_t *chip)
return 0;
}
-static int azx_dev_free(snd_device_t *device)
+static int azx_dev_free(struct snd_device *device)
{
return azx_free(device->device_data);
}
@@ -1407,13 +1390,13 @@ static int azx_dev_free(snd_device_t *device)
/*
* constructor
*/
-static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci,
- int posfix, int driver_type,
- azx_t **rchip)
+static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
+ int driver_type,
+ struct azx **rchip)
{
- azx_t *chip;
+ struct azx *chip;
int err = 0;
- static snd_device_ops_t ops = {
+ static struct snd_device_ops ops = {
.dev_free = azx_dev_free,
};
@@ -1437,7 +1420,7 @@ static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci,
chip->irq = -1;
chip->driver_type = driver_type;
- chip->position_fix = posfix;
+ chip->position_fix = position_fix ? position_fix : POS_FIX_POSBUF;
#if BITS_PER_LONG != 64
/* Fix up base address on ULI M5461 */
@@ -1545,8 +1528,8 @@ static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci,
static int __devinit azx_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
- snd_card_t *card;
- azx_t *chip;
+ struct snd_card *card;
+ struct azx *chip;
int err = 0;
card = snd_card_new(index, id, THIS_MODULE, 0);
@@ -1555,11 +1538,12 @@ static int __devinit azx_probe(struct pci_dev *pci, const struct pci_device_id *
return -ENOMEM;
}
- if ((err = azx_create(card, pci, position_fix, pci_id->driver_data,
+ if ((err = azx_create(card, pci, pci_id->driver_data,
&chip)) < 0) {
snd_card_free(card);
return err;
}
+ card->private_data = chip;
/* create codec instances */
if ((err = azx_codec_create(chip, model)) < 0) {
@@ -1579,7 +1563,6 @@ static int __devinit azx_probe(struct pci_dev *pci, const struct pci_device_id *
return err;
}
- snd_card_set_pm_callback(card, azx_suspend, azx_resume, chip);
snd_card_set_dev(card, &pci->dev);
if ((err = snd_card_register(card)) < 0) {
@@ -1619,7 +1602,10 @@ static struct pci_driver driver = {
.id_table = azx_ids,
.probe = azx_probe,
.remove = __devexit_p(azx_remove),
- SND_PCI_PM_CALLBACKS
+#ifdef CONFIG_PM
+ .suspend = azx_suspend,
+ .resume = azx_resume,
+#endif
};
static int __init alsa_card_azx_init(void)
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index f51a56f813c8..c82d2a72d13e 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -60,12 +60,12 @@
#define HDA_CODEC_MUTE(xname, nid, xindex, direction) \
HDA_CODEC_MUTE_MONO(xname, nid, 3, xindex, direction)
-int snd_hda_mixer_amp_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo);
-int snd_hda_mixer_amp_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol);
-int snd_hda_mixer_amp_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol);
-int snd_hda_mixer_amp_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo);
-int snd_hda_mixer_amp_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol);
-int snd_hda_mixer_amp_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol);
+int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo);
+int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo);
+int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
/* mono switch binding multiple inputs */
#define HDA_BIND_MUTE_MONO(xname, nid, channel, indices, direction) \
@@ -78,8 +78,8 @@ int snd_hda_mixer_amp_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
/* stereo switch binding multiple inputs */
#define HDA_BIND_MUTE(xname,nid,indices,dir) HDA_BIND_MUTE_MONO(xname,nid,3,indices,dir)
-int snd_hda_mixer_bind_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol);
-int snd_hda_mixer_bind_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol);
+int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid);
int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid);
@@ -87,7 +87,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid);
/*
* input MUX helper
*/
-#define HDA_MAX_NUM_INPUTS 8
+#define HDA_MAX_NUM_INPUTS 16
struct hda_input_mux_item {
const char *label;
unsigned int index;
@@ -97,12 +97,29 @@ struct hda_input_mux {
struct hda_input_mux_item items[HDA_MAX_NUM_INPUTS];
};
-int snd_hda_input_mux_info(const struct hda_input_mux *imux, snd_ctl_elem_info_t *uinfo);
+int snd_hda_input_mux_info(const struct hda_input_mux *imux, struct snd_ctl_elem_info *uinfo);
int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *imux,
- snd_ctl_elem_value_t *ucontrol, hda_nid_t nid,
+ struct snd_ctl_elem_value *ucontrol, hda_nid_t nid,
unsigned int *cur_val);
/*
+ * Channel mode helper
+ */
+struct hda_channel_mode {
+ int channels;
+ const struct hda_verb *sequence;
+};
+
+int snd_hda_ch_mode_info(struct hda_codec *codec, struct snd_ctl_elem_info *uinfo,
+ const struct hda_channel_mode *chmode, int num_chmodes);
+int snd_hda_ch_mode_get(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol,
+ const struct hda_channel_mode *chmode, int num_chmodes,
+ int max_channels);
+int snd_hda_ch_mode_put(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol,
+ const struct hda_channel_mode *chmode, int num_chmodes,
+ int *max_channelsp);
+
+/*
* Multi-channel / digital-out PCM helper
*/
@@ -121,11 +138,11 @@ struct hda_multi_out {
int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout);
int snd_hda_multi_out_dig_close(struct hda_codec *codec, struct hda_multi_out *mout);
int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out *mout,
- snd_pcm_substream_t *substream);
+ struct snd_pcm_substream *substream);
int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_out *mout,
unsigned int stream_tag,
unsigned int format,
- snd_pcm_substream_t *substream);
+ struct snd_pcm_substream *substream);
int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_out *mout);
/*
@@ -153,13 +170,13 @@ struct hda_board_config {
};
int snd_hda_check_board_config(struct hda_codec *codec, const struct hda_board_config *tbl);
-int snd_hda_add_new_ctls(struct hda_codec *codec, snd_kcontrol_new_t *knew);
+int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew);
/*
* power management
*/
#ifdef CONFIG_PM
-int snd_hda_resume_ctls(struct hda_codec *codec, snd_kcontrol_new_t *knew);
+int snd_hda_resume_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew);
int snd_hda_resume_spdif_out(struct hda_codec *codec);
int snd_hda_resume_spdif_in(struct hda_codec *codec);
#endif
@@ -194,9 +211,12 @@ enum {
AUTO_PIN_LAST
};
+extern const char *auto_pin_cfg_labels[AUTO_PIN_LAST];
+
struct auto_pin_cfg {
int line_outs;
- hda_nid_t line_out_pins[4]; /* sorted in the order of Front/Surr/CLFE/Side */
+ hda_nid_t line_out_pins[5]; /* sorted in the order of Front/Surr/CLFE/Side */
+ hda_nid_t speaker_pin;
hda_nid_t hp_pin;
hda_nid_t input_pins[AUTO_PIN_LAST];
hda_nid_t dig_out_pin;
@@ -209,6 +229,33 @@ struct auto_pin_cfg {
#define get_defcfg_sequence(cfg) (cfg & AC_DEFCFG_SEQUENCE)
#define get_defcfg_device(cfg) ((cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT)
-int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *cfg);
+int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *cfg,
+ hda_nid_t *ignore_nids);
+
+/* amp values */
+#define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8))
+#define AMP_IN_UNMUTE(idx) (0x7000 | ((idx)<<8))
+#define AMP_OUT_MUTE 0xb080
+#define AMP_OUT_UNMUTE 0xb000
+#define AMP_OUT_ZERO 0xb000
+/* pinctl values */
+#define PIN_IN 0x20
+#define PIN_VREF80 0x24
+#define PIN_VREF50 0x21
+#define PIN_OUT 0x40
+#define PIN_HP 0xc0
+#define PIN_HP_AMP 0x80
+
+/*
+ * get widget capabilities
+ */
+static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid)
+{
+ if (nid < codec->start_nid ||
+ nid >= codec->start_nid + codec->num_nodes)
+ return snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
+ return codec->wcaps[nid - codec->start_nid];
+}
+
#endif /* __SOUND_HDA_LOCAL_H */
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index 39ddf1cd9019..ca514a6a5875 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -26,6 +26,7 @@
#include <linux/pci.h>
#include <sound/core.h>
#include "hda_codec.h"
+#include "hda_local.h"
static const char *get_wid_type_name(unsigned int wid_value)
{
@@ -47,7 +48,7 @@ static const char *get_wid_type_name(unsigned int wid_value)
return "UNKOWN Widget";
}
-static void print_amp_caps(snd_info_buffer_t *buffer,
+static void print_amp_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid, int dir)
{
unsigned int caps;
@@ -66,7 +67,7 @@ static void print_amp_caps(snd_info_buffer_t *buffer,
(caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT);
}
-static void print_amp_vals(snd_info_buffer_t *buffer,
+static void print_amp_vals(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid,
int dir, int stereo, int indices)
{
@@ -91,7 +92,7 @@ static void print_amp_vals(snd_info_buffer_t *buffer,
snd_iprintf(buffer, "\n");
}
-static void print_pcm_caps(snd_info_buffer_t *buffer,
+static void print_pcm_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
unsigned int pcm = snd_hda_param_read(codec, nid, AC_PAR_PCM);
@@ -160,7 +161,7 @@ static const char *get_jack_color(u32 cfg)
return "UNKNOWN";
}
-static void print_pin_caps(snd_info_buffer_t *buffer,
+static void print_pin_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
@@ -194,7 +195,7 @@ static void print_pin_caps(snd_info_buffer_t *buffer,
}
-static void print_codec_info(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+static void print_codec_info(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
struct hda_codec *codec = entry->private_data;
char buf[32];
@@ -309,7 +310,7 @@ static void print_codec_info(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
int snd_hda_codec_proc_new(struct hda_codec *codec)
{
char name[32];
- snd_info_entry_t *entry;
+ struct snd_info_entry *entry;
int err;
snprintf(name, sizeof(name), "codec#%d", codec->addr);
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index d7d636decef8..1ada1b075c9a 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -1,5 +1,5 @@
/*
- * HD audio interface patch for AD1981HD, AD1983, AD1986A
+ * HD audio interface patch for AD1981HD, AD1983, AD1986A, AD1988
*
* Copyright (c) 2005 Takashi Iwai <tiwai@suse.de>
*
@@ -28,10 +28,10 @@
#include "hda_local.h"
struct ad198x_spec {
- snd_kcontrol_new_t *mixers[5];
+ struct snd_kcontrol_new *mixers[5];
int num_mixers;
- const struct hda_verb *init_verbs[3]; /* initialization verbs
+ const struct hda_verb *init_verbs[5]; /* initialization verbs
* don't forget NULL termination!
*/
unsigned int num_init_verbs;
@@ -41,6 +41,7 @@ struct ad198x_spec {
* max_channels, dacs must be set
* dig_out_nid and hp_nid are optional
*/
+ unsigned int cur_eapd;
/* capture */
unsigned int num_adc_nids;
@@ -49,10 +50,11 @@ struct ad198x_spec {
/* capture source */
const struct hda_input_mux *input_mux;
+ hda_nid_t *capsrc_nids;
unsigned int cur_mux[3];
/* channel model */
- const struct alc_channel_mode *channel_mode;
+ const struct hda_channel_mode *channel_mode;
int num_channel_mode;
/* PCM information */
@@ -60,12 +62,19 @@ struct ad198x_spec {
struct semaphore amp_mutex; /* PCM volume/mute control mutex */
unsigned int spdif_route;
+
+ /* dynamic controls, init_verbs and input_mux */
+ struct auto_pin_cfg autocfg;
+ unsigned int num_kctl_alloc, num_kctl_used;
+ struct snd_kcontrol_new *kctl_alloc;
+ struct hda_input_mux private_imux;
+ hda_nid_t private_dac_nids[4];
};
/*
* input MUX handling (common part)
*/
-static int ad198x_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
@@ -73,7 +82,7 @@ static int ad198x_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *u
return snd_hda_input_mux_info(spec->input_mux, uinfo);
}
-static int ad198x_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
@@ -83,14 +92,15 @@ static int ad198x_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u
return 0;
}
-static int ad198x_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
- spec->adc_nids[adc_idx], &spec->cur_mux[adc_idx]);
+ spec->capsrc_nids[adc_idx],
+ &spec->cur_mux[adc_idx]);
}
/*
@@ -135,7 +145,7 @@ static int ad198x_build_controls(struct hda_codec *codec)
*/
static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct ad198x_spec *spec = codec->spec;
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
@@ -145,7 +155,7 @@ static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct ad198x_spec *spec = codec->spec;
return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
@@ -154,7 +164,7 @@ static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct ad198x_spec *spec = codec->spec;
return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
@@ -165,7 +175,7 @@ static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
*/
static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct ad198x_spec *spec = codec->spec;
return snd_hda_multi_out_dig_open(codec, &spec->multiout);
@@ -173,7 +183,7 @@ static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct ad198x_spec *spec = codec->spec;
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
@@ -186,7 +196,7 @@ static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct ad198x_spec *spec = codec->spec;
snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
@@ -196,7 +206,7 @@ static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct ad198x_spec *spec = codec->spec;
snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
@@ -281,6 +291,14 @@ static int ad198x_build_pcms(struct hda_codec *codec)
static void ad198x_free(struct hda_codec *codec)
{
+ struct ad198x_spec *spec = codec->spec;
+ unsigned int i;
+
+ if (spec->kctl_alloc) {
+ for (i = 0; i < spec->num_kctl_used; i++)
+ kfree(spec->kctl_alloc[i].name);
+ kfree(spec->kctl_alloc);
+ }
kfree(codec->spec);
}
@@ -348,7 +366,7 @@ static struct hda_input_mux ad1986a_capture_source = {
#define ad1986a_pcm_amp_vol_info snd_hda_mixer_amp_volume_info
-static int ad1986a_pcm_amp_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int ad1986a_pcm_amp_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *ad = codec->spec;
@@ -359,7 +377,7 @@ static int ad1986a_pcm_amp_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_
return 0;
}
-static int ad1986a_pcm_amp_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int ad1986a_pcm_amp_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *ad = codec->spec;
@@ -377,7 +395,7 @@ static int ad1986a_pcm_amp_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_
#define ad1986a_pcm_amp_sw_info snd_hda_mixer_amp_switch_info
-static int ad1986a_pcm_amp_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int ad1986a_pcm_amp_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *ad = codec->spec;
@@ -388,7 +406,7 @@ static int ad1986a_pcm_amp_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
return 0;
}
-static int ad1986a_pcm_amp_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int ad1986a_pcm_amp_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *ad = codec->spec;
@@ -407,7 +425,7 @@ static int ad1986a_pcm_amp_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
/*
* mixers
*/
-static snd_kcontrol_new_t ad1986a_mixers[] = {
+static struct snd_kcontrol_new ad1986a_mixers[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",
@@ -535,6 +553,7 @@ static int patch_ad1986a(struct hda_codec *codec)
spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
spec->num_adc_nids = 1;
spec->adc_nids = ad1986a_adc_nids;
+ spec->capsrc_nids = ad1986a_adc_nids;
spec->input_mux = &ad1986a_capture_source;
spec->num_mixers = 1;
spec->mixers[0] = ad1986a_mixers;
@@ -570,7 +589,7 @@ static struct hda_input_mux ad1983_capture_source = {
/*
* SPDIF playback route
*/
-static int ad1983_spdif_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "PCM", "ADC" };
@@ -583,7 +602,7 @@ static int ad1983_spdif_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t
return 0;
}
-static int ad1983_spdif_route_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
@@ -592,7 +611,7 @@ static int ad1983_spdif_route_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
return 0;
}
-static int ad1983_spdif_route_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
@@ -606,7 +625,7 @@ static int ad1983_spdif_route_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
return 0;
}
-static snd_kcontrol_new_t ad1983_mixers[] = {
+static struct snd_kcontrol_new ad1983_mixers[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
@@ -698,6 +717,7 @@ static int patch_ad1983(struct hda_codec *codec)
spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
spec->num_adc_nids = 1;
spec->adc_nids = ad1983_adc_nids;
+ spec->capsrc_nids = ad1983_adc_nids;
spec->input_mux = &ad1983_capture_source;
spec->num_mixers = 1;
spec->mixers[0] = ad1983_mixers;
@@ -736,7 +756,7 @@ static struct hda_input_mux ad1981_capture_source = {
},
};
-static snd_kcontrol_new_t ad1981_mixers[] = {
+static struct snd_kcontrol_new ad1981_mixers[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
@@ -843,6 +863,7 @@ static int patch_ad1981(struct hda_codec *codec)
spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
spec->num_adc_nids = 1;
spec->adc_nids = ad1981_adc_nids;
+ spec->capsrc_nids = ad1981_adc_nids;
spec->input_mux = &ad1981_capture_source;
spec->num_mixers = 1;
spec->mixers[0] = ad1981_mixers;
@@ -857,11 +878,1278 @@ static int patch_ad1981(struct hda_codec *codec)
/*
+ * AD1988
+ *
+ * Output pins and routes
+ *
+ * Pin Mix Sel DAC (*)
+ * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
+ * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
+ * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
+ * port-D 0x12 (mute/hp) <- 0x29 <- 04
+ * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
+ * port-F 0x16 (mute) <- 0x2a <- 06
+ * port-G 0x24 (mute) <- 0x27 <- 05
+ * port-H 0x25 (mute) <- 0x28 <- 0a
+ * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
+ *
+ * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
+ * (*) DAC2/3/4 are swapped to DAC3/4/2 on AD198A rev.2 due to a h/w bug.
+ *
+ * Input pins and routes
+ *
+ * pin boost mix input # / adc input #
+ * port-A 0x11 -> 0x38 -> mix 2, ADC 0
+ * port-B 0x14 -> 0x39 -> mix 0, ADC 1
+ * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
+ * port-D 0x12 -> 0x3d -> mix 3, ADC 8
+ * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
+ * port-F 0x16 -> 0x3b -> mix 5, ADC 3
+ * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
+ * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
+ *
+ *
+ * DAC assignment
+ * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
+ * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
+ *
+ * Inputs of Analog Mix (0x20)
+ * 0:Port-B (front mic)
+ * 1:Port-C/G/H (line-in)
+ * 2:Port-A
+ * 3:Port-D (line-in/2)
+ * 4:Port-E/G/H (mic-in)
+ * 5:Port-F (mic2-in)
+ * 6:CD
+ * 7:Beep
+ *
+ * ADC selection
+ * 0:Port-A
+ * 1:Port-B (front mic-in)
+ * 2:Port-C (line-in)
+ * 3:Port-F (mic2-in)
+ * 4:Port-E (mic-in)
+ * 5:CD
+ * 6:Port-G
+ * 7:Port-H
+ * 8:Port-D (line-in/2)
+ * 9:Mix
+ *
+ * Proposed pin assignments by the datasheet
+ *
+ * 6-stack
+ * Port-A front headphone
+ * B front mic-in
+ * C rear line-in
+ * D rear front-out
+ * E rear mic-in
+ * F rear surround
+ * G rear CLFE
+ * H rear side
+ *
+ * 3-stack
+ * Port-A front headphone
+ * B front mic
+ * C rear line-in/surround
+ * D rear front-out
+ * E rear mic-in/CLFE
+ *
+ * laptop
+ * Port-A headphone
+ * B mic-in
+ * C docking station
+ * D internal speaker (with EAPD)
+ * E/F quad mic array
+ */
+
+
+/* models */
+enum {
+ AD1988_6STACK,
+ AD1988_6STACK_DIG,
+ AD1988_3STACK,
+ AD1988_3STACK_DIG,
+ AD1988_LAPTOP,
+ AD1988_LAPTOP_DIG,
+ AD1988_AUTO,
+ AD1988_MODEL_LAST,
+};
+
+/* reivision id to check workarounds */
+#define AD1988A_REV2 0x100200
+
+
+/*
+ * mixers
+ */
+
+static hda_nid_t ad1988_6stack_dac_nids[4] = {
+ 0x04, 0x06, 0x05, 0x0a
+};
+
+static hda_nid_t ad1988_3stack_dac_nids[3] = {
+ 0x04, 0x05, 0x0a
+};
+
+/* for AD1988A revision-2, DAC2-4 are swapped */
+static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
+ 0x04, 0x05, 0x0a, 0x06
+};
+
+static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
+ 0x04, 0x0a, 0x06
+};
+
+static hda_nid_t ad1988_adc_nids[3] = {
+ 0x08, 0x09, 0x0f
+};
+
+static hda_nid_t ad1988_capsrc_nids[3] = {
+ 0x0c, 0x0d, 0x0e
+};
+
+#define AD1988_SPDIF_OUT 0x02
+#define AD1988_SPDIF_IN 0x07
+
+static struct hda_input_mux ad1988_6stack_capture_source = {
+ .num_items = 5,
+ .items = {
+ { "Front Mic", 0x0 },
+ { "Line", 0x1 },
+ { "Mic", 0x4 },
+ { "CD", 0x5 },
+ { "Mix", 0x9 },
+ },
+};
+
+static struct hda_input_mux ad1988_laptop_capture_source = {
+ .num_items = 3,
+ .items = {
+ { "Mic/Line", 0x0 },
+ { "CD", 0x5 },
+ { "Mix", 0x9 },
+ },
+};
+
+/*
+ */
+static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ad198x_spec *spec = codec->spec;
+ return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
+ spec->num_channel_mode);
+}
+
+static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ad198x_spec *spec = codec->spec;
+ return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
+ spec->num_channel_mode, spec->multiout.max_channels);
+}
+
+static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ad198x_spec *spec = codec->spec;
+ return snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
+ spec->num_channel_mode, &spec->multiout.max_channels);
+}
+
+/*
+ * EAPD control
+ */
+static int ad1988_eapd_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int ad1988_eapd_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ad198x_spec *spec = codec->spec;
+ ucontrol->value.enumerated.item[0] = ! spec->cur_eapd;
+ return 0;
+}
+
+static int ad1988_eapd_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ad198x_spec *spec = codec->spec;
+ unsigned int eapd;
+ eapd = ! ucontrol->value.enumerated.item[0];
+ if (eapd == spec->cur_eapd && ! codec->in_resume)
+ return 0;
+ spec->cur_eapd = eapd;
+ snd_hda_codec_write(codec, 0x12 /* port-D */,
+ 0, AC_VERB_SET_EAPD_BTLENABLE,
+ eapd ? 0x02 : 0x00);
+ return 0;
+}
+
+/* 6-stack mode */
+static struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
+};
+
+static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
+};
+
+static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
+ HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
+ HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
+ HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
+ HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
+ HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
+
+ HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+
+ HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
+
+ { } /* end */
+};
+
+/* 3-stack mode */
+static struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
+};
+
+static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
+};
+
+static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
+ HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
+ HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
+ HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
+ HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
+
+ HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+
+ HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Channel Mode",
+ .info = ad198x_ch_mode_info,
+ .get = ad198x_ch_mode_get,
+ .put = ad198x_ch_mode_put,
+ },
+
+ { } /* end */
+};
+
+/* laptop mode */
+static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
+ HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
+ HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
+
+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
+
+ HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+
+ HDA_CODEC_VOLUME("Mic Boost", 0x39, 0x0, HDA_OUTPUT),
+
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "External Amplifier",
+ .info = ad1988_eapd_info,
+ .get = ad1988_eapd_get,
+ .put = ad1988_eapd_put,
+ },
+
+ { } /* end */
+};
+
+/* capture */
+static struct snd_kcontrol_new ad1988_capture_mixers[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ * FIXME: the controls appear in the "playback" view!
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 3,
+ .info = ad198x_mux_enum_info,
+ .get = ad198x_mux_enum_get,
+ .put = ad198x_mux_enum_put,
+ },
+ { } /* end */
+};
+
+static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[] = {
+ "PCM", "ADC1", "ADC2", "ADC3"
+ };
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 4;
+ if (uinfo->value.enumerated.item >= 4)
+ uinfo->value.enumerated.item = 3;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int sel;
+
+ sel = snd_hda_codec_read(codec, 0x02, 0, AC_VERB_GET_CONNECT_SEL, 0);
+ if (sel > 0) {
+ sel = snd_hda_codec_read(codec, 0x0b, 0, AC_VERB_GET_CONNECT_SEL, 0);
+ if (sel <= 3)
+ sel++;
+ else
+ sel = 0;
+ }
+ ucontrol->value.enumerated.item[0] = sel;
+ return 0;
+}
+
+static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int sel;
+ int change;
+
+ sel = snd_hda_codec_read(codec, 0x02, 0, AC_VERB_GET_CONNECT_SEL, 0);
+ if (! ucontrol->value.enumerated.item[0]) {
+ change = sel != 0;
+ if (change)
+ snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CONNECT_SEL, 0);
+ } else {
+ change = sel == 0;
+ if (change)
+ snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CONNECT_SEL, 1);
+ sel = snd_hda_codec_read(codec, 0x0b, 0, AC_VERB_GET_CONNECT_SEL, 0) + 1;
+ change |= sel == ucontrol->value.enumerated.item[0];
+ if (change)
+ snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CONNECT_SEL,
+ ucontrol->value.enumerated.item[0] - 1);
+ }
+ return change;
+}
+
+static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
+ HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Playback Source",
+ .info = ad1988_spdif_playback_source_info,
+ .get = ad1988_spdif_playback_source_get,
+ .put = ad1988_spdif_playback_source_put,
+ },
+ { } /* end */
+};
+
+static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
+ HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
+ { } /* end */
+};
+
+
+/*
+ * initialization verbs
+ */
+
+/*
+ * for 6-stack (+dig)
+ */
+static struct hda_verb ad1988_6stack_init_verbs[] = {
+ /* Front, Surround, CLFE, side DAC; unmute as default */
+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ /* Port-A front headphon path */
+ {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ /* Port-D line-out path */
+ {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ /* Port-F surround path */
+ {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ /* Port-G CLFE path */
+ {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ /* Port-H side path */
+ {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ /* Mono out path */
+ {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
+ {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
+ /* Port-B front mic-in path */
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ /* Port-C line-in path */
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* Port-E mic-in path */
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
+
+ { }
+};
+
+static struct hda_verb ad1988_capture_init_verbs[] = {
+ /* mute analog mix */
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
+ /* select ADCs - front-mic */
+ {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
+ {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
+ {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
+ /* ADCs; muted */
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+ { }
+};
+
+static struct hda_verb ad1988_spdif_init_verbs[] = {
+ /* SPDIF out sel */
+ {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
+ {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
+ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ /* SPDIF out pin */
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x17}, /* 0dB */
+
+ { }
+};
+
+/*
+ * verbs for 3stack (+dig)
+ */
+static struct hda_verb ad1988_3stack_ch2_init[] = {
+ /* set port-C to line-in */
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+ /* set port-E to mic-in */
+ { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+ { } /* end */
+};
+
+static struct hda_verb ad1988_3stack_ch6_init[] = {
+ /* set port-C to surround out */
+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ /* set port-E to CLFE out */
+ { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { } /* end */
+};
+
+static struct hda_channel_mode ad1988_3stack_modes[2] = {
+ { 2, ad1988_3stack_ch2_init },
+ { 6, ad1988_3stack_ch6_init },
+};
+
+static struct hda_verb ad1988_3stack_init_verbs[] = {
+ /* Front, Surround, CLFE, side DAC; unmute as default */
+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ /* Port-A front headphon path */
+ {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ /* Port-D line-out path */
+ {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ /* Mono out path */
+ {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
+ {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
+ /* Port-B front mic-in path */
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ /* Port-C line-in/surround path - 6ch mode as default */
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
+ {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* Port-E mic-in/CLFE path - 6ch mode as default */
+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
+ {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* mute analog mix */
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
+ /* select ADCs - front-mic */
+ {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
+ {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
+ {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
+ /* ADCs; muted */
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ { }
+};
+
+/*
+ * verbs for laptop mode (+dig)
+ */
+static struct hda_verb ad1988_laptop_hp_on[] = {
+ /* unmute port-A and mute port-D */
+ { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { } /* end */
+};
+static struct hda_verb ad1988_laptop_hp_off[] = {
+ /* mute port-A and unmute port-D */
+ { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { } /* end */
+};
+
+#define AD1988_HP_EVENT 0x01
+
+static struct hda_verb ad1988_laptop_init_verbs[] = {
+ /* Front, Surround, CLFE, side DAC; unmute as default */
+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ /* Port-A front headphon path */
+ {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ /* unsolicited event for pin-sense */
+ {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
+ /* Port-D line-out path + EAPD */
+ {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
+ /* Mono out path */
+ {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
+ {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
+ /* Port-B mic-in path */
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ /* Port-C docking station - try to output */
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* mute analog mix */
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
+ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
+ /* select ADCs - mic */
+ {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
+ {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
+ {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
+ /* ADCs; muted */
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ { }
+};
+
+static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ if ((res >> 26) != AD1988_HP_EVENT)
+ return;
+ if (snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) & (1 << 31))
+ snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
+ else
+ snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
+}
+
+
+/*
+ * Automatic parse of I/O pins from the BIOS configuration
+ */
+
+#define NUM_CONTROL_ALLOC 32
+#define NUM_VERB_ALLOC 32
+
+enum {
+ AD_CTL_WIDGET_VOL,
+ AD_CTL_WIDGET_MUTE,
+ AD_CTL_BIND_MUTE,
+};
+static struct snd_kcontrol_new ad1988_control_templates[] = {
+ HDA_CODEC_VOLUME(NULL, 0, 0, 0),
+ HDA_CODEC_MUTE(NULL, 0, 0, 0),
+ HDA_BIND_MUTE(NULL, 0, 0, 0),
+};
+
+/* add dynamic controls */
+static int add_control(struct ad198x_spec *spec, int type, const char *name,
+ unsigned long val)
+{
+ struct snd_kcontrol_new *knew;
+
+ if (spec->num_kctl_used >= spec->num_kctl_alloc) {
+ int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
+
+ knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
+ if (! knew)
+ return -ENOMEM;
+ if (spec->kctl_alloc) {
+ memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
+ kfree(spec->kctl_alloc);
+ }
+ spec->kctl_alloc = knew;
+ spec->num_kctl_alloc = num;
+ }
+
+ knew = &spec->kctl_alloc[spec->num_kctl_used];
+ *knew = ad1988_control_templates[type];
+ knew->name = kstrdup(name, GFP_KERNEL);
+ if (! knew->name)
+ return -ENOMEM;
+ knew->private_value = val;
+ spec->num_kctl_used++;
+ return 0;
+}
+
+#define AD1988_PIN_CD_NID 0x18
+#define AD1988_PIN_BEEP_NID 0x10
+
+static hda_nid_t ad1988_mixer_nids[8] = {
+ /* A B C D E F G H */
+ 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
+};
+
+static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
+{
+ static hda_nid_t idx_to_dac[8] = {
+ /* A B C D E F G H */
+ 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
+ };
+ static hda_nid_t idx_to_dac_rev2[8] = {
+ /* A B C D E F G H */
+ 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
+ };
+ if (codec->revision_id == AD1988A_REV2)
+ return idx_to_dac_rev2[idx];
+ else
+ return idx_to_dac[idx];
+}
+
+static hda_nid_t ad1988_boost_nids[8] = {
+ 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
+};
+
+static int ad1988_pin_idx(hda_nid_t nid)
+{
+ static hda_nid_t ad1988_io_pins[8] = {
+ 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
+ };
+ int i;
+ for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
+ if (ad1988_io_pins[i] == nid)
+ return i;
+ return 0; /* should be -1 */
+}
+
+static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
+{
+ static int loopback_idx[8] = {
+ 2, 0, 1, 3, 4, 5, 1, 4
+ };
+ switch (nid) {
+ case AD1988_PIN_CD_NID:
+ return 6;
+ default:
+ return loopback_idx[ad1988_pin_idx(nid)];
+ }
+}
+
+static int ad1988_pin_to_adc_idx(hda_nid_t nid)
+{
+ static int adc_idx[8] = {
+ 0, 1, 2, 8, 4, 3, 6, 7
+ };
+ switch (nid) {
+ case AD1988_PIN_CD_NID:
+ return 5;
+ default:
+ return adc_idx[ad1988_pin_idx(nid)];
+ }
+}
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
+{
+ struct ad198x_spec *spec = codec->spec;
+ int i, idx;
+
+ spec->multiout.dac_nids = spec->private_dac_nids;
+
+ /* check the pins hardwired to audio widget */
+ for (i = 0; i < cfg->line_outs; i++) {
+ idx = ad1988_pin_idx(cfg->line_out_pins[i]);
+ spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx);
+ }
+ spec->multiout.num_dacs = cfg->line_outs;
+ return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ char name[32];
+ static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
+ hda_nid_t nid;
+ int i, err;
+
+ for (i = 0; i < cfg->line_outs; i++) {
+ hda_nid_t dac = spec->multiout.dac_nids[i];
+ if (! dac)
+ continue;
+ nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
+ if (i == 2) {
+ /* Center/LFE */
+ err = add_control(spec, AD_CTL_WIDGET_VOL,
+ "Center Playback Volume",
+ HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = add_control(spec, AD_CTL_WIDGET_VOL,
+ "LFE Playback Volume",
+ HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = add_control(spec, AD_CTL_BIND_MUTE,
+ "Center Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
+ if (err < 0)
+ return err;
+ err = add_control(spec, AD_CTL_BIND_MUTE,
+ "LFE Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
+ if (err < 0)
+ return err;
+ } else {
+ sprintf(name, "%s Playback Volume", chname[i]);
+ err = add_control(spec, AD_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ sprintf(name, "%s Playback Switch", chname[i]);
+ err = add_control(spec, AD_CTL_BIND_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
+ if (err < 0)
+ return err;
+ }
+ }
+ return 0;
+}
+
+/* add playback controls for speaker and HP outputs */
+static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
+ const char *pfx)
+{
+ struct ad198x_spec *spec = codec->spec;
+ hda_nid_t nid;
+ int idx, err;
+ char name[32];
+
+ if (! pin)
+ return 0;
+
+ idx = ad1988_pin_idx(pin);
+ nid = ad1988_idx_to_dac(codec, idx);
+ if (! spec->multiout.dac_nids[0]) {
+ /* use this as the primary output */
+ spec->multiout.dac_nids[0] = nid;
+ if (! spec->multiout.num_dacs)
+ spec->multiout.num_dacs = 1;
+ } else
+ /* specify the DAC as the extra output */
+ spec->multiout.hp_nid = nid;
+ /* control HP volume/switch on the output mixer amp */
+ sprintf(name, "%s Playback Volume", pfx);
+ if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
+ return err;
+ nid = ad1988_mixer_nids[idx];
+ sprintf(name, "%s Playback Switch", pfx);
+ if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
+ return err;
+ return 0;
+}
+
+/* create input playback/capture controls for the given pin */
+static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
+ const char *ctlname, int boost)
+{
+ char name[32];
+ int err, idx;
+
+ sprintf(name, "%s Playback Volume", ctlname);
+ idx = ad1988_pin_to_loopback_idx(pin);
+ if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
+ return err;
+ sprintf(name, "%s Playback Switch", ctlname);
+ if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
+ return err;
+ if (boost) {
+ hda_nid_t bnid;
+ idx = ad1988_pin_idx(pin);
+ bnid = ad1988_boost_nids[idx];
+ if (bnid) {
+ sprintf(name, "%s Boost", ctlname);
+ return add_control(spec, AD_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
+
+ }
+ }
+ return 0;
+}
+
+/* create playback/capture controls for input pins */
+static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ struct hda_input_mux *imux = &spec->private_imux;
+ int i, err;
+
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ err = new_analog_input(spec, cfg->input_pins[i],
+ auto_pin_cfg_labels[i],
+ i <= AUTO_PIN_FRONT_MIC);
+ if (err < 0)
+ return err;
+ imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
+ imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
+ imux->num_items++;
+ }
+ imux->items[imux->num_items].label = "Mix";
+ imux->items[imux->num_items].index = 9;
+ imux->num_items++;
+
+ if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
+ "Analog Mix Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
+ return err;
+ if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
+ "Analog Mix Playback Switch",
+ HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
+ return err;
+
+ return 0;
+}
+
+static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
+ hda_nid_t nid, int pin_type,
+ int dac_idx)
+{
+ /* set as output */
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+ switch (nid) {
+ case 0x11: /* port-A - DAC 04 */
+ snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
+ break;
+ case 0x14: /* port-B - DAC 06 */
+ snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
+ break;
+ case 0x15: /* port-C - DAC 05 */
+ snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
+ break;
+ case 0x17: /* port-E - DAC 0a */
+ snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
+ break;
+ case 0x13: /* mono - DAC 04 */
+ snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
+ break;
+ }
+}
+
+static void ad1988_auto_init_multi_out(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->autocfg.line_outs; i++) {
+ hda_nid_t nid = spec->autocfg.line_out_pins[i];
+ ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
+ }
+}
+
+static void ad1988_auto_init_extra_out(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec = codec->spec;
+ hda_nid_t pin;
+
+ pin = spec->autocfg.speaker_pin;
+ if (pin) /* connect to front */
+ ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
+ pin = spec->autocfg.hp_pin;
+ if (pin) /* connect to front */
+ ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+}
+
+static void ad1988_auto_init_analog_input(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec = codec->spec;
+ int i, idx;
+
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ hda_nid_t nid = spec->autocfg.input_pins[i];
+ if (! nid)
+ continue;
+ switch (nid) {
+ case 0x15: /* port-C */
+ snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
+ break;
+ case 0x17: /* port-E */
+ snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
+ break;
+ }
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
+ if (nid != AD1988_PIN_CD_NID)
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_MUTE);
+ idx = ad1988_pin_idx(nid);
+ if (ad1988_boost_nids[idx])
+ snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_ZERO);
+ }
+}
+
+/* parse the BIOS configuration and set up the alc_spec */
+/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
+static int ad1988_parse_auto_config(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec = codec->spec;
+ int err;
+
+ if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
+ return err;
+ if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
+ return err;
+ if (! spec->autocfg.line_outs && ! spec->autocfg.speaker_pin &&
+ ! spec->autocfg.hp_pin)
+ return 0; /* can't find valid BIOS pin config */
+ if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
+ (err = ad1988_auto_create_extra_out(codec, spec->autocfg.speaker_pin,
+ "Speaker")) < 0 ||
+ (err = ad1988_auto_create_extra_out(codec, spec->autocfg.speaker_pin,
+ "Headphone")) < 0 ||
+ (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
+ return err;
+
+ spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+ if (spec->autocfg.dig_out_pin)
+ spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
+ if (spec->autocfg.dig_in_pin)
+ spec->dig_in_nid = AD1988_SPDIF_IN;
+
+ if (spec->kctl_alloc)
+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+ spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
+
+ spec->input_mux = &spec->private_imux;
+
+ return 1;
+}
+
+/* init callback for auto-configuration model -- overriding the default init */
+static int ad1988_auto_init(struct hda_codec *codec)
+{
+ ad198x_init(codec);
+ ad1988_auto_init_multi_out(codec);
+ ad1988_auto_init_extra_out(codec);
+ ad1988_auto_init_analog_input(codec);
+ return 0;
+}
+
+
+/*
+ */
+
+static struct hda_board_config ad1988_cfg_tbl[] = {
+ { .modelname = "6stack", .config = AD1988_6STACK },
+ { .modelname = "6stack-dig", .config = AD1988_6STACK_DIG },
+ { .modelname = "3stack", .config = AD1988_3STACK },
+ { .modelname = "3stack-dig", .config = AD1988_3STACK_DIG },
+ { .modelname = "laptop", .config = AD1988_LAPTOP },
+ { .modelname = "laptop-dig", .config = AD1988_LAPTOP_DIG },
+ { .modelname = "auto", .config = AD1988_AUTO },
+ {}
+};
+
+static int patch_ad1988(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec;
+ int board_config;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ init_MUTEX(&spec->amp_mutex);
+ codec->spec = spec;
+
+ if (codec->revision_id == AD1988A_REV2)
+ snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
+
+ board_config = snd_hda_check_board_config(codec, ad1988_cfg_tbl);
+ if (board_config < 0 || board_config >= AD1988_MODEL_LAST) {
+ printk(KERN_INFO "hda_codec: Unknown model for AD1988, trying auto-probe from BIOS...\n");
+ board_config = AD1988_AUTO;
+ }
+
+ if (board_config == AD1988_AUTO) {
+ /* automatic parse from the BIOS config */
+ int err = ad1988_parse_auto_config(codec);
+ if (err < 0) {
+ ad198x_free(codec);
+ return err;
+ } else if (! err) {
+ printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
+ board_config = AD1988_6STACK;
+ }
+ }
+
+ switch (board_config) {
+ case AD1988_6STACK:
+ case AD1988_6STACK_DIG:
+ spec->multiout.max_channels = 8;
+ spec->multiout.num_dacs = 4;
+ if (codec->revision_id == AD1988A_REV2)
+ spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
+ else
+ spec->multiout.dac_nids = ad1988_6stack_dac_nids;
+ spec->input_mux = &ad1988_6stack_capture_source;
+ spec->num_mixers = 2;
+ if (codec->revision_id == AD1988A_REV2)
+ spec->mixers[0] = ad1988_6stack_mixers1_rev2;
+ else
+ spec->mixers[0] = ad1988_6stack_mixers1;
+ spec->mixers[1] = ad1988_6stack_mixers2;
+ spec->num_init_verbs = 1;
+ spec->init_verbs[0] = ad1988_6stack_init_verbs;
+ if (board_config == AD1988_6STACK_DIG) {
+ spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
+ spec->dig_in_nid = AD1988_SPDIF_IN;
+ }
+ break;
+ case AD1988_3STACK:
+ case AD1988_3STACK_DIG:
+ spec->multiout.max_channels = 6;
+ spec->multiout.num_dacs = 3;
+ if (codec->revision_id == AD1988A_REV2)
+ spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
+ else
+ spec->multiout.dac_nids = ad1988_3stack_dac_nids;
+ spec->input_mux = &ad1988_6stack_capture_source;
+ spec->channel_mode = ad1988_3stack_modes;
+ spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
+ spec->num_mixers = 2;
+ if (codec->revision_id == AD1988A_REV2)
+ spec->mixers[0] = ad1988_3stack_mixers1_rev2;
+ else
+ spec->mixers[0] = ad1988_3stack_mixers1;
+ spec->mixers[1] = ad1988_3stack_mixers2;
+ spec->num_init_verbs = 1;
+ spec->init_verbs[0] = ad1988_3stack_init_verbs;
+ if (board_config == AD1988_3STACK_DIG)
+ spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
+ break;
+ case AD1988_LAPTOP:
+ case AD1988_LAPTOP_DIG:
+ spec->multiout.max_channels = 2;
+ spec->multiout.num_dacs = 1;
+ spec->multiout.dac_nids = ad1988_3stack_dac_nids;
+ spec->input_mux = &ad1988_laptop_capture_source;
+ spec->num_mixers = 1;
+ spec->mixers[0] = ad1988_laptop_mixers;
+ spec->num_init_verbs = 1;
+ spec->init_verbs[0] = ad1988_laptop_init_verbs;
+ if (board_config == AD1988_LAPTOP_DIG)
+ spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
+ break;
+ }
+
+ spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
+ spec->adc_nids = ad1988_adc_nids;
+ spec->capsrc_nids = ad1988_capsrc_nids;
+ spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
+ spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
+ if (spec->multiout.dig_out_nid) {
+ spec->mixers[spec->num_mixers++] = ad1988_spdif_out_mixers;
+ spec->init_verbs[spec->num_init_verbs++] = ad1988_spdif_init_verbs;
+ }
+ if (spec->dig_in_nid)
+ spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
+
+ codec->patch_ops = ad198x_patch_ops;
+ switch (board_config) {
+ case AD1988_AUTO:
+ codec->patch_ops.init = ad1988_auto_init;
+ break;
+ case AD1988_LAPTOP:
+ case AD1988_LAPTOP_DIG:
+ codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
+ break;
+ }
+
+ return 0;
+}
+
+
+/*
* patch entries
*/
struct hda_codec_preset snd_hda_preset_analog[] = {
{ .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
{ .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
{ .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
+ { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index 523c362ec44d..d38ce22507ae 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -44,7 +44,6 @@ enum {
struct cmi_spec {
int board_config;
- unsigned int surr_switch: 1; /* switchable line,mic */
unsigned int no_line_in: 1; /* no line-in (5-jack) */
unsigned int front_panel: 1; /* has front-panel 2-jack */
@@ -62,9 +61,8 @@ struct cmi_spec {
unsigned int cur_mux[2];
/* channel mode */
- unsigned int num_ch_modes;
- unsigned int cur_ch_mode;
- const struct cmi_channel_mode *channel_modes;
+ int num_channel_modes;
+ const struct hda_channel_mode *channel_modes;
struct hda_pcm pcm_rec[2]; /* PCM information */
@@ -78,30 +76,17 @@ struct cmi_spec {
struct hda_verb multi_init[9]; /* 2 verbs for each pin + terminator */
};
-/* amp values */
-#define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8))
-#define AMP_IN_UNMUTE(idx) (0x7000 | ((idx)<<8))
-#define AMP_OUT_MUTE 0xb080
-#define AMP_OUT_UNMUTE 0xb000
-#define AMP_OUT_ZERO 0xb000
-/* pinctl values */
-#define PIN_IN 0x20
-#define PIN_VREF80 0x24
-#define PIN_VREF50 0x21
-#define PIN_OUT 0x40
-#define PIN_HP 0xc0
-
/*
* input MUX
*/
-static int cmi_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+static int cmi_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct cmi_spec *spec = codec->spec;
return snd_hda_input_mux_info(spec->input_mux, uinfo);
}
-static int cmi_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int cmi_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct cmi_spec *spec = codec->spec;
@@ -111,7 +96,7 @@ static int cmi_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucon
return 0;
}
-static int cmi_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int cmi_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct cmi_spec *spec = codec->spec;
@@ -158,63 +143,39 @@ static struct hda_verb cmi9880_ch8_init[] = {
{}
};
-struct cmi_channel_mode {
- unsigned int channels;
- const struct hda_verb *sequence;
-};
-
-static struct cmi_channel_mode cmi9880_channel_modes[3] = {
+static struct hda_channel_mode cmi9880_channel_modes[3] = {
{ 2, cmi9880_ch2_init },
{ 6, cmi9880_ch6_init },
{ 8, cmi9880_ch8_init },
};
-static int cmi_ch_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+static int cmi_ch_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct cmi_spec *spec = codec->spec;
-
- snd_assert(spec->channel_modes, return -EINVAL);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = spec->num_ch_modes;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- sprintf(uinfo->value.enumerated.name, "%dch",
- spec->channel_modes[uinfo->value.enumerated.item].channels);
- return 0;
+ return snd_hda_ch_mode_info(codec, uinfo, spec->channel_modes,
+ spec->num_channel_modes);
}
-static int cmi_ch_mode_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int cmi_ch_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct cmi_spec *spec = codec->spec;
-
- ucontrol->value.enumerated.item[0] = spec->cur_ch_mode;
- return 0;
+ return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_modes,
+ spec->num_channel_modes, spec->multiout.max_channels);
}
-static int cmi_ch_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int cmi_ch_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct cmi_spec *spec = codec->spec;
-
- snd_assert(spec->channel_modes, return -EINVAL);
- if (ucontrol->value.enumerated.item[0] >= spec->num_ch_modes)
- ucontrol->value.enumerated.item[0] = spec->num_ch_modes;
- if (ucontrol->value.enumerated.item[0] == spec->cur_ch_mode &&
- ! codec->in_resume)
- return 0;
-
- spec->cur_ch_mode = ucontrol->value.enumerated.item[0];
- snd_hda_sequence_write(codec, spec->channel_modes[spec->cur_ch_mode].sequence);
- spec->multiout.max_channels = spec->channel_modes[spec->cur_ch_mode].channels;
- return 1;
+ return snd_hda_ch_mode_put(codec, ucontrol, spec->channel_modes,
+ spec->num_channel_modes, &spec->multiout.max_channels);
}
/*
*/
-static snd_kcontrol_new_t cmi9880_basic_mixer[] = {
+static struct snd_kcontrol_new cmi9880_basic_mixer[] = {
/* CMI9880 has no playback volumes! */
HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), /* front */
HDA_CODEC_MUTE("Surround Playback Switch", 0x04, 0x0, HDA_OUTPUT),
@@ -246,7 +207,7 @@ static snd_kcontrol_new_t cmi9880_basic_mixer[] = {
/*
* shared I/O pins
*/
-static snd_kcontrol_new_t cmi9880_ch_mode_mixer[] = {
+static struct snd_kcontrol_new cmi9880_ch_mode_mixer[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Channel Mode",
@@ -361,7 +322,7 @@ static int cmi9880_build_controls(struct hda_codec *codec)
err = snd_hda_add_new_ctls(codec, cmi9880_basic_mixer);
if (err < 0)
return err;
- if (spec->surr_switch) {
+ if (spec->channel_modes) {
err = snd_hda_add_new_ctls(codec, cmi9880_ch_mode_mixer);
if (err < 0)
return err;
@@ -475,7 +436,7 @@ static int cmi9880_resume(struct hda_codec *codec)
cmi9880_init(codec);
snd_hda_resume_ctls(codec, cmi9880_basic_mixer);
- if (spec->surr_switch)
+ if (spec->channel_modes)
snd_hda_resume_ctls(codec, cmi9880_ch_mode_mixer);
if (spec->multiout.dig_out_nid)
snd_hda_resume_spdif_out(codec);
@@ -491,7 +452,7 @@ static int cmi9880_resume(struct hda_codec *codec)
*/
static int cmi9880_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct cmi_spec *spec = codec->spec;
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
@@ -501,7 +462,7 @@ static int cmi9880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct cmi_spec *spec = codec->spec;
return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
@@ -510,7 +471,7 @@ static int cmi9880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
static int cmi9880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct cmi_spec *spec = codec->spec;
return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
@@ -521,7 +482,7 @@ static int cmi9880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
*/
static int cmi9880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct cmi_spec *spec = codec->spec;
return snd_hda_multi_out_dig_open(codec, &spec->multiout);
@@ -529,7 +490,7 @@ static int cmi9880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
static int cmi9880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct cmi_spec *spec = codec->spec;
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
@@ -542,7 +503,7 @@ static int cmi9880_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct cmi_spec *spec = codec->spec;
@@ -553,7 +514,7 @@ static int cmi9880_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
static int cmi9880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct cmi_spec *spec = codec->spec;
@@ -685,14 +646,13 @@ static int patch_cmi9880(struct hda_codec *codec)
switch (spec->board_config) {
case CMI_MINIMAL:
case CMI_MIN_FP:
- spec->surr_switch = 1;
+ spec->channel_modes = cmi9880_channel_modes;
if (spec->board_config == CMI_MINIMAL)
- spec->num_ch_modes = 2;
+ spec->num_channel_modes = 2;
else {
spec->front_panel = 1;
- spec->num_ch_modes = 3;
+ spec->num_channel_modes = 3;
}
- spec->channel_modes = cmi9880_channel_modes;
spec->multiout.max_channels = cmi9880_channel_modes[0].channels;
spec->input_mux = &cmi9880_basic_mux;
break;
@@ -727,19 +687,18 @@ static int patch_cmi9880(struct hda_codec *codec)
get_defcfg_connect(port_f) == AC_JACK_PORT_NONE) {
port_g = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
port_h = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
- spec->surr_switch = 1;
+ spec->channel_modes = cmi9880_channel_modes;
/* no front panel */
if (get_defcfg_connect(port_g) == AC_JACK_PORT_NONE ||
get_defcfg_connect(port_h) == AC_JACK_PORT_NONE) {
/* no optional rear panel */
spec->board_config = CMI_MINIMAL;
spec->front_panel = 0;
- spec->num_ch_modes = 2;
+ spec->num_channel_modes = 2;
} else {
spec->board_config = CMI_MIN_FP;
- spec->num_ch_modes = 3;
+ spec->num_channel_modes = 3;
}
- spec->channel_modes = cmi9880_channel_modes;
spec->input_mux = &cmi9880_basic_mux;
spec->multiout.max_channels = cmi9880_channel_modes[0].channels;
} else {
@@ -752,7 +711,7 @@ static int patch_cmi9880(struct hda_codec *codec)
spec->dig_in_nid = CMI_DIG_IN_NID;
spec->multiout.max_channels = 8;
}
- snd_hda_parse_pin_def_config(codec, &cfg);
+ snd_hda_parse_pin_def_config(codec, &cfg, NULL);
if (cfg.line_outs) {
spec->multiout.max_channels = cfg.line_outs * 2;
cmi9880_fill_multi_dac_nids(codec, &cfg);
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index cffb83fdcff7..ad9e501a9818 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -3,7 +3,8 @@
*
* HD audio interface patch for ALC 260/880/882 codecs
*
- * Copyright (c) 2004 PeiSen Hou <pshou@realtek.com.tw>
+ * Copyright (c) 2004 Kailang Yang <kailang@realtek.com.tw>
+ * PeiSen Hou <pshou@realtek.com.tw>
* Takashi Iwai <tiwai@suse.de>
*
* This driver is free software; you can redistribute it and/or modify
@@ -39,17 +40,20 @@ enum {
ALC880_5ST_DIG,
ALC880_W810,
ALC880_Z71V,
- ALC880_AUTO,
ALC880_6ST,
ALC880_6ST_DIG,
ALC880_F1734,
ALC880_ASUS,
ALC880_ASUS_DIG,
ALC880_ASUS_W1V,
+ ALC880_ASUS_DIG2,
ALC880_UNIWILL_DIG,
+ ALC880_CLEVO,
+ ALC880_TCL_S700,
#ifdef CONFIG_SND_DEBUG
ALC880_TEST,
#endif
+ ALC880_AUTO,
ALC880_MODEL_LAST /* last tag */
};
@@ -57,30 +61,45 @@ enum {
enum {
ALC260_BASIC,
ALC260_HP,
- ALC260_FUJITSU_S702x,
+ ALC260_HP_3013,
+ ALC260_FUJITSU_S702X,
+ ALC260_AUTO,
ALC260_MODEL_LAST /* last tag */
};
-/* amp values */
-#define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8))
-#define AMP_IN_UNMUTE(idx) (0x7000 | ((idx)<<8))
-#define AMP_OUT_MUTE 0xb080
-#define AMP_OUT_UNMUTE 0xb000
-#define AMP_OUT_ZERO 0xb000
-/* pinctl values */
-#define PIN_IN 0x20
-#define PIN_VREF80 0x24
-#define PIN_VREF50 0x21
-#define PIN_OUT 0x40
-#define PIN_HP 0xc0
-#define PIN_HP_AMP 0x80
+/* ALC262 models */
+enum {
+ ALC262_BASIC,
+ ALC262_AUTO,
+ ALC262_MODEL_LAST /* last tag */
+};
+
+/* ALC861 models */
+enum {
+ ALC861_3ST,
+ ALC861_3ST_DIG,
+ ALC861_6ST_DIG,
+ ALC861_AUTO,
+ ALC861_MODEL_LAST,
+};
+
+/* ALC882 models */
+enum {
+ ALC882_3ST_DIG,
+ ALC882_6ST_DIG,
+ ALC882_AUTO,
+ ALC882_MODEL_LAST,
+};
+
+/* for GPIO Poll */
+#define GPIO_MASK 0x03
struct alc_spec {
/* codec parameterization */
- snd_kcontrol_new_t *mixers[3]; /* mixer arrays */
+ struct snd_kcontrol_new *mixers[5]; /* mixer arrays */
unsigned int num_mixers;
- const struct hda_verb *init_verbs[3]; /* initialization verbs
+ const struct hda_verb *init_verbs[5]; /* initialization verbs
* don't forget NULL termination!
*/
unsigned int num_init_verbs;
@@ -109,7 +128,7 @@ struct alc_spec {
unsigned int cur_mux[3];
/* channel model */
- const struct alc_channel_mode *channel_mode;
+ const struct hda_channel_mode *channel_mode;
int num_channel_mode;
/* PCM information */
@@ -118,23 +137,41 @@ struct alc_spec {
/* dynamic controls, init_verbs and input_mux */
struct auto_pin_cfg autocfg;
unsigned int num_kctl_alloc, num_kctl_used;
- snd_kcontrol_new_t *kctl_alloc;
+ struct snd_kcontrol_new *kctl_alloc;
struct hda_input_mux private_imux;
- hda_nid_t private_dac_nids[4];
+ hda_nid_t private_dac_nids[5];
+};
+
+/*
+ * configuration template - to be copied to the spec instance
+ */
+struct alc_config_preset {
+ struct snd_kcontrol_new *mixers[5]; /* should be identical size with spec */
+ const struct hda_verb *init_verbs[5];
+ unsigned int num_dacs;
+ hda_nid_t *dac_nids;
+ hda_nid_t dig_out_nid; /* optional */
+ hda_nid_t hp_nid; /* optional */
+ unsigned int num_adc_nids;
+ hda_nid_t *adc_nids;
+ hda_nid_t dig_in_nid;
+ unsigned int num_channel_mode;
+ const struct hda_channel_mode *channel_mode;
+ const struct hda_input_mux *input_mux;
};
/*
* input MUX handling
*/
-static int alc_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+static int alc_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
return snd_hda_input_mux_info(spec->input_mux, uinfo);
}
-static int alc_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
@@ -144,7 +181,7 @@ static int alc_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucon
return 0;
}
-static int alc_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
@@ -157,63 +194,28 @@ static int alc_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucon
/*
* channel mode setting
*/
-struct alc_channel_mode {
- int channels;
- const struct hda_verb *sequence;
-};
-
-static int alc880_ch_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+static int alc_ch_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
- int items = kcontrol->private_value ? (int)kcontrol->private_value : 2;
-
- snd_assert(spec->channel_mode, return -ENXIO);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = items;
- if (uinfo->value.enumerated.item >= items)
- uinfo->value.enumerated.item = items - 1;
- sprintf(uinfo->value.enumerated.name, "%dch",
- spec->channel_mode[uinfo->value.enumerated.item].channels);
- return 0;
+ return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
+ spec->num_channel_mode);
}
-static int alc880_ch_mode_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int alc_ch_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
- int items = kcontrol->private_value ? (int)kcontrol->private_value : 2;
- int i;
-
- snd_assert(spec->channel_mode, return -ENXIO);
- for (i = 0; i < items; i++) {
- if (spec->multiout.max_channels == spec->channel_mode[i].channels) {
- ucontrol->value.enumerated.item[0] = i;
- break;
- }
- }
- return 0;
+ return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
+ spec->num_channel_mode, spec->multiout.max_channels);
}
-static int alc880_ch_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int alc_ch_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
- int mode;
-
- snd_assert(spec->channel_mode, return -ENXIO);
- mode = ucontrol->value.enumerated.item[0] ? 1 : 0;
- if (spec->multiout.max_channels == spec->channel_mode[mode].channels &&
- ! codec->in_resume)
- return 0;
-
- /* change the current channel setting */
- spec->multiout.max_channels = spec->channel_mode[mode].channels;
- if (spec->channel_mode[mode].sequence)
- snd_hda_sequence_write(codec, spec->channel_mode[mode].sequence);
-
- return 1;
+ return snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
+ spec->num_channel_mode, &spec->multiout.max_channels);
}
@@ -222,7 +224,7 @@ static int alc880_ch_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *uc
* supported, so VrefEn can't be controlled using these functions as they
* stand.
*/
-static int alc_pinctl_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+static int alc_pinctl_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
@@ -231,7 +233,7 @@ static int alc_pinctl_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t
return 0;
}
-static int alc_pinctl_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int alc_pinctl_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = kcontrol->private_value & 0xffff;
@@ -244,7 +246,7 @@ static int alc_pinctl_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
return 0;
}
-static int alc_pinctl_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int alc_pinctl_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = kcontrol->private_value & 0xffff;
@@ -266,6 +268,36 @@ static int alc_pinctl_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
.put = alc_pinctl_switch_put, \
.private_value = (nid) | (mask<<16) }
+
+/*
+ * set up from the preset table
+ */
+static void setup_preset(struct alc_spec *spec, const struct alc_config_preset *preset)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++)
+ spec->mixers[spec->num_mixers++] = preset->mixers[i];
+ for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i]; i++)
+ spec->init_verbs[spec->num_init_verbs++] = preset->init_verbs[i];
+
+ spec->channel_mode = preset->channel_mode;
+ spec->num_channel_mode = preset->num_channel_mode;
+
+ spec->multiout.max_channels = spec->channel_mode[0].channels;
+
+ spec->multiout.num_dacs = preset->num_dacs;
+ spec->multiout.dac_nids = preset->dac_nids;
+ spec->multiout.dig_out_nid = preset->dig_out_nid;
+ spec->multiout.hp_nid = preset->hp_nid;
+
+ spec->input_mux = preset->input_mux;
+
+ spec->num_adc_nids = preset->num_adc_nids;
+ spec->adc_nids = preset->adc_nids;
+ spec->dig_in_nid = preset->dig_in_nid;
+}
+
/*
* ALC880 3-stack model
*
@@ -286,6 +318,7 @@ static hda_nid_t alc880_adc_nids[3] = {
/* The datasheet says the node 0x07 is connected from inputs,
* but it shows zero connection in the real implementation on some devices.
+ * Note: this is a 915GAV bug, fixed on 915GLV
*/
static hda_nid_t alc880_adc_nids_alt[2] = {
/* ADC1-2 */
@@ -328,12 +361,12 @@ static struct hda_verb alc880_threestack_ch6_init[] = {
{ } /* end */
};
-static struct alc_channel_mode alc880_threestack_modes[2] = {
+static struct hda_channel_mode alc880_threestack_modes[2] = {
{ 2, alc880_threestack_ch2_init },
{ 6, alc880_threestack_ch6_init },
};
-static snd_kcontrol_new_t alc880_three_stack_mixer[] = {
+static struct snd_kcontrol_new alc880_three_stack_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
@@ -356,15 +389,15 @@ static snd_kcontrol_new_t alc880_three_stack_mixer[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Channel Mode",
- .info = alc880_ch_mode_info,
- .get = alc880_ch_mode_get,
- .put = alc880_ch_mode_put,
+ .info = alc_ch_mode_info,
+ .get = alc_ch_mode_get,
+ .put = alc_ch_mode_put,
},
{ } /* end */
};
/* capture mixer elements */
-static snd_kcontrol_new_t alc880_capture_mixer[] = {
+static struct snd_kcontrol_new alc880_capture_mixer[] = {
HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
@@ -388,7 +421,7 @@ static snd_kcontrol_new_t alc880_capture_mixer[] = {
};
/* capture mixer elements (in case NID 0x07 not available) */
-static snd_kcontrol_new_t alc880_capture_alt_mixer[] = {
+static struct snd_kcontrol_new alc880_capture_alt_mixer[] = {
HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
@@ -420,7 +453,7 @@ static snd_kcontrol_new_t alc880_capture_alt_mixer[] = {
*/
/* additional mixers to alc880_three_stack_mixer */
-static snd_kcontrol_new_t alc880_five_stack_mixer[] = {
+static struct snd_kcontrol_new alc880_five_stack_mixer[] = {
HDA_CODEC_VOLUME("Side Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Side Playback Switch", 0x0d, 2, HDA_INPUT),
{ } /* end */
@@ -443,7 +476,7 @@ static struct hda_verb alc880_fivestack_ch8_init[] = {
{ } /* end */
};
-static struct alc_channel_mode alc880_fivestack_modes[2] = {
+static struct hda_channel_mode alc880_fivestack_modes[2] = {
{ 6, alc880_fivestack_ch6_init },
{ 8, alc880_fivestack_ch8_init },
};
@@ -473,11 +506,11 @@ static struct hda_input_mux alc880_6stack_capture_source = {
};
/* fixed 8-channels */
-static struct alc_channel_mode alc880_sixstack_modes[1] = {
+static struct hda_channel_mode alc880_sixstack_modes[1] = {
{ 8, NULL },
};
-static snd_kcontrol_new_t alc880_six_stack_mixer[] = {
+static struct snd_kcontrol_new alc880_six_stack_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
@@ -501,9 +534,9 @@ static snd_kcontrol_new_t alc880_six_stack_mixer[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Channel Mode",
- .info = alc880_ch_mode_info,
- .get = alc880_ch_mode_get,
- .put = alc880_ch_mode_put,
+ .info = alc_ch_mode_info,
+ .get = alc_ch_mode_get,
+ .put = alc_ch_mode_put,
},
{ } /* end */
};
@@ -540,12 +573,12 @@ static hda_nid_t alc880_w810_dac_nids[3] = {
};
/* fixed 6 channels */
-static struct alc_channel_mode alc880_w810_modes[1] = {
+static struct hda_channel_mode alc880_w810_modes[1] = {
{ 6, NULL }
};
/* Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, HP = 0x1b */
-static snd_kcontrol_new_t alc880_w810_base_mixer[] = {
+static struct snd_kcontrol_new alc880_w810_base_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
@@ -572,11 +605,11 @@ static hda_nid_t alc880_z71v_dac_nids[1] = {
#define ALC880_Z71V_HP_DAC 0x03
/* fixed 2 channels */
-static struct alc_channel_mode alc880_2_jack_modes[1] = {
+static struct hda_channel_mode alc880_2_jack_modes[1] = {
{ 2, NULL }
};
-static snd_kcontrol_new_t alc880_z71v_mixer[] = {
+static struct snd_kcontrol_new alc880_z71v_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
@@ -602,7 +635,7 @@ static hda_nid_t alc880_f1734_dac_nids[1] = {
};
#define ALC880_F1734_HP_DAC 0x02
-static snd_kcontrol_new_t alc880_f1734_mixer[] = {
+static struct snd_kcontrol_new alc880_f1734_mixer[] = {
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
HDA_CODEC_VOLUME("Internal Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
@@ -627,7 +660,7 @@ static snd_kcontrol_new_t alc880_f1734_mixer[] = {
#define alc880_asus_dac_nids alc880_w810_dac_nids /* identical with w810 */
#define alc880_asus_modes alc880_threestack_modes /* 2/6 channel mode */
-static snd_kcontrol_new_t alc880_asus_mixer[] = {
+static struct snd_kcontrol_new alc880_asus_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
@@ -645,9 +678,9 @@ static snd_kcontrol_new_t alc880_asus_mixer[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Channel Mode",
- .info = alc880_ch_mode_info,
- .get = alc880_ch_mode_get,
- .put = alc880_ch_mode_put,
+ .info = alc_ch_mode_info,
+ .get = alc_ch_mode_get,
+ .put = alc_ch_mode_put,
},
{ } /* end */
};
@@ -662,19 +695,46 @@ static snd_kcontrol_new_t alc880_asus_mixer[] = {
*/
/* additional mixers to alc880_asus_mixer */
-static snd_kcontrol_new_t alc880_asus_w1v_mixer[] = {
+static struct snd_kcontrol_new alc880_asus_w1v_mixer[] = {
HDA_CODEC_VOLUME("Line2 Playback Volume", 0x0b, 0x03, HDA_INPUT),
HDA_CODEC_MUTE("Line2 Playback Switch", 0x0b, 0x03, HDA_INPUT),
{ } /* end */
};
/* additional mixers to alc880_asus_mixer */
-static snd_kcontrol_new_t alc880_pcbeep_mixer[] = {
+static struct snd_kcontrol_new alc880_pcbeep_mixer[] = {
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
{ } /* end */
};
+/* TCL S700 */
+static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0B, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x0B, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0B, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0B, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ * FIXME: the controls appear in the "playback" view!
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 1,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
+ .put = alc_mux_enum_put,
+ },
+ { } /* end */
+};
+
/*
* build control elements
*/
@@ -974,6 +1034,8 @@ static struct hda_verb alc880_gpio1_init_verbs[] = {
{0x01, AC_VERB_SET_GPIO_MASK, 0x01},
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
{0x01, AC_VERB_SET_GPIO_DATA, 0x01},
+
+ { }
};
/* Enable GPIO mask and set output */
@@ -981,8 +1043,59 @@ static struct hda_verb alc880_gpio2_init_verbs[] = {
{0x01, AC_VERB_SET_GPIO_MASK, 0x02},
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
{0x01, AC_VERB_SET_GPIO_DATA, 0x02},
+
+ { }
};
+/* Clevo m520g init */
+static struct hda_verb alc880_pin_clevo_init_verbs[] = {
+ /* headphone output */
+ {0x11, AC_VERB_SET_CONNECT_SEL, 0x01},
+ /* line-out */
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ /* Line-in */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ /* CD */
+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ /* Mic1 (rear panel) */
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ /* Mic2 (front panel) */
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ /* headphone */
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ /* change to EAPD mode */
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x3060},
+
+ { }
+};
+
+static struct hda_verb alc880_pin_tcl_S700_init_verbs[] = {
+ /* Headphone output */
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ /* Front output*/
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+ /* Line In pin widget for input */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ /* CD pin widget for input */
+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ /* Mic1 (rear panel) pin widget for input and vref at 80% */
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+
+ /* change to EAPD mode */
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x3070},
+
+ { }
+};
/*
*/
@@ -1023,7 +1136,7 @@ static int alc_resume(struct hda_codec *codec)
*/
static int alc880_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct alc_spec *spec = codec->spec;
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
@@ -1033,7 +1146,7 @@ static int alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct alc_spec *spec = codec->spec;
return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
@@ -1042,7 +1155,7 @@ static int alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
static int alc880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct alc_spec *spec = codec->spec;
return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
@@ -1053,7 +1166,7 @@ static int alc880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
*/
static int alc880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct alc_spec *spec = codec->spec;
return snd_hda_multi_out_dig_open(codec, &spec->multiout);
@@ -1061,7 +1174,7 @@ static int alc880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct alc_spec *spec = codec->spec;
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
@@ -1074,7 +1187,7 @@ static int alc880_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct alc_spec *spec = codec->spec;
@@ -1085,7 +1198,7 @@ static int alc880_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
static int alc880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct alc_spec *spec = codec->spec;
@@ -1147,15 +1260,23 @@ static int alc_build_pcms(struct hda_codec *codec)
codec->pcm_info = info;
info->name = spec->stream_name_analog;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback);
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
-
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0;
- for (i = 0; i < spec->num_channel_mode; i++) {
- if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels;
+ if (spec->stream_analog_playback) {
+ snd_assert(spec->multiout.dac_nids, return -EINVAL);
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback);
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
+ }
+ if (spec->stream_analog_capture) {
+ snd_assert(spec->adc_nids, return -EINVAL);
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
+ }
+
+ if (spec->channel_mode) {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0;
+ for (i = 0; i < spec->num_channel_mode; i++) {
+ if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels;
+ }
}
}
@@ -1163,11 +1284,13 @@ static int alc_build_pcms(struct hda_codec *codec)
codec->num_pcms++;
info++;
info->name = spec->stream_name_digital;
- if (spec->multiout.dig_out_nid) {
+ if (spec->multiout.dig_out_nid &&
+ spec->stream_digital_playback) {
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback);
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
}
- if (spec->dig_in_nid) {
+ if (spec->dig_in_nid &&
+ spec->stream_digital_capture) {
info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_digital_capture);
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
}
@@ -1227,14 +1350,14 @@ static struct hda_input_mux alc880_test_capture_source = {
},
};
-static struct alc_channel_mode alc880_test_modes[4] = {
+static struct hda_channel_mode alc880_test_modes[4] = {
{ 2, NULL },
{ 4, NULL },
{ 6, NULL },
{ 8, NULL },
};
-static int alc_test_pin_ctl_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+static int alc_test_pin_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = {
"N/A", "Line Out", "HP Out",
@@ -1249,7 +1372,7 @@ static int alc_test_pin_ctl_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *
return 0;
}
-static int alc_test_pin_ctl_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int alc_test_pin_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
@@ -1275,7 +1398,7 @@ static int alc_test_pin_ctl_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *
return 0;
}
-static int alc_test_pin_ctl_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int alc_test_pin_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
@@ -1301,7 +1424,7 @@ static int alc_test_pin_ctl_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *
return 0;
}
-static int alc_test_pin_src_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+static int alc_test_pin_src_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = {
"Front", "Surround", "CLFE", "Side"
@@ -1315,7 +1438,7 @@ static int alc_test_pin_src_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *
return 0;
}
-static int alc_test_pin_src_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int alc_test_pin_src_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
@@ -1326,7 +1449,7 @@ static int alc_test_pin_src_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *
return 0;
}
-static int alc_test_pin_src_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
@@ -1359,7 +1482,7 @@ static int alc_test_pin_src_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *
.private_value = nid \
}
-static snd_kcontrol_new_t alc880_test_mixer[] = {
+static struct snd_kcontrol_new alc880_test_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("CLFE Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
@@ -1393,9 +1516,9 @@ static snd_kcontrol_new_t alc880_test_mixer[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Channel Mode",
- .info = alc880_ch_mode_info,
- .get = alc880_ch_mode_get,
- .put = alc880_ch_mode_put,
+ .info = alc_ch_mode_info,
+ .get = alc_ch_mode_get,
+ .put = alc_ch_mode_put,
},
{ } /* end */
};
@@ -1491,6 +1614,8 @@ static struct hda_board_config alc880_cfg_tbl[] = {
{ .pci_subvendor = 0x107b, .pci_subdevice = 0x4038, .config = ALC880_3ST },
{ .pci_subvendor = 0x107b, .pci_subdevice = 0x4040, .config = ALC880_3ST },
{ .pci_subvendor = 0x107b, .pci_subdevice = 0x4041, .config = ALC880_3ST },
+ /* TCL S700 */
+ { .pci_subvendor = 0x19db, .pci_subdevice = 0x4188, .config = ALC880_TCL_S700 },
/* Back 3 jack, front 2 jack (Internal add Aux-In) */
{ .pci_subvendor = 0x1025, .pci_subdevice = 0xe310, .config = ALC880_3ST },
@@ -1501,6 +1626,8 @@ static struct hda_board_config alc880_cfg_tbl[] = {
{ .modelname = "3stack-digout", .config = ALC880_3ST_DIG },
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe308, .config = ALC880_3ST_DIG },
{ .pci_subvendor = 0x1025, .pci_subdevice = 0x0070, .config = ALC880_3ST_DIG },
+ /* Clevo m520G NB */
+ { .pci_subvendor = 0x1558, .pci_subdevice = 0x0520, .config = ALC880_CLEVO },
/* Back 3 jack plus 1 SPDIF out jack, front 2 jack (Internal add Aux-In)*/
{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe305, .config = ALC880_3ST_DIG },
@@ -1538,6 +1665,8 @@ static struct hda_board_config alc880_cfg_tbl[] = {
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1964, .config = ALC880_Z71V },
{ .modelname = "6stack", .config = ALC880_6ST },
+ { .pci_subvendor = 0x1043, .pci_subdevice = 0x8196, .config = ALC880_6ST }, /* ASUS P5GD1-HVM */
+ { .pci_subvendor = 0x1043, .pci_subdevice = 0x81b4, .config = ALC880_6ST },
{ .pci_subvendor = 0x1019, .pci_subdevice = 0xa884, .config = ALC880_6ST }, /* Acer APFV */
{ .modelname = "6stack-digout", .config = ALC880_6ST_DIG },
@@ -1545,6 +1674,11 @@ static struct hda_board_config alc880_cfg_tbl[] = {
{ .pci_subvendor = 0x8086, .pci_subdevice = 0x2668, .config = ALC880_6ST_DIG },
{ .pci_subvendor = 0x1462, .pci_subdevice = 0x1150, .config = ALC880_6ST_DIG },
{ .pci_subvendor = 0xe803, .pci_subdevice = 0x1019, .config = ALC880_6ST_DIG },
+ { .pci_subvendor = 0x1039, .pci_subdevice = 0x1234, .config = ALC880_6ST_DIG },
+ { .pci_subvendor = 0x1025, .pci_subdevice = 0x0077, .config = ALC880_6ST_DIG },
+ { .pci_subvendor = 0x1025, .pci_subdevice = 0x0078, .config = ALC880_6ST_DIG },
+ { .pci_subvendor = 0x1025, .pci_subdevice = 0x0087, .config = ALC880_6ST_DIG },
+ { .pci_subvendor = 0x1297, .pci_subdevice = 0xc790, .config = ALC880_6ST_DIG }, /* Shuttle ST20G5 */
{ .modelname = "asus", .config = ALC880_ASUS },
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1964, .config = ALC880_ASUS_DIG },
@@ -1558,37 +1692,26 @@ static struct hda_board_config alc880_cfg_tbl[] = {
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1123, .config = ALC880_ASUS_DIG },
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1143, .config = ALC880_ASUS },
{ .pci_subvendor = 0x1043, .pci_subdevice = 0x10b3, .config = ALC880_ASUS_W1V },
+ { .pci_subvendor = 0x1558, .pci_subdevice = 0x5401, .config = ALC880_ASUS_DIG2 },
{ .modelname = "uniwill", .config = ALC880_UNIWILL_DIG },
{ .pci_subvendor = 0x1584, .pci_subdevice = 0x9050, .config = ALC880_UNIWILL_DIG },
{ .modelname = "F1734", .config = ALC880_F1734 },
{ .pci_subvendor = 0x1734, .pci_subdevice = 0x107c, .config = ALC880_F1734 },
+ { .pci_subvendor = 0x1584, .pci_subdevice = 0x9054, .config = ALC880_F1734 },
#ifdef CONFIG_SND_DEBUG
{ .modelname = "test", .config = ALC880_TEST },
#endif
+ { .modelname = "auto", .config = ALC880_AUTO },
{}
};
/*
- * configuration template - to be copied to the spec instance
+ * ALC880 codec presets
*/
-struct alc_config_preset {
- snd_kcontrol_new_t *mixers[4];
- const struct hda_verb *init_verbs[4];
- unsigned int num_dacs;
- hda_nid_t *dac_nids;
- hda_nid_t dig_out_nid; /* optional */
- hda_nid_t hp_nid; /* optional */
- unsigned int num_adc_nids;
- hda_nid_t *adc_nids;
- unsigned int num_channel_mode;
- const struct alc_channel_mode *channel_mode;
- const struct hda_input_mux *input_mux;
-};
-
static struct alc_config_preset alc880_presets[] = {
[ALC880_3ST] = {
.mixers = { alc880_three_stack_mixer },
@@ -1609,6 +1732,18 @@ static struct alc_config_preset alc880_presets[] = {
.channel_mode = alc880_threestack_modes,
.input_mux = &alc880_capture_source,
},
+ [ALC880_TCL_S700] = {
+ .mixers = { alc880_tcl_s700_mixer },
+ .init_verbs = { alc880_volume_init_verbs,
+ alc880_pin_tcl_S700_init_verbs,
+ alc880_gpio2_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc880_dac_nids),
+ .dac_nids = alc880_dac_nids,
+ .hp_nid = 0x03,
+ .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
+ .channel_mode = alc880_2_jack_modes,
+ .input_mux = &alc880_capture_source,
+ },
[ALC880_5ST] = {
.mixers = { alc880_three_stack_mixer, alc880_five_stack_mixer},
.init_verbs = { alc880_volume_init_verbs, alc880_pin_5stack_init_verbs },
@@ -1700,6 +1835,17 @@ static struct alc_config_preset alc880_presets[] = {
.channel_mode = alc880_asus_modes,
.input_mux = &alc880_capture_source,
},
+ [ALC880_ASUS_DIG2] = {
+ .mixers = { alc880_asus_mixer },
+ .init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs,
+ alc880_gpio2_init_verbs }, /* use GPIO2 */
+ .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
+ .dac_nids = alc880_asus_dac_nids,
+ .dig_out_nid = ALC880_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
+ .channel_mode = alc880_asus_modes,
+ .input_mux = &alc880_capture_source,
+ },
[ALC880_ASUS_W1V] = {
.mixers = { alc880_asus_mixer, alc880_asus_w1v_mixer },
.init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs,
@@ -1721,6 +1867,17 @@ static struct alc_config_preset alc880_presets[] = {
.channel_mode = alc880_asus_modes,
.input_mux = &alc880_capture_source,
},
+ [ALC880_CLEVO] = {
+ .mixers = { alc880_three_stack_mixer },
+ .init_verbs = { alc880_volume_init_verbs,
+ alc880_pin_clevo_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc880_dac_nids),
+ .dac_nids = alc880_dac_nids,
+ .hp_nid = 0x03,
+ .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
+ .channel_mode = alc880_threestack_modes,
+ .input_mux = &alc880_capture_source,
+ },
#ifdef CONFIG_SND_DEBUG
[ALC880_TEST] = {
.mixers = { alc880_test_mixer },
@@ -1747,7 +1904,7 @@ enum {
ALC_CTL_WIDGET_MUTE,
ALC_CTL_BIND_MUTE,
};
-static snd_kcontrol_new_t alc880_control_templates[] = {
+static struct snd_kcontrol_new alc880_control_templates[] = {
HDA_CODEC_VOLUME(NULL, 0, 0, 0),
HDA_CODEC_MUTE(NULL, 0, 0, 0),
HDA_BIND_MUTE(NULL, 0, 0, 0),
@@ -1756,7 +1913,7 @@ static snd_kcontrol_new_t alc880_control_templates[] = {
/* add dynamic controls */
static int add_control(struct alc_spec *spec, int type, const char *name, unsigned long val)
{
- snd_kcontrol_new_t *knew;
+ struct snd_kcontrol_new *knew;
if (spec->num_kctl_used >= spec->num_kctl_alloc) {
int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
@@ -1809,7 +1966,7 @@ static int alc880_auto_fill_dac_nids(struct alc_spec *spec, const struct auto_pi
nid = cfg->line_out_pins[i];
if (alc880_is_fixed_pin(nid)) {
int idx = alc880_fixed_pin_idx(nid);
- spec->multiout.dac_nids[i] = alc880_dac_to_idx(idx);
+ spec->multiout.dac_nids[i] = alc880_idx_to_dac(idx);
assigned[idx] = 1;
}
}
@@ -1832,7 +1989,8 @@ static int alc880_auto_fill_dac_nids(struct alc_spec *spec, const struct auto_pi
}
/* add playback controls from the parsed DAC table */
-static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg)
+static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
+ const struct auto_pin_cfg *cfg)
{
char name[32];
static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
@@ -1868,15 +2026,16 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, const struct
return err;
}
}
-
return 0;
}
-/* add playback controls for HP output */
-static int alc880_auto_create_hp_ctls(struct alc_spec *spec, hda_nid_t pin)
+/* add playback controls for speaker and HP outputs */
+static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
+ const char *pfx)
{
hda_nid_t nid;
int err;
+ char name[32];
if (! pin)
return 0;
@@ -1889,14 +2048,16 @@ static int alc880_auto_create_hp_ctls(struct alc_spec *spec, hda_nid_t pin)
if (! spec->multiout.num_dacs)
spec->multiout.num_dacs = 1;
} else
- /* specify the DAC as the extra HP output */
+ /* specify the DAC as the extra output */
spec->multiout.hp_nid = nid;
/* control HP volume/switch on the output mixer amp */
nid = alc880_idx_to_mixer(alc880_fixed_pin_idx(pin));
- if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Headphone Playback Volume",
+ sprintf(name, "%s Playback Volume", pfx);
+ if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
return err;
- if ((err = add_control(spec, ALC_CTL_BIND_MUTE, "Headphone Playback Switch",
+ sprintf(name, "%s Playback Switch", pfx);
+ if ((err = add_control(spec, ALC_CTL_BIND_MUTE, name,
HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
return err;
} else if (alc880_is_multi_pin(pin)) {
@@ -1908,7 +2069,8 @@ static int alc880_auto_create_hp_ctls(struct alc_spec *spec, hda_nid_t pin)
spec->multiout.num_dacs = 1;
}
/* we have only a switch on HP-out PIN */
- if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Headphone Playback Switch",
+ sprintf(name, "%s Playback Switch", pfx);
+ if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT))) < 0)
return err;
}
@@ -1916,38 +2078,39 @@ static int alc880_auto_create_hp_ctls(struct alc_spec *spec, hda_nid_t pin)
}
/* create input playback/capture controls for the given pin */
-static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, const char *ctlname)
+static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, const char *ctlname,
+ int idx, hda_nid_t mix_nid)
{
char name[32];
- int err, idx;
+ int err;
sprintf(name, "%s Playback Volume", ctlname);
- idx = alc880_input_pin_idx(pin);
if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(0x0b, 3, idx, HDA_INPUT))) < 0)
+ HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT))) < 0)
return err;
sprintf(name, "%s Playback Switch", ctlname);
if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(0x0b, 3, idx, HDA_INPUT))) < 0)
+ HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT))) < 0)
return err;
return 0;
}
/* create playback/capture controls for input pins */
-static int alc880_auto_create_analog_input_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg)
+static int alc880_auto_create_analog_input_ctls(struct alc_spec *spec,
+ const struct auto_pin_cfg *cfg)
{
- static char *labels[AUTO_PIN_LAST] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux"
- };
struct hda_input_mux *imux = &spec->private_imux;
- int i, err;
+ int i, err, idx;
for (i = 0; i < AUTO_PIN_LAST; i++) {
if (alc880_is_input_pin(cfg->input_pins[i])) {
- err = new_analog_input(spec, cfg->input_pins[i], labels[i]);
+ idx = alc880_input_pin_idx(cfg->input_pins[i]);
+ err = new_analog_input(spec, cfg->input_pins[i],
+ auto_pin_cfg_labels[i],
+ idx, 0x0b);
if (err < 0)
return err;
- imux->items[imux->num_items].label = labels[i];
+ imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
imux->items[imux->num_items].index = alc880_input_pin_idx(cfg->input_pins[i]);
imux->num_items++;
}
@@ -1955,7 +2118,8 @@ static int alc880_auto_create_analog_input_ctls(struct alc_spec *spec, const str
return 0;
}
-static void alc880_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type,
+static void alc880_auto_set_output_and_unmute(struct hda_codec *codec,
+ hda_nid_t nid, int pin_type,
int dac_idx)
{
/* set as output */
@@ -1982,11 +2146,14 @@ static void alc880_auto_init_multi_out(struct hda_codec *codec)
}
}
-static void alc880_auto_init_hp_out(struct hda_codec *codec)
+static void alc880_auto_init_extra_out(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
hda_nid_t pin;
+ pin = spec->autocfg.speaker_pin;
+ if (pin) /* connect to front */
+ alc880_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
pin = spec->autocfg.hp_pin;
if (pin) /* connect to front */
alc880_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
@@ -2015,15 +2182,21 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
int err;
+ static hda_nid_t alc880_ignore[] = { 0x1d, 0 };
- if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg)) < 0)
+ if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+ alc880_ignore)) < 0)
return err;
- if ((err = alc880_auto_fill_dac_nids(spec, &spec->autocfg)) < 0)
- return err;
- if (! spec->autocfg.line_outs && ! spec->autocfg.hp_pin)
+ if (! spec->autocfg.line_outs && ! spec->autocfg.speaker_pin &&
+ ! spec->autocfg.hp_pin)
return 0; /* can't find valid BIOS pin config */
- if ((err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
- (err = alc880_auto_create_hp_ctls(spec, spec->autocfg.hp_pin)) < 0 ||
+
+ if ((err = alc880_auto_fill_dac_nids(spec, &spec->autocfg)) < 0 ||
+ (err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
+ (err = alc880_auto_create_extra_out(spec, spec->autocfg.speaker_pin,
+ "Speaker")) < 0 ||
+ (err = alc880_auto_create_extra_out(spec, spec->autocfg.speaker_pin,
+ "Headphone")) < 0 ||
(err = alc880_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
return err;
@@ -2049,7 +2222,7 @@ static int alc880_auto_init(struct hda_codec *codec)
{
alc_init(codec);
alc880_auto_init_multi_out(codec);
- alc880_auto_init_hp_out(codec);
+ alc880_auto_init_extra_out(codec);
alc880_auto_init_analog_input(codec);
return 0;
}
@@ -2062,7 +2235,7 @@ static int patch_alc880(struct hda_codec *codec)
{
struct alc_spec *spec;
int board_config;
- int i, err;
+ int err;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
@@ -2088,36 +2261,8 @@ static int patch_alc880(struct hda_codec *codec)
}
}
- if (board_config != ALC880_AUTO) {
- /* set up from the preset table */
- const struct alc_config_preset *preset;
-
- preset = &alc880_presets[board_config];
-
- for (i = 0; preset->mixers[i]; i++) {
- snd_assert(spec->num_mixers < ARRAY_SIZE(spec->mixers), break);
- spec->mixers[spec->num_mixers++] = preset->mixers[i];
- }
- for (i = 0; preset->init_verbs[i]; i++) {
- snd_assert(spec->num_init_verbs < ARRAY_SIZE(spec->init_verbs), break);
- spec->init_verbs[spec->num_init_verbs++] = preset->init_verbs[i];
- }
-
- spec->channel_mode = preset->channel_mode;
- spec->num_channel_mode = preset->num_channel_mode;
-
- spec->multiout.max_channels = spec->channel_mode[0].channels;
-
- spec->multiout.num_dacs = preset->num_dacs;
- spec->multiout.dac_nids = preset->dac_nids;
- spec->multiout.dig_out_nid = preset->dig_out_nid;
- spec->multiout.hp_nid = preset->hp_nid;
-
- spec->input_mux = preset->input_mux;
-
- spec->num_adc_nids = preset->num_adc_nids;
- spec->adc_nids = preset->adc_nids;
- }
+ if (board_config != ALC880_AUTO)
+ setup_preset(spec, &alc880_presets[board_config]);
spec->stream_name_analog = "ALC880 Analog";
spec->stream_analog_playback = &alc880_pcm_analog_playback;
@@ -2129,8 +2274,7 @@ static int patch_alc880(struct hda_codec *codec)
if (! spec->adc_nids && spec->input_mux) {
/* check whether NID 0x07 is valid */
- unsigned int wcap = snd_hda_param_read(codec, alc880_adc_nids[0],
- AC_PAR_AUDIO_WIDGET_CAP);
+ unsigned int wcap = get_wcaps(codec, alc880_adc_nids[0]);
wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */
if (wcap != AC_WID_AUD_IN) {
spec->adc_nids = alc880_adc_nids_alt;
@@ -2167,11 +2311,16 @@ static hda_nid_t alc260_adc_nids[1] = {
0x04,
};
-static hda_nid_t alc260_hp_adc_nids[1] = {
+static hda_nid_t alc260_adc_nids_alt[1] = {
/* ADC1 */
0x05,
};
+static hda_nid_t alc260_hp_adc_nids[2] = {
+ /* ADC1, 0 */
+ 0x05, 0x04
+};
+
#define ALC260_DIGOUT_NID 0x03
#define ALC260_DIGIN_NID 0x06
@@ -2202,42 +2351,30 @@ static struct hda_input_mux alc260_fujitsu_capture_source = {
* element which allows changing the channel mode, so the verb list is
* never used.
*/
-static struct alc_channel_mode alc260_modes[1] = {
+static struct hda_channel_mode alc260_modes[1] = {
{ 2, NULL },
};
-static snd_kcontrol_new_t alc260_base_mixer[] = {
+
+/* Mixer combinations
+ *
+ * basic: base_output + input + pc_beep + capture
+ * HP: base_output + input + capture_alt
+ * HP_3013: hp_3013 + input + capture
+ * fujitsu: fujitsu + capture
+ */
+
+static struct snd_kcontrol_new alc260_base_output_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x07, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x07, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x07, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x07, 0x05, HDA_INPUT),
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT),
HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x04, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x04, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
{ } /* end */
-};
+};
-static snd_kcontrol_new_t alc260_hp_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT),
+static struct snd_kcontrol_new alc260_input_mixer[] = {
HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
@@ -2246,23 +2383,28 @@ static snd_kcontrol_new_t alc260_hp_mixer[] = {
HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x07, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x07, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x05, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x05, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
{ } /* end */
};
-static snd_kcontrol_new_t alc260_fujitsu_mixer[] = {
+static struct snd_kcontrol_new alc260_pc_beep_mixer[] = {
+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x07, 0x05, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x07, 0x05, HDA_INPUT),
+ { } /* end */
+};
+
+static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Aux-In Playback Volume", 0x07, 0x06, HDA_INPUT),
+ HDA_CODEC_MUTE("Aux-In Playback Switch", 0x07, 0x06, HDA_INPUT),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("iSpeaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("iSpeaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT),
+ { } /* end */
+};
+
+static struct snd_kcontrol_new alc260_fujitsu_mixer[] = {
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Headphone Playback Switch", 0x08, 2, HDA_INPUT),
ALC_PINCTL_SWITCH("Headphone Amp Switch", 0x14, PIN_HP_AMP),
@@ -2274,11 +2416,24 @@ static snd_kcontrol_new_t alc260_fujitsu_mixer[] = {
HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT),
HDA_CODEC_VOLUME("Internal Speaker Playback Volume", 0x09, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Internal Speaker Playback Switch", 0x09, 2, HDA_INPUT),
+ { } /* end */
+};
+
+/* capture mixer elements */
+static struct snd_kcontrol_new alc260_capture_mixer[] = {
HDA_CODEC_VOLUME("Capture Volume", 0x04, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x04, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x05, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x05, 0x0, HDA_INPUT),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ * FIXME: the controls appear in the "playback" view!
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 2,
.info = alc_mux_enum_info,
.get = alc_mux_enum_get,
.put = alc_mux_enum_put,
@@ -2286,6 +2441,28 @@ static snd_kcontrol_new_t alc260_fujitsu_mixer[] = {
{ } /* end */
};
+static struct snd_kcontrol_new alc260_capture_alt_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x05, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x05, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ * FIXME: the controls appear in the "playback" view!
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 1,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
+ .put = alc_mux_enum_put,
+ },
+ { } /* end */
+};
+
+/*
+ * initialization verbs
+ */
static struct hda_verb alc260_init_verbs[] = {
/* Line In pin widget for input */
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
@@ -2347,6 +2524,100 @@ static struct hda_verb alc260_init_verbs[] = {
{ }
};
+static struct hda_verb alc260_hp_init_verbs[] = {
+ /* Headphone and output */
+ {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
+ /* mono output */
+ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ /* Mic1 (rear panel) pin widget for input and vref at 80% */
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+ /* Mic2 (front panel) pin widget for input and vref at 80% */
+ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+ /* Line In pin widget for input */
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+ /* Line-2 pin widget for output */
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ /* CD pin widget for input */
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+ /* unmute amp left and right */
+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+ /* set connection select to line in (default select for this ADC) */
+ {0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
+ /* unmute Line-Out mixer amp left and right (volume = 0) */
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ /* mute pin widget amp left and right (no gain on this amp) */
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+ /* unmute HP mixer amp left and right (volume = 0) */
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ /* mute pin widget amp left and right (no gain on this amp) */
+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+ /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03 */
+ /* unmute CD */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+ /* unmute Line In */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+ /* unmute Mic */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
+ /* Unmute Front out path */
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ /* Unmute Headphone out path */
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ /* Unmute Mono out path */
+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ { }
+};
+
+static struct hda_verb alc260_hp_3013_init_verbs[] = {
+ /* Line out and output */
+ {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ /* mono output */
+ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ /* Mic1 (rear panel) pin widget for input and vref at 80% */
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+ /* Mic2 (front panel) pin widget for input and vref at 80% */
+ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+ /* Line In pin widget for input */
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+ /* Headphone pin widget for output */
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
+ /* CD pin widget for input */
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+ /* unmute amp left and right */
+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+ /* set connection select to line in (default select for this ADC) */
+ {0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
+ /* unmute Line-Out mixer amp left and right (volume = 0) */
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ /* mute pin widget amp left and right (no gain on this amp) */
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+ /* unmute HP mixer amp left and right (volume = 0) */
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ /* mute pin widget amp left and right (no gain on this amp) */
+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+ /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03 */
+ /* unmute CD */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+ /* unmute Line In */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+ /* unmute Mic */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
+ /* Unmute Front out path */
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ /* Unmute Headphone out path */
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ /* Unmute Mono out path */
+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ { }
+};
+
/* Initialisation sequence for ALC260 as configured in Fujitsu S702x
* laptops.
*/
@@ -2399,6 +2670,8 @@ static struct hda_verb alc260_fujitsu_init_verbs[] = {
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
+
+ { }
};
static struct hda_pcm_stream alc260_pcm_analog_playback = {
@@ -2413,18 +2686,338 @@ static struct hda_pcm_stream alc260_pcm_analog_capture = {
.channels_max = 2,
};
+#define alc260_pcm_digital_playback alc880_pcm_digital_playback
+#define alc260_pcm_digital_capture alc880_pcm_digital_capture
+
+/*
+ * for BIOS auto-configuration
+ */
+
+static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid,
+ const char *pfx)
+{
+ hda_nid_t nid_vol;
+ unsigned long vol_val, sw_val;
+ char name[32];
+ int err;
+
+ if (nid >= 0x0f && nid < 0x11) {
+ nid_vol = nid - 0x7;
+ vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT);
+ sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ } else if (nid == 0x11) {
+ nid_vol = nid - 0x7;
+ vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT);
+ sw_val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT);
+ } else if (nid >= 0x12 && nid <= 0x15) {
+ nid_vol = 0x08;
+ vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT);
+ sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ } else
+ return 0; /* N/A */
+
+ snprintf(name, sizeof(name), "%s Playback Volume", pfx);
+ if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name, vol_val)) < 0)
+ return err;
+ snprintf(name, sizeof(name), "%s Playback Switch", pfx);
+ if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, sw_val)) < 0)
+ return err;
+ return 1;
+}
+
+/* add playback controls from the parsed DAC table */
+static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ hda_nid_t nid;
+ int err;
+
+ spec->multiout.num_dacs = 1;
+ spec->multiout.dac_nids = spec->private_dac_nids;
+ spec->multiout.dac_nids[0] = 0x02;
+
+ nid = cfg->line_out_pins[0];
+ if (nid) {
+ err = alc260_add_playback_controls(spec, nid, "Front");
+ if (err < 0)
+ return err;
+ }
+
+ nid = cfg->speaker_pin;
+ if (nid) {
+ err = alc260_add_playback_controls(spec, nid, "Speaker");
+ if (err < 0)
+ return err;
+ }
+
+ nid = cfg->hp_pin;
+ if (nid) {
+ err = alc260_add_playback_controls(spec, nid, "Headphone");
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/* create playback/capture controls for input pins */
+static int alc260_auto_create_analog_input_ctls(struct alc_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ struct hda_input_mux *imux = &spec->private_imux;
+ int i, err, idx;
+
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ if (cfg->input_pins[i] >= 0x12) {
+ idx = cfg->input_pins[i] - 0x12;
+ err = new_analog_input(spec, cfg->input_pins[i],
+ auto_pin_cfg_labels[i], idx, 0x07);
+ if (err < 0)
+ return err;
+ imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
+ imux->items[imux->num_items].index = idx;
+ imux->num_items++;
+ }
+ if ((cfg->input_pins[i] >= 0x0f) && (cfg->input_pins[i] <= 0x10)){
+ idx = cfg->input_pins[i] - 0x09;
+ err = new_analog_input(spec, cfg->input_pins[i],
+ auto_pin_cfg_labels[i], idx, 0x07);
+ if (err < 0)
+ return err;
+ imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
+ imux->items[imux->num_items].index = idx;
+ imux->num_items++;
+ }
+ }
+ return 0;
+}
+
+static void alc260_auto_set_output_and_unmute(struct hda_codec *codec,
+ hda_nid_t nid, int pin_type,
+ int sel_idx)
+{
+ /* set as output */
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+ /* need the manual connection? */
+ if (nid >= 0x12) {
+ int idx = nid - 0x12;
+ snd_hda_codec_write(codec, idx + 0x0b, 0,
+ AC_VERB_SET_CONNECT_SEL, sel_idx);
+
+ }
+}
+
+static void alc260_auto_init_multi_out(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t nid;
+
+ nid = spec->autocfg.line_out_pins[0];
+ if (nid)
+ alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
+
+ nid = spec->autocfg.speaker_pin;
+ if (nid)
+ alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
+
+ nid = spec->autocfg.hp_pin;
+ if (nid)
+ alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
+}
+
+#define ALC260_PIN_CD_NID 0x16
+static void alc260_auto_init_analog_input(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ hda_nid_t nid = spec->autocfg.input_pins[i];
+ if (nid >= 0x12) {
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
+ if (nid != ALC260_PIN_CD_NID)
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_MUTE);
+ }
+ }
+}
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc260_volume_init_verbs[] = {
+ /*
+ * Unmute ADC0-1 and set the default input to mic-in
+ */
+ {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+ * mixer widget
+ * Note: PASD motherboards uses the Line In 2 as the input for front panel
+ * mic (mic 2)
+ */
+ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+ /*
+ * Set up output mixers (0x08 - 0x0a)
+ */
+ /* set vol=0 to output mixers */
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ /* set up input amps for analog loopback */
+ /* Amp Indices: DAC = 0, mixer = 1 */
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+ { }
+};
+
+static int alc260_parse_auto_config(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int wcap;
+ int err;
+ static hda_nid_t alc260_ignore[] = { 0x17, 0 };
+
+ if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+ alc260_ignore)) < 0)
+ return err;
+ if ((err = alc260_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0)
+ return err;
+ if (! spec->kctl_alloc)
+ return 0; /* can't find valid BIOS pin config */
+ if ((err = alc260_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
+ return err;
+
+ spec->multiout.max_channels = 2;
+
+ if (spec->autocfg.dig_out_pin)
+ spec->multiout.dig_out_nid = ALC260_DIGOUT_NID;
+ if (spec->kctl_alloc)
+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+ spec->init_verbs[spec->num_init_verbs++] = alc260_volume_init_verbs;
+
+ spec->input_mux = &spec->private_imux;
+
+ /* check whether NID 0x04 is valid */
+ wcap = get_wcaps(codec, 0x04);
+ wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */
+ if (wcap != AC_WID_AUD_IN) {
+ spec->adc_nids = alc260_adc_nids_alt;
+ spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt);
+ spec->mixers[spec->num_mixers] = alc260_capture_alt_mixer;
+ } else {
+ spec->adc_nids = alc260_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
+ spec->mixers[spec->num_mixers] = alc260_capture_mixer;
+ }
+ spec->num_mixers++;
+
+ return 1;
+}
+
+/* init callback for auto-configuration model -- overriding the default init */
+static int alc260_auto_init(struct hda_codec *codec)
+{
+ alc_init(codec);
+ alc260_auto_init_multi_out(codec);
+ alc260_auto_init_analog_input(codec);
+ return 0;
+}
+
+/*
+ * ALC260 configurations
+ */
static struct hda_board_config alc260_cfg_tbl[] = {
+ { .modelname = "basic", .config = ALC260_BASIC },
{ .modelname = "hp", .config = ALC260_HP },
- { .pci_subvendor = 0x103c, .config = ALC260_HP },
- { .modelname = "fujitsu", .config = ALC260_FUJITSU_S702x },
- { .pci_subvendor = 0x10cf, .pci_subdevice = 0x1326, .config = ALC260_FUJITSU_S702x },
+ { .pci_subvendor = 0x103c, .pci_subdevice = 0x3010, .config = ALC260_HP },
+ { .pci_subvendor = 0x103c, .pci_subdevice = 0x3011, .config = ALC260_HP },
+ { .pci_subvendor = 0x103c, .pci_subdevice = 0x3012, .config = ALC260_HP },
+ { .pci_subvendor = 0x103c, .pci_subdevice = 0x3013, .config = ALC260_HP_3013 },
+ { .pci_subvendor = 0x103c, .pci_subdevice = 0x3014, .config = ALC260_HP },
+ { .pci_subvendor = 0x103c, .pci_subdevice = 0x3015, .config = ALC260_HP },
+ { .pci_subvendor = 0x103c, .pci_subdevice = 0x3016, .config = ALC260_HP },
+ { .modelname = "fujitsu", .config = ALC260_FUJITSU_S702X },
+ { .pci_subvendor = 0x10cf, .pci_subdevice = 0x1326, .config = ALC260_FUJITSU_S702X },
+ { .modelname = "auto", .config = ALC260_AUTO },
{}
};
+static struct alc_config_preset alc260_presets[] = {
+ [ALC260_BASIC] = {
+ .mixers = { alc260_base_output_mixer,
+ alc260_input_mixer,
+ alc260_pc_beep_mixer,
+ alc260_capture_mixer },
+ .init_verbs = { alc260_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc260_dac_nids),
+ .dac_nids = alc260_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc260_adc_nids),
+ .adc_nids = alc260_adc_nids,
+ .num_channel_mode = ARRAY_SIZE(alc260_modes),
+ .channel_mode = alc260_modes,
+ .input_mux = &alc260_capture_source,
+ },
+ [ALC260_HP] = {
+ .mixers = { alc260_base_output_mixer,
+ alc260_input_mixer,
+ alc260_capture_alt_mixer },
+ .init_verbs = { alc260_hp_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc260_dac_nids),
+ .dac_nids = alc260_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
+ .adc_nids = alc260_hp_adc_nids,
+ .num_channel_mode = ARRAY_SIZE(alc260_modes),
+ .channel_mode = alc260_modes,
+ .input_mux = &alc260_capture_source,
+ },
+ [ALC260_HP_3013] = {
+ .mixers = { alc260_hp_3013_mixer,
+ alc260_input_mixer,
+ alc260_capture_alt_mixer },
+ .init_verbs = { alc260_hp_3013_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc260_dac_nids),
+ .dac_nids = alc260_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
+ .adc_nids = alc260_hp_adc_nids,
+ .num_channel_mode = ARRAY_SIZE(alc260_modes),
+ .channel_mode = alc260_modes,
+ .input_mux = &alc260_capture_source,
+ },
+ [ALC260_FUJITSU_S702X] = {
+ .mixers = { alc260_fujitsu_mixer,
+ alc260_capture_mixer },
+ .init_verbs = { alc260_fujitsu_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc260_dac_nids),
+ .dac_nids = alc260_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc260_adc_nids),
+ .adc_nids = alc260_adc_nids,
+ .num_channel_mode = ARRAY_SIZE(alc260_modes),
+ .channel_mode = alc260_modes,
+ .input_mux = &alc260_fujitsu_capture_source,
+ },
+};
+
static int patch_alc260(struct hda_codec *codec)
{
struct alc_spec *spec;
- int board_config;
+ int err, board_config;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
@@ -2435,60 +3028,35 @@ static int patch_alc260(struct hda_codec *codec)
board_config = snd_hda_check_board_config(codec, alc260_cfg_tbl);
if (board_config < 0 || board_config >= ALC260_MODEL_LAST) {
snd_printd(KERN_INFO "hda_codec: Unknown model for ALC260\n");
- board_config = ALC260_BASIC;
+ board_config = ALC260_AUTO;
}
- switch (board_config) {
- case ALC260_HP:
- spec->mixers[spec->num_mixers] = alc260_hp_mixer;
- spec->num_mixers++;
- break;
- case ALC260_FUJITSU_S702x:
- spec->mixers[spec->num_mixers] = alc260_fujitsu_mixer;
- spec->num_mixers++;
- break;
- default:
- spec->mixers[spec->num_mixers] = alc260_base_mixer;
- spec->num_mixers++;
- break;
- }
-
- if (board_config != ALC260_FUJITSU_S702x) {
- spec->init_verbs[0] = alc260_init_verbs;
- spec->num_init_verbs = 1;
- } else {
- spec->init_verbs[0] = alc260_fujitsu_init_verbs;
- spec->num_init_verbs = 1;
+ if (board_config == ALC260_AUTO) {
+ /* automatic parse from the BIOS config */
+ err = alc260_parse_auto_config(codec);
+ if (err < 0) {
+ alc_free(codec);
+ return err;
+ } else if (! err) {
+ printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n");
+ board_config = ALC260_BASIC;
+ }
}
- spec->channel_mode = alc260_modes;
- spec->num_channel_mode = ARRAY_SIZE(alc260_modes);
+ if (board_config != ALC260_AUTO)
+ setup_preset(spec, &alc260_presets[board_config]);
spec->stream_name_analog = "ALC260 Analog";
spec->stream_analog_playback = &alc260_pcm_analog_playback;
spec->stream_analog_capture = &alc260_pcm_analog_capture;
- spec->multiout.max_channels = spec->channel_mode[0].channels;
- spec->multiout.num_dacs = ARRAY_SIZE(alc260_dac_nids);
- spec->multiout.dac_nids = alc260_dac_nids;
-
- if (board_config != ALC260_FUJITSU_S702x) {
- spec->input_mux = &alc260_capture_source;
- } else {
- spec->input_mux = &alc260_fujitsu_capture_source;
- }
- switch (board_config) {
- case ALC260_HP:
- spec->num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids);
- spec->adc_nids = alc260_hp_adc_nids;
- break;
- default:
- spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
- spec->adc_nids = alc260_adc_nids;
- break;
- }
+ spec->stream_name_digital = "ALC260 Digital";
+ spec->stream_digital_playback = &alc260_pcm_digital_playback;
+ spec->stream_digital_capture = &alc260_pcm_digital_capture;
codec->patch_ops = alc_patch_ops;
+ if (board_config == ALC260_AUTO)
+ codec->patch_ops.init = alc260_auto_init;
return 0;
}
@@ -2505,8 +3073,10 @@ static int patch_alc260(struct hda_codec *codec)
* In addition, an independent DAC for the multi-playback (not used in this
* driver yet).
*/
+#define ALC882_DIGOUT_NID 0x06
+#define ALC882_DIGIN_NID 0x0a
-static struct alc_channel_mode alc882_ch_modes[1] = {
+static struct hda_channel_mode alc882_ch_modes[1] = {
{ 8, NULL }
};
@@ -2515,10 +3085,9 @@ static hda_nid_t alc882_dac_nids[4] = {
0x02, 0x03, 0x04, 0x05
};
-static hda_nid_t alc882_adc_nids[3] = {
- /* ADC0-2 */
- 0x07, 0x08, 0x09,
-};
+/* identical with ALC880 */
+#define alc882_adc_nids alc880_adc_nids
+#define alc882_adc_nids_alt alc880_adc_nids_alt
/* input MUX */
/* FIXME: should be a matrix-type input source selection */
@@ -2536,7 +3105,7 @@ static struct hda_input_mux alc882_capture_source = {
#define alc882_mux_enum_info alc_mux_enum_info
#define alc882_mux_enum_get alc_mux_enum_get
-static int alc882_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int alc882_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
@@ -2561,10 +3130,37 @@ static int alc882_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u
return 1;
}
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc882_sixstack_ch6_init[] = {
+ { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
+ { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { } /* end */
+};
+
+/*
+ * 8ch mode
+ */
+static struct hda_verb alc882_sixstack_ch8_init[] = {
+ { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { } /* end */
+};
+
+static struct hda_channel_mode alc882_sixstack_modes[2] = {
+ { 6, alc882_sixstack_ch6_init },
+ { 8, alc882_sixstack_ch8_init },
+};
+
/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
* Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
*/
-static snd_kcontrol_new_t alc882_base_mixer[] = {
+static struct snd_kcontrol_new alc882_base_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
@@ -2604,6 +3200,17 @@ static snd_kcontrol_new_t alc882_base_mixer[] = {
{ } /* end */
};
+static struct snd_kcontrol_new alc882_chmode_mixer[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Channel Mode",
+ .info = alc_ch_mode_info,
+ .get = alc_ch_mode_get,
+ .put = alc_ch_mode_put,
+ },
+ { } /* end */
+};
+
static struct hda_verb alc882_init_verbs[] = {
/* Front mixer: unmute input/output amp left and right (volume = 0) */
{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
@@ -2684,9 +3291,264 @@ static struct hda_verb alc882_init_verbs[] = {
{ }
};
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc882_auto_init_verbs[] = {
+ /*
+ * Unmute ADC0-2 and set the default input to mic-in
+ */
+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+ * mixer widget
+ * Note: PASD motherboards uses the Line In 2 as the input for front panel
+ * mic (mic 2)
+ */
+ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+ /*
+ * Set up output mixers (0x0c - 0x0f)
+ */
+ /* set vol=0 to output mixers */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ /* set up input amps for analog loopback */
+ /* Amp Indices: DAC = 0, mixer = 1 */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+ /* FIXME: use matrix-type input source selection */
+ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+ /* Input mixer2 */
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+ /* Input mixer3 */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+
+ { }
+};
+
+/* capture mixer elements */
+static struct snd_kcontrol_new alc882_capture_alt_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ * FIXME: the controls appear in the "playback" view!
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 2,
+ .info = alc882_mux_enum_info,
+ .get = alc882_mux_enum_get,
+ .put = alc882_mux_enum_put,
+ },
+ { } /* end */
+};
+
+static struct snd_kcontrol_new alc882_capture_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ * FIXME: the controls appear in the "playback" view!
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 3,
+ .info = alc882_mux_enum_info,
+ .get = alc882_mux_enum_get,
+ .put = alc882_mux_enum_put,
+ },
+ { } /* end */
+};
+
+/* pcm configuration: identiacal with ALC880 */
+#define alc882_pcm_analog_playback alc880_pcm_analog_playback
+#define alc882_pcm_analog_capture alc880_pcm_analog_capture
+#define alc882_pcm_digital_playback alc880_pcm_digital_playback
+#define alc882_pcm_digital_capture alc880_pcm_digital_capture
+
+/*
+ * configuration and preset
+ */
+static struct hda_board_config alc882_cfg_tbl[] = {
+ { .modelname = "3stack-dig", .config = ALC861_3ST_DIG },
+ { .modelname = "6stack-dig", .config = ALC861_6ST_DIG },
+ { .pci_subvendor = 0x1462, .pci_subdevice = 0x6668, .config = ALC882_6ST_DIG }, /* MSI */
+ { .pci_subvendor = 0x105b, .pci_subdevice = 0x6668, .config = ALC882_6ST_DIG }, /* Foxconn */
+ { .pci_subvendor = 0x1019, .pci_subdevice = 0x6668, .config = ALC882_6ST_DIG }, /* ECS */
+ { .modelname = "auto", .config = ALC861_AUTO },
+ {}
+};
+
+static struct alc_config_preset alc882_presets[] = {
+ [ALC882_3ST_DIG] = {
+ .mixers = { alc882_base_mixer },
+ .init_verbs = { alc882_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc882_dac_nids),
+ .dac_nids = alc882_dac_nids,
+ .dig_out_nid = ALC882_DIGOUT_NID,
+ .num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
+ .adc_nids = alc882_adc_nids,
+ .dig_in_nid = ALC882_DIGIN_NID,
+ .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
+ .channel_mode = alc882_ch_modes,
+ .input_mux = &alc882_capture_source,
+ },
+ [ALC882_6ST_DIG] = {
+ .mixers = { alc882_base_mixer, alc882_chmode_mixer },
+ .init_verbs = { alc882_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc882_dac_nids),
+ .dac_nids = alc882_dac_nids,
+ .dig_out_nid = ALC882_DIGOUT_NID,
+ .num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
+ .adc_nids = alc882_adc_nids,
+ .dig_in_nid = ALC882_DIGIN_NID,
+ .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes),
+ .channel_mode = alc882_sixstack_modes,
+ .input_mux = &alc882_capture_source,
+ },
+};
+
+
+/*
+ * BIOS auto configuration
+ */
+static void alc882_auto_set_output_and_unmute(struct hda_codec *codec,
+ hda_nid_t nid, int pin_type,
+ int dac_idx)
+{
+ /* set as output */
+ struct alc_spec *spec = codec->spec;
+ int idx;
+
+ if (spec->multiout.dac_nids[dac_idx] == 0x25)
+ idx = 4;
+ else
+ idx = spec->multiout.dac_nids[dac_idx] - 2;
+
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
+
+}
+
+static void alc882_auto_init_multi_out(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i <= HDA_SIDE; i++) {
+ hda_nid_t nid = spec->autocfg.line_out_pins[i];
+ if (nid)
+ alc882_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
+ }
+}
+
+static void alc882_auto_init_hp_out(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t pin;
+
+ pin = spec->autocfg.hp_pin;
+ if (pin) /* connect to front */
+ alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); /* use dac 0 */
+}
+
+#define alc882_is_input_pin(nid) alc880_is_input_pin(nid)
+#define ALC882_PIN_CD_NID ALC880_PIN_CD_NID
+
+static void alc882_auto_init_analog_input(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ hda_nid_t nid = spec->autocfg.input_pins[i];
+ if (alc882_is_input_pin(nid)) {
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
+ if (nid != ALC882_PIN_CD_NID)
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_MUTE);
+ }
+ }
+}
+
+/* almost identical with ALC880 parser... */
+static int alc882_parse_auto_config(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int err = alc880_parse_auto_config(codec);
+
+ if (err < 0)
+ return err;
+ else if (err > 0)
+ /* hack - override the init verbs */
+ spec->init_verbs[0] = alc882_auto_init_verbs;
+ return err;
+}
+
+/* init callback for auto-configuration model -- overriding the default init */
+static int alc882_auto_init(struct hda_codec *codec)
+{
+ alc_init(codec);
+ alc882_auto_init_multi_out(codec);
+ alc882_auto_init_hp_out(codec);
+ alc882_auto_init_analog_input(codec);
+ return 0;
+}
+
+/*
+ * ALC882 Headphone poll in 3.5.1a or 3.5.2
+ */
+
static int patch_alc882(struct hda_codec *codec)
{
struct alc_spec *spec;
+ int err, board_config;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
@@ -2694,35 +3556,1134 @@ static int patch_alc882(struct hda_codec *codec)
codec->spec = spec;
- spec->mixers[spec->num_mixers] = alc882_base_mixer;
- spec->num_mixers++;
+ board_config = snd_hda_check_board_config(codec, alc882_cfg_tbl);
- spec->multiout.dig_out_nid = ALC880_DIGOUT_NID;
- spec->dig_in_nid = ALC880_DIGIN_NID;
- spec->init_verbs[0] = alc882_init_verbs;
- spec->num_init_verbs = 1;
+ if (board_config < 0 || board_config >= ALC882_MODEL_LAST) {
+ printk(KERN_INFO "hda_codec: Unknown model for ALC882, trying auto-probe from BIOS...\n");
+ board_config = ALC882_AUTO;
+ }
- spec->channel_mode = alc882_ch_modes;
- spec->num_channel_mode = ARRAY_SIZE(alc882_ch_modes);
+ if (board_config == ALC882_AUTO) {
+ /* automatic parse from the BIOS config */
+ err = alc882_parse_auto_config(codec);
+ if (err < 0) {
+ alc_free(codec);
+ return err;
+ } else if (! err) {
+ printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n");
+ board_config = ALC882_3ST_DIG;
+ }
+ }
+
+ if (board_config != ALC882_AUTO)
+ setup_preset(spec, &alc882_presets[board_config]);
spec->stream_name_analog = "ALC882 Analog";
- spec->stream_analog_playback = &alc880_pcm_analog_playback;
- spec->stream_analog_capture = &alc880_pcm_analog_capture;
+ spec->stream_analog_playback = &alc882_pcm_analog_playback;
+ spec->stream_analog_capture = &alc882_pcm_analog_capture;
spec->stream_name_digital = "ALC882 Digital";
- spec->stream_digital_playback = &alc880_pcm_digital_playback;
- spec->stream_digital_capture = &alc880_pcm_digital_capture;
+ spec->stream_digital_playback = &alc882_pcm_digital_playback;
+ spec->stream_digital_capture = &alc882_pcm_digital_capture;
- spec->multiout.max_channels = spec->channel_mode[0].channels;
- spec->multiout.num_dacs = ARRAY_SIZE(alc882_dac_nids);
- spec->multiout.dac_nids = alc882_dac_nids;
+ if (! spec->adc_nids && spec->input_mux) {
+ /* check whether NID 0x07 is valid */
+ unsigned int wcap = get_wcaps(codec, 0x07);
+ wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */
+ if (wcap != AC_WID_AUD_IN) {
+ spec->adc_nids = alc882_adc_nids_alt;
+ spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids_alt);
+ spec->mixers[spec->num_mixers] = alc882_capture_alt_mixer;
+ spec->num_mixers++;
+ } else {
+ spec->adc_nids = alc882_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids);
+ spec->mixers[spec->num_mixers] = alc882_capture_mixer;
+ spec->num_mixers++;
+ }
+ }
+
+ codec->patch_ops = alc_patch_ops;
+ if (board_config == ALC882_AUTO)
+ codec->patch_ops.init = alc882_auto_init;
+
+ return 0;
+}
+
+/*
+ * ALC262 support
+ */
+
+#define ALC262_DIGOUT_NID ALC880_DIGOUT_NID
+#define ALC262_DIGIN_NID ALC880_DIGIN_NID
+
+#define alc262_dac_nids alc260_dac_nids
+#define alc262_adc_nids alc882_adc_nids
+#define alc262_adc_nids_alt alc882_adc_nids_alt
+
+#define alc262_modes alc260_modes
+#define alc262_capture_source alc882_capture_source
- spec->input_mux = &alc882_capture_source;
- spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids);
- spec->adc_nids = alc882_adc_nids;
+static struct snd_kcontrol_new alc262_base_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+ /* HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Beelp Playback Switch", 0x0b, 0x05, HDA_INPUT), */
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .count = 1,
+ .info = alc882_mux_enum_info,
+ .get = alc882_mux_enum_get,
+ .put = alc882_mux_enum_put,
+ },
+ { } /* end */
+};
+
+#define alc262_capture_mixer alc882_capture_mixer
+#define alc262_capture_alt_mixer alc882_capture_alt_mixer
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc262_init_verbs[] = {
+ /*
+ * Unmute ADC0-2 and set the default input to mic-in
+ */
+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+ * mixer widget
+ * Note: PASD motherboards uses the Line In 2 as the input for front panel
+ * mic (mic 2)
+ */
+ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+ /*
+ * Set up output mixers (0x0c - 0x0e)
+ */
+ /* set vol=0 to output mixers */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ /* set up input amps for analog loopback */
+ /* Amp Indices: DAC = 0, mixer = 1 */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+
+ /* FIXME: use matrix-type input source selection */
+ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+ /* Input mixer2 */
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+ /* Input mixer3 */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+
+ { }
+};
+
+/* add playback controls from the parsed DAC table */
+static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg)
+{
+ hda_nid_t nid;
+ int err;
+
+ spec->multiout.num_dacs = 1; /* only use one dac */
+ spec->multiout.dac_nids = spec->private_dac_nids;
+ spec->multiout.dac_nids[0] = 2;
+
+ nid = cfg->line_out_pins[0];
+ if (nid) {
+ if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Front Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT))) < 0)
+ return err;
+ if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Front Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
+ return err;
+ }
+
+ nid = cfg->speaker_pin;
+ if (nid) {
+ if (nid == 0x16) {
+ if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Speaker Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT))) < 0)
+ return err;
+ if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Speaker Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
+ return err;
+ } else {
+ if (! cfg->line_out_pins[0])
+ if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Speaker Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT))) < 0)
+ return err;
+ if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Speaker Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
+ return err;
+ }
+ }
+ nid = cfg->hp_pin;
+ if (nid) {
+ /* spec->multiout.hp_nid = 2; */
+ if (nid == 0x16) {
+ if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Headphone Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT))) < 0)
+ return err;
+ if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Headphone Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
+ return err;
+ } else {
+ if (! cfg->line_out_pins[0])
+ if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Headphone Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT))) < 0)
+ return err;
+ if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Headphone Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
+ return err;
+ }
+ }
+ return 0;
+}
+
+/* identical with ALC880 */
+#define alc262_auto_create_analog_input_ctls alc880_auto_create_analog_input_ctls
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc262_volume_init_verbs[] = {
+ /*
+ * Unmute ADC0-2 and set the default input to mic-in
+ */
+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+ * mixer widget
+ * Note: PASD motherboards uses the Line In 2 as the input for front panel
+ * mic (mic 2)
+ */
+ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+ /*
+ * Set up output mixers (0x0c - 0x0f)
+ */
+ /* set vol=0 to output mixers */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+ /* set up input amps for analog loopback */
+ /* Amp Indices: DAC = 0, mixer = 1 */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+ /* FIXME: use matrix-type input source selection */
+ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+ /* Input mixer2 */
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+ /* Input mixer3 */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+
+ { }
+};
+
+/* pcm configuration: identiacal with ALC880 */
+#define alc262_pcm_analog_playback alc880_pcm_analog_playback
+#define alc262_pcm_analog_capture alc880_pcm_analog_capture
+#define alc262_pcm_digital_playback alc880_pcm_digital_playback
+#define alc262_pcm_digital_capture alc880_pcm_digital_capture
+
+/*
+ * BIOS auto configuration
+ */
+static int alc262_parse_auto_config(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int err;
+ static hda_nid_t alc262_ignore[] = { 0x1d, 0 };
+
+ if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+ alc262_ignore)) < 0)
+ return err;
+ if (! spec->autocfg.line_outs && ! spec->autocfg.speaker_pin &&
+ ! spec->autocfg.hp_pin)
+ return 0; /* can't find valid BIOS pin config */
+ if ((err = alc262_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
+ (err = alc262_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
+ return err;
+
+ spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+ if (spec->autocfg.dig_out_pin)
+ spec->multiout.dig_out_nid = ALC262_DIGOUT_NID;
+ if (spec->autocfg.dig_in_pin)
+ spec->dig_in_nid = ALC262_DIGIN_NID;
+
+ if (spec->kctl_alloc)
+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+ spec->init_verbs[spec->num_init_verbs++] = alc262_volume_init_verbs;
+ spec->input_mux = &spec->private_imux;
+
+ return 1;
+}
+
+#define alc262_auto_init_multi_out alc882_auto_init_multi_out
+#define alc262_auto_init_hp_out alc882_auto_init_hp_out
+#define alc262_auto_init_analog_input alc882_auto_init_analog_input
+
+
+/* init callback for auto-configuration model -- overriding the default init */
+static int alc262_auto_init(struct hda_codec *codec)
+{
+ alc_init(codec);
+ alc262_auto_init_multi_out(codec);
+ alc262_auto_init_hp_out(codec);
+ alc262_auto_init_analog_input(codec);
+ return 0;
+}
+
+/*
+ * configuration and preset
+ */
+static struct hda_board_config alc262_cfg_tbl[] = {
+ { .modelname = "basic", .config = ALC262_BASIC },
+ { .modelname = "auto", .config = ALC262_AUTO },
+ {}
+};
+
+static struct alc_config_preset alc262_presets[] = {
+ [ALC262_BASIC] = {
+ .mixers = { alc262_base_mixer },
+ .init_verbs = { alc262_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc262_dac_nids),
+ .dac_nids = alc262_dac_nids,
+ .hp_nid = 0x03,
+ .num_channel_mode = ARRAY_SIZE(alc262_modes),
+ .channel_mode = alc262_modes,
+ .input_mux = &alc262_capture_source,
+ },
+};
+
+static int patch_alc262(struct hda_codec *codec)
+{
+ struct alc_spec *spec;
+ int board_config;
+ int err;
+
+ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+#if 0
+ /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is under-run */
+ {
+ int tmp;
+ snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7);
+ tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0);
+ snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7);
+ snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x80);
+ }
+#endif
+
+ board_config = snd_hda_check_board_config(codec, alc262_cfg_tbl);
+ if (board_config < 0 || board_config >= ALC262_MODEL_LAST) {
+ printk(KERN_INFO "hda_codec: Unknown model for ALC262, trying auto-probe from BIOS...\n");
+ board_config = ALC262_AUTO;
+ }
+
+ if (board_config == ALC262_AUTO) {
+ /* automatic parse from the BIOS config */
+ err = alc262_parse_auto_config(codec);
+ if (err < 0) {
+ alc_free(codec);
+ return err;
+ } else if (! err) {
+ printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n");
+ board_config = ALC262_BASIC;
+ }
+ }
+
+ if (board_config != ALC262_AUTO)
+ setup_preset(spec, &alc262_presets[board_config]);
+
+ spec->stream_name_analog = "ALC262 Analog";
+ spec->stream_analog_playback = &alc262_pcm_analog_playback;
+ spec->stream_analog_capture = &alc262_pcm_analog_capture;
+
+ spec->stream_name_digital = "ALC262 Digital";
+ spec->stream_digital_playback = &alc262_pcm_digital_playback;
+ spec->stream_digital_capture = &alc262_pcm_digital_capture;
+
+ if (! spec->adc_nids && spec->input_mux) {
+ /* check whether NID 0x07 is valid */
+ unsigned int wcap = get_wcaps(codec, 0x07);
+
+ wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */
+ if (wcap != AC_WID_AUD_IN) {
+ spec->adc_nids = alc262_adc_nids_alt;
+ spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids_alt);
+ spec->mixers[spec->num_mixers] = alc262_capture_alt_mixer;
+ spec->num_mixers++;
+ } else {
+ spec->adc_nids = alc262_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids);
+ spec->mixers[spec->num_mixers] = alc262_capture_mixer;
+ spec->num_mixers++;
+ }
+ }
codec->patch_ops = alc_patch_ops;
+ if (board_config == ALC262_AUTO)
+ codec->patch_ops.init = alc262_auto_init;
+
+ return 0;
+}
+
+
+/*
+ * ALC861 channel source setting (2/6 channel selection for 3-stack)
+ */
+
+/*
+ * set the path ways for 2 channel output
+ * need to set the codec line out and mic 1 pin widgets to inputs
+ */
+static struct hda_verb alc861_threestack_ch2_init[] = {
+ /* set pin widget 1Ah (line in) for input */
+ { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+ /* set pin widget 18h (mic1/2) for input, for mic also enable the vref */
+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c },
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, //mic
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, //line in
+ { } /* end */
+};
+/*
+ * 6ch mode
+ * need to set the codec line out and mic 1 pin widgets to outputs
+ */
+static struct hda_verb alc861_threestack_ch6_init[] = {
+ /* set pin widget 1Ah (line in) for output (Back Surround)*/
+ { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+ /* set pin widget 18h (mic1) for output (CLFE)*/
+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+
+ { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 },
+ { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 },
+
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, //mic
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, //line in
+ { } /* end */
+};
+
+static struct hda_channel_mode alc861_threestack_modes[2] = {
+ { 2, alc861_threestack_ch2_init },
+ { 6, alc861_threestack_ch6_init },
+};
+
+/* patch-ALC861 */
+
+static struct snd_kcontrol_new alc861_base_mixer[] = {
+ /* output mixer control */
+ HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT),
+
+ /*Input mixer control */
+ /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
+
+ /* Capture mixer control */
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .count = 1,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
+ .put = alc_mux_enum_put,
+ },
+ { } /* end */
+};
+
+static struct snd_kcontrol_new alc861_3ST_mixer[] = {
+ /* output mixer control */
+ HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
+ /*HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), */
+
+ /* Input mixer control */
+ /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
+
+ /* Capture mixer control */
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .count = 1,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
+ .put = alc_mux_enum_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Channel Mode",
+ .info = alc_ch_mode_info,
+ .get = alc_ch_mode_get,
+ .put = alc_ch_mode_put,
+ .private_value = ARRAY_SIZE(alc861_threestack_modes),
+ },
+ { } /* end */
+};
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc861_base_init_verbs[] = {
+ /*
+ * Unmute ADC0 and set the default input to mic-in
+ */
+ /* port-A for surround (rear panel) */
+ { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+ { 0x0e, AC_VERB_SET_CONNECT_SEL, 0x00 },
+ /* port-B for mic-in (rear panel) with vref */
+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+ /* port-C for line-in (rear panel) */
+ { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+ /* port-D for Front */
+ { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+ { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
+ /* port-E for HP out (front panel) */
+ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+ /* route front PCM to HP */
+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ /* port-F for mic-in (front panel) with vref */
+ { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+ /* port-G for CLFE (rear panel) */
+ { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+ { 0x1f, AC_VERB_SET_CONNECT_SEL, 0x00 },
+ /* port-H for side (rear panel) */
+ { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+ { 0x20, AC_VERB_SET_CONNECT_SEL, 0x00 },
+ /* CD-in */
+ { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+ /* route front mic to ADC1*/
+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+ /* Unmute DAC0~3 & spdif out*/
+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+ /* Unmute Mixer 14 (mic) 1c (Line in)*/
+ {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+ /* Unmute Stereo Mixer 15 */
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, //Output 0~12 step
+
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, // hp used DAC 3 (Front)
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+
+ { }
+};
+
+static struct hda_verb alc861_threestack_init_verbs[] = {
+ /*
+ * Unmute ADC0 and set the default input to mic-in
+ */
+ /* port-A for surround (rear panel) */
+ { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
+ /* port-B for mic-in (rear panel) with vref */
+ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+ /* port-C for line-in (rear panel) */
+ { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+ /* port-D for Front */
+ { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+ { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
+ /* port-E for HP out (front panel) */
+ { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+ /* route front PCM to HP */
+ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ /* port-F for mic-in (front panel) with vref */
+ { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+ /* port-G for CLFE (rear panel) */
+ { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
+ /* port-H for side (rear panel) */
+ { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
+ /* CD-in */
+ { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+ /* route front mic to ADC1*/
+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ /* Unmute DAC0~3 & spdif out*/
+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+ /* Unmute Mixer 14 (mic) 1c (Line in)*/
+ {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+ /* Unmute Stereo Mixer 15 */
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, //Output 0~12 step
+
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, // hp used DAC 3 (Front)
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ { }
+};
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc861_auto_init_verbs[] = {
+ /*
+ * Unmute ADC0 and set the default input to mic-in
+ */
+// {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+ /* Unmute DAC0~3 & spdif out*/
+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+ /* Unmute Mixer 14 (mic) 1c (Line in)*/
+ {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+ /* Unmute Stereo Mixer 15 */
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c},
+
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+
+ {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, // set Mic 1
+
+ { }
+};
+
+/* pcm configuration: identiacal with ALC880 */
+#define alc861_pcm_analog_playback alc880_pcm_analog_playback
+#define alc861_pcm_analog_capture alc880_pcm_analog_capture
+#define alc861_pcm_digital_playback alc880_pcm_digital_playback
+#define alc861_pcm_digital_capture alc880_pcm_digital_capture
+
+
+#define ALC861_DIGOUT_NID 0x07
+
+static struct hda_channel_mode alc861_8ch_modes[1] = {
+ { 8, NULL }
+};
+
+static hda_nid_t alc861_dac_nids[4] = {
+ /* front, surround, clfe, side */
+ 0x03, 0x06, 0x05, 0x04
+};
+
+static hda_nid_t alc861_adc_nids[1] = {
+ /* ADC0-2 */
+ 0x08,
+};
+
+static struct hda_input_mux alc861_capture_source = {
+ .num_items = 5,
+ .items = {
+ { "Mic", 0x0 },
+ { "Front Mic", 0x3 },
+ { "Line", 0x1 },
+ { "CD", 0x4 },
+ { "Mixer", 0x5 },
+ },
+};
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int alc861_auto_fill_dac_nids(struct alc_spec *spec, const struct auto_pin_cfg *cfg)
+{
+ int i;
+ hda_nid_t nid;
+
+ spec->multiout.dac_nids = spec->private_dac_nids;
+ for (i = 0; i < cfg->line_outs; i++) {
+ nid = cfg->line_out_pins[i];
+ if (nid) {
+ if (i >= ARRAY_SIZE(alc861_dac_nids))
+ continue;
+ spec->multiout.dac_nids[i] = alc861_dac_nids[i];
+ }
+ }
+ spec->multiout.num_dacs = cfg->line_outs;
+ return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int alc861_auto_create_multi_out_ctls(struct alc_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ char name[32];
+ static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
+ hda_nid_t nid;
+ int i, idx, err;
+
+ for (i = 0; i < cfg->line_outs; i++) {
+ nid = spec->multiout.dac_nids[i];
+ if (! nid)
+ continue;
+ if (nid == 0x05) {
+ /* Center/LFE */
+ if ((err = add_control(spec, ALC_CTL_BIND_MUTE, "Center Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT))) < 0)
+ return err;
+ if ((err = add_control(spec, ALC_CTL_BIND_MUTE, "LFE Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
+ return err;
+ } else {
+ for (idx = 0; idx < ARRAY_SIZE(alc861_dac_nids) - 1; idx++)
+ if (nid == alc861_dac_nids[idx])
+ break;
+ sprintf(name, "%s Playback Switch", chname[idx]);
+ if ((err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
+ return err;
+ }
+ }
+ return 0;
+}
+
+static int alc861_auto_create_hp_ctls(struct alc_spec *spec, hda_nid_t pin)
+{
+ int err;
+ hda_nid_t nid;
+
+ if (! pin)
+ return 0;
+ if ((pin >= 0x0b && pin <= 0x10) || pin == 0x1f || pin == 0x20) {
+ nid = 0x03;
+ if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Headphone Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
+ return err;
+ spec->multiout.hp_nid = nid;
+ }
+ return 0;
+}
+
+/* create playback/capture controls for input pins */
+static int alc861_auto_create_analog_input_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg)
+{
+ struct hda_input_mux *imux = &spec->private_imux;
+ int i, err, idx, idx1;
+
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ switch(cfg->input_pins[i]) {
+ case 0x0c:
+ idx1 = 1;
+ idx = 2; // Line In
+ break;
+ case 0x0f:
+ idx1 = 2;
+ idx = 2; // Line In
+ break;
+ case 0x0d:
+ idx1 = 0;
+ idx = 1; // Mic In
+ break;
+ case 0x10:
+ idx1 = 3;
+ idx = 1; // Mic In
+ break;
+ case 0x11:
+ idx1 = 4;
+ idx = 0; // CD
+ break;
+ default:
+ continue;
+ }
+
+ err = new_analog_input(spec, cfg->input_pins[i],
+ auto_pin_cfg_labels[i], idx, 0x15);
+ if (err < 0)
+ return err;
+
+ imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
+ imux->items[imux->num_items].index = idx1;
+ imux->num_items++;
+ }
+ return 0;
+}
+
+static struct snd_kcontrol_new alc861_capture_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ *FIXME: the controls appear in the "playback" view!
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 1,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
+ .put = alc_mux_enum_put,
+ },
+ { } /* end */
+};
+
+static void alc861_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid,
+ int pin_type, int dac_idx)
+{
+ /* set as output */
+
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+ snd_hda_codec_write(codec, dac_idx, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+
+}
+
+static void alc861_auto_init_multi_out(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->autocfg.line_outs; i++) {
+ hda_nid_t nid = spec->autocfg.line_out_pins[i];
+ if (nid)
+ alc861_auto_set_output_and_unmute(codec, nid, PIN_OUT, spec->multiout.dac_nids[i]);
+ }
+}
+
+static void alc861_auto_init_hp_out(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t pin;
+
+ pin = spec->autocfg.hp_pin;
+ if (pin) /* connect to front */
+ alc861_auto_set_output_and_unmute(codec, pin, PIN_HP, spec->multiout.dac_nids[0]);
+}
+
+static void alc861_auto_init_analog_input(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ hda_nid_t nid = spec->autocfg.input_pins[i];
+ if ((nid>=0x0c) && (nid <=0x11)) {
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
+ }
+ }
+}
+
+/* parse the BIOS configuration and set up the alc_spec */
+/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
+static int alc861_parse_auto_config(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int err;
+ static hda_nid_t alc861_ignore[] = { 0x1d, 0 };
+
+ if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+ alc861_ignore)) < 0)
+ return err;
+ if (! spec->autocfg.line_outs && ! spec->autocfg.speaker_pin &&
+ ! spec->autocfg.hp_pin)
+ return 0; /* can't find valid BIOS pin config */
+
+ if ((err = alc861_auto_fill_dac_nids(spec, &spec->autocfg)) < 0 ||
+ (err = alc861_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
+ (err = alc861_auto_create_hp_ctls(spec, spec->autocfg.hp_pin)) < 0 ||
+ (err = alc861_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
+ return err;
+
+ spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+ if (spec->autocfg.dig_out_pin)
+ spec->multiout.dig_out_nid = ALC861_DIGOUT_NID;
+
+ if (spec->kctl_alloc)
+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+ spec->init_verbs[spec->num_init_verbs++] = alc861_auto_init_verbs;
+
+ spec->input_mux = &spec->private_imux;
+
+ spec->adc_nids = alc861_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids);
+ spec->mixers[spec->num_mixers] = alc861_capture_mixer;
+ spec->num_mixers++;
+
+ return 1;
+}
+
+/* init callback for auto-configuration model -- overriding the default init */
+static int alc861_auto_init(struct hda_codec *codec)
+{
+ alc_init(codec);
+ alc861_auto_init_multi_out(codec);
+ alc861_auto_init_hp_out(codec);
+ alc861_auto_init_analog_input(codec);
+
+ return 0;
+}
+
+
+/*
+ * configuration and preset
+ */
+static struct hda_board_config alc861_cfg_tbl[] = {
+ { .modelname = "3stack", .config = ALC861_3ST },
+ { .pci_subvendor = 0x8086, .pci_subdevice = 0xd600, .config = ALC861_3ST },
+ { .modelname = "3stack-dig", .config = ALC861_3ST_DIG },
+ { .modelname = "6stack-dig", .config = ALC861_6ST_DIG },
+ { .modelname = "auto", .config = ALC861_AUTO },
+ {}
+};
+
+static struct alc_config_preset alc861_presets[] = {
+ [ALC861_3ST] = {
+ .mixers = { alc861_3ST_mixer },
+ .init_verbs = { alc861_threestack_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc861_dac_nids),
+ .dac_nids = alc861_dac_nids,
+ .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
+ .channel_mode = alc861_threestack_modes,
+ .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
+ .adc_nids = alc861_adc_nids,
+ .input_mux = &alc861_capture_source,
+ },
+ [ALC861_3ST_DIG] = {
+ .mixers = { alc861_base_mixer },
+ .init_verbs = { alc861_threestack_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc861_dac_nids),
+ .dac_nids = alc861_dac_nids,
+ .dig_out_nid = ALC861_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
+ .channel_mode = alc861_threestack_modes,
+ .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
+ .adc_nids = alc861_adc_nids,
+ .input_mux = &alc861_capture_source,
+ },
+ [ALC861_6ST_DIG] = {
+ .mixers = { alc861_base_mixer },
+ .init_verbs = { alc861_base_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc861_dac_nids),
+ .dac_nids = alc861_dac_nids,
+ .dig_out_nid = ALC861_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc861_8ch_modes),
+ .channel_mode = alc861_8ch_modes,
+ .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
+ .adc_nids = alc861_adc_nids,
+ .input_mux = &alc861_capture_source,
+ },
+};
+
+
+static int patch_alc861(struct hda_codec *codec)
+{
+ struct alc_spec *spec;
+ int board_config;
+ int err;
+
+ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ board_config = snd_hda_check_board_config(codec, alc861_cfg_tbl);
+ if (board_config < 0 || board_config >= ALC861_MODEL_LAST) {
+ printk(KERN_INFO "hda_codec: Unknown model for ALC861, trying auto-probe from BIOS...\n");
+ board_config = ALC861_AUTO;
+ }
+
+ if (board_config == ALC861_AUTO) {
+ /* automatic parse from the BIOS config */
+ err = alc861_parse_auto_config(codec);
+ if (err < 0) {
+ alc_free(codec);
+ return err;
+ } else if (! err) {
+ printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n");
+ board_config = ALC861_3ST_DIG;
+ }
+ }
+
+ if (board_config != ALC861_AUTO)
+ setup_preset(spec, &alc861_presets[board_config]);
+
+ spec->stream_name_analog = "ALC861 Analog";
+ spec->stream_analog_playback = &alc861_pcm_analog_playback;
+ spec->stream_analog_capture = &alc861_pcm_analog_capture;
+
+ spec->stream_name_digital = "ALC861 Digital";
+ spec->stream_digital_playback = &alc861_pcm_digital_playback;
+ spec->stream_digital_capture = &alc861_pcm_digital_capture;
+
+ codec->patch_ops = alc_patch_ops;
+ if (board_config == ALC861_AUTO)
+ codec->patch_ops.init = alc861_auto_init;
+
return 0;
}
@@ -2731,7 +4692,11 @@ static int patch_alc882(struct hda_codec *codec)
*/
struct hda_codec_preset snd_hda_preset_realtek[] = {
{ .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
+ { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
{ .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
{ .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
+ { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 },
+ { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
+ { .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 },
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c
index 9c7fe0b3200a..8f8840e6002b 100644
--- a/sound/pci/hda/patch_si3054.c
+++ b/sound/pci/hda/patch_si3054.c
@@ -95,8 +95,8 @@ struct si3054_spec {
#define PRIVATE_REG(val) ((val>>16)&0xffff)
#define PRIVATE_MASK(val) (val&0xffff)
-static int si3054_switch_info(snd_kcontrol_t *kcontrol,
- snd_ctl_elem_info_t *uinfo)
+static int si3054_switch_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
@@ -105,8 +105,8 @@ static int si3054_switch_info(snd_kcontrol_t *kcontrol,
return 0;
}
-static int si3054_switch_get(snd_kcontrol_t *kcontrol,
- snd_ctl_elem_value_t *uvalue)
+static int si3054_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
u16 reg = PRIVATE_REG(kcontrol->private_value);
@@ -115,8 +115,8 @@ static int si3054_switch_get(snd_kcontrol_t *kcontrol,
return 0;
}
-static int si3054_switch_put(snd_kcontrol_t *kcontrol,
- snd_ctl_elem_value_t *uvalue)
+static int si3054_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
u16 reg = PRIVATE_REG(kcontrol->private_value);
@@ -138,7 +138,7 @@ static int si3054_switch_put(snd_kcontrol_t *kcontrol,
}
-static snd_kcontrol_new_t si3054_modem_mixer[] = {
+static struct snd_kcontrol_new si3054_modem_mixer[] = {
SI3054_KCONTROL("Off-hook Switch", SI3054_GPIO_CONTROL, SI3054_GPIO_OH),
SI3054_KCONTROL("Caller ID Switch", SI3054_GPIO_CONTROL, SI3054_GPIO_CID),
{}
@@ -158,7 +158,7 @@ static int si3054_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
u16 val;
@@ -175,10 +175,10 @@ static int si3054_pcm_prepare(struct hda_pcm_stream *hinfo,
static int si3054_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
static unsigned int rates[] = { 8000, 9600, 16000 };
- static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+ static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
@@ -295,6 +295,7 @@ static int patch_si3054(struct hda_codec *codec)
* patch entries
*/
struct hda_codec_preset snd_hda_preset_si3054[] = {
+ { .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
{}
};
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 33a8adaea768..61903848cd43 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -4,7 +4,7 @@
* HD audio interface patch for SigmaTel STAC92xx
*
* Copyright (c) 2005 Embedded Alley Solutions, Inc.
- * <matt@embeddedalley.com>
+ * Matt Porter <mporter@embeddedalley.com>
*
* Based on patch_cmedia.c and patch_realtek.c
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
@@ -34,17 +34,22 @@
#include "hda_codec.h"
#include "hda_local.h"
-#undef STAC_TEST
-
#define NUM_CONTROL_ALLOC 32
#define STAC_HP_EVENT 0x37
#define STAC_UNSOL_ENABLE (AC_USRSP_EN | STAC_HP_EVENT)
+#define STAC_REF 0
+#define STAC_D945GTP3 1
+#define STAC_D945GTP5 2
+
struct sigmatel_spec {
- snd_kcontrol_new_t *mixers[4];
+ struct snd_kcontrol_new *mixers[4];
unsigned int num_mixers;
+ int board_config;
unsigned int surr_switch: 1;
+ unsigned int line_switch: 1;
+ unsigned int mic_switch: 1;
/* playback */
struct hda_multi_out multiout;
@@ -57,31 +62,28 @@ struct sigmatel_spec {
unsigned int num_muxes;
hda_nid_t dig_in_nid;
-#ifdef STAC_TEST
/* pin widgets */
hda_nid_t *pin_nids;
unsigned int num_pins;
unsigned int *pin_configs;
-#endif
/* codec specific stuff */
struct hda_verb *init;
- snd_kcontrol_new_t *mixer;
+ struct snd_kcontrol_new *mixer;
/* capture source */
struct hda_input_mux *input_mux;
unsigned int cur_mux[2];
- /* channel mode */
- unsigned int num_ch_modes;
- unsigned int cur_ch_mode;
+ /* i/o switches */
+ unsigned int io_switch[2];
struct hda_pcm pcm_rec[2]; /* PCM information */
/* dynamic controls and input_mux */
struct auto_pin_cfg autocfg;
unsigned int num_kctl_alloc, num_kctl_used;
- snd_kcontrol_new_t *kctl_alloc;
+ struct snd_kcontrol_new *kctl_alloc;
struct hda_input_mux private_imux;
};
@@ -105,7 +107,6 @@ static hda_nid_t stac922x_mux_nids[2] = {
0x12, 0x13,
};
-#ifdef STAC_TEST
static hda_nid_t stac9200_pin_nids[8] = {
0x08, 0x09, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
};
@@ -114,16 +115,15 @@ static hda_nid_t stac922x_pin_nids[10] = {
0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x10, 0x11, 0x15, 0x1b,
};
-#endif
-static int stac92xx_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
return snd_hda_input_mux_info(spec->input_mux, uinfo);
}
-static int stac92xx_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int stac92xx_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
@@ -133,7 +133,7 @@ static int stac92xx_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
return 0;
}
-static int stac92xx_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
@@ -155,50 +155,7 @@ static struct hda_verb stac922x_core_init[] = {
{}
};
-static int stac922x_channel_modes[3] = {2, 6, 8};
-
-static int stac922x_ch_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = spec->num_ch_modes;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- sprintf(uinfo->value.enumerated.name, "%dch",
- stac922x_channel_modes[uinfo->value.enumerated.item]);
- return 0;
-}
-
-static int stac922x_ch_mode_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
-
- ucontrol->value.enumerated.item[0] = spec->cur_ch_mode;
- return 0;
-}
-
-static int stac922x_ch_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
-
- if (ucontrol->value.enumerated.item[0] >= spec->num_ch_modes)
- ucontrol->value.enumerated.item[0] = spec->num_ch_modes;
- if (ucontrol->value.enumerated.item[0] == spec->cur_ch_mode &&
- ! codec->in_resume)
- return 0;
-
- spec->cur_ch_mode = ucontrol->value.enumerated.item[0];
- spec->multiout.max_channels = stac922x_channel_modes[spec->cur_ch_mode];
-
- return 1;
-}
-
-static snd_kcontrol_new_t stac9200_mixer[] = {
+static struct snd_kcontrol_new stac9200_mixer[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
{
@@ -216,7 +173,7 @@ static snd_kcontrol_new_t stac9200_mixer[] = {
};
/* This needs to be generated dynamically based on sequence */
-static snd_kcontrol_new_t stac922x_mixer[] = {
+static struct snd_kcontrol_new stac922x_mixer[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Input Source",
@@ -226,22 +183,10 @@ static snd_kcontrol_new_t stac922x_mixer[] = {
.put = stac92xx_mux_enum_put,
},
HDA_CODEC_VOLUME("Capture Volume", 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x17, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Mux Capture Volume", 0x12, 0x0, HDA_OUTPUT),
{ } /* end */
};
-static snd_kcontrol_new_t stac922x_ch_mode_mixer[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = stac922x_ch_mode_info,
- .get = stac922x_ch_mode_get,
- .put = stac922x_ch_mode_put,
- },
- { } /* end */
-};
-
static int stac92xx_build_controls(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
@@ -258,11 +203,6 @@ static int stac92xx_build_controls(struct hda_codec *codec)
return err;
}
- if (spec->surr_switch) {
- err = snd_hda_add_new_ctls(codec, stac922x_ch_mode_mixer);
- if (err < 0)
- return err;
- }
if (spec->multiout.dig_out_nid) {
err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
if (err < 0)
@@ -276,18 +216,67 @@ static int stac92xx_build_controls(struct hda_codec *codec)
return 0;
}
-#ifdef STAC_TEST
-static unsigned int stac9200_pin_configs[8] = {
+static unsigned int ref9200_pin_configs[8] = {
0x01c47010, 0x01447010, 0x0221401f, 0x01114010,
0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
};
-static unsigned int stac922x_pin_configs[10] = {
- 0x01014010, 0x01014011, 0x01014012, 0x0221401f,
- 0x01813122, 0x01014014, 0x01441030, 0x01c41030,
+static unsigned int *stac9200_brd_tbl[] = {
+ ref9200_pin_configs,
+};
+
+static struct hda_board_config stac9200_cfg_tbl[] = {
+ { .modelname = "ref",
+ .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2668, /* DFI LanParty */
+ .config = STAC_REF },
+ {} /* terminator */
+};
+
+static unsigned int ref922x_pin_configs[10] = {
+ 0x01014010, 0x01016011, 0x01012012, 0x0221401f,
+ 0x01813122, 0x01011014, 0x01441030, 0x01c41030,
0x40000100, 0x40000100,
};
+static unsigned int d945gtp3_pin_configs[10] = {
+ 0x0221401f, 0x01a19022, 0x01813021, 0x01114010,
+ 0x40000100, 0x40000100, 0x40000100, 0x40000100,
+ 0x02a19120, 0x40000100,
+};
+
+static unsigned int d945gtp5_pin_configs[10] = {
+ 0x0221401f, 0x01111012, 0x01813024, 0x01114010,
+ 0x01a19021, 0x01116011, 0x01452130, 0x40000100,
+ 0x02a19320, 0x40000100,
+};
+
+static unsigned int *stac922x_brd_tbl[] = {
+ ref922x_pin_configs,
+ d945gtp3_pin_configs,
+ d945gtp5_pin_configs,
+};
+
+static struct hda_board_config stac922x_cfg_tbl[] = {
+ { .modelname = "ref",
+ .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x2668, /* DFI LanParty */
+ .config = STAC_REF }, /* SigmaTel reference board */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x0101,
+ .config = STAC_D945GTP3 }, /* Intel D945GTP - 3 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x0404,
+ .config = STAC_D945GTP5 }, /* Intel D945GTP - 5 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x0303,
+ .config = STAC_D945GTP5 }, /* Intel D945GNT - 5 Stack */
+ { .pci_subvendor = PCI_VENDOR_ID_INTEL,
+ .pci_subdevice = 0x0013,
+ .config = STAC_D945GTP5 }, /* Intel D955XBK - 5 Stack */
+ {} /* terminator */
+};
+
static void stac92xx_set_config_regs(struct hda_codec *codec)
{
int i;
@@ -310,86 +299,34 @@ static void stac92xx_set_config_regs(struct hda_codec *codec)
pin_cfg = snd_hda_codec_read(codec, spec->pin_nids[i], 0,
AC_VERB_GET_CONFIG_DEFAULT,
0x00);
- printk("pin nid %2.2x pin config %8.8x\n", spec->pin_nids[i], pin_cfg);
+ snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x pin config %8.8x\n", spec->pin_nids[i], pin_cfg);
}
}
-#endif
/*
* Analog playback callbacks
*/
static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct sigmatel_spec *spec = codec->spec;
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
}
-/*
- * set up the i/o for analog out
- * when the digital out is available, copy the front out to digital out, too.
- */
-static int stac92xx_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_out *mout,
- unsigned int stream_tag,
- unsigned int format,
- snd_pcm_substream_t *substream)
-{
- hda_nid_t *nids = mout->dac_nids;
- int chs = substream->runtime->channels;
- int i;
-
- down(&codec->spdif_mutex);
- if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
- if (chs == 2 &&
- snd_hda_is_supported_format(codec, mout->dig_out_nid, format) &&
- ! (codec->spdif_status & IEC958_AES0_NONAUDIO)) {
- mout->dig_out_used = HDA_DIG_ANALOG_DUP;
- /* setup digital receiver */
- snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
- stream_tag, 0, format);
- } else {
- mout->dig_out_used = 0;
- snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);
- }
- }
- up(&codec->spdif_mutex);
-
- /* front */
- snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 0, format);
- if (mout->hp_nid)
- /* headphone out will just decode front left/right (stereo) */
- snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format);
- /* surrounds */
- if (mout->max_channels > 2)
- for (i = 1; i < mout->num_dacs; i++) {
- if ((mout->max_channels == 6) && (i == 3))
- break;
- if (chs >= (i + 1) * 2) /* independent out */
- snd_hda_codec_setup_stream(codec, nids[i], stream_tag, i * 2,
- format);
- else /* copy front */
- snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 0,
- format);
- }
- return 0;
-}
-
-
static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct sigmatel_spec *spec = codec->spec;
- return stac92xx_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
+ return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, format, substream);
}
static int stac92xx_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct sigmatel_spec *spec = codec->spec;
return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
@@ -400,7 +337,7 @@ static int stac92xx_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
*/
static int stac92xx_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct sigmatel_spec *spec = codec->spec;
return snd_hda_multi_out_dig_open(codec, &spec->multiout);
@@ -408,7 +345,7 @@ static int stac92xx_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
static int stac92xx_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct sigmatel_spec *spec = codec->spec;
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
@@ -422,7 +359,7 @@ static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct sigmatel_spec *spec = codec->spec;
@@ -433,7 +370,7 @@ static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
- snd_pcm_substream_t *substream)
+ struct snd_pcm_substream *substream)
{
struct sigmatel_spec *spec = codec->spec;
@@ -511,20 +448,76 @@ static int stac92xx_build_pcms(struct hda_codec *codec)
return 0;
}
+static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type)
+
+{
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+}
+
+static int stac92xx_io_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ int io_idx = kcontrol-> private_value & 0xff;
+
+ ucontrol->value.integer.value[0] = spec->io_switch[io_idx];
+ return 0;
+}
+
+static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ hda_nid_t nid = kcontrol->private_value >> 8;
+ int io_idx = kcontrol-> private_value & 0xff;
+ unsigned short val = ucontrol->value.integer.value[0];
+
+ spec->io_switch[io_idx] = val;
+
+ if (val)
+ stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
+ else
+ stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_IN_EN);
+
+ return 1;
+}
+
+#define STAC_CODEC_IO_SWITCH(xname, xpval) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = 0, \
+ .info = stac92xx_io_switch_info, \
+ .get = stac92xx_io_switch_get, \
+ .put = stac92xx_io_switch_put, \
+ .private_value = xpval, \
+ }
+
+
enum {
STAC_CTL_WIDGET_VOL,
STAC_CTL_WIDGET_MUTE,
+ STAC_CTL_WIDGET_IO_SWITCH,
};
-static snd_kcontrol_new_t stac92xx_control_templates[] = {
+static struct snd_kcontrol_new stac92xx_control_templates[] = {
HDA_CODEC_VOLUME(NULL, 0, 0, 0),
HDA_CODEC_MUTE(NULL, 0, 0, 0),
+ STAC_CODEC_IO_SWITCH(NULL, 0),
};
/* add dynamic controls */
static int stac92xx_add_control(struct sigmatel_spec *spec, int type, const char *name, unsigned long val)
{
- snd_kcontrol_new_t *knew;
+ struct snd_kcontrol_new *knew;
if (spec->num_kctl_used >= spec->num_kctl_alloc) {
int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
@@ -550,6 +543,51 @@ static int stac92xx_add_control(struct sigmatel_spec *spec, int type, const char
return 0;
}
+/* flag inputs as additional dynamic lineouts */
+static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cfg *cfg)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ switch (cfg->line_outs) {
+ case 3:
+ /* add line-in as side */
+ if (cfg->input_pins[AUTO_PIN_LINE]) {
+ cfg->line_out_pins[3] = cfg->input_pins[AUTO_PIN_LINE];
+ spec->line_switch = 1;
+ cfg->line_outs++;
+ }
+ break;
+ case 2:
+ /* add line-in as clfe and mic as side */
+ if (cfg->input_pins[AUTO_PIN_LINE]) {
+ cfg->line_out_pins[2] = cfg->input_pins[AUTO_PIN_LINE];
+ spec->line_switch = 1;
+ cfg->line_outs++;
+ }
+ if (cfg->input_pins[AUTO_PIN_MIC]) {
+ cfg->line_out_pins[3] = cfg->input_pins[AUTO_PIN_MIC];
+ spec->mic_switch = 1;
+ cfg->line_outs++;
+ }
+ break;
+ case 1:
+ /* add line-in as surr and mic as clfe */
+ if (cfg->input_pins[AUTO_PIN_LINE]) {
+ cfg->line_out_pins[1] = cfg->input_pins[AUTO_PIN_LINE];
+ spec->line_switch = 1;
+ cfg->line_outs++;
+ }
+ if (cfg->input_pins[AUTO_PIN_MIC]) {
+ cfg->line_out_pins[2] = cfg->input_pins[AUTO_PIN_MIC];
+ spec->mic_switch = 1;
+ cfg->line_outs++;
+ }
+ break;
+ }
+
+ return 0;
+}
+
/* fill in the dac_nids table from the parsed pin configuration */
static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
{
@@ -578,7 +616,7 @@ static int stac92xx_auto_create_multi_out_ctls(struct sigmatel_spec *spec, const
int i, err;
for (i = 0; i < cfg->line_outs; i++) {
- if (! spec->multiout.dac_nids[i])
+ if (!spec->multiout.dac_nids[i])
continue;
nid = spec->multiout.dac_nids[i];
@@ -609,6 +647,14 @@ static int stac92xx_auto_create_multi_out_ctls(struct sigmatel_spec *spec, const
}
}
+ if (spec->line_switch)
+ if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, "Line In as Output Switch", cfg->input_pins[AUTO_PIN_LINE] << 8)) < 0)
+ return err;
+
+ if (spec->mic_switch)
+ if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, "Mic as Output Switch", (cfg->input_pins[AUTO_PIN_MIC] << 8) | 1)) < 0)
+ return err;
+
return 0;
}
@@ -624,7 +670,7 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec, struct auto_pin
if (! pin)
return 0;
- wid_caps = snd_hda_param_read(codec, pin, AC_PAR_AUDIO_WIDGET_CAP);
+ wid_caps = get_wcaps(codec, pin);
if (wid_caps & AC_WCAP_UNSOL_CAP)
/* Enable unsolicited responses on the HP widget */
snd_hda_codec_write(codec, pin, 0,
@@ -656,9 +702,6 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec, struct auto_pin
static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
{
struct sigmatel_spec *spec = codec->spec;
- static char *labels[AUTO_PIN_LAST] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux"
- };
struct hda_input_mux *imux = &spec->private_imux;
hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
int i, j, k;
@@ -666,7 +709,10 @@ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const
for (i = 0; i < AUTO_PIN_LAST; i++) {
int index = -1;
if (cfg->input_pins[i]) {
- imux->items[imux->num_items].label = labels[i];
+ /* Enable active pin widget as an input */
+ stac92xx_auto_set_pinctl(codec, cfg->input_pins[i], AC_PINCTL_IN_EN);
+
+ imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
for (j=0; j<spec->num_muxes; j++) {
int num_cons = snd_hda_get_connections(codec, spec->mux_nids[j], con_lst, HDA_MAX_NUM_INPUTS);
@@ -686,12 +732,6 @@ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const
return 0;
}
-static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type)
-
-{
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
-}
-
static void stac92xx_auto_init_multi_out(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
@@ -718,7 +758,9 @@ static int stac922x_parse_auto_config(struct hda_codec *codec)
struct sigmatel_spec *spec = codec->spec;
int err;
- if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg)) < 0)
+ if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
+ return err;
+ if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0)
return err;
if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
return err;
@@ -731,15 +773,8 @@ static int stac922x_parse_auto_config(struct hda_codec *codec)
return err;
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- if (spec->multiout.max_channels > 2) {
+ if (spec->multiout.max_channels > 2)
spec->surr_switch = 1;
- spec->cur_ch_mode = 1;
- spec->num_ch_modes = 2;
- if (spec->multiout.max_channels == 8) {
- spec->cur_ch_mode++;
- spec->num_ch_modes++;
- }
- }
if (spec->autocfg.dig_out_pin) {
spec->multiout.dig_out_nid = 0x08;
@@ -763,7 +798,7 @@ static int stac9200_parse_auto_config(struct hda_codec *codec)
struct sigmatel_spec *spec = codec->spec;
int err;
- if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg)) < 0)
+ if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
return err;
if ((err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
@@ -786,33 +821,10 @@ static int stac9200_parse_auto_config(struct hda_codec *codec)
return 1;
}
-static int stac92xx_init_pstate(struct hda_codec *codec)
-{
- hda_nid_t nid, nid_start;
- int nodes;
-
- snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_POWER_STATE, 0x00);
-
- nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid_start);
- for (nid = nid_start; nid < nodes + nid_start; nid++) {
- unsigned int wid_caps = snd_hda_param_read(codec, nid,
- AC_PAR_AUDIO_WIDGET_CAP);
- if (wid_caps & AC_WCAP_POWER)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_POWER_STATE, 0x00);
- }
-
- mdelay(100);
-
- return 0;
-}
-
static int stac92xx_init(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
- stac92xx_init_pstate(codec);
-
snd_hda_sequence_write(codec, spec->init);
stac92xx_auto_init_multi_out(codec);
@@ -924,13 +936,16 @@ static int patch_stac9200(struct hda_codec *codec)
return -ENOMEM;
codec->spec = spec;
+ spec->board_config = snd_hda_check_board_config(codec, stac9200_cfg_tbl);
+ if (spec->board_config < 0)
+ snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9200, using BIOS defaults\n");
+ else {
+ spec->num_pins = 8;
+ spec->pin_nids = stac9200_pin_nids;
+ spec->pin_configs = stac9200_brd_tbl[spec->board_config];
+ stac92xx_set_config_regs(codec);
+ }
-#ifdef STAC_TEST
- spec->pin_nids = stac9200_pin_nids;
- spec->num_pins = 8;
- spec->pin_configs = stac9200_pin_configs;
- stac92xx_set_config_regs(codec);
-#endif
spec->multiout.max_channels = 2;
spec->multiout.num_dacs = 1;
spec->multiout.dac_nids = stac9200_dac_nids;
@@ -962,13 +977,16 @@ static int patch_stac922x(struct hda_codec *codec)
return -ENOMEM;
codec->spec = spec;
+ spec->board_config = snd_hda_check_board_config(codec, stac922x_cfg_tbl);
+ if (spec->board_config < 0)
+ snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, using BIOS defaults\n");
+ else {
+ spec->num_pins = 10;
+ spec->pin_nids = stac922x_pin_nids;
+ spec->pin_configs = stac922x_brd_tbl[spec->board_config];
+ stac92xx_set_config_regs(codec);
+ }
-#ifdef STAC_TEST
- spec->num_pins = 10;
- spec->pin_nids = stac922x_pin_nids;
- spec->pin_configs = stac922x_pin_configs;
- stac92xx_set_config_regs(codec);
-#endif
spec->adc_nids = stac922x_adc_nids;
spec->mux_nids = stac922x_mux_nids;
spec->num_muxes = 2;