diff options
Diffstat (limited to 'sound/pci/hda/patch_hdmi.c')
| -rw-r--r-- | sound/pci/hda/patch_hdmi.c | 234 | 
1 files changed, 154 insertions, 80 deletions
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index de8ac5c07fd..32930e66885 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -44,16 +44,6 @@ static bool static_hdmi_pcm;  module_param(static_hdmi_pcm, bool, 0644);  MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info"); -/* - * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device - * could support N independent pipes, each of them can be connected to one or - * more ports (DVI, HDMI or DisplayPort). - * - * The HDA correspondence of pipes/ports are converter/pin nodes. - */ -#define MAX_HDMI_CVTS	8 -#define MAX_HDMI_PINS	8 -  struct hdmi_spec_per_cvt {  	hda_nid_t cvt_nid;  	int assigned; @@ -80,16 +70,17 @@ struct hdmi_spec_per_pin {  	bool non_pcm;  	bool chmap_set;		/* channel-map override by ALSA API? */  	unsigned char chmap[8]; /* ALSA API channel-map */ +	char pcm_name[8];	/* filled in build_pcm callbacks */  };  struct hdmi_spec {  	int num_cvts; -	struct hdmi_spec_per_cvt cvts[MAX_HDMI_CVTS]; -	hda_nid_t cvt_nids[MAX_HDMI_CVTS]; +	struct snd_array cvts; /* struct hdmi_spec_per_cvt */ +	hda_nid_t cvt_nids[4]; /* only for haswell fix */  	int num_pins; -	struct hdmi_spec_per_pin pins[MAX_HDMI_PINS]; -	struct hda_pcm pcm_rec[MAX_HDMI_PINS]; +	struct snd_array pins; /* struct hdmi_spec_per_pin */ +	struct snd_array pcm_rec; /* struct hda_pcm */  	unsigned int channels_max; /* max over all cvts */  	struct hdmi_eld temp_eld; @@ -304,12 +295,19 @@ static struct cea_channel_speaker_allocation channel_allocations[] = {   * HDMI routines   */ +#define get_pin(spec, idx) \ +	((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx)) +#define get_cvt(spec, idx) \ +	((struct hdmi_spec_per_cvt  *)snd_array_elem(&spec->cvts, idx)) +#define get_pcm_rec(spec, idx) \ +	((struct hda_pcm *)snd_array_elem(&spec->pcm_rec, idx)) +  static int pin_nid_to_pin_index(struct hdmi_spec *spec, hda_nid_t pin_nid)  {  	int pin_idx;  	for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) -		if (spec->pins[pin_idx].pin_nid == pin_nid) +		if (get_pin(spec, pin_idx)->pin_nid == pin_nid)  			return pin_idx;  	snd_printk(KERN_WARNING "HDMI: pin nid %d not registered\n", pin_nid); @@ -322,7 +320,7 @@ static int hinfo_to_pin_index(struct hdmi_spec *spec,  	int pin_idx;  	for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) -		if (&spec->pcm_rec[pin_idx].stream[0] == hinfo) +		if (get_pcm_rec(spec, pin_idx)->stream == hinfo)  			return pin_idx;  	snd_printk(KERN_WARNING "HDMI: hinfo %p not registered\n", hinfo); @@ -334,7 +332,7 @@ static int cvt_nid_to_cvt_index(struct hdmi_spec *spec, hda_nid_t cvt_nid)  	int cvt_idx;  	for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) -		if (spec->cvts[cvt_idx].cvt_nid == cvt_nid) +		if (get_cvt(spec, cvt_idx)->cvt_nid == cvt_nid)  			return cvt_idx;  	snd_printk(KERN_WARNING "HDMI: cvt nid %d not registered\n", cvt_nid); @@ -352,7 +350,7 @@ static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,  	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;  	pin_idx = kcontrol->private_value; -	eld = &spec->pins[pin_idx].sink_eld; +	eld = &get_pin(spec, pin_idx)->sink_eld;  	mutex_lock(&eld->lock);  	uinfo->count = eld->eld_valid ? eld->eld_size : 0; @@ -370,7 +368,7 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,  	int pin_idx;  	pin_idx = kcontrol->private_value; -	eld = &spec->pins[pin_idx].sink_eld; +	eld = &get_pin(spec, pin_idx)->sink_eld;  	mutex_lock(&eld->lock);  	if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data)) { @@ -410,11 +408,11 @@ static int hdmi_create_eld_ctl(struct hda_codec *codec, int pin_idx,  	kctl->private_value = pin_idx;  	kctl->id.device = device; -	err = snd_hda_ctl_add(codec, spec->pins[pin_idx].pin_nid, kctl); +	err = snd_hda_ctl_add(codec, get_pin(spec, pin_idx)->pin_nid, kctl);  	if (err < 0)  		return err; -	spec->pins[pin_idx].eld_ctl = kctl; +	get_pin(spec, pin_idx)->eld_ctl = kctl;  	return 0;  } @@ -875,14 +873,14 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx,  				       struct snd_pcm_substream *substream)  {  	struct hdmi_spec *spec = codec->spec; -	struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; +	struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);  	hda_nid_t pin_nid = per_pin->pin_nid;  	int channels = substream->runtime->channels;  	struct hdmi_eld *eld;  	int ca;  	union audio_infoframe ai; -	eld = &spec->pins[pin_idx].sink_eld; +	eld = &per_pin->sink_eld;  	if (!eld->monitor_present)  		return; @@ -977,7 +975,7 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)  	if (pin_idx < 0)  		return; -	hdmi_present_sense(&spec->pins[pin_idx], 1); +	hdmi_present_sense(get_pin(spec, pin_idx), 1);  	snd_hda_jack_report_sync(codec);  } @@ -1020,6 +1018,41 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)  		hdmi_non_intrinsic_event(codec, res);  } +static void haswell_verify_pin_D0(struct hda_codec *codec, hda_nid_t nid) +{ +	int pwr, lamp, ramp; + +	pwr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); +	pwr = (pwr & AC_PWRST_ACTUAL) >> AC_PWRST_ACTUAL_SHIFT; +	if (pwr != AC_PWRST_D0) { +		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, +				    AC_PWRST_D0); +		msleep(40); +		pwr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); +		pwr = (pwr & AC_PWRST_ACTUAL) >> AC_PWRST_ACTUAL_SHIFT; +		snd_printd("Haswell HDMI audio: Power for pin 0x%x is now D%d\n", nid, pwr); +	} + +	lamp = snd_hda_codec_read(codec, nid, 0, +				  AC_VERB_GET_AMP_GAIN_MUTE, +				  AC_AMP_GET_LEFT | AC_AMP_GET_OUTPUT); +	ramp = snd_hda_codec_read(codec, nid, 0, +				  AC_VERB_GET_AMP_GAIN_MUTE, +				  AC_AMP_GET_RIGHT | AC_AMP_GET_OUTPUT); +	if (lamp != ramp) { +		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, +				    AC_AMP_SET_RIGHT | AC_AMP_SET_OUTPUT | lamp); + +		lamp = snd_hda_codec_read(codec, nid, 0, +				  AC_VERB_GET_AMP_GAIN_MUTE, +				  AC_AMP_GET_LEFT | AC_AMP_GET_OUTPUT); +		ramp = snd_hda_codec_read(codec, nid, 0, +				  AC_VERB_GET_AMP_GAIN_MUTE, +				  AC_AMP_GET_RIGHT | AC_AMP_GET_OUTPUT); +		snd_printd("Haswell HDMI audio: Mute after set on pin 0x%x: [0x%x 0x%x]\n", nid, lamp, ramp); +	} +} +  /*   * Callbacks   */ @@ -1034,6 +1067,9 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,  	int pinctl;  	int new_pinctl = 0; +	if (codec->vendor_id == 0x80862807) +		haswell_verify_pin_D0(codec, pin_nid); +  	if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) {  		pinctl = snd_hda_codec_read(codec, pin_nid, 0,  					    AC_VERB_GET_PIN_WIDGET_CONTROL, 0); @@ -1083,12 +1119,12 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,  	pin_idx = hinfo_to_pin_index(spec, hinfo);  	if (snd_BUG_ON(pin_idx < 0))  		return -EINVAL; -	per_pin = &spec->pins[pin_idx]; +	per_pin = get_pin(spec, pin_idx);  	eld = &per_pin->sink_eld;  	/* Dynamically assign converter to stream */  	for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { -		per_cvt = &spec->cvts[cvt_idx]; +		per_cvt = get_cvt(spec, cvt_idx);  		/* Must not already be assigned */  		if (per_cvt->assigned) @@ -1151,7 +1187,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,  static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)  {  	struct hdmi_spec *spec = codec->spec; -	struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; +	struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);  	hda_nid_t pin_nid = per_pin->pin_nid;  	if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) { @@ -1275,14 +1311,13 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)  	if (get_defcfg_connect(config) == AC_JACK_PORT_NONE)  		return 0; -	if (snd_BUG_ON(spec->num_pins >= MAX_HDMI_PINS)) -		return -E2BIG; -  	if (codec->vendor_id == 0x80862807)  		intel_haswell_fixup_connect_list(codec, pin_nid);  	pin_idx = spec->num_pins; -	per_pin = &spec->pins[pin_idx]; +	per_pin = snd_array_new(&spec->pins); +	if (!per_pin) +		return -ENOMEM;  	per_pin->pin_nid = pin_nid;  	per_pin->non_pcm = false; @@ -1299,19 +1334,16 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)  static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)  {  	struct hdmi_spec *spec = codec->spec; -	int cvt_idx;  	struct hdmi_spec_per_cvt *per_cvt;  	unsigned int chans;  	int err; -	if (snd_BUG_ON(spec->num_cvts >= MAX_HDMI_CVTS)) -		return -E2BIG; -  	chans = get_wcaps(codec, cvt_nid);  	chans = get_wcaps_channels(chans); -	cvt_idx = spec->num_cvts; -	per_cvt = &spec->cvts[cvt_idx]; +	per_cvt = snd_array_new(&spec->cvts); +	if (!per_cvt) +		return -ENOMEM;  	per_cvt->cvt_nid = cvt_nid;  	per_cvt->channels_min = 2; @@ -1328,7 +1360,9 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)  	if (err < 0)  		return err; -	spec->cvt_nids[spec->num_cvts++] = cvt_nid; +	if (spec->num_cvts < ARRAY_SIZE(spec->cvt_nids)) +		spec->cvt_nids[spec->num_cvts] = cvt_nid; +	spec->num_cvts++;  	return 0;  } @@ -1384,13 +1418,6 @@ static int hdmi_parse_codec(struct hda_codec *codec)  /*   */ -static char *get_hdmi_pcm_name(int idx) -{ -	static char names[MAX_HDMI_PINS][8]; -	sprintf(&names[idx][0], "HDMI %d", idx); -	return &names[idx][0]; -} -  static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)  {  	struct hda_spdif_out *spdif; @@ -1417,7 +1444,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,  	hda_nid_t cvt_nid = hinfo->nid;  	struct hdmi_spec *spec = codec->spec;  	int pin_idx = hinfo_to_pin_index(spec, hinfo); -	hda_nid_t pin_nid = spec->pins[pin_idx].pin_nid; +	hda_nid_t pin_nid = get_pin(spec, pin_idx)->pin_nid;  	bool non_pcm;  	non_pcm = check_non_pcm_per_cvt(codec, cvt_nid); @@ -1450,7 +1477,7 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,  		cvt_idx = cvt_nid_to_cvt_index(spec, hinfo->nid);  		if (snd_BUG_ON(cvt_idx < 0))  			return -EINVAL; -		per_cvt = &spec->cvts[cvt_idx]; +		per_cvt = get_cvt(spec, cvt_idx);  		snd_BUG_ON(!per_cvt->assigned);  		per_cvt->assigned = 0; @@ -1459,7 +1486,7 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,  		pin_idx = hinfo_to_pin_index(spec, hinfo);  		if (snd_BUG_ON(pin_idx < 0))  			return -EINVAL; -		per_pin = &spec->pins[pin_idx]; +		per_pin = get_pin(spec, pin_idx);  		snd_hda_spdif_ctls_unassign(codec, pin_idx);  		per_pin->chmap_set = false; @@ -1553,7 +1580,7 @@ static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,  	struct hda_codec *codec = info->private_data;  	struct hdmi_spec *spec = codec->spec;  	int pin_idx = kcontrol->private_value; -	struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; +	struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);  	int i;  	for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++) @@ -1568,7 +1595,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,  	struct hda_codec *codec = info->private_data;  	struct hdmi_spec *spec = codec->spec;  	int pin_idx = kcontrol->private_value; -	struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; +	struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);  	unsigned int ctl_idx;  	struct snd_pcm_substream *substream;  	unsigned char chmap[8]; @@ -1613,9 +1640,14 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)  	for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {  		struct hda_pcm *info;  		struct hda_pcm_stream *pstr; +		struct hdmi_spec_per_pin *per_pin; -		info = &spec->pcm_rec[pin_idx]; -		info->name = get_hdmi_pcm_name(pin_idx); +		per_pin = get_pin(spec, pin_idx); +		sprintf(per_pin->pcm_name, "HDMI %d", pin_idx); +		info = snd_array_new(&spec->pcm_rec); +		if (!info) +			return -ENOMEM; +		info->name = per_pin->pcm_name;  		info->pcm_type = HDA_PCM_TYPE_HDMI;  		info->own_chmap = true; @@ -1626,7 +1658,7 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)  	}  	codec->num_pcms = spec->num_pins; -	codec->pcm_info = spec->pcm_rec; +	codec->pcm_info = spec->pcm_rec.list;  	return 0;  } @@ -1635,8 +1667,8 @@ static int generic_hdmi_build_jack(struct hda_codec *codec, int pin_idx)  {  	char hdmi_str[32] = "HDMI/DP";  	struct hdmi_spec *spec = codec->spec; -	struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; -	int pcmdev = spec->pcm_rec[pin_idx].device; +	struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); +	int pcmdev = get_pcm_rec(spec, pin_idx)->device;  	if (pcmdev > 0)  		sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev); @@ -1654,7 +1686,7 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)  	int pin_idx;  	for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { -		struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; +		struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);  		err = generic_hdmi_build_jack(codec, pin_idx);  		if (err < 0) @@ -1669,9 +1701,8 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)  		snd_hda_spdif_ctls_unassign(codec, pin_idx);  		/* add control for ELD Bytes */ -		err = hdmi_create_eld_ctl(codec, -					pin_idx, -					spec->pcm_rec[pin_idx].device); +		err = hdmi_create_eld_ctl(codec, pin_idx, +					  get_pcm_rec(spec, pin_idx)->device);  		if (err < 0)  			return err; @@ -1709,7 +1740,7 @@ static int generic_hdmi_init_per_pins(struct hda_codec *codec)  	int pin_idx;  	for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { -		struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; +		struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);  		struct hdmi_eld *eld = &per_pin->sink_eld;  		per_pin->codec = codec; @@ -1726,7 +1757,7 @@ static int generic_hdmi_init(struct hda_codec *codec)  	int pin_idx;  	for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { -		struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; +		struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);  		hda_nid_t pin_nid = per_pin->pin_nid;  		hdmi_init_pin(codec, pin_nid); @@ -1735,13 +1766,27 @@ static int generic_hdmi_init(struct hda_codec *codec)  	return 0;  } +static void hdmi_array_init(struct hdmi_spec *spec, int nums) +{ +	snd_array_init(&spec->pins, sizeof(struct hdmi_spec_per_pin), nums); +	snd_array_init(&spec->cvts, sizeof(struct hdmi_spec_per_cvt), nums); +	snd_array_init(&spec->pcm_rec, sizeof(struct hda_pcm), nums); +} + +static void hdmi_array_free(struct hdmi_spec *spec) +{ +	snd_array_free(&spec->pins); +	snd_array_free(&spec->cvts); +	snd_array_free(&spec->pcm_rec); +} +  static void generic_hdmi_free(struct hda_codec *codec)  {  	struct hdmi_spec *spec = codec->spec;  	int pin_idx;  	for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { -		struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx]; +		struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);  		struct hdmi_eld *eld = &per_pin->sink_eld;  		cancel_delayed_work(&per_pin->work); @@ -1749,6 +1794,7 @@ static void generic_hdmi_free(struct hda_codec *codec)  	}  	flush_workqueue(codec->bus->workq); +	hdmi_array_free(spec);  	kfree(spec);  } @@ -1775,6 +1821,7 @@ static void intel_haswell_fixup_connect_list(struct hda_codec *codec,  	/* override pins connection list */  	snd_printdd("hdmi: haswell: override pin connection 0x%x\n", nid); +	nconns = max(spec->num_cvts, 4);  	snd_hda_override_conn_list(codec, nid, spec->num_cvts, spec->cvt_nids);  } @@ -1855,6 +1902,7 @@ static int patch_generic_hdmi(struct hda_codec *codec)  		return -ENOMEM;  	codec->spec = spec; +	hdmi_array_init(spec, 4);  	snd_hda_pick_fixup(codec, hdmi_models, hdmi_fixup_tbl, hdmi_fixups);  	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); @@ -1882,24 +1930,30 @@ static int patch_generic_hdmi(struct hda_codec *codec)  static int simple_playback_build_pcms(struct hda_codec *codec)  {  	struct hdmi_spec *spec = codec->spec; -	struct hda_pcm *info = spec->pcm_rec; +	struct hda_pcm *info;  	unsigned int chans;  	struct hda_pcm_stream *pstr; +	struct hdmi_spec_per_cvt *per_cvt; -	codec->num_pcms = 1; -	codec->pcm_info = info; - -	chans = get_wcaps(codec, spec->cvts[0].cvt_nid); +	per_cvt = get_cvt(spec, 0); +	chans = get_wcaps(codec, per_cvt->cvt_nid);  	chans = get_wcaps_channels(chans); -	info->name = get_hdmi_pcm_name(0); +	info = snd_array_new(&spec->pcm_rec); +	if (!info) +		return -ENOMEM; +	info->name = get_pin(spec, 0)->pcm_name; +	sprintf(info->name, "HDMI 0");  	info->pcm_type = HDA_PCM_TYPE_HDMI;  	pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];  	*pstr = spec->pcm_playback; -	pstr->nid = spec->cvts[0].cvt_nid; +	pstr->nid = per_cvt->cvt_nid;  	if (pstr->channels_max <= 2 && chans && chans <= 16)  		pstr->channels_max = chans; +	codec->num_pcms = 1; +	codec->pcm_info = info; +  	return 0;  } @@ -1919,11 +1973,12 @@ static void simple_hdmi_unsol_event(struct hda_codec *codec,  static int simple_playback_build_controls(struct hda_codec *codec)  {  	struct hdmi_spec *spec = codec->spec; +	struct hdmi_spec_per_cvt *per_cvt;  	int err; -	err = snd_hda_create_spdif_out_ctls(codec, -					    spec->cvts[0].cvt_nid, -					    spec->cvts[0].cvt_nid); +	per_cvt = get_cvt(spec, 0); +	err = snd_hda_create_spdif_out_ctls(codec, per_cvt->cvt_nid, +					    per_cvt->cvt_nid);  	if (err < 0)  		return err;  	return simple_hdmi_build_jack(codec, 0); @@ -1932,7 +1987,8 @@ static int simple_playback_build_controls(struct hda_codec *codec)  static int simple_playback_init(struct hda_codec *codec)  {  	struct hdmi_spec *spec = codec->spec; -	hda_nid_t pin = spec->pins[0].pin_nid; +	struct hdmi_spec_per_pin *per_pin = get_pin(spec, 0); +	hda_nid_t pin = per_pin->pin_nid;  	snd_hda_codec_write(codec, pin, 0,  			    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); @@ -1948,6 +2004,7 @@ static void simple_playback_free(struct hda_codec *codec)  {  	struct hdmi_spec *spec = codec->spec; +	hdmi_array_free(spec);  	kfree(spec);  } @@ -2111,20 +2168,29 @@ static int patch_simple_hdmi(struct hda_codec *codec,  			     hda_nid_t cvt_nid, hda_nid_t pin_nid)  {  	struct hdmi_spec *spec; +	struct hdmi_spec_per_cvt *per_cvt; +	struct hdmi_spec_per_pin *per_pin;  	spec = kzalloc(sizeof(*spec), GFP_KERNEL);  	if (!spec)  		return -ENOMEM;  	codec->spec = spec; +	hdmi_array_init(spec, 1);  	spec->multiout.num_dacs = 0;  /* no analog */  	spec->multiout.max_channels = 2;  	spec->multiout.dig_out_nid = cvt_nid;  	spec->num_cvts = 1;  	spec->num_pins = 1; -	spec->cvts[0].cvt_nid = cvt_nid; -	spec->pins[0].pin_nid = pin_nid; +	per_pin = snd_array_new(&spec->pins); +	per_cvt = snd_array_new(&spec->cvts); +	if (!per_pin || !per_cvt) { +		simple_playback_free(codec); +		return -ENOMEM; +	} +	per_cvt->cvt_nid = cvt_nid; +	per_pin->pin_nid = pin_nid;  	spec->pcm_playback = simple_pcm_playback;  	codec->patch_ops = simple_hdmi_patch_ops; @@ -2201,9 +2267,11 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,  	int i;  	struct hdmi_spec *spec = codec->spec;  	struct hda_spdif_out *spdif; +	struct hdmi_spec_per_cvt *per_cvt;  	mutex_lock(&codec->spdif_mutex); -	spdif = snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid); +	per_cvt = get_cvt(spec, 0); +	spdif = snd_hda_spdif_out_of_nid(codec, per_cvt->cvt_nid);  	chs = substream->runtime->channels; @@ -2325,13 +2393,17 @@ static int nvhdmi_7x_8ch_build_pcms(struct hda_codec *codec)  {  	struct hdmi_spec *spec = codec->spec;  	int err = simple_playback_build_pcms(codec); -	spec->pcm_rec[0].own_chmap = true; +	if (!err) { +		struct hda_pcm *info = get_pcm_rec(spec, 0); +		info->own_chmap = true; +	}  	return err;  }  static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec)  {  	struct hdmi_spec *spec = codec->spec; +	struct hda_pcm *info;  	struct snd_pcm_chmap *chmap;  	int err; @@ -2340,7 +2412,8 @@ static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec)  		return err;  	/* add channel maps */ -	err = snd_pcm_add_chmap_ctls(spec->pcm_rec[0].pcm, +	info = get_pcm_rec(spec, 0); +	err = snd_pcm_add_chmap_ctls(info->pcm,  				     SNDRV_PCM_STREAM_PLAYBACK,  				     snd_pcm_alt_chmaps, 8, 0, &chmap);  	if (err < 0) @@ -2395,6 +2468,7 @@ static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,  					struct snd_pcm_substream *substream)  {  	struct hdmi_spec *spec = codec->spec; +	struct hdmi_spec_per_cvt *per_cvt = get_cvt(spec, 0);  	int chans = substream->runtime->channels;  	int i, err; @@ -2402,11 +2476,11 @@ static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,  					  substream);  	if (err < 0)  		return err; -	snd_hda_codec_write(codec, spec->cvts[0].cvt_nid, 0, +	snd_hda_codec_write(codec, per_cvt->cvt_nid, 0,  			    AC_VERB_SET_CVT_CHAN_COUNT, chans - 1);  	/* FIXME: XXX */  	for (i = 0; i < chans; i++) { -		snd_hda_codec_write(codec, spec->cvts[0].cvt_nid, 0, +		snd_hda_codec_write(codec, per_cvt->cvt_nid, 0,  				    AC_VERB_SET_HDMI_CHAN_SLOT,  				    (i << 4) | i);  	}  |