ALSA: hda/hdmi: Add Intel silent stream support
External HDMI receivers have analog circuitry that needs to be powered-on when exiting standby, and a mechanism to detect PCM v. IEC61937 data. These two steps take time and up to 2-3 seconds of audio may be muted when starting playback. Intel hardware (Haswell and beyond) can keep the link active with a 'silent stream', so that the receiver does not go through those two steps when valid audio is transmitted. This mechanism relies on an setting the channel_id as 0xf, sending info packet and preventing the codec from going to D3, which will increase the platform static power consumption. The info packet assumes a basic 2ch stereo, and the silent stream is enabled when connecting a monitor. In case of format changes the detection of PCM v. IEC61937 needs to be re-run. In this case there is no way to avoid the 2-3s mute. The silent stream is enabled with a Kconfig option, as well as a kernel parameter should there be a need to override the build time default. This approach is used based on the power_save capability as an example, but in the future, it may be used with a kcontrol, depending on UCM support for HDaudio legacy. Signed-off-by: Harsha Priya <harshapriya.n@intel.com> Signed-off-by: Emmanuel Jillela <emmanuel.jillela@intel.com> Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com> Reported-by: kernel test robot <lkp@intel.com> Link: https://lore.kernel.org/r/1594068797-14011-1-git-send-email-harshapriya.n@intel.com Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
ad1e0b7de0
commit
951894cf30
@ -240,6 +240,20 @@ config SND_HDA_POWER_SAVE_DEFAULT
|
|||||||
The default time-out value in seconds for HD-audio automatic
|
The default time-out value in seconds for HD-audio automatic
|
||||||
power-save mode. 0 means to disable the power-save mode.
|
power-save mode. 0 means to disable the power-save mode.
|
||||||
|
|
||||||
|
config SND_HDA_INTEL_HDMI_SILENT_STREAM
|
||||||
|
bool "Enable Silent Stream always for HDMI"
|
||||||
|
depends on SND_HDA_INTEL
|
||||||
|
help
|
||||||
|
Intel hardware has a feature called 'silent stream', that
|
||||||
|
keeps external HDMI receiver's analog circuitry powered on
|
||||||
|
avoiding 2-3 sec silence during playback start. This mechanism
|
||||||
|
relies on setting channel_id as 0xf, sending info packet and
|
||||||
|
preventing codec D3 entry (increasing platform static power
|
||||||
|
consumption when HDMI receiver is plugged-in). 2-3 sec silence
|
||||||
|
at the playback start is expected whenever there is format change.
|
||||||
|
(default is 2 channel format).
|
||||||
|
Say Y to enable Silent Stream feature.
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
@ -42,6 +42,11 @@ static bool enable_acomp = true;
|
|||||||
module_param(enable_acomp, bool, 0444);
|
module_param(enable_acomp, bool, 0444);
|
||||||
MODULE_PARM_DESC(enable_acomp, "Enable audio component binding (default=yes)");
|
MODULE_PARM_DESC(enable_acomp, "Enable audio component binding (default=yes)");
|
||||||
|
|
||||||
|
static bool enable_silent_stream =
|
||||||
|
IS_ENABLED(CONFIG_SND_HDA_INTEL_HDMI_SILENT_STREAM);
|
||||||
|
module_param(enable_silent_stream, bool, 0644);
|
||||||
|
MODULE_PARM_DESC(enable_silent_stream, "Enable Silent Stream for HDMI devices");
|
||||||
|
|
||||||
struct hdmi_spec_per_cvt {
|
struct hdmi_spec_per_cvt {
|
||||||
hda_nid_t cvt_nid;
|
hda_nid_t cvt_nid;
|
||||||
int assigned;
|
int assigned;
|
||||||
@ -167,6 +172,7 @@ struct hdmi_spec {
|
|||||||
hda_nid_t vendor_nid;
|
hda_nid_t vendor_nid;
|
||||||
const int *port_map;
|
const int *port_map;
|
||||||
int port_num;
|
int port_num;
|
||||||
|
bool send_silent_stream; /* Flag to enable silent stream feature */
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_SND_HDA_COMPONENT
|
#ifdef CONFIG_SND_HDA_COMPONENT
|
||||||
@ -1634,21 +1640,72 @@ static void hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
|
|||||||
snd_hda_power_down_pm(codec);
|
snd_hda_power_down_pm(codec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void silent_stream_enable(struct hda_codec *codec,
|
||||||
|
struct hdmi_spec_per_pin *per_pin)
|
||||||
|
{
|
||||||
|
unsigned int newval, oldval;
|
||||||
|
|
||||||
|
codec_dbg(codec, "hdmi: enabling silent stream for NID %d\n",
|
||||||
|
per_pin->pin_nid);
|
||||||
|
|
||||||
|
mutex_lock(&per_pin->lock);
|
||||||
|
|
||||||
|
if (!per_pin->channels)
|
||||||
|
per_pin->channels = 2;
|
||||||
|
|
||||||
|
oldval = snd_hda_codec_read(codec, per_pin->pin_nid, 0,
|
||||||
|
AC_VERB_GET_CONV, 0);
|
||||||
|
newval = (oldval & 0xF0) | 0xF;
|
||||||
|
snd_hda_codec_write(codec, per_pin->pin_nid, 0,
|
||||||
|
AC_VERB_SET_CHANNEL_STREAMID, newval);
|
||||||
|
|
||||||
|
hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
|
||||||
|
|
||||||
|
mutex_unlock(&per_pin->lock);
|
||||||
|
}
|
||||||
|
|
||||||
/* update ELD and jack state via audio component */
|
/* update ELD and jack state via audio component */
|
||||||
static void sync_eld_via_acomp(struct hda_codec *codec,
|
static void sync_eld_via_acomp(struct hda_codec *codec,
|
||||||
struct hdmi_spec_per_pin *per_pin)
|
struct hdmi_spec_per_pin *per_pin)
|
||||||
{
|
{
|
||||||
struct hdmi_spec *spec = codec->spec;
|
struct hdmi_spec *spec = codec->spec;
|
||||||
struct hdmi_eld *eld = &spec->temp_eld;
|
struct hdmi_eld *eld = &spec->temp_eld;
|
||||||
|
bool monitor_prev, monitor_next;
|
||||||
|
|
||||||
mutex_lock(&per_pin->lock);
|
mutex_lock(&per_pin->lock);
|
||||||
eld->monitor_present = false;
|
eld->monitor_present = false;
|
||||||
|
monitor_prev = per_pin->sink_eld.monitor_present;
|
||||||
eld->eld_size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid,
|
eld->eld_size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid,
|
||||||
per_pin->dev_id, &eld->monitor_present,
|
per_pin->dev_id, &eld->monitor_present,
|
||||||
eld->eld_buffer, ELD_MAX_SIZE);
|
eld->eld_buffer, ELD_MAX_SIZE);
|
||||||
eld->eld_valid = (eld->eld_size > 0);
|
eld->eld_valid = (eld->eld_size > 0);
|
||||||
update_eld(codec, per_pin, eld, 0);
|
update_eld(codec, per_pin, eld, 0);
|
||||||
|
monitor_next = per_pin->sink_eld.monitor_present;
|
||||||
mutex_unlock(&per_pin->lock);
|
mutex_unlock(&per_pin->lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Power-up will call hdmi_present_sense, so the PM calls
|
||||||
|
* have to be done without mutex held.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (spec->send_silent_stream) {
|
||||||
|
int pm_ret;
|
||||||
|
|
||||||
|
if (!monitor_prev && monitor_next) {
|
||||||
|
pm_ret = snd_hda_power_up_pm(codec);
|
||||||
|
if (pm_ret < 0)
|
||||||
|
codec_err(codec,
|
||||||
|
"Monitor plugged-in, Failed to power up codec ret=[%d]\n",
|
||||||
|
pm_ret);
|
||||||
|
silent_stream_enable(codec, per_pin);
|
||||||
|
} else if (monitor_prev && !monitor_next) {
|
||||||
|
pm_ret = snd_hda_power_down_pm(codec);
|
||||||
|
if (pm_ret < 0)
|
||||||
|
codec_err(codec,
|
||||||
|
"Monitor plugged-out, Failed to power down codec ret=[%d]\n",
|
||||||
|
pm_ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
|
static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
|
||||||
@ -2791,6 +2848,13 @@ static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid,
|
|||||||
spec->ops.setup_stream = i915_hsw_setup_stream;
|
spec->ops.setup_stream = i915_hsw_setup_stream;
|
||||||
spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup;
|
spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable silent stream feature, if it is enabled via
|
||||||
|
* module param or Kconfig option
|
||||||
|
*/
|
||||||
|
if (enable_silent_stream)
|
||||||
|
spec->send_silent_stream = true;
|
||||||
|
|
||||||
return parse_intel_hdmi(codec);
|
return parse_intel_hdmi(codec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user