Commit 86a0dbe0 authored by Ville Syrjälä's avatar Ville Syrjälä Committed by Greg Kroah-Hartman

drm/i915/sdvo: Implement proper HDMI audio support for SDVO

commit d74408f5 upstream.

Our SDVO audio support is pretty bogus. We can't push audio over the
SDVO bus, so trying to enable audio in the SDVO control register doesn't
do anything. In fact it looks like the SDVO encoder will always mix in
the audio coming over HDA, and there's no (at least documented) way to
disable that from our side. So HDMI audio does work currently on gen4
but only by luck really. On gen3 it got broken by the referenced commit.
And what has always been missing on every platform is the ELD.

To pass the ELD to the audio driver we need to write it to magic buffer
in the SDVO encoder hardware which then gets pulled out via HDA in the
other end. Ie. pretty much the same thing we had for native HDMI before
we started to just pass the ELD between the drivers. This sort of
explains why we even have that silly hardware buffer with native HDMI.

$ cat /proc/asound/card0/eld#1.0
-monitor_present		0
-eld_valid		0
+monitor_present		1
+eld_valid		1
+monitor_name		LG TV
+connection_type		HDMI
+...

This also fixes our state readout since we can now query the SDVO
encoder about the state of the "ELD valid" and "presence detect"
bits. As mentioned those don't actually control whether audio
gets sent over the HDMI cable, but it's the best we can do. And with
the state checker appeased we can re-enable HDMI audio for gen3.

