/* * Copyright © 2006-2007 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Authors: * Eric Anholt */ #include #include #include #include #include "drmP.h" #include "intel_drv.h" #include "i915_drm.h" #include "i915_drv.h" #include "intel_dp.h" #include "drm_crtc_helper.h" #define HAS_eDP (intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) 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, bool schedule); typedef struct { /* given values */ int n; int m1, m2; int p1, p2; /* derived values */ int dot; int vco; int m; int p; } intel_clock_t; typedef struct { int min, max; } intel_range_t; typedef struct { int dot_limit; int p2_slow, p2_fast; } intel_p2_t; #define INTEL_P2_NUM 2 typedef struct intel_limit intel_limit_t; struct intel_limit { intel_range_t dot, vco, n, m, m1, m2, p, p1; 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 #define I8XX_DOT_MAX 350000 #define I8XX_VCO_MIN 930000 #define I8XX_VCO_MAX 1400000 #define I8XX_N_MIN 3 #define I8XX_N_MAX 16 #define I8XX_M_MIN 96 #define I8XX_M_MAX 140 #define I8XX_M1_MIN 18 #define I8XX_M1_MAX 26 #define I8XX_M2_MIN 6 #define I8XX_M2_MAX 16 #define I8XX_P_MIN 4 #define I8XX_P_MAX 128 #define I8XX_P1_MIN 2 #define I8XX_P1_MAX 33 #define I8XX_P1_LVDS_MIN 1 #define I8XX_P1_LVDS_MAX 6 #define I8XX_P2_SLOW 4 #define I8XX_P2_FAST 2 #define I8XX_P2_LVDS_SLOW 14 #define I8XX_P2_LVDS_FAST 7 #define I8XX_P2_SLOW_LIMIT 165000 #define I9XX_DOT_MIN 20000 #define I9XX_DOT_MAX 400000 #define I9XX_VCO_MIN 1400000 #define I9XX_VCO_MAX 2800000 #define IGD_VCO_MIN 1700000 #define IGD_VCO_MAX 3500000 #define I9XX_N_MIN 1 #define I9XX_N_MAX 6 /* IGD's Ncounter is a ring counter */ #define IGD_N_MIN 3 #define IGD_N_MAX 6 #define I9XX_M_MIN 70 #define I9XX_M_MAX 120 #define IGD_M_MIN 2 #define IGD_M_MAX 256 #define I9XX_M1_MIN 10 #define I9XX_M1_MAX 22 #define I9XX_M2_MIN 5 #define I9XX_M2_MAX 9 /* IGD M1 is reserved, and must be 0 */ #define IGD_M1_MIN 0 #define IGD_M1_MAX 0 #define IGD_M2_MIN 0 #define IGD_M2_MAX 254 #define I9XX_P_SDVO_DAC_MIN 5 #define I9XX_P_SDVO_DAC_MAX 80 #define I9XX_P_LVDS_MIN 7 #define I9XX_P_LVDS_MAX 98 #define IGD_P_LVDS_MIN 7 #define IGD_P_LVDS_MAX 112 #define I9XX_P1_MIN 1 #define I9XX_P1_MAX 8 #define I9XX_P2_SDVO_DAC_SLOW 10 #define I9XX_P2_SDVO_DAC_FAST 5 #define I9XX_P2_SDVO_DAC_SLOW_LIMIT 200000 #define I9XX_P2_LVDS_SLOW 14 #define I9XX_P2_LVDS_FAST 7 #define I9XX_P2_LVDS_SLOW_LIMIT 112000 /*The parameter is for SDVO on G4x platform*/ #define G4X_DOT_SDVO_MIN 25000 #define G4X_DOT_SDVO_MAX 270000 #define G4X_VCO_MIN 1750000 #define G4X_VCO_MAX 3500000 #define G4X_N_SDVO_MIN 1 #define G4X_N_SDVO_MAX 4 #define G4X_M_SDVO_MIN 104 #define G4X_M_SDVO_MAX 138 #define G4X_M1_SDVO_MIN 17 #define G4X_M1_SDVO_MAX 23 #define G4X_M2_SDVO_MIN 5 #define G4X_M2_SDVO_MAX 11 #define G4X_P_SDVO_MIN 10 #define G4X_P_SDVO_MAX 30 #define G4X_P1_SDVO_MIN 1 #define G4X_P1_SDVO_MAX 3 #define G4X_P2_SDVO_SLOW 10 #define G4X_P2_SDVO_FAST 10 #define G4X_P2_SDVO_LIMIT 270000 /*The parameter is for HDMI_DAC on G4x platform*/ #define G4X_DOT_HDMI_DAC_MIN 22000 #define G4X_DOT_HDMI_DAC_MAX 400000 #define G4X_N_HDMI_DAC_MIN 1 #define G4X_N_HDMI_DAC_MAX 4 #define G4X_M_HDMI_DAC_MIN 104 #define G4X_M_HDMI_DAC_MAX 138 #define G4X_M1_HDMI_DAC_MIN 16 #define G4X_M1_HDMI_DAC_MAX 23 #define G4X_M2_HDMI_DAC_MIN 5 #define G4X_M2_HDMI_DAC_MAX 11 #define G4X_P_HDMI_DAC_MIN 5 #define G4X_P_HDMI_DAC_MAX 80 #define G4X_P1_HDMI_DAC_MIN 1 #define G4X_P1_HDMI_DAC_MAX 8 #define G4X_P2_HDMI_DAC_SLOW 10 #define G4X_P2_HDMI_DAC_FAST 5 #define G4X_P2_HDMI_DAC_LIMIT 165000 /*The parameter is for SINGLE_CHANNEL_LVDS on G4x platform*/ #define G4X_DOT_SINGLE_CHANNEL_LVDS_MIN 20000 #define G4X_DOT_SINGLE_CHANNEL_LVDS_MAX 115000 #define G4X_N_SINGLE_CHANNEL_LVDS_MIN 1 #define G4X_N_SINGLE_CHANNEL_LVDS_MAX 3 #define G4X_M_SINGLE_CHANNEL_LVDS_MIN 104 #define G4X_M_SINGLE_CHANNEL_LVDS_MAX 138 #define G4X_M1_SINGLE_CHANNEL_LVDS_MIN 17 #define G4X_M1_SINGLE_CHANNEL_LVDS_MAX 23 #define G4X_M2_SINGLE_CHANNEL_LVDS_MIN 5 #define G4X_M2_SINGLE_CHANNEL_LVDS_MAX 11 #define G4X_P_SINGLE_CHANNEL_LVDS_MIN 28 #define G4X_P_SINGLE_CHANNEL_LVDS_MAX 112 #define G4X_P1_SINGLE_CHANNEL_LVDS_MIN 2 #define G4X_P1_SINGLE_CHANNEL_LVDS_MAX 8 #define G4X_P2_SINGLE_CHANNEL_LVDS_SLOW 14 #define G4X_P2_SINGLE_CHANNEL_LVDS_FAST 14 #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_MAX 224000 #define G4X_N_DUAL_CHANNEL_LVDS_MIN 1 #define G4X_N_DUAL_CHANNEL_LVDS_MAX 3 #define G4X_M_DUAL_CHANNEL_LVDS_MIN 104 #define G4X_M_DUAL_CHANNEL_LVDS_MAX 138 #define G4X_M1_DUAL_CHANNEL_LVDS_MIN 17 #define G4X_M1_DUAL_CHANNEL_LVDS_MAX 23 #define G4X_M2_DUAL_CHANNEL_LVDS_MIN 5 #define G4X_M2_DUAL_CHANNEL_LVDS_MAX 11 #define G4X_P_DUAL_CHANNEL_LVDS_MIN 14 #define G4X_P_DUAL_CHANNEL_LVDS_MAX 42 #define G4X_P1_DUAL_CHANNEL_LVDS_MIN 2 #define G4X_P1_DUAL_CHANNEL_LVDS_MAX 6 #define G4X_P2_DUAL_CHANNEL_LVDS_SLOW 7 #define G4X_P2_DUAL_CHANNEL_LVDS_FAST 7 #define G4X_P2_DUAL_CHANNEL_LVDS_LIMIT 0 /*The parameter is for DISPLAY PORT on G4x platform*/ #define G4X_DOT_DISPLAY_PORT_MIN 161670 #define G4X_DOT_DISPLAY_PORT_MAX 227000 #define G4X_N_DISPLAY_PORT_MIN 1 #define G4X_N_DISPLAY_PORT_MAX 2 #define G4X_M_DISPLAY_PORT_MIN 97 #define G4X_M_DISPLAY_PORT_MAX 108 #define G4X_M1_DISPLAY_PORT_MIN 0x10 #define G4X_M1_DISPLAY_PORT_MAX 0x12 #define G4X_M2_DISPLAY_PORT_MIN 0x05 #define G4X_M2_DISPLAY_PORT_MAX 0x06 #define G4X_P_DISPLAY_PORT_MIN 10 #define G4X_P_DISPLAY_PORT_MAX 20 #define G4X_P1_DISPLAY_PORT_MIN 1 #define G4X_P1_DISPLAY_PORT_MAX 2 #define G4X_P2_DISPLAY_PORT_SLOW 10 #define G4X_P2_DISPLAY_PORT_FAST 10 #define G4X_P2_DISPLAY_PORT_LIMIT 0 /* IGDNG */ /* as we calculate clock using (register_value + 2) for N/M1/M2, so here the range value for them is (actual_value-2). */ #define IGDNG_DOT_MIN 25000 #define IGDNG_DOT_MAX 350000 #define IGDNG_VCO_MIN 1760000 #define IGDNG_VCO_MAX 3510000 #define IGDNG_N_MIN 1 #define IGDNG_N_MAX 5 #define IGDNG_M_MIN 79 #define IGDNG_M_MAX 118 #define IGDNG_M1_MIN 12 #define IGDNG_M1_MAX 23 #define IGDNG_M2_MIN 5 #define IGDNG_M2_MAX 9 #define IGDNG_P_SDVO_DAC_MIN 5 #define IGDNG_P_SDVO_DAC_MAX 80 #define IGDNG_P_LVDS_MIN 28 #define IGDNG_P_LVDS_MAX 112 #define IGDNG_P1_MIN 1 #define IGDNG_P1_MAX 8 #define IGDNG_P2_SDVO_DAC_SLOW 10 #define IGDNG_P2_SDVO_DAC_FAST 5 #define IGDNG_P2_LVDS_SLOW 14 /* single channel */ #define IGDNG_P2_LVDS_FAST 7 /* double channel */ #define IGDNG_P2_DOT_LIMIT 225000 /* 225Mhz */ 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 intel_igdng_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_pll_g4x_dp(const intel_limit_t *, struct drm_crtc *crtc, int target, int refclk, intel_clock_t *best_clock); static bool intel_find_pll_igdng_dp(const intel_limit_t *, struct drm_crtc *crtc, int target, int refclk, intel_clock_t *best_clock); static const intel_limit_t intel_limits_i8xx_dvo = { .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX }, .vco = { .min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX }, .n = { .min = I8XX_N_MIN, .max = I8XX_N_MAX }, .m = { .min = I8XX_M_MIN, .max = I8XX_M_MAX }, .m1 = { .min = I8XX_M1_MIN, .max = I8XX_M1_MAX }, .m2 = { .min = I8XX_M2_MIN, .max = I8XX_M2_MAX }, .p = { .min = I8XX_P_MIN, .max = I8XX_P_MAX }, .p1 = { .min = I8XX_P1_MIN, .max = I8XX_P1_MAX }, .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 = { .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX }, .vco = { .min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX }, .n = { .min = I8XX_N_MIN, .max = I8XX_N_MAX }, .m = { .min = I8XX_M_MIN, .max = I8XX_M_MAX }, .m1 = { .min = I8XX_M1_MIN, .max = I8XX_M1_MAX }, .m2 = { .min = I8XX_M2_MIN, .max = I8XX_M2_MAX }, .p = { .min = I8XX_P_MIN, .max = I8XX_P_MAX }, .p1 = { .min = I8XX_P1_LVDS_MIN, .max = I8XX_P1_LVDS_MAX }, .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 = { .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX }, .vco = { .min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX }, .n = { .min = I9XX_N_MIN, .max = I9XX_N_MAX }, .m = { .min = I9XX_M_MIN, .max = I9XX_M_MAX }, .m1 = { .min = I9XX_M1_MIN, .max = I9XX_M1_MAX }, .m2 = { .min = I9XX_M2_MIN, .max = I9XX_M2_MAX }, .p = { .min = I9XX_P_SDVO_DAC_MIN, .max = I9XX_P_SDVO_DAC_MAX }, .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX }, .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 = { .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX }, .vco = { .min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX }, .n = { .min = I9XX_N_MIN, .max = I9XX_N_MAX }, .m = { .min = I9XX_M_MIN, .max = I9XX_M_MAX }, .m1 = { .min = I9XX_M1_MIN, .max = I9XX_M1_MAX }, .m2 = { .min = I9XX_M2_MIN, .max = I9XX_M2_MAX }, .p = { .min = I9XX_P_LVDS_MIN, .max = I9XX_P_LVDS_MAX }, .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX }, /* The single-channel range is 25-112Mhz, and dual-channel * is 80-224Mhz. Prefer single channel as much as possible. */ .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*/ static const intel_limit_t intel_limits_g4x_sdvo = { .dot = { .min = G4X_DOT_SDVO_MIN, .max = G4X_DOT_SDVO_MAX }, .vco = { .min = G4X_VCO_MIN, .max = G4X_VCO_MAX}, .n = { .min = G4X_N_SDVO_MIN, .max = G4X_N_SDVO_MAX }, .m = { .min = G4X_M_SDVO_MIN, .max = G4X_M_SDVO_MAX }, .m1 = { .min = G4X_M1_SDVO_MIN, .max = G4X_M1_SDVO_MAX }, .m2 = { .min = G4X_M2_SDVO_MIN, .max = G4X_M2_SDVO_MAX }, .p = { .min = G4X_P_SDVO_MIN, .max = G4X_P_SDVO_MAX }, .p1 = { .min = G4X_P1_SDVO_MIN, .max = G4X_P1_SDVO_MAX}, .p2 = { .dot_limit = G4X_P2_SDVO_LIMIT, .p2_slow = G4X_P2_SDVO_SLOW, .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 = { .dot = { .min = G4X_DOT_HDMI_DAC_MIN, .max = G4X_DOT_HDMI_DAC_MAX }, .vco = { .min = G4X_VCO_MIN, .max = G4X_VCO_MAX}, .n = { .min = G4X_N_HDMI_DAC_MIN, .max = G4X_N_HDMI_DAC_MAX }, .m = { .min = G4X_M_HDMI_DAC_MIN, .max = G4X_M_HDMI_DAC_MAX }, .m1 = { .min = G4X_M1_HDMI_DAC_MIN, .max = G4X_M1_HDMI_DAC_MAX }, .m2 = { .min = G4X_M2_HDMI_DAC_MIN, .max = G4X_M2_HDMI_DAC_MAX }, .p = { .min = G4X_P_HDMI_DAC_MIN, .max = G4X_P_HDMI_DAC_MAX }, .p1 = { .min = G4X_P1_HDMI_DAC_MIN, .max = G4X_P1_HDMI_DAC_MAX}, .p2 = { .dot_limit = G4X_P2_HDMI_DAC_LIMIT, .p2_slow = G4X_P2_HDMI_DAC_SLOW, .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 = { .dot = { .min = G4X_DOT_SINGLE_CHANNEL_LVDS_MIN, .max = G4X_DOT_SINGLE_CHANNEL_LVDS_MAX }, .vco = { .min = G4X_VCO_MIN, .max = G4X_VCO_MAX }, .n = { .min = G4X_N_SINGLE_CHANNEL_LVDS_MIN, .max = G4X_N_SINGLE_CHANNEL_LVDS_MAX }, .m = { .min = G4X_M_SINGLE_CHANNEL_LVDS_MIN, .max = G4X_M_SINGLE_CHANNEL_LVDS_MAX }, .m1 = { .min = G4X_M1_SINGLE_CHANNEL_LVDS_MIN, .max = G4X_M1_SINGLE_CHANNEL_LVDS_MAX }, .m2 = { .min = G4X_M2_SINGLE_CHANNEL_LVDS_MIN, .max = G4X_M2_SINGLE_CHANNEL_LVDS_MAX }, .p = { .min = G4X_P_SINGLE_CHANNEL_LVDS_MIN, .max = G4X_P_SINGLE_CHANNEL_LVDS_MAX }, .p1 = { .min = G4X_P1_SINGLE_CHANNEL_LVDS_MIN, .max = G4X_P1_SINGLE_CHANNEL_LVDS_MAX }, .p2 = { .dot_limit = G4X_P2_SINGLE_CHANNEL_LVDS_LIMIT, .p2_slow = G4X_P2_SINGLE_CHANNEL_LVDS_SLOW, .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 = { .dot = { .min = G4X_DOT_DUAL_CHANNEL_LVDS_MIN, .max = G4X_DOT_DUAL_CHANNEL_LVDS_MAX }, .vco = { .min = G4X_VCO_MIN, .max = G4X_VCO_MAX }, .n = { .min = G4X_N_DUAL_CHANNEL_LVDS_MIN, .max = G4X_N_DUAL_CHANNEL_LVDS_MAX }, .m = { .min = G4X_M_DUAL_CHANNEL_LVDS_MIN, .max = G4X_M_DUAL_CHANNEL_LVDS_MAX }, .m1 = { .min = G4X_M1_DUAL_CHANNEL_LVDS_MIN, .max = G4X_M1_DUAL_CHANNEL_LVDS_MAX }, .m2 = { .min = G4X_M2_DUAL_CHANNEL_LVDS_MIN, .max = G4X_M2_DUAL_CHANNEL_LVDS_MAX }, .p = { .min = G4X_P_DUAL_CHANNEL_LVDS_MIN, .max = G4X_P_DUAL_CHANNEL_LVDS_MAX }, .p1 = { .min = G4X_P1_DUAL_CHANNEL_LVDS_MIN, .max = G4X_P1_DUAL_CHANNEL_LVDS_MAX }, .p2 = { .dot_limit = G4X_P2_DUAL_CHANNEL_LVDS_LIMIT, .p2_slow = G4X_P2_DUAL_CHANNEL_LVDS_SLOW, .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 = { .dot = { .min = G4X_DOT_DISPLAY_PORT_MIN, .max = G4X_DOT_DISPLAY_PORT_MAX }, .vco = { .min = G4X_VCO_MIN, .max = G4X_VCO_MAX}, .n = { .min = G4X_N_DISPLAY_PORT_MIN, .max = G4X_N_DISPLAY_PORT_MAX }, .m = { .min = G4X_M_DISPLAY_PORT_MIN, .max = G4X_M_DISPLAY_PORT_MAX }, .m1 = { .min = G4X_M1_DISPLAY_PORT_MIN, .max = G4X_M1_DISPLAY_PORT_MAX }, .m2 = { .min = G4X_M2_DISPLAY_PORT_MIN, .max = G4X_M2_DISPLAY_PORT_MAX }, .p = { .min = G4X_P_DISPLAY_PORT_MIN, .max = G4X_P_DISPLAY_PORT_MAX }, .p1 = { .min = G4X_P1_DISPLAY_PORT_MIN, .max = G4X_P1_DISPLAY_PORT_MAX}, .p2 = { .dot_limit = G4X_P2_DISPLAY_PORT_LIMIT, .p2_slow = G4X_P2_DISPLAY_PORT_SLOW, .p2_fast = G4X_P2_DISPLAY_PORT_FAST }, .find_pll = intel_find_pll_g4x_dp, }; static const intel_limit_t intel_limits_igd_sdvo = { .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX}, .vco = { .min = IGD_VCO_MIN, .max = IGD_VCO_MAX }, .n = { .min = IGD_N_MIN, .max = IGD_N_MAX }, .m = { .min = IGD_M_MIN, .max = IGD_M_MAX }, .m1 = { .min = IGD_M1_MIN, .max = IGD_M1_MAX }, .m2 = { .min = IGD_M2_MIN, .max = IGD_M2_MAX }, .p = { .min = I9XX_P_SDVO_DAC_MIN, .max = I9XX_P_SDVO_DAC_MAX }, .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX }, .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 = { .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX }, .vco = { .min = IGD_VCO_MIN, .max = IGD_VCO_MAX }, .n = { .min = IGD_N_MIN, .max = IGD_N_MAX }, .m = { .min = IGD_M_MIN, .max = IGD_M_MAX }, .m1 = { .min = IGD_M1_MIN, .max = IGD_M1_MAX }, .m2 = { .min = IGD_M2_MIN, .max = IGD_M2_MAX }, .p = { .min = IGD_P_LVDS_MIN, .max = IGD_P_LVDS_MAX }, .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX }, /* IGD only supports single-channel mode. */ .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 = { .dot = { .min = IGDNG_DOT_MIN, .max = IGDNG_DOT_MAX }, .vco = { .min = IGDNG_VCO_MIN, .max = IGDNG_VCO_MAX }, .n = { .min = IGDNG_N_MIN, .max = IGDNG_N_MAX }, .m = { .min = IGDNG_M_MIN, .max = IGDNG_M_MAX }, .m1 = { .min = IGDNG_M1_MIN, .max = IGDNG_M1_MAX }, .m2 = { .min = IGDNG_M2_MIN, .max = IGDNG_M2_MAX }, .p = { .min = IGDNG_P_SDVO_DAC_MIN, .max = IGDNG_P_SDVO_DAC_MAX }, .p1 = { .min = IGDNG_P1_MIN, .max = IGDNG_P1_MAX }, .p2 = { .dot_limit = IGDNG_P2_DOT_LIMIT, .p2_slow = IGDNG_P2_SDVO_DAC_SLOW, .p2_fast = IGDNG_P2_SDVO_DAC_FAST }, .find_pll = intel_igdng_find_best_PLL, }; static const intel_limit_t intel_limits_igdng_lvds = { .dot = { .min = IGDNG_DOT_MIN, .max = IGDNG_DOT_MAX }, .vco = { .min = IGDNG_VCO_MIN, .max = IGDNG_VCO_MAX }, .n = { .min = IGDNG_N_MIN, .max = IGDNG_N_MAX }, .m = { .min = IGDNG_M_MIN, .max = IGDNG_M_MAX }, .m1 = { .min = IGDNG_M1_MIN, .max = IGDNG_M1_MAX }, .m2 = { .min = IGDNG_M2_MIN, .max = IGDNG_M2_MAX }, .p = { .min = IGDNG_P_LVDS_MIN, .max = IGDNG_P_LVDS_MAX }, .p1 = { .min = IGDNG_P1_MIN, .max = IGDNG_P1_MAX }, .p2 = { .dot_limit = IGDNG_P2_DOT_LIMIT, .p2_slow = IGDNG_P2_LVDS_SLOW, .p2_fast = IGDNG_P2_LVDS_FAST }, .find_pll = intel_igdng_find_best_PLL, }; static const intel_limit_t *intel_igdng_limit(struct drm_crtc *crtc) { const intel_limit_t *limit; if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) limit = &intel_limits_igdng_lvds; else limit = &intel_limits_igdng_sdvo; return limit; } static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; const intel_limit_t *limit; if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP) /* LVDS with dual channel */ limit = &intel_limits_g4x_dual_channel_lvds; else /* LVDS with dual channel */ limit = &intel_limits_g4x_single_channel_lvds; } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI) || intel_pipe_has_type(crtc, INTEL_OUTPUT_ANALOG)) { limit = &intel_limits_g4x_hdmi; } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO)) { limit = &intel_limits_g4x_sdvo; } else if (intel_pipe_has_type (crtc, INTEL_OUTPUT_DISPLAYPORT)) { limit = &intel_limits_g4x_display_port; } else /* The option is for other outputs */ limit = &intel_limits_i9xx_sdvo; return limit; } static const intel_limit_t *intel_limit(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; const intel_limit_t *limit; if (IS_IGDNG(dev)) limit = intel_igdng_limit(crtc); else if (IS_G4X(dev)) { limit = intel_g4x_limit(crtc); } else if (IS_I9XX(dev) && !IS_IGD(dev)) { if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) limit = &intel_limits_i9xx_lvds; else limit = &intel_limits_i9xx_sdvo; } else if (IS_IGD(dev)) { if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) limit = &intel_limits_igd_lvds; else limit = &intel_limits_igd_sdvo; } else { if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) limit = &intel_limits_i8xx_lvds; else limit = &intel_limits_i8xx_dvo; } return limit; } /* m1 is reserved as 0 in IGD, n is a ring counter */ static void igd_clock(int refclk, intel_clock_t *clock) { clock->m = clock->m2 + 2; clock->p = clock->p1 * clock->p2; clock->vco = refclk * clock->m / clock->n; clock->dot = clock->vco / clock->p; } static void intel_clock(struct drm_device *dev, int refclk, intel_clock_t *clock) { if (IS_IGD(dev)) { igd_clock(refclk, clock); return; } clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); clock->p = clock->p1 * clock->p2; clock->vco = refclk * clock->m / (clock->n + 2); clock->dot = clock->vco / clock->p; } /** * Returns whether any output on the specified pipe is of the specified type */ bool intel_pipe_has_type (struct drm_crtc *crtc, int type) { struct drm_device *dev = crtc->dev; struct drm_mode_config *mode_config = &dev->mode_config; struct drm_connector *l_entry; list_for_each_entry(l_entry, &mode_config->connector_list, head) { if (l_entry->encoder && l_entry->encoder->crtc == crtc) { struct intel_output *intel_output = to_intel_output(l_entry); if (intel_output->type == type) return true; } } return false; } struct drm_connector * intel_pipe_get_output (struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_mode_config *mode_config = &dev->mode_config; struct drm_connector *l_entry, *ret = NULL; list_for_each_entry(l_entry, &mode_config->connector_list, head) { if (l_entry->encoder && l_entry->encoder->crtc == crtc) { ret = l_entry; break; } } return ret; } #define INTELPllInvalid(s) do { /* DRM_DEBUG(s); */ return false; } while (0) /** * Returns whether the given set of divisors are valid for a given refclk with * the given connectors. */ static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock) { const intel_limit_t *limit = intel_limit (crtc); struct drm_device *dev = crtc->dev; if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1) INTELPllInvalid ("p1 out of range\n"); if (clock->p < limit->p.min || limit->p.max < clock->p) INTELPllInvalid ("p out of range\n"); if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2) INTELPllInvalid ("m2 out of range\n"); if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1) INTELPllInvalid ("m1 out of range\n"); if (clock->m1 <= clock->m2 && !IS_IGD(dev)) INTELPllInvalid ("m1 <= m2\n"); if (clock->m < limit->m.min || limit->m.max < clock->m) INTELPllInvalid ("m out of range\n"); if (clock->n < limit->n.min || limit->n.max < clock->n) INTELPllInvalid ("n out of range\n"); if (clock->vco < limit->vco.min || limit->vco.max < clock->vco) INTELPllInvalid ("vco out of range\n"); /* XXX: We may need to be checking "Dot clock" depending on the multiplier, * connector, etc., rather than just a single range. */ if (clock->dot < limit->dot.min || limit->dot.max < clock->dot) INTELPllInvalid ("dot out of range\n"); return true; } static bool intel_find_best_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; struct drm_i915_private *dev_priv = dev->dev_private; intel_clock_t clock; int err = target; if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && (I915_READ(LVDS)) != 0) { /* * For LVDS, if the panel is on, just rely on its current * settings for dual-channel. We haven't figured out how to * reliably set up different single/dual channel state, if we * even can. */ if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP) clock.p2 = limit->p2.p2_fast; else clock.p2 = limit->p2.p2_slow; } else { if (target < limit->p2.dot_limit) clock.p2 = limit->p2.p2_slow; else clock.p2 = limit->p2.p2_fast; } memset (best_clock, 0, sizeof (*best_clock)); 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); if (!intel_PLL_is_valid(crtc, &clock)) continue; this_err = abs(clock.dot - target); if (this_err < err) { *best_clock = clock; err = this_err; } } } } } 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) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; intel_clock_t clock; int max_n; bool found; /* approximately equals target * 0.00488 */ int err_most = (target >> 8) + (target >> 10); found = false; if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP) clock.p2 = limit->p2.p2_fast; else clock.p2 = limit->p2.p2_slow; } else { if (target < limit->p2.dot_limit) clock.p2 = limit->p2.p2_slow; else clock.p2 = limit->p2.p2_fast; } 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 */ 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--) { 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_most) { *best_clock = clock; err_most = this_err; max_n = clock.n; found = true; } } } } } return found; } static bool intel_find_pll_igdng_dp(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; if (target < 200000) { clock.n = 1; clock.p1 = 2; clock.p2 = 10; clock.m1 = 12; clock.m2 = 9; } else { clock.n = 2; clock.p1 = 1; clock.p2 = 10; clock.m1 = 14; clock.m2 = 8; } intel_clock(dev, refclk, &clock); memcpy(best_clock, &clock, sizeof(intel_clock_t)); return true; } static bool intel_igdng_find_best_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; struct drm_i915_private *dev_priv = dev->dev_private; intel_clock_t clock; int max_n; bool found; int err_most = 47; found = false; /* eDP has only 2 clock choice, no n/m/p setting */ if (HAS_eDP) return true; if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) return intel_find_pll_igdng_dp(limit, crtc, target, refclk, best_clock); if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { if ((I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP) clock.p2 = limit->p2.p2_fast; else clock.p2 = limit->p2.p2_slow; } else { if (target < limit->p2.dot_limit) clock.p2 = limit->p2.p2_slow; else clock.p2 = limit->p2.p2_fast; } memset(best_clock, 0, sizeof(*best_clock)); max_n = limit->n.max; 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); if (!intel_PLL_is_valid(crtc, &clock)) continue; this_err = abs((10000 - (target*10000/clock.dot))); if (this_err < err_most) { *best_clock = clock; err_most = this_err; max_n = clock.n; found = true; /* found on first matching */ goto out; } } } } } out: return found; } /* DisplayPort has only two frequencies, 162MHz and 270MHz */ static bool intel_find_pll_g4x_dp(const intel_limit_t *limit, struct drm_crtc *crtc, int target, int refclk, intel_clock_t *best_clock) { intel_clock_t clock; if (target < 200000) { clock.p1 = 2; clock.p2 = 10; clock.n = 2; clock.m1 = 23; clock.m2 = 8; } else { clock.p1 = 1; clock.p2 = 10; clock.n = 1; clock.m1 = 14; clock.m2 = 2; } clock.m = 5 * (clock.m1 + 2) + (clock.m2 + 2); clock.p = (clock.p1 * clock.p2); clock.dot = 96000 * clock.m / (clock.n + 2) / clock.p; clock.vco = 0; memcpy(best_clock, &clock, sizeof(intel_clock_t)); return true; } void intel_wait_for_vblank(struct drm_device *dev) { /* Wait for 20ms, i.e. one cycle at 50hz. */ mdelay(20); } /* Parameters have changed, update FBC info */ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_framebuffer *fb = crtc->fb; struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); struct drm_i915_gem_object *obj_priv = intel_fb->obj->driver_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int plane, i; u32 fbc_ctl, fbc_ctl2; dev_priv->cfb_pitch = dev_priv->cfb_size / FBC_LL_SIZE; if (fb->pitch < dev_priv->cfb_pitch) dev_priv->cfb_pitch = fb->pitch; /* FBC_CTL wants 64B units */ dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1; dev_priv->cfb_fence = obj_priv->fence_reg; dev_priv->cfb_plane = intel_crtc->plane; plane = dev_priv->cfb_plane == 0 ? FBC_CTL_PLANEA : FBC_CTL_PLANEB; /* Clear old tags */ for (i = 0; i < (FBC_LL_SIZE / 32) + 1; i++) I915_WRITE(FBC_TAG + (i * 4), 0); /* Set it up... */ fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | plane; if (obj_priv->tiling_mode != I915_TILING_NONE) fbc_ctl2 |= FBC_CTL_CPU_FENCE; I915_WRITE(FBC_CONTROL2, fbc_ctl2); I915_WRITE(FBC_FENCE_OFF, crtc->y); /* enable it... */ fbc_ctl = FBC_CTL_EN | FBC_CTL_PERIODIC; fbc_ctl |= (dev_priv->cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT; fbc_ctl |= (interval & 0x2fff) << FBC_CTL_INTERVAL_SHIFT; if (obj_priv->tiling_mode != I915_TILING_NONE) fbc_ctl |= dev_priv->cfb_fence; I915_WRITE(FBC_CONTROL, fbc_ctl); DRM_DEBUG("enabled FBC, pitch %ld, yoff %d, plane %d, ", dev_priv->cfb_pitch, crtc->y, dev_priv->cfb_plane); } void i8xx_disable_fbc(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; u32 fbc_ctl; if (!I915_HAS_FBC(dev)) return; /* Disable compression */ fbc_ctl = I915_READ(FBC_CONTROL); fbc_ctl &= ~FBC_CTL_EN; I915_WRITE(FBC_CONTROL, fbc_ctl); /* Wait for compressing bit to clear */ while (I915_READ(FBC_STATUS) & FBC_STAT_COMPRESSING) ; /* nothing */ intel_wait_for_vblank(dev); DRM_DEBUG("disabled FBC\n"); } static bool i8xx_fbc_enabled(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; return I915_READ(FBC_CONTROL) & FBC_CTL_EN; } static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_framebuffer *fb = crtc->fb; struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); struct drm_i915_gem_object *obj_priv = intel_fb->obj->driver_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int plane = (intel_crtc->plane == 0 ? DPFC_CTL_PLANEA : DPFC_CTL_PLANEB); unsigned long stall_watermark = 200; u32 dpfc_ctl; dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1; dev_priv->cfb_fence = obj_priv->fence_reg; dev_priv->cfb_plane = intel_crtc->plane; dpfc_ctl = plane | DPFC_SR_EN | DPFC_CTL_LIMIT_1X; if (obj_priv->tiling_mode != I915_TILING_NONE) { dpfc_ctl |= DPFC_CTL_FENCE_EN | dev_priv->cfb_fence; I915_WRITE(DPFC_CHICKEN, DPFC_HT_MODIFY); } else { I915_WRITE(DPFC_CHICKEN, ~DPFC_HT_MODIFY); } I915_WRITE(DPFC_CONTROL, dpfc_ctl); I915_WRITE(DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN | (stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) | (interval << DPFC_RECOMP_TIMER_COUNT_SHIFT)); I915_WRITE(DPFC_FENCE_YOFF, crtc->y); /* enable it... */ I915_WRITE(DPFC_CONTROL, I915_READ(DPFC_CONTROL) | DPFC_CTL_EN); DRM_DEBUG("enabled fbc on plane %d\n", intel_crtc->plane); } void g4x_disable_fbc(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; u32 dpfc_ctl; /* Disable compression */ dpfc_ctl = I915_READ(DPFC_CONTROL); dpfc_ctl &= ~DPFC_CTL_EN; I915_WRITE(DPFC_CONTROL, dpfc_ctl); intel_wait_for_vblank(dev); DRM_DEBUG("disabled FBC\n"); } static bool g4x_fbc_enabled(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; return I915_READ(DPFC_CONTROL) & DPFC_CTL_EN; } /** * intel_update_fbc - enable/disable FBC as needed * @crtc: CRTC to point the compressor at * @mode: mode in use * * Set up the framebuffer compression hardware at mode set time. We * enable it if possible: * - plane A only (on pre-965) * - no pixel mulitply/line duplication * - no alpha buffer discard * - no dual wide * - framebuffer <= 2048 in width, 1536 in height * * We can't assume that any compression will take place (worst case), * so the compressed buffer has to be the same size as the uncompressed * one. It also must reside (along with the line length buffer) in * stolen memory. * * We need to enable/disable FBC on a global basis. */ static void intel_update_fbc(struct drm_crtc *crtc, struct drm_display_mode *mode) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_framebuffer *fb = crtc->fb; struct intel_framebuffer *intel_fb; struct drm_i915_gem_object *obj_priv; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int plane = intel_crtc->plane; if (!i915_powersave) return; if (!dev_priv->display.fbc_enabled || !dev_priv->display.enable_fbc || !dev_priv->display.disable_fbc) return; if (!crtc->fb) return; intel_fb = to_intel_framebuffer(fb); obj_priv = intel_fb->obj->driver_private; /* * If FBC is already on, we just have to verify that we can * keep it that way... * Need to disable if: * - changing FBC params (stride, fence, mode) * - new fb is too large to fit in compressed buffer * - going to an unsupported config (interlace, pixel multiply, etc.) */ if (intel_fb->obj->size > dev_priv->cfb_size) { DRM_DEBUG("framebuffer too large, disabling compression\n"); goto out_disable; } if ((mode->flags & DRM_MODE_FLAG_INTERLACE) || (mode->flags & DRM_MODE_FLAG_DBLSCAN)) { DRM_DEBUG("mode incompatible with compression, disabling\n"); goto out_disable; } if ((mode->hdisplay > 2048) || (mode->vdisplay > 1536)) { DRM_DEBUG("mode too large for compression, disabling\n"); goto out_disable; } if ((IS_I915GM(dev) || IS_I945GM(dev)) && plane != 0) { DRM_DEBUG("plane not 0, disabling compression\n"); goto out_disable; } if (obj_priv->tiling_mode != I915_TILING_X) { DRM_DEBUG("framebuffer not tiled, disabling compression\n"); goto out_disable; } if (dev_priv->display.fbc_enabled(crtc)) { /* We can re-enable it in this case, but need to update pitch */ if (fb->pitch > dev_priv->cfb_pitch) dev_priv->display.disable_fbc(dev); if (obj_priv->fence_reg != dev_priv->cfb_fence) dev_priv->display.disable_fbc(dev); if (plane != dev_priv->cfb_plane) dev_priv->display.disable_fbc(dev); } if (!dev_priv->display.fbc_enabled(crtc)) { /* Now try to turn it back on if possible */ dev_priv->display.enable_fbc(crtc, 500); } return; out_disable: DRM_DEBUG("unsupported config, disabling FBC\n"); /* Multiple disables should be harmless */ if (dev_priv->display.fbc_enabled(crtc)) dev_priv->display.disable_fbc(dev); } static int intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_master_private *master_priv; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_framebuffer *intel_fb; struct drm_i915_gem_object *obj_priv; struct drm_gem_object *obj; int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; unsigned long Start, Offset; int dspbase = (plane == 0 ? DSPAADDR : DSPBADDR); int dspsurf = (plane == 0 ? DSPASURF : DSPBSURF); int dspstride = (plane == 0) ? DSPASTRIDE : DSPBSTRIDE; int dsptileoff = (plane == 0 ? DSPATILEOFF : DSPBTILEOFF); int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR; u32 dspcntr, alignment; int ret; /* no fb bound */ if (!crtc->fb) { DRM_DEBUG("No FB bound\n"); return 0; } switch (plane) { case 0: case 1: break; default: DRM_ERROR("Can't update plane %d in SAREA\n", plane); return -EINVAL; } intel_fb = to_intel_framebuffer(crtc->fb); obj = intel_fb->obj; obj_priv = obj->driver_private; switch (obj_priv->tiling_mode) { case I915_TILING_NONE: alignment = 64 * 1024; break; case I915_TILING_X: /* pin() will align the object as required by fence */ alignment = 0; break; case I915_TILING_Y: /* FIXME: Is this true? */ DRM_ERROR("Y tiled not allowed for scan out buffers\n"); return -EINVAL; default: BUG(); } mutex_lock(&dev->struct_mutex); ret = i915_gem_object_pin(obj, alignment); if (ret != 0) { mutex_unlock(&dev->struct_mutex); return ret; } ret = i915_gem_object_set_to_gtt_domain(obj, 1); if (ret != 0) { i915_gem_object_unpin(obj); mutex_unlock(&dev->struct_mutex); return ret; } /* Install a fence for tiled scan-out. Pre-i965 always needs a fence, * whereas 965+ only requires a fence if using framebuffer compression. * For simplicity, we always install a fence as the cost is not that onerous. */ if (obj_priv->fence_reg == I915_FENCE_REG_NONE && obj_priv->tiling_mode != I915_TILING_NONE) { ret = i915_gem_object_get_fence_reg(obj); if (ret != 0) { i915_gem_object_unpin(obj); mutex_unlock(&dev->struct_mutex); return ret; } } dspcntr = I915_READ(dspcntr_reg); /* Mask out pixel format bits in case we change it */ dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; switch (crtc->fb->bits_per_pixel) { case 8: dspcntr |= DISPPLANE_8BPP; break; case 16: if (crtc->fb->depth == 15) dspcntr |= DISPPLANE_15_16BPP; else dspcntr |= DISPPLANE_16BPP; break; case 24: case 32: dspcntr |= DISPPLANE_32BPP_NO_ALPHA; break; default: DRM_ERROR("Unknown color depth\n"); i915_gem_object_unpin(obj); mutex_unlock(&dev->struct_mutex); return -EINVAL; } if (IS_I965G(dev)) { if (obj_priv->tiling_mode != I915_TILING_NONE) dspcntr |= DISPPLANE_TILED; else dspcntr &= ~DISPPLANE_TILED; } if (IS_IGDNG(dev)) /* must disable */ dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE; I915_WRITE(dspcntr_reg, dspcntr); Start = obj_priv->gtt_offset; Offset = y * crtc->fb->pitch + x * (crtc->fb->bits_per_pixel / 8); DRM_DEBUG("Writing base %08lX %08lX %d %d\n", Start, Offset, x, y); I915_WRITE(dspstride, crtc->fb->pitch); if (IS_I965G(dev)) { I915_WRITE(dspbase, Offset); I915_READ(dspbase); I915_WRITE(dspsurf, Start); I915_READ(dspsurf); I915_WRITE(dsptileoff, (y << 16) | x); } else { I915_WRITE(dspbase, Start + Offset); I915_READ(dspbase); } if ((IS_I965G(dev) || plane == 0)) intel_update_fbc(crtc, &crtc->mode); intel_wait_for_vblank(dev); if (old_fb) { intel_fb = to_intel_framebuffer(old_fb); obj_priv = intel_fb->obj->driver_private; i915_gem_object_unpin(intel_fb->obj); } intel_increase_pllclock(crtc, true); mutex_unlock(&dev->struct_mutex); if (!dev->primary->master) return 0; master_priv = dev->primary->master->driver_priv; if (!master_priv->sarea_priv) return 0; if (pipe) { master_priv->sarea_priv->pipeB_x = x; master_priv->sarea_priv->pipeB_y = y; } else { master_priv->sarea_priv->pipeA_x = x; master_priv->sarea_priv->pipeA_y = y; } return 0; } /* Disable the VGA plane that we never use */ static void i915_disable_vga (struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; u8 sr1; u32 vga_reg; if (IS_IGDNG(dev)) vga_reg = CPU_VGACNTRL; else vga_reg = VGACNTRL; if (I915_READ(vga_reg) & VGA_DISP_DISABLE) return; I915_WRITE8(VGA_SR_INDEX, 1); sr1 = I915_READ8(VGA_SR_DATA); I915_WRITE8(VGA_SR_DATA, sr1 | (1 << 5)); udelay(100); I915_WRITE(vga_reg, VGA_DISP_DISABLE); } static void igdng_disable_pll_edp (struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 dpa_ctl; DRM_DEBUG("\n"); dpa_ctl = I915_READ(DP_A); dpa_ctl &= ~DP_PLL_ENABLE; I915_WRITE(DP_A, dpa_ctl); } static void igdng_enable_pll_edp (struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 dpa_ctl; dpa_ctl = I915_READ(DP_A); dpa_ctl |= DP_PLL_ENABLE; I915_WRITE(DP_A, dpa_ctl); udelay(200); } static void igdng_set_pll_edp (struct drm_crtc *crtc, int clock) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 dpa_ctl; DRM_DEBUG("eDP PLL enable for clock %d\n", clock); dpa_ctl = I915_READ(DP_A); dpa_ctl &= ~DP_PLL_FREQ_MASK; if (clock < 200000) { u32 temp; dpa_ctl |= DP_PLL_FREQ_160MHZ; /* workaround for 160Mhz: 1) program 0x4600c bits 15:0 = 0x8124 2) program 0x46010 bit 0 = 1 3) program 0x46034 bit 24 = 1 4) program 0x64000 bit 14 = 1 */ temp = I915_READ(0x4600c); temp &= 0xffff0000; I915_WRITE(0x4600c, temp | 0x8124); temp = I915_READ(0x46010); I915_WRITE(0x46010, temp | 1); temp = I915_READ(0x46034); I915_WRITE(0x46034, temp | (1 << 24)); } else { dpa_ctl |= DP_PLL_FREQ_270MHZ; } I915_WRITE(DP_A, dpa_ctl); udelay(500); } static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; int pch_dpll_reg = (pipe == 0) ? PCH_DPLL_A : PCH_DPLL_B; int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR; int dspbase_reg = (plane == 0) ? DSPAADDR : DSPBADDR; int fdi_tx_reg = (pipe == 0) ? FDI_TXA_CTL : FDI_TXB_CTL; int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL; int fdi_rx_iir_reg = (pipe == 0) ? FDI_RXA_IIR : FDI_RXB_IIR; int fdi_rx_imr_reg = (pipe == 0) ? FDI_RXA_IMR : FDI_RXB_IMR; int transconf_reg = (pipe == 0) ? TRANSACONF : TRANSBCONF; int pf_ctl_reg = (pipe == 0) ? PFA_CTL_1 : PFB_CTL_1; int pf_win_size = (pipe == 0) ? PFA_WIN_SZ : PFB_WIN_SZ; int pf_win_pos = (pipe == 0) ? PFA_WIN_POS : PFB_WIN_POS; int cpu_htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; int cpu_hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; int cpu_hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; int cpu_vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B; int cpu_vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B; int cpu_vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B; int trans_htot_reg = (pipe == 0) ? TRANS_HTOTAL_A : TRANS_HTOTAL_B; int trans_hblank_reg = (pipe == 0) ? TRANS_HBLANK_A : TRANS_HBLANK_B; int trans_hsync_reg = (pipe == 0) ? TRANS_HSYNC_A : TRANS_HSYNC_B; int trans_vtot_reg = (pipe == 0) ? TRANS_VTOTAL_A : TRANS_VTOTAL_B; int trans_vblank_reg = (pipe == 0) ? TRANS_VBLANK_A : TRANS_VBLANK_B; int trans_vsync_reg = (pipe == 0) ? TRANS_VSYNC_A : TRANS_VSYNC_B; u32 temp; int tries = 5, j, n; /* XXX: When our outputs are all unaware of DPMS modes other than off * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. */ switch (mode) { case DRM_MODE_DPMS_ON: case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: DRM_DEBUG("crtc %d dpms on\n", pipe); if (HAS_eDP) { /* enable eDP PLL */ igdng_enable_pll_edp(crtc); } else { /* enable PCH DPLL */ temp = I915_READ(pch_dpll_reg); if ((temp & DPLL_VCO_ENABLE) == 0) { I915_WRITE(pch_dpll_reg, temp | DPLL_VCO_ENABLE); I915_READ(pch_dpll_reg); } /* enable PCH FDI RX PLL, wait warmup plus DMI latency */ temp = I915_READ(fdi_rx_reg); I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE | FDI_SEL_PCDCLK | FDI_DP_PORT_WIDTH_X4); /* default 4 lanes */ I915_READ(fdi_rx_reg); udelay(200); /* Enable CPU FDI TX PLL, always on for IGDNG */ temp = I915_READ(fdi_tx_reg); if ((temp & FDI_TX_PLL_ENABLE) == 0) { I915_WRITE(fdi_tx_reg, temp | FDI_TX_PLL_ENABLE); I915_READ(fdi_tx_reg); udelay(100); } } /* Enable panel fitting for LVDS */ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { temp = I915_READ(pf_ctl_reg); I915_WRITE(pf_ctl_reg, temp | PF_ENABLE | PF_FILTER_MED_3x3); /* currently full aspect */ I915_WRITE(pf_win_pos, 0); I915_WRITE(pf_win_size, (dev_priv->panel_fixed_mode->hdisplay << 16) | (dev_priv->panel_fixed_mode->vdisplay)); } /* Enable CPU pipe */ temp = I915_READ(pipeconf_reg); if ((temp & PIPEACONF_ENABLE) == 0) { I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE); I915_READ(pipeconf_reg); udelay(100); } /* configure and enable CPU plane */ temp = I915_READ(dspcntr_reg); if ((temp & DISPLAY_PLANE_ENABLE) == 0) { I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE); /* Flush the plane changes */ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); } if (!HAS_eDP) { /* enable CPU FDI TX and PCH FDI RX */ temp = I915_READ(fdi_tx_reg); temp |= FDI_TX_ENABLE; temp |= FDI_DP_PORT_WIDTH_X4; /* default */ temp &= ~FDI_LINK_TRAIN_NONE; temp |= FDI_LINK_TRAIN_PATTERN_1; I915_WRITE(fdi_tx_reg, temp); I915_READ(fdi_tx_reg); temp = I915_READ(fdi_rx_reg); temp &= ~FDI_LINK_TRAIN_NONE; temp |= FDI_LINK_TRAIN_PATTERN_1; I915_WRITE(fdi_rx_reg, temp | FDI_RX_ENABLE); I915_READ(fdi_rx_reg); udelay(150); /* Train FDI. */ /* umask FDI RX Interrupt symbol_lock and bit_lock bit for train result */ temp = I915_READ(fdi_rx_imr_reg); temp &= ~FDI_RX_SYMBOL_LOCK; temp &= ~FDI_RX_BIT_LOCK; I915_WRITE(fdi_rx_imr_reg, temp); I915_READ(fdi_rx_imr_reg); udelay(150); temp = I915_READ(fdi_rx_iir_reg); DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp); if ((temp & FDI_RX_BIT_LOCK) == 0) { for (j = 0; j < tries; j++) { temp = I915_READ(fdi_rx_iir_reg); DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp); if (temp & FDI_RX_BIT_LOCK) break; udelay(200); } if (j != tries) I915_WRITE(fdi_rx_iir_reg, temp | FDI_RX_BIT_LOCK); else DRM_DEBUG("train 1 fail\n"); } else { I915_WRITE(fdi_rx_iir_reg, temp | FDI_RX_BIT_LOCK); DRM_DEBUG("train 1 ok 2!\n"); } temp = I915_READ(fdi_tx_reg); temp &= ~FDI_LINK_TRAIN_NONE; temp |= FDI_LINK_TRAIN_PATTERN_2; I915_WRITE(fdi_tx_reg, temp); temp = I915_READ(fdi_rx_reg); temp &= ~FDI_LINK_TRAIN_NONE; temp |= FDI_LINK_TRAIN_PATTERN_2; I915_WRITE(fdi_rx_reg, temp); udelay(150); temp = I915_READ(fdi_rx_iir_reg); DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp); if ((temp & FDI_RX_SYMBOL_LOCK) == 0) { for (j = 0; j < tries; j++) { temp = I915_READ(fdi_rx_iir_reg); DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp); if (temp & FDI_RX_SYMBOL_LOCK) break; udelay(200); } if (j != tries) { I915_WRITE(fdi_rx_iir_reg, temp | FDI_RX_SYMBOL_LOCK); DRM_DEBUG("train 2 ok 1!\n"); } else DRM_DEBUG("train 2 fail\n"); } else { I915_WRITE(fdi_rx_iir_reg, temp | FDI_RX_SYMBOL_LOCK); DRM_DEBUG("train 2 ok 2!\n"); } DRM_DEBUG("train done\n"); /* set transcoder timing */ I915_WRITE(trans_htot_reg, I915_READ(cpu_htot_reg)); I915_WRITE(trans_hblank_reg, I915_READ(cpu_hblank_reg)); I915_WRITE(trans_hsync_reg, I915_READ(cpu_hsync_reg)); I915_WRITE(trans_vtot_reg, I915_READ(cpu_vtot_reg)); I915_WRITE(trans_vblank_reg, I915_READ(cpu_vblank_reg)); I915_WRITE(trans_vsync_reg, I915_READ(cpu_vsync_reg)); /* enable PCH transcoder */ temp = I915_READ(transconf_reg); I915_WRITE(transconf_reg, temp | TRANS_ENABLE); I915_READ(transconf_reg); while ((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) == 0) ; /* enable normal */ temp = I915_READ(fdi_tx_reg); temp &= ~FDI_LINK_TRAIN_NONE; I915_WRITE(fdi_tx_reg, temp | FDI_LINK_TRAIN_NONE | FDI_TX_ENHANCE_FRAME_ENABLE); I915_READ(fdi_tx_reg); temp = I915_READ(fdi_rx_reg); temp &= ~FDI_LINK_TRAIN_NONE; I915_WRITE(fdi_rx_reg, temp | FDI_LINK_TRAIN_NONE | FDI_RX_ENHANCE_FRAME_ENABLE); I915_READ(fdi_rx_reg); /* wait one idle pattern time */ udelay(100); } intel_crtc_load_lut(crtc); break; case DRM_MODE_DPMS_OFF: DRM_DEBUG("crtc %d dpms off\n", pipe); i915_disable_vga(dev); /* Disable display plane */ temp = I915_READ(dspcntr_reg); if ((temp & DISPLAY_PLANE_ENABLE) != 0) { I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE); /* Flush the plane changes */ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); I915_READ(dspbase_reg); } /* disable cpu pipe, disable after all planes disabled */ temp = I915_READ(pipeconf_reg); if ((temp & PIPEACONF_ENABLE) != 0) { I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE); I915_READ(pipeconf_reg); n = 0; /* wait for cpu pipe off, pipe state */ while ((I915_READ(pipeconf_reg) & I965_PIPECONF_ACTIVE) != 0) { n++; if (n < 60) { udelay(500); continue; } else { DRM_DEBUG("pipe %d off delay\n", pipe); break; } } } else DRM_DEBUG("crtc %d is disabled\n", pipe); if (HAS_eDP) { igdng_disable_pll_edp(crtc); } /* disable CPU FDI tx and PCH FDI rx */ temp = I915_READ(fdi_tx_reg); I915_WRITE(fdi_tx_reg, temp & ~FDI_TX_ENABLE); I915_READ(fdi_tx_reg); temp = I915_READ(fdi_rx_reg); I915_WRITE(fdi_rx_reg, temp & ~FDI_RX_ENABLE); I915_READ(fdi_rx_reg); udelay(100); /* still set train pattern 1 */ temp = I915_READ(fdi_tx_reg); temp &= ~FDI_LINK_TRAIN_NONE; temp |= FDI_LINK_TRAIN_PATTERN_1; I915_WRITE(fdi_tx_reg, temp); temp = I915_READ(fdi_rx_reg); temp &= ~FDI_LINK_TRAIN_NONE; temp |= FDI_LINK_TRAIN_PATTERN_1; I915_WRITE(fdi_rx_reg, temp); udelay(100); /* disable PCH transcoder */ temp = I915_READ(transconf_reg); if ((temp & TRANS_ENABLE) != 0) { I915_WRITE(transconf_reg, temp & ~TRANS_ENABLE); I915_READ(transconf_reg); n = 0; /* wait for PCH transcoder off, transcoder state */ while ((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) != 0) { n++; if (n < 60) { udelay(500); continue; } else { DRM_DEBUG("transcoder %d off delay\n", pipe); break; } } } /* disable PCH DPLL */ temp = I915_READ(pch_dpll_reg); if ((temp & DPLL_VCO_ENABLE) != 0) { I915_WRITE(pch_dpll_reg, temp & ~DPLL_VCO_ENABLE); I915_READ(pch_dpll_reg); } temp = I915_READ(fdi_rx_reg); if ((temp & FDI_RX_PLL_ENABLE) != 0) { temp &= ~FDI_SEL_PCDCLK; temp &= ~FDI_RX_PLL_ENABLE; I915_WRITE(fdi_rx_reg, temp); I915_READ(fdi_rx_reg); } /* Disable CPU FDI TX PLL */ temp = I915_READ(fdi_tx_reg); if ((temp & FDI_TX_PLL_ENABLE) != 0) { I915_WRITE(fdi_tx_reg, temp & ~FDI_TX_PLL_ENABLE); I915_READ(fdi_tx_reg); udelay(100); } /* Disable PF */ temp = I915_READ(pf_ctl_reg); if ((temp & PF_ENABLE) != 0) { I915_WRITE(pf_ctl_reg, temp & ~PF_ENABLE); I915_READ(pf_ctl_reg); } I915_WRITE(pf_win_size, 0); /* Wait for the clocks to turn off. */ udelay(150); break; } } static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR; int dspbase_reg = (plane == 0) ? DSPAADDR : DSPBADDR; int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; u32 temp; /* XXX: When our outputs are all unaware of DPMS modes other than off * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. */ switch (mode) { case DRM_MODE_DPMS_ON: case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: intel_update_watermarks(dev); /* Enable the DPLL */ temp = I915_READ(dpll_reg); if ((temp & DPLL_VCO_ENABLE) == 0) { I915_WRITE(dpll_reg, temp); I915_READ(dpll_reg); /* Wait for the clocks to stabilize. */ udelay(150); I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); I915_READ(dpll_reg); /* Wait for the clocks to stabilize. */ udelay(150); I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); I915_READ(dpll_reg); /* Wait for the clocks to stabilize. */ udelay(150); } /* Enable the pipe */ temp = I915_READ(pipeconf_reg); if ((temp & PIPEACONF_ENABLE) == 0) I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE); /* Enable the plane */ temp = I915_READ(dspcntr_reg); if ((temp & DISPLAY_PLANE_ENABLE) == 0) { I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE); /* Flush the plane changes */ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); } intel_crtc_load_lut(crtc); if ((IS_I965G(dev) || plane == 0)) intel_update_fbc(crtc, &crtc->mode); /* Give the overlay scaler a chance to enable if it's on this pipe */ //intel_crtc_dpms_video(crtc, true); TODO break; case DRM_MODE_DPMS_OFF: intel_update_watermarks(dev); /* Give the overlay scaler a chance to disable if it's on this pipe */ //intel_crtc_dpms_video(crtc, FALSE); TODO if (dev_priv->cfb_plane == plane && dev_priv->display.disable_fbc) dev_priv->display.disable_fbc(dev); /* Disable the VGA plane that we never use */ i915_disable_vga(dev); /* Disable display plane */ temp = I915_READ(dspcntr_reg); if ((temp & DISPLAY_PLANE_ENABLE) != 0) { I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE); /* Flush the plane changes */ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); I915_READ(dspbase_reg); } if (!IS_I9XX(dev)) { /* Wait for vblank for the disable to take effect */ intel_wait_for_vblank(dev); } /* Next, disable display pipes */ temp = I915_READ(pipeconf_reg); if ((temp & PIPEACONF_ENABLE) != 0) { I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE); I915_READ(pipeconf_reg); } /* Wait for vblank for the disable to take effect. */ intel_wait_for_vblank(dev); temp = I915_READ(dpll_reg); if ((temp & DPLL_VCO_ENABLE) != 0) { I915_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE); I915_READ(dpll_reg); } /* Wait for the clocks to turn off. */ udelay(150); break; } } /** * Sets the power management mode of the pipe and plane. * * This code should probably grow support for turning the cursor off and back * on appropriately at the same time as we're turning the pipe off/on. */ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_master_private *master_priv; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_crtc->pipe; bool enabled; dev_priv->display.dpms(crtc, mode); intel_crtc->dpms_mode = mode; if (!dev->primary->master) return; master_priv = dev->primary->master->driver_priv; if (!master_priv->sarea_priv) return; enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF; switch (pipe) { case 0: master_priv->sarea_priv->pipeA_w = enabled ? crtc->mode.hdisplay : 0; master_priv->sarea_priv->pipeA_h = enabled ? crtc->mode.vdisplay : 0; break; case 1: master_priv->sarea_priv->pipeB_w = enabled ? crtc->mode.hdisplay : 0; master_priv->sarea_priv->pipeB_h = enabled ? crtc->mode.vdisplay : 0; break; default: DRM_ERROR("Can't update pipe %d in SAREA\n", pipe); break; } } static void intel_crtc_prepare (struct drm_crtc *crtc) { struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); } static void intel_crtc_commit (struct drm_crtc *crtc) { struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); } void intel_encoder_prepare (struct drm_encoder *encoder) { struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; /* lvds has its own version of prepare see intel_lvds_prepare */ encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF); } void intel_encoder_commit (struct drm_encoder *encoder) { struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; /* lvds has its own version of commit see intel_lvds_commit */ encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); } static bool intel_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = crtc->dev; if (IS_IGDNG(dev)) { /* FDI link clock is fixed at 2.7G */ if (mode->clock * 3 > 27000 * 4) return MODE_CLOCK_HIGH; } return true; } static int i945_get_display_clock_speed(struct drm_device *dev) { return 400000; } static int i915_get_display_clock_speed(struct drm_device *dev) { return 333000; } static int i9xx_misc_get_display_clock_speed(struct drm_device *dev) { return 200000; } static int i915gm_get_display_clock_speed(struct drm_device *dev) { u16 gcfgc = 0; pci_read_config_word(dev->pdev, GCFGC, &gcfgc); if (gcfgc & GC_LOW_FREQUENCY_ENABLE) return 133000; else { switch (gcfgc & GC_DISPLAY_CLOCK_MASK) { case GC_DISPLAY_CLOCK_333_MHZ: return 333000; default: case GC_DISPLAY_CLOCK_190_200_MHZ: return 190000; } } } static int i865_get_display_clock_speed(struct drm_device *dev) { return 266000; } static int i855_get_display_clock_speed(struct drm_device *dev) { u16 hpllcc = 0; /* Assume that the hardware is in the high speed state. This * should be the default. */ switch (hpllcc & GC_CLOCK_CONTROL_MASK) { case GC_CLOCK_133_200: case GC_CLOCK_100_200: return 200000; case GC_CLOCK_166_250: return 250000; case GC_CLOCK_100_133: return 133000; } /* Shouldn't happen */ return 0; } static int i830_get_display_clock_speed(struct drm_device *dev) { return 133000; } /** * Return the pipe currently connected to the panel fitter, * or -1 if the panel fitter is not present or not in use */ static int intel_panel_fitter_pipe (struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; u32 pfit_control; /* i830 doesn't have a panel fitter */ if (IS_I830(dev)) return -1; pfit_control = I915_READ(PFIT_CONTROL); /* See if the panel fitter is in use */ if ((pfit_control & PFIT_ENABLE) == 0) return -1; /* 965 can place panel fitter on either pipe */ if (IS_I965G(dev)) return (pfit_control >> 29) & 0x3; /* older chips can only use pipe 1 */ return 1; } struct fdi_m_n { u32 tu; u32 gmch_m; u32 gmch_n; u32 link_m; u32 link_n; }; static void fdi_reduce_ratio(u32 *num, u32 *den) { while (*num > 0xffffff || *den > 0xffffff) { *num >>= 1; *den >>= 1; } } #define DATA_N 0x800000 #define LINK_N 0x80000 static void igdng_compute_m_n(int bits_per_pixel, int nlanes, int pixel_clock, int link_clock, struct fdi_m_n *m_n) { u64 temp; m_n->tu = 64; /* default size */ temp = (u64) DATA_N * pixel_clock; temp = div_u64(temp, link_clock); m_n->gmch_m = div_u64(temp * bits_per_pixel, nlanes); m_n->gmch_m >>= 3; /* convert to bytes_per_pixel */ m_n->gmch_n = DATA_N; fdi_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); temp = (u64) LINK_N * pixel_clock; m_n->link_m = div_u64(temp, link_clock); m_n->link_n = LINK_N; fdi_reduce_ratio(&m_n->link_m, &m_n->link_n); } struct intel_watermark_params { unsigned long fifo_size; unsigned long max_wm; unsigned long default_wm; unsigned long guard_size; unsigned long cacheline_size; }; /* IGD has different values for various configs */ static struct intel_watermark_params igd_display_wm = { IGD_DISPLAY_FIFO, IGD_MAX_WM, IGD_DFT_WM, IGD_GUARD_WM, IGD_FIFO_LINE_SIZE }; static struct intel_watermark_params igd_display_hplloff_wm = { IGD_DISPLAY_FIFO, IGD_MAX_WM, IGD_DFT_HPLLOFF_WM, IGD_GUARD_WM, IGD_FIFO_LINE_SIZE }; static struct intel_watermark_params igd_cursor_wm = { IGD_CURSOR_FIFO, IGD_CURSOR_MAX_WM, IGD_CURS