From: Takashi Iwai 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 --- --- 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