From patchwork Thu Jul 23 21:32:02 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jesse Barnes X-Patchwork-Id: 37029 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n6NLWB94009469 for ; Thu, 23 Jul 2009 21:32:11 GMT Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 981B39E8F2; Thu, 23 Jul 2009 14:32:10 -0700 (PDT) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from outbound-mail-104.bluehost.com (outbound-mail-104.bluehost.com [69.89.22.14]) by gabe.freedesktop.org (Postfix) with SMTP id 19B0A9E7EE for ; Thu, 23 Jul 2009 14:32:07 -0700 (PDT) Received: (qmail 11834 invoked by uid 0); 23 Jul 2009 21:32:07 -0000 Received: from unknown (HELO box514.bluehost.com) (74.220.219.114) by outboundproxy3.bluehost.com with SMTP; 23 Jul 2009 21:32:07 -0000 Received: from [75.111.28.251] (helo=jbarnes-g45) by box514.bluehost.com with esmtpsa (TLSv1:AES128-SHA:128) (Exim 4.69) (envelope-from ) id 1MU5tV-000788-Fg; Thu, 23 Jul 2009 15:32:05 -0600 Date: Thu, 23 Jul 2009 14:32:02 -0700 From: Jesse Barnes To: Jesse Barnes Message-ID: <20090723143202.672558c9@jbarnes-g45> In-Reply-To: <20090723141840.2993a31f@jbarnes-g45> References: <20090722121607.29042660@jbarnes-g45> <20090723132723.5c1bf3e3@jbarnes-g45> <20090723141840.2993a31f@jbarnes-g45> X-Mailer: Claws Mail 3.7.2 (GTK+ 2.17.5; i486-pc-linux-gnu) Mime-Version: 1.0 X-Identified-User: {10642:box514.bluehost.com:virtuous:virtuousgeek.org} {sentby:smtp auth 75.111.28.251 authed with jbarnes@virtuousgeek.org} Cc: intel-gfx@lists.freedesktop.org Subject: Re: [Intel-gfx] [RFC] drm/i915: downclock support X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.9 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: intel-gfx-bounces@lists.freedesktop.org Errors-To: intel-gfx-bounces@lists.freedesktop.org Arg, I'm spamming sorry. Last one for today, I hope. This one makes the low frequency available booleans into per-crtc variables, and allows for pixel clock reduction on non-LVDS displays as well (not sure if that'll work well though). Anyway I'll let Matthew post the next one if necessary. diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 8851315..60ea984 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -2004,8 +2004,11 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device) status = acpi_remove_notify_handler(device->dev->handle, ACPI_DEVICE_NOTIFY, acpi_video_device_notify); - sysfs_remove_link(&device->backlight->dev.kobj, "device"); - backlight_device_unregister(device->backlight); + if (device->backlight) { + sysfs_remove_link(&device->backlight->dev.kobj, "device"); + backlight_device_unregister(device->backlight); + device->backlight = NULL; + } if (device->cdev) { sysfs_remove_link(&device->dev->dev.kobj, "thermal_cooling"); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index fc4b68a..910bf75 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -43,6 +43,9 @@ module_param_named(modeset, i915_modeset, int, 0400); unsigned int i915_fbpercrtc = 0; module_param_named(fbpercrtc, i915_fbpercrtc, int, 0400); +unsigned int i915_powersave = 0; +module_param_named(powersave, i915_powersave, int, 0400); + static struct drm_driver driver; static struct pci_device_id pciidlist[] = { diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index b05b44d..8ca78e5 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -437,6 +437,11 @@ typedef struct drm_i915_private { struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT]; } mm; struct sdvo_device_mapping sdvo_mappings[2]; + + /* Reclocking support */ + struct work_struct idle_work; + struct timer_list idle_timer; + bool busy; } drm_i915_private_t; /** driver private structure attached to each drm_gem_object */ @@ -566,6 +571,7 @@ enum intel_chip_family { extern struct drm_ioctl_desc i915_ioctls[]; extern int i915_max_ioctl; extern unsigned int i915_fbpercrtc; +extern unsigned int i915_powersave; extern int i915_master_create(struct drm_device *dev, struct drm_master *master); extern void i915_master_destroy(struct drm_device *dev, struct drm_master *master); @@ -893,6 +899,9 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); /* dsparb controlled by hw only */ #define DSPARB_HWCONTROL(dev) (IS_G4X(dev) || IS_IGDNG(dev)) +#define HAS_FW_BLC(dev) (IS_I9XX(dev) || IS_G4X(dev) || IS_IGDNG(dev)) +#define HAS_PIPE_CXSR(dev) (IS_G4X(dev) || IS_IGDNG(dev)) + #define PRIMARY_RINGBUFFER_SIZE (128*1024) #endif diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 5bf4203..ce2d9b8 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -29,6 +29,7 @@ #include "drm.h" #include "i915_drm.h" #include "i915_drv.h" +#include "intel_drv.h" #include #include @@ -980,6 +981,7 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, { struct drm_i915_gem_set_domain *args = data; struct drm_gem_object *obj; + struct drm_i915_gem_object *obj_priv; uint32_t read_domains = args->read_domains; uint32_t write_domain = args->write_domain; int ret; @@ -1003,8 +1005,12 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) return -EBADF; + obj_priv = obj->driver_private; mutex_lock(&dev->struct_mutex); + + intel_mark_busy(dev, obj); + #if WATCH_BUF DRM_INFO("set_domain_ioctl %p(%zd), %08x %08x\n", obj, obj->size, read_domains, write_domain); @@ -2761,6 +2767,8 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj) BUG_ON(obj->pending_read_domains & I915_GEM_DOMAIN_CPU); BUG_ON(obj->pending_write_domain == I915_GEM_DOMAIN_CPU); + intel_mark_busy(dev, obj); + #if WATCH_BUF DRM_INFO("%s: object %p read %08x -> %08x write %08x -> %08x\n", __func__, obj, diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 897a116..0c4b8f9 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -55,7 +55,7 @@ /* PCI config space */ #define HPLLCC 0xc0 /* 855 only */ -#define GC_CLOCK_CONTROL_MASK (3 << 0) +#define GC_CLOCK_CONTROL_MASK (0xf << 0) #define GC_CLOCK_133_200 (0 << 0) #define GC_CLOCK_100_200 (1 << 0) #define GC_CLOCK_100_133 (2 << 0) @@ -1569,6 +1569,7 @@ #define PIPECONF_PROGRESSIVE (0 << 21) #define PIPECONF_INTERLACE_W_FIELD_INDICATION (6 << 21) #define PIPECONF_INTERLACE_FIELD_0_ONLY (7 << 21) +#define PIPECONF_CXSR_DOWNCLOCK (1<<16) #define PIPEASTAT 0x70024 #define PIPE_FIFO_UNDERRUN_STATUS (1UL<<31) #define PIPE_CRC_ERROR_ENABLE (1UL<<29) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a58bfad..81f7687 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -36,6 +36,7 @@ bool intel_pipe_has_type (struct drm_crtc *crtc, int type); static void intel_update_watermarks(struct drm_device *dev); +static void intel_increase_displayclock(struct drm_crtc *crtc); typedef struct { /* given values */ @@ -65,6 +66,8 @@ struct intel_limit { intel_p2_t p2; bool (* find_pll)(const intel_limit_t *, struct drm_crtc *, int, int, intel_clock_t *); + bool (* find_reduced_pll)(const intel_limit_t *, struct drm_crtc *, + int, int, intel_clock_t *); }; #define I8XX_DOT_MIN 25000 @@ -190,7 +193,7 @@ struct intel_limit { #define G4X_P2_SINGLE_CHANNEL_LVDS_LIMIT 0 /*The parameter is for DUAL_CHANNEL_LVDS on G4x platform*/ -#define G4X_DOT_DUAL_CHANNEL_LVDS_MIN 80000 +#define G4X_DOT_DUAL_CHANNEL_LVDS_MIN 10000 #define G4X_DOT_DUAL_CHANNEL_LVDS_MAX 224000 #define G4X_N_DUAL_CHANNEL_LVDS_MIN 1 #define G4X_N_DUAL_CHANNEL_LVDS_MAX 3 @@ -259,6 +262,9 @@ static bool intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, int target, int refclk, intel_clock_t *best_clock); static bool +intel_find_best_reduced_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, + int target, int refclk, intel_clock_t *best_clock); +static bool intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, int target, int refclk, intel_clock_t *best_clock); static bool @@ -281,6 +287,7 @@ static const intel_limit_t intel_limits_i8xx_dvo = { .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT, .p2_slow = I8XX_P2_SLOW, .p2_fast = I8XX_P2_FAST }, .find_pll = intel_find_best_PLL, + .find_reduced_pll = intel_find_best_reduced_PLL, }; static const intel_limit_t intel_limits_i8xx_lvds = { @@ -295,6 +302,7 @@ static const intel_limit_t intel_limits_i8xx_lvds = { .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT, .p2_slow = I8XX_P2_LVDS_SLOW, .p2_fast = I8XX_P2_LVDS_FAST }, .find_pll = intel_find_best_PLL, + .find_reduced_pll = intel_find_best_reduced_PLL, }; static const intel_limit_t intel_limits_i9xx_sdvo = { @@ -309,6 +317,7 @@ static const intel_limit_t intel_limits_i9xx_sdvo = { .p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT, .p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST }, .find_pll = intel_find_best_PLL, + .find_reduced_pll = intel_find_best_reduced_PLL, }; static const intel_limit_t intel_limits_i9xx_lvds = { @@ -326,6 +335,7 @@ static const intel_limit_t intel_limits_i9xx_lvds = { .p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT, .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_FAST }, .find_pll = intel_find_best_PLL, + .find_reduced_pll = intel_find_best_reduced_PLL, }; /* below parameter and function is for G4X Chipset Family*/ @@ -343,6 +353,7 @@ static const intel_limit_t intel_limits_g4x_sdvo = { .p2_fast = G4X_P2_SDVO_FAST }, .find_pll = intel_g4x_find_best_PLL, + .find_reduced_pll = intel_g4x_find_best_PLL, }; static const intel_limit_t intel_limits_g4x_hdmi = { @@ -359,6 +370,7 @@ static const intel_limit_t intel_limits_g4x_hdmi = { .p2_fast = G4X_P2_HDMI_DAC_FAST }, .find_pll = intel_g4x_find_best_PLL, + .find_reduced_pll = intel_g4x_find_best_PLL, }; static const intel_limit_t intel_limits_g4x_single_channel_lvds = { @@ -383,6 +395,7 @@ static const intel_limit_t intel_limits_g4x_single_channel_lvds = { .p2_fast = G4X_P2_SINGLE_CHANNEL_LVDS_FAST }, .find_pll = intel_g4x_find_best_PLL, + .find_reduced_pll = intel_g4x_find_best_PLL, }; static const intel_limit_t intel_limits_g4x_dual_channel_lvds = { @@ -407,6 +420,7 @@ static const intel_limit_t intel_limits_g4x_dual_channel_lvds = { .p2_fast = G4X_P2_DUAL_CHANNEL_LVDS_FAST }, .find_pll = intel_g4x_find_best_PLL, + .find_reduced_pll = intel_g4x_find_best_PLL, }; static const intel_limit_t intel_limits_g4x_display_port = { @@ -444,6 +458,7 @@ static const intel_limit_t intel_limits_igd_sdvo = { .p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT, .p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST }, .find_pll = intel_find_best_PLL, + .find_reduced_pll = intel_find_best_reduced_PLL, }; static const intel_limit_t intel_limits_igd_lvds = { @@ -459,6 +474,7 @@ static const intel_limit_t intel_limits_igd_lvds = { .p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT, .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_SLOW }, .find_pll = intel_find_best_PLL, + .find_reduced_pll = intel_find_best_reduced_PLL, }; static const intel_limit_t intel_limits_igdng_sdvo = { @@ -666,15 +682,16 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, memset (best_clock, 0, sizeof (*best_clock)); - for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) { - for (clock.m2 = limit->m2.min; clock.m2 <= limit->m2.max; clock.m2++) { - /* m1 is always 0 in IGD */ - if (clock.m2 >= clock.m1 && !IS_IGD(dev)) - break; - for (clock.n = limit->n.min; clock.n <= limit->n.max; - clock.n++) { - for (clock.p1 = limit->p1.min; - clock.p1 <= limit->p1.max; clock.p1++) { + for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) { + for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; + clock.m1++) { + for (clock.m2 = limit->m2.min; + clock.m2 <= limit->m2.max; clock.m2++) { + /* m1 is always 0 in IGD */ + if (clock.m2 >= clock.m1 && !IS_IGD(dev)) + break; + for (clock.n = limit->n.min; + clock.n <= limit->n.max; clock.n++) { int this_err; intel_clock(dev, refclk, &clock); @@ -695,6 +712,46 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, return (err != target); } + +static bool +intel_find_best_reduced_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, + int target, int refclk, intel_clock_t *best_clock) + +{ + struct drm_device *dev = crtc->dev; + intel_clock_t clock; + int err = target; + bool found = false; + + memcpy(&clock, best_clock, sizeof(intel_clock_t)); + + for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) { + for (clock.m2 = limit->m2.min; clock.m2 <= limit->m2.max; clock.m2++) { + /* m1 is always 0 in IGD */ + if (clock.m2 >= clock.m1 && !IS_IGD(dev)) + break; + for (clock.n = limit->n.min; clock.n <= limit->n.max; + clock.n++) { + int this_err; + + intel_clock(dev, refclk, &clock); + +// if (!intel_PLL_is_valid(crtc, &clock)) +// continue; + + this_err = abs(clock.dot - target); + if (this_err < err) { + *best_clock = clock; + err = this_err; + found = true; + } + } + } + } + + return found; +} + static bool intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, int target, int refclk, intel_clock_t *best_clock) @@ -725,7 +782,7 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, max_n = limit->n.max; /* based on hardware requriment prefer smaller n to precision */ for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) { - /* based on hardware requirment prefere larger m1,m2, p1 */ + /* based on hardware requirment prefere larger m1,m2, */ for (clock.m1 = limit->m1.max; clock.m1 >= limit->m1.min; clock.m1--) { for (clock.m2 = limit->m2.max; @@ -735,8 +792,8 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, int this_err; intel_clock(dev, refclk, &clock); - if (!intel_PLL_is_valid(crtc, &clock)) - continue; +// if (!intel_PLL_is_valid(crtc, &clock)) +// continue; this_err = abs(clock.dot - target) ; if (this_err < err_most) { *best_clock = clock; @@ -778,15 +835,14 @@ intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, memset(best_clock, 0, sizeof(*best_clock)); max_n = limit->n.max; - /* based on hardware requriment prefer smaller n to precision */ - for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) { - /* based on hardware requirment prefere larger m1,m2, p1 */ - for (clock.m1 = limit->m1.max; - clock.m1 >= limit->m1.min; clock.m1--) { - for (clock.m2 = limit->m2.max; - clock.m2 >= limit->m2.min; clock.m2--) { - for (clock.p1 = limit->p1.max; - clock.p1 >= limit->p1.min; clock.p1--) { + for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) { + /* based on hardware requriment prefer smaller n to precision */ + for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) { + /* based on hardware requirment prefere larger m1,m2 */ + for (clock.m1 = limit->m1.max; + clock.m1 >= limit->m1.min; clock.m1--) { + for (clock.m2 = limit->m2.max; + clock.m2 >= limit->m2.min; clock.m2--) { int this_err; intel_clock(dev, refclk, &clock); @@ -976,8 +1032,12 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, if (old_fb) { intel_fb = to_intel_framebuffer(old_fb); + obj_priv = intel_fb->obj->driver_private; + intel_increase_displayclock(crtc); i915_gem_object_unpin(intel_fb->obj); } + intel_increase_displayclock(crtc); + mutex_unlock(&dev->struct_mutex); if (!dev->primary->master) @@ -1855,6 +1915,18 @@ static int intel_get_fifo_size(struct drm_device *dev, int plane) return size; } +static void g4x_update_wm(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 fw_blc_self = I915_READ(FW_BLC_SELF); + + if (i915_powersave) + fw_blc_self |= FW_BLC_SELF_EN; + else + fw_blc_self &= ~FW_BLC_SELF_EN; + I915_WRITE(FW_BLC_SELF, fw_blc_self); +} + static void i965_update_wm(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -1906,7 +1978,8 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock, cwm = 2; /* Calc sr entries for one plane configs */ - if (sr_hdisplay && (!planea_clock || !planeb_clock)) { + if (HAS_FW_BLC(dev) && sr_hdisplay && + (!planea_clock || !planeb_clock)) { /* self-refresh has much higher latency */ const static int sr_latency_ns = 6000; @@ -1921,8 +1994,7 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock, srwm = total_size - sr_entries; if (srwm < 0) srwm = 1; - if (IS_I9XX(dev)) - I915_WRITE(FW_BLC_SELF, (srwm & 0x3f)); + I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN | (srwm & 0x3f)); } DRM_DEBUG("Setting FIFO watermarks - A: %d, B: %d, C: %d, SR %d\n", @@ -1994,9 +2066,6 @@ static void intel_update_watermarks(struct drm_device *dev) unsigned long planea_clock = 0, planeb_clock = 0, sr_clock = 0; int enabled = 0, pixel_size = 0; - if (DSPARB_HWCONTROL(dev)) - return; - /* Get the clock config from both planes */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { intel_crtc = to_intel_crtc(crtc); @@ -2029,7 +2098,9 @@ static void intel_update_watermarks(struct drm_device *dev) else if (IS_IGD(dev)) igd_disable_cxsr(dev); - if (IS_I965G(dev)) + if (IS_G4X(dev)) + g4x_update_wm(dev); + else if (IS_I965G(dev)) i965_update_wm(dev); else if (IS_I9XX(dev) || IS_MOBILE(dev)) i9xx_update_wm(dev, planea_clock, planeb_clock, sr_hdisplay, @@ -2063,9 +2134,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS; int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; int refclk, num_outputs = 0; - intel_clock_t clock; - u32 dpll = 0, fp = 0, dspcntr, pipeconf; - bool ok, is_sdvo = false, is_dvo = false; + intel_clock_t clock, reduced_clock; + u32 dpll = 0, fp = 0, fp2 = 0, dspcntr, pipeconf; + bool ok, has_reduced_clock = false, is_sdvo = false, is_dvo = false; bool is_crt = false, is_lvds = false, is_tv = false, is_dp = false; struct drm_mode_config *mode_config = &dev->mode_config; struct drm_connector *connector; @@ -2143,6 +2214,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, return -EINVAL; } + if (limit->find_reduced_pll) { + memcpy(&reduced_clock, &clock, sizeof(intel_clock_t)); + has_reduced_clock = limit->find_reduced_pll(limit, crtc, + (adjusted_mode->clock/4), + refclk, + &reduced_clock); + } + /* SDVO TV has fixed PLL values depend on its clock range, this mirrors vbios setting. */ if (is_sdvo && is_tv) { @@ -2170,10 +2249,17 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, 270000, /* lane clock */ &m_n); - if (IS_IGD(dev)) + if (IS_IGD(dev)) { fp = (1 << clock.n) << 16 | clock.m1 << 8 | clock.m2; - else + if (has_reduced_clock) + fp2 = (1 << reduced_clock.n) << 16 | + reduced_clock.m1 << 8 | reduced_clock.m2; + } else { fp = clock.n << 16 | clock.m1 << 8 | clock.m2; + if (has_reduced_clock) + fp2 = reduced_clock.n << 16 | reduced_clock.m1 << 8 | + reduced_clock.m2; + } if (!IS_IGDNG(dev)) dpll = DPLL_VGA_MODE_DIS; @@ -2202,6 +2288,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, /* also FPA1 */ if (IS_IGDNG(dev)) dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT; + if (IS_G4X(dev) && has_reduced_clock) + dpll |= (1 << (reduced_clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT; } switch (clock.p2) { case 5: @@ -2344,6 +2432,21 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, intel_dp_set_m_n(crtc, mode, adjusted_mode); I915_WRITE(fp_reg, fp); + if (has_reduced_clock && i915_powersave) { + I915_WRITE(fp_reg + 4, fp2); + intel_crtc->lowfreq_avail = true; + if (HAS_PIPE_CXSR(dev)) { + DRM_DEBUG("enabling CxSR downclocking\n"); + pipeconf |= PIPECONF_CXSR_DOWNCLOCK; + } + } else { + I915_WRITE(fp_reg + 4, fp); + intel_crtc->lowfreq_avail = false; + if (HAS_PIPE_CXSR(dev)) { + DRM_DEBUG("disabling CxSR downclocking\n"); + pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK; + } + } I915_WRITE(dpll_reg, dpll); I915_READ(dpll_reg); /* Wait for the clocks to stabilize. */ @@ -2854,6 +2957,227 @@ struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev, return mode; } +#define GPU_IDLE_TIMEOUT 500 /* ms */ + +/* When this timer fires, we've been idle for awhile */ +static void intel_gpu_idle_timer(unsigned long arg) +{ + struct drm_device *dev = (struct drm_device *)arg; + drm_i915_private_t *dev_priv = dev->dev_private; + + DRM_DEBUG("idle timer fired, downclocking\n"); + + dev_priv->busy = false; + + schedule_work(&dev_priv->idle_work); +} + +void intel_increase_renderclock(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + + if (IS_G4X(dev) || IS_I9XX(dev)) { + u16 gcfgc; + + /* Adjust render clock... */ + pci_read_config_word(dev->pdev, GCFGC, &gcfgc); + + /* Up to maximum... */ + gcfgc &= ~0xf; + if (IS_G4X(dev)) + gcfgc |= 0xd; + else if (IS_I9XX(dev)) + gcfgc |= GC_CLOCK_166_250; + + pci_write_config_word(dev->pdev, GCFGC, gcfgc); + } else if (IS_I85X(dev)) { + u16 hpllcc; + + /* Adjust render clock... */ + pci_read_config_word(dev->pdev, HPLLCC, &hpllcc); + + /* Up to maximum... */ + hpllcc &= ~GC_CLOCK_CONTROL_MASK; + hpllcc |= GC_CLOCK_166_250; + + pci_write_config_word(dev->pdev, HPLLCC, hpllcc); + } + DRM_DEBUG("increasing render clock frequency\n"); + + /* Schedule downclock */ + mod_timer(&dev_priv->idle_timer, jiffies + + msecs_to_jiffies(GPU_IDLE_TIMEOUT)); +} + +void intel_decrease_renderclock(struct drm_device *dev) +{ + if (IS_G4X(dev) || IS_I9XX(dev)) { + u16 gcfgc; + + /* Adjust render clock... */ + pci_read_config_word(dev->pdev, GCFGC, &gcfgc); + + /* Up to maximum... */ + gcfgc &= ~0xf; + gcfgc |= GC_DISPLAY_CLOCK_190_200_MHZ; + + pci_write_config_word(dev->pdev, GCFGC, gcfgc); + } else if (IS_I85X(dev)) { + u16 hpllcc; + + /* Adjust render clock... */ + pci_read_config_word(dev->pdev, HPLLCC, &hpllcc); + + /* Up to maximum... */ + hpllcc &= ~GC_CLOCK_CONTROL_MASK; + hpllcc |= GC_CLOCK_133_200; + + pci_write_config_word(dev->pdev, HPLLCC, hpllcc); + } + DRM_DEBUG("decreasing render clock frequency\n"); +} + +#define CRTC_IDLE_TIMEOUT 1000 /* ms */ + +static void intel_crtc_idle_timer(unsigned long arg) +{ + struct intel_crtc *intel_crtc = (struct intel_crtc *)arg; + struct drm_crtc *crtc = &intel_crtc->base; + drm_i915_private_t *dev_priv = crtc->dev->dev_private; + + DRM_DEBUG("idle timer fired, downclocking\n"); + + intel_crtc->busy = false; + + schedule_work(&dev_priv->idle_work); +} + +static void intel_increase_displayclock(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; + int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; + int dpll = I915_READ(dpll_reg); + + if (!HAS_PIPE_CXSR(dev) && (dpll & DISPLAY_RATE_SELECT_FPA1)) { + DRM_DEBUG("upclocking LVDS\n"); + + I915_WRITE(dpll_reg, dpll & ~DISPLAY_RATE_SELECT_FPA1); + dpll = I915_READ(dpll_reg); + intel_wait_for_vblank(dev); + dpll = I915_READ(dpll_reg); + if (dpll & DISPLAY_RATE_SELECT_FPA1) + DRM_DEBUG("failed to upclock LVDS!\n"); + } + + /* Schedule downclock */ + mod_timer(&intel_crtc->idle_timer, jiffies + + msecs_to_jiffies(CRTC_IDLE_TIMEOUT)); +} + +static void intel_decrease_displayclock(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; + int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; + int dpll = I915_READ(dpll_reg); + + /* + * Since this is called by a timer, we should never get here in + * the manual case. + */ + if (!HAS_PIPE_CXSR(dev) && intel_crtc->lowfreq_avail) { + DRM_DEBUG("downclocking LVDS\n"); + + dpll |= DISPLAY_RATE_SELECT_FPA1; + I915_WRITE(dpll_reg, dpll); + dpll = I915_READ(dpll_reg); + intel_wait_for_vblank(dev); + dpll = I915_READ(dpll_reg); + if (!(dpll & DISPLAY_RATE_SELECT_FPA1)) + DRM_DEBUG("failed to downclock LVDS!\n"); + } + +} + +/** + * intel_idle_update - adjust clocks for idleness + * @work: work struct + * + * Either the GPU or display (or both) went idle. Check the busy status + * here and adjust the CRTC and GPU clocks as necessary. + */ +static void intel_idle_update(struct work_struct *work) +{ + drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, + idle_work); + struct drm_device *dev = dev_priv->dev; + struct drm_crtc *crtc; + struct intel_crtc *intel_crtc; + + if (!i915_powersave) + return; + + mutex_lock(&dev->struct_mutex); + + /* GPU isn't processing, downclock it. */ + if (!dev_priv->busy) + intel_decrease_renderclock(dev); + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + /* Skip inactive CRTCs */ + if (!crtc->fb) + continue; + + intel_crtc = to_intel_crtc(crtc); + if (!intel_crtc->busy) + intel_decrease_displayclock(crtc); + } + + mutex_unlock(&dev->struct_mutex); +} + +/** + * intel_mark_busy - mark the GPU and possibly the display busy + * @dev: drm device + * @obj: object we're operating on + * + * Callers can use this function to indicate that the GPU is busy processing + * commands. If @obj matches one of the CRTC objects (i.e. it's a scanout + * buffer), we'll also mark the display as busy, so we know to increase its + * clock frequency. + */ +void intel_mark_busy(struct drm_device *dev, struct drm_gem_object *obj) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_crtc *crtc = NULL; + struct intel_framebuffer *intel_fb; + struct intel_crtc *intel_crtc; + + dev_priv->busy = true; + intel_increase_renderclock(dev); + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (!crtc->fb) + continue; + + intel_crtc = to_intel_crtc(crtc); + intel_fb = to_intel_framebuffer(crtc->fb); + if (intel_fb->obj == obj) { + intel_crtc->busy = true; + intel_increase_displayclock(crtc); + mod_timer(&intel_crtc->idle_timer, jiffies + + msecs_to_jiffies(CRTC_IDLE_TIMEOUT)); + } + } + + +} + static void intel_crtc_destroy(struct drm_crtc *crtc) { struct intel_crtc *intel_crtc = to_intel_crtc(crtc); @@ -2909,6 +3233,10 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) intel_crtc->mode_set.crtc = &intel_crtc->base; intel_crtc->mode_set.connectors = (struct drm_connector **)(intel_crtc + 1); intel_crtc->mode_set.num_connectors = 0; + intel_crtc->busy = false; + + setup_timer(&intel_crtc->idle_timer, intel_crtc_idle_timer, + (unsigned long)intel_crtc); if (i915_fbpercrtc) { @@ -3174,6 +3502,7 @@ static const struct drm_mode_config_funcs intel_mode_funcs = { void intel_modeset_init(struct drm_device *dev) { + struct drm_i915_private *dev_priv = dev->dev_private; int num_pipe; int i; @@ -3213,10 +3542,27 @@ void intel_modeset_init(struct drm_device *dev) } intel_setup_outputs(dev); + + INIT_WORK(&dev_priv->idle_work, intel_idle_update); + setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer, + (unsigned long)dev); } void intel_modeset_cleanup(struct drm_device *dev) { + struct drm_i915_private *dev_priv = dev->dev_private; + + /* Clean up up/down clocking */ + if (!HAS_PIPE_CXSR(dev)) { + u32 dpll_b = I915_READ(DPLL_B); + + del_timer_sync(&dev_priv->idle_timer); + dpll_b &= ~DISPLAY_RATE_SELECT_FPA1; + I915_WRITE(DPLL_B, dpll_b); + POSTING_READ(DPLL_B); + intel_wait_for_vblank(dev); + } + drm_mode_config_cleanup(dev); } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 004541c..44fe933 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -98,6 +98,9 @@ struct intel_crtc { struct intel_framebuffer *fbdev_fb; /* a mode_set for fbdev users on this crtc */ struct drm_mode_set mode_set; + bool busy; /* is scanout buffer being updated frequently? */ + struct timer_list idle_timer; + bool lowfreq_avail; }; #define to_intel_crtc(x) container_of(x, struct intel_crtc, base) @@ -116,6 +119,7 @@ extern void intel_hdmi_init(struct drm_device *dev, int sdvox_reg); extern bool intel_sdvo_init(struct drm_device *dev, int output_device); extern void intel_dvo_init(struct drm_device *dev); extern void intel_tv_init(struct drm_device *dev); +extern void intel_mark_busy(struct drm_device *dev, struct drm_gem_object *obj); extern void intel_lvds_init(struct drm_device *dev); extern void intel_dp_init(struct drm_device *dev, int dp_reg); void