Cc: stable@vger.kernel.org
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: zardam@gmail.com
Tested-by: zardam@gmail.com
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=108976
Fixes: de44e256 ("drm/i915/sdvo: Shut up state checker with hdmi cards on gen3")
Signed-off-by: default avatarVille Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190409144054.24561-3-ville.syrjala@linux.intel.comReviewed-by: default avatarImre Deak <imre.deak@intel.com>
(cherry picked from commit dc49a56b)
Signed-off-by: default avatarJani Nikula <jani.nikula@intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 4a12cc87
...@@ -909,6 +909,13 @@ static bool intel_sdvo_set_colorimetry(struct intel_sdvo *intel_sdvo, ...@@ -909,6 +909,13 @@ static bool intel_sdvo_set_colorimetry(struct intel_sdvo *intel_sdvo,
return intel_sdvo_set_value(intel_sdvo, SDVO_CMD_SET_COLORIMETRY, &mode, 1); return intel_sdvo_set_value(intel_sdvo, SDVO_CMD_SET_COLORIMETRY, &mode, 1);
} }
static bool intel_sdvo_set_audio_state(struct intel_sdvo *intel_sdvo,
u8 audio_state)
{
return intel_sdvo_set_value(intel_sdvo, SDVO_CMD_SET_AUDIO_STAT,
&audio_state, 1);
}
#if 0 #if 0
static void intel_sdvo_dump_hdmi_buf(struct intel_sdvo *intel_sdvo) static void intel_sdvo_dump_hdmi_buf(struct intel_sdvo *intel_sdvo)
{ {
...@@ -1366,11 +1373,6 @@ static void intel_sdvo_pre_enable(struct intel_encoder *intel_encoder, ...@@ -1366,11 +1373,6 @@ static void intel_sdvo_pre_enable(struct intel_encoder *intel_encoder,
else else
sdvox |= SDVO_PIPE_SEL(crtc->pipe); sdvox |= SDVO_PIPE_SEL(crtc->pipe);
if (crtc_state->has_audio) {
WARN_ON_ONCE(INTEL_GEN(dev_priv) < 4);
sdvox |= SDVO_AUDIO_ENABLE;
}
if (INTEL_GEN(dev_priv) >= 4) { if (INTEL_GEN(dev_priv) >= 4) {
/* done in crtc_mode_set as the dpll_md reg must be written early */ /* done in crtc_mode_set as the dpll_md reg must be written early */
} else if (IS_I945G(dev_priv) || IS_I945GM(dev_priv) || } else if (IS_I945G(dev_priv) || IS_I945GM(dev_priv) ||
...@@ -1510,8 +1512,13 @@ static void intel_sdvo_get_config(struct intel_encoder *encoder, ...@@ -1510,8 +1512,13 @@ static void intel_sdvo_get_config(struct intel_encoder *encoder,
if (sdvox & HDMI_COLOR_RANGE_16_235) if (sdvox & HDMI_COLOR_RANGE_16_235)
pipe_config->limited_color_range = true; pipe_config->limited_color_range = true;
if (sdvox & SDVO_AUDIO_ENABLE) if (intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_AUDIO_STAT,
pipe_config->has_audio = true; &val, 1)) {
u8 mask = SDVO_AUDIO_ELD_VALID | SDVO_AUDIO_PRESENCE_DETECT;
if ((val & mask) == mask)
pipe_config->has_audio = true;
}
if (intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_ENCODE, if (intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_ENCODE,
&val, 1)) { &val, 1)) {
...@@ -1524,6 +1531,32 @@ static void intel_sdvo_get_config(struct intel_encoder *encoder, ...@@ -1524,6 +1531,32 @@ static void intel_sdvo_get_config(struct intel_encoder *encoder,
pipe_config->pixel_multiplier, encoder_pixel_multiplier); pipe_config->pixel_multiplier, encoder_pixel_multiplier);
} }
static void intel_sdvo_disable_audio(struct intel_sdvo *intel_sdvo)
{
intel_sdvo_set_audio_state(intel_sdvo, 0);
}
static void intel_sdvo_enable_audio(struct intel_sdvo *intel_sdvo,
const struct intel_crtc_state *crtc_state,
const struct drm_connector_state *conn_state)
{
const struct drm_display_mode *adjusted_mode =
&crtc_state->base.adjusted_mode;
struct drm_connector *connector = conn_state->connector;
u8 *eld = connector->eld;
eld[6] = drm_av_sync_delay(connector, adjusted_mode) / 2;
intel_sdvo_set_audio_state(intel_sdvo, 0);
intel_sdvo_write_infoframe(intel_sdvo, SDVO_HBUF_INDEX_ELD,
SDVO_HBUF_TX_DISABLED,
eld, drm_eld_size(eld));
intel_sdvo_set_audio_state(intel_sdvo, SDVO_AUDIO_ELD_VALID |
SDVO_AUDIO_PRESENCE_DETECT);
}
static void intel_disable_sdvo(struct intel_encoder *encoder, static void intel_disable_sdvo(struct intel_encoder *encoder,
const struct intel_crtc_state *old_crtc_state, const struct intel_crtc_state *old_crtc_state,
const struct drm_connector_state *conn_state) const struct drm_connector_state *conn_state)
...@@ -1533,6 +1566,9 @@ static void intel_disable_sdvo(struct intel_encoder *encoder, ...@@ -1533,6 +1566,9 @@ static void intel_disable_sdvo(struct intel_encoder *encoder,
struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->base.crtc); struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->base.crtc);
u32 temp; u32 temp;
if (old_crtc_state->has_audio)
intel_sdvo_disable_audio(intel_sdvo);
intel_sdvo_set_active_outputs(intel_sdvo, 0); intel_sdvo_set_active_outputs(intel_sdvo, 0);
if (0) if (0)
intel_sdvo_set_encoder_power_state(intel_sdvo, intel_sdvo_set_encoder_power_state(intel_sdvo,
...@@ -1618,6 +1654,9 @@ static void intel_enable_sdvo(struct intel_encoder *encoder, ...@@ -1618,6 +1654,9 @@ static void intel_enable_sdvo(struct intel_encoder *encoder,
intel_sdvo_set_encoder_power_state(intel_sdvo, intel_sdvo_set_encoder_power_state(intel_sdvo,
DRM_MODE_DPMS_ON); DRM_MODE_DPMS_ON);
intel_sdvo_set_active_outputs(intel_sdvo, intel_sdvo->attached_output); intel_sdvo_set_active_outputs(intel_sdvo, intel_sdvo->attached_output);
if (pipe_config->has_audio)
intel_sdvo_enable_audio(intel_sdvo, pipe_config, conn_state);
} }
static enum drm_mode_status static enum drm_mode_status
...@@ -2480,7 +2519,6 @@ static bool ...@@ -2480,7 +2519,6 @@ static bool
intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device) intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device)
{ {
struct drm_encoder *encoder = &intel_sdvo->base.base; struct drm_encoder *encoder = &intel_sdvo->base.base;
struct drm_i915_private *dev_priv = to_i915(encoder->dev);
struct drm_connector *connector; struct drm_connector *connector;
struct intel_encoder *intel_encoder = to_intel_encoder(encoder); struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
struct intel_connector *intel_connector; struct intel_connector *intel_connector;
...@@ -2517,9 +2555,7 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device) ...@@ -2517,9 +2555,7 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device)
encoder->encoder_type = DRM_MODE_ENCODER_TMDS; encoder->encoder_type = DRM_MODE_ENCODER_TMDS;
connector->connector_type = DRM_MODE_CONNECTOR_DVID; connector->connector_type = DRM_MODE_CONNECTOR_DVID;
/* gen3 doesn't do the hdmi bits in the SDVO register */ if (intel_sdvo_is_hdmi_connector(intel_sdvo, device)) {
if (INTEL_GEN(dev_priv) >= 4 &&
intel_sdvo_is_hdmi_connector(intel_sdvo, device)) {
connector->connector_type = DRM_MODE_CONNECTOR_HDMIA; connector->connector_type = DRM_MODE_CONNECTOR_HDMIA;
intel_sdvo_connector->is_hdmi = true; intel_sdvo_connector->is_hdmi = true;
} }
......
...@@ -707,6 +707,9 @@ struct intel_sdvo_enhancements_arg { ...@@ -707,6 +707,9 @@ struct intel_sdvo_enhancements_arg {
#define SDVO_CMD_GET_AUDIO_ENCRYPT_PREFER 0x90 #define SDVO_CMD_GET_AUDIO_ENCRYPT_PREFER 0x90
#define SDVO_CMD_SET_AUDIO_STAT 0x91 #define SDVO_CMD_SET_AUDIO_STAT 0x91
#define SDVO_CMD_GET_AUDIO_STAT 0x92 #define SDVO_CMD_GET_AUDIO_STAT 0x92
#define SDVO_AUDIO_ELD_VALID (1 << 0)
#define SDVO_AUDIO_PRESENCE_DETECT (1 << 1)
#define SDVO_AUDIO_CP_READY (1 << 2)
#define SDVO_CMD_SET_HBUF_INDEX 0x93 #define SDVO_CMD_SET_HBUF_INDEX 0x93
#define SDVO_HBUF_INDEX_ELD 0 #define SDVO_HBUF_INDEX_ELD 0
#define SDVO_HBUF_INDEX_AVI_IF 1 #define SDVO_HBUF_INDEX_AVI_IF 1
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment