diff -up linux-2.6.30.noarch/drivers/gpu/drm/i915/i915_drv.c.jx linux-2.6.30.noarch/drivers/gpu/drm/i915/i915_drv.c --- linux-2.6.30.noarch/drivers/gpu/drm/i915/i915_drv.c.jx 2009-08-03 14:00:43.000000000 -0400 +++ linux-2.6.30.noarch/drivers/gpu/drm/i915/i915_drv.c 2009-08-03 14:01:07.000000000 -0400 @@ -43,6 +43,21 @@ module_param_named(modeset, i915_modeset unsigned int i915_fbpercrtc = 0; module_param_named(fbpercrtc, i915_fbpercrtc, int, 0400); +unsigned int i915_powersave = 1; +module_param_named(powersave, i915_powersave, int, 0400); + +unsigned int i915_lvdsclock = 1; +module_param_named(lvdsclock, i915_lvdsclock, int, 0400); + +unsigned int i915_lvdsscale = 85; +module_param_named(lvdsscale, i915_lvdsscale, int, 0400); + +unsigned int i915_displayclock = 1; +module_param_named(displayclock, i915_displayclock, int, 0600); + +unsigned int i915_renderclock = 0; +module_param_named(renderclock, i915_renderclock, int, 0600); + static struct drm_driver driver; static struct pci_device_id pciidlist[] = { diff -up linux-2.6.30.noarch/drivers/gpu/drm/i915/i915_drv.h.jx linux-2.6.30.noarch/drivers/gpu/drm/i915/i915_drv.h --- linux-2.6.30.noarch/drivers/gpu/drm/i915/i915_drv.h.jx 2009-08-03 14:00:44.000000000 -0400 +++ linux-2.6.30.noarch/drivers/gpu/drm/i915/i915_drv.h 2009-08-03 14:01:07.000000000 -0400 @@ -438,6 +438,12 @@ 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; + u16 orig_clock; } drm_i915_private_t; /** driver private structure attached to each drm_gem_object */ @@ -567,6 +573,11 @@ 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 unsigned int i915_lvdsclock; +extern unsigned int i915_lvdsscale; +extern unsigned int i915_displayclock; +extern unsigned int i915_renderclock; 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); @@ -895,6 +906,9 @@ extern int i915_wait_ring(struct drm_dev /* 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 -up linux-2.6.30.noarch/drivers/gpu/drm/i915/i915_gem.c.jx linux-2.6.30.noarch/drivers/gpu/drm/i915/i915_gem.c --- linux-2.6.30.noarch/drivers/gpu/drm/i915/i915_gem.c.jx 2009-08-03 14:00:43.000000000 -0400 +++ linux-2.6.30.noarch/drivers/gpu/drm/i915/i915_gem.c 2009-08-03 14:01:07.000000000 -0400 @@ -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_dev { 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_dev 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 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 -up linux-2.6.30.noarch/drivers/gpu/drm/i915/i915_reg.h.jx linux-2.6.30.noarch/drivers/gpu/drm/i915/i915_reg.h --- linux-2.6.30.noarch/drivers/gpu/drm/i915/i915_reg.h.jx 2009-08-03 14:00:44.000000000 -0400 +++ linux-2.6.30.noarch/drivers/gpu/drm/i915/i915_reg.h 2009-08-03 14:01:07.000000000 -0400 @@ -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) @@ -65,6 +65,25 @@ #define GC_DISPLAY_CLOCK_190_200_MHZ (0 << 4) #define GC_DISPLAY_CLOCK_333_MHZ (4 << 4) #define GC_DISPLAY_CLOCK_MASK (7 << 4) +#define GM45_GC_RENDER_CLOCK_MASK (0xf << 0) +#define GM45_GC_RENDER_CLOCK_266_MHZ (8 << 0) +#define GM45_GC_RENDER_CLOCK_320_MHZ (9 << 0) +#define GM45_GC_RENDER_CLOCK_400_MHZ (0xb << 0) +#define GM45_GC_RENDER_CLOCK_533_MHZ (0xc << 0) +#define I965_GC_RENDER_CLOCK_MASK (0xf << 0) +#define I965_GC_RENDER_CLOCK_267_MHZ (2 << 0) +#define I965_GC_RENDER_CLOCK_333_MHZ (3 << 0) +#define I965_GC_RENDER_CLOCK_444_MHZ (4 << 0) +#define I965_GC_RENDER_CLOCK_533_MHZ (5 << 0) +#define I945_GC_RENDER_CLOCK_MASK (7 << 0) +#define I945_GC_RENDER_CLOCK_166_MHZ (0 << 0) +#define I945_GC_RENDER_CLOCK_200_MHZ (1 << 0) +#define I945_GC_RENDER_CLOCK_250_MHZ (3 << 0) +#define I945_GC_RENDER_CLOCK_400_MHZ (5 << 0) +#define I915_GC_RENDER_CLOCK_MASK (7 << 0) +#define I915_GC_RENDER_CLOCK_166_MHZ (0 << 0) +#define I915_GC_RENDER_CLOCK_200_MHZ (1 << 0) +#define I915_GC_RENDER_CLOCK_333_MHZ (4 << 0) #define LBB 0xf4 /* VGA stuff */ @@ -1586,6 +1605,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 -up linux-2.6.30.noarch/drivers/gpu/drm/i915/intel_display.c.jx linux-2.6.30.noarch/drivers/gpu/drm/i915/intel_display.c --- linux-2.6.30.noarch/drivers/gpu/drm/i915/intel_display.c.jx 2009-08-03 14:00:44.000000000 -0400 +++ linux-2.6.30.noarch/drivers/gpu/drm/i915/intel_display.c 2009-08-03 14:11:56.000000000 -0400 @@ -38,6 +38,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_pllclock(struct drm_crtc *crtc, int schedule); typedef struct { /* given values */ @@ -67,6 +68,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 @@ -192,7 +195,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 @@ -261,6 +264,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 @@ -286,6 +292,7 @@ static const intel_limit_t intel_limits_ .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 = { @@ -300,6 +307,7 @@ static const intel_limit_t intel_limits_ .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 = { @@ -314,6 +322,7 @@ static const intel_limit_t intel_limits_ .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 = { @@ -331,6 +340,7 @@ static const intel_limit_t intel_limits_ .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*/ @@ -348,6 +358,7 @@ static const intel_limit_t intel_limits_ .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 = { @@ -364,6 +375,7 @@ static const intel_limit_t intel_limits_ .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 = { @@ -388,6 +400,7 @@ static const intel_limit_t intel_limits_ .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 = { @@ -412,6 +425,7 @@ static const intel_limit_t intel_limits_ .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 = { @@ -449,6 +463,7 @@ static const intel_limit_t intel_limits_ .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 = { @@ -464,6 +479,7 @@ static const intel_limit_t intel_limits_ .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 = { @@ -688,15 +704,16 @@ intel_find_best_PLL(const intel_limit_t 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); @@ -717,6 +734,46 @@ intel_find_best_PLL(const intel_limit_t 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) @@ -747,7 +804,7 @@ intel_g4x_find_best_PLL(const intel_limi 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; @@ -757,8 +814,8 @@ intel_g4x_find_best_PLL(const intel_limi 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; @@ -832,15 +889,14 @@ intel_igdng_find_best_PLL(const intel_li 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); @@ -1029,6 +1085,7 @@ intel_pipe_set_base(struct drm_crtc *crt intel_wait_for_vblank(dev); i915_gem_object_unpin(intel_fb->obj); } + intel_increase_pllclock(crtc, true); if (!dev->primary->master) return 0; @@ -2036,6 +2093,18 @@ static int intel_get_fifo_size(struct dr 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; @@ -2087,7 +2156,7 @@ static void i9xx_update_wm(struct drm_de 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; @@ -2102,8 +2171,7 @@ static void i9xx_update_wm(struct drm_de 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", @@ -2177,9 +2245,6 @@ static void intel_update_watermarks(stru 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); @@ -2212,7 +2277,9 @@ static void intel_update_watermarks(stru 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, @@ -2246,9 +2313,9 @@ static int intel_crtc_mode_set(struct dr 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; bool is_edp = false; struct drm_mode_config *mode_config = &dev->mode_config; @@ -2331,6 +2398,14 @@ static int intel_crtc_mode_set(struct dr return -EINVAL; } + if (limit->find_reduced_pll && is_lvds) { + memcpy(&reduced_clock, &clock, sizeof(intel_clock_t)); + has_reduced_clock = limit->find_reduced_pll(limit, crtc, + (adjusted_mode->clock*i915_lvdsscale/100), + refclk, + &reduced_clock); + } + /* SDVO TV has fixed PLL values depend on its clock range, this mirrors vbios setting. */ if (is_sdvo && is_tv) { @@ -2376,10 +2451,17 @@ static int intel_crtc_mode_set(struct dr link_bw, &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; @@ -2408,6 +2490,8 @@ static int intel_crtc_mode_set(struct dr /* 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: @@ -2534,9 +2618,25 @@ static int intel_crtc_mode_set(struct dr } if (is_dp) intel_dp_set_m_n(crtc, mode, adjusted_mode); + if (!is_edp) { I915_WRITE(fp_reg, fp); + if (has_reduced_clock && i915_powersave && i915_lvdsclock) { + 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. */ @@ -2748,6 +2848,8 @@ fail_locked: return ret; } +#define CRTC_IDLE_TIMEOUT 1000 /* ms */ + static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) { struct drm_device *dev = crtc->dev; @@ -2757,6 +2859,12 @@ static int intel_crtc_cursor_move(struct uint32_t temp = 0; uint32_t adder; + if (!intel_crtc->busy) + intel_increase_pllclock(crtc, true); + intel_crtc->busy = true; + mod_timer(&intel_crtc->idle_timer, jiffies + + msecs_to_jiffies(CRTC_IDLE_TIMEOUT)); + if (x < 0) { temp |= CURSOR_POS_SIGN << CURSOR_X_SHIFT; x = -x; @@ -3054,6 +3162,286 @@ struct drm_display_mode *intel_crtc_mode 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, int schedule) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + + if (!i915_renderclock) + return; + + if (IS_G4X(dev) || IS_I9XX(dev)) { + /* Up to maximum... */ + pci_write_config_word(dev->pdev, GCFGC, dev_priv->orig_clock); + } else if (IS_I85X(dev)) { + pci_write_config_word(dev->pdev, HPLLCC, dev_priv->orig_clock); + } + DRM_DEBUG("increasing render clock frequency\n"); + + if (!schedule) + return; + + /* Schedule downclock */ + mod_timer(&dev_priv->idle_timer, jiffies + + msecs_to_jiffies(GPU_IDLE_TIMEOUT)); +} + +void intel_decrease_renderclock(struct drm_device *dev) +{ + if (!i915_renderclock) + return; + + if (IS_G4X(dev)) { + u16 gcfgc; + + /* Adjust render clock... */ + pci_read_config_word(dev->pdev, GCFGC, &gcfgc); + + /* Down to minimum... */ + gcfgc &= ~GM45_GC_RENDER_CLOCK_MASK; + gcfgc |= GM45_GC_RENDER_CLOCK_266_MHZ; + + pci_write_config_word(dev->pdev, GCFGC, gcfgc); + } else if (IS_I965G(dev)) { + u16 gcfgc; + + /* Adjust render clock... */ + pci_read_config_word(dev->pdev, GCFGC, &gcfgc); + + /* Down to minimum... */ + gcfgc &= ~I965_GC_RENDER_CLOCK_MASK; + gcfgc |= I965_GC_RENDER_CLOCK_267_MHZ; + + pci_write_config_word(dev->pdev, GCFGC, gcfgc); + } else if (IS_I945G(dev) || IS_I945GM(dev)) { + u16 gcfgc; + + /* Adjust render clock... */ + pci_read_config_word(dev->pdev, GCFGC, &gcfgc); + + /* Down to minimum... */ + gcfgc &= ~I945_GC_RENDER_CLOCK_MASK; + gcfgc |= I945_GC_RENDER_CLOCK_166_MHZ; + + pci_write_config_word(dev->pdev, GCFGC, gcfgc); + } else if (IS_I915G(dev) || IS_I915GM(dev)) { + u16 gcfgc; + + /* Adjust render clock... */ + pci_read_config_word(dev->pdev, GCFGC, &gcfgc); + + /* Down to minimum... */ + gcfgc &= ~I915_GC_RENDER_CLOCK_MASK; + gcfgc |= I915_GC_RENDER_CLOCK_166_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"); +} + +/* Note that no increase function is needed for this - increase_renderclock() + * will also rewrite these bits + */ +void intel_decrease_displayclock(struct drm_device *dev) +{ + if (!i915_displayclock) + return; + + if (IS_I945G(dev) || IS_I945GM(dev) || IS_I915G(dev) || + IS_I915GM(dev)) { + u16 gcfgc; + + /* Adjust render clock... */ + pci_read_config_word(dev->pdev, GCFGC, &gcfgc); + + /* Down to minimum... */ + gcfgc &= ~0xf0; + gcfgc |= 0x80; + + pci_write_config_word(dev->pdev, GCFGC, gcfgc); + } +} + +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_pllclock(struct drm_crtc *crtc, int schedule) +{ + 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 (!i915_lvdsclock) + return; + + if (!HAS_PIPE_CXSR(dev) && (dpll & DISPLAY_RATE_SELECT_FPA1)) { + DRM_DEBUG("upclocking LVDS\n"); + + I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) | (0xabcd << 16)); + 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"); + I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) & 0x3); + } + + if (!schedule) + return; + + /* Schedule downclock */ + mod_timer(&intel_crtc->idle_timer, jiffies + + msecs_to_jiffies(CRTC_IDLE_TIMEOUT)); +} + +static void intel_decrease_pllclock(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 (!i915_lvdsclock) + return; + + /* + * 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"); + I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) | (0xabcd << 16)); + + 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"); + I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) & 0x3); + } + +} + +/** + * 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); + intel_decrease_displayclock(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_pllclock(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, true); + + 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) { + if (!intel_crtc->busy) + intel_increase_pllclock(crtc, true); + intel_crtc->busy = true; + 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); @@ -3110,6 +3498,10 @@ static void intel_crtc_init(struct drm_d 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) { @@ -3399,6 +3791,7 @@ static const struct drm_mode_config_func void intel_modeset_init(struct drm_device *dev) { + struct drm_i915_private *dev_priv = dev->dev_private; int num_pipe; int i; @@ -3433,15 +3826,38 @@ void intel_modeset_init(struct drm_devic DRM_DEBUG("%d display pipe%s available.\n", num_pipe, num_pipe > 1 ? "s" : ""); + if (IS_I85X(dev)) + pci_read_config_word(dev->pdev, HPLLCC, &dev_priv->orig_clock); + else if (IS_I9XX(dev) || IS_G4X(dev)) + pci_read_config_word(dev->pdev, GCFGC, &dev_priv->orig_clock); + for (i = 0; i < num_pipe; i++) { intel_crtc_init(dev, i); } 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); + } + + intel_increase_renderclock(dev, false); drm_mode_config_cleanup(dev); } diff -up linux-2.6.30.noarch/drivers/gpu/drm/i915/intel_drv.h.jx linux-2.6.30.noarch/drivers/gpu/drm/i915/intel_drv.h --- linux-2.6.30.noarch/drivers/gpu/drm/i915/intel_drv.h.jx 2009-08-03 14:00:44.000000000 -0400 +++ linux-2.6.30.noarch/drivers/gpu/drm/i915/intel_drv.h 2009-08-03 14:01:07.000000000 -0400 @@ -99,6 +99,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) @@ -117,6 +120,7 @@ extern void intel_hdmi_init(struct drm_d 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