summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/intel_hdmi.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-07-09 16:04:31 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2013-07-09 16:04:31 -0700
commit2e17c5a97e231f3cb426f4b7895eab5be5c5442e (patch)
tree80871817427250200d6931a45ccb4833c4add74a /drivers/gpu/drm/i915/intel_hdmi.c
parent5f097cd249f00683442c3e265d6f27d80fc83563 (diff)
parent774d8e34e46506222bb5e2888e3ef42b2775715f (diff)
downloadlinux-2e17c5a97e231f3cb426f4b7895eab5be5c5442e.tar.bz2
Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
Pull drm updates from Dave Airlie: "Okay this is the big one, I was stalled on the fbdev pull req as I stupidly let fbdev guys merge a patch I required to fix a warning with some patches I had, they ended up merging the patch from the wrong place, but the warning should be fixed. In future I'll just take the patch myself! Outside drm: There are some snd changes for the HDMI audio interactions on haswell, they've been acked for inclusion via my tree. This relies on the wound/wait tree from Ingo which is already merged. Major changes: AMD finally released the dynamic power management code for all their GPUs from r600->present day, this is great, off by default for now but also a huge amount of code, in fact it is most of this pull request. Since it landed there has been a lot of community testing and Alex has sent a lot of fixes for any bugs found so far. I suspect radeon might now be the biggest kernel driver ever :-P p.s. radeon.dpm=1 to enable dynamic powermanagement for anyone. New drivers: Renesas r-car display unit. Other highlights: - core: GEM CMA prime support, use new w/w mutexs for TTM reservations, cursor hotspot, doc updates - dvo chips: chrontel 7010B support - i915: Haswell (fbc, ips, vecs, watermarks, audio powerwell), Valleyview (enabled by default, rc6), lots of pll reworking, 30bpp support (this time for sure) - nouveau: async buffer object deletion, context/register init updates, kernel vp2 engine support, GF117 support, GK110 accel support (with external nvidia ucode), context cleanups. - exynos: memory leak fixes, Add S3C64XX SoC series support, device tree updates, common clock framework support, - qxl: cursor hotspot support, multi-monitor support, suspend/resume support - mgag200: hw cursor support, g200 mode limiting - shmobile: prime support - tegra: fixes mostly I've been banging on this quite a lot due to the size of it, and it seems to okay on everything I've tested it on." * 'drm-next' of git://people.freedesktop.org/~airlied/linux: (811 commits) drm/radeon/dpm: implement vblank_too_short callback for si drm/radeon/dpm: implement vblank_too_short callback for cayman drm/radeon/dpm: implement vblank_too_short callback for btc drm/radeon/dpm: implement vblank_too_short callback for evergreen drm/radeon/dpm: implement vblank_too_short callback for 7xx drm/radeon/dpm: add checks against vblank time drm/radeon/dpm: add helper to calculate vblank time drm/radeon: remove stray line in old pm code drm/radeon/dpm: fix display_gap programming on rv7xx drm/nvc0/gr: fix gpc firmware regression drm/nouveau: fix minor thinko causing bo moves to not be async on kepler drm/radeon/dpm: implement force performance level for TN drm/radeon/dpm: implement force performance level for ON/LN drm/radeon/dpm: implement force performance level for SI drm/radeon/dpm: implement force performance level for cayman drm/radeon/dpm: implement force performance levels for 7xx/eg/btc drm/radeon/dpm: add infrastructure to force performance levels drm/radeon: fix surface setup on r1xx drm/radeon: add support for 3d perf states on older asics drm/radeon: set default clocks for SI when DPM is disabled ...
Diffstat (limited to 'drivers/gpu/drm/i915/intel_hdmi.c')
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c158
1 files changed, 151 insertions, 7 deletions
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index a9057930f2b2..98df2a0c85bd 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -602,7 +602,7 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
u32 hdmi_val;
hdmi_val = SDVO_ENCODING_HDMI;
- if (!HAS_PCH_SPLIT(dev) && !IS_VALLEYVIEW(dev))
+ if (!HAS_PCH_SPLIT(dev))
hdmi_val |= intel_hdmi->color_range;
if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
hdmi_val |= SDVO_VSYNC_ACTIVE_HIGH;
@@ -658,6 +658,28 @@ static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder,
return true;
}
+static void intel_hdmi_get_config(struct intel_encoder *encoder,
+ struct intel_crtc_config *pipe_config)
+{
+ struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
+ struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+ u32 tmp, flags = 0;
+
+ tmp = I915_READ(intel_hdmi->hdmi_reg);
+
+ if (tmp & SDVO_HSYNC_ACTIVE_HIGH)
+ flags |= DRM_MODE_FLAG_PHSYNC;
+ else
+ flags |= DRM_MODE_FLAG_NHSYNC;
+
+ if (tmp & SDVO_VSYNC_ACTIVE_HIGH)
+ flags |= DRM_MODE_FLAG_PVSYNC;
+ else
+ flags |= DRM_MODE_FLAG_NVSYNC;
+
+ pipe_config->adjusted_mode.flags |= flags;
+}
+
static void intel_enable_hdmi(struct intel_encoder *encoder)
{
struct drm_device *dev = encoder->base.dev;
@@ -697,6 +719,14 @@ static void intel_enable_hdmi(struct intel_encoder *encoder)
I915_WRITE(intel_hdmi->hdmi_reg, temp);
POSTING_READ(intel_hdmi->hdmi_reg);
}
+
+ if (IS_VALLEYVIEW(dev)) {
+ struct intel_digital_port *dport =
+ enc_to_dig_port(&encoder->base);
+ int channel = vlv_dport_to_channel(dport);
+
+ vlv_wait_port_ready(dev_priv, channel);
+ }
}
static void intel_disable_hdmi(struct intel_encoder *encoder)
@@ -775,6 +805,8 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
struct drm_device *dev = encoder->base.dev;
struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
+ int clock_12bpc = pipe_config->requested_mode.clock * 3 / 2;
+ int desired_bpp;
if (intel_hdmi->color_range_auto) {
/* See CEA-861-E - 5.1 Default Encoding Parameters */
@@ -794,14 +826,29 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
/*
* HDMI is either 12 or 8, so if the display lets 10bpc sneak
* through, clamp it down. Note that g4x/vlv don't support 12bpc hdmi
- * outputs.
+ * outputs. We also need to check that the higher clock still fits
+ * within limits.
*/
- if (pipe_config->pipe_bpp > 8*3 && HAS_PCH_SPLIT(dev)) {
- DRM_DEBUG_KMS("forcing bpc to 12 for HDMI\n");
- pipe_config->pipe_bpp = 12*3;
+ if (pipe_config->pipe_bpp > 8*3 && clock_12bpc <= 225000
+ && HAS_PCH_SPLIT(dev)) {
+ DRM_DEBUG_KMS("picking bpc to 12 for HDMI output\n");
+ desired_bpp = 12*3;
+
+ /* Need to adjust the port link by 1.5x for 12bpc. */
+ pipe_config->port_clock = clock_12bpc;
} else {
- DRM_DEBUG_KMS("forcing bpc to 8 for HDMI\n");
- pipe_config->pipe_bpp = 8*3;
+ DRM_DEBUG_KMS("picking bpc to 8 for HDMI output\n");
+ desired_bpp = 8*3;
+ }
+
+ if (!pipe_config->bw_constrained) {
+ DRM_DEBUG_KMS("forcing pipe bpc to %i for HDMI\n", desired_bpp);
+ pipe_config->pipe_bpp = desired_bpp;
+ }
+
+ if (adjusted_mode->clock > 225000) {
+ DRM_DEBUG_KMS("too high HDMI clock, rejecting mode\n");
+ return false;
}
return true;
@@ -955,6 +1002,97 @@ done:
return 0;
}
+static void intel_hdmi_pre_enable(struct intel_encoder *encoder)
+{
+ struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
+ struct drm_device *dev = encoder->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc =
+ to_intel_crtc(encoder->base.crtc);
+ int port = vlv_dport_to_channel(dport);
+ int pipe = intel_crtc->pipe;
+ u32 val;
+
+ if (!IS_VALLEYVIEW(dev))
+ return;
+
+ /* Enable clock channels for this port */
+ val = vlv_dpio_read(dev_priv, DPIO_DATA_LANE_A(port));
+ val = 0;
+ if (pipe)
+ val |= (1<<21);
+ else
+ val &= ~(1<<21);
+ val |= 0x001000c4;
+ vlv_dpio_write(dev_priv, DPIO_DATA_CHANNEL(port), val);
+
+ /* HDMI 1.0V-2dB */
+ vlv_dpio_write(dev_priv, DPIO_TX_OCALINIT(port), 0);
+ vlv_dpio_write(dev_priv, DPIO_TX_SWING_CTL4(port),
+ 0x2b245f5f);
+ vlv_dpio_write(dev_priv, DPIO_TX_SWING_CTL2(port),
+ 0x5578b83a);
+ vlv_dpio_write(dev_priv, DPIO_TX_SWING_CTL3(port),
+ 0x0c782040);
+ vlv_dpio_write(dev_priv, DPIO_TX3_SWING_CTL4(port),
+ 0x2b247878);
+ vlv_dpio_write(dev_priv, DPIO_PCS_STAGGER0(port), 0x00030000);
+ vlv_dpio_write(dev_priv, DPIO_PCS_CTL_OVER1(port),
+ 0x00002000);
+ vlv_dpio_write(dev_priv, DPIO_TX_OCALINIT(port),
+ DPIO_TX_OCALINIT_EN);
+
+ /* Program lane clock */
+ vlv_dpio_write(dev_priv, DPIO_PCS_CLOCKBUF0(port),
+ 0x00760018);
+ vlv_dpio_write(dev_priv, DPIO_PCS_CLOCKBUF8(port),
+ 0x00400888);
+}
+
+static void intel_hdmi_pre_pll_enable(struct intel_encoder *encoder)
+{
+ struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
+ struct drm_device *dev = encoder->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int port = vlv_dport_to_channel(dport);
+
+ if (!IS_VALLEYVIEW(dev))
+ return;
+
+ /* Program Tx lane resets to default */
+ vlv_dpio_write(dev_priv, DPIO_PCS_TX(port),
+ DPIO_PCS_TX_LANE2_RESET |
+ DPIO_PCS_TX_LANE1_RESET);
+ vlv_dpio_write(dev_priv, DPIO_PCS_CLK(port),
+ DPIO_PCS_CLK_CRI_RXEB_EIOS_EN |
+ DPIO_PCS_CLK_CRI_RXDIGFILTSG_EN |
+ (1<<DPIO_PCS_CLK_DATAWIDTH_SHIFT) |
+ DPIO_PCS_CLK_SOFT_RESET);
+
+ /* Fix up inter-pair skew failure */
+ vlv_dpio_write(dev_priv, DPIO_PCS_STAGGER1(port), 0x00750f00);
+ vlv_dpio_write(dev_priv, DPIO_TX_CTL(port), 0x00001500);
+ vlv_dpio_write(dev_priv, DPIO_TX_LANE(port), 0x40400000);
+
+ vlv_dpio_write(dev_priv, DPIO_PCS_CTL_OVER1(port),
+ 0x00002000);
+ vlv_dpio_write(dev_priv, DPIO_TX_OCALINIT(port),
+ DPIO_TX_OCALINIT_EN);
+}
+
+static void intel_hdmi_post_disable(struct intel_encoder *encoder)
+{
+ struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
+ struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+ int port = vlv_dport_to_channel(dport);
+
+ /* Reset lanes to avoid HDMI flicker (VLV w/a) */
+ mutex_lock(&dev_priv->dpio_lock);
+ vlv_dpio_write(dev_priv, DPIO_PCS_TX(port), 0x00000000);
+ vlv_dpio_write(dev_priv, DPIO_PCS_CLK(port), 0x00e00060);
+ mutex_unlock(&dev_priv->dpio_lock);
+}
+
static void intel_hdmi_destroy(struct drm_connector *connector)
{
drm_sysfs_connector_remove(connector);
@@ -1094,6 +1232,12 @@ void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port)
intel_encoder->enable = intel_enable_hdmi;
intel_encoder->disable = intel_disable_hdmi;
intel_encoder->get_hw_state = intel_hdmi_get_hw_state;
+ intel_encoder->get_config = intel_hdmi_get_config;
+ if (IS_VALLEYVIEW(dev)) {
+ intel_encoder->pre_enable = intel_hdmi_pre_enable;
+ intel_encoder->pre_pll_enable = intel_hdmi_pre_pll_enable;
+ intel_encoder->post_disable = intel_hdmi_post_disable;
+ }
intel_encoder->type = INTEL_OUTPUT_HDMI;
intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);