1
0
Fork 0

ALSA: hda - auto parse intelhdmi cvt/pin configurations

Signed-off-by: Wu Fengguang <fengguang.wu@intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
wifi-calibration
Wu Fengguang 2009-10-30 11:45:35 +01:00 committed by Takashi Iwai
parent 69fb346896
commit f424367c3a
1 changed files with 119 additions and 1 deletions

View File

@ -213,6 +213,10 @@ static struct cea_channel_speaker_allocation channel_allocations[] = {
};
/*
* HDA/HDMI auto parsing
*/
static int hda_node_index(hda_nid_t *nids, hda_nid_t nid)
{
int i;
@ -225,6 +229,113 @@ static int hda_node_index(hda_nid_t *nids, hda_nid_t nid)
return -EINVAL;
}
static int intel_hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
{
struct intel_hdmi_spec *spec = codec->spec;
hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
int conn_len, curr;
int index;
if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
snd_printk(KERN_WARNING
"HDMI: pin %d wcaps %#x "
"does not support connection list\n",
pin_nid, get_wcaps(codec, pin_nid));
return -EINVAL;
}
conn_len = snd_hda_get_connections(codec, pin_nid, conn_list,
HDA_MAX_CONNECTIONS);
if (conn_len > 1)
curr = snd_hda_codec_read(codec, pin_nid, 0,
AC_VERB_GET_CONNECT_SEL, 0);
else
curr = 0;
index = hda_node_index(spec->pin, pin_nid);
if (index < 0)
return -EINVAL;
spec->pin_cvt[index] = conn_list[curr];
return 0;
}
static int intel_hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
{
struct intel_hdmi_spec *spec = codec->spec;
if (spec->num_pins >= INTEL_HDMI_PINS) {
snd_printk(KERN_WARNING
"HDMI: no space for pin %d \n", pin_nid);
return -EINVAL;
}
spec->pin[spec->num_pins] = pin_nid;
spec->num_pins++;
/*
* It is assumed that converter nodes come first in the node list and
* hence have been registered and usable now.
*/
return intel_hdmi_read_pin_conn(codec, pin_nid);
}
static int intel_hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid)
{
struct intel_hdmi_spec *spec = codec->spec;
if (spec->num_cvts >= INTEL_HDMI_CVTS) {
snd_printk(KERN_WARNING
"HDMI: no space for converter %d \n", nid);
return -EINVAL;
}
spec->cvt[spec->num_cvts] = nid;
spec->num_cvts++;
return 0;
}
static int intel_hdmi_parse_codec(struct hda_codec *codec)
{
hda_nid_t nid;
int i, nodes;
nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
if (!nid || nodes < 0) {
snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n");
return -EINVAL;
}
for (i = 0; i < nodes; i++, nid++) {
unsigned int caps;
unsigned int type;
caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
type = get_wcaps_type(caps);
if (!(caps & AC_WCAP_DIGITAL))
continue;
switch (type) {
case AC_WID_AUD_OUT:
if (intel_hdmi_add_cvt(codec, nid) < 0)
return -EINVAL;
break;
case AC_WID_PIN:
caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
if (!(caps & AC_PINCAP_HDMI))
continue;
if (intel_hdmi_add_pin(codec, nid) < 0)
return -EINVAL;
break;
}
}
return 0;
}
/*
* HDMI routines
*/
@ -756,8 +867,15 @@ static int do_patch_intel_hdmi(struct hda_codec *codec, int spec_id)
if (spec == NULL)
return -ENOMEM;
*spec = static_specs[spec_id];
codec->spec = spec;
if (intel_hdmi_parse_codec(codec) < 0) {
codec->spec = NULL;
kfree(spec);
return -EINVAL;
}
if (memcmp(spec, static_specs + spec_id, sizeof(*spec)))
snd_printk(KERN_WARNING
"HDMI: auto parse disagree with known config\n");
codec->patch_ops = intel_hdmi_patch_ops;
for (i = 0; i < spec->num_pins; i++)