--- /dev/null
+From: Takashi Iwai <tiwai@suse.de>
+Subject: ALSA: hda - Slave SPDIF support
+Patch-mainline: 2.6.28-rc1
+References:
+
+Add the support of slave SPDIF outputs for multiple SPDIF devices.
+
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+
+---
+---
+ sound/pci/hda/hda_codec.c | 72 ++++++++++++++++++++++++++-----------
+ sound/pci/hda/hda_codec.h | 89 +++++++++++++++++++++++++++++++++++++++++++---
+ sound/pci/hda/hda_local.h | 15 ++++---
+ 3 files changed, 146 insertions(+), 30 deletions(-)
+
+--- a/sound/pci/hda/hda_codec.c
++++ b/sound/pci/hda/hda_codec.c
+@@ -1430,6 +1430,29 @@ static unsigned int convert_to_spdif_sta
+ return sbits;
+ }
+
++/* set digital convert verbs both for the given NID and its slaves */
++static void set_dig_out(struct hda_codec *codec, hda_nid_t nid,
++ int verb, int val)
++{
++ hda_nid_t *d;
++
++ snd_hda_codec_write(codec, nid, 0, verb, val);
++ d = codec->slave_dig_outs;
++ if (!d)
++ return;
++ for (; *d; d++)
++ snd_hda_codec_write(codec, *d, 0, verb, val);
++}
++
++static inline void set_dig_out_convert(struct hda_codec *codec, hda_nid_t nid,
++ int dig1, int dig2)
++{
++ if (dig1 != -1)
++ set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_1, dig1);
++ if (dig2 != -1)
++ set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_2, dig2);
++}
++
+ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+ {
+@@ -1448,14 +1471,8 @@ static int snd_hda_spdif_default_put(str
+ change = codec->spdif_ctls != val;
+ codec->spdif_ctls = val;
+
+- if (change) {
+- snd_hda_codec_write_cache(codec, nid, 0,
+- AC_VERB_SET_DIGI_CONVERT_1,
+- val & 0xff);
+- snd_hda_codec_write_cache(codec, nid, 0,
+- AC_VERB_SET_DIGI_CONVERT_2,
+- val >> 8);
+- }
++ if (change)
++ set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff);
+
+ mutex_unlock(&codec->spdif_mutex);
+ return change;
+@@ -1487,9 +1504,7 @@ static int snd_hda_spdif_out_switch_put(
+ change = codec->spdif_ctls != val;
+ if (change) {
+ codec->spdif_ctls = val;
+- snd_hda_codec_write_cache(codec, nid, 0,
+- AC_VERB_SET_DIGI_CONVERT_1,
+- val & 0xff);
++ set_dig_out_convert(codec, nid, val & 0xff, -1);
+ /* unmute amp switch (if any) */
+ if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&
+ (val & AC_DIG1_ENABLE))
+@@ -2583,14 +2598,31 @@ static void setup_dig_out_stream(struct
+ unsigned int stream_tag, unsigned int format)
+ {
+ /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
+- if (codec->spdif_ctls & AC_DIG1_ENABLE)
+- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
+- codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
++ if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
++ set_dig_out_convert(codec, nid,
++ codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff,
++ -1);
+ snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
++ if (codec->slave_dig_outs) {
++ hda_nid_t *d;
++ for (d = codec->slave_dig_outs; *d; d++)
++ snd_hda_codec_setup_stream(codec, *d, stream_tag, 0,
++ format);
++ }
+ /* turn on again (if needed) */
+- if (codec->spdif_ctls & AC_DIG1_ENABLE)
+- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
+- codec->spdif_ctls & 0xff);
++ if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
++ set_dig_out_convert(codec, nid,
++ codec->spdif_ctls & 0xff, -1);
++}
++
++static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid)
++{
++ snd_hda_codec_cleanup_stream(codec, nid);
++ if (codec->slave_dig_outs) {
++ hda_nid_t *d;
++ for (d = codec->slave_dig_outs; *d; d++)
++ snd_hda_codec_cleanup_stream(codec, *d);
++ }
+ }
+
+ /*
+@@ -2602,7 +2634,7 @@ int snd_hda_multi_out_dig_open(struct hd
+ mutex_lock(&codec->spdif_mutex);
+ if (mout->dig_out_used == HDA_DIG_ANALOG_DUP)
+ /* already opened as analog dup; reset it once */
+- snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid);
++ cleanup_dig_out_stream(codec, mout->dig_out_nid);
+ mout->dig_out_used = HDA_DIG_EXCLUSIVE;
+ mutex_unlock(&codec->spdif_mutex);
+ return 0;
+@@ -2697,7 +2729,7 @@ int snd_hda_multi_out_analog_prepare(str
+ stream_tag, format);
+ } else {
+ mout->dig_out_used = 0;
+- snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid);
++ cleanup_dig_out_stream(codec, mout->dig_out_nid);
+ }
+ }
+ mutex_unlock(&codec->spdif_mutex);
+@@ -2748,7 +2780,7 @@ int snd_hda_multi_out_analog_cleanup(str
+ mout->extra_out_nid[i]);
+ mutex_lock(&codec->spdif_mutex);
+ if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
+- snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid);
++ cleanup_dig_out_stream(codec, mout->dig_out_nid);
+ mout->dig_out_used = 0;
+ }
+ mutex_unlock(&codec->spdif_mutex);
+--- a/sound/pci/hda/hda_codec.h
++++ b/sound/pci/hda/hda_codec.h
+@@ -90,6 +90,14 @@ enum {
+ #define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c
+ /* f20: AFG/MFG */
+ #define AC_VERB_GET_SUBSYSTEM_ID 0x0f20
++#define AC_VERB_GET_CVT_CHAN_COUNT 0x0f2d
++#define AC_VERB_GET_HDMI_DIP_SIZE 0x0f2e
++#define AC_VERB_GET_HDMI_ELDD 0x0f2f
++#define AC_VERB_GET_HDMI_DIP_INDEX 0x0f30
++#define AC_VERB_GET_HDMI_DIP_DATA 0x0f31
++#define AC_VERB_GET_HDMI_DIP_XMIT 0x0f32
++#define AC_VERB_GET_HDMI_CP_CTRL 0x0f33
++#define AC_VERB_GET_HDMI_CHAN_SLOT 0x0f34
+
+ /*
+ * SET verbs
+@@ -121,7 +129,14 @@ enum {
+ #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1 0x71d
+ #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2 0x71e
+ #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_3 0x71f
++#define AC_VERB_SET_EAPD 0x788
+ #define AC_VERB_SET_CODEC_RESET 0x7ff
++#define AC_VERB_SET_CVT_CHAN_COUNT 0x72d
++#define AC_VERB_SET_HDMI_DIP_INDEX 0x730
++#define AC_VERB_SET_HDMI_DIP_DATA 0x731
++#define AC_VERB_SET_HDMI_DIP_XMIT 0x732
++#define AC_VERB_SET_HDMI_CP_CTRL 0x733
++#define AC_VERB_SET_HDMI_CHAN_SLOT 0x734
+
+ /*
+ * Parameter IDs
+@@ -143,6 +158,7 @@ enum {
+ #define AC_PAR_GPIO_CAP 0x11
+ #define AC_PAR_AMP_OUT_CAP 0x12
+ #define AC_PAR_VOL_KNB_CAP 0x13
++#define AC_PAR_HDMI_LPCM_CAP 0x20
+
+ /*
+ * AC_VERB_PARAMETERS results (32bit)
+@@ -171,6 +187,8 @@ enum {
+ #define AC_WCAP_DIGITAL (1<<9) /* digital I/O */
+ #define AC_WCAP_POWER (1<<10) /* power control */
+ #define AC_WCAP_LR_SWAP (1<<11) /* L/R swap */
++#define AC_WCAP_CP_CAPS (1<<12) /* content protection */
++#define AC_WCAP_CHAN_CNT_EXT (7<<13) /* channel count ext */
+ #define AC_WCAP_DELAY (0xf<<16)
+ #define AC_WCAP_DELAY_SHIFT 16
+ #define AC_WCAP_TYPE (0xf<<20)
+@@ -206,9 +224,20 @@ enum {
+ /* Input converter SDI select */
+ #define AC_SDI_SELECT (0xf<<0)
+
+-/* Unsolicited response */
++/* Unsolicited response control */
+ #define AC_UNSOL_TAG (0x3f<<0)
+ #define AC_UNSOL_ENABLED (1<<7)
++#define AC_USRSP_EN AC_UNSOL_ENABLED
++
++/* Unsolicited responses */
++#define AC_UNSOL_RES_TAG (0x3f<<26)
++#define AC_UNSOL_RES_TAG_SHIFT 26
++#define AC_UNSOL_RES_SUBTAG (0x1f<<21)
++#define AC_UNSOL_RES_SUBTAG_SHIFT 21
++#define AC_UNSOL_RES_ELDV (1<<1) /* ELD Data valid (for HDMI) */
++#define AC_UNSOL_RES_PD (1<<0) /* pinsense detect */
++#define AC_UNSOL_RES_CP_STATE (1<<1) /* content protection */
++#define AC_UNSOL_RES_CP_READY (1<<0) /* content protection */
+
+ /* Pin widget capabilies */
+ #define AC_PINCAP_IMP_SENSE (1<<0) /* impedance sense capable */
+@@ -222,6 +251,10 @@ enum {
+ * but is marked reserved in the Intel HDA specification.
+ */
+ #define AC_PINCAP_LR_SWAP (1<<7) /* L/R swap */
++/* Note: The same bit as LR_SWAP is newly defined as HDMI capability
++ * in HD-audio specification
++ */
++#define AC_PINCAP_HDMI (1<<7) /* HDMI pin */
+ #define AC_PINCAP_VREF (0x37<<8)
+ #define AC_PINCAP_VREF_SHIFT 8
+ #define AC_PINCAP_EAPD (1<<16) /* EAPD capable */
+@@ -272,6 +305,22 @@ enum {
+ #define AC_KNBCAP_NUM_STEPS (0x7f<<0)
+ #define AC_KNBCAP_DELTA (1<<7)
+
++/* HDMI LPCM capabilities */
++#define AC_LPCMCAP_48K_CP_CHNS (0x0f<<0) /* max channels w/ CP-on */
++#define AC_LPCMCAP_48K_NO_CHNS (0x0f<<4) /* max channels w/o CP-on */
++#define AC_LPCMCAP_48K_20BIT (1<<8) /* 20b bitrate supported */
++#define AC_LPCMCAP_48K_24BIT (1<<9) /* 24b bitrate supported */
++#define AC_LPCMCAP_96K_CP_CHNS (0x0f<<10) /* max channels w/ CP-on */
++#define AC_LPCMCAP_96K_NO_CHNS (0x0f<<14) /* max channels w/o CP-on */
++#define AC_LPCMCAP_96K_20BIT (1<<18) /* 20b bitrate supported */
++#define AC_LPCMCAP_96K_24BIT (1<<19) /* 24b bitrate supported */
++#define AC_LPCMCAP_192K_CP_CHNS (0x0f<<20) /* max channels w/ CP-on */
++#define AC_LPCMCAP_192K_NO_CHNS (0x0f<<24) /* max channels w/o CP-on */
++#define AC_LPCMCAP_192K_20BIT (1<<28) /* 20b bitrate supported */
++#define AC_LPCMCAP_192K_24BIT (1<<29) /* 24b bitrate supported */
++#define AC_LPCMCAP_44K (1<<30) /* 44.1kHz support */
++#define AC_LPCMCAP_44K_MS (1<<31) /* 44.1kHz-multiplies support */
++
+ /*
+ * Control Parameters
+ */
+@@ -317,18 +366,44 @@ enum {
+ #define AC_PINCTL_OUT_EN (1<<6)
+ #define AC_PINCTL_HP_EN (1<<7)
+
+-/* Unsolicited response - 8bit */
+-#define AC_USRSP_EN (1<<7)
+-
+ /* Pin sense - 32bit */
+ #define AC_PINSENSE_IMPEDANCE_MASK (0x7fffffff)
+ #define AC_PINSENSE_PRESENCE (1<<31)
++#define AC_PINSENSE_ELDV (1<<30) /* ELD valid (HDMI) */
+
+ /* EAPD/BTL enable - 32bit */
+ #define AC_EAPDBTL_BALANCED (1<<0)
+ #define AC_EAPDBTL_EAPD (1<<1)
+ #define AC_EAPDBTL_LR_SWAP (1<<2)
+
++/* HDMI ELD data */
++#define AC_ELDD_ELD_VALID (1<<31)
++#define AC_ELDD_ELD_DATA 0xff
++
++/* HDMI DIP size */
++#define AC_DIPSIZE_ELD_BUF (1<<3) /* ELD buf size of packet size */
++#define AC_DIPSIZE_PACK_IDX (0x07<<0) /* packet index */
++
++/* HDMI DIP index */
++#define AC_DIPIDX_PACK_IDX (0x07<<5) /* packet idnex */
++#define AC_DIPIDX_BYTE_IDX (0x1f<<0) /* byte index */
++
++/* HDMI DIP xmit (transmit) control */
++#define AC_DIPXMIT_MASK (0x3<<6)
++#define AC_DIPXMIT_DISABLE (0x0<<6) /* disable xmit */
++#define AC_DIPXMIT_ONCE (0x2<<6) /* xmit once then disable */
++#define AC_DIPXMIT_BEST (0x3<<6) /* best effort */
++
++/* HDMI content protection (CP) control */
++#define AC_CPCTRL_CES (1<<9) /* current encryption state */
++#define AC_CPCTRL_READY (1<<8) /* ready bit */
++#define AC_CPCTRL_SUBTAG (0x1f<<3) /* subtag for unsol-resp */
++#define AC_CPCTRL_STATE (3<<0) /* current CP request state */
++
++/* Converter channel <-> HDMI slot mapping */
++#define AC_CVTMAP_HDMI_SLOT (0xf<<0) /* HDMI slot number */
++#define AC_CVTMAP_CHAN (0xf<<4) /* converter channel number */
++
+ /* configuration default - 32bit */
+ #define AC_DEFCFG_SEQUENCE (0xf<<0)
+ #define AC_DEFCFG_DEF_ASSOC (0xf<<4)
+@@ -650,9 +725,15 @@ struct hda_codec {
+ unsigned int spdif_status; /* IEC958 status bits */
+ unsigned short spdif_ctls; /* SPDIF control bits */
+ unsigned int spdif_in_enable; /* SPDIF input enable? */
++ hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
+
+ struct snd_hwdep *hwdep; /* assigned hwdep device */
+
++ /* misc flags */
++ unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each
++ * status change
++ * (e.g. Realtek codecs)
++ */
+ #ifdef CONFIG_SND_HDA_POWER_SAVE
+ unsigned int power_on :1; /* current (global) power-state */
+ unsigned int power_transition :1; /* power-state in transition */
+--- a/sound/pci/hda/hda_local.h
++++ b/sound/pci/hda/hda_local.h
+@@ -368,12 +368,15 @@ int snd_hda_parse_pin_def_config(struct
+ #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
++#define PIN_IN (AC_PINCTL_IN_EN)
++#define PIN_VREFHIZ (AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ)
++#define PIN_VREF50 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_50)
++#define PIN_VREFGRD (AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD)
++#define PIN_VREF80 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_80)
++#define PIN_VREF100 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_100)
++#define PIN_OUT (AC_PINCTL_OUT_EN)
++#define PIN_HP (AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN)
++#define PIN_HP_AMP (AC_PINCTL_HP_EN)
+
+ /*
+ * get widget capabilities