From a5071c2fd91f320bc40952df59517053a7346fa9 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 19 Jul 2011 15:38:56 -0700 Subject: [PATCH 01/55] drm/i915: provide more error output when mode sets fail If a mode set fails we may get a message from drm_crtc_helper if we're lucky, but it won't tell us anything about *why* we failed to set a mode. So add a few DRM_ERRORs for the cases that shouldn't happen so we can debug things more easily. Signed-off-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_display.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b5b15bda71d9..261ffe47a5d2 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2096,7 +2096,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, /* no fb bound */ if (!crtc->fb) { - DRM_DEBUG_KMS("No FB bound\n"); + DRM_ERROR("No FB bound\n"); return 0; } @@ -2105,6 +2105,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, case 1: break; default: + DRM_ERROR("no plane for crtc\n"); return -EINVAL; } @@ -2114,6 +2115,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, NULL); if (ret != 0) { mutex_unlock(&dev->struct_mutex); + DRM_ERROR("pin & fence failed\n"); return ret; } @@ -2142,6 +2144,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, if (ret) { i915_gem_object_unpin(to_intel_framebuffer(crtc->fb)->obj); mutex_unlock(&dev->struct_mutex); + DRM_ERROR("failed to update base address\n"); return ret; } From f0b69efc29b024747a88ce020dada425e3193d5a Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 19 Jul 2011 16:21:40 -0700 Subject: [PATCH 02/55] drm/i915: Skip GPU wait for scanout pin while wedged Failing to pin a scanout buffer will most likely lead to a black screen, so if the GPU is wedged, then just let the pin happen and hope that things work out OK. v2: Just ignore any error from i915_gem_object_wait_rendering, as suggested by Chris Wilson Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index e9d1d5c3a696..e46f2734acc5 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3113,7 +3113,7 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj, if (pipelined != obj->ring) { ret = i915_gem_object_wait_rendering(obj); - if (ret) + if (ret == -ERESTARTSYS) return ret; } From 842d452985300f4ec14c68cb86046e8a1a3b7251 Mon Sep 17 00:00:00 2001 From: Ole Henrik Jahren Date: Fri, 22 Jul 2011 15:56:01 +0200 Subject: [PATCH 03/55] drm/i915: Fix typo in DRM_I915_OVERLAY_PUT_IMAGE ioctl define Because of a typo, calling ioctl with DRM_IOCTL_I915_OVERLAY_PUT_IMAGE is broken if the macro is used directly. When using libdrm the bug is not hit, since libdrm handles the ioctl encoding internally. The typo also leads to the .cmd and .cmd_drv fields of the drm_ioctl structure for DRM_I915_OVERLAY_PUT_IMAGE having inconsistent content. Signed-off-by: Ole Henrik Jahren Acked-by: Daniel Vetter Cc: stable@kernel.org Signed-off-by: Keith Packard --- include/drm/i915_drm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index c4d6dbfa3ff4..28c0d114cb52 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -237,7 +237,7 @@ typedef struct _drm_i915_sarea { #define DRM_IOCTL_I915_GEM_GET_APERTURE DRM_IOR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_APERTURE, struct drm_i915_gem_get_aperture) #define DRM_IOCTL_I915_GET_PIPE_FROM_CRTC_ID DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GET_PIPE_FROM_CRTC_ID, struct drm_i915_get_pipe_from_crtc_id) #define DRM_IOCTL_I915_GEM_MADVISE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MADVISE, struct drm_i915_gem_madvise) -#define DRM_IOCTL_I915_OVERLAY_PUT_IMAGE DRM_IOW(DRM_COMMAND_BASE + DRM_IOCTL_I915_OVERLAY_ATTRS, struct drm_intel_overlay_put_image) +#define DRM_IOCTL_I915_OVERLAY_PUT_IMAGE DRM_IOW(DRM_COMMAND_BASE + DRM_I915_OVERLAY_PUT_IMAGE, struct drm_intel_overlay_put_image) #define DRM_IOCTL_I915_OVERLAY_ATTRS DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_OVERLAY_ATTRS, struct drm_intel_overlay_attrs) /* Allow drivers to submit batchbuffers directly to hardware, relying From f3234706a77bd6e1592ae71fb3268e04cb030dba Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 22 Jul 2011 10:44:39 -0700 Subject: [PATCH 04/55] drm/i915: Initialize RCS ring status page address in intel_render_ring_init_dri Physically-addressed hardware status pages are initialized early in the driver load process by i915_init_phys_hws. For UMS environments, the ring structure is not initialized until the X server starts. At that point, the entire ring structure is re-initialized with all new values. Any values set in the ring structure (including ring->status_page.page_addr) will be lost when the ring is re-initialized. This patch moves the initialization of the status_page.page_addr value to intel_render_ring_init_dri. Signed-off-by: Keith Packard Cc: stable@kernel.org --- drivers/gpu/drm/i915/i915_dma.c | 6 ++---- drivers/gpu/drm/i915/intel_ringbuffer.c | 3 +++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 296fbd66f0e1..7eef6e11d9ac 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -61,7 +61,6 @@ static void i915_write_hws_pga(struct drm_device *dev) static int i915_init_phys_hws(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; - struct intel_ring_buffer *ring = LP_RING(dev_priv); /* Program Hardware Status Page */ dev_priv->status_page_dmah = @@ -71,10 +70,9 @@ static int i915_init_phys_hws(struct drm_device *dev) DRM_ERROR("Can not allocate hardware status page\n"); return -ENOMEM; } - ring->status_page.page_addr = - (void __force __iomem *)dev_priv->status_page_dmah->vaddr; - memset_io(ring->status_page.page_addr, 0, PAGE_SIZE); + memset_io((void __force __iomem *)dev_priv->status_page_dmah->vaddr, + 0, PAGE_SIZE); i915_write_hws_pga(dev); diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 95c4b1429935..1f61fc7b754c 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1319,6 +1319,9 @@ int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size) ring->get_seqno = pc_render_get_seqno; } + if (!I915_NEED_GFX_HWS(dev)) + ring->status_page.page_addr = dev_priv->status_page_dmah->vaddr; + ring->dev = dev; INIT_LIST_HEAD(&ring->active_list); INIT_LIST_HEAD(&ring->request_list); From 9c54c0dd948d715ccfd79e97d852f80eeb53254a Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 15 Jun 2011 23:32:33 +0200 Subject: [PATCH 05/55] drm/i915: load the LUT before pipe enable on ILK+ Per the specs and to address https://bugs.freedesktop.org/show_bug.cgi?id=36888. Signed-off-by: Jesse Barnes Cc: stable@kernel.org Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_display.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 0f1c799afea1..5609c065aaf4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2699,14 +2699,18 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) I915_WRITE(PF_WIN_SZ(pipe), dev_priv->pch_pf_size); } + /* + * On ILK+ LUT must be loaded before the pipe is running but with + * clocks enabled + */ + intel_crtc_load_lut(crtc); + intel_enable_pipe(dev_priv, pipe, is_pch_port); intel_enable_plane(dev_priv, plane, pipe); if (is_pch_port) ironlake_pch_enable(crtc); - intel_crtc_load_lut(crtc); - mutex_lock(&dev->struct_mutex); intel_update_fbc(dev); mutex_unlock(&dev->struct_mutex); From 97cdd7101079adc3c626d159c62d43de949516c8 Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Tue, 12 Jul 2011 17:38:00 -0400 Subject: [PATCH 06/55] drm/i915/dp: Zero the DPCD data before connection probe Signed-off-by: Adam Jackson Reviewed-by: Keith Packard Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_dp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index e2aced6eec4c..de24f314eda9 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1673,6 +1673,7 @@ intel_dp_detect(struct drm_connector *connector, bool force) struct edid *edid = NULL; intel_dp->has_audio = false; + memset(intel_dp->dpcd, 0, sizeof(intel_dp->dpcd)); if (HAS_PCH_SPLIT(dev)) status = ironlake_dp_detect(intel_dp); From 1b9be9d09d85b3697418dc444db30d069203ff7d Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Tue, 12 Jul 2011 17:38:01 -0400 Subject: [PATCH 07/55] drm/i915/dp: Move DPCD dump to common code instead of PCH-only No reason not to see this on g4x, after all. Signed-off-by: Adam Jackson Reviewed-by: Keith Packard Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_dp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index de24f314eda9..0be85a00bb52 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1615,8 +1615,6 @@ ironlake_dp_detect(struct intel_dp *intel_dp) sizeof (intel_dp->dpcd)); if (ret && intel_dp->dpcd[DP_DPCD_REV] != 0) status = connector_status_connected; - DRM_DEBUG_KMS("DPCD: %hx%hx%hx%hx\n", intel_dp->dpcd[0], - intel_dp->dpcd[1], intel_dp->dpcd[2], intel_dp->dpcd[3]); return status; } @@ -1679,6 +1677,10 @@ intel_dp_detect(struct drm_connector *connector, bool force) status = ironlake_dp_detect(intel_dp); else status = g4x_dp_detect(intel_dp); + + DRM_DEBUG_KMS("DPCD: %hx%hx%hx%hx\n", intel_dp->dpcd[0], + intel_dp->dpcd[1], intel_dp->dpcd[2], intel_dp->dpcd[3]); + if (status != connector_status_connected) return status; From 9de88e6e89a2222061af8e1448f6f346e3413fc8 Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Tue, 12 Jul 2011 17:38:02 -0400 Subject: [PATCH 08/55] drm/i915/dp: Read more DPCD registers on connection probe For parity with radeon and nouveau, and also because I suspect we're going to need it to get format-conversion dongles right. Signed-off-by: Adam Jackson Reviewed-by: Keith Packard Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_dp.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 0be85a00bb52..2f0566b12f71 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -52,7 +52,7 @@ struct intel_dp { uint32_t color_range; uint8_t link_bw; uint8_t lane_count; - uint8_t dpcd[4]; + uint8_t dpcd[8]; struct i2c_adapter adapter; struct i2c_algo_dp_aux_data algo; bool is_pch_edp; @@ -1678,8 +1678,10 @@ intel_dp_detect(struct drm_connector *connector, bool force) else status = g4x_dp_detect(intel_dp); - DRM_DEBUG_KMS("DPCD: %hx%hx%hx%hx\n", intel_dp->dpcd[0], - intel_dp->dpcd[1], intel_dp->dpcd[2], intel_dp->dpcd[3]); + DRM_DEBUG_KMS("DPCD: %hx%hx%hx%hx%hx%hx%hx%hx\n", intel_dp->dpcd[0], + intel_dp->dpcd[1], intel_dp->dpcd[2], intel_dp->dpcd[3], + intel_dp->dpcd[4], intel_dp->dpcd[5], intel_dp->dpcd[6], + intel_dp->dpcd[7]); if (status != connector_status_connected) return status; From ac66ae8346fff704301e24ac55da1d76020660b2 Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Tue, 12 Jul 2011 17:38:03 -0400 Subject: [PATCH 09/55] drm/i915/dp: Better hexdump of DPCD %hx alone prints 0 as "0", not "00". Signed-off-by: Adam Jackson Reviewed-by: Keith Packard Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_dp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 2f0566b12f71..13bddd34c057 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1678,10 +1678,10 @@ intel_dp_detect(struct drm_connector *connector, bool force) else status = g4x_dp_detect(intel_dp); - DRM_DEBUG_KMS("DPCD: %hx%hx%hx%hx%hx%hx%hx%hx\n", intel_dp->dpcd[0], - intel_dp->dpcd[1], intel_dp->dpcd[2], intel_dp->dpcd[3], - intel_dp->dpcd[4], intel_dp->dpcd[5], intel_dp->dpcd[6], - intel_dp->dpcd[7]); + DRM_DEBUG_KMS("DPCD: %02hx%02hx%02hx%02hx%02hx%02hx%02hx%02hx\n", + intel_dp->dpcd[0], intel_dp->dpcd[1], intel_dp->dpcd[2], + intel_dp->dpcd[3], intel_dp->dpcd[4], intel_dp->dpcd[5], + intel_dp->dpcd[6], intel_dp->dpcd[7]); if (status != connector_status_connected) return status; From 71ba9000e673d6171a52f2a8b14e0419087f7199 Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Tue, 12 Jul 2011 17:38:04 -0400 Subject: [PATCH 10/55] drm/i915/dp: Retry DPCD fetch on G4X too Signed-off-by: Adam Jackson Reviewed-by: Keith Packard Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_dp.c | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 13bddd34c057..9a0c3ca1ffa9 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1595,11 +1595,23 @@ intel_dp_check_link_status(struct intel_dp *intel_dp) } } +static enum drm_connector_status +i915_dp_detect_common(struct intel_dp *intel_dp) +{ + enum drm_connector_status status = connector_status_disconnected; + + if (intel_dp_aux_native_read_retry(intel_dp, 0x000, intel_dp->dpcd, + sizeof (intel_dp->dpcd)) && + (intel_dp->dpcd[DP_DPCD_REV] != 0)) + status = connector_status_connected; + + return status; +} + static enum drm_connector_status ironlake_dp_detect(struct intel_dp *intel_dp) { enum drm_connector_status status; - bool ret; /* Can't disconnect eDP, but you can close the lid... */ if (is_edp(intel_dp)) { @@ -1609,13 +1621,7 @@ ironlake_dp_detect(struct intel_dp *intel_dp) return status; } - status = connector_status_disconnected; - ret = intel_dp_aux_native_read_retry(intel_dp, - 0x000, intel_dp->dpcd, - sizeof (intel_dp->dpcd)); - if (ret && intel_dp->dpcd[DP_DPCD_REV] != 0) - status = connector_status_connected; - return status; + return i915_dp_detect_common(intel_dp); } static enum drm_connector_status @@ -1623,7 +1629,6 @@ g4x_dp_detect(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - enum drm_connector_status status; uint32_t temp, bit; switch (intel_dp->output_reg) { @@ -1645,15 +1650,7 @@ g4x_dp_detect(struct intel_dp *intel_dp) if ((temp & bit) == 0) return connector_status_disconnected; - status = connector_status_disconnected; - if (intel_dp_aux_native_read(intel_dp, 0x000, intel_dp->dpcd, - sizeof (intel_dp->dpcd)) == sizeof (intel_dp->dpcd)) - { - if (intel_dp->dpcd[DP_DPCD_REV] != 0) - status = connector_status_connected; - } - - return status; + return i915_dp_detect_common(intel_dp); } /** From a2cab1b24a4ea75a68fa21bfb7d5b1a45121583c Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Tue, 12 Jul 2011 17:38:05 -0400 Subject: [PATCH 11/55] drm/i915/dp: Explicitly request 8/10 channel coding It's not clear what a sink would do if you wrote zero to this register - which I guess would mean "I don't support any channel encodings, good luck" - but let's not find out. Signed-off-by: Adam Jackson Reviewed-by: Keith Packard Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_dp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 9a0c3ca1ffa9..1c3a36feaf69 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -769,6 +769,7 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, memset(intel_dp->link_configuration, 0, DP_LINK_CONFIGURATION_SIZE); intel_dp->link_configuration[0] = intel_dp->link_bw; intel_dp->link_configuration[1] = intel_dp->lane_count; + intel_dp->link_configuration[8] = DP_SET_ANSI_8B10B; /* * Check for DPCD version > 1.1 and enhanced framing support From a65e34c79c88895766ab1f8a5afa451eed26622b Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 25 Jul 2011 10:04:56 -0700 Subject: [PATCH 12/55] drm/i915: Hold mode_config->mutex during hotplug processing Hotplug detection is a mode setting operation and must hold the struct_mutex or risk colliding with other mode setting operations. In particular, the display port hotplug function attempts to re-train the link if the monitor is supposed to be running when plugged back in. If that happens while mode setting is underway, the link will get scrambled, leaving it in an inconsistent state. This is a special case -- usually the driver mode setting entry points are covered by the upper level DRM code, but in this case the function is invoked as a work function not under the control of DRM. Signed-off-by: Keith Packard Cc: stable@kernel.org Reviewed-by: Jesse Barnes --- drivers/gpu/drm/i915/i915_irq.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 3b03f85ea627..9da2a2c99953 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -306,6 +306,7 @@ static void i915_hotplug_work_func(struct work_struct *work) struct drm_mode_config *mode_config = &dev->mode_config; struct intel_encoder *encoder; + mutex_lock(&mode_config->mutex); DRM_DEBUG_KMS("running encoder hotplug functions\n"); list_for_each_entry(encoder, &mode_config->encoder_list, base.head) @@ -314,6 +315,8 @@ static void i915_hotplug_work_func(struct work_struct *work) /* Just fire off a uevent and let userspace tell us what to do */ drm_helper_hpd_irq_event(dev); + + mutex_unlock(&mode_config->mutex); } static void i915_handle_rps_change(struct drm_device *dev) From 302983e9059e9ef5de3ca7671918eeb237c5971e Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Wed, 13 Jul 2011 16:32:32 -0400 Subject: [PATCH 13/55] drm/i915/pch: Fix integer math bugs in panel fitting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consider a 1600x900 panel, upscaling a 1360x768 mode, full-aspect. The old math would give you: scaled_width = 1600 * 768; /* 1228800 */ scaled_height = 1360 * 900; /* 1224000 */ if (scaled_width > scaled_height) { /* pillarbox, and true */ width = 1224000 / 768; /* int(1593.75) = 1593 */ x = (1600 - 1593 + 1) / 2; /* 4 */ y = 0; height = 768; } /* ... */ This is broken. The total width of scanout would then be 1593 + 4 + 4, or 1601, which is wider than the panel itself. The hardware very dutifully implements this, and you end up with a black 45° diagonal from the top-left corner to the bottom edge of the screen. It's a cool effect and all, but not what you wanted. Similar things happen for the letterbox case. The problem is that you have an integer number of pixels, which means it's usually impossible to upscale equally on both axes. 1360/768 is 1.7708, 1600/900 is 1.7777. Since we're constrained on the one axis, the other one wants to come out as an even number of pixels (the panel is almost certainly even on both axes, and the x/y offsets will be applied on both sides). In the math above, if 'width' comes out even, rounding down is correct; if it's odd, you'd rather round up. So just increment width/height in those cases. Tested on a Lenovo T500 (Ironlake). Signed-off-by: Adam Jackson Tested-By: Daniel Manrique Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=38851 Reviewed-by: Chris Wilson Cc: stable@kernel.org Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_panel.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index a06ff07a4d3b..05f500cd9c24 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -83,11 +83,15 @@ intel_pch_panel_fitting(struct drm_device *dev, u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay; if (scaled_width > scaled_height) { /* pillar */ width = scaled_height / mode->vdisplay; + if (width & 1) + width++; x = (adjusted_mode->hdisplay - width + 1) / 2; y = 0; height = adjusted_mode->vdisplay; } else if (scaled_width < scaled_height) { /* letter */ height = scaled_width / mode->hdisplay; + if (height & 1) + height++; y = (adjusted_mode->vdisplay - height + 1) / 2; x = 0; width = adjusted_mode->hdisplay; From 81055854d096959898fdc17ed11729eb019eff07 Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Thu, 21 Jul 2011 17:48:37 -0400 Subject: [PATCH 14/55] drm/i915/dp: Explicitly disable symbol scrambling while training The DP spec says training patterns 1 and 2 are to be sent non-scrambled, and the GPU docs claim that happens (or at least, there's no explicit scrambling control). But the sink may be confused if we don't explicitly tell it what we're doing, so play it safe. Signed-off-by: Adam Jackson Reviewed-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_dp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 1c3a36feaf69..8aecb072466b 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1370,7 +1370,8 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) reg = DP | DP_LINK_TRAIN_PAT_1; if (!intel_dp_set_link_train(intel_dp, reg, - DP_TRAINING_PATTERN_1)) + DP_TRAINING_PATTERN_1 | + DP_LINK_SCRAMBLING_DISABLE)) break; /* Set training pattern 1 */ @@ -1445,7 +1446,8 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) /* channel eq pattern */ if (!intel_dp_set_link_train(intel_dp, reg, - DP_TRAINING_PATTERN_2)) + DP_TRAINING_PATTERN_2 | + DP_LINK_SCRAMBLING_DISABLE)) break; udelay(400); From e85194641bec56179dcf5e1704ce5c6bf30340c6 Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Thu, 21 Jul 2011 17:48:38 -0400 Subject: [PATCH 15/55] drm/i915/dp: Don't turn CPT DP ports on too early The docs say the port has to come on in training pattern 1; at this point, though, ->DP is in normal mode. The intent here is to wait until the port is in fact sending data, but that doesn't happen since we've broken the sequence the hardware expects, and the vblank wait will time out and kvetch in the log. Signed-off-by: Adam Jackson Reviewed-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_dp.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 8aecb072466b..dcc7ae6d4141 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1334,10 +1334,16 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) u32 reg; uint32_t DP = intel_dp->DP; - /* Enable output, wait for it to become active */ - I915_WRITE(intel_dp->output_reg, intel_dp->DP); - POSTING_READ(intel_dp->output_reg); - intel_wait_for_vblank(dev, intel_crtc->pipe); + /* + * On CPT we have to enable the port in training pattern 1, which + * will happen below in intel_dp_set_link_train. Otherwise, enable + * the port and wait for it to become active. + */ + if (!HAS_PCH_CPT(dev)) { + I915_WRITE(intel_dp->output_reg, intel_dp->DP); + POSTING_READ(intel_dp->output_reg); + intel_wait_for_vblank(dev, intel_crtc->pipe); + } /* Write the link configuration data */ intel_dp_aux_native_write(intel_dp, DP_LINK_BW_SET, From 40ee3381dd1010432acc13e907329029096c5bfc Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 28 Jul 2011 15:31:19 -0700 Subject: [PATCH 16/55] drm/i915: Fixup for 'Hold mode_config->mutex during hotplug' drm_helper_hpd_irq_event queues another work proc to go and deliver the user-space event, and that function also wants to hold the config mutex, so we shouldn't hold the mutex across the drm_helper_hpd_irq_event call. Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_irq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 9da2a2c99953..9b1d669f7d4b 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -313,10 +313,10 @@ static void i915_hotplug_work_func(struct work_struct *work) if (encoder->hot_plug) encoder->hot_plug(encoder); + mutex_unlock(&mode_config->mutex); + /* Just fire off a uevent and let userspace tell us what to do */ drm_helper_hpd_irq_event(dev); - - mutex_unlock(&mode_config->mutex); } static void i915_handle_rps_change(struct drm_device *dev) From 92fd8fd13b7570f6a8ba519c4e8ec98f10a86ce9 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 25 Jul 2011 19:50:10 -0700 Subject: [PATCH 17/55] drm/i915: Use dp_detect_common in hotplug helper function This uses the common dpcd reading routine, i915_dp_detect_common, instead of open-coding a call to intel_dp_aux_native_read. Besides reducing duplicated code, this also gains the read retries which may be necessary when a cable is first plugged back in and the link needs to be retrained. Signed-off-by: Keith Packard Reviewed-by: Jesse Barnes Reviewed-by: Adam Jackson --- drivers/gpu/drm/i915/intel_dp.c | 39 ++++++++++++++++----------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index dcc7ae6d4141..45db8106ab12 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1567,6 +1567,20 @@ intel_dp_link_down(struct intel_dp *intel_dp) POSTING_READ(intel_dp->output_reg); } +static enum drm_connector_status +i915_dp_detect_common(struct intel_dp *intel_dp) +{ + enum drm_connector_status status = connector_status_disconnected; + + if (intel_dp_aux_native_read_retry(intel_dp, 0x000, intel_dp->dpcd, + sizeof (intel_dp->dpcd)) && + (intel_dp->dpcd[DP_DPCD_REV] != 0)) { + status = connector_status_connected; + } + + return status; +} + /* * According to DP spec * 5.1.2: @@ -1579,44 +1593,29 @@ intel_dp_link_down(struct intel_dp *intel_dp) static void intel_dp_check_link_status(struct intel_dp *intel_dp) { - int ret; - if (!intel_dp->base.base.crtc) return; + /* Try to read receiver status if the link appears to be up */ if (!intel_dp_get_link_status(intel_dp)) { intel_dp_link_down(intel_dp); return; } - /* Try to read receiver status if the link appears to be up */ - ret = intel_dp_aux_native_read(intel_dp, - 0x000, intel_dp->dpcd, - sizeof (intel_dp->dpcd)); - if (ret != sizeof(intel_dp->dpcd)) { + /* Now read the DPCD to see if it's actually running */ + if (i915_dp_detect_common(intel_dp) != connector_status_connected) { intel_dp_link_down(intel_dp); return; } if (!intel_channel_eq_ok(intel_dp)) { + DRM_DEBUG_KMS("%s: channel EQ not ok, retraining\n", + drm_get_encoder_name(&intel_dp->base.base)); intel_dp_start_link_train(intel_dp); intel_dp_complete_link_train(intel_dp); } } -static enum drm_connector_status -i915_dp_detect_common(struct intel_dp *intel_dp) -{ - enum drm_connector_status status = connector_status_disconnected; - - if (intel_dp_aux_native_read_retry(intel_dp, 0x000, intel_dp->dpcd, - sizeof (intel_dp->dpcd)) && - (intel_dp->dpcd[DP_DPCD_REV] != 0)) - status = connector_status_connected; - - return status; -} - static enum drm_connector_status ironlake_dp_detect(struct intel_dp *intel_dp) { From 26d61aad7a46115628341e9eb95433f30efef21a Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 25 Jul 2011 20:01:09 -0700 Subject: [PATCH 18/55] drm/i915: Rename i915_dp_detect_common to intel_dp_get_dpcd This describes the function better, allowing it to be used where the DPCD value is relevant. Signed-off-by: Keith Packard Reviewed-by: Jesse Barnes Reviewed-by: Adam Jackson --- drivers/gpu/drm/i915/intel_dp.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 45db8106ab12..41674e1a26c7 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1567,18 +1567,16 @@ intel_dp_link_down(struct intel_dp *intel_dp) POSTING_READ(intel_dp->output_reg); } -static enum drm_connector_status -i915_dp_detect_common(struct intel_dp *intel_dp) +static bool +intel_dp_get_dpcd(struct intel_dp *intel_dp) { - enum drm_connector_status status = connector_status_disconnected; - if (intel_dp_aux_native_read_retry(intel_dp, 0x000, intel_dp->dpcd, sizeof (intel_dp->dpcd)) && (intel_dp->dpcd[DP_DPCD_REV] != 0)) { - status = connector_status_connected; + return true; } - return status; + return false; } /* @@ -1603,7 +1601,7 @@ intel_dp_check_link_status(struct intel_dp *intel_dp) } /* Now read the DPCD to see if it's actually running */ - if (i915_dp_detect_common(intel_dp) != connector_status_connected) { + if (!intel_dp_get_dpcd(intel_dp)) { intel_dp_link_down(intel_dp); return; } @@ -1616,6 +1614,14 @@ intel_dp_check_link_status(struct intel_dp *intel_dp) } } +static enum drm_connector_status +intel_dp_detect_dpcd(struct intel_dp *intel_dp) +{ + if (intel_dp_get_dpcd(intel_dp)) + return connector_status_connected; + return connector_status_disconnected; +} + static enum drm_connector_status ironlake_dp_detect(struct intel_dp *intel_dp) { @@ -1629,7 +1635,7 @@ ironlake_dp_detect(struct intel_dp *intel_dp) return status; } - return i915_dp_detect_common(intel_dp); + return intel_dp_detect_dpcd(intel_dp); } static enum drm_connector_status @@ -1658,7 +1664,7 @@ g4x_dp_detect(struct intel_dp *intel_dp) if ((temp & bit) == 0) return connector_status_disconnected; - return i915_dp_detect_common(intel_dp); + return intel_dp_detect_dpcd(intel_dp); } /** From 59f3e272d788305e16098f0b18309919c9216d67 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 25 Jul 2011 20:01:56 -0700 Subject: [PATCH 19/55] drm/i915: In intel_dp_init, replace read of DPCD with intel_dp_get_dpcd Eliminates an open-coded read and also gains the retry behaviour of intel_dp_get_dpcd, which seems like a good idea. Signed-off-by: Keith Packard Reviewed-by: Jesse Barnes Reviewed-by: Adam Jackson --- drivers/gpu/drm/i915/intel_dp.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 41674e1a26c7..9f134d2b7bec 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2015,7 +2015,7 @@ intel_dp_init(struct drm_device *dev, int output_reg) /* Cache some DPCD data in the eDP case */ if (is_edp(intel_dp)) { - int ret; + bool ret; u32 pp_on, pp_div; pp_on = I915_READ(PCH_PP_ON_DELAYS); @@ -2028,11 +2028,9 @@ intel_dp_init(struct drm_device *dev, int output_reg) dev_priv->panel_t12 *= 100; /* t12 in 100ms units */ ironlake_edp_panel_vdd_on(intel_dp); - ret = intel_dp_aux_native_read(intel_dp, DP_DPCD_REV, - intel_dp->dpcd, - sizeof(intel_dp->dpcd)); + ret = intel_dp_get_dpcd(intel_dp); ironlake_edp_panel_vdd_off(intel_dp); - if (ret == sizeof(intel_dp->dpcd)) { + if (ret) { if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11) dev_priv->no_aux_handshake = intel_dp->dpcd[DP_MAX_DOWNSPREAD] & From f0575e92974d328e8816ed89704c985a7d7d90ac Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 25 Jul 2011 22:12:43 -0700 Subject: [PATCH 20/55] drm/i915: DP_PIPE_ENABLED must check transcoder on CPT Display port pipe selection on CPT is not done with a bit in the output register, rather it is controlled by a couple of bits in the separate transcoder register which indicate which display port output is connected to the transcoder. This patch replaces the simplistic macro DP_PIPE_ENABLED with the rather more complicated function dp_pipe_enabled which checks the output register to see if that is enabled, and then goes on to either check the output register pipe selection bit (on non-CPT) or the transcoder DP selection bits (on CPT). Before this patch, any time the mode of pipe A was changed, any display port outputs on pipe B would get disabled as intel_disable_pch_ports would ensure that the mode setting operation could occur on pipe A without interference from other outputs connected to that pch port Signed-off-by: Keith Packard Reviewed-by: Jesse Barnes Reviewed-by: Adam Jackson --- drivers/gpu/drm/i915/i915_reg.h | 3 -- drivers/gpu/drm/i915/intel_display.c | 45 +++++++++++++++++++++------- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 5d5def756c9e..f7315653a842 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2083,9 +2083,6 @@ #define DP_PIPEB_SELECT (1 << 30) #define DP_PIPE_MASK (1 << 30) -#define DP_PIPE_ENABLED(V, P) \ - (((V) & (DP_PIPE_MASK | DP_PORT_EN)) == ((P) << 30 | DP_PORT_EN)) - /* Link training mode - select a suitable mode for each stage */ #define DP_LINK_TRAIN_PAT_1 (0 << 28) #define DP_LINK_TRAIN_PAT_2 (1 << 28) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5609c065aaf4..fc26cb4c9ba3 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -979,11 +979,29 @@ static void assert_transcoder_disabled(struct drm_i915_private *dev_priv, pipe_name(pipe)); } +static bool dp_pipe_enabled(struct drm_i915_private *dev_priv, enum pipe pipe, + int reg, u32 port_sel, u32 val) +{ + if ((val & DP_PORT_EN) == 0) + return false; + + if (HAS_PCH_CPT(dev_priv->dev)) { + u32 trans_dp_ctl_reg = TRANS_DP_CTL(pipe); + u32 trans_dp_ctl = I915_READ(trans_dp_ctl_reg); + if ((trans_dp_ctl & TRANS_DP_PORT_SEL_MASK) != port_sel) + return false; + } else { + if ((val & DP_PIPE_MASK) != (pipe << 30)) + return false; + } + return true; +} + static void assert_pch_dp_disabled(struct drm_i915_private *dev_priv, - enum pipe pipe, int reg) + enum pipe pipe, int reg, u32 port_sel) { u32 val = I915_READ(reg); - WARN(DP_PIPE_ENABLED(val, pipe), + WARN(dp_pipe_enabled(dev_priv, pipe, reg, port_sel, val), "PCH DP (0x%08x) enabled on transcoder %c, should be disabled\n", reg, pipe_name(pipe)); } @@ -1003,9 +1021,9 @@ static void assert_pch_ports_disabled(struct drm_i915_private *dev_priv, int reg; u32 val; - assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_B); - assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_C); - assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_D); + assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_B, TRANS_DP_PORT_SEL_B); + assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_C, TRANS_DP_PORT_SEL_C); + assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_D, TRANS_DP_PORT_SEL_D); reg = PCH_ADPA; val = I915_READ(reg); @@ -1334,19 +1352,24 @@ static void intel_disable_plane(struct drm_i915_private *dev_priv, } static void disable_pch_dp(struct drm_i915_private *dev_priv, - enum pipe pipe, int reg) + enum pipe pipe, int reg, u32 port_sel) { u32 val = I915_READ(reg); - if (DP_PIPE_ENABLED(val, pipe)) + if (dp_pipe_enabled(dev_priv, pipe, reg, port_sel, val)) { + DRM_DEBUG_KMS("Disabling pch dp %x on pipe %d\n", reg, pipe); I915_WRITE(reg, val & ~DP_PORT_EN); + } } static void disable_pch_hdmi(struct drm_i915_private *dev_priv, enum pipe pipe, int reg) { u32 val = I915_READ(reg); - if (HDMI_PIPE_ENABLED(val, pipe)) + if (HDMI_PIPE_ENABLED(val, pipe)) { + DRM_DEBUG_KMS("Disabling pch HDMI %x on pipe %d\n", + reg, pipe); I915_WRITE(reg, val & ~PORT_ENABLE); + } } /* Disable any ports connected to this transcoder */ @@ -1358,9 +1381,9 @@ static void intel_disable_pch_ports(struct drm_i915_private *dev_priv, val = I915_READ(PCH_PP_CONTROL); I915_WRITE(PCH_PP_CONTROL, val | PANEL_UNLOCK_REGS); - disable_pch_dp(dev_priv, pipe, PCH_DP_B); - disable_pch_dp(dev_priv, pipe, PCH_DP_C); - disable_pch_dp(dev_priv, pipe, PCH_DP_D); + disable_pch_dp(dev_priv, pipe, PCH_DP_B, TRANS_DP_PORT_SEL_B); + disable_pch_dp(dev_priv, pipe, PCH_DP_C, TRANS_DP_PORT_SEL_C); + disable_pch_dp(dev_priv, pipe, PCH_DP_D, TRANS_DP_PORT_SEL_D); reg = PCH_ADPA; val = I915_READ(reg); From d2b996ac698aebb28557355857927b8b934bb4f9 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 25 Jul 2011 22:37:51 -0700 Subject: [PATCH 21/55] Revert and fix "drm/i915/dp: remove DPMS mode tracking from DP" This reverts commit 885a50147f00a8a80108904bf58a18af357717f3. We actually *do* need to track DPMS state so that on hotplug, we don't retrain the link until DPMS is disabled. However, that code had avery small bug -- it wouldn't set the dpms_mode at mode set time, and so link retraining would not actually occur on monitor hotplug until the monitor had gone through a DPMS off/DPMS on cycle. Signed-off-by: Keith Packard Tested-by: Andrew Lutomirski --- drivers/gpu/drm/i915/intel_dp.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 9f134d2b7bec..449364187bc0 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -50,6 +50,7 @@ struct intel_dp { bool has_audio; int force_audio; uint32_t color_range; + int dpms_mode; uint8_t link_bw; uint8_t lane_count; uint8_t dpcd[8]; @@ -1011,6 +1012,8 @@ static void intel_dp_commit(struct drm_encoder *encoder) if (is_edp(intel_dp)) ironlake_edp_backlight_on(dev); + + intel_dp->dpms_mode = DRM_MODE_DPMS_ON; } static void @@ -1045,6 +1048,7 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode) if (is_edp(intel_dp)) ironlake_edp_backlight_on(dev); } + intel_dp->dpms_mode = mode; } /* @@ -1591,6 +1595,9 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) static void intel_dp_check_link_status(struct intel_dp *intel_dp) { + if (intel_dp->dpms_mode != DRM_MODE_DPMS_ON) + return; + if (!intel_dp->base.base.crtc) return; @@ -1939,6 +1946,7 @@ intel_dp_init(struct drm_device *dev, int output_reg) return; intel_dp->output_reg = output_reg; + intel_dp->dpms_mode = -1; intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL); if (!intel_connector) { From 120eced9efe7fdb5123db4ea47e9adee9b66284e Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 27 Jul 2011 01:21:40 -0700 Subject: [PATCH 22/55] drm/i915: Set crtc DPMS mode to ON in intel_crtc_mode_set This corrects the DPMS mode tracking so that the DPMS code will actually turn the CRTC off the next time the screen saves. Signed-off-by: Keith Packard Reviewed-by: Jesse Barnes --- drivers/gpu/drm/i915/intel_display.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index fc26cb4c9ba3..d9b8c15998d9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5273,6 +5273,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, drm_vblank_post_modeset(dev, pipe); + intel_crtc->dpms_mode = DRM_MODE_DPMS_ON; + return ret; } From 3bcf603f6d5d18bd9d076dc280de71f48add4101 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 27 Jul 2011 11:51:40 -0700 Subject: [PATCH 23/55] drm/i915: apply timing generator bug workaround on CPT and PPT On CougarPoint and PantherPoint PCH chips, the timing generator may fail to start after DP training completes. This is due to a bug in the FDI autotraining detect logic (which will stall the timing generator and re-enable it once training completes), so disable it to avoid silent DP mode setting failures. Signed-off-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_reg.h | 5 +++++ drivers/gpu/drm/i915/intel_display.c | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index f7315653a842..00bd51070314 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3072,6 +3072,11 @@ #define TRANS_6BPC (2<<5) #define TRANS_12BPC (3<<5) +#define _TRANSA_CHICKEN2 0xf0064 +#define _TRANSB_CHICKEN2 0xf1064 +#define TRANS_CHICKEN2(pipe) _PIPE(pipe, _TRANSA_CHICKEN2, _TRANSB_CHICKEN2) +#define TRANS_AUTOTRAIN_GEN_STALL_DIS (1<<31) + #define SOUTH_CHICKEN2 0xc2004 #define DPLS_EDP_PPS_FIX_DIS (1<<0) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d9b8c15998d9..502efc37b074 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7524,6 +7524,7 @@ static void ibx_init_clock_gating(struct drm_device *dev) static void cpt_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + int pipe; /* * On Ibex Peak and Cougar Point, we need to disable clock @@ -7533,6 +7534,9 @@ static void cpt_init_clock_gating(struct drm_device *dev) I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE); I915_WRITE(SOUTH_CHICKEN2, I915_READ(SOUTH_CHICKEN2) | DPLS_EDP_PPS_FIX_DIS); + /* Without this, mode sets may fail silently on FDI */ + for_each_pipe(pipe) + I915_WRITE(TRANS_CHICKEN2(pipe), TRANS_AUTOTRAIN_GEN_STALL_DIS); } static void ironlake_teardown_rc6(struct drm_device *dev) From 2704cf5fbd248871a745d210733c6319959d2b0c Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 28 Jul 2011 11:52:45 -0700 Subject: [PATCH 24/55] drm/i915: flush plane control changes on ILK+ as well After writing to the plane control reg we need to write to the surface reg to trigger the double buffered register latch. On previous chipsets, writing to DSPADDR was enough, but on ILK+ DSPSURF is the reg that triggers the double buffer latch. v2: write DSPADDR too to cover pre-965 chipsets v3: use flush_display_plane instead, that's what it's for v4: send the right patch Signed-off-by: Jesse Barnes Tested-by: Keith Packard Reviewed-by: Keith Packard Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_display.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 502efc37b074..a7a7b672c4c2 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1323,8 +1323,8 @@ static void intel_enable_plane(struct drm_i915_private *dev_priv, static void intel_flush_display_plane(struct drm_i915_private *dev_priv, enum plane plane) { - u32 reg = DSPADDR(plane); - I915_WRITE(reg, I915_READ(reg)); + I915_WRITE(DSPADDR(plane), I915_READ(DSPADDR(plane))); + I915_WRITE(DSPSURF(plane), I915_READ(DSPSURF(plane))); } /** From d74362c9e45689d8d7e3d4bcf6681c4358ef4f2e Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 28 Jul 2011 14:47:14 -0700 Subject: [PATCH 25/55] drm/i915: Flush other plane register writes Writes to the plane control register are buffered in the chip until a write to the DSPADDR (pre-965) or DSPSURF (post-965) register occurs. This patch adds flushes in: intel_enable_plane gen6_init_clock_gating ivybridge_init_clock_gating Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_display.c | 31 ++++++++++++++++------------ 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a7a7b672c4c2..8f7ed7304269 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1290,6 +1290,17 @@ static void intel_disable_pipe(struct drm_i915_private *dev_priv, intel_wait_for_pipe_off(dev_priv->dev, pipe); } +/* + * Plane regs are double buffered, going from enabled->disabled needs a + * trigger in order to latch. The display address reg provides this. + */ +static void intel_flush_display_plane(struct drm_i915_private *dev_priv, + enum plane plane) +{ + I915_WRITE(DSPADDR(plane), I915_READ(DSPADDR(plane))); + I915_WRITE(DSPSURF(plane), I915_READ(DSPSURF(plane))); +} + /** * intel_enable_plane - enable a display plane on a given pipe * @dev_priv: i915 private structure @@ -1313,20 +1324,10 @@ static void intel_enable_plane(struct drm_i915_private *dev_priv, return; I915_WRITE(reg, val | DISPLAY_PLANE_ENABLE); + intel_flush_display_plane(dev_priv, plane); intel_wait_for_vblank(dev_priv->dev, pipe); } -/* - * Plane regs are double buffered, going from enabled->disabled needs a - * trigger in order to latch. The display address reg provides this. - */ -static void intel_flush_display_plane(struct drm_i915_private *dev_priv, - enum plane plane) -{ - I915_WRITE(DSPADDR(plane), I915_READ(DSPADDR(plane))); - I915_WRITE(DSPSURF(plane), I915_READ(DSPSURF(plane))); -} - /** * intel_disable_plane - disable a display plane * @dev_priv: i915 private structure @@ -7418,10 +7419,12 @@ static void gen6_init_clock_gating(struct drm_device *dev) ILK_DPARB_CLK_GATE | ILK_DPFD_CLK_GATE); - for_each_pipe(pipe) + for_each_pipe(pipe) { I915_WRITE(DSPCNTR(pipe), I915_READ(DSPCNTR(pipe)) | DISPPLANE_TRICKLE_FEED_DISABLE); + intel_flush_display_plane(dev_priv, pipe); + } } static void ivybridge_init_clock_gating(struct drm_device *dev) @@ -7438,10 +7441,12 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) I915_WRITE(ILK_DSPCLK_GATE, IVB_VRHUNIT_CLK_GATE); - for_each_pipe(pipe) + for_each_pipe(pipe) { I915_WRITE(DSPCNTR(pipe), I915_READ(DSPCNTR(pipe)) | DISPPLANE_TRICKLE_FEED_DISABLE); + intel_flush_display_plane(dev_priv, pipe); + } } static void g4x_init_clock_gating(struct drm_device *dev) From cb0e093162d7b6589c2217a00e2abfef686b32d6 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 28 Jul 2011 14:50:30 -0700 Subject: [PATCH 26/55] drm/i915: fix CB tuning check for ILK+ CB tuning is needed to handle potential process variations that might cause clock jitter for certain PLL settings. However, we were setting it incorrectly since we were using the wrong M value as a check (M1 when we needed to use the whole M value). Fix it up, making my HDMI attached display a little prettier (used to have occasional dots crawl across the display). Signed-off-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8f7ed7304269..32c8c956d975 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4994,7 +4994,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, } else if (is_sdvo && is_tv) factor = 20; - if (clock.m1 < factor * clock.n) + if (clock.m < factor * clock.n) fp |= FP_CB_TUNE; dpll = 0; From b055c8f3ef9f7bc6ba415d900f298d7801a9d1d4 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 8 Jul 2011 11:31:57 -0700 Subject: [PATCH 27/55] drm/i915/hdmi: send AVI info frames on ILK+ as well On Ironlake and above, we have per-transcoder DIP registers, so use them for sending DIPs like AVI infoframes on ILK and above. Signed-off-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_reg.h | 14 +++++++++++ drivers/gpu/drm/i915/intel_hdmi.c | 39 ++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 00bd51070314..30d8aae9ac4c 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3020,6 +3020,20 @@ #define _TRANSA_DP_LINK_M2 0xe0048 #define _TRANSA_DP_LINK_N2 0xe004c +/* Per-transcoder DIP controls */ + +#define _VIDEO_DIP_CTL_A 0xe0200 +#define _VIDEO_DIP_DATA_A 0xe0208 +#define _VIDEO_DIP_GCP_A 0xe0210 + +#define _VIDEO_DIP_CTL_B 0xe1200 +#define _VIDEO_DIP_DATA_B 0xe1208 +#define _VIDEO_DIP_GCP_B 0xe1210 + +#define TVIDEO_DIP_CTL(pipe) _PIPE(pipe, _VIDEO_DIP_CTL_A, _VIDEO_DIP_CTL_B) +#define TVIDEO_DIP_DATA(pipe) _PIPE(pipe, _VIDEO_DIP_DATA_A, _VIDEO_DIP_DATA_B) +#define TVIDEO_DIP_GCP(pipe) _PIPE(pipe, _VIDEO_DIP_GCP_A, _VIDEO_DIP_GCP_B) + #define _TRANS_HTOTAL_B 0xe1000 #define _TRANS_HBLANK_B 0xe1004 #define _TRANS_HSYNC_B 0xe1008 diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index aa0a8e83142e..c220255ef1d9 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -112,6 +112,40 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder) VIDEO_DIP_ENABLE_AVI); } +static void intel_ironlake_hdmi_set_avi_infoframe(struct drm_encoder *encoder) +{ + struct dip_infoframe avi_if = { + .type = DIP_TYPE_AVI, + .ver = DIP_VERSION_AVI, + .len = DIP_LEN_AVI, + }; + uint32_t *data = (uint32_t *)&avi_if; + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + struct drm_crtc *crtc = encoder->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int reg = TVIDEO_DIP_CTL(intel_crtc->pipe); + unsigned i; + + if (!intel_hdmi->has_hdmi_sink) + return; + + intel_wait_for_vblank(dev, intel_crtc->pipe); + + I915_WRITE(reg, VIDEO_DIP_SELECT_AVI); + + intel_dip_infoframe_csum(&avi_if); + for (i = 0; i < sizeof(avi_if); i += 4) { + I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data); + data++; + } + + I915_WRITE(reg, VIDEO_DIP_ENABLE | VIDEO_DIP_SELECT_AVI | + VIDEO_DIP_FREQ_VSYNC | (DIP_LEN_AVI << 8) | + VIDEO_DIP_ENABLE_AVI); +} + static void intel_hdmi_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -149,7 +183,10 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder, I915_WRITE(intel_hdmi->sdvox_reg, sdvox); POSTING_READ(intel_hdmi->sdvox_reg); - intel_hdmi_set_avi_infoframe(encoder); + if (HAS_PCH_SPLIT(dev)) + intel_ironlake_hdmi_set_avi_infoframe(encoder); + else + intel_hdmi_set_avi_infoframe(encoder); } static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) From e0e3fb482105c65ce6f5480a86092e966a29ed79 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 29 Jul 2011 14:45:21 -0700 Subject: [PATCH 28/55] drm/i915: Ignore GPU wedged errors while pinning scanout buffers Failing to pin a scanout buffer will most likely lead to a black screen, so if the GPU is wedged, then just let the pin happen and hope that things work out OK. Signed-off-by: Keith Packard Reviewed-by: Chris Wilson --- drivers/gpu/drm/i915/i915_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index a087e1bf0c2f..d5c7c7bccef5 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3019,7 +3019,7 @@ i915_gem_object_set_to_display_plane(struct drm_i915_gem_object *obj, /* Currently, we are always called from an non-interruptible context. */ if (pipelined != obj->ring) { ret = i915_gem_object_wait_rendering(obj); - if (ret) + if (ret == -ERESTARTSYS) return ret; } From b066254fee2b0b4d1323295f8ae34c9442222165 Mon Sep 17 00:00:00 2001 From: Pieterjan Camerlynck Date: Tue, 26 Jul 2011 16:23:54 +0200 Subject: [PATCH 29/55] i915: add Dell OptiPlex FX170 to intel_no_lvds The Dell OptiPlex FX170 claims to have LVDS, but doesn't. Signed-off-by: Pieterjan Camerlynck Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_lvds.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index b28f7bd9f88a..2e8ddfcba40c 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -688,6 +688,14 @@ static const struct dmi_system_id intel_no_lvds[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Studio Hybrid 140g"), }, }, + { + .callback = intel_no_lvds_dmi_callback, + .ident = "Dell OptiPlex FX170", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex FX170"), + }, + }, { .callback = intel_no_lvds_dmi_callback, .ident = "AOpen Mini PC", From 358733e9047cafcc185ca19b8c369c659ac0c4cf Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 27 Jul 2011 11:53:01 -0700 Subject: [PATCH 30/55] drm/i915: add GPU max frequency control file Mainly for use in debugging and benchmarking, this file allows the user to control the max frequency used by the GPU. Frequency may still vary based on workload (if the frequency is set to higher than the minimum) but won't go over the newly set value. Signed-off-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_debugfs.c | 90 +++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 0a893f7400fa..782b781df9e0 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1300,6 +1300,76 @@ static const struct file_operations i915_wedged_fops = { .llseek = default_llseek, }; +static int +i915_max_freq_open(struct inode *inode, + struct file *filp) +{ + filp->private_data = inode->i_private; + return 0; +} + +static ssize_t +i915_max_freq_read(struct file *filp, + char __user *ubuf, + size_t max, + loff_t *ppos) +{ + struct drm_device *dev = filp->private_data; + drm_i915_private_t *dev_priv = dev->dev_private; + char buf[80]; + int len; + + len = snprintf(buf, sizeof (buf), + "max freq: %d\n", dev_priv->max_delay * 50); + + if (len > sizeof (buf)) + len = sizeof (buf); + + return simple_read_from_buffer(ubuf, max, ppos, buf, len); +} + +static ssize_t +i915_max_freq_write(struct file *filp, + const char __user *ubuf, + size_t cnt, + loff_t *ppos) +{ + struct drm_device *dev = filp->private_data; + struct drm_i915_private *dev_priv = dev->dev_private; + char buf[20]; + int val = 1; + + if (cnt > 0) { + if (cnt > sizeof (buf) - 1) + return -EINVAL; + + if (copy_from_user(buf, ubuf, cnt)) + return -EFAULT; + buf[cnt] = 0; + + val = simple_strtoul(buf, NULL, 0); + } + + DRM_DEBUG_DRIVER("Manually setting max freq to %d\n", val); + + /* + * Turbo will still be enabled, but won't go above the set value. + */ + dev_priv->max_delay = val / 50; + + gen6_set_rps(dev, val / 50); + + return cnt; +} + +static const struct file_operations i915_max_freq_fops = { + .owner = THIS_MODULE, + .open = i915_max_freq_open, + .read = i915_max_freq_read, + .write = i915_max_freq_write, + .llseek = default_llseek, +}; + /* As the drm_debugfs_init() routines are called before dev->dev_private is * allocated we need to hook into the minor for release. */ static int @@ -1399,6 +1469,21 @@ static int i915_forcewake_create(struct dentry *root, struct drm_minor *minor) return drm_add_fake_info_node(minor, ent, &i915_forcewake_fops); } +static int i915_max_freq_create(struct dentry *root, struct drm_minor *minor) +{ + struct drm_device *dev = minor->dev; + struct dentry *ent; + + ent = debugfs_create_file("i915_max_freq", + S_IRUGO | S_IWUSR, + root, dev, + &i915_max_freq_fops); + if (IS_ERR(ent)) + return PTR_ERR(ent); + + return drm_add_fake_info_node(minor, ent, &i915_max_freq_fops); +} + static struct drm_info_list i915_debugfs_list[] = { {"i915_capabilities", i915_capabilities, 0}, {"i915_gem_objects", i915_gem_object_info, 0}, @@ -1449,6 +1534,9 @@ int i915_debugfs_init(struct drm_minor *minor) return ret; ret = i915_forcewake_create(minor->debugfs_root, minor); + if (ret) + return ret; + ret = i915_max_freq_create(minor->debugfs_root, minor); if (ret) return ret; @@ -1465,6 +1553,8 @@ void i915_debugfs_cleanup(struct drm_minor *minor) 1, minor); drm_debugfs_remove_files((struct drm_info_list *) &i915_wedged_fops, 1, minor); + drm_debugfs_remove_files((struct drm_info_list *) &i915_max_freq_fops, + 1, minor); } #endif /* CONFIG_DEBUG_FS */ From 013a41ec541d5daa0c9f2b5126d2e820902c052d Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 19 Jul 2011 15:38:56 -0700 Subject: [PATCH 31/55] drm/i915: provide more error output when mode sets fail If a mode set fails we may get a message from drm_crtc_helper if we're lucky, but it won't tell us anything about *why* we failed to set a mode. So add a few DRM_ERRORs for the cases that shouldn't happen so we can debug things more easily. Signed-off-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_display.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 32c8c956d975..d2fb8cf3de0d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1958,7 +1958,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, /* no fb bound */ if (!crtc->fb) { - DRM_DEBUG_KMS("No FB bound\n"); + DRM_ERROR("No FB bound\n"); return 0; } @@ -1967,6 +1967,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, case 1: break; default: + DRM_ERROR("no plane for crtc\n"); return -EINVAL; } @@ -1976,6 +1977,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, NULL); if (ret != 0) { mutex_unlock(&dev->struct_mutex); + DRM_ERROR("pin & fence failed\n"); return ret; } @@ -2004,6 +2006,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, if (ret) { i915_gem_object_unpin(to_intel_framebuffer(crtc->fb)->obj); mutex_unlock(&dev->struct_mutex); + DRM_ERROR("failed to update base address\n"); return ret; } From 070d329ae52e2fde341771d753a5b728145881f4 Mon Sep 17 00:00:00 2001 From: Michel Alexandre Salim Date: Thu, 28 Jul 2011 18:52:06 +0200 Subject: [PATCH 32/55] drm/i915: Add quirk to disable SSC on Sony Vaio Y2 Using the new quirk added to support disabling SSC on Lenovo U160 (#36656, commit 435793dfb8aec7b2e19f72d5bce8a22fd0b57839), also register the Vaio as a special case and disable SSC for it. This patch fixes #34437 on fdo bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=34437 Signed-off-by: Michel Alexandre Salim Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_display.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d2fb8cf3de0d..3d2900cbf431 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7892,6 +7892,9 @@ struct intel_quirk intel_quirks[] = { /* Lenovo U160 cannot use SSC on LVDS */ { 0x0046, 0x17aa, 0x3920, quirk_ssc_force_disable }, + + /* Sony Vaio Y cannot use SSC on LVDS */ + { 0x0046, 0x104d, 0x9076, quirk_ssc_force_disable }, }; static void intel_init_quirks(struct drm_device *dev) From 291427f5fdadec6e4be2924172e83588880e1539 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 29 Jul 2011 12:42:37 -0700 Subject: [PATCH 33/55] drm/i915: apply phase pointer override on SNB+ too These bits moved around on SNB and above. v2: again with the git send-email fail v3: add macros for getting per-pipe override & enable bits v4: enable phase sync pointer on SNB and IVB configs as well Signed-off-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_reg.h | 5 +++++ drivers/gpu/drm/i915/intel_display.c | 31 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 30d8aae9ac4c..a7f7a347c700 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3091,6 +3091,11 @@ #define TRANS_CHICKEN2(pipe) _PIPE(pipe, _TRANSA_CHICKEN2, _TRANSB_CHICKEN2) #define TRANS_AUTOTRAIN_GEN_STALL_DIS (1<<31) +#define SOUTH_CHICKEN1 0xc2000 +#define FDIA_PHASE_SYNC_SHIFT_OVR 19 +#define FDIA_PHASE_SYNC_SHIFT_EN 18 +#define FDI_PHASE_SYNC_OVR(pipe) (1<<(FDIA_PHASE_SYNC_SHIFT_OVR - ((pipe) * 2))) +#define FDI_PHASE_SYNC_EN(pipe) (1<<(FDIA_PHASE_SYNC_SHIFT_EN - ((pipe) * 2))) #define SOUTH_CHICKEN2 0xc2004 #define DPLS_EDP_PPS_FIX_DIS (1<<0) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 3d2900cbf431..53164606918f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2113,6 +2113,18 @@ static void intel_fdi_normal_train(struct drm_crtc *crtc) FDI_FE_ERRC_ENABLE); } +static void cpt_phase_pointer_enable(struct drm_device *dev, int pipe) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 flags = I915_READ(SOUTH_CHICKEN1); + + flags |= FDI_PHASE_SYNC_OVR(pipe); + I915_WRITE(SOUTH_CHICKEN1, flags); /* once to unlock... */ + flags |= FDI_PHASE_SYNC_EN(pipe); + I915_WRITE(SOUTH_CHICKEN1, flags); /* then again to enable */ + POSTING_READ(SOUTH_CHICKEN1); +} + /* The FDI link training functions for ILK/Ibexpeak. */ static void ironlake_fdi_link_train(struct drm_crtc *crtc) { @@ -2263,6 +2275,9 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc) POSTING_READ(reg); udelay(150); + if (HAS_PCH_CPT(dev)) + cpt_phase_pointer_enable(dev, pipe); + for (i = 0; i < 4; i++ ) { reg = FDI_TX_CTL(pipe); temp = I915_READ(reg); @@ -2379,6 +2394,9 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc) POSTING_READ(reg); udelay(150); + if (HAS_PCH_CPT(dev)) + cpt_phase_pointer_enable(dev, pipe); + for (i = 0; i < 4; i++ ) { reg = FDI_TX_CTL(pipe); temp = I915_READ(reg); @@ -2488,6 +2506,17 @@ static void ironlake_fdi_pll_enable(struct drm_crtc *crtc) } } +static void cpt_phase_pointer_disable(struct drm_device *dev, int pipe) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 flags = I915_READ(SOUTH_CHICKEN1); + + flags &= ~(FDI_PHASE_SYNC_EN(pipe)); + I915_WRITE(SOUTH_CHICKEN1, flags); /* once to disable... */ + flags &= ~(FDI_PHASE_SYNC_OVR(pipe)); + I915_WRITE(SOUTH_CHICKEN1, flags); /* then again to lock */ + POSTING_READ(SOUTH_CHICKEN1); +} static void ironlake_fdi_disable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -2517,6 +2546,8 @@ static void ironlake_fdi_disable(struct drm_crtc *crtc) I915_WRITE(FDI_RX_CHICKEN(pipe), I915_READ(FDI_RX_CHICKEN(pipe) & ~FDI_RX_PHASE_SYNC_POINTER_EN)); + } else if (HAS_PCH_CPT(dev)) { + cpt_phase_pointer_disable(dev, pipe); } /* still set train pattern 1 */ From cda2bb78c24de7674eafa3210314dc75bed344a6 Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Tue, 26 Jul 2011 16:53:06 -0400 Subject: [PATCH 34/55] drm/i915/pch: Save/restore PCH_PORT_HOTPLUG across suspend At least on a Lenovo X220 the HPD bits of this are enabled at boot but cleared after resume, which means plug interrupts stop working. This also happens to fix DP displays re-lighting on resume. I'm quite certain that's an accident: the first DP link train inevitably fails on that machine, and it's only serendipity that we're getting multiple plug interrupts and the second train works. But I shall take my victories where I get them. Signed-off-by: Adam Jackson Tested-by: Keith Packard Reviewed-by: Keith Packard Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_suspend.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ce7914c4c044..e0d0e278f62d 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -541,6 +541,7 @@ typedef struct drm_i915_private { u32 savePIPEB_LINK_M1; u32 savePIPEB_LINK_N1; u32 saveMCHBAR_RENDER_STANDBY; + u32 savePCH_PORT_HOTPLUG; struct { /** Bridge to intel-gtt-ko */ diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index 5257cfc34c35..27693c05c6d2 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -814,6 +814,7 @@ int i915_save_state(struct drm_device *dev) dev_priv->saveFDI_RXB_IMR = I915_READ(_FDI_RXB_IMR); dev_priv->saveMCHBAR_RENDER_STANDBY = I915_READ(RSTDBYCTL); + dev_priv->savePCH_PORT_HOTPLUG = I915_READ(PCH_PORT_HOTPLUG); } else { dev_priv->saveIER = I915_READ(IER); dev_priv->saveIMR = I915_READ(IMR); @@ -865,6 +866,7 @@ int i915_restore_state(struct drm_device *dev) I915_WRITE(GTIMR, dev_priv->saveGTIMR); I915_WRITE(_FDI_RXA_IMR, dev_priv->saveFDI_RXA_IMR); I915_WRITE(_FDI_RXB_IMR, dev_priv->saveFDI_RXB_IMR); + I915_WRITE(PCH_PORT_HOTPLUG, dev_priv->savePCH_PORT_HOTPLUG); } else { I915_WRITE(IER, dev_priv->saveIER); I915_WRITE(IMR, dev_priv->saveIMR); From 62ac41a6e443ef26b9de862c6e20c088e2b04dde Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 28 Jul 2011 12:55:14 -0700 Subject: [PATCH 35/55] drm/i915: don't use uninitialized EDID bpc values when picking pipe bpp The EDID parser will zero out the bpc value, and the driver needs to handle that case. In our picker, we'll just ignore 0 values as far as bpp picking goes. Fixes https://bugs.freedesktop.org/show_bug.cgi?id=39323. Signed-off-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_display.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2bf5bb63fe41..35364e68a091 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4600,7 +4600,9 @@ static bool intel_choose_pipe_bpp_dither(struct drm_crtc *crtc, if (connector->encoder != encoder) continue; - if (connector->display_info.bpc < display_bpc) { + /* Don't use an invalid EDID bpc value */ + if (connector->display_info.bpc && + connector->display_info.bpc < display_bpc) { DRM_DEBUG_DRIVER("clamping display bpc (was %d) to EDID reported max of %d\n", display_bpc, connector->display_info.bpc); display_bpc = connector->display_info.bpc; } @@ -5215,7 +5217,8 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, temp |= PIPE_12BPC; break; default: - WARN(1, "intel_choose_pipe_bpp returned invalid value\n"); + WARN(1, "intel_choose_pipe_bpp returned invalid value %d\n", + pipe_bpp); temp |= PIPE_8BPC; pipe_bpp = 24; break; From 11bee43ebba0bfc92165c059f6e9869197ea8889 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Mon, 1 Aug 2011 15:02:20 -0700 Subject: [PATCH 36/55] drm/i915/dp: wait for previous AUX channel activity to clear Before initiating a new read or write on the DP AUX channel, wait for any outstanding activity to complete. This may happen during normal retry behavior. If the wait fails (i.e. after 1ms the AUX channel is still busy) dump a backtrace to make the caller easier to spot. v2: use msleep instead, and timeout after 3ms (only ever saw 1 retry with msleep in testing) v3: fix backtrace check to trigger if the 3ms wait times out Fixes https://bugs.freedesktop.org/show_bug.cgi?id=38136. Signed-off-by: Jesse Barnes Reviewed-by: Keith Packard Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_dp.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 449364187bc0..ba72fbcbbbc9 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -315,9 +315,17 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, else precharge = 5; - if (I915_READ(ch_ctl) & DP_AUX_CH_CTL_SEND_BUSY) { - DRM_ERROR("dp_aux_ch not started status 0x%08x\n", - I915_READ(ch_ctl)); + /* Try to wait for any previous AUX channel activity */ + for (try = 0; try < 3; try++) { + status = I915_READ(ch_ctl); + if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0) + break; + msleep(1); + } + + if (try == 3) { + WARN(1, "dp_aux_ch not started status 0x%08x\n", + I915_READ(ch_ctl)); return -EBUSY; } From 4edd17a25c99f34bd7a75c1daf31afe840237da8 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 3 Aug 2011 10:34:58 -0700 Subject: [PATCH 37/55] Revert "drm/i915/dp: Zero the DPCD data before connection probe" This reverts commit 97cdd7101079adc3c626d159c62d43de949516c8. Clearing the dpcd data means that if the fetch fails, any previous data will be lost. On eDP, this is no fun as we only fetch dpcd at init time, so the memset will destroy that the next time through. --- drivers/gpu/drm/i915/intel_dp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index ba72fbcbbbc9..2f901c05ea2c 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1697,7 +1697,6 @@ intel_dp_detect(struct drm_connector *connector, bool force) struct edid *edid = NULL; intel_dp->has_audio = false; - memset(intel_dp->dpcd, 0, sizeof(intel_dp->dpcd)); if (HAS_PCH_SPLIT(dev)) status = ironlake_dp_detect(intel_dp); From 4e20fa65a3ea789510eed1a15deb9e8aab2b8202 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 3 Aug 2011 10:52:24 -0700 Subject: [PATCH 38/55] drm/i915: Try enabling RC6 by default (again) Jesse Barnes and I found a couple of issues where incorrect mode setting would cause problems with RC6 enabled. We're hopeful that fixing those will resolve the outstanding issues with a few machines that had trouble before 3.0 with rc6. Cc: Pekka Enberg Cc: Francesco Allertsen Cc: Ted Phelps Cc: Gu Rui Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=38567 Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=38332 Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index ce045a8cf82c..60e4b9ef618b 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -62,7 +62,7 @@ module_param_named(semaphores, i915_semaphores, int, 0600); MODULE_PARM_DESC(semaphores, "Use semaphores for inter-ring sync (default: false)"); -unsigned int i915_enable_rc6 __read_mostly = 0; +unsigned int i915_enable_rc6 __read_mostly = 1; module_param_named(i915_enable_rc6, i915_enable_rc6, int, 0600); MODULE_PARM_DESC(i915_enable_rc6, "Enable power-saving render C-state 6 (default: true)"); From ebec9a7bf11f843b0602b06c402f04bf4213b35a Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 3 Aug 2011 09:22:54 -0700 Subject: [PATCH 39/55] drm: track CEA version number if present Drivers need to know the CEA version number in addition to other display info (like whether the display is an HDMI sink) before enabling certain features. So track the CEA version number in the display info structure. Signed-off-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/drm_edid.c | 9 +++++++++ include/drm/drm_crtc.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 09292193dafe..e12f8b0cec94 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1439,6 +1439,8 @@ EXPORT_SYMBOL(drm_detect_monitor_audio); static void drm_add_display_info(struct edid *edid, struct drm_display_info *info) { + u8 *edid_ext; + info->width_mm = edid->width_cm * 10; info->height_mm = edid->height_cm * 10; @@ -1483,6 +1485,13 @@ static void drm_add_display_info(struct edid *edid, info->color_formats = DRM_COLOR_FORMAT_YCRCB444; if (info->color_formats & DRM_EDID_FEATURE_RGB_YCRCB422) info->color_formats = DRM_COLOR_FORMAT_YCRCB422; + + /* Get data from CEA blocks if present */ + edid_ext = drm_find_cea_extension(edid); + if (!edid_ext) + return; + + info->cea_rev = edid_ext[1]; } /** diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 33d12f87f0e0..d515bc8f4ba7 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -205,6 +205,8 @@ struct drm_display_info { enum subpixel_order subpixel_order; u32 color_formats; + u8 cea_rev; + char *raw_edid; /* if any */ }; From 45187ace97f7b3deb559b25348ccb7e301c158c9 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 3 Aug 2011 09:22:55 -0700 Subject: [PATCH 40/55] drm/i915/hdmi: split infoframe setting from infoframe type code This makes it easier to add support for other infoframes (e.g. SPD, vendor specific). Signed-off-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_reg.h | 1 + drivers/gpu/drm/i915/intel_drv.h | 2 + drivers/gpu/drm/i915/intel_hdmi.c | 163 ++++++++++++++++++++---------- 3 files changed, 113 insertions(+), 53 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index be67a596eee5..56cae72a5207 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1506,6 +1506,7 @@ #define VIDEO_DIP_SELECT_AVI (0 << 19) #define VIDEO_DIP_SELECT_VENDOR (1 << 19) #define VIDEO_DIP_SELECT_SPD (3 << 19) +#define VIDEO_DIP_SELECT_MASK (3 << 19) #define VIDEO_DIP_FREQ_ONCE (0 << 16) #define VIDEO_DIP_FREQ_VSYNC (1 << 16) #define VIDEO_DIP_FREQ_2VSYNC (2 << 16) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 6e990f9760ef..2f47dcf0b367 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -178,6 +178,8 @@ struct intel_crtc { #define to_intel_encoder(x) container_of(x, struct intel_encoder, base) #define to_intel_framebuffer(x) container_of(x, struct intel_framebuffer, base) +#define DIP_HEADER_SIZE 5 + #define DIP_TYPE_AVI 0x82 #define DIP_VERSION_AVI 0x2 #define DIP_LEN_AVI 13 diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 105e2b892f3a..d43cebdea1ee 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -45,6 +45,8 @@ struct intel_hdmi { bool has_hdmi_sink; bool has_audio; int force_audio; + void (*write_infoframe)(struct drm_encoder *encoder, + struct dip_infoframe *frame); }; static struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder) @@ -58,37 +60,70 @@ static struct intel_hdmi *intel_attached_hdmi(struct drm_connector *connector) struct intel_hdmi, base); } -void intel_dip_infoframe_csum(struct dip_infoframe *avi_if) +void intel_dip_infoframe_csum(struct dip_infoframe *frame) { - uint8_t *data = (uint8_t *)avi_if; + uint8_t *data = (uint8_t *)frame; uint8_t sum = 0; unsigned i; - avi_if->checksum = 0; - avi_if->ecc = 0; + frame->checksum = 0; + frame->ecc = 0; - for (i = 0; i < sizeof(*avi_if); i++) + /* Header isn't part of the checksum */ + for (i = 5; i < frame->len; i++) sum += data[i]; - avi_if->checksum = 0x100 - sum; + frame->checksum = 0x100 - sum; } -static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder) +static u32 intel_infoframe_index(struct dip_infoframe *frame) { - struct dip_infoframe avi_if = { - .type = DIP_TYPE_AVI, - .ver = DIP_VERSION_AVI, - .len = DIP_LEN_AVI, - }; - uint32_t *data = (uint32_t *)&avi_if; + u32 flags = 0; + + switch (frame->type) { + case DIP_TYPE_AVI: + flags |= VIDEO_DIP_SELECT_AVI; + break; + case DIP_TYPE_SPD: + flags |= VIDEO_DIP_SELECT_SPD; + break; + default: + DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); + break; + } + + return flags; +} + +static u32 intel_infoframe_flags(struct dip_infoframe *frame) +{ + u32 flags = 0; + + switch (frame->type) { + case DIP_TYPE_AVI: + flags |= VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_FREQ_VSYNC; + break; + case DIP_TYPE_SPD: + flags |= VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_FREQ_2VSYNC; + break; + default: + DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); + break; + } + + return flags; +} + +static void i9xx_write_infoframe(struct drm_encoder *encoder, + struct dip_infoframe *frame) +{ + uint32_t *data = (uint32_t *)frame; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); - u32 port; - unsigned i; + u32 port, flags, val = I915_READ(VIDEO_DIP_CTL); + unsigned i, len = DIP_HEADER_SIZE + frame->len; - if (!intel_hdmi->has_hdmi_sink) - return; /* XXX first guess at handling video port, is this corrent? */ if (intel_hdmi->sdvox_reg == SDVOB) @@ -98,52 +133,72 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder) else return; - I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port | - VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC); + flags = intel_infoframe_index(frame); - intel_dip_infoframe_csum(&avi_if); - for (i = 0; i < sizeof(avi_if); i += 4) { + val &= ~VIDEO_DIP_SELECT_MASK; + + I915_WRITE(VIDEO_DIP_CTL, val | port | flags); + + for (i = 0; i < len; i += 4) { I915_WRITE(VIDEO_DIP_DATA, *data); data++; } - I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port | - VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC | - VIDEO_DIP_ENABLE_AVI); + flags |= intel_infoframe_flags(frame); + + I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | val | port | flags); } -static void intel_ironlake_hdmi_set_avi_infoframe(struct drm_encoder *encoder) +static void ironlake_write_infoframe(struct drm_encoder *encoder, + struct dip_infoframe *frame) +{ + uint32_t *data = (uint32_t *)frame; + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc = encoder->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int reg = TVIDEO_DIP_CTL(intel_crtc->pipe); + unsigned i, len = DIP_HEADER_SIZE + frame->len; + u32 flags, val = I915_READ(reg); + + intel_wait_for_vblank(dev, intel_crtc->pipe); + + flags = intel_infoframe_index(frame); + + val &= ~VIDEO_DIP_SELECT_MASK; + + I915_WRITE(reg, val | flags); + + for (i = 0; i < len; i += 4) { + I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data); + data++; + } + + flags |= intel_infoframe_flags(frame); + + I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags); +} +static void intel_set_infoframe(struct drm_encoder *encoder, + struct dip_infoframe *frame) +{ + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + + if (!intel_hdmi->has_hdmi_sink) + return; + + intel_dip_infoframe_csum(frame); + intel_hdmi->write_infoframe(encoder, frame); +} + +static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder) { struct dip_infoframe avi_if = { .type = DIP_TYPE_AVI, .ver = DIP_VERSION_AVI, .len = DIP_LEN_AVI, }; - uint32_t *data = (uint32_t *)&avi_if; - struct drm_device *dev = encoder->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); - struct drm_crtc *crtc = encoder->crtc; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int reg = TVIDEO_DIP_CTL(intel_crtc->pipe); - unsigned i; - if (!intel_hdmi->has_hdmi_sink) - return; - - intel_wait_for_vblank(dev, intel_crtc->pipe); - - I915_WRITE(reg, VIDEO_DIP_SELECT_AVI); - - intel_dip_infoframe_csum(&avi_if); - for (i = 0; i < sizeof(avi_if); i += 4) { - I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data); - data++; - } - - I915_WRITE(reg, VIDEO_DIP_ENABLE | VIDEO_DIP_SELECT_AVI | - VIDEO_DIP_FREQ_VSYNC | (DIP_LEN_AVI << 8) | - VIDEO_DIP_ENABLE_AVI); + intel_set_infoframe(encoder, &avi_if); } static void intel_hdmi_mode_set(struct drm_encoder *encoder, @@ -189,10 +244,7 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder, I915_WRITE(intel_hdmi->sdvox_reg, sdvox); POSTING_READ(intel_hdmi->sdvox_reg); - if (HAS_PCH_SPLIT(dev)) - intel_ironlake_hdmi_set_avi_infoframe(encoder); - else - intel_hdmi_set_avi_infoframe(encoder); + intel_hdmi_set_avi_infoframe(encoder); } static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) @@ -470,6 +522,11 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) intel_hdmi->sdvox_reg = sdvox_reg; + if (!HAS_PCH_SPLIT(dev)) + intel_hdmi->write_infoframe = i9xx_write_infoframe; + else + intel_hdmi->write_infoframe = ironlake_write_infoframe; + drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs); intel_hdmi_add_properties(intel_hdmi, connector); From c0864cb39c68696e80657360eba63da5e743b7aa Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 3 Aug 2011 09:22:56 -0700 Subject: [PATCH 41/55] drm/i915/hdmi: HDMI source product description infoframe support Set an SPD infoframe if the sink supports it. Signed-off-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_drv.h | 21 +++++++++++++++++++++ drivers/gpu/drm/i915/intel_hdmi.c | 16 ++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 2f47dcf0b367..7b330e76a435 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -184,6 +184,22 @@ struct intel_crtc { #define DIP_VERSION_AVI 0x2 #define DIP_LEN_AVI 13 +#define DIP_TYPE_SPD 0x3 +#define DIP_VERSION_SPD 0x1 +#define DIP_LEN_SPD 25 +#define DIP_SPD_UNKNOWN 0 +#define DIP_SPD_DSTB 0x1 +#define DIP_SPD_DVDP 0x2 +#define DIP_SPD_DVHS 0x3 +#define DIP_SPD_HDDVR 0x4 +#define DIP_SPD_DVC 0x5 +#define DIP_SPD_DSC 0x6 +#define DIP_SPD_VCD 0x7 +#define DIP_SPD_GAME 0x8 +#define DIP_SPD_PC 0x9 +#define DIP_SPD_BD 0xa +#define DIP_SPD_SCD 0xb + struct dip_infoframe { uint8_t type; /* HB0 */ uint8_t ver; /* HB1 */ @@ -208,6 +224,11 @@ struct dip_infoframe { uint16_t left_bar_end; uint16_t right_bar_start; } avi; + struct { + uint8_t vn[8]; + uint8_t pd[16]; + uint8_t sdi; + } spd; uint8_t payload[27]; } __attribute__ ((packed)) body; } __attribute__((packed)); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index d43cebdea1ee..226ba830f383 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -201,6 +201,21 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder) intel_set_infoframe(encoder, &avi_if); } +static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder) +{ + struct dip_infoframe spd_if; + + memset(&spd_if, 0, sizeof(spd_if)); + spd_if.type = DIP_TYPE_SPD; + spd_if.ver = DIP_VERSION_SPD; + spd_if.len = DIP_LEN_SPD; + strcpy(spd_if.body.spd.vn, "Intel"); + strcpy(spd_if.body.spd.pd, "Integrated gfx"); + spd_if.body.spd.sdi = DIP_SPD_PC; + + intel_set_infoframe(encoder, &spd_if); +} + static void intel_hdmi_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -245,6 +260,7 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder, POSTING_READ(intel_hdmi->sdvox_reg); intel_hdmi_set_avi_infoframe(encoder); + intel_hdmi_set_spd_infoframe(encoder); } static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) From 07b7ddd9b7f17a567e3ac2b33a4dffcb2a4524e0 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 3 Aug 2011 11:28:44 -0700 Subject: [PATCH 42/55] drm/i915: allow cache sharing policy control Expose the SNB+ cache sharing policy register in debugfs. The new file, i915_cache_sharing, has 4 values, 0-3, with 0 being "max uncore resources" and 3 being the minimum. Exposing this control should make benchmarking easier and help us choose a good default. Signed-off-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_debugfs.c | 99 +++++++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_reg.h | 8 +++ 2 files changed, 107 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 5bf7bf57c641..a8ab6263e0d7 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1408,6 +1408,85 @@ static const struct file_operations i915_max_freq_fops = { .llseek = default_llseek, }; +static int +i915_cache_sharing_open(struct inode *inode, + struct file *filp) +{ + filp->private_data = inode->i_private; + return 0; +} + +static ssize_t +i915_cache_sharing_read(struct file *filp, + char __user *ubuf, + size_t max, + loff_t *ppos) +{ + struct drm_device *dev = filp->private_data; + drm_i915_private_t *dev_priv = dev->dev_private; + char buf[80]; + u32 snpcr; + int len; + + mutex_lock(&dev_priv->dev->struct_mutex); + snpcr = I915_READ(GEN6_MBCUNIT_SNPCR); + mutex_unlock(&dev_priv->dev->struct_mutex); + + len = snprintf(buf, sizeof (buf), + "%d\n", (snpcr & GEN6_MBC_SNPCR_MASK) >> + GEN6_MBC_SNPCR_SHIFT); + + if (len > sizeof (buf)) + len = sizeof (buf); + + return simple_read_from_buffer(ubuf, max, ppos, buf, len); +} + +static ssize_t +i915_cache_sharing_write(struct file *filp, + const char __user *ubuf, + size_t cnt, + loff_t *ppos) +{ + struct drm_device *dev = filp->private_data; + struct drm_i915_private *dev_priv = dev->dev_private; + char buf[20]; + u32 snpcr; + int val = 1; + + if (cnt > 0) { + if (cnt > sizeof (buf) - 1) + return -EINVAL; + + if (copy_from_user(buf, ubuf, cnt)) + return -EFAULT; + buf[cnt] = 0; + + val = simple_strtoul(buf, NULL, 0); + } + + if (val < 0 || val > 3) + return -EINVAL; + + DRM_DEBUG_DRIVER("Manually setting uncore sharing to %d\n", val); + + /* Update the cache sharing policy here as well */ + snpcr = I915_READ(GEN6_MBCUNIT_SNPCR); + snpcr &= ~GEN6_MBC_SNPCR_MASK; + snpcr |= (val << GEN6_MBC_SNPCR_SHIFT); + I915_WRITE(GEN6_MBCUNIT_SNPCR, snpcr); + + return cnt; +} + +static const struct file_operations i915_cache_sharing_fops = { + .owner = THIS_MODULE, + .open = i915_cache_sharing_open, + .read = i915_cache_sharing_read, + .write = i915_cache_sharing_write, + .llseek = default_llseek, +}; + /* As the drm_debugfs_init() routines are called before dev->dev_private is * allocated we need to hook into the minor for release. */ static int @@ -1522,6 +1601,21 @@ static int i915_max_freq_create(struct dentry *root, struct drm_minor *minor) return drm_add_fake_info_node(minor, ent, &i915_max_freq_fops); } +static int i915_cache_sharing_create(struct dentry *root, struct drm_minor *minor) +{ + struct drm_device *dev = minor->dev; + struct dentry *ent; + + ent = debugfs_create_file("i915_cache_sharing", + S_IRUGO | S_IWUSR, + root, dev, + &i915_cache_sharing_fops); + if (IS_ERR(ent)) + return PTR_ERR(ent); + + return drm_add_fake_info_node(minor, ent, &i915_cache_sharing_fops); +} + static struct drm_info_list i915_debugfs_list[] = { {"i915_capabilities", i915_capabilities, 0}, {"i915_gem_objects", i915_gem_object_info, 0}, @@ -1576,6 +1670,9 @@ int i915_debugfs_init(struct drm_minor *minor) if (ret) return ret; ret = i915_max_freq_create(minor->debugfs_root, minor); + if (ret) + return ret; + ret = i915_cache_sharing_create(minor->debugfs_root, minor); if (ret) return ret; @@ -1594,6 +1691,8 @@ void i915_debugfs_cleanup(struct drm_minor *minor) 1, minor); drm_debugfs_remove_files((struct drm_info_list *) &i915_max_freq_fops, 1, minor); + drm_debugfs_remove_files((struct drm_info_list *) &i915_cache_sharing_fops, + 1, minor); } #endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 56cae72a5207..d1331f771e2f 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -78,6 +78,14 @@ #define GRDOM_RENDER (1<<2) #define GRDOM_MEDIA (3<<2) +#define GEN6_MBCUNIT_SNPCR 0x900c /* for LLC config */ +#define GEN6_MBC_SNPCR_SHIFT 21 +#define GEN6_MBC_SNPCR_MASK (3<<21) +#define GEN6_MBC_SNPCR_MAX (0<<21) +#define GEN6_MBC_SNPCR_MED (1<<21) +#define GEN6_MBC_SNPCR_LOW (2<<21) +#define GEN6_MBC_SNPCR_MIN (3<<21) /* only 1/16th of the cache is shared */ + #define GEN6_GDRST 0x941c #define GEN6_GRDOM_FULL (1 << 0) #define GEN6_GRDOM_RENDER (1 << 1) From b8709894f248b569d7a0f32cc5e84fa14f7585f0 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 27 Jul 2011 04:17:25 +0000 Subject: [PATCH 43/55] drm/radeon/kms: fix version comment due to merge timing Compute cs support was actually added in 2.11.0 rather than 2.10.0, but the patch was written prior. Update comment to match. Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/radeon_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 85f033f19a8a..e71d2ed7fa11 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -50,8 +50,8 @@ * 2.7.0 - fixups for r600 2D tiling support. (no external ABI change), add eg dyn gpr regs * 2.8.0 - pageflip support, r500 US_FORMAT regs. r500 ARGB2101010 colorbuf, r300->r500 CMASK, clock crystal query * 2.9.0 - r600 tiling (s3tc,rgtc) working, SET_PREDICATION packet 3 on r600 + eg, backend query - * 2.10.0 - fusion 2D tiling, initial compute support for the CS checker - * 2.11.0 - backend map + * 2.10.0 - fusion 2D tiling + * 2.11.0 - backend map, initial compute support for the CS checker */ #define KMS_DRIVER_MAJOR 2 #define KMS_DRIVER_MINOR 11 From 884988398f43378a170026184648ce8ff14dcc9f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 27 Jul 2011 09:53:40 +0000 Subject: [PATCH 44/55] drm/radeon: off by one in check_reg() functions This off by one range check was copy and pasted a couple places. It's not really harmful, but we should fix it anyway. Signed-off-by: Dan Carpenter Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/evergreen_cs.c | 2 +- drivers/gpu/drm/radeon/r600_cs.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/radeon/evergreen_cs.c b/drivers/gpu/drm/radeon/evergreen_cs.c index 189e86522b5b..a134790903d3 100644 --- a/drivers/gpu/drm/radeon/evergreen_cs.c +++ b/drivers/gpu/drm/radeon/evergreen_cs.c @@ -428,7 +428,7 @@ static inline int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u3 last_reg = ARRAY_SIZE(evergreen_reg_safe_bm); i = (reg >> 7); - if (i > last_reg) { + if (i >= last_reg) { dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx); return -EINVAL; } diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c index db8ef1905d5f..cf83aa05a684 100644 --- a/drivers/gpu/drm/radeon/r600_cs.c +++ b/drivers/gpu/drm/radeon/r600_cs.c @@ -915,12 +915,11 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx { struct r600_cs_track *track = (struct r600_cs_track *)p->track; struct radeon_cs_reloc *reloc; - u32 last_reg = ARRAY_SIZE(r600_reg_safe_bm); u32 m, i, tmp, *ib; int r; i = (reg >> 7); - if (i > last_reg) { + if (i >= ARRAY_SIZE(r600_reg_safe_bm)) { dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx); return -EINVAL; } From c41b9ee901bb2c7e3eacfa7e171de50c15d61c0b Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Sat, 30 Jul 2011 18:12:24 +0000 Subject: [PATCH 45/55] drm/radeon/kms: add thermal chip quirk for asus 9600xt The board has an lm63 compatible thermal chip, but no thermal chip entry in the vbios tables. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=39513 Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/radeon_combios.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c index a74217cd192f..e0138b674aca 100644 --- a/drivers/gpu/drm/radeon/radeon_combios.c +++ b/drivers/gpu/drm/radeon/radeon_combios.c @@ -2557,6 +2557,7 @@ void radeon_combios_get_power_modes(struct radeon_device *rdev) u16 offset, misc, misc2 = 0; u8 rev, blocks, tmp; int state_index = 0; + struct radeon_i2c_bus_rec i2c_bus; rdev->pm.default_power_state_index = -1; @@ -2575,7 +2576,6 @@ void radeon_combios_get_power_modes(struct radeon_device *rdev) offset = combios_get_table_offset(dev, COMBIOS_OVERDRIVE_INFO_TABLE); if (offset) { u8 thermal_controller = 0, gpio = 0, i2c_addr = 0, clk_bit = 0, data_bit = 0; - struct radeon_i2c_bus_rec i2c_bus; rev = RBIOS8(offset); @@ -2617,6 +2617,25 @@ void radeon_combios_get_power_modes(struct radeon_device *rdev) i2c_new_device(&rdev->pm.i2c_bus->adapter, &info); } } + } else { + /* boards with a thermal chip, but no overdrive table */ + + /* Asus 9600xt has an f75375 on the monid bus */ + if ((dev->pdev->device == 0x4152) && + (dev->pdev->subsystem_vendor == 0x1043) && + (dev->pdev->subsystem_device == 0xc002)) { + i2c_bus = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0); + rdev->pm.i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus); + if (rdev->pm.i2c_bus) { + struct i2c_board_info info = { }; + const char *name = "f75375"; + info.addr = 0x28; + strlcpy(info.type, name, sizeof(info.type)); + i2c_new_device(&rdev->pm.i2c_bus->adapter, &info); + DRM_INFO("Possible %s thermal controller at 0x%02x\n", + name, info.addr); + } + } } if (rdev->flags & RADEON_IS_MOBILITY) { From 4d5cb60d3f6c396899a515e0e667d0f855ed66cc Mon Sep 17 00:00:00 2001 From: Emil Velikov Date: Sat, 30 Jul 2011 14:26:01 +0000 Subject: [PATCH 46/55] drm/debugfs: Initialise empty variable [airlied: move char declaration] Signed-off-by: Emil Velikov Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_debugfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 9d8c892d07c9..9d2668a50872 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -90,7 +90,6 @@ int drm_debugfs_create_files(struct drm_info_list *files, int count, struct drm_device *dev = minor->dev; struct dentry *ent; struct drm_info_node *tmp; - char name[64]; int i, ret; for (i = 0; i < count; i++) { @@ -108,6 +107,9 @@ int drm_debugfs_create_files(struct drm_info_list *files, int count, ent = debugfs_create_file(files[i].name, S_IFREG | S_IRUGO, root, tmp, &drm_debugfs_fops); if (!ent) { + char name[64]; + strncpy(name, root->d_name.name, + min(root->d_name.len, 64U)); DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s/%s\n", name, files[i].name); kfree(tmp); From 69ad2ffe57a81c7b700e337f066b8b39c3655910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Luis=20V=C3=83=C6=92=C3=86=E2=80=99=C3=83?= =?UTF-8?q?=E2=80=9A=C3=82=C2=A1zquez=20Cao?= Date: Thu, 28 Jul 2011 05:44:04 +0000 Subject: [PATCH 47/55] drm/radeon: clean reg header files Reg header files are generated so they are not cleaned automagically. They need to be added to the clean-files list. Signed-off-by: Fernando Luis Vazquez Cao Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index 3896ef811102..9f363e0c4b60 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -5,6 +5,7 @@ ccflags-y := -Iinclude/drm hostprogs-y := mkregtable +clean-files := rn50_reg_safe.h r100_reg_safe.h r200_reg_safe.h rv515_reg_safe.h r300_reg_safe.h r420_reg_safe.h rs600_reg_safe.h r600_reg_safe.h evergreen_reg_safe.h cayman_reg_safe.h quiet_cmd_mkregtable = MKREGTABLE $@ cmd_mkregtable = $(obj)/mkregtable $< > $@ From 816985d4f96cda5159b6b65f3442964725030ae9 Mon Sep 17 00:00:00 2001 From: Bojan Prtvar Date: Wed, 3 Aug 2011 19:51:19 +0000 Subject: [PATCH 48/55] drm/radeon: fix potential NULL dereference in drivers/gpu/drm/radeon/atom.c kzalloc() can return NULL, so I added check for it Signed-off-by: Bojan Prtvar Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/atom.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c index ebdb0fdb8348..e88c64417a8a 100644 --- a/drivers/gpu/drm/radeon/atom.c +++ b/drivers/gpu/drm/radeon/atom.c @@ -1245,6 +1245,9 @@ struct atom_context *atom_parse(struct card_info *card, void *bios) char name[512]; int i; + if (!ctx) + return NULL; + ctx->card = card; ctx->bios = bios; From e1c44acc8cabda6b38864f25e809b306c4d2d790 Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Thu, 4 Aug 2011 05:41:00 +0000 Subject: [PATCH 49/55] drm: Fix irq install error handling The registered irq should be unregistered by free_irq() if irq_postinstall() returns the error after request_irq() is called successfully. [airlied: add vga switcheroo disable] Signed-off-by: Joonyoung Shim Signed-off-by: Kyungmin Park Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_irq.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 2022a5c966bb..9be574fbcc2f 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -368,6 +368,9 @@ int drm_irq_install(struct drm_device *dev) mutex_lock(&dev->struct_mutex); dev->irq_enabled = 0; mutex_unlock(&dev->struct_mutex); + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + vga_client_register(dev->pdev, NULL, NULL, NULL); + free_irq(drm_dev_to_irq(dev), dev); } return ret; From 5037f8acf448dd0de9868dc7410f45879d3d1a1b Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Thu, 4 Aug 2011 05:41:01 +0000 Subject: [PATCH 50/55] drm: Add NULL check about irq functions The struct drm_driver has some function pointers for irq. They are gpu specific and some functions aren't essential things. This can prevents creation of unnecessary dummy function for irq. Signed-off-by: Joonyoung Shim Signed-off-by: Kyungmin Park Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_irq.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 9be574fbcc2f..3830e9e478c0 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -291,11 +291,14 @@ static void drm_irq_vgaarb_nokms(void *cookie, bool state) if (!dev->irq_enabled) return; - if (state) - dev->driver->irq_uninstall(dev); - else { - dev->driver->irq_preinstall(dev); - dev->driver->irq_postinstall(dev); + if (state) { + if (dev->driver->irq_uninstall) + dev->driver->irq_uninstall(dev); + } else { + if (dev->driver->irq_preinstall) + dev->driver->irq_preinstall(dev); + if (dev->driver->irq_postinstall) + dev->driver->irq_postinstall(dev); } } @@ -338,7 +341,8 @@ int drm_irq_install(struct drm_device *dev) DRM_DEBUG("irq=%d\n", drm_dev_to_irq(dev)); /* Before installing handler */ - dev->driver->irq_preinstall(dev); + if (dev->driver->irq_preinstall) + dev->driver->irq_preinstall(dev); /* Install handler */ if (drm_core_check_feature(dev, DRIVER_IRQ_SHARED)) @@ -363,7 +367,9 @@ int drm_irq_install(struct drm_device *dev) vga_client_register(dev->pdev, (void *)dev, drm_irq_vgaarb_nokms, NULL); /* After installing handler */ - ret = dev->driver->irq_postinstall(dev); + if (dev->driver->irq_postinstall) + ret = dev->driver->irq_postinstall(dev); + if (ret < 0) { mutex_lock(&dev->struct_mutex); dev->irq_enabled = 0; @@ -416,7 +422,8 @@ int drm_irq_uninstall(struct drm_device *dev) if (!drm_core_check_feature(dev, DRIVER_MODESET)) vga_client_register(dev->pdev, NULL, NULL, NULL); - dev->driver->irq_uninstall(dev); + if (dev->driver->irq_uninstall) + dev->driver->irq_uninstall(dev); free_irq(drm_dev_to_irq(dev), dev); From 051963d4832ed61e5ae74f5330b0a94489e101b9 Mon Sep 17 00:00:00 2001 From: Thomas Reim Date: Fri, 29 Jul 2011 14:28:57 +0000 Subject: [PATCH 51/55] drm: Separate EDID Header Check from EDID Block Check Provides function drm_edid_header_is_valid() for EDID header check and replaces EDID header check part of function drm_edid_block_valid() by a call of drm_edid_header_is_valid(). This is a prerequisite to extend DDC probing, e. g. in function radeon_ddc_probe() for Radeon devices, by a central EDID header check. Tested for kernel 2.6.35, 2.6.38 and 3.0 Cc: Signed-off-by: Thomas Reim Reviewed-by: Alex Deucher Acked-by: Stephen Michaels Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_edid.c | 24 ++++++++++++++++++------ include/drm/drm_crtc.h | 1 + 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 7496d245f28c..7425e5c9bd75 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -127,6 +127,23 @@ static const u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; + /* + * Sanity check the header of the base EDID block. Return 8 if the header + * is perfect, down to 0 if it's totally wrong. + */ +int drm_edid_header_is_valid(const u8 *raw_edid) +{ + int i, score = 0; + + for (i = 0; i < sizeof(edid_header); i++) + if (raw_edid[i] == edid_header[i]) + score++; + + return score; +} +EXPORT_SYMBOL(drm_edid_header_is_valid); + + /* * Sanity check the EDID block (base or extension). Return 0 if the block * doesn't check out, or 1 if it's valid. @@ -139,12 +156,7 @@ drm_edid_block_valid(u8 *raw_edid) struct edid *edid = (struct edid *)raw_edid; if (raw_edid[0] == 0x00) { - int score = 0; - - for (i = 0; i < sizeof(edid_header); i++) - if (raw_edid[i] == edid_header[i]) - score++; - + int score = drm_edid_header_is_valid(raw_edid); if (score == 8) ; else if (score >= 6) { DRM_DEBUG("Fixing EDID header, your hardware may be failing\n"); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index d515bc8f4ba7..44335e57eaaa 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -804,6 +804,7 @@ extern struct drm_display_mode *drm_gtf_mode_complex(struct drm_device *dev, extern int drm_add_modes_noedid(struct drm_connector *connector, int hdisplay, int vdisplay); +extern int drm_edid_header_is_valid(const u8 *raw_edid); extern bool drm_edid_is_valid(struct edid *edid); struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev, int hsize, int vsize, int fresh); From e384fab8c6f3ca88600bcb2ebdf0eb2f90864fab Mon Sep 17 00:00:00 2001 From: Thomas Reim Date: Fri, 29 Jul 2011 14:28:58 +0000 Subject: [PATCH 52/55] drm/radeon: Extended DDC Probing for Connectors with Improperly Wired DDC Lines (here: Asus M2A-VM HDMI) Some integrated ATI Radeon chipset implementations with add-on HDMI card (e. g. Asus M2A-VM HDMI) indicate the availability of a DDC even when the add-on card is not plugged in or HDMI is disabled in BIOS setup. In this case, drm_get_edid() and drm_edid_block_valid() periodically dump data and kernel errors into system log files and onto terminals. For these connectors DDC probing is extended by a check for a correct EDID header. Only in case a valid EDID header is also found, the (HDMI or DVI) connector will be used by the Radeon driver. This prevents the kernel driver from useless flooding of logs and terminal sessions with EDID dumps and error messages. This patch adds a flag 'requires_extended_probe' to the radeon_connector structure. In function radeon_connector_needs_extended_probe() this flag can be set on a chipset family/vendor/connector type specific basis. In addition, function radeon_ddc_probe() has been adapted to perform extended DDC probing if required by the connector's flag. Requires function drm_edid_header_is_valid() in DRM module provided by [PATCH] drm: Separate EDID Header Check from EDID Block Check. Tested for kernel 2.6.35, 2.6.38 and 3.0 on Asus M2A-VM HDMI board BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=668196 BugLink: http://bugs.launchpad.net/bugs/7228066 Cc: Signed-off-by: Thomas Reim Reviewed-by: Alex Deucher Acked-by: Stephen Michaels Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/radeon_connectors.c | 45 ++++++++++++++++++++-- drivers/gpu/drm/radeon/radeon_display.c | 9 +++++ drivers/gpu/drm/radeon/radeon_i2c.c | 32 +++++++++++---- drivers/gpu/drm/radeon/radeon_mode.h | 6 ++- 4 files changed, 80 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 9792d4ffdc86..6a0f1befbc15 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -430,6 +430,36 @@ int radeon_connector_set_property(struct drm_connector *connector, struct drm_pr return 0; } +/* + * Some integrated ATI Radeon chipset implementations (e. g. + * Asus M2A-VM HDMI) may indicate the availability of a DDC, + * even when there's no monitor connected. For these connectors + * following DDC probe extension will be applied: check also for the + * availability of EDID with at least a correct EDID header. Only then, + * DDC is assumed to be available. This prevents drm_get_edid() and + * drm_edid_block_valid() from periodically dumping data and kernel + * errors into the logs and onto the terminal. + */ +static bool radeon_connector_needs_extended_probe(struct radeon_device *dev, + uint32_t supported_device, + int connector_type) +{ + /* Asus M2A-VM HDMI board sends data to i2c bus even, + * if HDMI add-on card is not plugged in or HDMI is disabled in + * BIOS. Valid DDC can only be assumed, if also a valid EDID header + * can be retrieved via i2c bus during DDC probe */ + if ((dev->pdev->device == 0x791e) && + (dev->pdev->subsystem_vendor == 0x1043) && + (dev->pdev->subsystem_device == 0x826d)) { + if ((connector_type == DRM_MODE_CONNECTOR_HDMIA) && + (supported_device == ATOM_DEVICE_DFP2_SUPPORT)) + return true; + } + + /* Default: no EDID header probe required for DDC probing */ + return false; +} + static void radeon_fixup_lvds_native_mode(struct drm_encoder *encoder, struct drm_connector *connector) { @@ -661,7 +691,8 @@ radeon_vga_detect(struct drm_connector *connector, bool force) ret = connector_status_disconnected; if (radeon_connector->ddc_bus) - dret = radeon_ddc_probe(radeon_connector); + dret = radeon_ddc_probe(radeon_connector, + radeon_connector->requires_extended_probe); if (dret) { if (radeon_connector->edid) { kfree(radeon_connector->edid); @@ -833,7 +864,8 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) bool dret = false; if (radeon_connector->ddc_bus) - dret = radeon_ddc_probe(radeon_connector); + dret = radeon_ddc_probe(radeon_connector, + radeon_connector->requires_extended_probe); if (dret) { if (radeon_connector->edid) { kfree(radeon_connector->edid); @@ -1251,7 +1283,8 @@ radeon_dp_detect(struct drm_connector *connector, bool force) if (radeon_dp_getdpcd(radeon_connector)) ret = connector_status_connected; } else { - if (radeon_ddc_probe(radeon_connector)) + if (radeon_ddc_probe(radeon_connector, + radeon_connector->requires_extended_probe)) ret = connector_status_connected; } } @@ -1406,6 +1439,9 @@ radeon_add_atom_connector(struct drm_device *dev, radeon_connector->shared_ddc = shared_ddc; radeon_connector->connector_object_id = connector_object_id; radeon_connector->hpd = *hpd; + radeon_connector->requires_extended_probe = + radeon_connector_needs_extended_probe(rdev, supported_device, + connector_type); radeon_connector->router = *router; if (router->ddc_valid || router->cd_valid) { radeon_connector->router_bus = radeon_i2c_lookup(rdev, &router->i2c_info); @@ -1752,6 +1788,9 @@ radeon_add_legacy_connector(struct drm_device *dev, radeon_connector->devices = supported_device; radeon_connector->connector_object_id = connector_object_id; radeon_connector->hpd = *hpd; + radeon_connector->requires_extended_probe = + radeon_connector_needs_extended_probe(rdev, supported_device, + connector_type); switch (connector_type) { case DRM_MODE_CONNECTOR_VGA: drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 28f4655905bc..1a858944e4f3 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -751,8 +751,17 @@ static int radeon_ddc_dump(struct drm_connector *connector) if (!radeon_connector->ddc_bus) return -1; edid = drm_get_edid(connector, &radeon_connector->ddc_bus->adapter); + /* Log EDID retrieval status here. In particular with regard to + * connectors with requires_extended_probe flag set, that will prevent + * function radeon_dvi_detect() to fetch EDID on this connector, + * as long as there is no valid EDID header found */ if (edid) { + DRM_INFO("Radeon display connector %s: Found valid EDID", + drm_get_connector_name(connector)); kfree(edid); + } else { + DRM_INFO("Radeon display connector %s: No monitor connected or invalid EDID", + drm_get_connector_name(connector)); } return ret; } diff --git a/drivers/gpu/drm/radeon/radeon_i2c.c b/drivers/gpu/drm/radeon/radeon_i2c.c index 781196db792f..6c111c1fa3f9 100644 --- a/drivers/gpu/drm/radeon/radeon_i2c.c +++ b/drivers/gpu/drm/radeon/radeon_i2c.c @@ -32,17 +32,17 @@ * radeon_ddc_probe * */ -bool radeon_ddc_probe(struct radeon_connector *radeon_connector) +bool radeon_ddc_probe(struct radeon_connector *radeon_connector, bool requires_extended_probe) { - u8 out_buf[] = { 0x0, 0x0}; - u8 buf[2]; + u8 out = 0x0; + u8 buf[8]; int ret; struct i2c_msg msgs[] = { { .addr = 0x50, .flags = 0, .len = 1, - .buf = out_buf, + .buf = &out, }, { .addr = 0x50, @@ -52,15 +52,31 @@ bool radeon_ddc_probe(struct radeon_connector *radeon_connector) } }; + /* Read 8 bytes from i2c for extended probe of EDID header */ + if (requires_extended_probe) + msgs[1].len = 8; + /* on hw with routers, select right port */ if (radeon_connector->router.ddc_valid) radeon_router_select_ddc_port(radeon_connector); ret = i2c_transfer(&radeon_connector->ddc_bus->adapter, msgs, 2); - if (ret == 2) - return true; - - return false; + if (ret != 2) + /* Couldn't find an accessible DDC on this connector */ + return false; + if (requires_extended_probe) { + /* Probe also for valid EDID header + * EDID header starts with: + * 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00. + * Only the first 6 bytes must be valid as + * drm_edid_block_valid() can fix the last 2 bytes */ + if (drm_edid_header_is_valid(buf) < 6) { + /* Couldn't find an accessible EDID on this + * connector */ + return false; + } + } + return true; } /* bit banging i2c */ diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 6df4e3cec0c2..d09031c03e26 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -438,6 +438,9 @@ struct radeon_connector { struct radeon_i2c_chan *ddc_bus; /* some systems have an hdmi and vga port with a shared ddc line */ bool shared_ddc; + /* for some Radeon chip families we apply an additional EDID header + check as part of the DDC probe */ + bool requires_extended_probe; bool use_digital; /* we need to mind the EDID between detect and get modes due to analog/digital/tvencoder */ @@ -514,7 +517,8 @@ extern void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c, u8 val); extern void radeon_router_select_ddc_port(struct radeon_connector *radeon_connector); extern void radeon_router_select_cd_port(struct radeon_connector *radeon_connector); -extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector); +extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector, + bool requires_extended_probe); extern int radeon_ddc_get_modes(struct radeon_connector *radeon_connector); extern struct drm_encoder *radeon_best_encoder(struct drm_connector *connector); From d522d9cc5bdd41214084383fc3e6d882f6916a78 Mon Sep 17 00:00:00 2001 From: Thomas Reim Date: Fri, 29 Jul 2011 14:28:59 +0000 Subject: [PATCH 53/55] drm/radeon: Log Subsystem Vendor and Device Information Log PCI subsystem vendor and subsystem device ID in addition to PCI vendor and device ID during kernel mode initialisation. This helps to better identify radeon devices of third-party vendors, e. g. for bug analysis. Tested for kernel 2.6.35, 2.6.38 and 3.0 on Asus M2A-VM HDMI board Cc: Signed-off-by: Thomas Reim Reviewed-by: Alex Deucher Acked-by: Stephen Michaels Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/radeon_device.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 7cfaa7e2f3b5..440e6ecccc40 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -704,8 +704,9 @@ int radeon_device_init(struct radeon_device *rdev, rdev->gpu_lockup = false; rdev->accel_working = false; - DRM_INFO("initializing kernel modesetting (%s 0x%04X:0x%04X).\n", - radeon_family_name[rdev->family], pdev->vendor, pdev->device); + DRM_INFO("initializing kernel modesetting (%s 0x%04X:0x%04X 0x%04X:0x%04X).\n", + radeon_family_name[rdev->family], pdev->vendor, pdev->device, + pdev->subsystem_vendor, pdev->subsystem_device); /* mutex initialization are all done here so we * can recall function without having locking issues */ From a81b31e9fc98e067b7e7f1244861c97e44268e2d Mon Sep 17 00:00:00 2001 From: Thomas Reim Date: Fri, 29 Jul 2011 14:29:00 +0000 Subject: [PATCH 54/55] drm/radeon: Extended DDC Probing for ECS A740GM-M DVI-D Connector ECS A740GM-M with ATI RADEON 2100 sends data to i2c bus for a DVI connector that is not implemented/existent on the board. Fix by applying extented DDC probing for this connector. Requires [PATCH] drm/radeon: Extended DDC Probing for Connectors with Improperly Wired DDC Lines Tested for kernel 2.6.38 on Asus ECS A740GM-M board BugLink: http://bugs.launchpad.net/bugs/810926 Cc: Signed-off-by: Thomas Reim Reviewed-by: Alex Deucher Acked-by: Stephen Michaels Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/radeon_connectors.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 6a0f1befbc15..6d6b5f16bc09 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -455,6 +455,15 @@ static bool radeon_connector_needs_extended_probe(struct radeon_device *dev, (supported_device == ATOM_DEVICE_DFP2_SUPPORT)) return true; } + /* ECS A740GM-M with ATI RADEON 2100 sends data to i2c bus + * for a DVI connector that is not implemented */ + if ((dev->pdev->device == 0x796e) && + (dev->pdev->subsystem_vendor == 0x1019) && + (dev->pdev->subsystem_device == 0x2615)) { + if ((connector_type == DRM_MODE_CONNECTOR_DVID) && + (supported_device == ATOM_DEVICE_DFP2_SUPPORT)) + return true; + } /* Default: no EDID header probe required for DDC probing */ return false; From 39060a07781b4930656752943cf5d66376d0533c Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 5 Aug 2011 10:56:29 +0100 Subject: [PATCH 55/55] Revert "drm/i915: Try enabling RC6 by default (again)" This reverts commit 4e20fa65a3ea789510eed1a15deb9e8aab2b8202. Francesco Allertsen still has a broken configuration. Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 60e4b9ef618b..ce045a8cf82c 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -62,7 +62,7 @@ module_param_named(semaphores, i915_semaphores, int, 0600); MODULE_PARM_DESC(semaphores, "Use semaphores for inter-ring sync (default: false)"); -unsigned int i915_enable_rc6 __read_mostly = 1; +unsigned int i915_enable_rc6 __read_mostly = 0; module_param_named(i915_enable_rc6, i915_enable_rc6, int, 0600); MODULE_PARM_DESC(i915_enable_rc6, "Enable power-saving render C-state 6 (default: true)");