|
@@ -31,6 +31,7 @@
|
|
|
#include <linux/kernel.h>
|
|
|
#include <linux/slab.h>
|
|
|
#include <linux/vgaarb.h>
|
|
|
+#include <drm/drm_edid.h>
|
|
|
#include "drmP.h"
|
|
|
#include "intel_drv.h"
|
|
|
#include "i915_drm.h"
|
|
@@ -5669,6 +5670,131 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static void g4x_write_eld(struct drm_connector *connector,
|
|
|
+ struct drm_crtc *crtc)
|
|
|
+{
|
|
|
+ struct drm_i915_private *dev_priv = connector->dev->dev_private;
|
|
|
+ uint8_t *eld = connector->eld;
|
|
|
+ uint32_t eldv;
|
|
|
+ uint32_t len;
|
|
|
+ uint32_t i;
|
|
|
+
|
|
|
+ i = I915_READ(G4X_AUD_VID_DID);
|
|
|
+
|
|
|
+ if (i == INTEL_AUDIO_DEVBLC || i == INTEL_AUDIO_DEVCL)
|
|
|
+ eldv = G4X_ELDV_DEVCL_DEVBLC;
|
|
|
+ else
|
|
|
+ eldv = G4X_ELDV_DEVCTG;
|
|
|
+
|
|
|
+ i = I915_READ(G4X_AUD_CNTL_ST);
|
|
|
+ i &= ~(eldv | G4X_ELD_ADDR);
|
|
|
+ len = (i >> 9) & 0x1f; /* ELD buffer size */
|
|
|
+ I915_WRITE(G4X_AUD_CNTL_ST, i);
|
|
|
+
|
|
|
+ if (!eld[0])
|
|
|
+ return;
|
|
|
+
|
|
|
+ len = min_t(uint8_t, eld[2], len);
|
|
|
+ DRM_DEBUG_DRIVER("ELD size %d\n", len);
|
|
|
+ for (i = 0; i < len; i++)
|
|
|
+ I915_WRITE(G4X_HDMIW_HDMIEDID, *((uint32_t *)eld + i));
|
|
|
+
|
|
|
+ i = I915_READ(G4X_AUD_CNTL_ST);
|
|
|
+ i |= eldv;
|
|
|
+ I915_WRITE(G4X_AUD_CNTL_ST, i);
|
|
|
+}
|
|
|
+
|
|
|
+static void ironlake_write_eld(struct drm_connector *connector,
|
|
|
+ struct drm_crtc *crtc)
|
|
|
+{
|
|
|
+ struct drm_i915_private *dev_priv = connector->dev->dev_private;
|
|
|
+ uint8_t *eld = connector->eld;
|
|
|
+ uint32_t eldv;
|
|
|
+ uint32_t i;
|
|
|
+ int len;
|
|
|
+ int hdmiw_hdmiedid;
|
|
|
+ int aud_cntl_st;
|
|
|
+ int aud_cntrl_st2;
|
|
|
+
|
|
|
+ if (IS_IVYBRIDGE(connector->dev)) {
|
|
|
+ hdmiw_hdmiedid = GEN7_HDMIW_HDMIEDID_A;
|
|
|
+ aud_cntl_st = GEN7_AUD_CNTRL_ST_A;
|
|
|
+ aud_cntrl_st2 = GEN7_AUD_CNTRL_ST2;
|
|
|
+ } else {
|
|
|
+ hdmiw_hdmiedid = GEN5_HDMIW_HDMIEDID_A;
|
|
|
+ aud_cntl_st = GEN5_AUD_CNTL_ST_A;
|
|
|
+ aud_cntrl_st2 = GEN5_AUD_CNTL_ST2;
|
|
|
+ }
|
|
|
+
|
|
|
+ i = to_intel_crtc(crtc)->pipe;
|
|
|
+ hdmiw_hdmiedid += i * 0x100;
|
|
|
+ aud_cntl_st += i * 0x100;
|
|
|
+
|
|
|
+ DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(i));
|
|
|
+
|
|
|
+ i = I915_READ(aud_cntl_st);
|
|
|
+ i = (i >> 29) & 0x3; /* DIP_Port_Select, 0x1 = PortB */
|
|
|
+ if (!i) {
|
|
|
+ DRM_DEBUG_DRIVER("Audio directed to unknown port\n");
|
|
|
+ /* operate blindly on all ports */
|
|
|
+ eldv = GEN5_ELD_VALIDB;
|
|
|
+ eldv |= GEN5_ELD_VALIDB << 4;
|
|
|
+ eldv |= GEN5_ELD_VALIDB << 8;
|
|
|
+ } else {
|
|
|
+ DRM_DEBUG_DRIVER("ELD on port %c\n", 'A' + i);
|
|
|
+ eldv = GEN5_ELD_VALIDB << ((i - 1) * 4);
|
|
|
+ }
|
|
|
+
|
|
|
+ i = I915_READ(aud_cntrl_st2);
|
|
|
+ i &= ~eldv;
|
|
|
+ I915_WRITE(aud_cntrl_st2, i);
|
|
|
+
|
|
|
+ if (!eld[0])
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
|
|
|
+ DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n");
|
|
|
+ eld[5] |= (1 << 2); /* Conn_Type, 0x1 = DisplayPort */
|
|
|
+ }
|
|
|
+
|
|
|
+ i = I915_READ(aud_cntl_st);
|
|
|
+ i &= ~GEN5_ELD_ADDRESS;
|
|
|
+ I915_WRITE(aud_cntl_st, i);
|
|
|
+
|
|
|
+ len = min_t(uint8_t, eld[2], 21); /* 84 bytes of hw ELD buffer */
|
|
|
+ DRM_DEBUG_DRIVER("ELD size %d\n", len);
|
|
|
+ for (i = 0; i < len; i++)
|
|
|
+ I915_WRITE(hdmiw_hdmiedid, *((uint32_t *)eld + i));
|
|
|
+
|
|
|
+ i = I915_READ(aud_cntrl_st2);
|
|
|
+ i |= eldv;
|
|
|
+ I915_WRITE(aud_cntrl_st2, i);
|
|
|
+}
|
|
|
+
|
|
|
+void intel_write_eld(struct drm_encoder *encoder,
|
|
|
+ struct drm_display_mode *mode)
|
|
|
+{
|
|
|
+ struct drm_crtc *crtc = encoder->crtc;
|
|
|
+ struct drm_connector *connector;
|
|
|
+ struct drm_device *dev = encoder->dev;
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+
|
|
|
+ connector = drm_select_eld(encoder, mode);
|
|
|
+ if (!connector)
|
|
|
+ return;
|
|
|
+
|
|
|
+ DRM_DEBUG_DRIVER("ELD on [CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
|
|
|
+ connector->base.id,
|
|
|
+ drm_get_connector_name(connector),
|
|
|
+ connector->encoder->base.id,
|
|
|
+ drm_get_encoder_name(connector->encoder));
|
|
|
+
|
|
|
+ connector->eld[6] = drm_av_sync_delay(connector, mode) / 2;
|
|
|
+
|
|
|
+ if (dev_priv->display.write_eld)
|
|
|
+ dev_priv->display.write_eld(connector, crtc);
|
|
|
+}
|
|
|
+
|
|
|
/** Loads the palette/gamma unit for the CRTC with the prepared values */
|
|
|
void intel_crtc_load_lut(struct drm_crtc *crtc)
|
|
|
{
|
|
@@ -8185,6 +8311,7 @@ static void intel_init_display(struct drm_device *dev)
|
|
|
}
|
|
|
dev_priv->display.fdi_link_train = ironlake_fdi_link_train;
|
|
|
dev_priv->display.init_clock_gating = ironlake_init_clock_gating;
|
|
|
+ dev_priv->display.write_eld = ironlake_write_eld;
|
|
|
} else if (IS_GEN6(dev)) {
|
|
|
if (SNB_READ_WM0_LATENCY()) {
|
|
|
dev_priv->display.update_wm = sandybridge_update_wm;
|
|
@@ -8195,6 +8322,7 @@ static void intel_init_display(struct drm_device *dev)
|
|
|
}
|
|
|
dev_priv->display.fdi_link_train = gen6_fdi_link_train;
|
|
|
dev_priv->display.init_clock_gating = gen6_init_clock_gating;
|
|
|
+ dev_priv->display.write_eld = ironlake_write_eld;
|
|
|
} else if (IS_IVYBRIDGE(dev)) {
|
|
|
/* FIXME: detect B0+ stepping and use auto training */
|
|
|
dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train;
|
|
@@ -8206,7 +8334,7 @@ static void intel_init_display(struct drm_device *dev)
|
|
|
dev_priv->display.update_wm = NULL;
|
|
|
}
|
|
|
dev_priv->display.init_clock_gating = ivybridge_init_clock_gating;
|
|
|
-
|
|
|
+ dev_priv->display.write_eld = ironlake_write_eld;
|
|
|
} else
|
|
|
dev_priv->display.update_wm = NULL;
|
|
|
} else if (IS_PINEVIEW(dev)) {
|
|
@@ -8226,6 +8354,7 @@ static void intel_init_display(struct drm_device *dev)
|
|
|
dev_priv->display.update_wm = pineview_update_wm;
|
|
|
dev_priv->display.init_clock_gating = gen3_init_clock_gating;
|
|
|
} else if (IS_G4X(dev)) {
|
|
|
+ dev_priv->display.write_eld = g4x_write_eld;
|
|
|
dev_priv->display.update_wm = g4x_update_wm;
|
|
|
dev_priv->display.init_clock_gating = g4x_init_clock_gating;
|
|
|
} else if (IS_GEN4(dev)) {
|