From 17793c9a4659b272a9f892d44940062ed8b5fd0e Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Fri, 7 Mar 2014 08:30:36 +0000 Subject: [PATCH 01/75] drm/i915: Process page flags once rather than per pwrite/pread We used to lock individual pages inside the buffer object and so needed to update the page flags every time. However, we now pin the pages into the object for the duration of the pwrite/pread (and hopefully much longer) and so we can forgo the flag updates until we release all the pages. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Reviewed-by: Brad Volkin <bradley.d.volkin@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_gem.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 177c20722656e..da5d9ca98024d 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -510,12 +510,10 @@ i915_gem_shmem_pread(struct drm_device *dev, mutex_lock(&dev->struct_mutex); -next_page: - mark_page_accessed(page); - if (ret) goto out; +next_page: remain -= page_length; user_data += page_length; offset += page_length; @@ -831,13 +829,10 @@ i915_gem_shmem_pwrite(struct drm_device *dev, mutex_lock(&dev->struct_mutex); -next_page: - set_page_dirty(page); - mark_page_accessed(page); - if (ret) goto out; +next_page: remain -= page_length; user_data += page_length; offset += page_length; From c2831a94b5e77a407db0708816949d4a87416a8e Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Fri, 7 Mar 2014 08:30:37 +0000 Subject: [PATCH 02/75] drm/i915: Do not force non-caching copies for pwrite along shmem path We don't always want to write into main memory with pwrite. The shmem fast path in particular is used for memory that is cacheable - under such circumstances forcing the cache eviction is undesirable. As we will always flush the cache when targeting incoherent buffers, we can rely on that second pass to apply the cache coherency rules and so benefit from in-cache copies otherwise. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Reviewed-by: Brad Volkin <bradley.d.volkin@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_gem.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index da5d9ca98024d..92b0b4164b1da 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -693,9 +693,8 @@ shmem_pwrite_fast(struct page *page, int shmem_page_offset, int page_length, if (needs_clflush_before) drm_clflush_virt_range(vaddr + shmem_page_offset, page_length); - ret = __copy_from_user_inatomic_nocache(vaddr + shmem_page_offset, - user_data, - page_length); + ret = __copy_from_user_inatomic(vaddr + shmem_page_offset, + user_data, page_length); if (needs_clflush_after) drm_clflush_virt_range(vaddr + shmem_page_offset, page_length); From 46f297fb83d4f9a6f6891964beb184664341a28b Mon Sep 17 00:00:00 2001 From: Jesse Barnes <jbarnes@virtuousgeek.org> Date: Fri, 7 Mar 2014 08:57:48 -0800 Subject: [PATCH 03/75] drm/i915: add plane_config fetching infrastructure v2 Early at init time, we can try to read out the plane config structure and try to preserve it if possible. v2: alloc fb obj at init time after fetching plane config Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_drv.h | 3 + drivers/gpu/drm/i915/intel_display.c | 92 ++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_drv.h | 9 +++ 3 files changed, 104 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 7b6dfdfb2d8d1..bfb537942dbef 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -406,6 +406,7 @@ struct drm_i915_error_state { struct intel_connector; struct intel_crtc_config; +struct intel_plane_config; struct intel_crtc; struct intel_limit; struct dpll; @@ -444,6 +445,8 @@ struct drm_i915_display_funcs { * fills out the pipe-config with the hw state. */ bool (*get_pipe_config)(struct intel_crtc *, struct intel_crtc_config *); + void (*get_plane_config)(struct intel_crtc *, + struct intel_plane_config *); int (*crtc_mode_set)(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 56edc84098565..482b8d4bd94a8 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2047,6 +2047,70 @@ unsigned long intel_gen4_compute_page_offset(int *x, int *y, } } +int intel_format_to_fourcc(int format) +{ + switch (format) { + case DISPPLANE_8BPP: + return DRM_FORMAT_C8; + case DISPPLANE_BGRX555: + return DRM_FORMAT_XRGB1555; + case DISPPLANE_BGRX565: + return DRM_FORMAT_RGB565; + default: + case DISPPLANE_BGRX888: + return DRM_FORMAT_XRGB8888; + case DISPPLANE_RGBX888: + return DRM_FORMAT_XBGR8888; + case DISPPLANE_BGRX101010: + return DRM_FORMAT_XRGB2101010; + case DISPPLANE_RGBX101010: + return DRM_FORMAT_XBGR2101010; + } +} + +static void intel_alloc_plane_obj(struct intel_crtc *crtc, + struct intel_plane_config *plane_config) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_gem_object *obj = NULL; + struct drm_mode_fb_cmd2 mode_cmd = { 0 }; + u32 base = plane_config->base; + + if (!plane_config->fb) + return; + + obj = i915_gem_object_create_stolen_for_preallocated(dev, base, base, + plane_config->size); + if (!obj) + return; + + if (plane_config->tiled) { + obj->tiling_mode = I915_TILING_X; + obj->stride = plane_config->fb->base.pitches[0]; + } + + mode_cmd.pixel_format = plane_config->fb->base.pixel_format; + mode_cmd.width = plane_config->fb->base.width; + mode_cmd.height = plane_config->fb->base.height; + mode_cmd.pitches[0] = plane_config->fb->base.pitches[0]; + + mutex_lock(&dev->struct_mutex); + + if (intel_framebuffer_init(dev, plane_config->fb, &mode_cmd, obj)) { + DRM_DEBUG_KMS("intel fb init failed\n"); + goto out_unref_obj; + } + + mutex_unlock(&dev->struct_mutex); + DRM_DEBUG_KMS("plane fb obj %p\n", plane_config->fb->obj); + return; + +out_unref_obj: + drm_gem_object_unreference(&obj->base); + mutex_unlock(&dev->struct_mutex); + +} + static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb, int x, int y) { @@ -11033,6 +11097,7 @@ void intel_modeset_init(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; int sprite, ret; enum pipe pipe; + struct intel_crtc *crtc; drm_mode_config_init(dev); @@ -11095,6 +11160,33 @@ void intel_modeset_init(struct drm_device *dev) mutex_lock(&dev->mode_config.mutex); intel_modeset_setup_hw_state(dev, false); mutex_unlock(&dev->mode_config.mutex); + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, + base.head) { + if (!crtc->active) + continue; + +#if IS_ENABLED(CONFIG_FB) + /* + * We don't have a good way of freeing the buffer w/o the FB + * layer owning it... + * Note that reserving the BIOS fb up front prevents us + * from stuffing other stolen allocations like the ring + * on top. This prevents some ugliness at boot time, and + * can even allow for smooth boot transitions if the BIOS + * fb is large enough for the active pipe configuration. + */ + if (dev_priv->display.get_plane_config) { + dev_priv->display.get_plane_config(crtc, + &crtc->plane_config); + /* + * If the fb is shared between multiple heads, we'll + * just get the first one. + */ + intel_alloc_plane_obj(crtc, &crtc->plane_config); + } +#endif + } } static void diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 9c7090590776b..48af57bbc73df 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -218,6 +218,13 @@ typedef struct dpll { int p; } intel_clock_t; +struct intel_plane_config { + struct intel_framebuffer *fb; /* ends up managed by intel_fbdev.c */ + bool tiled; + int size; + u32 base; +}; + struct intel_crtc_config { /** * quirks - bitfield with hw state readout quirks @@ -366,6 +373,7 @@ struct intel_crtc { int16_t cursor_width, cursor_height; bool cursor_visible; + struct intel_plane_config plane_config; struct intel_crtc_config config; struct intel_crtc_config *new_config; bool new_enabled; @@ -740,6 +748,7 @@ intel_display_port_power_domain(struct intel_encoder *intel_encoder); int valleyview_get_vco(struct drm_i915_private *dev_priv); void intel_mode_from_pipe_config(struct drm_display_mode *mode, struct intel_crtc_config *pipe_config); +int intel_format_to_fourcc(int format); /* intel_dp.c */ void intel_dp_init(struct drm_device *dev, int output_reg, enum port port); From 1ad292b51e358c8b6e9b8966889c21f1fe705489 Mon Sep 17 00:00:00 2001 From: Jesse Barnes <jbarnes@virtuousgeek.org> Date: Fri, 7 Mar 2014 08:57:49 -0800 Subject: [PATCH 04/75] drm/i915: get_plane_config for i9xx v13 Read out the current plane configuration at init time into a new plane_config structure. This allows us to track any existing framebuffers attached to the plane and potentially re-use them in our fbdev code for a smooth handoff. v2: update for new pitch_for_width function (Jesse) comment how get_plane_config works with shared fbs (Jesse) v3: s/ARGB/XRGB (Ville) use pipesrc width/height (Ville) fix fourcc comment (Bob) use drm_format_plane_cpp (Ville) v4: use fb for tracking fb data object (Ville) v5: fix up gen2 pitch limits (Ville) v6: read out stride as well (Daniel) v7: split out init ordering changes (Daniel) don't fetch config if !CONFIG_FB v8: use proper height in get_plane_config (Chris) v9: fix CONFIG_FB check for modular configs (Jani) v10: add comment about stolen allocation stomping v11: drop hw state readout hunk (Daniel) v12: handle tiled BIOS fbs (Kristian) pull out common bits (Jesse) v13: move fb obj alloc out to _init Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_display.c | 63 ++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 482b8d4bd94a8..4476497757798 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5669,6 +5669,67 @@ static void vlv_crtc_clock_get(struct intel_crtc *crtc, pipe_config->port_clock = clock.dot / 5; } +static void i9xx_get_plane_config(struct intel_crtc *crtc, + struct intel_plane_config *plane_config) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 val, base, offset; + int pipe = crtc->pipe, plane = crtc->plane; + int fourcc, pixel_format; + int aligned_height; + + plane_config->fb = kzalloc(sizeof(*plane_config->fb), GFP_KERNEL); + if (!plane_config->fb) { + DRM_DEBUG_KMS("failed to alloc fb\n"); + return; + } + + val = I915_READ(DSPCNTR(plane)); + + if (INTEL_INFO(dev)->gen >= 4) + if (val & DISPPLANE_TILED) + plane_config->tiled = true; + + pixel_format = val & DISPPLANE_PIXFORMAT_MASK; + fourcc = intel_format_to_fourcc(pixel_format); + plane_config->fb->base.pixel_format = fourcc; + plane_config->fb->base.bits_per_pixel = + drm_format_plane_cpp(fourcc, 0) * 8; + + if (INTEL_INFO(dev)->gen >= 4) { + if (plane_config->tiled) + offset = I915_READ(DSPTILEOFF(plane)); + else + offset = I915_READ(DSPLINOFF(plane)); + base = I915_READ(DSPSURF(plane)) & 0xfffff000; + } else { + base = I915_READ(DSPADDR(plane)); + } + plane_config->base = base; + + val = I915_READ(PIPESRC(pipe)); + plane_config->fb->base.width = ((val >> 16) & 0xfff) + 1; + plane_config->fb->base.height = ((val >> 0) & 0xfff) + 1; + + val = I915_READ(DSPSTRIDE(pipe)); + plane_config->fb->base.pitches[0] = val & 0xffffff80; + + aligned_height = intel_align_height(dev, plane_config->fb->base.height, + plane_config->tiled); + + plane_config->size = ALIGN(plane_config->fb->base.pitches[0] * + aligned_height, PAGE_SIZE); + + DRM_DEBUG_KMS("pipe/plane %d/%d with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n", + pipe, plane, plane_config->fb->base.width, + plane_config->fb->base.height, + plane_config->fb->base.bits_per_pixel, base, + plane_config->fb->base.pitches[0], + plane_config->size); + +} + static bool i9xx_get_pipe_config(struct intel_crtc *crtc, struct intel_crtc_config *pipe_config) { @@ -10828,6 +10889,7 @@ static void intel_init_display(struct drm_device *dev) dev_priv->display.update_plane = ironlake_update_plane; } else if (IS_VALLEYVIEW(dev)) { dev_priv->display.get_pipe_config = i9xx_get_pipe_config; + dev_priv->display.get_plane_config = i9xx_get_plane_config; dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set; dev_priv->display.crtc_enable = valleyview_crtc_enable; dev_priv->display.crtc_disable = i9xx_crtc_disable; @@ -10835,6 +10897,7 @@ static void intel_init_display(struct drm_device *dev) dev_priv->display.update_plane = i9xx_update_plane; } else { dev_priv->display.get_pipe_config = i9xx_get_pipe_config; + dev_priv->display.get_plane_config = i9xx_get_plane_config; dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set; dev_priv->display.crtc_enable = i9xx_crtc_enable; dev_priv->display.crtc_disable = i9xx_crtc_disable; From 4c6baa595f4e8516bb9cf0081765f90856aa2fe3 Mon Sep 17 00:00:00 2001 From: Jesse Barnes <jbarnes@virtuousgeek.org> Date: Fri, 7 Mar 2014 08:57:50 -0800 Subject: [PATCH 05/75] drm/i915: get_plane_config support for ILK+ v3 This should allow BIOS fb inheritance to work on ILK+ machines too. v2: handle tiled BIOS fbs (Kristian) split out common bits (Jesse) v3: alloc fb obj out in _init Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_display.c | 62 ++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 4476497757798..9fc18770d2072 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6677,6 +6677,66 @@ static void ironlake_get_pfit_config(struct intel_crtc *crtc, } } +static void ironlake_get_plane_config(struct intel_crtc *crtc, + struct intel_plane_config *plane_config) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 val, base, offset; + int pipe = crtc->pipe, plane = crtc->plane; + int fourcc, pixel_format; + int aligned_height; + + plane_config->fb = kzalloc(sizeof(*plane_config->fb), GFP_KERNEL); + if (!plane_config->fb) { + DRM_DEBUG_KMS("failed to alloc fb\n"); + return; + } + + val = I915_READ(DSPCNTR(plane)); + + if (INTEL_INFO(dev)->gen >= 4) + if (val & DISPPLANE_TILED) + plane_config->tiled = true; + + pixel_format = val & DISPPLANE_PIXFORMAT_MASK; + fourcc = intel_format_to_fourcc(pixel_format); + plane_config->fb->base.pixel_format = fourcc; + plane_config->fb->base.bits_per_pixel = + drm_format_plane_cpp(fourcc, 0) * 8; + + base = I915_READ(DSPSURF(plane)) & 0xfffff000; + if (IS_HASWELL(dev) || IS_BROADWELL(dev)) { + offset = I915_READ(DSPOFFSET(plane)); + } else { + if (plane_config->tiled) + offset = I915_READ(DSPTILEOFF(plane)); + else + offset = I915_READ(DSPLINOFF(plane)); + } + plane_config->base = base; + + val = I915_READ(PIPESRC(pipe)); + plane_config->fb->base.width = ((val >> 16) & 0xfff) + 1; + plane_config->fb->base.height = ((val >> 0) & 0xfff) + 1; + + val = I915_READ(DSPSTRIDE(pipe)); + plane_config->fb->base.pitches[0] = val & 0xffffff80; + + aligned_height = intel_align_height(dev, plane_config->fb->base.height, + plane_config->tiled); + + plane_config->size = ALIGN(plane_config->fb->base.pitches[0] * + aligned_height, PAGE_SIZE); + + DRM_DEBUG_KMS("pipe/plane %d/%d with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n", + pipe, plane, plane_config->fb->base.width, + plane_config->fb->base.height, + plane_config->fb->base.bits_per_pixel, base, + plane_config->fb->base.pitches[0], + plane_config->size); +} + static bool ironlake_get_pipe_config(struct intel_crtc *crtc, struct intel_crtc_config *pipe_config) { @@ -10875,6 +10935,7 @@ static void intel_init_display(struct drm_device *dev) if (HAS_DDI(dev)) { dev_priv->display.get_pipe_config = haswell_get_pipe_config; + dev_priv->display.get_plane_config = ironlake_get_plane_config; dev_priv->display.crtc_mode_set = haswell_crtc_mode_set; dev_priv->display.crtc_enable = haswell_crtc_enable; dev_priv->display.crtc_disable = haswell_crtc_disable; @@ -10882,6 +10943,7 @@ static void intel_init_display(struct drm_device *dev) dev_priv->display.update_plane = ironlake_update_plane; } else if (HAS_PCH_SPLIT(dev)) { dev_priv->display.get_pipe_config = ironlake_get_pipe_config; + dev_priv->display.get_plane_config = ironlake_get_plane_config; dev_priv->display.crtc_mode_set = ironlake_crtc_mode_set; dev_priv->display.crtc_enable = ironlake_crtc_enable; dev_priv->display.crtc_disable = ironlake_crtc_disable; From d978ef14456a38034f6c0e94a794129501f89200 Mon Sep 17 00:00:00 2001 From: Jesse Barnes <jbarnes@virtuousgeek.org> Date: Fri, 7 Mar 2014 08:57:51 -0800 Subject: [PATCH 06/75] drm/i915: Wrap the preallocated BIOS framebuffer and preserve for KMS fbcon v12 Retrieve current framebuffer config info from the regs and create an fb object for the buffer the BIOS or boot loader left us. This should allow for smooth transitions to userspace apps once we finish the initial configuration construction. v2: check for non-native modes and adjust (Jesse) fixup aperture and cmap frees (Imre) use unlocked unref if init_bios fails (Jesse) fix curly brace around DSPADDR check (Imre) comment failure path for pin_and_fence (Imre) v3: fixup fixup of aperture frees (Chris) v4: update to current bits (locking & pin_and_fence hack) (Jesse) v5: move fb config fetch to display code (Jesse) re-order hw state readout on initial load to suit fb inherit (Jesse) re-add pin_and_fence in fbdev code to make sure we refcount properly (Je v6: rename to plane_config (Daniel) check for valid object when initializing BIOS fb (Jesse) split from plane_config readout and other display changes (Jesse) drop use_bios_fb option (Chris) update comments (Jesse) rework fbdev_init_bios for clarity (Jesse) drop fb obj ref under lock (Chris) v7: use fb object from plane_config instead (Ville) take ref on fb object (Jesse) v8: put under i915_fastboot option (Jesse) fix fb ptr checking (Jesse) inform drm_fb_helper if we fail to enable a connector (Jesse) drop unnecessary enabled[] modifications in failure cases (Chris) split from BIOS connector config readout (Daniel) don't memset the fb buffer if preallocated (Chris) alloc ifbdev up front and pass to init_bios (Chris) check for bad ifbdev in restore_mode too (Chris) v9: fix up !fastboot bpp setting (Jesse) fix up !fastboot helper alloc (Jesse) make sure BIOS fb is sufficient for biggest active pipe (Jesse) v10:fix up size calculation for proposed fbs (Chris) go back to two pass pipe fb assignment (Chris) add warning for active pipes w/o fbs (Chris) clean up num_pipes checks in fbdev_init and fbdev_restore_mode (Chris) move i915.fastboot into fbdev_init (Chris) v11:make BIOS connector config usage unconditional (Daniel) v12:fix up fb vs pipe size checking (Chris) Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_display.c | 1 - drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_fbdev.c | 174 +++++++++++++++++++++++++-- 3 files changed, 166 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9fc18770d2072..d3c29116711fa 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2108,7 +2108,6 @@ static void intel_alloc_plane_obj(struct intel_crtc *crtc, out_unref_obj: drm_gem_object_unreference(&obj->base); mutex_unlock(&dev->struct_mutex); - } static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb, diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 48af57bbc73df..75baa64a357d0 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -113,6 +113,7 @@ struct intel_fbdev { struct intel_framebuffer *fb; struct list_head fbdev_list; struct drm_display_mode *our_mode; + int preferred_bpp; }; struct intel_encoder { diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index 6b5beed28d700..32a05edc517cf 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -128,6 +128,7 @@ static int intelfb_create(struct drm_fb_helper *helper, struct drm_framebuffer *fb; struct drm_i915_gem_object *obj; int size, ret; + bool prealloc = false; mutex_lock(&dev->struct_mutex); @@ -139,6 +140,7 @@ static int intelfb_create(struct drm_fb_helper *helper, intel_fb = ifbdev->fb; } else { DRM_DEBUG_KMS("re-using BIOS fb\n"); + prealloc = true; sizes->fb_width = intel_fb->base.width; sizes->fb_height = intel_fb->base.height; } @@ -200,7 +202,7 @@ static int intelfb_create(struct drm_fb_helper *helper, * If the object is stolen however, it will be full of whatever * garbage was left in there. */ - if (ifbdev->fb->obj->stolen) + if (ifbdev->fb->obj->stolen && !prealloc) memset_io(info->screen_base, 0, info->screen_size); /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ @@ -454,27 +456,179 @@ static void intel_fbdev_destroy(struct drm_device *dev, drm_framebuffer_remove(&ifbdev->fb->base); } +/* + * Build an intel_fbdev struct using a BIOS allocated framebuffer, if possible. + * The core display code will have read out the current plane configuration, + * so we use that to figure out if there's an object for us to use as the + * fb, and if so, we re-use it for the fbdev configuration. + * + * Note we only support a single fb shared across pipes for boot (mostly for + * fbcon), so we just find the biggest and use that. + */ +static bool intel_fbdev_init_bios(struct drm_device *dev, + struct intel_fbdev *ifbdev) +{ + struct intel_framebuffer *fb = NULL; + struct drm_crtc *crtc; + struct intel_crtc *intel_crtc; + struct intel_plane_config *plane_config = NULL; + unsigned int max_size = 0; + + if (!i915.fastboot) + return false; + + /* Find the largest fb */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + intel_crtc = to_intel_crtc(crtc); + + if (!intel_crtc->active || !intel_crtc->plane_config.fb) { + DRM_DEBUG_KMS("pipe %c not active or no fb, skipping\n", + pipe_name(intel_crtc->pipe)); + continue; + } + + if (intel_crtc->plane_config.size > max_size) { + DRM_DEBUG_KMS("found possible fb from plane %c\n", + pipe_name(intel_crtc->pipe)); + plane_config = &intel_crtc->plane_config; + fb = plane_config->fb; + max_size = plane_config->size; + } + } + + if (!fb) { + DRM_DEBUG_KMS("no active fbs found, not using BIOS config\n"); + goto out; + } + + /* Now make sure all the pipes will fit into it */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + unsigned int cur_size; + + intel_crtc = to_intel_crtc(crtc); + + if (!intel_crtc->active) { + DRM_DEBUG_KMS("pipe %c not active, skipping\n", + pipe_name(intel_crtc->pipe)); + continue; + } + + DRM_DEBUG_KMS("checking plane %c for BIOS fb\n", + pipe_name(intel_crtc->pipe)); + + /* + * See if the plane fb we found above will fit on this + * pipe. Note we need to use the selected fb's bpp rather + * than the current pipe's, since they could be different. + */ + cur_size = intel_crtc->config.adjusted_mode.crtc_hdisplay * + intel_crtc->config.adjusted_mode.crtc_vdisplay; + DRM_DEBUG_KMS("pipe %c area: %d\n", pipe_name(intel_crtc->pipe), + cur_size); + cur_size *= fb->base.bits_per_pixel / 8; + DRM_DEBUG_KMS("total size %d (bpp %d)\n", cur_size, + fb->base.bits_per_pixel / 8); + + if (cur_size > max_size) { + DRM_DEBUG_KMS("fb not big enough for plane %c (%d vs %d)\n", + pipe_name(intel_crtc->pipe), + cur_size, max_size); + plane_config = NULL; + fb = NULL; + break; + } + + DRM_DEBUG_KMS("fb big enough for plane %c (%d >= %d)\n", + pipe_name(intel_crtc->pipe), + max_size, cur_size); + } + + /* Free unused fbs */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct intel_framebuffer *cur_fb; + + intel_crtc = to_intel_crtc(crtc); + cur_fb = intel_crtc->plane_config.fb; + + if (cur_fb && cur_fb != fb) + drm_framebuffer_unreference(&cur_fb->base); + } + + if (!fb) { + DRM_DEBUG_KMS("BIOS fb not suitable for all pipes, not using\n"); + goto out; + } + + ifbdev->preferred_bpp = plane_config->fb->base.bits_per_pixel; + ifbdev->fb = fb; + + /* Assuming a single fb across all pipes here */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + intel_crtc = to_intel_crtc(crtc); + + if (!intel_crtc->active) + continue; + + /* + * This should only fail on the first one so we don't need + * to cleanup any secondary crtc->fbs + */ + if (intel_pin_and_fence_fb_obj(dev, fb->obj, NULL)) + goto out_unref_obj; + + crtc->fb = &fb->base; + drm_gem_object_reference(&fb->obj->base); + drm_framebuffer_reference(&fb->base); + } + + /* Final pass to check if any active pipes don't have fbs */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + intel_crtc = to_intel_crtc(crtc); + + if (!intel_crtc->active) + continue; + + WARN(!crtc->fb, + "re-used BIOS config but lost an fb on crtc %d\n", + crtc->base.id); + } + + + DRM_DEBUG_KMS("using BIOS fb for initial console\n"); + return true; + +out_unref_obj: + drm_framebuffer_unreference(&fb->base); +out: + + return false; +} + int intel_fbdev_init(struct drm_device *dev) { struct intel_fbdev *ifbdev; struct drm_i915_private *dev_priv = dev->dev_private; int ret; - ifbdev = kzalloc(sizeof(*ifbdev), GFP_KERNEL); - if (!ifbdev) + if (WARN_ON(INTEL_INFO(dev)->num_pipes == 0)) + return -ENODEV; + + ifbdev = kzalloc(sizeof(struct intel_fbdev), GFP_KERNEL); + if (ifbdev == NULL) return -ENOMEM; - dev_priv->fbdev = ifbdev; ifbdev->helper.funcs = &intel_fb_helper_funcs; + if (!intel_fbdev_init_bios(dev, ifbdev)) + ifbdev->preferred_bpp = 32; ret = drm_fb_helper_init(dev, &ifbdev->helper, - INTEL_INFO(dev)->num_pipes, - 4); + INTEL_INFO(dev)->num_pipes, 4); if (ret) { kfree(ifbdev); return ret; } + dev_priv->fbdev = ifbdev; drm_fb_helper_single_add_all_connectors(&ifbdev->helper); return 0; @@ -483,9 +637,10 @@ int intel_fbdev_init(struct drm_device *dev) void intel_fbdev_initial_config(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_fbdev *ifbdev = dev_priv->fbdev; /* Due to peculiar init order wrt to hpd handling this is separate. */ - drm_fb_helper_initial_config(&dev_priv->fbdev->helper, 32); + drm_fb_helper_initial_config(&ifbdev->helper, ifbdev->preferred_bpp); } void intel_fbdev_fini(struct drm_device *dev) @@ -523,7 +678,8 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state) void intel_fbdev_output_poll_changed(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - drm_fb_helper_hotplug_event(&dev_priv->fbdev->helper); + if (dev_priv->fbdev) + drm_fb_helper_hotplug_event(&dev_priv->fbdev->helper); } void intel_fbdev_restore_mode(struct drm_device *dev) @@ -531,7 +687,7 @@ void intel_fbdev_restore_mode(struct drm_device *dev) int ret; struct drm_i915_private *dev_priv = dev->dev_private; - if (INTEL_INFO(dev)->num_pipes == 0) + if (!dev_priv->fbdev) return; drm_modeset_lock_all(dev); From 484b41dd70a9fbea894632d8926bbb93f05021c7 Mon Sep 17 00:00:00 2001 From: Jesse Barnes <jbarnes@virtuousgeek.org> Date: Fri, 7 Mar 2014 08:57:55 -0800 Subject: [PATCH 07/75] drm/i915: remove early fb allocation dependency on CONFIG_FB v2 By stuffing the fb allocation into the crtc, we get mode set lifetime refcounting for free, but have to handle the initial pin & fence slightly differently. It also means we can move the shared fb handling into the core rather than leaving it out in the fbdev code. v2: null out crtc->fb on error (Daniel) take fbdev fb ref and remove unused error path (Daniel) Requested-by: Daniel Vetter <daniel@ffwll.ch> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_display.c | 145 +++++++++++++++++++-------- drivers/gpu/drm/i915/intel_drv.h | 1 - drivers/gpu/drm/i915/intel_fbdev.c | 38 +------ 3 files changed, 105 insertions(+), 79 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d3c29116711fa..8710496bab4c9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2068,7 +2068,7 @@ int intel_format_to_fourcc(int format) } } -static void intel_alloc_plane_obj(struct intel_crtc *crtc, +static bool intel_alloc_plane_obj(struct intel_crtc *crtc, struct intel_plane_config *plane_config) { struct drm_device *dev = crtc->base.dev; @@ -2076,38 +2076,76 @@ static void intel_alloc_plane_obj(struct intel_crtc *crtc, struct drm_mode_fb_cmd2 mode_cmd = { 0 }; u32 base = plane_config->base; - if (!plane_config->fb) - return; - obj = i915_gem_object_create_stolen_for_preallocated(dev, base, base, plane_config->size); if (!obj) - return; + return false; if (plane_config->tiled) { obj->tiling_mode = I915_TILING_X; - obj->stride = plane_config->fb->base.pitches[0]; + obj->stride = crtc->base.fb->pitches[0]; } - mode_cmd.pixel_format = plane_config->fb->base.pixel_format; - mode_cmd.width = plane_config->fb->base.width; - mode_cmd.height = plane_config->fb->base.height; - mode_cmd.pitches[0] = plane_config->fb->base.pitches[0]; + mode_cmd.pixel_format = crtc->base.fb->pixel_format; + mode_cmd.width = crtc->base.fb->width; + mode_cmd.height = crtc->base.fb->height; + mode_cmd.pitches[0] = crtc->base.fb->pitches[0]; mutex_lock(&dev->struct_mutex); - if (intel_framebuffer_init(dev, plane_config->fb, &mode_cmd, obj)) { + if (intel_framebuffer_init(dev, to_intel_framebuffer(crtc->base.fb), + &mode_cmd, obj)) { DRM_DEBUG_KMS("intel fb init failed\n"); goto out_unref_obj; } mutex_unlock(&dev->struct_mutex); - DRM_DEBUG_KMS("plane fb obj %p\n", plane_config->fb->obj); - return; + + DRM_DEBUG_KMS("plane fb obj %p\n", obj); + return true; out_unref_obj: drm_gem_object_unreference(&obj->base); mutex_unlock(&dev->struct_mutex); + return false; +} + +static void intel_find_plane_obj(struct intel_crtc *intel_crtc, + struct intel_plane_config *plane_config) +{ + struct drm_device *dev = intel_crtc->base.dev; + struct drm_crtc *c; + struct intel_crtc *i; + struct intel_framebuffer *fb; + + if (!intel_crtc->base.fb) + return; + + if (intel_alloc_plane_obj(intel_crtc, plane_config)) + return; + + kfree(intel_crtc->base.fb); + + /* + * Failed to alloc the obj, check to see if we should share + * an fb with another CRTC instead + */ + list_for_each_entry(c, &dev->mode_config.crtc_list, head) { + i = to_intel_crtc(c); + + if (c == &intel_crtc->base) + continue; + + if (!i->active || !c->fb) + continue; + + fb = to_intel_framebuffer(c->fb); + if (i915_gem_obj_ggtt_offset(fb->obj) == plane_config->base) { + drm_framebuffer_reference(c->fb); + intel_crtc->base.fb = c->fb; + break; + } + } } static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb, @@ -5678,8 +5716,8 @@ static void i9xx_get_plane_config(struct intel_crtc *crtc, int fourcc, pixel_format; int aligned_height; - plane_config->fb = kzalloc(sizeof(*plane_config->fb), GFP_KERNEL); - if (!plane_config->fb) { + crtc->base.fb = kzalloc(sizeof(struct intel_framebuffer), GFP_KERNEL); + if (!crtc->base.fb) { DRM_DEBUG_KMS("failed to alloc fb\n"); return; } @@ -5692,8 +5730,8 @@ static void i9xx_get_plane_config(struct intel_crtc *crtc, pixel_format = val & DISPPLANE_PIXFORMAT_MASK; fourcc = intel_format_to_fourcc(pixel_format); - plane_config->fb->base.pixel_format = fourcc; - plane_config->fb->base.bits_per_pixel = + crtc->base.fb->pixel_format = fourcc; + crtc->base.fb->bits_per_pixel = drm_format_plane_cpp(fourcc, 0) * 8; if (INTEL_INFO(dev)->gen >= 4) { @@ -5708,23 +5746,23 @@ static void i9xx_get_plane_config(struct intel_crtc *crtc, plane_config->base = base; val = I915_READ(PIPESRC(pipe)); - plane_config->fb->base.width = ((val >> 16) & 0xfff) + 1; - plane_config->fb->base.height = ((val >> 0) & 0xfff) + 1; + crtc->base.fb->width = ((val >> 16) & 0xfff) + 1; + crtc->base.fb->height = ((val >> 0) & 0xfff) + 1; val = I915_READ(DSPSTRIDE(pipe)); - plane_config->fb->base.pitches[0] = val & 0xffffff80; + crtc->base.fb->pitches[0] = val & 0xffffff80; - aligned_height = intel_align_height(dev, plane_config->fb->base.height, + aligned_height = intel_align_height(dev, crtc->base.fb->height, plane_config->tiled); - plane_config->size = ALIGN(plane_config->fb->base.pitches[0] * + plane_config->size = ALIGN(crtc->base.fb->pitches[0] * aligned_height, PAGE_SIZE); DRM_DEBUG_KMS("pipe/plane %d/%d with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n", - pipe, plane, plane_config->fb->base.width, - plane_config->fb->base.height, - plane_config->fb->base.bits_per_pixel, base, - plane_config->fb->base.pitches[0], + pipe, plane, crtc->base.fb->width, + crtc->base.fb->height, + crtc->base.fb->bits_per_pixel, base, + crtc->base.fb->pitches[0], plane_config->size); } @@ -6686,8 +6724,8 @@ static void ironlake_get_plane_config(struct intel_crtc *crtc, int fourcc, pixel_format; int aligned_height; - plane_config->fb = kzalloc(sizeof(*plane_config->fb), GFP_KERNEL); - if (!plane_config->fb) { + crtc->base.fb = kzalloc(sizeof(struct intel_framebuffer), GFP_KERNEL); + if (!crtc->base.fb) { DRM_DEBUG_KMS("failed to alloc fb\n"); return; } @@ -6700,8 +6738,8 @@ static void ironlake_get_plane_config(struct intel_crtc *crtc, pixel_format = val & DISPPLANE_PIXFORMAT_MASK; fourcc = intel_format_to_fourcc(pixel_format); - plane_config->fb->base.pixel_format = fourcc; - plane_config->fb->base.bits_per_pixel = + crtc->base.fb->pixel_format = fourcc; + crtc->base.fb->bits_per_pixel = drm_format_plane_cpp(fourcc, 0) * 8; base = I915_READ(DSPSURF(plane)) & 0xfffff000; @@ -6716,23 +6754,23 @@ static void ironlake_get_plane_config(struct intel_crtc *crtc, plane_config->base = base; val = I915_READ(PIPESRC(pipe)); - plane_config->fb->base.width = ((val >> 16) & 0xfff) + 1; - plane_config->fb->base.height = ((val >> 0) & 0xfff) + 1; + crtc->base.fb->width = ((val >> 16) & 0xfff) + 1; + crtc->base.fb->height = ((val >> 0) & 0xfff) + 1; val = I915_READ(DSPSTRIDE(pipe)); - plane_config->fb->base.pitches[0] = val & 0xffffff80; + crtc->base.fb->pitches[0] = val & 0xffffff80; - aligned_height = intel_align_height(dev, plane_config->fb->base.height, + aligned_height = intel_align_height(dev, crtc->base.fb->height, plane_config->tiled); - plane_config->size = ALIGN(plane_config->fb->base.pitches[0] * + plane_config->size = ALIGN(crtc->base.fb->pitches[0] * aligned_height, PAGE_SIZE); DRM_DEBUG_KMS("pipe/plane %d/%d with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n", - pipe, plane, plane_config->fb->base.width, - plane_config->fb->base.height, - plane_config->fb->base.bits_per_pixel, base, - plane_config->fb->base.pitches[0], + pipe, plane, crtc->base.fb->width, + crtc->base.fb->height, + crtc->base.fb->bits_per_pixel, base, + crtc->base.fb->pitches[0], plane_config->size); } @@ -11290,10 +11328,7 @@ void intel_modeset_init(struct drm_device *dev) if (!crtc->active) continue; -#if IS_ENABLED(CONFIG_FB) /* - * We don't have a good way of freeing the buffer w/o the FB - * layer owning it... * Note that reserving the BIOS fb up front prevents us * from stuffing other stolen allocations like the ring * on top. This prevents some ugliness at boot time, and @@ -11307,9 +11342,8 @@ void intel_modeset_init(struct drm_device *dev) * If the fb is shared between multiple heads, we'll * just get the first one. */ - intel_alloc_plane_obj(crtc, &crtc->plane_config); + intel_find_plane_obj(crtc, &crtc->plane_config); } -#endif } } @@ -11680,9 +11714,32 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, void intel_modeset_gem_init(struct drm_device *dev) { + struct drm_crtc *c; + struct intel_framebuffer *fb; + intel_modeset_init_hw(dev); intel_setup_overlay(dev); + + /* + * Make sure any fbs we allocated at startup are properly + * pinned & fenced. When we do the allocation it's too early + * for this. + */ + mutex_lock(&dev->struct_mutex); + list_for_each_entry(c, &dev->mode_config.crtc_list, head) { + if (!c->fb) + continue; + + fb = to_intel_framebuffer(c->fb); + if (intel_pin_and_fence_fb_obj(dev, fb->obj, NULL)) { + DRM_ERROR("failed to pin boot fb on pipe %d\n", + to_intel_crtc(c)->pipe); + drm_framebuffer_unreference(c->fb); + c->fb = NULL; + } + } + mutex_unlock(&dev->struct_mutex); } void intel_connector_unregister(struct intel_connector *intel_connector) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 75baa64a357d0..5360d1628263e 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -220,7 +220,6 @@ typedef struct dpll { } intel_clock_t; struct intel_plane_config { - struct intel_framebuffer *fb; /* ends up managed by intel_fbdev.c */ bool tiled; int size; u32 base; diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index 32a05edc517cf..d6d78c86c232b 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -481,7 +481,7 @@ static bool intel_fbdev_init_bios(struct drm_device *dev, list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { intel_crtc = to_intel_crtc(crtc); - if (!intel_crtc->active || !intel_crtc->plane_config.fb) { + if (!intel_crtc->active || !crtc->fb) { DRM_DEBUG_KMS("pipe %c not active or no fb, skipping\n", pipe_name(intel_crtc->pipe)); continue; @@ -491,7 +491,7 @@ static bool intel_fbdev_init_bios(struct drm_device *dev, DRM_DEBUG_KMS("found possible fb from plane %c\n", pipe_name(intel_crtc->pipe)); plane_config = &intel_crtc->plane_config; - fb = plane_config->fb; + fb = to_intel_framebuffer(crtc->fb); max_size = plane_config->size; } } @@ -543,43 +543,15 @@ static bool intel_fbdev_init_bios(struct drm_device *dev, max_size, cur_size); } - /* Free unused fbs */ - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct intel_framebuffer *cur_fb; - - intel_crtc = to_intel_crtc(crtc); - cur_fb = intel_crtc->plane_config.fb; - - if (cur_fb && cur_fb != fb) - drm_framebuffer_unreference(&cur_fb->base); - } - if (!fb) { DRM_DEBUG_KMS("BIOS fb not suitable for all pipes, not using\n"); goto out; } - ifbdev->preferred_bpp = plane_config->fb->base.bits_per_pixel; + ifbdev->preferred_bpp = fb->base.bits_per_pixel; ifbdev->fb = fb; - /* Assuming a single fb across all pipes here */ - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - intel_crtc = to_intel_crtc(crtc); - - if (!intel_crtc->active) - continue; - - /* - * This should only fail on the first one so we don't need - * to cleanup any secondary crtc->fbs - */ - if (intel_pin_and_fence_fb_obj(dev, fb->obj, NULL)) - goto out_unref_obj; - - crtc->fb = &fb->base; - drm_gem_object_reference(&fb->obj->base); - drm_framebuffer_reference(&fb->base); - } + drm_framebuffer_reference(&ifbdev->fb->base); /* Final pass to check if any active pipes don't have fbs */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { @@ -597,8 +569,6 @@ static bool intel_fbdev_init_bios(struct drm_device *dev, DRM_DEBUG_KMS("using BIOS fb for initial console\n"); return true; -out_unref_obj: - drm_framebuffer_unreference(&fb->base); out: return false; From d1a59868efa65379482c79de997973b06cefb9d2 Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Mon, 10 Mar 2014 08:07:01 +0000 Subject: [PATCH 08/75] drm/i915: Prevent use-after-free of inherited framebuffer During KMS takeover, we try to capture the current configuration and preserve it across our initialisation. For a variety of reasons, we may fail this, for example if the current mode was using the legacy VGA plane. Under such circumstances, we discard the fb in the plane config and tried to find a matching fb on another CRTC. This obviously also failed, leaving the plane config fb dangling, pointing to the freed block. Regression from commit 484b41dd70a9fbea894632d8926bbb93f05021c7 Author: Jesse Barnes <jbarnes@virtuousgeek.org> Date: Fri Mar 7 08:57:55 2014 -0800 drm/i915: remove early fb allocation dependency on CONFIG_FB v2 Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=75963 Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_display.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8710496bab4c9..1f180a45e0820 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2125,6 +2125,7 @@ static void intel_find_plane_obj(struct intel_crtc *intel_crtc, return; kfree(intel_crtc->base.fb); + intel_crtc->base.fb = NULL; /* * Failed to alloc the obj, check to see if we should share From ff2652ea46fe8bcd78d8d74148bb8f9624f90936 Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Mon, 10 Mar 2014 08:07:02 +0000 Subject: [PATCH 09/75] drm/i915: Avoid requesting a zero-sized stolen object The stolen allocator objects loudly if the caller requests a zero-sized object. This is a useful verbose check as in most cases the request should have been pruned much early. Here we just want to silently return before attempting the allocation. Regression from commit 484b41dd70a9fbea894632d8926bbb93f05021c7 Author: Jesse Barnes <jbarnes@virtuousgeek.org> Date: Fri Mar 7 08:57:55 2014 -0800 drm/i915: remove early fb allocation dependency on CONFIG_FB v2 Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=75963 Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- 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 1f180a45e0820..500435fd2aea7 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2076,6 +2076,9 @@ static bool intel_alloc_plane_obj(struct intel_crtc *crtc, struct drm_mode_fb_cmd2 mode_cmd = { 0 }; u32 base = plane_config->base; + if (plane_config->size == 0) + return false; + obj = i915_gem_object_create_stolen_for_preallocated(dev, base, base, plane_config->size); if (!obj) From 842f1c8b7649f92b25e91f75e3bcdcf94daa77e8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter <daniel.vetter@ffwll.ch> Date: Mon, 10 Mar 2014 10:01:44 +0100 Subject: [PATCH 10/75] drm/i915: move dev_priv->suspend around When adding new gunk, _always_ think of a good place. Start/end usually just means that this didn't happen, and on top of that results in needless conflicts with other patches doing the same. Introduced in commit 62d5d69b49b6fea9905e36e67cc6c4fc5a17d75f Author: Mika Kuoppala <mika.kuoppala@linux.intel.com> Date: Tue Feb 25 17:11:28 2014 +0200 drm/i915: Add suspend count to error state Cc: Mika Kuoppala <mika.kuoppala@linux.intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_drv.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index bfb537942dbef..ba4f2b1d0c7f6 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1613,6 +1613,7 @@ typedef struct drm_i915_private { u32 fdi_rx_config; + u32 suspend_count; struct i915_suspend_saved_registers regfile; struct { @@ -1641,8 +1642,6 @@ typedef struct drm_i915_private { struct i915_dri1_state dri1; /* Old ums support infrastructure, same warning applies. */ struct i915_ums_state ums; - - u32 suspend_count; } drm_i915_private_t; static inline struct drm_i915_private *to_i915(const struct drm_device *dev) From bc079e8b1684e1de505ec06f8c2339ae60a329e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= <ville.syrjala@linux.intel.com> Date: Mon, 3 Mar 2014 16:15:28 +0200 Subject: [PATCH 11/75] drm/i915: Make encoder cloning more flexible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we allow encoders to indicate whether they can be part of a cloned set with just one flag. That's not flexible enough to describe the actual hardware capabilities. Instead make it a bitmask of encoder types with which the current encoder can be cloned. For now we set the bitmask to allow DVO+DVO and DVO+VGA, which should match what the old boolean flag allowed. We will add some more cloning options in the future. Note that this patch also removes the encoder.possible_clones setting from encoder setup code - we compute this dynamically. Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Reviewed-by: Rodrigo Vivi <rodrigo.vivi@gmail.com> [danvet: Add Ville's explanation why removing the encoder possible_clones is save.] Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_crt.c | 2 +- drivers/gpu/drm/i915/intel_ddi.c | 2 +- drivers/gpu/drm/i915/intel_display.c | 53 +++++++++++++++++++--------- drivers/gpu/drm/i915/intel_dp.c | 2 +- drivers/gpu/drm/i915/intel_drv.h | 6 +--- drivers/gpu/drm/i915/intel_dsi.c | 2 +- drivers/gpu/drm/i915/intel_dvo.c | 5 +-- drivers/gpu/drm/i915/intel_hdmi.c | 2 +- drivers/gpu/drm/i915/intel_lvds.c | 2 +- drivers/gpu/drm/i915/intel_sdvo.c | 2 +- drivers/gpu/drm/i915/intel_tv.c | 3 +- 11 files changed, 48 insertions(+), 33 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 4ef6d69c078d3..32b7d49306f8c 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -839,7 +839,7 @@ void intel_crt_init(struct drm_device *dev) intel_connector_attach_encoder(intel_connector, &crt->base); crt->base.type = INTEL_OUTPUT_ANALOG; - crt->base.cloneable = true; + crt->base.cloneable = 1 << INTEL_OUTPUT_DVO; if (IS_I830(dev)) crt->base.crtc_mask = (1 << 0); else diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index e2665e09d5dff..3565d61531f02 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1717,7 +1717,7 @@ void intel_ddi_init(struct drm_device *dev, enum port port) intel_encoder->type = INTEL_OUTPUT_UNKNOWN; intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); - intel_encoder->cloneable = false; + intel_encoder->cloneable = 0; intel_encoder->hot_plug = intel_ddi_hot_plug; if (init_dp) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 500435fd2aea7..307ce4401faa7 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9221,23 +9221,47 @@ static void intel_dump_pipe_config(struct intel_crtc *crtc, DRM_DEBUG_KMS("double wide: %i\n", pipe_config->double_wide); } -static bool check_encoder_cloning(struct drm_crtc *crtc) +static bool encoders_cloneable(const struct intel_encoder *a, + const struct intel_encoder *b) { - int num_encoders = 0; - bool uncloneable_encoders = false; + /* masks could be asymmetric, so check both ways */ + return a == b || (a->cloneable & (1 << b->type) && + b->cloneable & (1 << a->type)); +} + +static bool check_single_encoder_cloning(struct intel_crtc *crtc, + struct intel_encoder *encoder) +{ + struct drm_device *dev = crtc->base.dev; + struct intel_encoder *source_encoder; + + list_for_each_entry(source_encoder, + &dev->mode_config.encoder_list, base.head) { + if (source_encoder->new_crtc != crtc) + continue; + + if (!encoders_cloneable(encoder, source_encoder)) + return false; + } + + return true; +} + +static bool check_encoder_cloning(struct intel_crtc *crtc) +{ + struct drm_device *dev = crtc->base.dev; struct intel_encoder *encoder; - list_for_each_entry(encoder, &crtc->dev->mode_config.encoder_list, - base.head) { - if (&encoder->new_crtc->base != crtc) + list_for_each_entry(encoder, + &dev->mode_config.encoder_list, base.head) { + if (encoder->new_crtc != crtc) continue; - num_encoders++; - if (!encoder->cloneable) - uncloneable_encoders = true; + if (!check_single_encoder_cloning(crtc, encoder)) + return false; } - return !(num_encoders > 1 && uncloneable_encoders); + return true; } static struct intel_crtc_config * @@ -9251,7 +9275,7 @@ intel_modeset_pipe_config(struct drm_crtc *crtc, int plane_bpp, ret = -EINVAL; bool retry = true; - if (!check_encoder_cloning(crtc)) { + if (!check_encoder_cloning(to_intel_crtc(crtc))) { DRM_DEBUG_KMS("rejecting invalid cloning configuration\n"); return ERR_PTR(-EINVAL); } @@ -10614,12 +10638,7 @@ static int intel_encoder_clones(struct intel_encoder *encoder) list_for_each_entry(source_encoder, &dev->mode_config.encoder_list, base.head) { - - if (encoder == source_encoder) - index_mask |= (1 << entry); - - /* Intel hw has only one MUX where enocoders could be cloned. */ - if (encoder->cloneable && source_encoder->cloneable) + if (encoders_cloneable(encoder, source_encoder)) index_mask |= (1 << entry); entry++; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 7584348b7e898..ee96bf8c91306 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -3980,7 +3980,7 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port) intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT; intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); - intel_encoder->cloneable = false; + intel_encoder->cloneable = 0; intel_encoder->hot_plug = intel_dp_hot_plug; if (!intel_dp_init_connector(intel_dig_port, intel_connector)) { diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 5360d1628263e..2546cae0b4f03 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -125,11 +125,7 @@ struct intel_encoder { struct intel_crtc *new_crtc; int type; - /* - * Intel hw has only one MUX where encoders could be clone, hence a - * simple flag is enough to compute the possible_clones mask. - */ - bool cloneable; + unsigned int cloneable; bool connectors_active; void (*hot_plug)(struct intel_encoder *); bool (*compute_config)(struct intel_encoder *, diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c index cf7322e95278a..33656647f8bcf 100644 --- a/drivers/gpu/drm/i915/intel_dsi.c +++ b/drivers/gpu/drm/i915/intel_dsi.c @@ -620,7 +620,7 @@ bool intel_dsi_init(struct drm_device *dev) intel_encoder->type = INTEL_OUTPUT_DSI; intel_encoder->crtc_mask = (1 << 0); /* XXX */ - intel_encoder->cloneable = false; + intel_encoder->cloneable = 0; drm_connector_init(dev, connector, &intel_dsi_connector_funcs, DRM_MODE_CONNECTOR_DSI); diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index 86eeb8b7d4358..7fe3feedfe039 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -522,14 +522,15 @@ void intel_dvo_init(struct drm_device *dev) intel_encoder->crtc_mask = (1 << 0) | (1 << 1); switch (dvo->type) { case INTEL_DVO_CHIP_TMDS: - intel_encoder->cloneable = true; + intel_encoder->cloneable = (1 << INTEL_OUTPUT_ANALOG) | + (1 << INTEL_OUTPUT_DVO); drm_connector_init(dev, connector, &intel_dvo_connector_funcs, DRM_MODE_CONNECTOR_DVII); encoder_type = DRM_MODE_ENCODER_TMDS; break; case INTEL_DVO_CHIP_LVDS: - intel_encoder->cloneable = false; + intel_encoder->cloneable = 0; drm_connector_init(dev, connector, &intel_dvo_connector_funcs, DRM_MODE_CONNECTOR_LVDS); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index f410cc03e08a0..4575a91d035b8 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1318,7 +1318,7 @@ void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port) intel_encoder->type = INTEL_OUTPUT_HDMI; intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); - intel_encoder->cloneable = false; + intel_encoder->cloneable = 0; intel_dig_port->port = port; intel_dig_port->hdmi.hdmi_reg = hdmi_reg; diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index fecff3c2b9e18..ef5e5661efb21 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -963,7 +963,7 @@ void intel_lvds_init(struct drm_device *dev) intel_connector_attach_encoder(intel_connector, intel_encoder); intel_encoder->type = INTEL_OUTPUT_LVDS; - intel_encoder->cloneable = false; + intel_encoder->cloneable = 0; if (HAS_PCH_SPLIT(dev)) intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); else if (IS_GEN4(dev)) diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 825853d82a4d5..9a0b71f6aed6e 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -3032,7 +3032,7 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob) * simplistic anyway to express such constraints, so just give up on * cloning for SDVO encoders. */ - intel_sdvo->base.cloneable = false; + intel_sdvo->base.cloneable = 0; intel_sdvo_select_ddc_bus(dev_priv, intel_sdvo, sdvo_reg); diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index b64fc1c6ff3f6..5be4ab218054d 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1639,9 +1639,8 @@ intel_tv_init(struct drm_device *dev) intel_connector_attach_encoder(intel_connector, intel_encoder); intel_encoder->type = INTEL_OUTPUT_TVOUT; intel_encoder->crtc_mask = (1 << 0) | (1 << 1); - intel_encoder->cloneable = false; + intel_encoder->cloneable = 0; intel_encoder->base.possible_crtcs = ((1 << 0) | (1 << 1)); - intel_encoder->base.possible_clones = (1 << INTEL_OUTPUT_TVOUT); intel_tv->type = DRM_MODE_CONNECTOR_Unknown; /* BIOS margin values */ From 718006329556b95c4394cc9163bafac85ccbe2f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= <ville.syrjala@linux.intel.com> Date: Mon, 3 Mar 2014 16:15:29 +0200 Subject: [PATCH 12/75] drm/i915: Don't use HDMI 12bpc when cloning with other encoder types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When cloning HDMI with other output types, we can't use 12bpc since the clocks for the other encoder types would be off. So have intel_hdmi_compute_config() check if there are other encoders besides HDMI being fed from the same pipe, and if so, pick 8bpc insted if 12bpc. Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Reviewed-by: Rodrigo Vivi <rodrigo.vivi@gmail.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_hdmi.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index d13c65d607b2d..a89e15a40bf77 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -848,6 +848,30 @@ intel_hdmi_mode_valid(struct drm_connector *connector, return MODE_OK; } +static bool hdmi_12bpc_possible(struct intel_crtc *crtc) +{ + struct drm_device *dev = crtc->base.dev; + struct intel_encoder *encoder; + int count = 0, count_hdmi = 0; + + if (!HAS_PCH_SPLIT(dev)) + return false; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { + if (encoder->new_crtc != crtc) + continue; + + count_hdmi += encoder->type == INTEL_OUTPUT_HDMI; + count++; + } + + /* + * HDMI 12bpc affects the clocks, so it's only possible + * when not cloning with other encoder types. + */ + return count_hdmi > 0 && count_hdmi == count; +} + bool intel_hdmi_compute_config(struct intel_encoder *encoder, struct intel_crtc_config *pipe_config) { @@ -880,7 +904,8 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, * within limits. */ if (pipe_config->pipe_bpp > 8*3 && intel_hdmi->has_hdmi_sink && - clock_12bpc <= portclock_limit && HAS_PCH_SPLIT(dev)) { + clock_12bpc <= portclock_limit && + hdmi_12bpc_possible(encoder->new_crtc)) { DRM_DEBUG_KMS("picking bpc to 12 for HDMI output\n"); desired_bpp = 12*3; From 301ea74a57851c19e1438ceeaffab663f402f79f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= <ville.syrjala@linux.intel.com> Date: Mon, 3 Mar 2014 16:15:30 +0200 Subject: [PATCH 13/75] drm/i915: Allow HDMI+VGA cloning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HDMI+VGA cloning should be supported on all platforms. The only real obstacle is the 1.5x clock adjustment for 12bpc HDMI, but that is now taken care of, so we can allow HDMI+VGA cloning. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=73850 Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Reviewed-by: Rodrigo Vivi <rodrigo.vivi@gmail.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_crt.c | 2 +- drivers/gpu/drm/i915/intel_hdmi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 32b7d49306f8c..4b4e8f0f96216 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -839,7 +839,7 @@ void intel_crt_init(struct drm_device *dev) intel_connector_attach_encoder(intel_connector, &crt->base); crt->base.type = INTEL_OUTPUT_ANALOG; - crt->base.cloneable = 1 << INTEL_OUTPUT_DVO; + crt->base.cloneable = (1 << INTEL_OUTPUT_DVO) | (1 << INTEL_OUTPUT_HDMI); if (IS_I830(dev)) crt->base.crtc_mask = (1 << 0); else diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index a89e15a40bf77..6e806c668538c 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1343,7 +1343,7 @@ void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port) intel_encoder->type = INTEL_OUTPUT_HDMI; intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); - intel_encoder->cloneable = 0; + intel_encoder->cloneable = 1 << INTEL_OUTPUT_ANALOG; intel_dig_port->port = port; intel_dig_port->hdmi.hdmi_reg = hdmi_reg; From c6f1495d4c20cfc48ef3527012c31b46527f3839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= <ville.syrjala@linux.intel.com> Date: Mon, 3 Mar 2014 16:15:31 +0200 Subject: [PATCH 14/75] drm/i915: Allow HDMI+HDMI cloning on g4x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BSpec is a bit unclear whether HDMI+HDMI cloning should work on g4x. Tests on real hardware say that it does. Since g4x can't send infoframes to more than one HDMI port anyway, we don't lose anything by allow it. For PCH platforms BSpec explicitly forbids HDMI+HDMI cloning. Whether HDMI+HDMI cloning might also work on VLV is a bit unclear, but since we'd at least lose the capability of sending infoframes to more than one cloned HDMI port, it doesn't seem like a good idea to allow it. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=73850 Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Reviewed-by: Rodrigo Vivi <rodrigo.vivi@gmail.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_hdmi.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 6e806c668538c..b0413e190625b 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1344,6 +1344,13 @@ void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port) intel_encoder->type = INTEL_OUTPUT_HDMI; intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); intel_encoder->cloneable = 1 << INTEL_OUTPUT_ANALOG; + /* + * BSpec is unclear about HDMI+HDMI cloning on g4x, but it seems + * to work on real hardware. And since g4x can send infoframes to + * only one port anyway, nothing is lost by allowing it. + */ + if (IS_G4X(dev)) + intel_encoder->cloneable |= 1 << INTEL_OUTPUT_HDMI; intel_dig_port->port = port; intel_dig_port->hdmi.hdmi_reg = hdmi_reg; From 6c7fba04ecfddd634751239a52df0eccffc8700b Mon Sep 17 00:00:00 2001 From: Imre Deak <imre.deak@intel.com> Date: Mon, 10 Mar 2014 19:44:48 +0200 Subject: [PATCH 15/75] drm/i915: fix typo in display IRQ mask when disabling IRQs Introduced in commit e0e33f8ff6f0b6d286afc314802be4993341bd47 Author: Imre Deak <imre.deak@intel.com> Date: Tue Mar 4 19:23:07 2014 +0200 The impact was luckily minimal, due to the extra check we do against a software pipestat IRQ mask. Caught by Fengguang's 0-day tester. Cc: Fengguang Wu <fengguang.wu@intel.com> Signed-off-by: Imre Deak <imre.deak@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index be2713f12e082..c8e262fc750a4 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -3074,7 +3074,7 @@ static void valleyview_display_irqs_uninstall(struct drm_i915_private *dev_priv) iir_mask = I915_DISPLAY_PORT_INTERRUPT | I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | - I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT; dev_priv->irq_mask |= iir_mask; I915_WRITE(VLV_IER, ~dev_priv->irq_mask); From 8407bb9129da95fc4099b84cdbbc23e6d4f66aee Mon Sep 17 00:00:00 2001 From: Ben Widawsky <benjamin.widawsky@intel.com> Date: Sat, 8 Mar 2014 11:58:16 -0800 Subject: [PATCH 16/75] drm/i915/bdw: Use scratch page table for GEN8 PPGTT I'm not clear if the hardware is still subject to the same prefetching issues that made us use a scratch page in the first place. In either case, we're using garbage with the current code (we will end up using offset 0). This may be the cause of our current gem_cpu_reloc regression with PPGTT. I cannot test it at the moment. Signed-off-by: Ben Widawsky <ben@bwidawsk.net> Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_gem_gtt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 63a6dc7a6bb6c..91505419db407 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -1191,7 +1191,6 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt) ppgtt->base.clear_range = gen6_ppgtt_clear_range; ppgtt->base.insert_entries = gen6_ppgtt_insert_entries; ppgtt->base.cleanup = gen6_ppgtt_cleanup; - ppgtt->base.scratch = dev_priv->gtt.base.scratch; ppgtt->base.start = 0; ppgtt->base.total = GEN6_PPGTT_PD_ENTRIES * I915_PPGTT_PT_ENTRIES * PAGE_SIZE; ppgtt->debug_dump = gen6_dump_ppgtt; @@ -1214,6 +1213,7 @@ int i915_gem_init_ppgtt(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt) int ret = 0; ppgtt->base.dev = dev; + ppgtt->base.scratch = dev_priv->gtt.base.scratch; if (INTEL_INFO(dev)->gen < 8) ret = gen6_ppgtt_init(ppgtt); From 5a6c93fe802bd241c4287f116d0116aff0a4341a Mon Sep 17 00:00:00 2001 From: Ben Widawsky <benjamin.widawsky@intel.com> Date: Sat, 8 Mar 2014 11:58:17 -0800 Subject: [PATCH 17/75] drm/i915: Correct PPGTT total size Our code allows have a PPGTT that is smaller than the maximum size for GEN6-GEN7. Though I don't think this actually ever occurs, the code may as well work properly and more importantly look correct by using the variable size instead of the HW max. Signed-off-by: Ben Widawsky <ben@bwidawsk.net> Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_gem_gtt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 91505419db407..7727103d4918d 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -1192,7 +1192,7 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt) ppgtt->base.insert_entries = gen6_ppgtt_insert_entries; ppgtt->base.cleanup = gen6_ppgtt_cleanup; ppgtt->base.start = 0; - ppgtt->base.total = GEN6_PPGTT_PD_ENTRIES * I915_PPGTT_PT_ENTRIES * PAGE_SIZE; + ppgtt->base.total = ppgtt->num_pd_entries * I915_PPGTT_PT_ENTRIES * PAGE_SIZE; ppgtt->debug_dump = gen6_dump_ppgtt; ppgtt->pd_offset = From a51435a3137ad8ae75c288c39bd2d8b2696bae8f Mon Sep 17 00:00:00 2001 From: Naresh Kumar Kachhi <naresh.kumar.kachhi@intel.com> Date: Wed, 12 Mar 2014 16:39:40 +0530 Subject: [PATCH 18/75] drm/i915: disable rings before HW status page setup Rings should be idle before issuing sync_flush (in intel_ring_setup_status_page). This patch moves the ring disabling before doing the HW status page setup. Signed-off-by: Naresh Kumar Kachhi <naresh.kumar.kachhi@intel.com> Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_ringbuffer.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 859092130c180..42b4001443795 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -440,16 +440,16 @@ static int init_ring_common(struct intel_ring_buffer *ring) gen6_gt_force_wake_get(dev_priv, FORCEWAKE_ALL); - if (I915_NEED_GFX_HWS(dev)) - intel_ring_setup_status_page(ring); - else - ring_setup_phys_status_page(ring); - /* Stop the ring if it's running. */ I915_WRITE_CTL(ring, 0); I915_WRITE_HEAD(ring, 0); ring->write_tail(ring, 0); + if (I915_NEED_GFX_HWS(dev)) + intel_ring_setup_status_page(ring); + else + ring_setup_phys_status_page(ring); + head = I915_READ_HEAD(ring) & HEAD_ADDR; /* G45 ring initialization fails to reset head to zero */ From e9fea5747d2b3dbff47a8790c1cc4d7af80051d6 Mon Sep 17 00:00:00 2001 From: Naresh Kumar Kachhi <naresh.kumar.kachhi@intel.com> Date: Wed, 12 Mar 2014 16:39:41 +0530 Subject: [PATCH 19/75] drm/i915: wait for rings to become idle once disabled make sure we wait for rings to become idle once they are disabled. In case of timeout print an error message Signed-off-by: Naresh Kumar Kachhi <naresh.kumar.kachhi@intel.com> [danvet: Frob patch as suggested by Chris.] Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_reg.h | 2 ++ drivers/gpu/drm/i915/intel_ringbuffer.c | 2 ++ drivers/gpu/drm/i915/intel_ringbuffer.h | 2 ++ 3 files changed, 6 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 146609ab42bb9..6174fda4d58ea 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -748,6 +748,7 @@ enum punit_power_well { #define RING_INSTPS(base) ((base)+0x70) #define RING_DMA_FADD(base) ((base)+0x78) #define RING_INSTPM(base) ((base)+0xc0) +#define RING_MI_MODE(base) ((base)+0x9c) #define INSTPS 0x02070 /* 965+ only */ #define INSTDONE1 0x0207c /* 965+ only */ #define ACTHD_I965 0x02074 @@ -824,6 +825,7 @@ enum punit_power_well { # define VS_TIMER_DISPATCH (1 << 6) # define MI_FLUSH_ENABLE (1 << 12) # define ASYNC_FLIP_PERF_DISABLE (1 << 14) +# define MODE_IDLE (1 << 9) #define GEN6_GT_MODE 0x20d0 #define GEN7_GT_MODE 0x7008 diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 42b4001443795..617634b6a6c2b 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -444,6 +444,8 @@ static int init_ring_common(struct intel_ring_buffer *ring) I915_WRITE_CTL(ring, 0); I915_WRITE_HEAD(ring, 0); ring->write_tail(ring, 0); + if (wait_for_atomic((I915_READ_MODE(ring) & MODE_IDLE) != 0, 1000)) + DRM_ERROR("%s :timed out trying to stop ring\n", ring->name); if (I915_NEED_GFX_HWS(dev)) intel_ring_setup_status_page(ring); diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 09af92099c1bb..f11ceb230db48 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -33,6 +33,8 @@ struct intel_hw_status_page { #define I915_READ_IMR(ring) I915_READ(RING_IMR((ring)->mmio_base)) #define I915_WRITE_IMR(ring, val) I915_WRITE(RING_IMR((ring)->mmio_base), val) +#define I915_READ_MODE(ring) I915_READ(RING_MI_MODE((ring)->mmio_base)) + enum intel_ring_hangcheck_action { HANGCHECK_IDLE = 0, HANGCHECK_WAIT, From 02f6a1e750df8201561171c47472435557a65864 Mon Sep 17 00:00:00 2001 From: Naresh Kumar Kachhi <naresh.kumar.kachhi@intel.com> Date: Wed, 12 Mar 2014 16:39:42 +0530 Subject: [PATCH 20/75] drm/i915: warn if ring is active before sync flush Based on Bspec the command parser must be stopped prior to issuing sync flush. This should be done by the caller of intel_ring_setup_status_page. Patch adds a warning if it is not done. v2: rebased based on new patch (wait for ring to become idle) Signed-off-by: Naresh Kumar Kachhi <naresh.kumar.kachhi@intel.com> Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_ringbuffer.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 617634b6a6c2b..c50388a86bca0 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -984,6 +984,10 @@ void intel_ring_setup_status_page(struct intel_ring_buffer *ring) /* Flush the TLB for this page */ if (INTEL_INFO(dev)->gen >= 6) { u32 reg = RING_INSTPM(ring->mmio_base); + + /* ring should be idle before issuing a sync flush*/ + WARN_ON((I915_READ_MODE(ring) & MODE_IDLE) == 0); + I915_WRITE(reg, _MASKED_BIT_ENABLE(INSTPM_TLB_INVALIDATE | INSTPM_SYNC_FLUSH)); From 8ac36ec1e370cb8ff9c082972ad0570bba37381a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= <ville.syrjala@linux.intel.com> Date: Tue, 11 Mar 2014 19:37:33 +0200 Subject: [PATCH 21/75] drm/i915: Reduce the time we hold struct mutex in intel_pipe_set_base() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't need to hold struct_mutex all through intel_pipe_set_base(), just need to hold it while pinning/unpinning the buffers. So reduce the struct_mutext usage in intel_pipe_set_base() just like we did for the sprite code in: commit 82284b6becdbef6d8cd3fb43e8698510833a5129 Author: Ville Syrjälä <ville.syrjala@linux.intel.com> Date: Tue Oct 1 18:02:12 2013 +0300 drm/i915: Reduce the time we hold struct mutex in sprite update_plane code The FBC and PSR locking is still entirely fubar. That stuff was previouly done while holding struct_mutex, so leave it there for now. Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_display.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e71dda5feab27..8c3746f7b5236 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2477,8 +2477,8 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, ret = intel_pin_and_fence_fb_obj(dev, to_intel_framebuffer(fb)->obj, NULL); + mutex_unlock(&dev->struct_mutex); if (ret != 0) { - mutex_unlock(&dev->struct_mutex); DRM_ERROR("pin & fence failed\n"); return ret; } @@ -2516,6 +2516,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, ret = dev_priv->display.update_plane(crtc, fb, x, y); if (ret) { + mutex_lock(&dev->struct_mutex); intel_unpin_fb_obj(to_intel_framebuffer(fb)->obj); mutex_unlock(&dev->struct_mutex); DRM_ERROR("failed to update base address\n"); @@ -2530,9 +2531,12 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, if (old_fb) { if (intel_crtc->active && old_fb != fb) intel_wait_for_vblank(dev, intel_crtc->pipe); + mutex_lock(&dev->struct_mutex); intel_unpin_fb_obj(to_intel_framebuffer(old_fb)->obj); + mutex_unlock(&dev->struct_mutex); } + mutex_lock(&dev->struct_mutex); intel_update_fbc(dev); intel_edp_psr_update(dev); mutex_unlock(&dev->struct_mutex); From 065f2ec2afc850960dcebc3b00766bc31c4ffd3b Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Wed, 12 Mar 2014 09:13:13 +0000 Subject: [PATCH 22/75] drm/i915: Show cursor status in debugfs/i915_display_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I have the occasional absent cursor on i845 and I want to know why. This should help by revealing the last known cursor state. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_debugfs.c | 57 +++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index a90d31c786437..30fc893dd443e 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2248,24 +2248,67 @@ static void intel_connector_info(struct seq_file *m, intel_seq_print_mode(m, 2, mode); } +static bool cursor_active(struct drm_device *dev, int pipe) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 state; + + if (IS_845G(dev) || IS_I865G(dev)) + state = I915_READ(_CURACNTR) & CURSOR_ENABLE; + else if (INTEL_INFO(dev)->gen <= 6 || IS_VALLEYVIEW(dev)) + state = I915_READ(CURCNTR(pipe)) & CURSOR_MODE; + else + state = I915_READ(CURCNTR_IVB(pipe)) & CURSOR_MODE; + + return state; +} + +static bool cursor_position(struct drm_device *dev, int pipe, int *x, int *y) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 pos; + + if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev) || IS_BROADWELL(dev)) + pos = I915_READ(CURPOS_IVB(pipe)); + else + pos = I915_READ(CURPOS(pipe)); + + *x = (pos >> CURSOR_X_SHIFT) & CURSOR_POS_MASK; + if (pos & (CURSOR_POS_SIGN << CURSOR_X_SHIFT)) + *x = -*x; + + *y = (pos >> CURSOR_Y_SHIFT) & CURSOR_POS_MASK; + if (pos & (CURSOR_POS_SIGN << CURSOR_Y_SHIFT)) + *y = -*y; + + return cursor_active(dev, pipe); +} + static int i915_display_info(struct seq_file *m, void *unused) { struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; - struct drm_crtc *crtc; + struct intel_crtc *crtc; struct drm_connector *connector; drm_modeset_lock_all(dev); seq_printf(m, "CRTC info\n"); seq_printf(m, "---------\n"); - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) { + bool active; + int x, y; seq_printf(m, "CRTC %d: pipe: %c, active: %s\n", - crtc->base.id, pipe_name(intel_crtc->pipe), - intel_crtc->active ? "yes" : "no"); - if (intel_crtc->active) - intel_crtc_info(m, intel_crtc); + crtc->base.base.id, pipe_name(crtc->pipe), + yesno(crtc->active)); + if (crtc->active) + intel_crtc_info(m, crtc); + + active = cursor_position(dev, crtc->pipe, &x, &y); + seq_printf(m, "\tcursor visible? %s, position (%d, %d), addr 0x%08x, active? %s\n", + yesno(crtc->cursor_visible), + x, y, crtc->cursor_addr, + yesno(active)); } seq_printf(m, "\n"); From dc616b89dbc4bb6a99884d214bd1ed1e0eef59a0 Mon Sep 17 00:00:00 2001 From: Damien Lespiau <damien.lespiau@intel.com> Date: Thu, 13 Mar 2014 01:40:28 +0000 Subject: [PATCH 23/75] drm/i915/bdw: The TLB invalidation mechanism has been removed from INSTPM While wandering in the spec, I noticed that BDW removes those 2 bits from INSTPM. I couldn't find any direct way to invalidate the TLB (ie without the ring working already). Maybe someone will be more lucky. At least, we now know we may be a problem. Signed-off-by: Damien Lespiau <damien.lespiau@intel.com> Reviewed-by: Ben Widawsky <ben@bwidawsk.net> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_ringbuffer.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index c50388a86bca0..4eb3e062b4e3d 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -981,8 +981,14 @@ void intel_ring_setup_status_page(struct intel_ring_buffer *ring) I915_WRITE(mmio, (u32)ring->status_page.gfx_addr); POSTING_READ(mmio); - /* Flush the TLB for this page */ - if (INTEL_INFO(dev)->gen >= 6) { + /* + * Flush the TLB for this page + * + * FIXME: These two bits have disappeared on gen8, so a question + * arises: do we still need this and if so how should we go about + * invalidating the TLB? + */ + if (INTEL_INFO(dev)->gen >= 6 && INTEL_INFO(dev)->gen < 8) { u32 reg = RING_INSTPM(ring->mmio_base); /* ring should be idle before issuing a sync flush*/ From c5139450c6a8b309e4e6f25a2a5bcaceddf19b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= <ville.syrjala@linux.intel.com> Date: Wed, 12 Mar 2014 19:32:26 +0200 Subject: [PATCH 24/75] drm/i915: Drop WARN_ON(flags) from ppgtt_bind_vma() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We will call ppgtt_bind_vma() with flags != 0, so the WARN_ON(flags) is bogus. Kill it. Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_gem_gtt.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 7727103d4918d..0dce6fc9b1cc9 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -1243,8 +1243,6 @@ ppgtt_bind_vma(struct i915_vma *vma, enum i915_cache_level cache_level, u32 flags) { - WARN_ON(flags); - vma->vm->insert_entries(vma->vm, vma->obj->pages, vma->node.start, cache_level); } From 3ddffb7b8a7e296af4ff22b953836ac6bc484b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= <ville.syrjala@linux.intel.com> Date: Wed, 12 Mar 2014 19:32:27 +0200 Subject: [PATCH 25/75] drm/i915: Unbind all vmas whose new cache_level doesn't agree with the neighbours MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we change the cache_level for an object we need to make sure we don't put differing types of snoopable memory too close to each other on non-LLC machines. Currently i915_gem_object_set_cache_level() will stop looking when it finds just one vma that has such a conflict. Drop the bogus break statement to make sure it will unbind all vmas which need to be moved around to avoid the conflict. I suppose this is a theoretical issue as currently we don't enable ppgtt on non-LLC machines, so each object can only have one vma. Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_gem.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 92b0b4164b1da..70384c8d14048 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3482,8 +3482,6 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, ret = i915_vma_unbind(vma); if (ret) return ret; - - break; } } From 28d85cd367a3f5b4d891ebe9aaaa88a5c73a3a96 Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Thu, 13 Mar 2014 11:05:02 +0000 Subject: [PATCH 26/75] drm/i915: Reset forcewake before suspend Now that we regularly defer the forcewake dance to a timer func, it is likely to fire after we disable the device during suspend. This generates an oops as we detect inconsistency in the hardware state. So before suspend, we want to complete the outstanding dance and generally sanitize the registers before handing back to the BIOS. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_drv.c | 1 + drivers/gpu/drm/i915/intel_uncore.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 658fe24961ebf..5a0d34c47885d 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -467,6 +467,7 @@ static int i915_drm_freeze(struct drm_device *dev) i915_save_state(dev); intel_opregion_fini(dev); + intel_uncore_fini(dev); console_lock(); intel_fbdev_set_suspend(dev, FBINFO_STATE_SUSPENDED); diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index 7861d97600e18..361d1eae3cfd0 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -805,6 +805,10 @@ void intel_uncore_fini(struct drm_device *dev) /* Paranoia: make sure we have disabled everything before we exit. */ intel_uncore_sanitize(dev); intel_uncore_forcewake_reset(dev); + + dev_priv->uncore.forcewake_count = 0; + dev_priv->uncore.fw_rendercount = 0; + dev_priv->uncore.fw_mediacount = 0; } static const struct register_whitelist { From b2040f6fed736ccd2319768bc59833abe74148b8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter <daniel.vetter@ffwll.ch> Date: Thu, 13 Mar 2014 17:45:01 +0100 Subject: [PATCH 27/75] drm/i915: Remove erronous WARN in the vlv pipe crc code It's been in there since forever, and no one cared. Doesn't put a too good light onto our bug handling and QA efforts really ... References: https://bugs.freedesktop.org/attachment.cgi?id=90970 Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_debugfs.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 30fc893dd443e..d83d643af45c7 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2646,8 +2646,6 @@ static int vlv_pipe_crc_ctl_reg(struct drm_device *dev, if (need_stable_symbols) { uint32_t tmp = I915_READ(PORT_DFT2_G4X); - WARN_ON(!IS_G4X(dev)); - tmp |= DC_BALANCE_RESET_VLV; if (pipe == PIPE_A) tmp |= PIPE_A_SCRAMBLE_RESET; From 0294ae7b44bba7ab0d4cef9a8736287f38bdb4fd Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Thu, 13 Mar 2014 12:00:29 +0000 Subject: [PATCH 28/75] drm/i915: Consolidate forcewake resetting to a single function We have two paths that try to reset the forcewake registers back to known good values, with slightly different semantics and levels of paranoia. Combine the two by passing a parameter to either restore the forcewake status or to clear our bookkeeping, and raise the paranoia level to max. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Reviewed-by: Mika Kuoppala <mika.kuoppala@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_uncore.c | 81 +++++++++++++++-------------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index 361d1eae3cfd0..e6bb421a3dbd7 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -308,9 +308,17 @@ static void gen6_force_wake_timer(unsigned long arg) intel_runtime_pm_put(dev_priv); } -static void intel_uncore_forcewake_reset(struct drm_device *dev) +static void intel_uncore_forcewake_reset(struct drm_device *dev, bool restore) { struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long irqflags; + + del_timer_sync(&dev_priv->uncore.force_wake_timer); + + /* Hold uncore.lock across reset to prevent any register access + * with forcewake not set correctly + */ + spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); if (IS_VALLEYVIEW(dev)) vlv_force_wake_reset(dev_priv); @@ -319,6 +327,35 @@ static void intel_uncore_forcewake_reset(struct drm_device *dev) if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev) || IS_GEN8(dev)) __gen7_gt_force_wake_mt_reset(dev_priv); + + if (restore) { /* If reset with a user forcewake, try to restore */ + unsigned fw = 0; + + if (IS_VALLEYVIEW(dev)) { + if (dev_priv->uncore.fw_rendercount) + fw |= FORCEWAKE_RENDER; + + if (dev_priv->uncore.fw_mediacount) + fw |= FORCEWAKE_MEDIA; + } else { + if (dev_priv->uncore.forcewake_count) + fw = FORCEWAKE_ALL; + } + + if (fw) + dev_priv->uncore.funcs.force_wake_get(dev_priv, fw); + + if (IS_GEN6(dev) || IS_GEN7(dev)) + dev_priv->uncore.fifo_count = + __raw_i915_read32(dev_priv, GTFIFOCTL) & + GT_FIFO_FREE_ENTRIES_MASK; + } else { + dev_priv->uncore.forcewake_count = 0; + dev_priv->uncore.fw_rendercount = 0; + dev_priv->uncore.fw_mediacount = 0; + } + + spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } void intel_uncore_early_sanitize(struct drm_device *dev) @@ -344,7 +381,7 @@ void intel_uncore_early_sanitize(struct drm_device *dev) __raw_i915_write32(dev_priv, GTFIFODBG, __raw_i915_read32(dev_priv, GTFIFODBG)); - intel_uncore_forcewake_reset(dev); + intel_uncore_forcewake_reset(dev, false); } void intel_uncore_sanitize(struct drm_device *dev) @@ -798,17 +835,9 @@ void intel_uncore_init(struct drm_device *dev) void intel_uncore_fini(struct drm_device *dev) { - struct drm_i915_private *dev_priv = dev->dev_private; - - del_timer_sync(&dev_priv->uncore.force_wake_timer); - /* Paranoia: make sure we have disabled everything before we exit. */ intel_uncore_sanitize(dev); - intel_uncore_forcewake_reset(dev); - - dev_priv->uncore.forcewake_count = 0; - dev_priv->uncore.fw_rendercount = 0; - dev_priv->uncore.fw_mediacount = 0; + intel_uncore_forcewake_reset(dev, false); } static const struct register_whitelist { @@ -957,13 +986,6 @@ static int gen6_do_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; int ret; - unsigned long irqflags; - u32 fw_engine = 0; - - /* Hold uncore.lock across reset to prevent any register access - * with forcewake not set correctly - */ - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); /* Reset the chip */ @@ -976,29 +998,8 @@ static int gen6_do_reset(struct drm_device *dev) /* Spin waiting for the device to ack the reset request */ ret = wait_for((__raw_i915_read32(dev_priv, GEN6_GDRST) & GEN6_GRDOM_FULL) == 0, 500); - intel_uncore_forcewake_reset(dev); - - /* If reset with a user forcewake, try to restore */ - if (IS_VALLEYVIEW(dev)) { - if (dev_priv->uncore.fw_rendercount) - fw_engine |= FORCEWAKE_RENDER; - - if (dev_priv->uncore.fw_mediacount) - fw_engine |= FORCEWAKE_MEDIA; - } else { - if (dev_priv->uncore.forcewake_count) - fw_engine = FORCEWAKE_ALL; - } - - if (fw_engine) - dev_priv->uncore.funcs.force_wake_get(dev_priv, fw_engine); + intel_uncore_forcewake_reset(dev, true); - if (IS_GEN6(dev) || IS_GEN7(dev)) - dev_priv->uncore.fifo_count = - __raw_i915_read32(dev_priv, GTFIFOCTL) & - GT_FIFO_FREE_ENTRIES_MASK; - - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); return ret; } From 9dc4056026e0df30f6b29109e1e7a6958e7bea62 Mon Sep 17 00:00:00 2001 From: Jani Nikula <jani.nikula@intel.com> Date: Fri, 14 Mar 2014 16:51:12 +0200 Subject: [PATCH 29/75] drm/dp: let drivers specify the name of the I2C-over-AUX adapter Let the drivers specify the name of the I2C-over-AUX adapter to maintain backwards compatibility in the sysfs when converting to the new I2C-over-AUX helper infrastructure. The i915 driver currently uses DPDDC-A to DPDDC-D as names for the DP i2c adapters. These names show up in the i2c sysfs name attribute. We'd like to be able to maintain that when switching over to the new helpers. Due to i2c device and connector cleanup ordering issues we also recently made the drm device (instead of connector) the parent of the i2c adapters: commit 80f65de3c9b8101c1613fa82df500ba6a099a11c Author: Imre Deak <imre.deak@intel.com> Date: Tue Feb 11 17:12:49 2014 +0200 drm/i915: dp: fix order of dp aux i2c device cleanup With the name picked up from the adapter parent using dev_name(), it would be the same for all i2c adapters with the current I2C-over-AUX helpers. Signed-off-by: Jani Nikula <jani.nikula@intel.com> Reviewed-by: Thierry Reding <treding@nvidia.com> Acked-by: Dave Airlie <airlied@redhat.com> Reviewed-by: Rodrigo Vivi <rodrigo.vivi@gmail.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/drm_dp_helper.c | 3 ++- include/drm/drm_dp_helper.h | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 35251af3b14e8..17832d0481476 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -726,7 +726,8 @@ int drm_dp_aux_register_i2c_bus(struct drm_dp_aux *aux) aux->ddc.dev.parent = aux->dev; aux->ddc.dev.of_node = aux->dev->of_node; - strncpy(aux->ddc.name, dev_name(aux->dev), sizeof(aux->ddc.name)); + strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev), + sizeof(aux->ddc.name)); return i2c_add_adapter(&aux->ddc); } diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 42947566e755c..b4f58914bf7d9 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -438,6 +438,9 @@ struct drm_dp_aux_msg { * The .dev field should be set to a pointer to the device that implements * the AUX channel. * + * The .name field may be used to specify the name of the I2C adapter. If set to + * NULL, dev_name() of .dev will be used. + * * Drivers provide a hardware-specific implementation of how transactions * are executed via the .transfer() function. A pointer to a drm_dp_aux_msg * structure describing the transaction is passed into this function. Upon @@ -455,6 +458,7 @@ struct drm_dp_aux_msg { * should call drm_dp_aux_unregister_i2c_bus() to remove the I2C adapter. */ struct drm_dp_aux { + const char *name; struct i2c_adapter ddc; struct device *dev; From adddaaf4885403a2f2180fb522b5b97e1469b328 Mon Sep 17 00:00:00 2001 From: Jani Nikula <jani.nikula@intel.com> Date: Fri, 14 Mar 2014 16:51:13 +0200 Subject: [PATCH 30/75] drm/i915/dp: split edp_panel_vdd_on() for reuse Introduce _edp_panel_vdd_on() that returns true if the call enabled vdd, and a matching disable is needed. Keep edp_panel_vdd_on() as a helper for when it is expected the vdd is off. Signed-off-by: Jani Nikula <jani.nikula@intel.com> Reviewed-by: Rodrigo Vivi <rodrigo.vivi@gmail.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_dp.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 49d12d341ab2b..b463769b93e5d 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -91,6 +91,7 @@ static struct intel_dp *intel_attached_dp(struct drm_connector *connector) } static void intel_dp_link_down(struct intel_dp *intel_dp); +static bool _edp_panel_vdd_on(struct intel_dp *intel_dp); static void edp_panel_vdd_on(struct intel_dp *intel_dp); static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync); @@ -1162,23 +1163,21 @@ static u32 ironlake_get_pp_control(struct intel_dp *intel_dp) return control; } -static void edp_panel_vdd_on(struct intel_dp *intel_dp) +static bool _edp_panel_vdd_on(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; u32 pp_stat_reg, pp_ctrl_reg; + bool need_to_disable = !intel_dp->want_panel_vdd; if (!is_edp(intel_dp)) - return; - - WARN(intel_dp->want_panel_vdd, - "eDP VDD already requested on\n"); + return false; intel_dp->want_panel_vdd = true; if (edp_have_panel_vdd(intel_dp)) - return; + return need_to_disable; intel_runtime_pm_get(dev_priv); @@ -1204,6 +1203,17 @@ static void edp_panel_vdd_on(struct intel_dp *intel_dp) DRM_DEBUG_KMS("eDP was not running\n"); msleep(intel_dp->panel_power_up_delay); } + + return need_to_disable; +} + +static void edp_panel_vdd_on(struct intel_dp *intel_dp) +{ + if (is_edp(intel_dp)) { + bool vdd = _edp_panel_vdd_on(intel_dp); + + WARN(!vdd, "eDP VDD already requested on\n"); + } } static void edp_panel_vdd_off_sync(struct intel_dp *intel_dp) From 884f19e948894fc87b03b631fd03a0998c0ca1ef Mon Sep 17 00:00:00 2001 From: Jani Nikula <jani.nikula@intel.com> Date: Fri, 14 Mar 2014 16:51:14 +0200 Subject: [PATCH 31/75] drm/i915/dp: move edp vdd enable/disable at a lower level in i2c-over-aux This is prep work for conversion to generic drm i2c-over-aux helpers where we won't have the function to do this at. Signed-off-by: Jani Nikula <jani.nikula@intel.com> Reviewed-by: Rodrigo Vivi <rodrigo.vivi@gmail.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_dp.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index b463769b93e5d..17d73511e1482 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -461,6 +461,9 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, uint32_t status; int try, clock = 0; bool has_aux_irq = HAS_AUX_IRQ(dev); + bool vdd; + + vdd = _edp_panel_vdd_on(intel_dp); /* dp aux is extremely sensitive to irq latency, hence request the * lowest possible wakeup latency and so prevent the cpu from going into @@ -566,6 +569,9 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, pm_qos_update_request(&dev_priv->pm_qos, PM_QOS_DEFAULT_VALUE); intel_aux_display_runtime_put(dev_priv); + if (vdd) + edp_panel_vdd_off(intel_dp, false); + return ret; } @@ -678,8 +684,6 @@ intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, int reply_bytes; int ret; - edp_panel_vdd_on(intel_dp); - intel_dp_check_edp(intel_dp); /* Set up the command byte */ if (mode & MODE_I2C_READ) msg[0] = DP_AUX_I2C_READ << 4; @@ -781,7 +785,6 @@ intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, ret = -EREMOTEIO; out: - edp_panel_vdd_off(intel_dp, false); return ret; } From 9d1a1031e84f30f3671f0a650fc38a7c588acc8a Mon Sep 17 00:00:00 2001 From: Jani Nikula <jani.nikula@intel.com> Date: Fri, 14 Mar 2014 16:51:15 +0200 Subject: [PATCH 32/75] drm/i915/dp: use the new drm helpers for dp aux Functionality remains largely the same as before. Note that the retry loops and native reply handling all moved into the core drm helper functions now. Signed-off-by: Jani Nikula <jani.nikula@intel.com> [danvet: Fix up the stray ; Rodrigo spotted in his review and add a note to the commit message to answer Rodrigo's question in his review.] Reviewed-by: Rodrigo Vivi <rodrigo.vivi@gmail.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_dp.c | 257 ++++++++++++++----------------- drivers/gpu/drm/i915/intel_drv.h | 1 + 2 files changed, 116 insertions(+), 142 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 17d73511e1482..b31f6db5d0c0a 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -575,97 +575,77 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, return ret; } -/* Write data to the aux channel in native mode */ -static int -intel_dp_aux_native_write(struct intel_dp *intel_dp, - uint16_t address, uint8_t *send, int send_bytes) +#define HEADER_SIZE 4 +static ssize_t +intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) { + struct intel_dp *intel_dp = container_of(aux, struct intel_dp, aux); + uint8_t txbuf[20], rxbuf[20]; + size_t txsize, rxsize; int ret; - uint8_t msg[20]; - int msg_bytes; - uint8_t ack; - int retry; - if (WARN_ON(send_bytes > 16)) - return -E2BIG; + txbuf[0] = msg->request << 4; + txbuf[1] = msg->address >> 8; + txbuf[2] = msg->address & 0xff; + txbuf[3] = msg->size - 1; - intel_dp_check_edp(intel_dp); - msg[0] = DP_AUX_NATIVE_WRITE << 4; - msg[1] = address >> 8; - msg[2] = address & 0xff; - msg[3] = send_bytes - 1; - memcpy(&msg[4], send, send_bytes); - msg_bytes = send_bytes + 4; - for (retry = 0; retry < 7; retry++) { - ret = intel_dp_aux_ch(intel_dp, msg, msg_bytes, &ack, 1); - if (ret < 0) - return ret; - ack >>= 4; - if ((ack & DP_AUX_NATIVE_REPLY_MASK) == DP_AUX_NATIVE_REPLY_ACK) - return send_bytes; - else if ((ack & DP_AUX_NATIVE_REPLY_MASK) == DP_AUX_NATIVE_REPLY_DEFER) - usleep_range(400, 500); - else - return -EIO; - } + switch (msg->request & ~DP_AUX_I2C_MOT) { + case DP_AUX_NATIVE_WRITE: + case DP_AUX_I2C_WRITE: + txsize = HEADER_SIZE + msg->size; + rxsize = 1; - DRM_ERROR("too many retries, giving up\n"); - return -EIO; -} + if (WARN_ON(txsize > 20)) + return -E2BIG; -/* Write a single byte to the aux channel in native mode */ -static int -intel_dp_aux_native_write_1(struct intel_dp *intel_dp, - uint16_t address, uint8_t byte) -{ - return intel_dp_aux_native_write(intel_dp, address, &byte, 1); -} + memcpy(txbuf + HEADER_SIZE, msg->buffer, msg->size); -/* read bytes from a native aux channel */ -static int -intel_dp_aux_native_read(struct intel_dp *intel_dp, - uint16_t address, uint8_t *recv, int recv_bytes) -{ - uint8_t msg[4]; - int msg_bytes; - uint8_t reply[20]; - int reply_bytes; - uint8_t ack; - int ret; - int retry; + ret = intel_dp_aux_ch(intel_dp, txbuf, txsize, rxbuf, rxsize); + if (ret > 0) { + msg->reply = rxbuf[0] >> 4; - if (WARN_ON(recv_bytes > 19)) - return -E2BIG; + /* Return payload size. */ + ret = msg->size; + } + break; - intel_dp_check_edp(intel_dp); - msg[0] = DP_AUX_NATIVE_READ << 4; - msg[1] = address >> 8; - msg[2] = address & 0xff; - msg[3] = recv_bytes - 1; + case DP_AUX_NATIVE_READ: + case DP_AUX_I2C_READ: + txsize = HEADER_SIZE; + rxsize = msg->size + 1; - msg_bytes = 4; - reply_bytes = recv_bytes + 1; + if (WARN_ON(rxsize > 20)) + return -E2BIG; - for (retry = 0; retry < 7; retry++) { - ret = intel_dp_aux_ch(intel_dp, msg, msg_bytes, - reply, reply_bytes); - if (ret == 0) - return -EPROTO; - if (ret < 0) - return ret; - ack = reply[0] >> 4; - if ((ack & DP_AUX_NATIVE_REPLY_MASK) == DP_AUX_NATIVE_REPLY_ACK) { - memcpy(recv, reply + 1, ret - 1); - return ret - 1; + ret = intel_dp_aux_ch(intel_dp, txbuf, txsize, rxbuf, rxsize); + if (ret > 0) { + msg->reply = rxbuf[0] >> 4; + /* + * Assume happy day, and copy the data. The caller is + * expected to check msg->reply before touching it. + * + * Return payload size. + */ + ret--; + memcpy(msg->buffer, rxbuf + 1, ret); } - else if ((ack & DP_AUX_NATIVE_REPLY_MASK) == DP_AUX_NATIVE_REPLY_DEFER) - usleep_range(400, 500); - else - return -EIO; + break; + + default: + ret = -EINVAL; + break; } - DRM_ERROR("too many retries, giving up\n"); - return -EIO; + return ret; +} + +static void +intel_dp_aux_init(struct intel_dp *intel_dp, struct intel_connector *connector) +{ + struct drm_device *dev = intel_dp_to_dev(intel_dp); + + intel_dp->aux.dev = dev->dev; + intel_dp->aux.transfer = intel_dp_aux_transfer; } static int @@ -1472,8 +1452,8 @@ void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode) return; if (mode != DRM_MODE_DPMS_ON) { - ret = intel_dp_aux_native_write_1(intel_dp, DP_SET_POWER, - DP_SET_POWER_D3); + ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_SET_POWER, + DP_SET_POWER_D3); if (ret != 1) DRM_DEBUG_DRIVER("failed to write sink power state\n"); } else { @@ -1482,9 +1462,8 @@ void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode) * time to wake up. */ for (i = 0; i < 3; i++) { - ret = intel_dp_aux_native_write_1(intel_dp, - DP_SET_POWER, - DP_SET_POWER_D0); + ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_SET_POWER, + DP_SET_POWER_D0); if (ret == 1) break; msleep(1); @@ -1708,13 +1687,11 @@ static void intel_edp_psr_enable_sink(struct intel_dp *intel_dp) /* Enable PSR in sink */ if (intel_dp->psr_dpcd[1] & DP_PSR_NO_TRAIN_ON_EXIT) - intel_dp_aux_native_write_1(intel_dp, DP_PSR_EN_CFG, - DP_PSR_ENABLE & - ~DP_PSR_MAIN_LINK_ACTIVE); + drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG, + DP_PSR_ENABLE & ~DP_PSR_MAIN_LINK_ACTIVE); else - intel_dp_aux_native_write_1(intel_dp, DP_PSR_EN_CFG, - DP_PSR_ENABLE | - DP_PSR_MAIN_LINK_ACTIVE); + drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG, + DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE); /* Setup AUX registers */ I915_WRITE(EDP_PSR_AUX_DATA1(dev), EDP_PSR_DPCD_COMMAND); @@ -2026,26 +2003,25 @@ static void vlv_dp_pre_pll_enable(struct intel_encoder *encoder) /* * Native read with retry for link status and receiver capability reads for * cases where the sink may still be asleep. + * + * Sinks are *supposed* to come up within 1ms from an off state, but we're also + * supposed to retry 3 times per the spec. */ -static bool -intel_dp_aux_native_read_retry(struct intel_dp *intel_dp, uint16_t address, - uint8_t *recv, int recv_bytes) +static ssize_t +intel_dp_dpcd_read_wake(struct drm_dp_aux *aux, unsigned int offset, + void *buffer, size_t size) { - int ret, i; + ssize_t ret; + int i; - /* - * Sinks are *supposed* to come up within 1ms from an off state, - * but we're also supposed to retry 3 times per the spec. - */ for (i = 0; i < 3; i++) { - ret = intel_dp_aux_native_read(intel_dp, address, recv, - recv_bytes); - if (ret == recv_bytes) - return true; + ret = drm_dp_dpcd_read(aux, offset, buffer, size); + if (ret == size) + return ret; msleep(1); } - return false; + return ret; } /* @@ -2055,10 +2031,10 @@ intel_dp_aux_native_read_retry(struct intel_dp *intel_dp, uint16_t address, static bool intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE]) { - return intel_dp_aux_native_read_retry(intel_dp, - DP_LANE0_1_STATUS, - link_status, - DP_LINK_STATUS_SIZE); + return intel_dp_dpcd_read_wake(&intel_dp->aux, + DP_LANE0_1_STATUS, + link_status, + DP_LINK_STATUS_SIZE) == DP_LINK_STATUS_SIZE; } /* @@ -2572,8 +2548,8 @@ intel_dp_set_link_train(struct intel_dp *intel_dp, len = intel_dp->lane_count + 1; } - ret = intel_dp_aux_native_write(intel_dp, DP_TRAINING_PATTERN_SET, - buf, len); + ret = drm_dp_dpcd_write(&intel_dp->aux, DP_TRAINING_PATTERN_SET, + buf, len); return ret == len; } @@ -2602,9 +2578,8 @@ intel_dp_update_link_train(struct intel_dp *intel_dp, uint32_t *DP, I915_WRITE(intel_dp->output_reg, *DP); POSTING_READ(intel_dp->output_reg); - ret = intel_dp_aux_native_write(intel_dp, DP_TRAINING_LANE0_SET, - intel_dp->train_set, - intel_dp->lane_count); + ret = drm_dp_dpcd_write(&intel_dp->aux, DP_TRAINING_LANE0_SET, + intel_dp->train_set, intel_dp->lane_count); return ret == intel_dp->lane_count; } @@ -2660,11 +2635,11 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) link_config[1] = intel_dp->lane_count; if (drm_dp_enhanced_frame_cap(intel_dp->dpcd)) link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; - intel_dp_aux_native_write(intel_dp, DP_LINK_BW_SET, link_config, 2); + drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_BW_SET, link_config, 2); link_config[0] = 0; link_config[1] = DP_SET_ANSI_8B10B; - intel_dp_aux_native_write(intel_dp, DP_DOWNSPREAD_CTRL, link_config, 2); + drm_dp_dpcd_write(&intel_dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2); DP |= DP_PORT_EN; @@ -2907,8 +2882,8 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) char dpcd_hex_dump[sizeof(intel_dp->dpcd) * 3]; - if (intel_dp_aux_native_read_retry(intel_dp, 0x000, intel_dp->dpcd, - sizeof(intel_dp->dpcd)) == 0) + if (intel_dp_dpcd_read_wake(&intel_dp->aux, 0x000, intel_dp->dpcd, + sizeof(intel_dp->dpcd)) < 0) return false; /* aux transfer failed */ hex_dump_to_buffer(intel_dp->dpcd, sizeof(intel_dp->dpcd), @@ -2921,9 +2896,9 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) /* Check if the panel supports PSR */ memset(intel_dp->psr_dpcd, 0, sizeof(intel_dp->psr_dpcd)); if (is_edp(intel_dp)) { - intel_dp_aux_native_read_retry(intel_dp, DP_PSR_SUPPORT, - intel_dp->psr_dpcd, - sizeof(intel_dp->psr_dpcd)); + intel_dp_dpcd_read_wake(&intel_dp->aux, DP_PSR_SUPPORT, + intel_dp->psr_dpcd, + sizeof(intel_dp->psr_dpcd)); if (intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED) { dev_priv->psr.sink_support = true; DRM_DEBUG_KMS("Detected EDP PSR Panel.\n"); @@ -2945,9 +2920,9 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) if (intel_dp->dpcd[DP_DPCD_REV] == 0x10) return true; /* no per-port downstream info */ - if (intel_dp_aux_native_read_retry(intel_dp, DP_DOWNSTREAM_PORT_0, - intel_dp->downstream_ports, - DP_MAX_DOWNSTREAM_PORTS) == 0) + if (intel_dp_dpcd_read_wake(&intel_dp->aux, DP_DOWNSTREAM_PORT_0, + intel_dp->downstream_ports, + DP_MAX_DOWNSTREAM_PORTS) < 0) return false; /* downstream port status fetch failed */ return true; @@ -2963,11 +2938,11 @@ intel_dp_probe_oui(struct intel_dp *intel_dp) edp_panel_vdd_on(intel_dp); - if (intel_dp_aux_native_read_retry(intel_dp, DP_SINK_OUI, buf, 3)) + if (intel_dp_dpcd_read_wake(&intel_dp->aux, DP_SINK_OUI, buf, 3) == 3) DRM_DEBUG_KMS("Sink OUI: %02hx%02hx%02hx\n", buf[0], buf[1], buf[2]); - if (intel_dp_aux_native_read_retry(intel_dp, DP_BRANCH_OUI, buf, 3)) + if (intel_dp_dpcd_read_wake(&intel_dp->aux, DP_BRANCH_OUI, buf, 3) == 3) DRM_DEBUG_KMS("Branch OUI: %02hx%02hx%02hx\n", buf[0], buf[1], buf[2]); @@ -2982,46 +2957,40 @@ int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc) to_intel_crtc(intel_dig_port->base.base.crtc); u8 buf[1]; - if (!intel_dp_aux_native_read(intel_dp, DP_TEST_SINK_MISC, buf, 1)) + if (drm_dp_dpcd_readb(&intel_dp->aux, DP_TEST_SINK_MISC, buf) < 0) return -EAGAIN; if (!(buf[0] & DP_TEST_CRC_SUPPORTED)) return -ENOTTY; - if (!intel_dp_aux_native_write_1(intel_dp, DP_TEST_SINK, - DP_TEST_SINK_START)) + if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_TEST_SINK, + DP_TEST_SINK_START) < 0) return -EAGAIN; /* Wait 2 vblanks to be sure we will have the correct CRC value */ intel_wait_for_vblank(dev, intel_crtc->pipe); intel_wait_for_vblank(dev, intel_crtc->pipe); - if (!intel_dp_aux_native_read(intel_dp, DP_TEST_CRC_R_CR, crc, 6)) + if (drm_dp_dpcd_read(&intel_dp->aux, DP_TEST_CRC_R_CR, crc, 6) < 0) return -EAGAIN; - intel_dp_aux_native_write_1(intel_dp, DP_TEST_SINK, 0); + drm_dp_dpcd_writeb(&intel_dp->aux, DP_TEST_SINK, 0); return 0; } static bool intel_dp_get_sink_irq(struct intel_dp *intel_dp, u8 *sink_irq_vector) { - int ret; - - ret = intel_dp_aux_native_read_retry(intel_dp, - DP_DEVICE_SERVICE_IRQ_VECTOR, - sink_irq_vector, 1); - if (!ret) - return false; - - return true; + return intel_dp_dpcd_read_wake(&intel_dp->aux, + DP_DEVICE_SERVICE_IRQ_VECTOR, + sink_irq_vector, 1) == 1; } static void intel_dp_handle_test_request(struct intel_dp *intel_dp) { /* NAK by default */ - intel_dp_aux_native_write_1(intel_dp, DP_TEST_RESPONSE, DP_TEST_NAK); + drm_dp_dpcd_writeb(&intel_dp->aux, DP_TEST_RESPONSE, DP_TEST_NAK); } /* @@ -3060,9 +3029,9 @@ intel_dp_check_link_status(struct intel_dp *intel_dp) if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 && intel_dp_get_sink_irq(intel_dp, &sink_irq_vector)) { /* Clear interrupt source */ - intel_dp_aux_native_write_1(intel_dp, - DP_DEVICE_SERVICE_IRQ_VECTOR, - sink_irq_vector); + drm_dp_dpcd_writeb(&intel_dp->aux, + DP_DEVICE_SERVICE_IRQ_VECTOR, + sink_irq_vector); if (sink_irq_vector & DP_AUTOMATED_TEST_REQUEST) intel_dp_handle_test_request(intel_dp); @@ -3097,9 +3066,11 @@ intel_dp_detect_dpcd(struct intel_dp *intel_dp) if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 && intel_dp->downstream_ports[0] & DP_DS_PORT_HPD) { uint8_t reg; - if (!intel_dp_aux_native_read_retry(intel_dp, DP_SINK_COUNT, - ®, 1)) + + if (intel_dp_dpcd_read_wake(&intel_dp->aux, DP_SINK_COUNT, + ®, 1) < 0) return connector_status_unknown; + return DP_GET_SINK_COUNT(reg) ? connector_status_connected : connector_status_disconnected; } @@ -3925,6 +3896,8 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); } + intel_dp_aux_init(intel_dp, intel_connector); + error = intel_dp_i2c_init(intel_dp, intel_connector, name); WARN(error, "intel_dp_i2c_init failed with error %d for port %c\n", error, port_name(port)); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 9c7090590776b..578c18ed982c7 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -487,6 +487,7 @@ struct intel_dp { uint8_t downstream_ports[DP_MAX_DOWNSTREAM_PORTS]; struct i2c_adapter adapter; struct i2c_algo_dp_aux_data algo; + struct drm_dp_aux aux; uint8_t train_set[4]; int panel_power_up_delay; int panel_power_down_delay; From 33ad6626a1a9155fcbb04869c7cdde0552976396 Mon Sep 17 00:00:00 2001 From: Jani Nikula <jani.nikula@intel.com> Date: Fri, 14 Mar 2014 16:51:16 +0200 Subject: [PATCH 33/75] drm/i915/dp: move dp aux ch register init to aux init Do a slight rearrangement of the switch to prep for follow-up. Signed-off-by: Jani Nikula <jani.nikula@intel.com> Reviewed-by: Rodrigo Vivi <rodrigo.vivi@gmail.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_dp.c | 42 +++++++++++++++++---------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index b31f6db5d0c0a..623b50c46bbcf 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -643,6 +643,28 @@ static void intel_dp_aux_init(struct intel_dp *intel_dp, struct intel_connector *connector) { struct drm_device *dev = intel_dp_to_dev(intel_dp); + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + enum port port = intel_dig_port->port; + + switch (port) { + case PORT_A: + intel_dp->aux_ch_ctl_reg = DPA_AUX_CH_CTL; + break; + case PORT_B: + intel_dp->aux_ch_ctl_reg = PCH_DPB_AUX_CH_CTL; + break; + case PORT_C: + intel_dp->aux_ch_ctl_reg = PCH_DPC_AUX_CH_CTL; + break; + case PORT_D: + intel_dp->aux_ch_ctl_reg = PCH_DPD_AUX_CH_CTL; + break; + default: + BUG(); + } + + if (!HAS_DDI(dev)) + intel_dp->aux_ch_ctl_reg = intel_dp->output_reg + 0x10; intel_dp->aux.dev = dev->dev; intel_dp->aux.transfer = intel_dp_aux_transfer; @@ -3849,26 +3871,6 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, intel_connector->get_hw_state = intel_connector_get_hw_state; intel_connector->unregister = intel_dp_connector_unregister; - intel_dp->aux_ch_ctl_reg = intel_dp->output_reg + 0x10; - if (HAS_DDI(dev)) { - switch (intel_dig_port->port) { - case PORT_A: - intel_dp->aux_ch_ctl_reg = DPA_AUX_CH_CTL; - break; - case PORT_B: - intel_dp->aux_ch_ctl_reg = PCH_DPB_AUX_CH_CTL; - break; - case PORT_C: - intel_dp->aux_ch_ctl_reg = PCH_DPC_AUX_CH_CTL; - break; - case PORT_D: - intel_dp->aux_ch_ctl_reg = PCH_DPD_AUX_CH_CTL; - break; - default: - BUG(); - } - } - /* Set up the DDC bus. */ switch (port) { case PORT_A: From 0b99836f238f37a8632a3ab4f9a8cc2346a36d40 Mon Sep 17 00:00:00 2001 From: Jani Nikula <jani.nikula@intel.com> Date: Fri, 14 Mar 2014 16:51:17 +0200 Subject: [PATCH 34/75] drm/i915/dp: use the new drm helpers for dp i2c-over-aux The functionality remains largerly the same. The main difference is that i2c-over-aux defer timeouts are increased to be safe for all use cases instead of depending on DP device type and properties. Signed-off-by: Jani Nikula <jani.nikula@intel.com> Reviewed-by: Rodrigo Vivi <rodrigo.vivi@gmail.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_dp.c | 197 +++++-------------------------- drivers/gpu/drm/i915/intel_drv.h | 2 - 2 files changed, 30 insertions(+), 169 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 623b50c46bbcf..160d5b30b3bc6 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -645,19 +645,25 @@ intel_dp_aux_init(struct intel_dp *intel_dp, struct intel_connector *connector) struct drm_device *dev = intel_dp_to_dev(intel_dp); struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); enum port port = intel_dig_port->port; + const char *name = NULL; + int ret; switch (port) { case PORT_A: intel_dp->aux_ch_ctl_reg = DPA_AUX_CH_CTL; + name = "DPDDC-A"; break; case PORT_B: intel_dp->aux_ch_ctl_reg = PCH_DPB_AUX_CH_CTL; + name = "DPDDC-B"; break; case PORT_C: intel_dp->aux_ch_ctl_reg = PCH_DPC_AUX_CH_CTL; + name = "DPDDC-C"; break; case PORT_D: intel_dp->aux_ch_ctl_reg = PCH_DPD_AUX_CH_CTL; + name = "DPDDC-D"; break; default: BUG(); @@ -666,128 +672,27 @@ intel_dp_aux_init(struct intel_dp *intel_dp, struct intel_connector *connector) if (!HAS_DDI(dev)) intel_dp->aux_ch_ctl_reg = intel_dp->output_reg + 0x10; + intel_dp->aux.name = name; intel_dp->aux.dev = dev->dev; intel_dp->aux.transfer = intel_dp_aux_transfer; -} -static int -intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, - uint8_t write_byte, uint8_t *read_byte) -{ - struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; - struct intel_dp *intel_dp = container_of(adapter, - struct intel_dp, - adapter); - uint16_t address = algo_data->address; - uint8_t msg[5]; - uint8_t reply[2]; - unsigned retry; - int msg_bytes; - int reply_bytes; - int ret; - - /* Set up the command byte */ - if (mode & MODE_I2C_READ) - msg[0] = DP_AUX_I2C_READ << 4; - else - msg[0] = DP_AUX_I2C_WRITE << 4; + DRM_DEBUG_KMS("registering %s bus for %s\n", name, + connector->base.kdev->kobj.name); - if (!(mode & MODE_I2C_STOP)) - msg[0] |= DP_AUX_I2C_MOT << 4; - - msg[1] = address >> 8; - msg[2] = address; - - switch (mode) { - case MODE_I2C_WRITE: - msg[3] = 0; - msg[4] = write_byte; - msg_bytes = 5; - reply_bytes = 1; - break; - case MODE_I2C_READ: - msg[3] = 0; - msg_bytes = 4; - reply_bytes = 2; - break; - default: - msg_bytes = 3; - reply_bytes = 1; - break; + ret = drm_dp_aux_register_i2c_bus(&intel_dp->aux); + if (ret < 0) { + DRM_ERROR("drm_dp_aux_register_i2c_bus() for %s failed (%d)\n", + name, ret); + return; } - /* - * DP1.2 sections 2.7.7.1.5.6.1 and 2.7.7.1.6.6.1: A DP Source device is - * required to retry at least seven times upon receiving AUX_DEFER - * before giving up the AUX transaction. - */ - for (retry = 0; retry < 7; retry++) { - ret = intel_dp_aux_ch(intel_dp, - msg, msg_bytes, - reply, reply_bytes); - if (ret < 0) { - DRM_DEBUG_KMS("aux_ch failed %d\n", ret); - goto out; - } - - switch ((reply[0] >> 4) & DP_AUX_NATIVE_REPLY_MASK) { - case DP_AUX_NATIVE_REPLY_ACK: - /* I2C-over-AUX Reply field is only valid - * when paired with AUX ACK. - */ - break; - case DP_AUX_NATIVE_REPLY_NACK: - DRM_DEBUG_KMS("aux_ch native nack\n"); - ret = -EREMOTEIO; - goto out; - case DP_AUX_NATIVE_REPLY_DEFER: - /* - * For now, just give more slack to branch devices. We - * could check the DPCD for I2C bit rate capabilities, - * and if available, adjust the interval. We could also - * be more careful with DP-to-Legacy adapters where a - * long legacy cable may force very low I2C bit rates. - */ - if (intel_dp->dpcd[DP_DOWNSTREAMPORT_PRESENT] & - DP_DWN_STRM_PORT_PRESENT) - usleep_range(500, 600); - else - usleep_range(300, 400); - continue; - default: - DRM_ERROR("aux_ch invalid native reply 0x%02x\n", - reply[0]); - ret = -EREMOTEIO; - goto out; - } - - switch ((reply[0] >> 4) & DP_AUX_I2C_REPLY_MASK) { - case DP_AUX_I2C_REPLY_ACK: - if (mode == MODE_I2C_READ) { - *read_byte = reply[1]; - } - ret = reply_bytes - 1; - goto out; - case DP_AUX_I2C_REPLY_NACK: - DRM_DEBUG_KMS("aux_i2c nack\n"); - ret = -EREMOTEIO; - goto out; - case DP_AUX_I2C_REPLY_DEFER: - DRM_DEBUG_KMS("aux_i2c defer\n"); - udelay(100); - break; - default: - DRM_ERROR("aux_i2c invalid reply 0x%02x\n", reply[0]); - ret = -EREMOTEIO; - goto out; - } + ret = sysfs_create_link(&connector->base.kdev->kobj, + &intel_dp->aux.ddc.dev.kobj, + intel_dp->aux.ddc.dev.kobj.name); + if (ret < 0) { + DRM_ERROR("sysfs_create_link() for %s failed (%d)\n", name, ret); + drm_dp_aux_unregister_i2c_bus(&intel_dp->aux); } - - DRM_ERROR("too many retries, giving up\n"); - ret = -EREMOTEIO; - -out: - return ret; } static void @@ -796,43 +701,10 @@ intel_dp_connector_unregister(struct intel_connector *intel_connector) struct intel_dp *intel_dp = intel_attached_dp(&intel_connector->base); sysfs_remove_link(&intel_connector->base.kdev->kobj, - intel_dp->adapter.dev.kobj.name); + intel_dp->aux.ddc.dev.kobj.name); intel_connector_unregister(intel_connector); } -static int -intel_dp_i2c_init(struct intel_dp *intel_dp, - struct intel_connector *intel_connector, const char *name) -{ - int ret; - - DRM_DEBUG_KMS("i2c_init %s\n", name); - intel_dp->algo.running = false; - intel_dp->algo.address = 0; - intel_dp->algo.aux_ch = intel_dp_i2c_aux_ch; - - memset(&intel_dp->adapter, '\0', sizeof(intel_dp->adapter)); - intel_dp->adapter.owner = THIS_MODULE; - intel_dp->adapter.class = I2C_CLASS_DDC; - strncpy(intel_dp->adapter.name, name, sizeof(intel_dp->adapter.name) - 1); - intel_dp->adapter.name[sizeof(intel_dp->adapter.name) - 1] = '\0'; - intel_dp->adapter.algo_data = &intel_dp->algo; - intel_dp->adapter.dev.parent = intel_connector->base.dev->dev; - - ret = i2c_dp_aux_add_bus(&intel_dp->adapter); - if (ret < 0) - return ret; - - ret = sysfs_create_link(&intel_connector->base.kdev->kobj, - &intel_dp->adapter.dev.kobj, - intel_dp->adapter.dev.kobj.name); - - if (ret < 0) - i2c_del_adapter(&intel_dp->adapter); - - return ret; -} - static void intel_dp_set_clock(struct intel_encoder *encoder, struct intel_crtc_config *pipe_config, int link_bw) @@ -3098,7 +2970,7 @@ intel_dp_detect_dpcd(struct intel_dp *intel_dp) } /* If no HPD, poke DDC gently */ - if (drm_probe_ddc(&intel_dp->adapter)) + if (drm_probe_ddc(&intel_dp->aux.ddc)) return connector_status_connected; /* Well we tried, say unknown for unreliable port types */ @@ -3266,7 +3138,7 @@ intel_dp_detect(struct drm_connector *connector, bool force) if (intel_dp->force_audio != HDMI_AUDIO_AUTO) { intel_dp->has_audio = (intel_dp->force_audio == HDMI_AUDIO_ON); } else { - edid = intel_dp_get_edid(connector, &intel_dp->adapter); + edid = intel_dp_get_edid(connector, &intel_dp->aux.ddc); if (edid) { intel_dp->has_audio = drm_detect_monitor_audio(edid); kfree(edid); @@ -3302,7 +3174,7 @@ static int intel_dp_get_modes(struct drm_connector *connector) power_domain = intel_display_port_power_domain(intel_encoder); intel_display_power_get(dev_priv, power_domain); - ret = intel_dp_get_edid_modes(connector, &intel_dp->adapter); + ret = intel_dp_get_edid_modes(connector, &intel_dp->aux.ddc); intel_display_power_put(dev_priv, power_domain); if (ret) return ret; @@ -3335,7 +3207,7 @@ intel_dp_detect_audio(struct drm_connector *connector) power_domain = intel_display_port_power_domain(intel_encoder); intel_display_power_get(dev_priv, power_domain); - edid = intel_dp_get_edid(connector, &intel_dp->adapter); + edid = intel_dp_get_edid(connector, &intel_dp->aux.ddc); if (edid) { has_audio = drm_detect_monitor_audio(edid); kfree(edid); @@ -3457,7 +3329,7 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder) struct intel_dp *intel_dp = &intel_dig_port->dp; struct drm_device *dev = intel_dp_to_dev(intel_dp); - i2c_del_adapter(&intel_dp->adapter); + drm_dp_aux_unregister_i2c_bus(&intel_dp->aux); drm_encoder_cleanup(encoder); if (is_edp(intel_dp)) { cancel_delayed_work_sync(&intel_dp->panel_vdd_work); @@ -3769,7 +3641,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, /* We now know it's not a ghost, init power sequence regs. */ intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, power_seq); - edid = drm_get_edid(connector, &intel_dp->adapter); + edid = drm_get_edid(connector, &intel_dp->aux.ddc); if (edid) { if (drm_add_edid_modes(connector, edid)) { drm_mode_connector_update_edid_property(connector, @@ -3817,8 +3689,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, struct drm_i915_private *dev_priv = dev->dev_private; enum port port = intel_dig_port->port; struct edp_power_seq power_seq = { 0 }; - const char *name = NULL; - int type, error; + int type; /* intel_dp vfuncs */ if (IS_VALLEYVIEW(dev)) @@ -3871,23 +3742,19 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, intel_connector->get_hw_state = intel_connector_get_hw_state; intel_connector->unregister = intel_dp_connector_unregister; - /* Set up the DDC bus. */ + /* Set up the hotplug pin. */ switch (port) { case PORT_A: intel_encoder->hpd_pin = HPD_PORT_A; - name = "DPDDC-A"; break; case PORT_B: intel_encoder->hpd_pin = HPD_PORT_B; - name = "DPDDC-B"; break; case PORT_C: intel_encoder->hpd_pin = HPD_PORT_C; - name = "DPDDC-C"; break; case PORT_D: intel_encoder->hpd_pin = HPD_PORT_D; - name = "DPDDC-D"; break; default: BUG(); @@ -3900,14 +3767,10 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, intel_dp_aux_init(intel_dp, intel_connector); - error = intel_dp_i2c_init(intel_dp, intel_connector, name); - WARN(error, "intel_dp_i2c_init failed with error %d for port %c\n", - error, port_name(port)); - intel_dp->psr_setup_done = false; if (!intel_edp_init_connector(intel_dp, intel_connector, &power_seq)) { - i2c_del_adapter(&intel_dp->adapter); + drm_dp_aux_unregister_i2c_bus(&intel_dp->aux); if (is_edp(intel_dp)) { cancel_delayed_work_sync(&intel_dp->panel_vdd_work); mutex_lock(&dev->mode_config.mutex); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 578c18ed982c7..5ca293b3ef5c2 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -485,8 +485,6 @@ struct intel_dp { uint8_t dpcd[DP_RECEIVER_CAP_SIZE]; uint8_t psr_dpcd[EDP_PSR_RECEIVER_CAP_SIZE]; uint8_t downstream_ports[DP_MAX_DOWNSTREAM_PORTS]; - struct i2c_adapter adapter; - struct i2c_algo_dp_aux_data algo; struct drm_dp_aux aux; uint8_t train_set[4]; int panel_power_up_delay; From 05efeebd2838f0dedf765179244a7fb543fdca8a Mon Sep 17 00:00:00 2001 From: Daniel Vetter <daniel.vetter@ffwll.ch> Date: Tue, 18 Mar 2014 16:26:25 +0100 Subject: [PATCH 35/75] drm/i915: Fix up the forcewake timer initialization This is a regression introduced in commit 0294ae7b44bba7ab0d4cef9a8736287f38bdb4fd Author: Chris Wilson <chris@chris-wilson.co.uk> Date: Thu Mar 13 12:00:29 2014 +0000 drm/i915: Consolidate forcewake resetting to a single function The reordered setup sequence ended up calling del_timer_sync before the timer was set up correctly, resulting in endless hilarity when loading the driver. Compared to Ben's patch (which moved around the setup_timer call to sanitize_early) this moves the sanitize_early call around in the driver load call. This way we avoid calling setup_timer again in the resume code (where we also call sanitize_early). Cc: Chris Wilson <chris@chris-wilson.co.uk> Cc: Mika Kuoppala <mika.kuoppala@intel.com> Cc: Ben Widawsky <benjamin.widawsky@intel.com> Tested-by: Rodrigo Vivi <rodrigo.vivi@gmail.com> Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=76242 Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_dma.c | 2 -- drivers/gpu/drm/i915/intel_uncore.c | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index e4d2b9f15ae29..9faee49f210d8 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1608,8 +1608,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) goto put_bridge; } - intel_uncore_early_sanitize(dev); - /* This must be called before any calls to HAS_PCH_* */ intel_detect_pch(dev); diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index e6bb421a3dbd7..ab5165c66381c 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -727,6 +727,8 @@ void intel_uncore_init(struct drm_device *dev) setup_timer(&dev_priv->uncore.force_wake_timer, gen6_force_wake_timer, (unsigned long)dev_priv); + intel_uncore_early_sanitize(dev); + if (IS_VALLEYVIEW(dev)) { dev_priv->uncore.funcs.force_wake_get = __vlv_force_wake_get; dev_priv->uncore.funcs.force_wake_put = __vlv_force_wake_put; From a95f6a007042e76627d9722cb1a81f97c718f74b Mon Sep 17 00:00:00 2001 From: Mika Kuoppala <mika.kuoppala@linux.intel.com> Date: Fri, 14 Mar 2014 16:22:10 +0200 Subject: [PATCH 36/75] drm/i915: Switch to fake context on older gens We used to have per file descriptor hang stats for the i915_get_reset_stats_ioctl() and for default context banning. commit 0eea67eb26000657079b7fc41079097942339452 Author: Ben Widawsky <ben@bwidawsk.net> Date: Fri Dec 6 14:11:19 2013 -0800 drm/i915: Create a per file_priv default context made having separate hangstats in file_private redundant as i915_hw_context already contained hangstats. So commit c482972a086e03e6a6d27e4f7af2d868bf659648 Author: Ben Widawsky <benjamin.widawsky@intel.com> Date: Fri Dec 6 14:11:20 2013 -0800 drm/i915: Piggy back hangstats off of contexts consolidated the hangstats and enabled further improvements. commit 44e2c0705a19e09d7b0f30a591f92e473e5ef89e Author: Mika Kuoppala <mika.kuoppala@linux.intel.com> Date: Thu Jan 30 16:01:15 2014 +0200 drm/i915: Use i915_hw_context to set reset stats tried to reap full benefits of consolidation but fell short as we never 'switch' to the fake private context on gens that don't have hw_contexts, so request->ctx remained NULL on those. Fix this by 'switching' to fake context so that when request is submitted to ring, proper context gets assigned to it. Testcase: igt/drv_hangman Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=76055 Signed-off-by: Mika Kuoppala <mika.kuoppala@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_gem_context.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index ce41cff843466..b5a58372eb06b 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -775,9 +775,11 @@ int i915_switch_context(struct intel_ring_buffer *ring, BUG_ON(file && to == NULL); - /* We have the fake context, but don't supports switching. */ - if (!HAS_HW_CONTEXTS(ring->dev)) + /* We have the fake context */ + if (!HAS_HW_CONTEXTS(ring->dev)) { + ring->last_context = to; return 0; + } return do_switch(ring, to); } From 849e39f5d7e52eb44d37bbd5ce695f7cdcbe923c Mon Sep 17 00:00:00 2001 From: Paulo Zanoni <paulo.r.zanoni@intel.com> Date: Fri, 7 Mar 2014 20:05:20 -0300 Subject: [PATCH 37/75] drm/i915: properly disable the VDD when disabling the panel Commit b3064154dfd37deb386b1e459c54e1ca2460b3d5 tried to revert commit dff392dbd258381a6c3164f38420593f2d291e3b, but wasn't complete, which resulted in regressions on Haswell. So this commit should fix b3064154dfd37deb386b1e459c54e1ca2460b3d5 by undoing what it did and providing an actual complete revert of dff392dbd258381a6c3164f38420593f2d291e3b. Fixes regression introduced by: commit b3064154dfd37deb386b1e459c54e1ca2460b3d5 Author: Patrik Jakobsson <patrik.r.jakobsson@gmail.com> Date: Tue Mar 4 00:42:44 2014 +0100 drm/i915: Don't just say it, actually force edp vdd Testcase: igt/pm_pc8 Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com> Tested-by: Patrik Jakobsson <patrik.r.jakobsson@gmail.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_ddi.c | 1 + drivers/gpu/drm/i915/intel_dp.c | 9 ++++++--- drivers/gpu/drm/i915/intel_drv.h | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 3565d61531f02..fe1f5f012c4f7 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1340,6 +1340,7 @@ static void intel_ddi_post_disable(struct intel_encoder *intel_encoder) if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF); + edp_panel_vdd_on(intel_dp); intel_edp_panel_off(intel_dp); } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index d2b2f51f839fe..a76406b3b6107 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -91,7 +91,6 @@ static struct intel_dp *intel_attached_dp(struct drm_connector *connector) } static void intel_dp_link_down(struct intel_dp *intel_dp); -static void edp_panel_vdd_on(struct intel_dp *intel_dp); static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync); static int @@ -1162,7 +1161,7 @@ static u32 ironlake_get_pp_control(struct intel_dp *intel_dp) return control; } -static void edp_panel_vdd_on(struct intel_dp *intel_dp) +void edp_panel_vdd_on(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; @@ -1338,11 +1337,16 @@ void intel_edp_panel_off(struct intel_dp *intel_dp) pp_ctrl_reg = _pp_ctrl_reg(intel_dp); + intel_dp->want_panel_vdd = false; + I915_WRITE(pp_ctrl_reg, pp); POSTING_READ(pp_ctrl_reg); intel_dp->last_power_cycle = jiffies; wait_panel_off(intel_dp); + + /* We got a reference when we enabled the VDD. */ + intel_runtime_pm_put(dev_priv); } void intel_edp_backlight_on(struct intel_dp *intel_dp) @@ -1880,7 +1884,6 @@ static void intel_disable_dp(struct intel_encoder *encoder) intel_edp_backlight_off(intel_dp); intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF); intel_edp_panel_off(intel_dp); - edp_panel_vdd_off(intel_dp, true); /* cpu edp my only be disable _after_ the cpu pipe/plane is disabled. */ if (!(port == PORT_A || IS_VALLEYVIEW(dev))) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 2546cae0b4f03..20e11f24b9a14 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -767,6 +767,7 @@ void intel_edp_panel_off(struct intel_dp *intel_dp); void intel_edp_psr_enable(struct intel_dp *intel_dp); void intel_edp_psr_disable(struct intel_dp *intel_dp); void intel_edp_psr_update(struct drm_device *dev); +void edp_panel_vdd_on(struct intel_dp *intel_dp); /* intel_dsi.c */ From ae89f44d13a36192cc19c1de04aa41ab0b3624cc Mon Sep 17 00:00:00 2001 From: Ben Widawsky <benjamin.widawsky@intel.com> Date: Fri, 14 Mar 2014 23:01:58 -0700 Subject: [PATCH 38/75] drm/i915: Actually capture PP_DIR_BASE on error I have been seeing this for a long time, but ignored it because it's typically not terribly important. Recently, I really needed this info, and it was garbage. Proof that I should have fixed it sooner. Originally wrong from: commit 6c7a01ec3743a5a6ce9e53a69d7a6c2d8c715eb1 Author: Ben Widawsky <benjamin.widawsky@intel.com> Date: Thu Jan 30 00:19:40 2014 -0800 drm/i915: Capture PPGTT info on error capture Cc: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Ben Widawsky <ben@bwidawsk.net> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_gpu_error.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index 144a5e2bc7ef3..baf1ca690dc5f 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -850,10 +850,12 @@ static void i915_record_ring_state(struct drm_device *dev, } break; case 7: - ering->vm_info.pp_dir_base = RING_PP_DIR_BASE(ring); + ering->vm_info.pp_dir_base = + I915_READ(RING_PP_DIR_BASE(ring)); break; case 6: - ering->vm_info.pp_dir_base = RING_PP_DIR_BASE_READ(ring); + ering->vm_info.pp_dir_base = + I915_READ(RING_PP_DIR_BASE_READ(ring)); break; } } From 3123fcafe0703ee0fd8952b4a81bb18c1c08c5a5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter <daniel.vetter@ffwll.ch> Date: Sat, 15 Mar 2014 20:20:29 +0100 Subject: [PATCH 39/75] drm/i915: catch forcewake reference underruns Without this the new drv_suspend/forcewake subtest I've created doesn't result in immediately visible failures. Cc: Mika Kuoppala <mika.kuoppala@intel.com> Cc: Ben Widawsky <ben@bwidawsk.net> Cc: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_uncore.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index ab5165c66381c..c3832d9270a60 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -280,12 +280,17 @@ void vlv_force_wake_put(struct drm_i915_private *dev_priv, spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); - if (fw_engine & FORCEWAKE_RENDER && - --dev_priv->uncore.fw_rendercount != 0) - fw_engine &= ~FORCEWAKE_RENDER; - if (fw_engine & FORCEWAKE_MEDIA && - --dev_priv->uncore.fw_mediacount != 0) - fw_engine &= ~FORCEWAKE_MEDIA; + if (fw_engine & FORCEWAKE_RENDER) { + WARN_ON(!dev_priv->uncore.fw_rendercount); + if (--dev_priv->uncore.fw_rendercount != 0) + fw_engine &= ~FORCEWAKE_RENDER; + } + + if (fw_engine & FORCEWAKE_MEDIA) { + WARN_ON(!dev_priv->uncore.fw_mediacount); + if (--dev_priv->uncore.fw_mediacount != 0) + fw_engine &= ~FORCEWAKE_MEDIA; + } if (fw_engine) dev_priv->uncore.funcs.force_wake_put(dev_priv, fw_engine); @@ -301,6 +306,8 @@ static void gen6_force_wake_timer(unsigned long arg) assert_device_not_suspended(dev_priv); spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); + WARN_ON(!dev_priv->uncore.forcewake_count); + if (--dev_priv->uncore.forcewake_count == 0) dev_priv->uncore.funcs.force_wake_put(dev_priv, FORCEWAKE_ALL); spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); @@ -452,6 +459,8 @@ void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv, int fw_engine) spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); + WARN_ON(!dev_priv->uncore.forcewake_count); + if (--dev_priv->uncore.forcewake_count == 0) { dev_priv->uncore.forcewake_count++; delayed = true; From 24f3e092b8e2939365e2105c2eed3e3db2813aa6 Mon Sep 17 00:00:00 2001 From: Jani Nikula <jani.nikula@intel.com> Date: Mon, 17 Mar 2014 16:43:36 +0200 Subject: [PATCH 40/75] drm/i915: finish off reverting eDP VDD changes This is a small follow-up fix to the series of eDP VDD back and forth we've had recently. This is effectively a combined revert of three commits: commit 2c2894f698fffd8ff53e1e1d3834f9e1035b1f39 Author: Paulo Zanoni <paulo.r.zanoni@intel.com> Date: Fri Mar 7 20:05:20 2014 -0300 drm/i915: properly disable the VDD when disabling the panel commit b3064154dfd37deb386b1e459c54e1ca2460b3d5 Author: Patrik Jakobsson <patrik.r.jakobsson@gmail.com> Date: Tue Mar 4 00:42:44 2014 +0100 drm/i915: Don't just say it, actually force edp vdd commit dff392dbd258381a6c3164f38420593f2d291e3b Author: Paulo Zanoni <paulo.r.zanoni@intel.com> Date: Fri Dec 6 17:32:41 2013 -0200 drm/i915: don't touch the VDD when disabling the panel which shows that we're pretty close back to where we started already. The first two were basically reverting the last, but missing the WARN. Add that back. We also OCD the intel_ prefix back to intel_edp_panel_vdd_on() which was lost somewhere in between. The circle closes. For future reference, "drm/i915: don't touch the VDD when disabling the panel" failed to take into account commit 6cb49835da0426f69a2931bc2a0a8156344b0e41 Author: Daniel Vetter <daniel.vetter@ffwll.ch> Date: Sun May 20 17:14:50 2012 +0200 drm/i915: enable vdd when switching off the eDP panel and commit 35a38556d900b9cb5dfa2529c93944b847f8a8a4 Author: Daniel Vetter <daniel.vetter@ffwll.ch> Date: Sun Aug 12 22:17:14 2012 +0200 drm/i915: reorder edp disabling to fix ivb MacBook Air Cc: Patrik Jakobsson <patrik.r.jakobsson@gmail.com> Cc: Paulo Zanoni <paulo.r.zanoni@intel.com> Signed-off-by: Jani Nikula <jani.nikula@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_ddi.c | 2 +- drivers/gpu/drm/i915/intel_dp.c | 14 ++++++++------ drivers/gpu/drm/i915/intel_drv.h | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index fe1f5f012c4f7..070bf2e78d616 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1340,7 +1340,7 @@ static void intel_ddi_post_disable(struct intel_encoder *intel_encoder) if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF); - edp_panel_vdd_on(intel_dp); + intel_edp_panel_vdd_on(intel_dp); intel_edp_panel_off(intel_dp); } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index a76406b3b6107..fb8a967df0273 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -676,7 +676,7 @@ intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, int reply_bytes; int ret; - edp_panel_vdd_on(intel_dp); + intel_edp_panel_vdd_on(intel_dp); intel_dp_check_edp(intel_dp); /* Set up the command byte */ if (mode & MODE_I2C_READ) @@ -1161,7 +1161,7 @@ static u32 ironlake_get_pp_control(struct intel_dp *intel_dp) return control; } -void edp_panel_vdd_on(struct intel_dp *intel_dp) +void intel_edp_panel_vdd_on(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; @@ -1329,6 +1329,8 @@ void intel_edp_panel_off(struct intel_dp *intel_dp) edp_wait_backlight_off(intel_dp); + WARN(!intel_dp->want_panel_vdd, "Need VDD to turn off panel\n"); + pp = ironlake_get_pp_control(intel_dp); /* We need to switch off panel power _and_ force vdd, for otherwise some * panels get very unhappy and cease to work. */ @@ -1880,7 +1882,7 @@ static void intel_disable_dp(struct intel_encoder *encoder) /* Make sure the panel is off before trying to change the mode. But also * ensure that we have vdd while we switch off the panel. */ - edp_panel_vdd_on(intel_dp); + intel_edp_panel_vdd_on(intel_dp); intel_edp_backlight_off(intel_dp); intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF); intel_edp_panel_off(intel_dp); @@ -1913,7 +1915,7 @@ static void intel_enable_dp(struct intel_encoder *encoder) if (WARN_ON(dp_reg & DP_PORT_EN)) return; - edp_panel_vdd_on(intel_dp); + intel_edp_panel_vdd_on(intel_dp); intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); intel_dp_start_link_train(intel_dp); intel_edp_panel_on(intel_dp); @@ -2951,7 +2953,7 @@ intel_dp_probe_oui(struct intel_dp *intel_dp) if (!(intel_dp->dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_OUI_SUPPORT)) return; - edp_panel_vdd_on(intel_dp); + intel_edp_panel_vdd_on(intel_dp); if (intel_dp_aux_native_read_retry(intel_dp, DP_SINK_OUI, buf, 3)) DRM_DEBUG_KMS("Sink OUI: %02hx%02hx%02hx\n", @@ -3748,7 +3750,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, return true; /* Cache DPCD and EDID for edp. */ - edp_panel_vdd_on(intel_dp); + intel_edp_panel_vdd_on(intel_dp); has_dpcd = intel_dp_get_dpcd(intel_dp); edp_panel_vdd_off(intel_dp, false); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 20e11f24b9a14..e0064a18352d3 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -762,12 +762,12 @@ bool intel_dp_compute_config(struct intel_encoder *encoder, bool intel_dp_is_edp(struct drm_device *dev, enum port port); void intel_edp_backlight_on(struct intel_dp *intel_dp); void intel_edp_backlight_off(struct intel_dp *intel_dp); +void intel_edp_panel_vdd_on(struct intel_dp *intel_dp); void intel_edp_panel_on(struct intel_dp *intel_dp); void intel_edp_panel_off(struct intel_dp *intel_dp); void intel_edp_psr_enable(struct intel_dp *intel_dp); void intel_edp_psr_disable(struct intel_dp *intel_dp); void intel_edp_psr_update(struct drm_device *dev); -void edp_panel_vdd_on(struct intel_dp *intel_dp); /* intel_dsi.c */ From 83f26f16970cd4738585e642a6d90b063f1cebdb Mon Sep 17 00:00:00 2001 From: Damien Lespiau <damien.lespiau@intel.com> Date: Mon, 17 Mar 2014 17:59:48 +0000 Subject: [PATCH 41/75] drm/i915: Remove spurious '()' in WARN macros No need of any here. Signed-off-by: Damien Lespiau <damien.lespiau@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_display.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8c3746f7b5236..ffb0b632b7792 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1166,7 +1166,7 @@ static void assert_planes_disabled(struct drm_i915_private *dev_priv, if (INTEL_INFO(dev)->gen >= 4) { reg = DSPCNTR(pipe); val = I915_READ(reg); - WARN((val & DISPLAY_PLANE_ENABLE), + WARN(val & DISPLAY_PLANE_ENABLE, "plane %c assertion failure, should be disabled but not\n", plane_name(pipe)); return; @@ -1195,20 +1195,20 @@ static void assert_sprites_disabled(struct drm_i915_private *dev_priv, for_each_sprite(pipe, sprite) { reg = SPCNTR(pipe, sprite); val = I915_READ(reg); - WARN((val & SP_ENABLE), + WARN(val & SP_ENABLE, "sprite %c assertion failure, should be off on pipe %c but is still active\n", sprite_name(pipe, sprite), pipe_name(pipe)); } } else if (INTEL_INFO(dev)->gen >= 7) { reg = SPRCTL(pipe); val = I915_READ(reg); - WARN((val & SPRITE_ENABLE), + WARN(val & SPRITE_ENABLE, "sprite %c assertion failure, should be off on pipe %c but is still active\n", plane_name(pipe), pipe_name(pipe)); } else if (INTEL_INFO(dev)->gen >= 5) { reg = DVSCNTR(pipe); val = I915_READ(reg); - WARN((val & DVS_ENABLE), + WARN(val & DVS_ENABLE, "sprite %c assertion failure, should be off on pipe %c but is still active\n", plane_name(pipe), pipe_name(pipe)); } From fa50ad614892c99232ce30710ffa704c485bb679 Mon Sep 17 00:00:00 2001 From: Damien Lespiau <damien.lespiau@intel.com> Date: Mon, 17 Mar 2014 18:01:16 +0000 Subject: [PATCH 42/75] drm/i915: Rename intel_setup_wm_latency() to ilk_setup_wm_latency() This function is only used on ILK+, so rename it accordingly. Signed-off-by: Damien Lespiau <damien.lespiau@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_pm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index ad58ce3b7675f..1d0f34680bbb7 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2085,7 +2085,7 @@ static void intel_print_wm_latency(struct drm_device *dev, } } -static void intel_setup_wm_latency(struct drm_device *dev) +static void ilk_setup_wm_latency(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -5985,7 +5985,7 @@ void intel_init_pm(struct drm_device *dev) /* For FIFO watermark updates */ if (HAS_PCH_SPLIT(dev)) { - intel_setup_wm_latency(dev); + ilk_setup_wm_latency(dev); if ((IS_GEN5(dev) && dev_priv->wm.pri_latency[1] && dev_priv->wm.spr_latency[1] && dev_priv->wm.cur_latency[1]) || From e5081a538a565284fec5f30a937d98e460d5e780 Mon Sep 17 00:00:00 2001 From: Damien Lespiau <damien.lespiau@intel.com> Date: Tue, 18 Mar 2014 17:43:08 +0000 Subject: [PATCH 43/75] drm/i915: Use the correct format string modifier for ptrdiff_t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When compiling on 32bits, I have the following warning: drivers/gpu/drm/i915/i915_cmd_parser.c:405:4: warning: format ‘%ld’ expects argument of type ‘long int’, but argument 7 has type ‘int’ [-Wformat=] DRM_DEBUG_DRIVER("CMD: Command length exceeds batch length: 0x%08X length=%d batchlen=%ld\n", The ptrdiff_t type has its own modifier: 't'. Cc: Brad Volkin <bradley.d.volkin@intel.com> Signed-off-by: Damien Lespiau <damien.lespiau@intel.com> Reviewed-by: Brad Volkin <bradley.d.volkin@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_cmd_parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_cmd_parser.c b/drivers/gpu/drm/i915/i915_cmd_parser.c index 7a5756e9bb864..0eaed44aee6d9 100644 --- a/drivers/gpu/drm/i915/i915_cmd_parser.c +++ b/drivers/gpu/drm/i915/i915_cmd_parser.c @@ -402,7 +402,7 @@ int i915_parse_cmds(struct intel_ring_buffer *ring, length = ((*cmd & desc->length.mask) + LENGTH_BIAS); if ((batch_end - cmd) < length) { - DRM_DEBUG_DRIVER("CMD: Command length exceeds batch length: 0x%08X length=%d batchlen=%ld\n", + DRM_DEBUG_DRIVER("CMD: Command length exceeds batch length: 0x%08X length=%d batchlen=%td\n", *cmd, length, batch_end - cmd); From 9297ebf29ad9118edd6c0fedc84f03e35028827d Mon Sep 17 00:00:00 2001 From: Steven Rostedt <rostedt@goodmis.org> Date: Tue, 18 Mar 2014 11:27:37 -0400 Subject: [PATCH 44/75] drm/i915: Do not dereference pointers from ring buffer in evict event The TP_printk() should never dereference any pointers, because the ring buffer can be read at some unknown time in the future. If a device no longer exists, it can cause a kernel oops. This also makes this event useless when saving the ring buffer in userspaces tools such as perf and trace-cmd. The i915_gem_evict_vm dereferences the vm pointer which may also not exist when the ring buffer is read sometime in the future. Link: http://lkml.kernel.org/r/1395095198-20034-3-git-send-email-artagnon@gmail.com Reported-by: Ramkumar Ramachandra <artagnon@gmail.com> Cc: stable@vger.kernel.org # 3.13+ Fixes: bcccff847d1f "drm/i915: trace vm eviction instead of everything" Signed-off-by: Steven Rostedt <rostedt@goodmis.org> [danvet: Try to make it actually compile] Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_trace.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index b95a380958dbe..23c26f1f8b372 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -238,14 +238,16 @@ TRACE_EVENT(i915_gem_evict_vm, TP_ARGS(vm), TP_STRUCT__entry( + __field(u32, dev) __field(struct i915_address_space *, vm) ), TP_fast_assign( + __entry->dev = vm->dev->primary->index; __entry->vm = vm; ), - TP_printk("dev=%d, vm=%p", __entry->vm->dev->primary->index, __entry->vm) + TP_printk("dev=%d, vm=%p", __entry->dev, __entry->vm) ); TRACE_EVENT(i915_gem_ring_sync_to, From a2319c08bfd849ea32b4f890ce92df86074c5731 Mon Sep 17 00:00:00 2001 From: Ben Widawsky <benjamin.widawsky@intel.com> Date: Tue, 18 Mar 2014 16:09:37 -0700 Subject: [PATCH 45/75] drm/i915/bdw: Restore PPAT on thaw Apparently it is wiped out from under us, and we get some really fun caching artifacts upon resume (it seems to be WB for all types by default). Reported-by: James Ausmus <james.ausmus@intel.com> Signed-off-by: Ben Widawsky <ben@bwidawsk.net> Tested-by: James Ausmus <james.ausmus@intel.com> Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=76113 Tested-by: Timo Aaltonen <timo.aaltonen@canonical.com> Cc: stable@vger.kernel.org Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_gem_gtt.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 0dce6fc9b1cc9..ee535514aa419 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -30,6 +30,8 @@ #include "i915_trace.h" #include "intel_drv.h" +static void gen8_setup_private_ppat(struct drm_i915_private *dev_priv); + bool intel_enable_ppgtt(struct drm_device *dev, bool full) { if (i915.enable_ppgtt == 0 || !HAS_ALIASING_PPGTT(dev)) @@ -1370,8 +1372,10 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev) } - if (INTEL_INFO(dev)->gen >= 8) + if (INTEL_INFO(dev)->gen >= 8) { + gen8_setup_private_ppat(dev_priv); return; + } list_for_each_entry(vm, &dev_priv->vm_list, global_link) { /* TODO: Perhaps it shouldn't be gen6 specific */ From 11ea8b7d3e9b5d62217fbd12d66866172e187f9b Mon Sep 17 00:00:00 2001 From: Jesse Barnes <jbarnes@virtuousgeek.org> Date: Mon, 3 Mar 2014 14:27:57 -0800 Subject: [PATCH 46/75] drm/i915/vlv: no MCHBAR on VLV So don't try to allocate and program it, we're only fooling ourselves. Reported-by: "Chang, Junxiao" <junxiao.chang@intel.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> Reviewed-by: Junxiao Chang <junxiao.chang@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_dma.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 9faee49f210d8..7b674bfcb3380 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1187,6 +1187,9 @@ intel_setup_mchbar(struct drm_device *dev) u32 temp; bool enabled; + if (IS_VALLEYVIEW(dev)) + return; + dev_priv->mchbar_need_disable = false; if (IS_I915G(dev) || IS_I915GM(dev)) { From 262ca2b08fbdb9346e66ef30424b2226a00e0ffc Mon Sep 17 00:00:00 2001 From: Matt Roper <matthew.d.roper@intel.com> Date: Tue, 18 Mar 2014 17:22:55 -0700 Subject: [PATCH 47/75] drm/i915: Rename similar plane functions to avoid confusion The name 'update_plane' was used both for the primary plane functions in intel_display.c and the sprite/overlay functions in intel_sprite.c. Rename the primary plane functions to 'update_primary_plane' to avoid confusion. On a similar note, intel_display.c already had a function called intel_disable_primary_plane() that programs the hardware to disable a pipe's primary plane. When we hook up primary planes through the DRM plane interface, one of the natural handler names will be intel_primary_plane_disable(), which is very similar. To avoid confusion, rename the existing intel_disable_primary_plane() to intel_disable_primary_hw_plane() to make the two names a little more distinct. Cc: Intel Graphics Development <intel-gfx@lists.freedesktop.org> Signed-off-by: Matt Roper <matthew.d.roper@intel.com> [danvet: Fix up conflicts.] Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_drv.h | 5 ++- drivers/gpu/drm/i915/intel_display.c | 58 ++++++++++++++++------------ 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 70fbe904016f3..a937711b4b434 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -462,8 +462,9 @@ struct drm_i915_display_funcs { struct drm_framebuffer *fb, struct drm_i915_gem_object *obj, uint32_t flags); - int (*update_plane)(struct drm_crtc *crtc, struct drm_framebuffer *fb, - int x, int y); + int (*update_primary_plane)(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int x, int y); void (*hpd_irq_setup)(struct drm_device *dev); /* clock updates for mode set */ /* cursor updates */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index ffb0b632b7792..796054f1cf84e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1872,15 +1872,15 @@ void intel_flush_primary_plane(struct drm_i915_private *dev_priv, } /** - * intel_enable_primary_plane - enable the primary plane on a given pipe + * intel_enable_primary_hw_plane - enable the primary plane on a given pipe * @dev_priv: i915 private structure * @plane: plane to enable * @pipe: pipe being fed * * Enable @plane on @pipe, making sure that @pipe is running first. */ -static void intel_enable_primary_plane(struct drm_i915_private *dev_priv, - enum plane plane, enum pipe pipe) +static void intel_enable_primary_hw_plane(struct drm_i915_private *dev_priv, + enum plane plane, enum pipe pipe) { struct intel_crtc *intel_crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); @@ -1905,15 +1905,15 @@ static void intel_enable_primary_plane(struct drm_i915_private *dev_priv, } /** - * intel_disable_primary_plane - disable the primary plane + * intel_disable_primary_hw_plane - disable the primary hardware plane * @dev_priv: i915 private structure * @plane: plane to disable * @pipe: pipe consuming the data * * Disable @plane; should be an independent operation. */ -static void intel_disable_primary_plane(struct drm_i915_private *dev_priv, - enum plane plane, enum pipe pipe) +static void intel_disable_primary_hw_plane(struct drm_i915_private *dev_priv, + enum plane plane, enum pipe pipe) { struct intel_crtc *intel_crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); @@ -2152,8 +2152,9 @@ static void intel_find_plane_obj(struct intel_crtc *intel_crtc, } } -static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb, - int x, int y) +static int i9xx_update_primary_plane(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int x, int y) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -2252,8 +2253,9 @@ static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb, return 0; } -static int ironlake_update_plane(struct drm_crtc *crtc, - struct drm_framebuffer *fb, int x, int y) +static int ironlake_update_primary_plane(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int x, int y) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -2357,7 +2359,7 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, dev_priv->display.disable_fbc(dev); intel_increase_pllclock(crtc); - return dev_priv->display.update_plane(crtc, fb, x, y); + return dev_priv->display.update_primary_plane(crtc, fb, x, y); } void intel_display_handle_reset(struct drm_device *dev) @@ -2397,8 +2399,10 @@ void intel_display_handle_reset(struct drm_device *dev) * a NULL crtc->fb. */ if (intel_crtc->active && crtc->fb) - dev_priv->display.update_plane(crtc, crtc->fb, - crtc->x, crtc->y); + dev_priv->display.update_primary_plane(crtc, + crtc->fb, + crtc->x, + crtc->y); mutex_unlock(&crtc->mutex); } } @@ -2514,7 +2518,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, intel_crtc->config.pipe_src_h = adjusted_mode->crtc_vdisplay; } - ret = dev_priv->display.update_plane(crtc, fb, x, y); + ret = dev_priv->display.update_primary_plane(crtc, fb, x, y); if (ret) { mutex_lock(&dev->struct_mutex); intel_unpin_fb_obj(to_intel_framebuffer(fb)->obj); @@ -3695,7 +3699,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) intel_update_watermarks(crtc); intel_enable_pipe(intel_crtc); - intel_enable_primary_plane(dev_priv, plane, pipe); + intel_enable_primary_hw_plane(dev_priv, plane, pipe); intel_enable_planes(crtc); intel_crtc_update_cursor(crtc, true); @@ -3737,7 +3741,7 @@ static void haswell_crtc_enable_planes(struct drm_crtc *crtc) int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; - intel_enable_primary_plane(dev_priv, plane, pipe); + intel_enable_primary_hw_plane(dev_priv, plane, pipe); intel_enable_planes(crtc); intel_crtc_update_cursor(crtc, true); @@ -3767,7 +3771,7 @@ static void haswell_crtc_disable_planes(struct drm_crtc *crtc) intel_crtc_update_cursor(crtc, false); intel_disable_planes(crtc); - intel_disable_primary_plane(dev_priv, plane, pipe); + intel_disable_primary_hw_plane(dev_priv, plane, pipe); } /* @@ -3895,7 +3899,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) intel_crtc_update_cursor(crtc, false); intel_disable_planes(crtc); - intel_disable_primary_plane(dev_priv, plane, pipe); + intel_disable_primary_hw_plane(dev_priv, plane, pipe); if (intel_crtc->config.has_pch_encoder) intel_set_pch_fifo_underrun_reporting(dev, pipe, false); @@ -4378,7 +4382,7 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc) intel_update_watermarks(crtc); intel_enable_pipe(intel_crtc); intel_set_cpu_fifo_underrun_reporting(dev, pipe, true); - intel_enable_primary_plane(dev_priv, plane, pipe); + intel_enable_primary_hw_plane(dev_priv, plane, pipe); intel_enable_planes(crtc); intel_crtc_update_cursor(crtc, true); @@ -4417,7 +4421,7 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) intel_update_watermarks(crtc); intel_enable_pipe(intel_crtc); intel_set_cpu_fifo_underrun_reporting(dev, pipe, true); - intel_enable_primary_plane(dev_priv, plane, pipe); + intel_enable_primary_hw_plane(dev_priv, plane, pipe); intel_enable_planes(crtc); /* The fixup needs to happen before cursor is enabled */ if (IS_G4X(dev)) @@ -4473,7 +4477,7 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) intel_crtc_dpms_overlay(intel_crtc, false); intel_crtc_update_cursor(crtc, false); intel_disable_planes(crtc); - intel_disable_primary_plane(dev_priv, plane, pipe); + intel_disable_primary_hw_plane(dev_priv, plane, pipe); intel_set_cpu_fifo_underrun_reporting(dev, pipe, false); intel_disable_pipe(dev_priv, pipe); @@ -11018,7 +11022,8 @@ static void intel_init_display(struct drm_device *dev) dev_priv->display.crtc_enable = haswell_crtc_enable; dev_priv->display.crtc_disable = haswell_crtc_disable; dev_priv->display.off = haswell_crtc_off; - dev_priv->display.update_plane = ironlake_update_plane; + dev_priv->display.update_primary_plane = + ironlake_update_primary_plane; } else if (HAS_PCH_SPLIT(dev)) { dev_priv->display.get_pipe_config = ironlake_get_pipe_config; dev_priv->display.get_plane_config = ironlake_get_plane_config; @@ -11026,7 +11031,8 @@ static void intel_init_display(struct drm_device *dev) dev_priv->display.crtc_enable = ironlake_crtc_enable; dev_priv->display.crtc_disable = ironlake_crtc_disable; dev_priv->display.off = ironlake_crtc_off; - dev_priv->display.update_plane = ironlake_update_plane; + dev_priv->display.update_primary_plane = + ironlake_update_primary_plane; } else if (IS_VALLEYVIEW(dev)) { dev_priv->display.get_pipe_config = i9xx_get_pipe_config; dev_priv->display.get_plane_config = i9xx_get_plane_config; @@ -11034,7 +11040,8 @@ static void intel_init_display(struct drm_device *dev) dev_priv->display.crtc_enable = valleyview_crtc_enable; dev_priv->display.crtc_disable = i9xx_crtc_disable; dev_priv->display.off = i9xx_crtc_off; - dev_priv->display.update_plane = i9xx_update_plane; + dev_priv->display.update_primary_plane = + i9xx_update_primary_plane; } else { dev_priv->display.get_pipe_config = i9xx_get_pipe_config; dev_priv->display.get_plane_config = i9xx_get_plane_config; @@ -11042,7 +11049,8 @@ static void intel_init_display(struct drm_device *dev) dev_priv->display.crtc_enable = i9xx_crtc_enable; dev_priv->display.crtc_disable = i9xx_crtc_disable; dev_priv->display.off = i9xx_crtc_off; - dev_priv->display.update_plane = i9xx_update_plane; + dev_priv->display.update_primary_plane = + i9xx_update_primary_plane; } /* Returns the core display clock speed */ From b4d2a9a09361ade9409584748b0fc2be6bbb05a0 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni <paulo.r.zanoni@intel.com> Date: Fri, 7 Mar 2014 20:08:04 -0300 Subject: [PATCH 48/75] drm/i915: extract __hsw_do_{en, dis}able_package_c8 When we merge PC8 and runtime PM, these new functions are going to be called by the runtime suspend/resume functions, and their callers are going to be removed. v2: - Rebase Reviewed-by: Imre Deak <imre.deak@intel.com> (v1) Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_display.c | 64 +++++++++++++++++----------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 796054f1cf84e..da68ccfd5aa90 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7006,19 +7006,11 @@ static void hsw_restore_lcpll(struct drm_i915_private *dev_priv) gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL); } -void hsw_enable_pc8_work(struct work_struct *__work) +static void __hsw_do_enable_pc8(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = - container_of(to_delayed_work(__work), struct drm_i915_private, - pc8.enable_work); struct drm_device *dev = dev_priv->dev; uint32_t val; - WARN_ON(!HAS_PC8(dev)); - - if (dev_priv->pc8.enabled) - return; - DRM_DEBUG_KMS("Enabling package C8+\n"); dev_priv->pc8.enabled = true; @@ -7032,7 +7024,21 @@ void hsw_enable_pc8_work(struct work_struct *__work) lpt_disable_clkout_dp(dev); hsw_pc8_disable_interrupts(dev); hsw_disable_lcpll(dev_priv, true, true); +} + +void hsw_enable_pc8_work(struct work_struct *__work) +{ + struct drm_i915_private *dev_priv = + container_of(to_delayed_work(__work), struct drm_i915_private, + pc8.enable_work); + struct drm_device *dev = dev_priv->dev; + + WARN_ON(!HAS_PC8(dev)); + if (dev_priv->pc8.enabled) + return; + + __hsw_do_enable_pc8(dev_priv); intel_runtime_pm_put(dev_priv); } @@ -7050,29 +7056,13 @@ static void __hsw_enable_package_c8(struct drm_i915_private *dev_priv) msecs_to_jiffies(i915.pc8_timeout)); } -static void __hsw_disable_package_c8(struct drm_i915_private *dev_priv) +static void __hsw_do_disable_package_c8(struct drm_i915_private *dev_priv) { struct drm_device *dev = dev_priv->dev; uint32_t val; - WARN_ON(!mutex_is_locked(&dev_priv->pc8.lock)); - WARN(dev_priv->pc8.disable_count < 0, - "pc8.disable_count: %d\n", dev_priv->pc8.disable_count); - - dev_priv->pc8.disable_count++; - if (dev_priv->pc8.disable_count != 1) - return; - - WARN_ON(!HAS_PC8(dev)); - - cancel_delayed_work_sync(&dev_priv->pc8.enable_work); - if (!dev_priv->pc8.enabled) - return; - DRM_DEBUG_KMS("Disabling package C8+\n"); - intel_runtime_pm_get(dev_priv); - hsw_restore_lcpll(dev_priv); hsw_pc8_restore_interrupts(dev); lpt_init_pch_refclk(dev); @@ -7091,6 +7081,28 @@ static void __hsw_disable_package_c8(struct drm_i915_private *dev_priv) dev_priv->pc8.enabled = false; } +static void __hsw_disable_package_c8(struct drm_i915_private *dev_priv) +{ + struct drm_device *dev = dev_priv->dev; + + WARN_ON(!mutex_is_locked(&dev_priv->pc8.lock)); + WARN(dev_priv->pc8.disable_count < 0, + "pc8.disable_count: %d\n", dev_priv->pc8.disable_count); + + dev_priv->pc8.disable_count++; + if (dev_priv->pc8.disable_count != 1) + return; + + WARN_ON(!HAS_PC8(dev)); + + cancel_delayed_work_sync(&dev_priv->pc8.enable_work); + if (!dev_priv->pc8.enabled) + return; + + intel_runtime_pm_get(dev_priv); + __hsw_do_disable_package_c8(dev_priv); +} + void hsw_enable_package_c8(struct drm_i915_private *dev_priv) { if (!HAS_PC8(dev_priv->dev)) From a8a8bd547e6323c56295e1c5a03e30e765d42325 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni <paulo.r.zanoni@intel.com> Date: Fri, 7 Mar 2014 20:08:05 -0300 Subject: [PATCH 49/75] drm/i915: make PC8 be part of runtime PM suspend/resume Currently, when our driver becomes idle for i915.pc8_timeout (default: 5s) we enable PC8, so we save some power, but not everything we can. Then, while PC8 is enabled, if we stay idle for more autosuspend_delay_ms (default: 10s) we'll enter runtime PM and put the graphics device in D3 state, saving even more power. The two features are separate things with increasing levels of power savings, but if we disable PC8 we'll never get into D3. While from the modularity point of view it would be nice to keep these features as separate, we have reasons to merge them: - We are not aware of anybody wanting a "PC8 without D3" environment. - If we keep both features as separate, we'll have to to test both PC8 and PC8+D3 code paths. We're already having a major pain to make QA do automated testing of just one thing, testing both paths will cost even more. - Only Haswell+ supports PC8, so if we want to add runtime PM support to, for example, IVB, we'll have to copy some code from the PC8 feature to runtime PM, so merging both features as a single thing will make it easier for enabling runtime PM on other platforms. This patch only does the very basic steps required to have PC8 and runtime PM merged on a single feature: the next patches will take care of cleaning up everything. v2: - Rebase. v3: - Rebase. - Fully remove the deprecated i915 params since Daniel doesn't consider them as part of the ABI. v4: - Rebase. - Fix typo in the commit message. v5: - Rebase, again. - Add a huge comment explaining the different forcewake usage (Chris, Daniel). - Use open-coded forcewake functions (Daniel). Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com> Reviewed-by: Imre Deak <imre.deak@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_dma.c | 2 - drivers/gpu/drm/i915/i915_drv.c | 8 ++++ drivers/gpu/drm/i915/i915_drv.h | 7 +-- drivers/gpu/drm/i915/i915_params.c | 10 ----- drivers/gpu/drm/i915/intel_display.c | 65 +++++++++++++--------------- drivers/gpu/drm/i915/intel_drv.h | 3 +- drivers/gpu/drm/i915/intel_pm.c | 1 - 7 files changed, 43 insertions(+), 53 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 7b674bfcb3380..4e0a26a835008 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1823,8 +1823,6 @@ int i915_driver_unload(struct drm_device *dev) cancel_work_sync(&dev_priv->gpu_error.work); i915_destroy_error_state(dev); - cancel_delayed_work_sync(&dev_priv->pc8.enable_work); - if (dev->pdev->msi_enabled) pci_disable_msi(dev->pdev); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 5a0d34c47885d..3526f8e579146 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -849,6 +849,9 @@ static int i915_runtime_suspend(struct device *device) DRM_DEBUG_KMS("Suspending device\n"); + if (HAS_PC8(dev)) + __hsw_do_enable_pc8(dev_priv); + i915_gem_release_all_mmaps(dev_priv); del_timer_sync(&dev_priv->gpu_error.hangcheck_timer); @@ -863,6 +866,7 @@ static int i915_runtime_suspend(struct device *device) */ intel_opregion_notify_adapter(dev, PCI_D1); + DRM_DEBUG_KMS("Device suspended\n"); return 0; } @@ -879,6 +883,10 @@ static int i915_runtime_resume(struct device *device) intel_opregion_notify_adapter(dev, PCI_D0); dev_priv->pm.suspended = false; + if (HAS_PC8(dev)) + __hsw_do_disable_pc8(dev_priv); + + DRM_DEBUG_KMS("Device resumed\n"); return 0; } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index a937711b4b434..0e5148a817a6b 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1339,6 +1339,10 @@ struct ilk_wm_values { /* * This struct tracks the state needed for the Package C8+ feature. * + * TODO: we're merging the Package C8+ feature with the runtime PM support. To + * avoid having to update the documentation at each patch of the series, we'll + * do a final update at the end. + * * Package states C8 and deeper are really deep PC states that can only be * reached when all the devices on the system allow it, so even if the graphics * device allows PC8+, it doesn't mean the system will actually get to these @@ -1392,7 +1396,6 @@ struct i915_package_c8 { bool enabled; int disable_count; struct mutex lock; - struct delayed_work enable_work; struct { uint32_t deimr; @@ -2095,8 +2098,6 @@ struct i915_params { unsigned int preliminary_hw_support; int disable_power_well; int enable_ips; - int enable_pc8; - int pc8_timeout; int invert_brightness; int enable_cmd_parser; /* leave bools at the end to not create holes */ diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index a66ffb652bee9..d1d7980f0e010 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -42,8 +42,6 @@ struct i915_params i915 __read_mostly = { .disable_power_well = 1, .enable_ips = 1, .fastboot = 0, - .enable_pc8 = 1, - .pc8_timeout = 5000, .prefault_disable = 0, .reset = true, .invert_brightness = 0, @@ -135,14 +133,6 @@ module_param_named(fastboot, i915.fastboot, bool, 0600); MODULE_PARM_DESC(fastboot, "Try to skip unnecessary mode sets at boot time (default: false)"); -module_param_named(enable_pc8, i915.enable_pc8, int, 0600); -MODULE_PARM_DESC(enable_pc8, - "Enable support for low power package C states (PC8+) (default: true)"); - -module_param_named(pc8_timeout, i915.pc8_timeout, int, 0600); -MODULE_PARM_DESC(pc8_timeout, - "Number of msecs of idleness required to enter PC8+ (default: 5000)"); - module_param_named(prefault_disable, i915.prefault_disable, bool, 0600); MODULE_PARM_DESC(prefault_disable, "Disable page prefaulting for pread/pwrite/reloc (default:false). " diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index da68ccfd5aa90..1f79d1d51ea77 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6960,6 +6960,7 @@ static void hsw_disable_lcpll(struct drm_i915_private *dev_priv, static void hsw_restore_lcpll(struct drm_i915_private *dev_priv) { uint32_t val; + unsigned long irqflags; val = I915_READ(LCPLL_CTL); @@ -6967,9 +6968,22 @@ static void hsw_restore_lcpll(struct drm_i915_private *dev_priv) LCPLL_POWER_DOWN_ALLOW)) == LCPLL_PLL_LOCK) return; - /* Make sure we're not on PC8 state before disabling PC8, otherwise - * we'll hang the machine! */ - gen6_gt_force_wake_get(dev_priv, FORCEWAKE_ALL); + /* + * Make sure we're not on PC8 state before disabling PC8, otherwise + * we'll hang the machine. To prevent PC8 state, just enable force_wake. + * + * The other problem is that hsw_restore_lcpll() is called as part of + * the runtime PM resume sequence, so we can't just call + * gen6_gt_force_wake_get() because that function calls + * intel_runtime_pm_get(), and we can't change the runtime PM refcount + * while we are on the resume sequence. So to solve this problem we have + * to call special forcewake code that doesn't touch runtime PM and + * doesn't enable the forcewake delayed work. + */ + spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); + if (dev_priv->uncore.forcewake_count++ == 0) + dev_priv->uncore.funcs.force_wake_get(dev_priv, FORCEWAKE_ALL); + spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); if (val & LCPLL_POWER_DOWN_ALLOW) { val &= ~LCPLL_POWER_DOWN_ALLOW; @@ -7003,14 +7017,20 @@ static void hsw_restore_lcpll(struct drm_i915_private *dev_priv) DRM_ERROR("Switching back to LCPLL failed\n"); } - gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL); + /* See the big comment above. */ + spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); + if (--dev_priv->uncore.forcewake_count == 0) + dev_priv->uncore.funcs.force_wake_put(dev_priv, FORCEWAKE_ALL); + spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } -static void __hsw_do_enable_pc8(struct drm_i915_private *dev_priv) +void __hsw_do_enable_pc8(struct drm_i915_private *dev_priv) { struct drm_device *dev = dev_priv->dev; uint32_t val; + WARN_ON(!HAS_PC8(dev)); + DRM_DEBUG_KMS("Enabling package C8+\n"); dev_priv->pc8.enabled = true; @@ -7026,22 +7046,6 @@ static void __hsw_do_enable_pc8(struct drm_i915_private *dev_priv) hsw_disable_lcpll(dev_priv, true, true); } -void hsw_enable_pc8_work(struct work_struct *__work) -{ - struct drm_i915_private *dev_priv = - container_of(to_delayed_work(__work), struct drm_i915_private, - pc8.enable_work); - struct drm_device *dev = dev_priv->dev; - - WARN_ON(!HAS_PC8(dev)); - - if (dev_priv->pc8.enabled) - return; - - __hsw_do_enable_pc8(dev_priv); - intel_runtime_pm_put(dev_priv); -} - static void __hsw_enable_package_c8(struct drm_i915_private *dev_priv) { WARN_ON(!mutex_is_locked(&dev_priv->pc8.lock)); @@ -7052,15 +7056,16 @@ static void __hsw_enable_package_c8(struct drm_i915_private *dev_priv) if (dev_priv->pc8.disable_count != 0) return; - schedule_delayed_work(&dev_priv->pc8.enable_work, - msecs_to_jiffies(i915.pc8_timeout)); + intel_runtime_pm_put(dev_priv); } -static void __hsw_do_disable_package_c8(struct drm_i915_private *dev_priv) +void __hsw_do_disable_pc8(struct drm_i915_private *dev_priv) { struct drm_device *dev = dev_priv->dev; uint32_t val; + WARN_ON(!HAS_PC8(dev)); + DRM_DEBUG_KMS("Disabling package C8+\n"); hsw_restore_lcpll(dev_priv); @@ -7083,8 +7088,6 @@ static void __hsw_do_disable_package_c8(struct drm_i915_private *dev_priv) static void __hsw_disable_package_c8(struct drm_i915_private *dev_priv) { - struct drm_device *dev = dev_priv->dev; - WARN_ON(!mutex_is_locked(&dev_priv->pc8.lock)); WARN(dev_priv->pc8.disable_count < 0, "pc8.disable_count: %d\n", dev_priv->pc8.disable_count); @@ -7093,14 +7096,7 @@ static void __hsw_disable_package_c8(struct drm_i915_private *dev_priv) if (dev_priv->pc8.disable_count != 1) return; - WARN_ON(!HAS_PC8(dev)); - - cancel_delayed_work_sync(&dev_priv->pc8.enable_work); - if (!dev_priv->pc8.enabled) - return; - intel_runtime_pm_get(dev_priv); - __hsw_do_disable_package_c8(dev_priv); } void hsw_enable_package_c8(struct drm_i915_private *dev_priv) @@ -7158,9 +7154,6 @@ static void hsw_update_package_c8(struct drm_device *dev) if (!HAS_PC8(dev_priv->dev)) return; - if (!i915.enable_pc8) - return; - mutex_lock(&dev_priv->pc8.lock); allow = hsw_can_enable_package_c8(dev_priv); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 890c5cd982685..3290a900ad0f3 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -725,7 +725,8 @@ unsigned long intel_gen4_compute_page_offset(int *x, int *y, unsigned int bpp, unsigned int pitch); void intel_display_handle_reset(struct drm_device *dev); -void hsw_enable_pc8_work(struct work_struct *__work); +void __hsw_do_enable_pc8(struct drm_i915_private *dev_priv); +void __hsw_do_disable_pc8(struct drm_i915_private *dev_priv); void hsw_enable_package_c8(struct drm_i915_private *dev_priv); void hsw_disable_package_c8(struct drm_i915_private *dev_priv); void intel_dp_get_m_n(struct intel_crtc *crtc, diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 1d0f34680bbb7..ed3e9935737d0 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -6161,7 +6161,6 @@ void intel_pm_setup(struct drm_device *dev) dev_priv->pc8.irqs_disabled = false; dev_priv->pc8.enabled = false; dev_priv->pc8.disable_count = 1; /* requirements_met */ - INIT_DELAYED_WORK(&dev_priv->pc8.enable_work, hsw_enable_pc8_work); INIT_DELAYED_WORK(&dev_priv->rps.delayed_resume_work, intel_gen6_powersave_work); } From 9e6ea71a591ac4b8203bfaacab9a604abc021af4 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni <paulo.r.zanoni@intel.com> Date: Fri, 7 Mar 2014 20:08:06 -0300 Subject: [PATCH 50/75] drm/i915: get/put runtime PM when we get/put a power domain Any power domain will require the HW to be in PCI D0 state, so just do the simple thing. Dear maintainer: since intel_display_power_put() and intel_display_power_get() are almost identical, git-am has failed apply the patch on my local machine once: it added both chunks to put(), instead of one chunk to get() and another to put(). When you apply this patch to your tree, please check if it is correct. v2: - Add the warning above. v3: - Rebase. Reviewed-by: Imre Deak <imre.deak@intel.com> Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_pm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index ed3e9935737d0..5f23e6fada977 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -5577,6 +5577,8 @@ void intel_display_power_get(struct drm_i915_private *dev_priv, struct i915_power_well *power_well; int i; + intel_runtime_pm_get(dev_priv); + power_domains = &dev_priv->power_domains; mutex_lock(&power_domains->lock); @@ -5621,6 +5623,8 @@ void intel_display_power_put(struct drm_i915_private *dev_priv, } mutex_unlock(&power_domains->lock); + + intel_runtime_pm_put(dev_priv); } static struct i915_power_domains *hsw_pwr; From ba0239e03f04b8529b00ddacff20d771144c1515 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni <paulo.r.zanoni@intel.com> Date: Fri, 7 Mar 2014 20:08:07 -0300 Subject: [PATCH 51/75] drm/i915: remove dev_priv->pc8.requirements_met The requirements_met variable was used to track two things: enabled CRTCs and the power well. After the latest chagnes, we get a runtime PM reference whenever we get any of the power domains, and we get power domains when we enable CRTCs or the power well, so we should already be covered, not needing this specific tracking. v2: - Rebase. v3: - Rebase. Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com> Reviewed-by: Imre Deak <imre.deak@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_debugfs.c | 2 -- drivers/gpu/drm/i915/i915_drv.h | 1 - drivers/gpu/drm/i915/intel_display.c | 54 ---------------------------- drivers/gpu/drm/i915/intel_pm.c | 5 +-- 4 files changed, 3 insertions(+), 59 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index d83d643af45c7..02545ebf0dace 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2013,8 +2013,6 @@ static int i915_pc8_status(struct seq_file *m, void *unused) } mutex_lock(&dev_priv->pc8.lock); - seq_printf(m, "Requirements met: %s\n", - yesno(dev_priv->pc8.requirements_met)); seq_printf(m, "GPU idle: %s\n", yesno(!dev_priv->mm.busy)); seq_printf(m, "Disable count: %d\n", dev_priv->pc8.disable_count); seq_printf(m, "IRQs disabled: %s\n", diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 0e5148a817a6b..7f86c3123c6ee 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1390,7 +1390,6 @@ struct ilk_wm_values { * For more, read "Display Sequences for Package C8" on our documentation. */ struct i915_package_c8 { - bool requirements_met; bool irqs_disabled; /* Only true after the delayed work task actually enables it. */ bool enabled; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 1f79d1d51ea77..f7dc5eec6d565 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7119,63 +7119,9 @@ void hsw_disable_package_c8(struct drm_i915_private *dev_priv) mutex_unlock(&dev_priv->pc8.lock); } -static bool hsw_can_enable_package_c8(struct drm_i915_private *dev_priv) -{ - struct drm_device *dev = dev_priv->dev; - struct intel_crtc *crtc; - uint32_t val; - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) - if (crtc->base.enabled) - return false; - - /* This case is still possible since we have the i915.disable_power_well - * parameter and also the KVMr or something else might be requesting the - * power well. */ - val = I915_READ(HSW_PWR_WELL_DRIVER); - if (val != 0) { - DRM_DEBUG_KMS("Not enabling PC8: power well on\n"); - return false; - } - - return true; -} - -/* Since we're called from modeset_global_resources there's no way to - * symmetrically increase and decrease the refcount, so we use - * dev_priv->pc8.requirements_met to track whether we already have the refcount - * or not. - */ -static void hsw_update_package_c8(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - bool allow; - - if (!HAS_PC8(dev_priv->dev)) - return; - - mutex_lock(&dev_priv->pc8.lock); - - allow = hsw_can_enable_package_c8(dev_priv); - - if (allow == dev_priv->pc8.requirements_met) - goto done; - - dev_priv->pc8.requirements_met = allow; - - if (allow) - __hsw_enable_package_c8(dev_priv); - else - __hsw_disable_package_c8(dev_priv); - -done: - mutex_unlock(&dev_priv->pc8.lock); -} - static void haswell_modeset_global_resources(struct drm_device *dev) { modeset_update_crtc_power_domains(dev); - hsw_update_package_c8(dev); } static int haswell_crtc_mode_set(struct drm_crtc *crtc, diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 5f23e6fada977..6de0c23963372 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -5938,6 +5938,8 @@ void intel_init_runtime_pm(struct drm_i915_private *dev_priv) pm_runtime_set_autosuspend_delay(device, 10000); /* 10s */ pm_runtime_mark_last_busy(device); pm_runtime_use_autosuspend(device); + + pm_runtime_put_autosuspend(device); } void intel_fini_runtime_pm(struct drm_i915_private *dev_priv) @@ -6161,10 +6163,9 @@ void intel_pm_setup(struct drm_device *dev) mutex_init(&dev_priv->rps.hw_lock); mutex_init(&dev_priv->pc8.lock); - dev_priv->pc8.requirements_met = false; dev_priv->pc8.irqs_disabled = false; dev_priv->pc8.enabled = false; - dev_priv->pc8.disable_count = 1; /* requirements_met */ + dev_priv->pc8.disable_count = 0; INIT_DELAYED_WORK(&dev_priv->rps.delayed_resume_work, intel_gen6_powersave_work); } From 43694d69b4d87dde8243165a08a626ab0f034470 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni <paulo.r.zanoni@intel.com> Date: Fri, 7 Mar 2014 20:08:08 -0300 Subject: [PATCH 52/75] drm/i915: get runtime PM references when the GPU is idle/busy ... instead of PC8 references. Now that both are the same thing and we are killing PC8, just get the runtime PM reference. Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- 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 f7dc5eec6d565..a61efbfc81af5 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8409,7 +8409,7 @@ void intel_mark_busy(struct drm_device *dev) if (dev_priv->mm.busy) return; - hsw_disable_package_c8(dev_priv); + intel_runtime_pm_get(dev_priv); i915_update_gfx_val(dev_priv); dev_priv->mm.busy = true; } @@ -8438,7 +8438,7 @@ void intel_mark_idle(struct drm_device *dev) gen6_rps_idle(dev->dev_private); out: - hsw_enable_package_c8(dev_priv); + intel_runtime_pm_put(dev_priv); } void intel_mark_fb_busy(struct drm_i915_gem_object *obj, From 34f5754c239b0cae69130b8e67bb2226560c09d4 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni <paulo.r.zanoni@intel.com> Date: Fri, 7 Mar 2014 20:08:09 -0300 Subject: [PATCH 53/75] drm/i915: kill pc8.disable_count Since after the latest patches it's only being used to prevent getting/putting the runtime PM refcount. Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_debugfs.c | 1 - drivers/gpu/drm/i915/i915_drv.h | 1 - drivers/gpu/drm/i915/intel_display.c | 14 -------------- drivers/gpu/drm/i915/intel_pm.c | 1 - 4 files changed, 17 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 02545ebf0dace..4960bf6296152 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2014,7 +2014,6 @@ static int i915_pc8_status(struct seq_file *m, void *unused) mutex_lock(&dev_priv->pc8.lock); seq_printf(m, "GPU idle: %s\n", yesno(!dev_priv->mm.busy)); - seq_printf(m, "Disable count: %d\n", dev_priv->pc8.disable_count); seq_printf(m, "IRQs disabled: %s\n", yesno(dev_priv->pc8.irqs_disabled)); seq_printf(m, "Enabled: %s\n", yesno(dev_priv->pc8.enabled)); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 7f86c3123c6ee..41efb09282ae4 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1393,7 +1393,6 @@ struct i915_package_c8 { bool irqs_disabled; /* Only true after the delayed work task actually enables it. */ bool enabled; - int disable_count; struct mutex lock; struct { diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a61efbfc81af5..1e1cdcf9cc3c0 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7049,13 +7049,6 @@ void __hsw_do_enable_pc8(struct drm_i915_private *dev_priv) static void __hsw_enable_package_c8(struct drm_i915_private *dev_priv) { WARN_ON(!mutex_is_locked(&dev_priv->pc8.lock)); - WARN(dev_priv->pc8.disable_count < 1, - "pc8.disable_count: %d\n", dev_priv->pc8.disable_count); - - dev_priv->pc8.disable_count--; - if (dev_priv->pc8.disable_count != 0) - return; - intel_runtime_pm_put(dev_priv); } @@ -7089,13 +7082,6 @@ void __hsw_do_disable_pc8(struct drm_i915_private *dev_priv) static void __hsw_disable_package_c8(struct drm_i915_private *dev_priv) { WARN_ON(!mutex_is_locked(&dev_priv->pc8.lock)); - WARN(dev_priv->pc8.disable_count < 0, - "pc8.disable_count: %d\n", dev_priv->pc8.disable_count); - - dev_priv->pc8.disable_count++; - if (dev_priv->pc8.disable_count != 1) - return; - intel_runtime_pm_get(dev_priv); } diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 6de0c23963372..cc929d2a32442 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -6165,7 +6165,6 @@ void intel_pm_setup(struct drm_device *dev) mutex_init(&dev_priv->pc8.lock); dev_priv->pc8.irqs_disabled = false; dev_priv->pc8.enabled = false; - dev_priv->pc8.disable_count = 0; INIT_DELAYED_WORK(&dev_priv->rps.delayed_resume_work, intel_gen6_powersave_work); } From e1f2de6def8647cf69f8d15e944a9d9cda4292d4 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni <paulo.r.zanoni@intel.com> Date: Fri, 7 Mar 2014 20:08:10 -0300 Subject: [PATCH 54/75] drm/i915: remove an indirection level on PC8 functions After the latest changes, the indirection is useless. Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_display.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 1e1cdcf9cc3c0..e7e2894702f4a 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7046,12 +7046,6 @@ void __hsw_do_enable_pc8(struct drm_i915_private *dev_priv) hsw_disable_lcpll(dev_priv, true, true); } -static void __hsw_enable_package_c8(struct drm_i915_private *dev_priv) -{ - WARN_ON(!mutex_is_locked(&dev_priv->pc8.lock)); - intel_runtime_pm_put(dev_priv); -} - void __hsw_do_disable_pc8(struct drm_i915_private *dev_priv) { struct drm_device *dev = dev_priv->dev; @@ -7079,19 +7073,13 @@ void __hsw_do_disable_pc8(struct drm_i915_private *dev_priv) dev_priv->pc8.enabled = false; } -static void __hsw_disable_package_c8(struct drm_i915_private *dev_priv) -{ - WARN_ON(!mutex_is_locked(&dev_priv->pc8.lock)); - intel_runtime_pm_get(dev_priv); -} - void hsw_enable_package_c8(struct drm_i915_private *dev_priv) { if (!HAS_PC8(dev_priv->dev)) return; mutex_lock(&dev_priv->pc8.lock); - __hsw_enable_package_c8(dev_priv); + intel_runtime_pm_put(dev_priv); mutex_unlock(&dev_priv->pc8.lock); } @@ -7101,7 +7089,7 @@ void hsw_disable_package_c8(struct drm_i915_private *dev_priv) return; mutex_lock(&dev_priv->pc8.lock); - __hsw_disable_package_c8(dev_priv); + intel_runtime_pm_get(dev_priv); mutex_unlock(&dev_priv->pc8.lock); } From db8384f2e07bfa8cc607914dfa0b3cee81f59839 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni <paulo.r.zanoni@intel.com> Date: Fri, 7 Mar 2014 20:08:11 -0300 Subject: [PATCH 55/75] drm/i915: don't get/put PC8 reference on freeze/thaw We already get runtime PM references, and PC8 is now part of runtime PM, so this is enough. v2: - Rebase. Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_drv.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 3526f8e579146..9aa01bfcd0681 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -428,7 +428,6 @@ static int i915_drm_freeze(struct drm_device *dev) /* We do a lot of poking in a lot of registers, make sure they work * properly. */ - hsw_disable_package_c8(dev_priv); intel_display_set_init_power(dev_priv, true); drm_kms_helper_poll_disable(dev); @@ -604,10 +603,6 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings) schedule_work(&dev_priv->console_resume_work); } - /* Undo what we did at i915_drm_freeze so the refcount goes back to the - * expected level. */ - hsw_enable_package_c8(dev_priv); - mutex_lock(&dev_priv->modeset_restore_lock); dev_priv->modeset_restore = MODESET_DONE; mutex_unlock(&dev_priv->modeset_restore_lock); From d361ae269fef68e561978d7183a4a6d1c4c0a413 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni <paulo.r.zanoni@intel.com> Date: Fri, 7 Mar 2014 20:08:12 -0300 Subject: [PATCH 56/75] drm/i915: make intel_aux_display_runtime_get get runtime PM, not PC8 Because we merged the PC8 and runtime PM features, so calling intel_runtime_pm_get now has the same meaning, and we plan to just remove hsw_disable_package_c8 for this exact reason. My first patch tried to completely kill intel_aux_display_runtime_get/put, because I was assuming that whoever needed more than just runtime PM would have to get the appropriate power domain instead of that, but it seems some people still want the intel_aux_display_runtime_get abstraction, so keep it until someone else tries to replace it with the more-standard power domain calls. Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com> Reviewed-by: Imre Deak <imre.deak@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_pm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index cc929d2a32442..6ae0e0580b775 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -5888,15 +5888,14 @@ void intel_power_domains_init_hw(struct drm_i915_private *dev_priv) intel_power_domains_resume(dev_priv); } -/* Disables PC8 so we can use the GMBUS and DP AUX interrupts. */ void intel_aux_display_runtime_get(struct drm_i915_private *dev_priv) { - hsw_disable_package_c8(dev_priv); + intel_runtime_pm_get(dev_priv); } void intel_aux_display_runtime_put(struct drm_i915_private *dev_priv) { - hsw_enable_package_c8(dev_priv); + intel_runtime_pm_put(dev_priv); } void intel_runtime_pm_get(struct drm_i915_private *dev_priv) From 6a932d88ae88ed8c601956802be8f8f39a06821d Mon Sep 17 00:00:00 2001 From: Paulo Zanoni <paulo.r.zanoni@intel.com> Date: Fri, 7 Mar 2014 20:08:13 -0300 Subject: [PATCH 57/75] drm/i915: don't get/put PC8 when getting/putting power wells Because we already get/put runtime PM every time we get/put any power domain, and now PC8 and runtime PM are the same thing. With this, we can also now kill the hsw_{en,dis}able_package_c8 functions. v2: - Rebase. v3: - Rebase. v4: - Rebase. Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_display.c | 20 -------------------- drivers/gpu/drm/i915/intel_drv.h | 2 -- drivers/gpu/drm/i915/intel_pm.c | 2 -- 3 files changed, 24 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e7e2894702f4a..90d16a1fa6504 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7073,26 +7073,6 @@ void __hsw_do_disable_pc8(struct drm_i915_private *dev_priv) dev_priv->pc8.enabled = false; } -void hsw_enable_package_c8(struct drm_i915_private *dev_priv) -{ - if (!HAS_PC8(dev_priv->dev)) - return; - - mutex_lock(&dev_priv->pc8.lock); - intel_runtime_pm_put(dev_priv); - mutex_unlock(&dev_priv->pc8.lock); -} - -void hsw_disable_package_c8(struct drm_i915_private *dev_priv) -{ - if (!HAS_PC8(dev_priv->dev)) - return; - - mutex_lock(&dev_priv->pc8.lock); - intel_runtime_pm_get(dev_priv); - mutex_unlock(&dev_priv->pc8.lock); -} - static void haswell_modeset_global_resources(struct drm_device *dev) { modeset_update_crtc_power_domains(dev); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 3290a900ad0f3..345e95833cf47 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -727,8 +727,6 @@ unsigned long intel_gen4_compute_page_offset(int *x, int *y, void intel_display_handle_reset(struct drm_device *dev); void __hsw_do_enable_pc8(struct drm_i915_private *dev_priv); void __hsw_do_disable_pc8(struct drm_i915_private *dev_priv); -void hsw_enable_package_c8(struct drm_i915_private *dev_priv); -void hsw_disable_package_c8(struct drm_i915_private *dev_priv); void intel_dp_get_m_n(struct intel_crtc *crtc, struct intel_crtc_config *pipe_config); int intel_dotclock_calculate(int link_freq, const struct intel_link_m_n *m_n); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 6ae0e0580b775..012867e85cda6 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -5391,7 +5391,6 @@ static void hsw_power_well_sync_hw(struct drm_i915_private *dev_priv, static void hsw_power_well_enable(struct drm_i915_private *dev_priv, struct i915_power_well *power_well) { - hsw_disable_package_c8(dev_priv); hsw_set_power_well(dev_priv, power_well, true); } @@ -5399,7 +5398,6 @@ static void hsw_power_well_disable(struct drm_i915_private *dev_priv, struct i915_power_well *power_well) { hsw_set_power_well(dev_priv, power_well, false); - hsw_enable_package_c8(dev_priv); } static void i9xx_always_on_power_well_noop(struct drm_i915_private *dev_priv, From 7c8615d8f9faf7a33ad528a012e097631599207f Mon Sep 17 00:00:00 2001 From: Paulo Zanoni <paulo.r.zanoni@intel.com> Date: Fri, 7 Mar 2014 20:08:14 -0300 Subject: [PATCH 58/75] drm/i915: remove dev_priv->pc8.enabled It was just being used on debugfs and on a WARN inside hsw_set_power_well. But now that we PC8 is part of runtime PM and we get/put runtime PM when we get/put any power domain, we shouldn't need the WARN anymore. v2: - Rebase. v3: - Rebase. Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_debugfs.c | 1 - drivers/gpu/drm/i915/i915_drv.h | 2 -- drivers/gpu/drm/i915/intel_display.c | 3 --- drivers/gpu/drm/i915/intel_pm.c | 3 --- 4 files changed, 9 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 4960bf6296152..2365bb299276f 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2016,7 +2016,6 @@ static int i915_pc8_status(struct seq_file *m, void *unused) seq_printf(m, "GPU idle: %s\n", yesno(!dev_priv->mm.busy)); seq_printf(m, "IRQs disabled: %s\n", yesno(dev_priv->pc8.irqs_disabled)); - seq_printf(m, "Enabled: %s\n", yesno(dev_priv->pc8.enabled)); mutex_unlock(&dev_priv->pc8.lock); return 0; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 41efb09282ae4..02102712465ed 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1391,8 +1391,6 @@ struct ilk_wm_values { */ struct i915_package_c8 { bool irqs_disabled; - /* Only true after the delayed work task actually enables it. */ - bool enabled; struct mutex lock; struct { diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 90d16a1fa6504..1553fe7542c74 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7033,8 +7033,6 @@ void __hsw_do_enable_pc8(struct drm_i915_private *dev_priv) DRM_DEBUG_KMS("Enabling package C8+\n"); - dev_priv->pc8.enabled = true; - if (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) { val = I915_READ(SOUTH_DSPCLK_GATE_D); val &= ~PCH_LP_PARTITION_LEVEL_DISABLE; @@ -7070,7 +7068,6 @@ void __hsw_do_disable_pc8(struct drm_i915_private *dev_priv) mutex_lock(&dev_priv->rps.hw_lock); gen6_update_ring_freq(dev); mutex_unlock(&dev_priv->rps.hw_lock); - dev_priv->pc8.enabled = false; } static void haswell_modeset_global_resources(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 012867e85cda6..ddd0368460aed 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -5345,8 +5345,6 @@ static void hsw_set_power_well(struct drm_i915_private *dev_priv, bool is_enabled, enable_requested; uint32_t tmp; - WARN_ON(dev_priv->pc8.enabled); - tmp = I915_READ(HSW_PWR_WELL_DRIVER); is_enabled = tmp & HSW_PWR_WELL_STATE_ENABLED; enable_requested = tmp & HSW_PWR_WELL_ENABLE_REQUEST; @@ -6161,7 +6159,6 @@ void intel_pm_setup(struct drm_device *dev) mutex_init(&dev_priv->pc8.lock); dev_priv->pc8.irqs_disabled = false; - dev_priv->pc8.enabled = false; INIT_DELAYED_WORK(&dev_priv->rps.delayed_resume_work, intel_gen6_powersave_work); } From 5d584b2eca96543568a8db8eba008f2dab784367 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni <paulo.r.zanoni@intel.com> Date: Fri, 7 Mar 2014 20:08:15 -0300 Subject: [PATCH 59/75] drm/i915: move pc8.irqs_disabled to pm.irqs_disabled When other platforms add runtime PM support they will also need to disable interrupts, so move the variable to the runtime PM struct. Also notice that the longer-term goal is to completely kill the regsave struct, and I even have patches for that. v2: - Rebase. v3: - Rebase. Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_debugfs.c | 2 +- drivers/gpu/drm/i915/i915_drv.h | 10 ++--- drivers/gpu/drm/i915/i915_gem.c | 2 +- drivers/gpu/drm/i915/i915_irq.c | 58 ++++++++++++++-------------- drivers/gpu/drm/i915/intel_display.c | 4 +- drivers/gpu/drm/i915/intel_drv.h | 4 +- drivers/gpu/drm/i915/intel_pm.c | 3 +- 7 files changed, 42 insertions(+), 41 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 2365bb299276f..144b0ff0a3f56 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2015,7 +2015,7 @@ static int i915_pc8_status(struct seq_file *m, void *unused) mutex_lock(&dev_priv->pc8.lock); seq_printf(m, "GPU idle: %s\n", yesno(!dev_priv->mm.busy)); seq_printf(m, "IRQs disabled: %s\n", - yesno(dev_priv->pc8.irqs_disabled)); + yesno(dev_priv->pm.irqs_disabled)); mutex_unlock(&dev_priv->pc8.lock); return 0; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 02102712465ed..84a80b610afae 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1390,8 +1390,12 @@ struct ilk_wm_values { * For more, read "Display Sequences for Package C8" on our documentation. */ struct i915_package_c8 { - bool irqs_disabled; struct mutex lock; +}; + +struct i915_runtime_pm { + bool suspended; + bool irqs_disabled; struct { uint32_t deimr; @@ -1402,10 +1406,6 @@ struct i915_package_c8 { } regsave; }; -struct i915_runtime_pm { - bool suspended; -}; - enum intel_pipe_crc_source { INTEL_PIPE_CRC_SOURCE_NONE, INTEL_PIPE_CRC_SOURCE_PLANE1, diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 338fa6799ecfa..ee32759ffce3d 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1035,7 +1035,7 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, unsigned long timeout_expire; int ret; - WARN(dev_priv->pc8.irqs_disabled, "IRQs disabled\n"); + WARN(dev_priv->pm.irqs_disabled, "IRQs disabled\n"); if (i915_seqno_passed(ring->get_seqno(ring, true), seqno)) return 0; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index c8e262fc750a4..1c00751eca699 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -86,9 +86,9 @@ ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask) { assert_spin_locked(&dev_priv->irq_lock); - if (dev_priv->pc8.irqs_disabled) { + if (dev_priv->pm.irqs_disabled) { WARN(1, "IRQs disabled\n"); - dev_priv->pc8.regsave.deimr &= ~mask; + dev_priv->pm.regsave.deimr &= ~mask; return; } @@ -104,9 +104,9 @@ ironlake_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask) { assert_spin_locked(&dev_priv->irq_lock); - if (dev_priv->pc8.irqs_disabled) { + if (dev_priv->pm.irqs_disabled) { WARN(1, "IRQs disabled\n"); - dev_priv->pc8.regsave.deimr |= mask; + dev_priv->pm.regsave.deimr |= mask; return; } @@ -129,10 +129,10 @@ static void ilk_update_gt_irq(struct drm_i915_private *dev_priv, { assert_spin_locked(&dev_priv->irq_lock); - if (dev_priv->pc8.irqs_disabled) { + if (dev_priv->pm.irqs_disabled) { WARN(1, "IRQs disabled\n"); - dev_priv->pc8.regsave.gtimr &= ~interrupt_mask; - dev_priv->pc8.regsave.gtimr |= (~enabled_irq_mask & + dev_priv->pm.regsave.gtimr &= ~interrupt_mask; + dev_priv->pm.regsave.gtimr |= (~enabled_irq_mask & interrupt_mask); return; } @@ -167,10 +167,10 @@ static void snb_update_pm_irq(struct drm_i915_private *dev_priv, assert_spin_locked(&dev_priv->irq_lock); - if (dev_priv->pc8.irqs_disabled) { + if (dev_priv->pm.irqs_disabled) { WARN(1, "IRQs disabled\n"); - dev_priv->pc8.regsave.gen6_pmimr &= ~interrupt_mask; - dev_priv->pc8.regsave.gen6_pmimr |= (~enabled_irq_mask & + dev_priv->pm.regsave.gen6_pmimr &= ~interrupt_mask; + dev_priv->pm.regsave.gen6_pmimr |= (~enabled_irq_mask & interrupt_mask); return; } @@ -313,11 +313,11 @@ static void ibx_display_interrupt_update(struct drm_i915_private *dev_priv, assert_spin_locked(&dev_priv->irq_lock); - if (dev_priv->pc8.irqs_disabled && + if (dev_priv->pm.irqs_disabled && (interrupt_mask & SDE_HOTPLUG_MASK_CPT)) { WARN(1, "IRQs disabled\n"); - dev_priv->pc8.regsave.sdeimr &= ~interrupt_mask; - dev_priv->pc8.regsave.sdeimr |= (~enabled_irq_mask & + dev_priv->pm.regsave.sdeimr &= ~interrupt_mask; + dev_priv->pm.regsave.sdeimr |= (~enabled_irq_mask & interrupt_mask); return; } @@ -4118,32 +4118,32 @@ void intel_hpd_init(struct drm_device *dev) spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } -/* Disable interrupts so we can allow Package C8+. */ -void hsw_pc8_disable_interrupts(struct drm_device *dev) +/* Disable interrupts so we can allow runtime PM. */ +void hsw_runtime_pm_disable_interrupts(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; unsigned long irqflags; spin_lock_irqsave(&dev_priv->irq_lock, irqflags); - dev_priv->pc8.regsave.deimr = I915_READ(DEIMR); - dev_priv->pc8.regsave.sdeimr = I915_READ(SDEIMR); - dev_priv->pc8.regsave.gtimr = I915_READ(GTIMR); - dev_priv->pc8.regsave.gtier = I915_READ(GTIER); - dev_priv->pc8.regsave.gen6_pmimr = I915_READ(GEN6_PMIMR); + dev_priv->pm.regsave.deimr = I915_READ(DEIMR); + dev_priv->pm.regsave.sdeimr = I915_READ(SDEIMR); + dev_priv->pm.regsave.gtimr = I915_READ(GTIMR); + dev_priv->pm.regsave.gtier = I915_READ(GTIER); + dev_priv->pm.regsave.gen6_pmimr = I915_READ(GEN6_PMIMR); ironlake_disable_display_irq(dev_priv, 0xffffffff); ibx_disable_display_interrupt(dev_priv, 0xffffffff); ilk_disable_gt_irq(dev_priv, 0xffffffff); snb_disable_pm_irq(dev_priv, 0xffffffff); - dev_priv->pc8.irqs_disabled = true; + dev_priv->pm.irqs_disabled = true; spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } -/* Restore interrupts so we can recover from Package C8+. */ -void hsw_pc8_restore_interrupts(struct drm_device *dev) +/* Restore interrupts so we can recover from runtime PM. */ +void hsw_runtime_pm_restore_interrupts(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; unsigned long irqflags; @@ -4163,13 +4163,13 @@ void hsw_pc8_restore_interrupts(struct drm_device *dev) val = I915_READ(GEN6_PMIMR); WARN(val != 0xffffffff, "GEN6_PMIMR is 0x%08x\n", val); - dev_priv->pc8.irqs_disabled = false; + dev_priv->pm.irqs_disabled = false; - ironlake_enable_display_irq(dev_priv, ~dev_priv->pc8.regsave.deimr); - ibx_enable_display_interrupt(dev_priv, ~dev_priv->pc8.regsave.sdeimr); - ilk_enable_gt_irq(dev_priv, ~dev_priv->pc8.regsave.gtimr); - snb_enable_pm_irq(dev_priv, ~dev_priv->pc8.regsave.gen6_pmimr); - I915_WRITE(GTIER, dev_priv->pc8.regsave.gtier); + ironlake_enable_display_irq(dev_priv, ~dev_priv->pm.regsave.deimr); + ibx_enable_display_interrupt(dev_priv, ~dev_priv->pm.regsave.sdeimr); + ilk_enable_gt_irq(dev_priv, ~dev_priv->pm.regsave.gtimr); + snb_enable_pm_irq(dev_priv, ~dev_priv->pm.regsave.gen6_pmimr); + I915_WRITE(GTIER, dev_priv->pm.regsave.gtier); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 1553fe7542c74..4b5cffa3f0330 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7040,7 +7040,7 @@ void __hsw_do_enable_pc8(struct drm_i915_private *dev_priv) } lpt_disable_clkout_dp(dev); - hsw_pc8_disable_interrupts(dev); + hsw_runtime_pm_disable_interrupts(dev); hsw_disable_lcpll(dev_priv, true, true); } @@ -7054,7 +7054,7 @@ void __hsw_do_disable_pc8(struct drm_i915_private *dev_priv) DRM_DEBUG_KMS("Disabling package C8+\n"); hsw_restore_lcpll(dev_priv); - hsw_pc8_restore_interrupts(dev); + hsw_runtime_pm_restore_interrupts(dev); lpt_init_pch_refclk(dev); if (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) { diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 345e95833cf47..a0d4315045be0 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -621,8 +621,8 @@ void ilk_enable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask); void ilk_disable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask); void snb_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask); void snb_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask); -void hsw_pc8_disable_interrupts(struct drm_device *dev); -void hsw_pc8_restore_interrupts(struct drm_device *dev); +void hsw_runtime_pm_disable_interrupts(struct drm_device *dev); +void hsw_runtime_pm_restore_interrupts(struct drm_device *dev); /* intel_crt.c */ diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index ddd0368460aed..fcca82cd96958 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -6158,7 +6158,8 @@ void intel_pm_setup(struct drm_device *dev) mutex_init(&dev_priv->rps.hw_lock); mutex_init(&dev_priv->pc8.lock); - dev_priv->pc8.irqs_disabled = false; INIT_DELAYED_WORK(&dev_priv->rps.delayed_resume_work, intel_gen6_powersave_work); + + dev_priv->pm.irqs_disabled = false; } From d688e0955003794fc5a25a1fd574202d64541a34 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni <paulo.r.zanoni@intel.com> Date: Fri, 7 Mar 2014 20:08:16 -0300 Subject: [PATCH 60/75] drm/i915: kill struct i915_package_c8 The only remaining field of the struct was the lock, which was useless. v2: - Rebase. Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_debugfs.c | 2 -- drivers/gpu/drm/i915/i915_drv.h | 6 ------ drivers/gpu/drm/i915/intel_pm.c | 1 - 3 files changed, 9 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 144b0ff0a3f56..603791380cdf5 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2012,11 +2012,9 @@ static int i915_pc8_status(struct seq_file *m, void *unused) return 0; } - mutex_lock(&dev_priv->pc8.lock); seq_printf(m, "GPU idle: %s\n", yesno(!dev_priv->mm.busy)); seq_printf(m, "IRQs disabled: %s\n", yesno(dev_priv->pm.irqs_disabled)); - mutex_unlock(&dev_priv->pc8.lock); return 0; } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 84a80b610afae..20db5d804e20b 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1389,10 +1389,6 @@ struct ilk_wm_values { * * For more, read "Display Sequences for Package C8" on our documentation. */ -struct i915_package_c8 { - struct mutex lock; -}; - struct i915_runtime_pm { bool suspended; bool irqs_disabled; @@ -1633,8 +1629,6 @@ typedef struct drm_i915_private { struct ilk_wm_values hw; } wm; - struct i915_package_c8 pc8; - struct i915_runtime_pm pm; /* Old dri1 support infrastructure, beware the dragons ya fools entering diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index fcca82cd96958..5ab1a820efdc4 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -6157,7 +6157,6 @@ void intel_pm_setup(struct drm_device *dev) mutex_init(&dev_priv->rps.hw_lock); - mutex_init(&dev_priv->pc8.lock); INIT_DELAYED_WORK(&dev_priv->rps.delayed_resume_work, intel_gen6_powersave_work); From a14cb6fc851d09bdb2c95f26d81842d5b4fa7164 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni <paulo.r.zanoni@intel.com> Date: Fri, 7 Mar 2014 20:08:17 -0300 Subject: [PATCH 61/75] drm/i915: rename __hsw_do_{en, dis}able_pc8 After we removed all the intermediate abstractions, we can rename these functions to just hsw_{en,dis}able_pc8. v2: - Rebase. Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_drv.c | 4 ++-- drivers/gpu/drm/i915/intel_display.c | 4 ++-- drivers/gpu/drm/i915/intel_drv.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 9aa01bfcd0681..fa5d0ed763789 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -845,7 +845,7 @@ static int i915_runtime_suspend(struct device *device) DRM_DEBUG_KMS("Suspending device\n"); if (HAS_PC8(dev)) - __hsw_do_enable_pc8(dev_priv); + hsw_enable_pc8(dev_priv); i915_gem_release_all_mmaps(dev_priv); @@ -879,7 +879,7 @@ static int i915_runtime_resume(struct device *device) dev_priv->pm.suspended = false; if (HAS_PC8(dev)) - __hsw_do_disable_pc8(dev_priv); + hsw_disable_pc8(dev_priv); DRM_DEBUG_KMS("Device resumed\n"); return 0; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 4b5cffa3f0330..2fd53bc697aa4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7024,7 +7024,7 @@ static void hsw_restore_lcpll(struct drm_i915_private *dev_priv) spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } -void __hsw_do_enable_pc8(struct drm_i915_private *dev_priv) +void hsw_enable_pc8(struct drm_i915_private *dev_priv) { struct drm_device *dev = dev_priv->dev; uint32_t val; @@ -7044,7 +7044,7 @@ void __hsw_do_enable_pc8(struct drm_i915_private *dev_priv) hsw_disable_lcpll(dev_priv, true, true); } -void __hsw_do_disable_pc8(struct drm_i915_private *dev_priv) +void hsw_disable_pc8(struct drm_i915_private *dev_priv) { struct drm_device *dev = dev_priv->dev; uint32_t val; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index a0d4315045be0..60ffad376390e 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -725,8 +725,8 @@ unsigned long intel_gen4_compute_page_offset(int *x, int *y, unsigned int bpp, unsigned int pitch); void intel_display_handle_reset(struct drm_device *dev); -void __hsw_do_enable_pc8(struct drm_i915_private *dev_priv); -void __hsw_do_disable_pc8(struct drm_i915_private *dev_priv); +void hsw_enable_pc8(struct drm_i915_private *dev_priv); +void hsw_disable_pc8(struct drm_i915_private *dev_priv); void intel_dp_get_m_n(struct intel_crtc *crtc, struct intel_crtc_config *pipe_config); int intel_dotclock_calculate(int link_freq, const struct intel_link_m_n *m_n); From 765dab67528ac2490d8a9f68d05cc678861584d6 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni <paulo.r.zanoni@intel.com> Date: Fri, 7 Mar 2014 20:08:18 -0300 Subject: [PATCH 62/75] drm/i915: update the PC8 and runtime PM documentation Now that PC8 got much simpler, there are less things to document. Also, runtime PM already has a nice documentation, so we don't need to re-explain it on our driver. v2: - Rebase. - Fix typo (Jesse). Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_drv.h | 52 +++++++--------------------- drivers/gpu/drm/i915/intel_display.c | 23 ++++++++++++ 2 files changed, 35 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 20db5d804e20b..9cd870fb14720 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1337,47 +1337,19 @@ struct ilk_wm_values { }; /* - * This struct tracks the state needed for the Package C8+ feature. + * This struct helps tracking the state needed for runtime PM, which puts the + * device in PCI D3 state. Notice that when this happens, nothing on the + * graphics device works, even register access, so we don't get interrupts nor + * anything else. * - * TODO: we're merging the Package C8+ feature with the runtime PM support. To - * avoid having to update the documentation at each patch of the series, we'll - * do a final update at the end. + * Every piece of our code that needs to actually touch the hardware needs to + * either call intel_runtime_pm_get or call intel_display_power_get with the + * appropriate power domain. * - * Package states C8 and deeper are really deep PC states that can only be - * reached when all the devices on the system allow it, so even if the graphics - * device allows PC8+, it doesn't mean the system will actually get to these - * states. - * - * Our driver only allows PC8+ when all the outputs are disabled, the power well - * is disabled and the GPU is idle. When these conditions are met, we manually - * do the other conditions: disable the interrupts, clocks and switch LCPLL - * refclk to Fclk. - * - * When we really reach PC8 or deeper states (not just when we allow it) we lose - * the state of some registers, so when we come back from PC8+ we need to - * restore this state. We don't get into PC8+ if we're not in RC6, so we don't - * need to take care of the registers kept by RC6. - * - * The interrupt disabling is part of the requirements. We can only leave the - * PCH HPD interrupts enabled. If we're in PC8+ and we get another interrupt we - * can lock the machine. - * - * Ideally every piece of our code that needs PC8+ disabled would call - * hsw_disable_package_c8, which would increment disable_count and prevent the - * system from reaching PC8+. But we don't have a symmetric way to do this for - * everything, so we have the requirements_met variable. When we switch - * requirements_met to true we decrease disable_count, and increase it in the - * opposite case. The requirements_met variable is true when all the CRTCs, - * encoders and the power well are disabled. - * - * In addition to everything, we only actually enable PC8+ if disable_count - * stays at zero for at least some seconds. This is implemented with the - * enable_work variable. We do this so we don't enable/disable PC8 dozens of - * consecutive times when all screens are disabled and some background app - * queries the state of our connectors, or we have some application constantly - * waking up to use the GPU. Only after the enable_work function actually - * enables PC8+ the "enable" variable will become true, which means that it can - * be false even if disable_count is 0. + * Our driver uses the autosuspend delay feature, which means we'll only really + * suspend if we stay with zero refcount for a certain amount of time. The + * default value is currently very conservative (see intel_init_runtime_pm), but + * it can be changed with the standard runtime PM files from sysfs. * * The irqs_disabled variable becomes true exactly after we disable the IRQs and * goes back to false exactly before we reenable the IRQs. We use this variable @@ -1387,7 +1359,7 @@ struct ilk_wm_values { * inside struct regsave so when we restore the IRQs they will contain the * latest expected values. * - * For more, read "Display Sequences for Package C8" on our documentation. + * For more, read the Documentation/power/runtime_pm.txt. */ struct i915_runtime_pm { bool suspended; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2fd53bc697aa4..5f68491ae99af 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7024,6 +7024,29 @@ static void hsw_restore_lcpll(struct drm_i915_private *dev_priv) spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } +/* + * Package states C8 and deeper are really deep PC states that can only be + * reached when all the devices on the system allow it, so even if the graphics + * device allows PC8+, it doesn't mean the system will actually get to these + * states. Our driver only allows PC8+ when going into runtime PM. + * + * The requirements for PC8+ are that all the outputs are disabled, the power + * well is disabled and most interrupts are disabled, and these are also + * requirements for runtime PM. When these conditions are met, we manually do + * the other conditions: disable the interrupts, clocks and switch LCPLL refclk + * to Fclk. If we're in PC8+ and we get an non-hotplug interrupt, we can hard + * hang the machine. + * + * When we really reach PC8 or deeper states (not just when we allow it) we lose + * the state of some registers, so when we come back from PC8+ we need to + * restore this state. We don't get into PC8+ if we're not in RC6, so we don't + * need to take care of the registers kept by RC6. Notice that this happens even + * if we don't put the device in PCI D3 state (which is what currently happens + * because of the runtime PM support). + * + * For more, read "Display Sequences for Package C8" on the hardware + * documentation. + */ void hsw_enable_pc8(struct drm_i915_private *dev_priv) { struct drm_device *dev = dev_priv->dev; From 33688d95c458ffca6b247189cc6f15277fd6abf0 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni <paulo.r.zanoni@intel.com> Date: Fri, 7 Mar 2014 20:08:19 -0300 Subject: [PATCH 63/75] drm/i915: init pm.suspended earlier Function intel_init_runtime_pm is supposed to start allowing runtime PM from that point, but it's called very late on the driver initialization code, to prevent the driver from trying to suspend while still initializing. The problem is that variables are accessed earlier than that, so initalize them at intel_pm_setup, which is supposed to be the correct place. Notice that this shouldn't fix any specific bugs because dev_priv is zeroed when allocated, so the value is already correct right from the start. v2: - Rebase. Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_pm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 5ab1a820efdc4..39f3238bf1c37 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -5923,8 +5923,6 @@ void intel_init_runtime_pm(struct drm_i915_private *dev_priv) struct drm_device *dev = dev_priv->dev; struct device *device = &dev->pdev->dev; - dev_priv->pm.suspended = false; - if (!HAS_RUNTIME_PM(dev)) return; @@ -6160,5 +6158,6 @@ void intel_pm_setup(struct drm_device *dev) INIT_DELAYED_WORK(&dev_priv->rps.delayed_resume_work, intel_gen6_powersave_work); + dev_priv->pm.suspended = false; dev_priv->pm.irqs_disabled = false; } From d060c1695345df6c82b5253acb7055698b2bf002 Mon Sep 17 00:00:00 2001 From: Ben Widawsky <benjamin.widawsky@intel.com> Date: Wed, 19 Mar 2014 18:31:08 -0700 Subject: [PATCH 64/75] drm/i915: Reorganize the overclock code The existing code (which I changed last) was very convoluted. I believe it was attempting to skip the overclock portion if the previous pcode write failed. When I last touched the code, I was preserving this behavior. There is some benefit to doing it that way in that if the first pcode access fails, the later is likely invalid. Having a bit more confidence in my understanding of how things work, I now feel it's better to have clear, readable, code than to try to skip over this one operation in an unusual case. Signed-off-by: Ben Widawsky <ben@bwidawsk.net> Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_pm.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 39f3238bf1c37..dd3a1216eab09 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3326,7 +3326,7 @@ static void gen6_enable_rps(struct drm_device *dev) struct intel_ring_buffer *ring; u32 rp_state_cap, hw_max, hw_min; u32 gt_perf_status; - u32 rc6vids, pcu_mbox, rc6_mask = 0; + u32 rc6vids, pcu_mbox = 0, rc6_mask = 0; u32 gtfifodbg; int rc6_mode; int i, ret; @@ -3414,17 +3414,15 @@ static void gen6_enable_rps(struct drm_device *dev) I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10); ret = sandybridge_pcode_write(dev_priv, GEN6_PCODE_WRITE_MIN_FREQ_TABLE, 0); - if (!ret) { - pcu_mbox = 0; - ret = sandybridge_pcode_read(dev_priv, GEN6_READ_OC_PARAMS, &pcu_mbox); - if (!ret && (pcu_mbox & (1<<31))) { /* OC supported */ - DRM_DEBUG_DRIVER("Overclocking supported. Max: %dMHz, Overclock max: %dMHz\n", - (dev_priv->rps.max_delay & 0xff) * 50, - (pcu_mbox & 0xff) * 50); - dev_priv->rps.hw_max = pcu_mbox & 0xff; - } - } else { + if (ret) DRM_DEBUG_DRIVER("Failed to set the min frequency\n"); + + ret = sandybridge_pcode_read(dev_priv, GEN6_READ_OC_PARAMS, &pcu_mbox); + if (!ret && (pcu_mbox & (1<<31))) { /* OC supported */ + DRM_DEBUG_DRIVER("Overclocking supported. Max: %dMHz, Overclock max: %dMHz\n", + (dev_priv->rps.max_delay & 0xff) * 50, + (pcu_mbox & 0xff) * 50); + dev_priv->rps.hw_max = pcu_mbox & 0xff; } dev_priv->rps.power = HIGH_POWER; /* force a reset */ From 5a953add43ea62e862ba15b7f969cd838e456949 Mon Sep 17 00:00:00 2001 From: Ben Widawsky <benjamin.widawsky@intel.com> Date: Wed, 19 Mar 2014 18:31:09 -0700 Subject: [PATCH 65/75] drm/i915: Fix coding style for RPS Introduced: commit b8a5ff8d7c676a04e0da5ec16bb068dd39459042 Author: Jeff McGee <jeff.mcgee@intel.com> Date: Tue Feb 4 11:37:01 2014 -0600 drm/i915: Update rps interrupt limits Cc: Jeff McGee <jeff.mcgee@intel.com> Signed-off-by: Ben Widawsky <ben@bwidawsk.net> Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_sysfs.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index 0c741f4eefb04..e9ffefb720de8 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c @@ -356,12 +356,12 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev, valleyview_set_rps(dev, val); else gen6_set_rps(dev, val); - } - else if (!IS_VALLEYVIEW(dev)) - /* We still need gen6_set_rps to process the new max_delay - and update the interrupt limits even though frequency - request is unchanged. */ + } else if (!IS_VALLEYVIEW(dev)) { + /* We still need gen6_set_rps to process the new max_delay and + * update the interrupt limits even though frequency request is + * unchanged. */ gen6_set_rps(dev, dev_priv->rps.cur_delay); + } mutex_unlock(&dev_priv->rps.hw_lock); @@ -430,12 +430,12 @@ static ssize_t gt_min_freq_mhz_store(struct device *kdev, valleyview_set_rps(dev, val); else gen6_set_rps(dev, val); - } - else if (!IS_VALLEYVIEW(dev)) - /* We still need gen6_set_rps to process the new min_delay - and update the interrupt limits even though frequency - request is unchanged. */ + } else if (!IS_VALLEYVIEW(dev)) { + /* We still need gen6_set_rps to process the new min_delay and + * update the interrupt limits even though frequency request is + * unchanged. */ gen6_set_rps(dev, dev_priv->rps.cur_delay); + } mutex_unlock(&dev_priv->rps.hw_lock); From 1f05c944e007809d7040a1cc87b16934fedd23f6 Mon Sep 17 00:00:00 2001 From: Ben Widawsky <benjamin.widawsky@intel.com> Date: Wed, 19 Mar 2014 18:31:10 -0700 Subject: [PATCH 66/75] drm/i915: Store the HW min frequency as min_freq this leaves a temporarily awkward min_delay (the soft limit) with the new min_freq (the hardware limit). It's fixed in the next patch. Signed-off-by: Ben Widawsky <ben@bwidawsk.net> Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/intel_pm.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 9cd870fb14720..241f5e16cee63 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -987,6 +987,7 @@ struct intel_gen6_power_mgmt { u8 rp1_delay; u8 rp0_delay; u8 hw_max; + u8 min_freq; bool rp_up_masked; bool rp_down_masked; diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index dd3a1216eab09..dd631d19b2bd7 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3354,7 +3354,7 @@ static void gen6_enable_rps(struct drm_device *dev) /* In units of 50MHz */ dev_priv->rps.hw_max = hw_max = rp_state_cap & 0xff; - hw_min = (rp_state_cap >> 16) & 0xff; + dev_priv->rps.min_freq = hw_min = (rp_state_cap >> 16) & 0xff; dev_priv->rps.rp1_delay = (rp_state_cap >> 8) & 0xff; dev_priv->rps.rp0_delay = (rp_state_cap >> 0) & 0xff; dev_priv->rps.rpe_delay = dev_priv->rps.rp1_delay; From b39fb2977392c0a996700bb9b8416a7b2ecce8fd Mon Sep 17 00:00:00 2001 From: Ben Widawsky <benjamin.widawsky@intel.com> Date: Wed, 19 Mar 2014 18:31:11 -0700 Subject: [PATCH 67/75] drm/i915: Rename and comment all the RPS *stuff* The names of the struct members for RPS are stupid. Every time I need to do anything in this code I have to spend a significant amount of time to remember what it all means. By renaming the variables (and adding the comments) I hope to clear up the situation. Indeed doing this make some upcoming patches more readable. I've avoided ILK because it's possible that the naming used for Ironlake matches what is in the docs. I believe the ILK power docs were never published, and I am too lazy to dig them up. v2: leave rp0, and rp1 in the names. It is useful to have these limits available at times. min_freq and max_freq (which may be equal to rp0, or rp1 depending on the platform) represent the actual HW min and max. Cc: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Ben Widawsky <ben@bwidawsk.net> Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_debugfs.c | 26 +++--- drivers/gpu/drm/i915/i915_drv.h | 26 ++++-- drivers/gpu/drm/i915/i915_irq.c | 25 +++--- drivers/gpu/drm/i915/i915_sysfs.c | 32 ++++---- drivers/gpu/drm/i915/intel_pm.c | 118 ++++++++++++++-------------- 5 files changed, 120 insertions(+), 107 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 603791380cdf5..d1e0a360558f1 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1026,7 +1026,7 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused) max_freq * GT_FREQUENCY_MULTIPLIER); seq_printf(m, "Max overclocked frequency: %dMHz\n", - dev_priv->rps.hw_max * GT_FREQUENCY_MULTIPLIER); + dev_priv->rps.max_freq * GT_FREQUENCY_MULTIPLIER); } else if (IS_VALLEYVIEW(dev)) { u32 freq_sts, val; @@ -1498,8 +1498,8 @@ static int i915_ring_freq_table(struct seq_file *m, void *unused) seq_puts(m, "GPU freq (MHz)\tEffective CPU freq (MHz)\tEffective Ring freq (MHz)\n"); - for (gpu_freq = dev_priv->rps.min_delay; - gpu_freq <= dev_priv->rps.max_delay; + for (gpu_freq = dev_priv->rps.min_freq_softlimit; + gpu_freq <= dev_priv->rps.max_freq_softlimit; gpu_freq++) { ia_freq = gpu_freq; sandybridge_pcode_read(dev_priv, @@ -3449,9 +3449,9 @@ i915_max_freq_get(void *data, u64 *val) return ret; if (IS_VALLEYVIEW(dev)) - *val = vlv_gpu_freq(dev_priv, dev_priv->rps.max_delay); + *val = vlv_gpu_freq(dev_priv, dev_priv->rps.max_freq_softlimit); else - *val = dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER; + *val = dev_priv->rps.max_freq_softlimit * GT_FREQUENCY_MULTIPLIER; mutex_unlock(&dev_priv->rps.hw_lock); return 0; @@ -3488,16 +3488,16 @@ i915_max_freq_set(void *data, u64 val) do_div(val, GT_FREQUENCY_MULTIPLIER); rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); - hw_max = dev_priv->rps.hw_max; + hw_max = dev_priv->rps.max_freq; hw_min = (rp_state_cap >> 16) & 0xff; } - if (val < hw_min || val > hw_max || val < dev_priv->rps.min_delay) { + if (val < hw_min || val > hw_max || val < dev_priv->rps.min_freq_softlimit) { mutex_unlock(&dev_priv->rps.hw_lock); return -EINVAL; } - dev_priv->rps.max_delay = val; + dev_priv->rps.max_freq_softlimit = val; if (IS_VALLEYVIEW(dev)) valleyview_set_rps(dev, val); @@ -3530,9 +3530,9 @@ i915_min_freq_get(void *data, u64 *val) return ret; if (IS_VALLEYVIEW(dev)) - *val = vlv_gpu_freq(dev_priv, dev_priv->rps.min_delay); + *val = vlv_gpu_freq(dev_priv, dev_priv->rps.min_freq_softlimit); else - *val = dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER; + *val = dev_priv->rps.min_freq_softlimit * GT_FREQUENCY_MULTIPLIER; mutex_unlock(&dev_priv->rps.hw_lock); return 0; @@ -3569,16 +3569,16 @@ i915_min_freq_set(void *data, u64 val) do_div(val, GT_FREQUENCY_MULTIPLIER); rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); - hw_max = dev_priv->rps.hw_max; + hw_max = dev_priv->rps.max_freq; hw_min = (rp_state_cap >> 16) & 0xff; } - if (val < hw_min || val > hw_max || val > dev_priv->rps.max_delay) { + if (val < hw_min || val > hw_max || val > dev_priv->rps.max_freq_softlimit) { mutex_unlock(&dev_priv->rps.hw_lock); return -EINVAL; } - dev_priv->rps.min_delay = val; + dev_priv->rps.min_freq_softlimit = val; if (IS_VALLEYVIEW(dev)) valleyview_set_rps(dev, val); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 241f5e16cee63..c5c57608460c4 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -980,14 +980,24 @@ struct intel_gen6_power_mgmt { struct work_struct work; u32 pm_iir; - u8 cur_delay; - u8 min_delay; - u8 max_delay; - u8 rpe_delay; - u8 rp1_delay; - u8 rp0_delay; - u8 hw_max; - u8 min_freq; + /* Frequencies are stored in potentially platform dependent multiples. + * In other words, *_freq needs to be multiplied by X to be interesting. + * Soft limits are those which are used for the dynamic reclocking done + * by the driver (raise frequencies under heavy loads, and lower for + * lighter loads). Hard limits are those imposed by the hardware. + * + * A distinction is made for overclocking, which is never enabled by + * default, and is considered to be above the hard limit if it's + * possible at all. + */ + u8 cur_freq; /* Current frequency (cached, may not == HW) */ + u8 min_freq_softlimit; /* Minimum frequency permitted by the driver */ + u8 max_freq_softlimit; /* Max frequency permitted by the driver */ + u8 max_freq; /* Maximum frequency, RP0 if not overclocking */ + u8 min_freq; /* AKA RPn. Minimum frequency */ + u8 efficient_freq; /* AKA RPe. Pre-determined balanced frequency */ + u8 rp1_freq; /* "less than" RP0 power/freqency */ + u8 rp0_freq; /* Non-overclocked max frequency. */ bool rp_up_masked; bool rp_down_masked; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 1c00751eca699..acf1ab3ff0d98 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1075,7 +1075,7 @@ void gen6_set_pm_mask(struct drm_i915_private *dev_priv, u32 pm_iir, int new_delay) { if (pm_iir & GEN6_PM_RP_UP_THRESHOLD) { - if (new_delay >= dev_priv->rps.max_delay) { + if (new_delay >= dev_priv->rps.max_freq_softlimit) { /* Mask UP THRESHOLD Interrupts */ I915_WRITE(GEN6_PMINTRMSK, I915_READ(GEN6_PMINTRMSK) | @@ -1090,7 +1090,7 @@ void gen6_set_pm_mask(struct drm_i915_private *dev_priv, dev_priv->rps.rp_down_masked = false; } } else if (pm_iir & GEN6_PM_RP_DOWN_THRESHOLD) { - if (new_delay <= dev_priv->rps.min_delay) { + if (new_delay <= dev_priv->rps.min_freq_softlimit) { /* Mask DOWN THRESHOLD Interrupts */ I915_WRITE(GEN6_PMINTRMSK, I915_READ(GEN6_PMINTRMSK) | @@ -1136,38 +1136,39 @@ static void gen6_pm_rps_work(struct work_struct *work) adj *= 2; else adj = 1; - new_delay = dev_priv->rps.cur_delay + adj; + new_delay = dev_priv->rps.cur_freq + adj; /* * For better performance, jump directly * to RPe if we're below it. */ - if (new_delay < dev_priv->rps.rpe_delay) - new_delay = dev_priv->rps.rpe_delay; + if (new_delay < dev_priv->rps.efficient_freq) + new_delay = dev_priv->rps.efficient_freq; } else if (pm_iir & GEN6_PM_RP_DOWN_TIMEOUT) { - if (dev_priv->rps.cur_delay > dev_priv->rps.rpe_delay) - new_delay = dev_priv->rps.rpe_delay; + if (dev_priv->rps.cur_freq > dev_priv->rps.efficient_freq) + new_delay = dev_priv->rps.efficient_freq; else - new_delay = dev_priv->rps.min_delay; + new_delay = dev_priv->rps.min_freq_softlimit; adj = 0; } else if (pm_iir & GEN6_PM_RP_DOWN_THRESHOLD) { if (adj < 0) adj *= 2; else adj = -1; - new_delay = dev_priv->rps.cur_delay + adj; + new_delay = dev_priv->rps.cur_freq + adj; } else { /* unknown event */ - new_delay = dev_priv->rps.cur_delay; + new_delay = dev_priv->rps.cur_freq; } /* sysfs frequency interfaces may have snuck in while servicing the * interrupt */ new_delay = clamp_t(int, new_delay, - dev_priv->rps.min_delay, dev_priv->rps.max_delay); + dev_priv->rps.min_freq_softlimit, + dev_priv->rps.max_freq_softlimit); gen6_set_pm_mask(dev_priv, pm_iir, new_delay); - dev_priv->rps.last_adj = new_delay - dev_priv->rps.cur_delay; + dev_priv->rps.last_adj = new_delay - dev_priv->rps.cur_freq; if (IS_VALLEYVIEW(dev_priv->dev)) valleyview_set_rps(dev_priv->dev, new_delay); diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index e9ffefb720de8..e3fa8cd419da7 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c @@ -269,7 +269,7 @@ static ssize_t gt_cur_freq_mhz_show(struct device *kdev, freq = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); ret = vlv_gpu_freq(dev_priv, (freq >> 8) & 0xff); } else { - ret = dev_priv->rps.cur_delay * GT_FREQUENCY_MULTIPLIER; + ret = dev_priv->rps.cur_freq * GT_FREQUENCY_MULTIPLIER; } mutex_unlock(&dev_priv->rps.hw_lock); @@ -284,7 +284,7 @@ static ssize_t vlv_rpe_freq_mhz_show(struct device *kdev, struct drm_i915_private *dev_priv = dev->dev_private; return snprintf(buf, PAGE_SIZE, "%d\n", - vlv_gpu_freq(dev_priv, dev_priv->rps.rpe_delay)); + vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq)); } static ssize_t gt_max_freq_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf) @@ -298,9 +298,9 @@ static ssize_t gt_max_freq_mhz_show(struct device *kdev, struct device_attribute mutex_lock(&dev_priv->rps.hw_lock); if (IS_VALLEYVIEW(dev_priv->dev)) - ret = vlv_gpu_freq(dev_priv, dev_priv->rps.max_delay); + ret = vlv_gpu_freq(dev_priv, dev_priv->rps.max_freq_softlimit); else - ret = dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER; + ret = dev_priv->rps.max_freq_softlimit * GT_FREQUENCY_MULTIPLIER; mutex_unlock(&dev_priv->rps.hw_lock); return snprintf(buf, PAGE_SIZE, "%d\n", ret); @@ -334,13 +334,13 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev, val /= GT_FREQUENCY_MULTIPLIER; rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); - hw_max = dev_priv->rps.hw_max; + hw_max = dev_priv->rps.max_freq; non_oc_max = (rp_state_cap & 0xff); hw_min = ((rp_state_cap & 0xff0000) >> 16); } if (val < hw_min || val > hw_max || - val < dev_priv->rps.min_delay) { + val < dev_priv->rps.min_freq_softlimit) { mutex_unlock(&dev_priv->rps.hw_lock); return -EINVAL; } @@ -349,9 +349,9 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev, DRM_DEBUG("User requested overclocking to %d\n", val * GT_FREQUENCY_MULTIPLIER); - dev_priv->rps.max_delay = val; + dev_priv->rps.max_freq_softlimit = val; - if (dev_priv->rps.cur_delay > val) { + if (dev_priv->rps.cur_freq > val) { if (IS_VALLEYVIEW(dev)) valleyview_set_rps(dev, val); else @@ -360,7 +360,7 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev, /* We still need gen6_set_rps to process the new max_delay and * update the interrupt limits even though frequency request is * unchanged. */ - gen6_set_rps(dev, dev_priv->rps.cur_delay); + gen6_set_rps(dev, dev_priv->rps.cur_freq); } mutex_unlock(&dev_priv->rps.hw_lock); @@ -379,9 +379,9 @@ static ssize_t gt_min_freq_mhz_show(struct device *kdev, struct device_attribute mutex_lock(&dev_priv->rps.hw_lock); if (IS_VALLEYVIEW(dev_priv->dev)) - ret = vlv_gpu_freq(dev_priv, dev_priv->rps.min_delay); + ret = vlv_gpu_freq(dev_priv, dev_priv->rps.min_freq_softlimit); else - ret = dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER; + ret = dev_priv->rps.min_freq_softlimit * GT_FREQUENCY_MULTIPLIER; mutex_unlock(&dev_priv->rps.hw_lock); return snprintf(buf, PAGE_SIZE, "%d\n", ret); @@ -414,18 +414,18 @@ static ssize_t gt_min_freq_mhz_store(struct device *kdev, val /= GT_FREQUENCY_MULTIPLIER; rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); - hw_max = dev_priv->rps.hw_max; + hw_max = dev_priv->rps.max_freq; hw_min = ((rp_state_cap & 0xff0000) >> 16); } - if (val < hw_min || val > hw_max || val > dev_priv->rps.max_delay) { + if (val < hw_min || val > hw_max || val > dev_priv->rps.max_freq_softlimit) { mutex_unlock(&dev_priv->rps.hw_lock); return -EINVAL; } - dev_priv->rps.min_delay = val; + dev_priv->rps.min_freq_softlimit = val; - if (dev_priv->rps.cur_delay < val) { + if (dev_priv->rps.cur_freq < val) { if (IS_VALLEYVIEW(dev)) valleyview_set_rps(dev, val); else @@ -434,7 +434,7 @@ static ssize_t gt_min_freq_mhz_store(struct device *kdev, /* We still need gen6_set_rps to process the new min_delay and * update the interrupt limits even though frequency request is * unchanged. */ - gen6_set_rps(dev, dev_priv->rps.cur_delay); + gen6_set_rps(dev, dev_priv->rps.cur_freq); } mutex_unlock(&dev_priv->rps.hw_lock); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index dd631d19b2bd7..3db7c40cc9aee 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2907,9 +2907,9 @@ static u32 gen6_rps_limits(struct drm_i915_private *dev_priv, u8 val) * the hw runs at the minimal clock before selecting the desired * frequency, if the down threshold expires in that window we will not * receive a down interrupt. */ - limits = dev_priv->rps.max_delay << 24; - if (val <= dev_priv->rps.min_delay) - limits |= dev_priv->rps.min_delay << 16; + limits = dev_priv->rps.max_freq_softlimit << 24; + if (val <= dev_priv->rps.min_freq_softlimit) + limits |= dev_priv->rps.min_freq_softlimit << 16; return limits; } @@ -2921,26 +2921,26 @@ static void gen6_set_rps_thresholds(struct drm_i915_private *dev_priv, u8 val) new_power = dev_priv->rps.power; switch (dev_priv->rps.power) { case LOW_POWER: - if (val > dev_priv->rps.rpe_delay + 1 && val > dev_priv->rps.cur_delay) + if (val > dev_priv->rps.efficient_freq + 1 && val > dev_priv->rps.cur_freq) new_power = BETWEEN; break; case BETWEEN: - if (val <= dev_priv->rps.rpe_delay && val < dev_priv->rps.cur_delay) + if (val <= dev_priv->rps.efficient_freq && val < dev_priv->rps.cur_freq) new_power = LOW_POWER; - else if (val >= dev_priv->rps.rp0_delay && val > dev_priv->rps.cur_delay) + else if (val >= dev_priv->rps.rp0_freq && val > dev_priv->rps.cur_freq) new_power = HIGH_POWER; break; case HIGH_POWER: - if (val < (dev_priv->rps.rp1_delay + dev_priv->rps.rp0_delay) >> 1 && val < dev_priv->rps.cur_delay) + if (val < (dev_priv->rps.rp1_freq + dev_priv->rps.rp0_freq) >> 1 && val < dev_priv->rps.cur_freq) new_power = BETWEEN; break; } /* Max/min bins are special */ - if (val == dev_priv->rps.min_delay) + if (val == dev_priv->rps.min_freq_softlimit) new_power = LOW_POWER; - if (val == dev_priv->rps.max_delay) + if (val == dev_priv->rps.max_freq_softlimit) new_power = HIGH_POWER; if (new_power == dev_priv->rps.power) return; @@ -3014,10 +3014,10 @@ void gen6_set_rps(struct drm_device *dev, u8 val) struct drm_i915_private *dev_priv = dev->dev_private; WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); - WARN_ON(val > dev_priv->rps.max_delay); - WARN_ON(val < dev_priv->rps.min_delay); + WARN_ON(val > dev_priv->rps.max_freq_softlimit); + WARN_ON(val < dev_priv->rps.min_freq_softlimit); - if (val == dev_priv->rps.cur_delay) { + if (val == dev_priv->rps.cur_freq) { /* min/max delay may still have been modified so be sure to * write the limits value */ I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, @@ -3045,7 +3045,7 @@ void gen6_set_rps(struct drm_device *dev, u8 val) POSTING_READ(GEN6_RPNSWREQ); - dev_priv->rps.cur_delay = val; + dev_priv->rps.cur_freq = val; trace_intel_gpu_freq_change(val * 50); } @@ -3065,7 +3065,7 @@ static void vlv_set_rps_idle(struct drm_i915_private *dev_priv) * When we are idle. Drop to min voltage state. */ - if (dev_priv->rps.cur_delay <= dev_priv->rps.min_delay) + if (dev_priv->rps.cur_freq <= dev_priv->rps.min_freq_softlimit) return; /* Mask turbo interrupt so that they will not come in between */ @@ -3082,10 +3082,10 @@ static void vlv_set_rps_idle(struct drm_i915_private *dev_priv) return; } - dev_priv->rps.cur_delay = dev_priv->rps.min_delay; + dev_priv->rps.cur_freq = dev_priv->rps.min_freq_softlimit; vlv_punit_write(dev_priv, PUNIT_REG_GPU_FREQ_REQ, - dev_priv->rps.min_delay); + dev_priv->rps.min_freq_softlimit); if (wait_for(((vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS)) & GENFREQSTATUS) == 0, 5)) @@ -3099,7 +3099,7 @@ static void vlv_set_rps_idle(struct drm_i915_private *dev_priv) /* Unmask Up interrupts */ dev_priv->rps.rp_up_masked = true; gen6_set_pm_mask(dev_priv, GEN6_PM_RP_DOWN_THRESHOLD, - dev_priv->rps.min_delay); + dev_priv->rps.min_freq_softlimit); } void gen6_rps_idle(struct drm_i915_private *dev_priv) @@ -3111,7 +3111,7 @@ void gen6_rps_idle(struct drm_i915_private *dev_priv) if (IS_VALLEYVIEW(dev)) vlv_set_rps_idle(dev_priv); else - gen6_set_rps(dev_priv->dev, dev_priv->rps.min_delay); + gen6_set_rps(dev_priv->dev, dev_priv->rps.min_freq_softlimit); dev_priv->rps.last_adj = 0; } mutex_unlock(&dev_priv->rps.hw_lock); @@ -3124,9 +3124,9 @@ void gen6_rps_boost(struct drm_i915_private *dev_priv) mutex_lock(&dev_priv->rps.hw_lock); if (dev_priv->rps.enabled) { if (IS_VALLEYVIEW(dev)) - valleyview_set_rps(dev_priv->dev, dev_priv->rps.max_delay); + valleyview_set_rps(dev_priv->dev, dev_priv->rps.max_freq_softlimit); else - gen6_set_rps(dev_priv->dev, dev_priv->rps.max_delay); + gen6_set_rps(dev_priv->dev, dev_priv->rps.max_freq_softlimit); dev_priv->rps.last_adj = 0; } mutex_unlock(&dev_priv->rps.hw_lock); @@ -3137,20 +3137,20 @@ void valleyview_set_rps(struct drm_device *dev, u8 val) struct drm_i915_private *dev_priv = dev->dev_private; WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); - WARN_ON(val > dev_priv->rps.max_delay); - WARN_ON(val < dev_priv->rps.min_delay); + WARN_ON(val > dev_priv->rps.max_freq_softlimit); + WARN_ON(val < dev_priv->rps.min_freq_softlimit); DRM_DEBUG_DRIVER("GPU freq request from %d MHz (%u) to %d MHz (%u)\n", - vlv_gpu_freq(dev_priv, dev_priv->rps.cur_delay), - dev_priv->rps.cur_delay, + vlv_gpu_freq(dev_priv, dev_priv->rps.cur_freq), + dev_priv->rps.cur_freq, vlv_gpu_freq(dev_priv, val), val); - if (val == dev_priv->rps.cur_delay) + if (val == dev_priv->rps.cur_freq) return; vlv_punit_write(dev_priv, PUNIT_REG_GPU_FREQ_REQ, val); - dev_priv->rps.cur_delay = val; + dev_priv->rps.cur_freq = val; trace_intel_gpu_freq_change(vlv_gpu_freq(dev_priv, val)); } @@ -3292,8 +3292,8 @@ static void gen8_enable_rps(struct drm_device *dev) /* Docs recommend 900MHz, and 300 MHz respectively */ I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, - dev_priv->rps.max_delay << 24 | - dev_priv->rps.min_delay << 16); + dev_priv->rps.max_freq_softlimit << 24 | + dev_priv->rps.min_freq_softlimit << 16); I915_WRITE(GEN6_RP_UP_THRESHOLD, 7600000 / 128); /* 76ms busyness per EI, 90% */ I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 31300000 / 128); /* 313ms busyness per EI, 70%*/ @@ -3352,20 +3352,22 @@ static void gen6_enable_rps(struct drm_device *dev) rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS); - /* In units of 50MHz */ - dev_priv->rps.hw_max = hw_max = rp_state_cap & 0xff; + /* All of these values are in units of 50MHz */ + dev_priv->rps.cur_freq = 0; + /* hw_max = RP0 until we check for overclocking */ + dev_priv->rps.max_freq = hw_max = rp_state_cap & 0xff; + /* static values from HW: RP0 < RPe < RP1 < RPn (min_freq) */ + dev_priv->rps.rp1_freq = (rp_state_cap >> 8) & 0xff; + dev_priv->rps.rp0_freq = (rp_state_cap >> 0) & 0xff; + dev_priv->rps.efficient_freq = dev_priv->rps.rp1_freq; dev_priv->rps.min_freq = hw_min = (rp_state_cap >> 16) & 0xff; - dev_priv->rps.rp1_delay = (rp_state_cap >> 8) & 0xff; - dev_priv->rps.rp0_delay = (rp_state_cap >> 0) & 0xff; - dev_priv->rps.rpe_delay = dev_priv->rps.rp1_delay; - dev_priv->rps.cur_delay = 0; /* Preserve min/max settings in case of re-init */ - if (dev_priv->rps.max_delay == 0) - dev_priv->rps.max_delay = hw_max; + if (dev_priv->rps.max_freq_softlimit == 0) + dev_priv->rps.max_freq_softlimit = hw_max; - if (dev_priv->rps.min_delay == 0) - dev_priv->rps.min_delay = hw_min; + if (dev_priv->rps.min_freq_softlimit == 0) + dev_priv->rps.min_freq_softlimit = hw_min; /* disable the counters and set deterministic thresholds */ I915_WRITE(GEN6_RC_CONTROL, 0); @@ -3420,13 +3422,13 @@ static void gen6_enable_rps(struct drm_device *dev) ret = sandybridge_pcode_read(dev_priv, GEN6_READ_OC_PARAMS, &pcu_mbox); if (!ret && (pcu_mbox & (1<<31))) { /* OC supported */ DRM_DEBUG_DRIVER("Overclocking supported. Max: %dMHz, Overclock max: %dMHz\n", - (dev_priv->rps.max_delay & 0xff) * 50, + (dev_priv->rps.max_freq_softlimit & 0xff) * 50, (pcu_mbox & 0xff) * 50); - dev_priv->rps.hw_max = pcu_mbox & 0xff; + dev_priv->rps.max_freq = pcu_mbox & 0xff; } dev_priv->rps.power = HIGH_POWER; /* force a reset */ - gen6_set_rps(dev_priv->dev, dev_priv->rps.min_delay); + gen6_set_rps(dev_priv->dev, dev_priv->rps.min_freq_softlimit); gen6_enable_rps_interrupts(dev); @@ -3482,9 +3484,9 @@ void gen6_update_ring_freq(struct drm_device *dev) * to use for memory access. We do this by specifying the IA frequency * the PCU should use as a reference to determine the ring frequency. */ - for (gpu_freq = dev_priv->rps.max_delay; gpu_freq >= dev_priv->rps.min_delay; + for (gpu_freq = dev_priv->rps.max_freq_softlimit; gpu_freq >= dev_priv->rps.min_freq_softlimit; gpu_freq--) { - int diff = dev_priv->rps.max_delay - gpu_freq; + int diff = dev_priv->rps.max_freq_softlimit - gpu_freq; unsigned int ia_freq = 0, ring_freq = 0; if (INTEL_INFO(dev)->gen >= 8) { @@ -3650,20 +3652,20 @@ static void valleyview_enable_rps(struct drm_device *dev) DRM_DEBUG_DRIVER("GPLL enabled? %s\n", val & 0x10 ? "yes" : "no"); DRM_DEBUG_DRIVER("GPU status: 0x%08x\n", val); - dev_priv->rps.cur_delay = (val >> 8) & 0xff; + dev_priv->rps.cur_freq = (val >> 8) & 0xff; DRM_DEBUG_DRIVER("current GPU freq: %d MHz (%u)\n", - vlv_gpu_freq(dev_priv, dev_priv->rps.cur_delay), - dev_priv->rps.cur_delay); + vlv_gpu_freq(dev_priv, dev_priv->rps.cur_freq), + dev_priv->rps.cur_freq); - dev_priv->rps.hw_max = hw_max = valleyview_rps_max_freq(dev_priv); + dev_priv->rps.max_freq = hw_max = valleyview_rps_max_freq(dev_priv); DRM_DEBUG_DRIVER("max GPU freq: %d MHz (%u)\n", vlv_gpu_freq(dev_priv, hw_max), hw_max); - dev_priv->rps.rpe_delay = valleyview_rps_rpe_freq(dev_priv); + dev_priv->rps.efficient_freq = valleyview_rps_rpe_freq(dev_priv); DRM_DEBUG_DRIVER("RPe GPU freq: %d MHz (%u)\n", - vlv_gpu_freq(dev_priv, dev_priv->rps.rpe_delay), - dev_priv->rps.rpe_delay); + vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq), + dev_priv->rps.efficient_freq); hw_min = valleyview_rps_min_freq(dev_priv); DRM_DEBUG_DRIVER("min GPU freq: %d MHz (%u)\n", @@ -3671,17 +3673,17 @@ static void valleyview_enable_rps(struct drm_device *dev) hw_min); /* Preserve min/max settings in case of re-init */ - if (dev_priv->rps.max_delay == 0) - dev_priv->rps.max_delay = hw_max; + if (dev_priv->rps.max_freq_softlimit == 0) + dev_priv->rps.max_freq_softlimit = hw_max; - if (dev_priv->rps.min_delay == 0) - dev_priv->rps.min_delay = hw_min; + if (dev_priv->rps.min_freq_softlimit == 0) + dev_priv->rps.min_freq_softlimit = hw_min; DRM_DEBUG_DRIVER("setting GPU freq to %d MHz (%u)\n", - vlv_gpu_freq(dev_priv, dev_priv->rps.rpe_delay), - dev_priv->rps.rpe_delay); + vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq), + dev_priv->rps.efficient_freq); - valleyview_set_rps(dev_priv->dev, dev_priv->rps.rpe_delay); + valleyview_set_rps(dev_priv->dev, dev_priv->rps.efficient_freq); dev_priv->rps.rp_up_masked = false; dev_priv->rps.rp_down_masked = false; @@ -4122,7 +4124,7 @@ static unsigned long __i915_gfx_val(struct drm_i915_private *dev_priv) assert_spin_locked(&mchdev_lock); - pxvid = I915_READ(PXVFREQ_BASE + (dev_priv->rps.cur_delay * 4)); + pxvid = I915_READ(PXVFREQ_BASE + (dev_priv->rps.cur_freq * 4)); pxvid = (pxvid >> 24) & 0x7f; ext_v = pvid_to_extvid(dev_priv, pxvid); From 04da7e77e2cb8eadb84e12ecae3398e2a5b9ba70 Mon Sep 17 00:00:00 2001 From: Ben Widawsky <benjamin.widawsky@intel.com> Date: Wed, 19 Mar 2014 18:31:12 -0700 Subject: [PATCH 68/75] drm/i915: Remove extraneous MMIO for RPS The values created at initialization must always exist to use the interface. Reading them again is confusing, and pointless. More cleanups are coming in the next patch. Since I am not 100% certain, moreover on BYT, (though I am extremely close to that) that there is no need to leave the MMIO here, I wanted to make it a separate patch for the bisectable 'just-in-case' Signed-off-by: Ben Widawsky <ben@bwidawsk.net> Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_sysfs.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index e3fa8cd419da7..49554d9a6b712 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c @@ -313,7 +313,7 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev, struct drm_minor *minor = dev_to_drm_minor(kdev); struct drm_device *dev = minor->dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 val, rp_state_cap, hw_max, hw_min, non_oc_max; + u32 val, hw_max, hw_min, non_oc_max; ssize_t ret; ret = kstrtou32(buf, 0, &val); @@ -327,16 +327,14 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev, if (IS_VALLEYVIEW(dev_priv->dev)) { val = vlv_freq_opcode(dev_priv, val); - hw_max = valleyview_rps_max_freq(dev_priv); - hw_min = valleyview_rps_min_freq(dev_priv); - non_oc_max = hw_max; + non_oc_max = hw_max = dev_priv->rps.max_freq; + hw_min = dev_priv->rps.min_freq; } else { val /= GT_FREQUENCY_MULTIPLIER; - rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); hw_max = dev_priv->rps.max_freq; - non_oc_max = (rp_state_cap & 0xff); - hw_min = ((rp_state_cap & 0xff0000) >> 16); + non_oc_max = dev_priv->rps.rp0_freq; + hw_min = dev_priv->rps.min_freq; } if (val < hw_min || val > hw_max || @@ -394,7 +392,7 @@ static ssize_t gt_min_freq_mhz_store(struct device *kdev, struct drm_minor *minor = dev_to_drm_minor(kdev); struct drm_device *dev = minor->dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 val, rp_state_cap, hw_max, hw_min; + u32 val, hw_max, hw_min; ssize_t ret; ret = kstrtou32(buf, 0, &val); @@ -408,14 +406,13 @@ static ssize_t gt_min_freq_mhz_store(struct device *kdev, if (IS_VALLEYVIEW(dev)) { val = vlv_freq_opcode(dev_priv, val); - hw_max = valleyview_rps_max_freq(dev_priv); - hw_min = valleyview_rps_min_freq(dev_priv); + hw_max = dev_priv->rps.max_freq; + hw_min = dev_priv->rps.min_freq; } else { val /= GT_FREQUENCY_MULTIPLIER; - rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); hw_max = dev_priv->rps.max_freq; - hw_min = ((rp_state_cap & 0xff0000) >> 16); + hw_min = dev_priv->rps.min_freq; } if (val < hw_min || val > hw_max || val > dev_priv->rps.max_freq_softlimit) { From 2a5913a8670a6925c04e397e0a8ebd72cb4b2d26 Mon Sep 17 00:00:00 2001 From: Ben Widawsky <benjamin.widawsky@intel.com> Date: Wed, 19 Mar 2014 18:31:13 -0700 Subject: [PATCH 69/75] drm/i915: remove rps local variables With the renamed RPS struct members, it's easier to skip the local variables which no longer clarify anything, and if anything just make the code harder to read. The real motivation for this patch is actually the next patch, which attempts to consolidate some of the functionality. Cc: Jeff McGee <jeff.mcgee@intel.com> Signed-off-by: Ben Widawsky <ben@bwidawsk.net> Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_sysfs.c | 36 ++++++++++------------------ drivers/gpu/drm/i915/intel_pm.c | 40 ++++++++++++++++--------------- 2 files changed, 33 insertions(+), 43 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index 49554d9a6b712..9c57029f6f4b4 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c @@ -313,7 +313,7 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev, struct drm_minor *minor = dev_to_drm_minor(kdev); struct drm_device *dev = minor->dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 val, hw_max, hw_min, non_oc_max; + u32 val; ssize_t ret; ret = kstrtou32(buf, 0, &val); @@ -324,26 +324,19 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev, mutex_lock(&dev_priv->rps.hw_lock); - if (IS_VALLEYVIEW(dev_priv->dev)) { + if (IS_VALLEYVIEW(dev_priv->dev)) val = vlv_freq_opcode(dev_priv, val); - - non_oc_max = hw_max = dev_priv->rps.max_freq; - hw_min = dev_priv->rps.min_freq; - } else { + else val /= GT_FREQUENCY_MULTIPLIER; - hw_max = dev_priv->rps.max_freq; - non_oc_max = dev_priv->rps.rp0_freq; - hw_min = dev_priv->rps.min_freq; - } - - if (val < hw_min || val > hw_max || + if (val < dev_priv->rps.min_freq || + val > dev_priv->rps.max_freq || val < dev_priv->rps.min_freq_softlimit) { mutex_unlock(&dev_priv->rps.hw_lock); return -EINVAL; } - if (val > non_oc_max) + if (val > dev_priv->rps.rp0_freq) DRM_DEBUG("User requested overclocking to %d\n", val * GT_FREQUENCY_MULTIPLIER); @@ -392,7 +385,7 @@ static ssize_t gt_min_freq_mhz_store(struct device *kdev, struct drm_minor *minor = dev_to_drm_minor(kdev); struct drm_device *dev = minor->dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 val, hw_max, hw_min; + u32 val; ssize_t ret; ret = kstrtou32(buf, 0, &val); @@ -403,19 +396,14 @@ static ssize_t gt_min_freq_mhz_store(struct device *kdev, mutex_lock(&dev_priv->rps.hw_lock); - if (IS_VALLEYVIEW(dev)) { + if (IS_VALLEYVIEW(dev)) val = vlv_freq_opcode(dev_priv, val); - - hw_max = dev_priv->rps.max_freq; - hw_min = dev_priv->rps.min_freq; - } else { + else val /= GT_FREQUENCY_MULTIPLIER; - hw_max = dev_priv->rps.max_freq; - hw_min = dev_priv->rps.min_freq; - } - - if (val < hw_min || val > hw_max || val > dev_priv->rps.max_freq_softlimit) { + if (val < dev_priv->rps.min_freq || + val > dev_priv->rps.max_freq || + val > dev_priv->rps.max_freq_softlimit) { mutex_unlock(&dev_priv->rps.hw_lock); return -EINVAL; } diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 3db7c40cc9aee..fd68f93671bba 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3324,7 +3324,7 @@ static void gen6_enable_rps(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_ring_buffer *ring; - u32 rp_state_cap, hw_max, hw_min; + u32 rp_state_cap; u32 gt_perf_status; u32 rc6vids, pcu_mbox = 0, rc6_mask = 0; u32 gtfifodbg; @@ -3353,21 +3353,22 @@ static void gen6_enable_rps(struct drm_device *dev) gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS); /* All of these values are in units of 50MHz */ - dev_priv->rps.cur_freq = 0; - /* hw_max = RP0 until we check for overclocking */ - dev_priv->rps.max_freq = hw_max = rp_state_cap & 0xff; + dev_priv->rps.cur_freq = 0; /* static values from HW: RP0 < RPe < RP1 < RPn (min_freq) */ - dev_priv->rps.rp1_freq = (rp_state_cap >> 8) & 0xff; - dev_priv->rps.rp0_freq = (rp_state_cap >> 0) & 0xff; - dev_priv->rps.efficient_freq = dev_priv->rps.rp1_freq; - dev_priv->rps.min_freq = hw_min = (rp_state_cap >> 16) & 0xff; + dev_priv->rps.rp1_freq = (rp_state_cap >> 8) & 0xff; + dev_priv->rps.rp0_freq = (rp_state_cap >> 0) & 0xff; + dev_priv->rps.min_freq = (rp_state_cap >> 16) & 0xff; + /* XXX: only BYT has a special efficient freq */ + dev_priv->rps.efficient_freq = dev_priv->rps.rp1_freq; + /* hw_max = RP0 until we check for overclocking */ + dev_priv->rps.max_freq = dev_priv->rps.rp0_freq; /* Preserve min/max settings in case of re-init */ if (dev_priv->rps.max_freq_softlimit == 0) - dev_priv->rps.max_freq_softlimit = hw_max; + dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq; if (dev_priv->rps.min_freq_softlimit == 0) - dev_priv->rps.min_freq_softlimit = hw_min; + dev_priv->rps.min_freq_softlimit = dev_priv->rps.min_freq; /* disable the counters and set deterministic thresholds */ I915_WRITE(GEN6_RC_CONTROL, 0); @@ -3597,7 +3598,7 @@ static void valleyview_enable_rps(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_ring_buffer *ring; - u32 gtfifodbg, val, hw_max, hw_min, rc6_mode = 0; + u32 gtfifodbg, val, rc6_mode = 0; int i; WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); @@ -3657,27 +3658,28 @@ static void valleyview_enable_rps(struct drm_device *dev) vlv_gpu_freq(dev_priv, dev_priv->rps.cur_freq), dev_priv->rps.cur_freq); - dev_priv->rps.max_freq = hw_max = valleyview_rps_max_freq(dev_priv); + dev_priv->rps.max_freq = valleyview_rps_max_freq(dev_priv); + dev_priv->rps.rp0_freq = dev_priv->rps.max_freq; DRM_DEBUG_DRIVER("max GPU freq: %d MHz (%u)\n", - vlv_gpu_freq(dev_priv, hw_max), - hw_max); + vlv_gpu_freq(dev_priv, dev_priv->rps.max_freq), + dev_priv->rps.max_freq); dev_priv->rps.efficient_freq = valleyview_rps_rpe_freq(dev_priv); DRM_DEBUG_DRIVER("RPe GPU freq: %d MHz (%u)\n", vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq), dev_priv->rps.efficient_freq); - hw_min = valleyview_rps_min_freq(dev_priv); + dev_priv->rps.min_freq = valleyview_rps_min_freq(dev_priv); DRM_DEBUG_DRIVER("min GPU freq: %d MHz (%u)\n", - vlv_gpu_freq(dev_priv, hw_min), - hw_min); + vlv_gpu_freq(dev_priv, dev_priv->rps.min_freq), + dev_priv->rps.min_freq); /* Preserve min/max settings in case of re-init */ if (dev_priv->rps.max_freq_softlimit == 0) - dev_priv->rps.max_freq_softlimit = hw_max; + dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq; if (dev_priv->rps.min_freq_softlimit == 0) - dev_priv->rps.min_freq_softlimit = hw_min; + dev_priv->rps.min_freq_softlimit = dev_priv->rps.min_freq; DRM_DEBUG_DRIVER("setting GPU freq to %d MHz (%u)\n", vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq), From 6313c20490c8ae32ffb40f45c60734db53ca85ea Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Wed, 19 Mar 2014 13:45:45 +0000 Subject: [PATCH 70/75] drm/i915: Per-process stats work better when evaluated per-process The idea of printing objects used by each process is to judge how each process is using them. This means that we need to evaluate whether the object is bound for that particular process, rather than just whether it is bound into the global GTT. v2: Restore the non-full-ppgtt path for simplicity as we may not even create vma with older hardware. v3: Tweak handling of global entries and default context entries. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Cc: Ben Widawsky <benjamin.widawsky@intel.com> Reviewed-by: Ben Widawsky <ben@bwidawsk.net> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_debugfs.c | 49 ++++++++++++++++++++----- drivers/gpu/drm/i915/i915_drv.h | 2 + drivers/gpu/drm/i915/i915_gem_context.c | 1 + 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index d1e0a360558f1..2bba4c25c53e8 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -299,28 +299,57 @@ static int i915_gem_stolen_list_info(struct seq_file *m, void *data) } while (0) struct file_stats { + struct drm_i915_file_private *file_priv; int count; - size_t total, active, inactive, unbound; + size_t total, global, active, inactive, unbound; }; static int per_file_stats(int id, void *ptr, void *data) { struct drm_i915_gem_object *obj = ptr; struct file_stats *stats = data; + struct i915_vma *vma; stats->count++; stats->total += obj->base.size; - if (i915_gem_obj_ggtt_bound(obj)) { - if (!list_empty(&obj->ring_list)) - stats->active += obj->base.size; - else - stats->inactive += obj->base.size; + if (USES_FULL_PPGTT(obj->base.dev)) { + list_for_each_entry(vma, &obj->vma_list, vma_link) { + struct i915_hw_ppgtt *ppgtt; + + if (!drm_mm_node_allocated(&vma->node)) + continue; + + if (i915_is_ggtt(vma->vm)) { + stats->global += obj->base.size; + continue; + } + + ppgtt = container_of(vma->vm, struct i915_hw_ppgtt, base); + if (ppgtt->ctx && ppgtt->ctx->file_priv != stats->file_priv) + continue; + + if (obj->ring) /* XXX per-vma statistic */ + stats->active += obj->base.size; + else + stats->inactive += obj->base.size; + + return 0; + } } else { - if (!list_empty(&obj->global_list)) - stats->unbound += obj->base.size; + if (i915_gem_obj_ggtt_bound(obj)) { + stats->global += obj->base.size; + if (obj->ring) + stats->active += obj->base.size; + else + stats->inactive += obj->base.size; + return 0; + } } + if (!list_empty(&obj->global_list)) + stats->unbound += obj->base.size; + return 0; } @@ -411,6 +440,7 @@ static int i915_gem_object_info(struct seq_file *m, void* data) struct task_struct *task; memset(&stats, 0, sizeof(stats)); + stats.file_priv = file->driver_priv; idr_for_each(&file->object_idr, per_file_stats, &stats); /* * Although we have a valid reference on file->pid, that does @@ -420,12 +450,13 @@ static int i915_gem_object_info(struct seq_file *m, void* data) */ rcu_read_lock(); task = pid_task(file->pid, PIDTYPE_PID); - seq_printf(m, "%s: %u objects, %zu bytes (%zu active, %zu inactive, %zu unbound)\n", + seq_printf(m, "%s: %u objects, %zu bytes (%zu active, %zu inactive, %zu global, %zu unbound)\n", task ? task->comm : "<unknown>", stats.count, stats.total, stats.active, stats.inactive, + stats.global, stats.unbound); rcu_read_unlock(); } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index c5c57608460c4..9b8c1e06a7870 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -725,6 +725,8 @@ struct i915_hw_ppgtt { dma_addr_t *gen8_pt_dma_addr[4]; }; + struct i915_hw_context *ctx; + int (*enable)(struct i915_hw_ppgtt *ppgtt); int (*switch_mm)(struct i915_hw_ppgtt *ppgtt, struct intel_ring_buffer *ring, diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index b5a58372eb06b..6043062ffce73 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -215,6 +215,7 @@ create_vm_for_ctx(struct drm_device *dev, struct i915_hw_context *ctx) return ERR_PTR(ret); } + ppgtt->ctx = ctx; return ppgtt; } From c67a17e972913e03e37d9f55d3bcd7bfc0404a37 Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Wed, 19 Mar 2014 13:45:46 +0000 Subject: [PATCH 71/75] drm/i915: Print how many objects are shared in per-process stats The point of this measure is to gauge why a process has a lot of gem objects in uses and why. Especially for compositors it's interesting to know whether it's a leak of private objects or just a lot of use from buffers shared with clients. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Reviewed-by: Ben Widawsky <ben@bwidawsk.net> [danvet: Add a bit of commit message flesh to address Ben's comment.] Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_debugfs.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 2bba4c25c53e8..049dcb5256d1e 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -301,7 +301,9 @@ static int i915_gem_stolen_list_info(struct seq_file *m, void *data) struct file_stats { struct drm_i915_file_private *file_priv; int count; - size_t total, global, active, inactive, unbound; + size_t total, unbound; + size_t global, shared; + size_t active, inactive; }; static int per_file_stats(int id, void *ptr, void *data) @@ -313,6 +315,9 @@ static int per_file_stats(int id, void *ptr, void *data) stats->count++; stats->total += obj->base.size; + if (obj->base.name || obj->base.dma_buf) + stats->shared += obj->base.size; + if (USES_FULL_PPGTT(obj->base.dev)) { list_for_each_entry(vma, &obj->vma_list, vma_link) { struct i915_hw_ppgtt *ppgtt; @@ -450,13 +455,14 @@ static int i915_gem_object_info(struct seq_file *m, void* data) */ rcu_read_lock(); task = pid_task(file->pid, PIDTYPE_PID); - seq_printf(m, "%s: %u objects, %zu bytes (%zu active, %zu inactive, %zu global, %zu unbound)\n", + seq_printf(m, "%s: %u objects, %zu bytes (%zu active, %zu inactive, %zu global, %zu shared, %zu unbound)\n", task ? task->comm : "<unknown>", stats.count, stats.total, stats.active, stats.inactive, stats.global, + stats.shared, stats.unbound); rcu_read_unlock(); } From 4726e0b045b80c514377da35ca01467ef6a4de53 Mon Sep 17 00:00:00 2001 From: Sagar Kamble <sagar.a.kamble@intel.com> Date: Mon, 10 Mar 2014 17:06:23 +0530 Subject: [PATCH 72/75] drm/i915: Enabling 128x128 and 256x256 ARGB Cursor Support With this patch we allow larger cursor planes of sizes 128x128 and 256x256. v2: Added more precise check on size while setting cursor plane. v3: Changes related to restructuring cursor size restrictions and DRM_DEBUG usage. v4: Indentation related changes for setting cursor control and implementing DRM_CAP_CURSOR_WIDTH and DRM_CAP_CURSOR_HEIGHT Testcase: igt/kms_cursor_crc Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Cc: Jani Nikula <jani.nikula@linux.intel.com> Cc: David Airlie <airlied@linux.ie> Cc: dri-devel@lists.freedesktop.org Cc: linux-kernel@vger.kernel.org Signed-off-by: G, Pallavi <pallavi.g@intel.com> Signed-off-by: Sagar Kamble <sagar.a.kamble@intel.com> Reviewed-by: Imre Deak <imre.deak@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_reg.h | 4 +++ drivers/gpu/drm/i915/intel_display.c | 53 +++++++++++++++++++++++++--- drivers/gpu/drm/i915/intel_drv.h | 7 ++++ 3 files changed, 59 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 6174fda4d58ea..74f7d853eb581 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3553,7 +3553,11 @@ enum punit_power_well { /* New style CUR*CNTR flags */ #define CURSOR_MODE 0x27 #define CURSOR_MODE_DISABLE 0x00 +#define CURSOR_MODE_128_32B_AX 0x02 +#define CURSOR_MODE_256_32B_AX 0x03 #define CURSOR_MODE_64_32B_AX 0x07 +#define CURSOR_MODE_128_ARGB_AX ((1 << 5) | CURSOR_MODE_128_32B_AX) +#define CURSOR_MODE_256_ARGB_AX ((1 << 5) | CURSOR_MODE_256_32B_AX) #define CURSOR_MODE_64_ARGB_AX ((1 << 5) | CURSOR_MODE_64_32B_AX) #define MCURSOR_PIPE_SELECT (1 << 28) #define MCURSOR_PIPE_A 0x00 diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5f68491ae99af..7be5984431bb6 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7599,10 +7599,26 @@ static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base) bool visible = base != 0; if (intel_crtc->cursor_visible != visible) { + int16_t width = intel_crtc->cursor_width; uint32_t cntl = I915_READ(CURCNTR(pipe)); if (base) { cntl &= ~(CURSOR_MODE | MCURSOR_PIPE_SELECT); - cntl |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE; + cntl |= MCURSOR_GAMMA_ENABLE; + + switch (width) { + case 64: + cntl |= CURSOR_MODE_64_ARGB_AX; + break; + case 128: + cntl |= CURSOR_MODE_128_ARGB_AX; + break; + case 256: + cntl |= CURSOR_MODE_256_ARGB_AX; + break; + default: + WARN_ON(1); + return; + } cntl |= pipe << 28; /* Connect to correct pipe */ } else { cntl &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE); @@ -7627,10 +7643,25 @@ static void ivb_update_cursor(struct drm_crtc *crtc, u32 base) bool visible = base != 0; if (intel_crtc->cursor_visible != visible) { + int16_t width = intel_crtc->cursor_width; uint32_t cntl = I915_READ(CURCNTR_IVB(pipe)); if (base) { cntl &= ~CURSOR_MODE; - cntl |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE; + cntl |= MCURSOR_GAMMA_ENABLE; + switch (width) { + case 64: + cntl |= CURSOR_MODE_64_ARGB_AX; + break; + case 128: + cntl |= CURSOR_MODE_128_ARGB_AX; + break; + case 256: + cntl |= CURSOR_MODE_256_ARGB_AX; + break; + default: + WARN_ON(1); + return; + } } else { cntl &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE); cntl |= CURSOR_MODE_DISABLE; @@ -7726,9 +7757,11 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, goto finish; } - /* Currently we only support 64x64 cursors */ - if (width != 64 || height != 64) { - DRM_ERROR("we currently only support 64x64 cursors\n"); + /* Check for which cursor types we support */ + if (!((width == 64 && height == 64) || + (width == 128 && height == 128 && !IS_GEN2(dev)) || + (width == 256 && height == 256 && !IS_GEN2(dev)))) { + DRM_DEBUG("Cursor dimension not supported\n"); return -EINVAL; } @@ -10514,6 +10547,16 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) drm_crtc_init(dev, &intel_crtc->base, &intel_crtc_funcs); + if (IS_GEN2(dev)) { + intel_crtc->max_cursor_width = GEN2_CURSOR_WIDTH; + intel_crtc->max_cursor_height = GEN2_CURSOR_HEIGHT; + } else { + intel_crtc->max_cursor_width = CURSOR_WIDTH; + intel_crtc->max_cursor_height = CURSOR_HEIGHT; + } + dev->mode_config.cursor_width = intel_crtc->max_cursor_width; + dev->mode_config.cursor_height = intel_crtc->max_cursor_height; + drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256); for (i = 0; i < 256; i++) { intel_crtc->lut_r[i] = i; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 60ffad376390e..fa9910481ab0b 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -78,6 +78,12 @@ #define MAX_OUTPUTS 6 /* maximum connectors per crtcs in the mode set */ +/* Maximum cursor sizes */ +#define GEN2_CURSOR_WIDTH 64 +#define GEN2_CURSOR_HEIGHT 64 +#define CURSOR_WIDTH 256 +#define CURSOR_HEIGHT 256 + #define INTEL_I2C_BUS_DVO 1 #define INTEL_I2C_BUS_SDVO 2 @@ -367,6 +373,7 @@ struct intel_crtc { uint32_t cursor_addr; int16_t cursor_x, cursor_y; int16_t cursor_width, cursor_height; + int16_t max_cursor_width, max_cursor_height; bool cursor_visible; struct intel_plane_config plane_config; From df6f783a4ef6790780a67c491897ac45c4618735 Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Fri, 21 Mar 2014 07:40:56 +0000 Subject: [PATCH 73/75] drm/i915: Fix unsafe loop iteration over vma whilst unbinding them On non-LLC platforms, when changing the cache level of an object, we may need to unbind it so that prefetching across page boundaries does not cross into a different memory domain. This requires us to unbind conflicting vma, but we did so iterating over the objects vma in an unsafe manner (as the list was being modified as we iterated). The regression was introduced in commit 3089c6f239d7d2c4cb2dd5c353e8984cf79af1d7 Author: Ben Widawsky <ben@bwidawsk.net> Date: Wed Jul 31 17:00:03 2013 -0700 drm/i915: make caching operate on all address spaces apparently as far back as v3.12-rc1, but it has only just begun to trigger real world bug reports. Reported-and-tested-by: Nikolay Martynov <mar.kolya@gmail.com> Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=76384 Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Cc: Ben Widawsky <ben@bwidawsk.net> Cc: stable@vger.kernel.org Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_gem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index ee32759ffce3d..33bbaa0d44129 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3467,7 +3467,7 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, enum i915_cache_level cache_level) { struct drm_device *dev = obj->base.dev; - struct i915_vma *vma; + struct i915_vma *vma, *next; int ret; if (obj->cache_level == cache_level) @@ -3478,7 +3478,7 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, return -EBUSY; } - list_for_each_entry(vma, &obj->vma_list, vma_link) { + list_for_each_entry_safe(vma, next, &obj->vma_list, vma_link) { if (!i915_gem_valid_gtt_space(dev, &vma->node, cache_level)) { ret = i915_vma_unbind(vma); if (ret) From d0a7b6de04dab17ee2c0cde22705d48c4ce5f6e0 Mon Sep 17 00:00:00 2001 From: Jani Nikula <jani.nikula@intel.com> Date: Fri, 21 Mar 2014 14:56:32 +0200 Subject: [PATCH 74/75] drm/i915/sdvo: fix questionable return value check intel_sdvo_get_trained_inputs() returns a bool, check the status accordingly. Signed-off-by: Jani Nikula <jani.nikula@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/intel_sdvo.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 9a0b71f6aed6e..d27155adf5db2 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1461,7 +1461,7 @@ static void intel_enable_sdvo(struct intel_encoder *encoder) u32 temp; bool input1, input2; int i; - u8 status; + bool success; temp = I915_READ(intel_sdvo->sdvo_reg); if ((temp & SDVO_ENABLE) == 0) { @@ -1475,12 +1475,12 @@ static void intel_enable_sdvo(struct intel_encoder *encoder) for (i = 0; i < 2; i++) intel_wait_for_vblank(dev, intel_crtc->pipe); - status = intel_sdvo_get_trained_inputs(intel_sdvo, &input1, &input2); + success = intel_sdvo_get_trained_inputs(intel_sdvo, &input1, &input2); /* Warn if the device reported failure to sync. * A lot of SDVO devices fail to notify of sync, but it's * a given it the status is a success, we succeeded. */ - if (status == SDVO_CMD_STATUS_SUCCESS && !input1) { + if (success && !input1) { DRM_DEBUG_KMS("First %s output reported failure to " "sync\n", SDVO_NAME(intel_sdvo)); } From 698b3135acb94e838a33a69f1a7a684fe0d90734 Mon Sep 17 00:00:00 2001 From: Chris Wilson <chris@chris-wilson.co.uk> Date: Fri, 21 Mar 2014 13:16:43 +0000 Subject: [PATCH 75/75] drm/i915: Include a note about the dangers of I915_READ64/I915_WRITE64 It is important that the user is fully aware that the seemingly atomic read/write of a 64-bit value from MMIO space, may in fact be 2 separate operations of 32-bits. This can lead to hilarity, such as commit d18b9619034230b6f945e215276425636ca401fe Author: Chris Wilson <chris@chris-wilson.co.uk> Date: Wed Jul 10 13:36:23 2013 +0100 drm/i915: Fix incoherence with fence updates on Sandybridge+ Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> --- drivers/gpu/drm/i915/i915_drv.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 9b8c1e06a7870..3f62be0fb5c52 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2736,6 +2736,12 @@ void vlv_force_wake_put(struct drm_i915_private *dev_priv, int fw_engine); #define I915_READ_NOTRACE(reg) dev_priv->uncore.funcs.mmio_readl(dev_priv, (reg), false) #define I915_WRITE_NOTRACE(reg, val) dev_priv->uncore.funcs.mmio_writel(dev_priv, (reg), (val), false) +/* Be very careful with read/write 64-bit values. On 32-bit machines, they + * will be implemented using 2 32-bit writes in an arbitrary order with + * an arbitrary delay between them. This can cause the hardware to + * act upon the intermediate value, possibly leading to corruption and + * machine death. You have been warned. + */ #define I915_WRITE64(reg, val) dev_priv->uncore.funcs.mmio_writeq(dev_priv, (reg), (val), true) #define I915_READ64(reg) dev_priv->uncore.funcs.mmio_readq(dev_priv, (reg), true)