aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorWu Fengguang <fengguang.wu@intel.com>2011-09-05 02:25:34 -0400
committerKeith Packard <keithp@keithp.com>2011-09-21 17:52:41 -0400
commite0dac65ed45e72fe34cc7ccc76de0ba220bd38bb (patch)
treef6f1628bb63a9db0b77318e0486e91d26c918ae0 /drivers
parent76adaa34db407f174dd06370cb60f6029c33b465 (diff)
drm/i915: pass ELD to HDMI/DP audio driver
Add ELD support for Intel Eaglelake, IbexPeak/Ironlake, SandyBridge/CougarPoint and IvyBridge/PantherPoint chips. ELD (EDID-Like Data) describes to the HDMI/DP audio driver the audio capabilities of the plugged monitor. It's built and passed to audio driver in 2 steps: (1) at get_modes time, parse EDID and save ELD to drm_connector.eld[] (2) at mode_set time, write drm_connector.eld[] to the Transcoder's hw ELD buffer and set the ELD_valid bit to inform HDMI/DP audio driver This patch is tested OK on G45/HDMI, IbexPeak/HDMI and IvyBridge/HDMI+DP. Test scheme: plug in the HDMI/DP monitor, and run cat /proc/asound/card0/eld* to check if the monitor name, HDMI/DP type, etc. show up correctly. Minor imperfection: the GEN5_AUD_CNTL_ST/DIP_Port_Select field always reads 0 (reserved). Without knowing the port number, I worked it around by setting the ELD_valid bit for ALL the three ports. It's tested to not be a problem, because the audio driver will find invalid ELD data and hence rightfully abort, even when it sees the ELD_valid indicator. Thanks to Zhenyu and Pierre-Louis for a lot of valuable help and testing. CC: Zhao Yakui <yakui.zhao@intel.com> CC: Wang Zhenyu <zhenyu.z.wang@intel.com> CC: Jeremy Bush <contractfrombelow@gmail.com> CC: Christopher White <c.white@pulseforce.com> CC: Pierre-Louis Bossart <pierre-louis.bossart@intel.com> CC: Paul Menzel <paulepanter@users.sourceforge.net> Signed-off-by: Wu Fengguang <fengguang.wu@intel.com> Signed-off-by: Keith Packard <keithp@keithp.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h2
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h25
-rw-r--r--drivers/gpu/drm/i915/intel_display.c131
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c6
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h2
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c3
-rw-r--r--drivers/gpu/drm/i915/intel_modes.c2
7 files changed, 169 insertions, 2 deletions
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 32de06f0b172..15c0ca58ad8b 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -209,6 +209,8 @@ struct drm_i915_display_funcs {
209 struct drm_display_mode *adjusted_mode, 209 struct drm_display_mode *adjusted_mode,
210 int x, int y, 210 int x, int y,
211 struct drm_framebuffer *old_fb); 211 struct drm_framebuffer *old_fb);
212 void (*write_eld)(struct drm_connector *connector,
213 struct drm_crtc *crtc);
212 void (*fdi_link_train)(struct drm_crtc *crtc); 214 void (*fdi_link_train)(struct drm_crtc *crtc);
213 void (*init_clock_gating)(struct drm_device *dev); 215 void (*init_clock_gating)(struct drm_device *dev);
214 void (*init_pch_clock_gating)(struct drm_device *dev); 216 void (*init_pch_clock_gating)(struct drm_device *dev);
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 8d9fce1ef0b0..a363fdd54ba2 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -3470,4 +3470,29 @@
3470#define GEN6_PCODE_DATA 0x138128 3470#define GEN6_PCODE_DATA 0x138128
3471#define GEN6_PCODE_FREQ_IA_RATIO_SHIFT 8 3471#define GEN6_PCODE_FREQ_IA_RATIO_SHIFT 8
3472 3472
3473#define G4X_AUD_VID_DID 0x62020
3474#define INTEL_AUDIO_DEVCL 0x808629FB
3475#define INTEL_AUDIO_DEVBLC 0x80862801
3476#define INTEL_AUDIO_DEVCTG 0x80862802
3477
3478#define G4X_AUD_CNTL_ST 0x620B4
3479#define G4X_ELDV_DEVCL_DEVBLC (1 << 13)
3480#define G4X_ELDV_DEVCTG (1 << 14)
3481#define G4X_ELD_ADDR (0xf << 5)
3482#define G4X_ELD_ACK (1 << 4)
3483#define G4X_HDMIW_HDMIEDID 0x6210C
3484
3485#define GEN5_HDMIW_HDMIEDID_A 0xE2050
3486#define GEN5_AUD_CNTL_ST_A 0xE20B4
3487#define GEN5_ELD_BUFFER_SIZE (0x1f << 10)
3488#define GEN5_ELD_ADDRESS (0x1f << 5)
3489#define GEN5_ELD_ACK (1 << 4)
3490#define GEN5_AUD_CNTL_ST2 0xE20C0
3491#define GEN5_ELD_VALIDB (1 << 0)
3492#define GEN5_CP_READYB (1 << 1)
3493
3494#define GEN7_HDMIW_HDMIEDID_A 0xE5050
3495#define GEN7_AUD_CNTRL_ST_A 0xE50B4
3496#define GEN7_AUD_CNTRL_ST2 0xE50C0
3497
3473#endif /* _I915_REG_H_ */ 3498#endif /* _I915_REG_H_ */
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index a685957566c9..f0e5f9f32aa8 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -31,6 +31,7 @@
31#include <linux/kernel.h> 31#include <linux/kernel.h>
32#include <linux/slab.h> 32#include <linux/slab.h>
33#include <linux/vgaarb.h> 33#include <linux/vgaarb.h>
34#include <drm/drm_edid.h>
34#include "drmP.h" 35#include "drmP.h"
35#include "intel_drv.h" 36#include "intel_drv.h"
36#include "i915_drm.h" 37#include "i915_drm.h"
@@ -5669,6 +5670,131 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
5669 return ret; 5670 return ret;
5670} 5671}
5671 5672
5673static void g4x_write_eld(struct drm_connector *connector,
5674 struct drm_crtc *crtc)
5675{
5676 struct drm_i915_private *dev_priv = connector->dev->dev_private;
5677 uint8_t *eld = connector->eld;
5678 uint32_t eldv;
5679 uint32_t len;
5680 uint32_t i;
5681
5682 i = I915_READ(G4X_AUD_VID_DID);
5683
5684 if (i == INTEL_AUDIO_DEVBLC || i == INTEL_AUDIO_DEVCL)
5685 eldv = G4X_ELDV_DEVCL_DEVBLC;
5686 else
5687 eldv = G4X_ELDV_DEVCTG;
5688
5689 i = I915_READ(G4X_AUD_CNTL_ST);
5690 i &= ~(eldv | G4X_ELD_ADDR);
5691 len = (i >> 9) & 0x1f; /* ELD buffer size */
5692 I915_WRITE(G4X_AUD_CNTL_ST, i);
5693
5694 if (!eld[0])
5695 return;
5696
5697 len = min_t(uint8_t, eld[2], len);
5698 DRM_DEBUG_DRIVER("ELD size %d\n", len);
5699 for (i = 0; i < len; i++)
5700 I915_WRITE(G4X_HDMIW_HDMIEDID, *((uint32_t *)eld + i));
5701
5702 i = I915_READ(G4X_AUD_CNTL_ST);
5703 i |= eldv;
5704 I915_WRITE(G4X_AUD_CNTL_ST, i);
5705}
5706
5707static void ironlake_write_eld(struct drm_connector *connector,
5708 struct drm_crtc *crtc)
5709{
5710 struct drm_i915_private *dev_priv = connector->dev->dev_private;
5711 uint8_t *eld = connector->eld;
5712 uint32_t eldv;
5713 uint32_t i;
5714 int len;
5715 int hdmiw_hdmiedid;
5716 int aud_cntl_st;
5717 int aud_cntrl_st2;
5718
5719 if (IS_IVYBRIDGE(connector->dev)) {
5720 hdmiw_hdmiedid = GEN7_HDMIW_HDMIEDID_A;
5721 aud_cntl_st = GEN7_AUD_CNTRL_ST_A;
5722 aud_cntrl_st2 = GEN7_AUD_CNTRL_ST2;
5723 } else {
5724 hdmiw_hdmiedid = GEN5_HDMIW_HDMIEDID_A;
5725 aud_cntl_st = GEN5_AUD_CNTL_ST_A;
5726 aud_cntrl_st2 = GEN5_AUD_CNTL_ST2;
5727 }
5728
5729 i = to_intel_crtc(crtc)->pipe;
5730 hdmiw_hdmiedid += i * 0x100;
5731 aud_cntl_st += i * 0x100;
5732
5733 DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(i));
5734
5735 i = I915_READ(aud_cntl_st);
5736 i = (i >> 29) & 0x3; /* DIP_Port_Select, 0x1 = PortB */
5737 if (!i) {
5738 DRM_DEBUG_DRIVER("Audio directed to unknown port\n");
5739 /* operate blindly on all ports */
5740 eldv = GEN5_ELD_VALIDB;
5741 eldv |= GEN5_ELD_VALIDB << 4;
5742 eldv |= GEN5_ELD_VALIDB << 8;
5743 } else {
5744 DRM_DEBUG_DRIVER("ELD on port %c\n", 'A' + i);
5745 eldv = GEN5_ELD_VALIDB << ((i - 1) * 4);
5746 }
5747
5748 i = I915_READ(aud_cntrl_st2);
5749 i &= ~eldv;
5750 I915_WRITE(aud_cntrl_st2, i);
5751
5752 if (!eld[0])
5753 return;
5754
5755 if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
5756 DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n");
5757 eld[5] |= (1 << 2); /* Conn_Type, 0x1 = DisplayPort */
5758 }
5759
5760 i = I915_READ(aud_cntl_st);
5761 i &= ~GEN5_ELD_ADDRESS;
5762 I915_WRITE(aud_cntl_st, i);
5763
5764 len = min_t(uint8_t, eld[2], 21); /* 84 bytes of hw ELD buffer */
5765 DRM_DEBUG_DRIVER("ELD size %d\n", len);
5766 for (i = 0; i < len; i++)
5767 I915_WRITE(hdmiw_hdmiedid, *((uint32_t *)eld + i));
5768
5769 i = I915_READ(aud_cntrl_st2);
5770 i |= eldv;
5771 I915_WRITE(aud_cntrl_st2, i);
5772}
5773
5774void intel_write_eld(struct drm_encoder *encoder,
5775 struct drm_display_mode *mode)
5776{
5777 struct drm_crtc *crtc = encoder->crtc;
5778 struct drm_connector *connector;
5779 struct drm_device *dev = encoder->dev;
5780 struct drm_i915_private *dev_priv = dev->dev_private;
5781
5782 connector = drm_select_eld(encoder, mode);
5783 if (!connector)
5784 return;
5785
5786 DRM_DEBUG_DRIVER("ELD on [CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
5787 connector->base.id,
5788 drm_get_connector_name(connector),
5789 connector->encoder->base.id,
5790 drm_get_encoder_name(connector->encoder));
5791
5792 connector->eld[6] = drm_av_sync_delay(connector, mode) / 2;
5793
5794 if (dev_priv->display.write_eld)
5795 dev_priv->display.write_eld(connector, crtc);
5796}
5797
5672/** Loads the palette/gamma unit for the CRTC with the prepared values */ 5798/** Loads the palette/gamma unit for the CRTC with the prepared values */
5673void intel_crtc_load_lut(struct drm_crtc *crtc) 5799void intel_crtc_load_lut(struct drm_crtc *crtc)
5674{ 5800{
@@ -8185,6 +8311,7 @@ static void intel_init_display(struct drm_device *dev)
8185 } 8311 }
8186 dev_priv->display.fdi_link_train = ironlake_fdi_link_train; 8312 dev_priv->display.fdi_link_train = ironlake_fdi_link_train;
8187 dev_priv->display.init_clock_gating = ironlake_init_clock_gating; 8313 dev_priv->display.init_clock_gating = ironlake_init_clock_gating;
8314 dev_priv->display.write_eld = ironlake_write_eld;
8188 } else if (IS_GEN6(dev)) { 8315 } else if (IS_GEN6(dev)) {
8189 if (SNB_READ_WM0_LATENCY()) { 8316 if (SNB_READ_WM0_LATENCY()) {
8190 dev_priv->display.update_wm = sandybridge_update_wm; 8317 dev_priv->display.update_wm = sandybridge_update_wm;
@@ -8195,6 +8322,7 @@ static void intel_init_display(struct drm_device *dev)
8195 } 8322 }
8196 dev_priv->display.fdi_link_train = gen6_fdi_link_train; 8323 dev_priv->display.fdi_link_train = gen6_fdi_link_train;
8197 dev_priv->display.init_clock_gating = gen6_init_clock_gating; 8324 dev_priv->display.init_clock_gating = gen6_init_clock_gating;
8325 dev_priv->display.write_eld = ironlake_write_eld;
8198 } else if (IS_IVYBRIDGE(dev)) { 8326 } else if (IS_IVYBRIDGE(dev)) {
8199 /* FIXME: detect B0+ stepping and use auto training */ 8327 /* FIXME: detect B0+ stepping and use auto training */
8200 dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train; 8328 dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train;
@@ -8206,7 +8334,7 @@ static void intel_init_display(struct drm_device *dev)
8206 dev_priv->display.update_wm = NULL; 8334 dev_priv->display.update_wm = NULL;
8207 } 8335 }
8208 dev_priv->display.init_clock_gating = ivybridge_init_clock_gating; 8336 dev_priv->display.init_clock_gating = ivybridge_init_clock_gating;
8209 8337 dev_priv->display.write_eld = ironlake_write_eld;
8210 } else 8338 } else
8211 dev_priv->display.update_wm = NULL; 8339 dev_priv->display.update_wm = NULL;
8212 } else if (IS_PINEVIEW(dev)) { 8340 } else if (IS_PINEVIEW(dev)) {
@@ -8226,6 +8354,7 @@ static void intel_init_display(struct drm_device *dev)
8226 dev_priv->display.update_wm = pineview_update_wm; 8354 dev_priv->display.update_wm = pineview_update_wm;
8227 dev_priv->display.init_clock_gating = gen3_init_clock_gating; 8355 dev_priv->display.init_clock_gating = gen3_init_clock_gating;
8228 } else if (IS_G4X(dev)) { 8356 } else if (IS_G4X(dev)) {
8357 dev_priv->display.write_eld = g4x_write_eld;
8229 dev_priv->display.update_wm = g4x_update_wm; 8358 dev_priv->display.update_wm = g4x_update_wm;
8230 dev_priv->display.init_clock_gating = g4x_init_clock_gating; 8359 dev_priv->display.init_clock_gating = g4x_init_clock_gating;
8231 } else if (IS_GEN4(dev)) { 8360 } else if (IS_GEN4(dev)) {
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 4091f2182e6d..6cbde9ff1ec6 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -773,8 +773,12 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
773 intel_dp->DP |= DP_PORT_WIDTH_4; 773 intel_dp->DP |= DP_PORT_WIDTH_4;
774 break; 774 break;
775 } 775 }
776 if (intel_dp->has_audio) 776 if (intel_dp->has_audio) {
777 DRM_DEBUG_DRIVER("Enabling DP audio on pipe %c\n",
778 pipe_name(intel_crtc->pipe));
777 intel_dp->DP |= DP_AUDIO_OUTPUT_ENABLE; 779 intel_dp->DP |= DP_AUDIO_OUTPUT_ENABLE;
780 intel_write_eld(encoder, adjusted_mode);
781 }
778 782
779 memset(intel_dp->link_configuration, 0, DP_LINK_CONFIGURATION_SIZE); 783 memset(intel_dp->link_configuration, 0, DP_LINK_CONFIGURATION_SIZE);
780 intel_dp->link_configuration[0] = intel_dp->link_bw; 784 intel_dp->link_configuration[0] = intel_dp->link_bw;
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 375690b21624..b7e718639b13 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -380,4 +380,6 @@ extern void intel_fb_output_poll_changed(struct drm_device *dev);
380extern void intel_fb_restore_mode(struct drm_device *dev); 380extern void intel_fb_restore_mode(struct drm_device *dev);
381 381
382extern void intel_init_clock_gating(struct drm_device *dev); 382extern void intel_init_clock_gating(struct drm_device *dev);
383extern void intel_write_eld(struct drm_encoder *encoder,
384 struct drm_display_mode *mode);
383#endif /* __INTEL_DRV_H__ */ 385#endif /* __INTEL_DRV_H__ */
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 226ba830f383..75026ba41a8e 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -245,8 +245,11 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
245 sdvox |= HDMI_MODE_SELECT; 245 sdvox |= HDMI_MODE_SELECT;
246 246
247 if (intel_hdmi->has_audio) { 247 if (intel_hdmi->has_audio) {
248 DRM_DEBUG_DRIVER("Enabling HDMI audio on pipe %c\n",
249 pipe_name(intel_crtc->pipe));
248 sdvox |= SDVO_AUDIO_ENABLE; 250 sdvox |= SDVO_AUDIO_ENABLE;
249 sdvox |= SDVO_NULL_PACKETS_DURING_VSYNC; 251 sdvox |= SDVO_NULL_PACKETS_DURING_VSYNC;
252 intel_write_eld(encoder, adjusted_mode);
250 } 253 }
251 254
252 if (intel_crtc->pipe == 1) { 255 if (intel_crtc->pipe == 1) {
diff --git a/drivers/gpu/drm/i915/intel_modes.c b/drivers/gpu/drm/i915/intel_modes.c
index 3b26a3ba02dd..be2c6fe07d12 100644
--- a/drivers/gpu/drm/i915/intel_modes.c
+++ b/drivers/gpu/drm/i915/intel_modes.c
@@ -26,6 +26,7 @@
26#include <linux/slab.h> 26#include <linux/slab.h>
27#include <linux/i2c.h> 27#include <linux/i2c.h>
28#include <linux/fb.h> 28#include <linux/fb.h>
29#include <drm/drm_edid.h>
29#include "drmP.h" 30#include "drmP.h"
30#include "intel_drv.h" 31#include "intel_drv.h"
31#include "i915_drv.h" 32#include "i915_drv.h"
@@ -74,6 +75,7 @@ int intel_ddc_get_modes(struct drm_connector *connector,
74 if (edid) { 75 if (edid) {
75 drm_mode_connector_update_edid_property(connector, edid); 76 drm_mode_connector_update_edid_property(connector, edid);
76 ret = drm_add_edid_modes(connector, edid); 77 ret = drm_add_edid_modes(connector, edid);
78 drm_edid_to_eld(connector, edid);
77 connector->display_info.raw_edid = NULL; 79 connector->display_info.raw_edid = NULL;
78 kfree(edid); 80 kfree(edid);
79 } 81 }