summaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/hda_codec.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/hda/hda_codec.c')
-rw-r--r--sound/pci/hda/hda_codec.c341
1 files changed, 251 insertions, 90 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 0dbeeaf6113..4a6dd97deba 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)