From patchwork Wed Apr 9 21:44:33 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Paulo Zanoni X-Patchwork-Id: 3957741 Return-Path: X-Original-To: patchwork-intel-gfx@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id E699ABFF02 for ; Wed, 9 Apr 2014 21:45:11 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id CA6C620142 for ; Wed, 9 Apr 2014 21:45:06 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id 6692B2063F for ; Wed, 9 Apr 2014 21:45:01 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id F112A6EBD7; Wed, 9 Apr 2014 14:45:00 -0700 (PDT) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mail-yk0-f180.google.com (mail-yk0-f180.google.com [209.85.160.180]) by gabe.freedesktop.org (Postfix) with ESMTP id C374A6EBD7 for ; Wed, 9 Apr 2014 14:44:59 -0700 (PDT) Received: by mail-yk0-f180.google.com with SMTP id 19so2748615ykq.39 for ; Wed, 09 Apr 2014 14:44:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-type:content-transfer-encoding; bh=0G3jZjUtZ3ap0eRDJmduMQGCD8N0zMsKfoFJnPG4IEA=; b=tQYjEPqYJ8ajHchSsOkRTAYK+orY4WbmHFwXh/AinWCe4ZDThpoNOmAuxNo/Y9a+dT uN4f6ng8+sPXKCYLBJkKscIloXdyPnnSlWPkzjnROcLYPNfGF8xrgxuoCIMEpoMTQtCi 0ctRekhDCBbYmAqxW1OuF37qmkIO5SDA4nxnyB8xC9TU6v20bz52vOwc9DuvM5cOWqP1 ZUL3dp2M/nvXe5DgFzb0iwa/vX7rToovZNJhdUgchzHO4LpxqggGv/Wqusfjyk4EYLGs IfLSFs/kpHAPdfY7ZBgfDCMdhxCPyUCLclc3kjAN6N5rtsDYWZDCpVXBGuLIhij6W14w KBGw== X-Received: by 10.236.137.8 with SMTP id x8mr17061812yhi.4.1397079898375; Wed, 09 Apr 2014 14:44:58 -0700 (PDT) Received: from localhost.localdomain ([177.40.102.110]) by mx.google.com with ESMTPSA id f62sm3916352yhq.6.2014.04.09.14.44.55 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 09 Apr 2014 14:44:57 -0700 (PDT) From: Paulo Zanoni To: intel-gfx@lists.freedesktop.org Date: Wed, 9 Apr 2014 18:44:33 -0300 Message-Id: <1397079873-18257-5-git-send-email-przanoni@gmail.com> X-Mailer: git-send-email 1.9.0 In-Reply-To: <1397079873-18257-1-git-send-email-przanoni@gmail.com> References: <1397079873-18257-1-git-send-email-przanoni@gmail.com> MIME-Version: 1.0 Cc: Paulo Zanoni Subject: [Intel-gfx] [PATCH 4/4] drm/i915: extract intel_pll.c from intel_display.c X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Spam-Status: No, score=-4.4 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Paulo Zanoni Since intel_display.c is way too big for our little heads. Signed-off-by: Paulo Zanoni --- drivers/gpu/drm/i915/Makefile | 1 + drivers/gpu/drm/i915/intel_display.c | 1902 +--------------------------------- drivers/gpu/drm/i915/intel_drv.h | 77 +- drivers/gpu/drm/i915/intel_panel.c | 36 + drivers/gpu/drm/i915/intel_pll.c | 1779 +++++++++++++++++++++++++++++++ 5 files changed, 1930 insertions(+), 1865 deletions(-) create mode 100644 drivers/gpu/drm/i915/intel_pll.c diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 8e022cc..25e86ad 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -40,6 +40,7 @@ i915-y += intel_bios.o \ intel_fdi.o \ intel_modes.o \ intel_overlay.o \ + intel_pll.o \ intel_sideband.o \ intel_sprite.o i915-$(CONFIG_ACPI) += intel_acpi.o intel_opregion.o diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e23ca4b..3590130 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -41,13 +41,6 @@ #include #include -static void intel_increase_pllclock(struct drm_crtc *crtc); - -static void i9xx_crtc_clock_get(struct intel_crtc *crtc, - struct intel_crtc_config *pipe_config); -static void ironlake_pch_clock_get(struct intel_crtc *crtc, - struct intel_crtc_config *pipe_config); - static int intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, int x, int y, struct drm_framebuffer *old_fb); static int intel_framebuffer_init(struct drm_device *dev, @@ -55,21 +48,6 @@ static int intel_framebuffer_init(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd, struct drm_i915_gem_object *obj); -typedef struct { - int min, max; -} intel_range_t; - -typedef struct { - int dot_limit; - int p2_slow, p2_fast; -} intel_p2_t; - -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; -}; - int intel_pch_rawclk(struct drm_device *dev) { @@ -80,253 +58,6 @@ intel_pch_rawclk(struct drm_device *dev) return I915_READ(PCH_RAWCLK_FREQ) & RAWCLK_FREQ_MASK; } -static const intel_limit_t intel_limits_i8xx_dac = { - .dot = { .min = 25000, .max = 350000 }, - .vco = { .min = 908000, .max = 1512000 }, - .n = { .min = 2, .max = 16 }, - .m = { .min = 96, .max = 140 }, - .m1 = { .min = 18, .max = 26 }, - .m2 = { .min = 6, .max = 16 }, - .p = { .min = 4, .max = 128 }, - .p1 = { .min = 2, .max = 33 }, - .p2 = { .dot_limit = 165000, - .p2_slow = 4, .p2_fast = 2 }, -}; - -static const intel_limit_t intel_limits_i8xx_dvo = { - .dot = { .min = 25000, .max = 350000 }, - .vco = { .min = 908000, .max = 1512000 }, - .n = { .min = 2, .max = 16 }, - .m = { .min = 96, .max = 140 }, - .m1 = { .min = 18, .max = 26 }, - .m2 = { .min = 6, .max = 16 }, - .p = { .min = 4, .max = 128 }, - .p1 = { .min = 2, .max = 33 }, - .p2 = { .dot_limit = 165000, - .p2_slow = 4, .p2_fast = 4 }, -}; - -static const intel_limit_t intel_limits_i8xx_lvds = { - .dot = { .min = 25000, .max = 350000 }, - .vco = { .min = 908000, .max = 1512000 }, - .n = { .min = 2, .max = 16 }, - .m = { .min = 96, .max = 140 }, - .m1 = { .min = 18, .max = 26 }, - .m2 = { .min = 6, .max = 16 }, - .p = { .min = 4, .max = 128 }, - .p1 = { .min = 1, .max = 6 }, - .p2 = { .dot_limit = 165000, - .p2_slow = 14, .p2_fast = 7 }, -}; - -static const intel_limit_t intel_limits_i9xx_sdvo = { - .dot = { .min = 20000, .max = 400000 }, - .vco = { .min = 1400000, .max = 2800000 }, - .n = { .min = 1, .max = 6 }, - .m = { .min = 70, .max = 120 }, - .m1 = { .min = 8, .max = 18 }, - .m2 = { .min = 3, .max = 7 }, - .p = { .min = 5, .max = 80 }, - .p1 = { .min = 1, .max = 8 }, - .p2 = { .dot_limit = 200000, - .p2_slow = 10, .p2_fast = 5 }, -}; - -static const intel_limit_t intel_limits_i9xx_lvds = { - .dot = { .min = 20000, .max = 400000 }, - .vco = { .min = 1400000, .max = 2800000 }, - .n = { .min = 1, .max = 6 }, - .m = { .min = 70, .max = 120 }, - .m1 = { .min = 8, .max = 18 }, - .m2 = { .min = 3, .max = 7 }, - .p = { .min = 7, .max = 98 }, - .p1 = { .min = 1, .max = 8 }, - .p2 = { .dot_limit = 112000, - .p2_slow = 14, .p2_fast = 7 }, -}; - - -static const intel_limit_t intel_limits_g4x_sdvo = { - .dot = { .min = 25000, .max = 270000 }, - .vco = { .min = 1750000, .max = 3500000}, - .n = { .min = 1, .max = 4 }, - .m = { .min = 104, .max = 138 }, - .m1 = { .min = 17, .max = 23 }, - .m2 = { .min = 5, .max = 11 }, - .p = { .min = 10, .max = 30 }, - .p1 = { .min = 1, .max = 3}, - .p2 = { .dot_limit = 270000, - .p2_slow = 10, - .p2_fast = 10 - }, -}; - -static const intel_limit_t intel_limits_g4x_hdmi = { - .dot = { .min = 22000, .max = 400000 }, - .vco = { .min = 1750000, .max = 3500000}, - .n = { .min = 1, .max = 4 }, - .m = { .min = 104, .max = 138 }, - .m1 = { .min = 16, .max = 23 }, - .m2 = { .min = 5, .max = 11 }, - .p = { .min = 5, .max = 80 }, - .p1 = { .min = 1, .max = 8}, - .p2 = { .dot_limit = 165000, - .p2_slow = 10, .p2_fast = 5 }, -}; - -static const intel_limit_t intel_limits_g4x_single_channel_lvds = { - .dot = { .min = 20000, .max = 115000 }, - .vco = { .min = 1750000, .max = 3500000 }, - .n = { .min = 1, .max = 3 }, - .m = { .min = 104, .max = 138 }, - .m1 = { .min = 17, .max = 23 }, - .m2 = { .min = 5, .max = 11 }, - .p = { .min = 28, .max = 112 }, - .p1 = { .min = 2, .max = 8 }, - .p2 = { .dot_limit = 0, - .p2_slow = 14, .p2_fast = 14 - }, -}; - -static const intel_limit_t intel_limits_g4x_dual_channel_lvds = { - .dot = { .min = 80000, .max = 224000 }, - .vco = { .min = 1750000, .max = 3500000 }, - .n = { .min = 1, .max = 3 }, - .m = { .min = 104, .max = 138 }, - .m1 = { .min = 17, .max = 23 }, - .m2 = { .min = 5, .max = 11 }, - .p = { .min = 14, .max = 42 }, - .p1 = { .min = 2, .max = 6 }, - .p2 = { .dot_limit = 0, - .p2_slow = 7, .p2_fast = 7 - }, -}; - -static const intel_limit_t intel_limits_pineview_sdvo = { - .dot = { .min = 20000, .max = 400000}, - .vco = { .min = 1700000, .max = 3500000 }, - /* Pineview's Ncounter is a ring counter */ - .n = { .min = 3, .max = 6 }, - .m = { .min = 2, .max = 256 }, - /* Pineview only has one combined m divider, which we treat as m2. */ - .m1 = { .min = 0, .max = 0 }, - .m2 = { .min = 0, .max = 254 }, - .p = { .min = 5, .max = 80 }, - .p1 = { .min = 1, .max = 8 }, - .p2 = { .dot_limit = 200000, - .p2_slow = 10, .p2_fast = 5 }, -}; - -static const intel_limit_t intel_limits_pineview_lvds = { - .dot = { .min = 20000, .max = 400000 }, - .vco = { .min = 1700000, .max = 3500000 }, - .n = { .min = 3, .max = 6 }, - .m = { .min = 2, .max = 256 }, - .m1 = { .min = 0, .max = 0 }, - .m2 = { .min = 0, .max = 254 }, - .p = { .min = 7, .max = 112 }, - .p1 = { .min = 1, .max = 8 }, - .p2 = { .dot_limit = 112000, - .p2_slow = 14, .p2_fast = 14 }, -}; - -/* Ironlake / Sandybridge - * - * We calculate clock using (register_value + 2) for N/M1/M2, so here - * the range value for them is (actual_value - 2). - */ -static const intel_limit_t intel_limits_ironlake_dac = { - .dot = { .min = 25000, .max = 350000 }, - .vco = { .min = 1760000, .max = 3510000 }, - .n = { .min = 1, .max = 5 }, - .m = { .min = 79, .max = 127 }, - .m1 = { .min = 12, .max = 22 }, - .m2 = { .min = 5, .max = 9 }, - .p = { .min = 5, .max = 80 }, - .p1 = { .min = 1, .max = 8 }, - .p2 = { .dot_limit = 225000, - .p2_slow = 10, .p2_fast = 5 }, -}; - -static const intel_limit_t intel_limits_ironlake_single_lvds = { - .dot = { .min = 25000, .max = 350000 }, - .vco = { .min = 1760000, .max = 3510000 }, - .n = { .min = 1, .max = 3 }, - .m = { .min = 79, .max = 118 }, - .m1 = { .min = 12, .max = 22 }, - .m2 = { .min = 5, .max = 9 }, - .p = { .min = 28, .max = 112 }, - .p1 = { .min = 2, .max = 8 }, - .p2 = { .dot_limit = 225000, - .p2_slow = 14, .p2_fast = 14 }, -}; - -static const intel_limit_t intel_limits_ironlake_dual_lvds = { - .dot = { .min = 25000, .max = 350000 }, - .vco = { .min = 1760000, .max = 3510000 }, - .n = { .min = 1, .max = 3 }, - .m = { .min = 79, .max = 127 }, - .m1 = { .min = 12, .max = 22 }, - .m2 = { .min = 5, .max = 9 }, - .p = { .min = 14, .max = 56 }, - .p1 = { .min = 2, .max = 8 }, - .p2 = { .dot_limit = 225000, - .p2_slow = 7, .p2_fast = 7 }, -}; - -/* LVDS 100mhz refclk limits. */ -static const intel_limit_t intel_limits_ironlake_single_lvds_100m = { - .dot = { .min = 25000, .max = 350000 }, - .vco = { .min = 1760000, .max = 3510000 }, - .n = { .min = 1, .max = 2 }, - .m = { .min = 79, .max = 126 }, - .m1 = { .min = 12, .max = 22 }, - .m2 = { .min = 5, .max = 9 }, - .p = { .min = 28, .max = 112 }, - .p1 = { .min = 2, .max = 8 }, - .p2 = { .dot_limit = 225000, - .p2_slow = 14, .p2_fast = 14 }, -}; - -static const intel_limit_t intel_limits_ironlake_dual_lvds_100m = { - .dot = { .min = 25000, .max = 350000 }, - .vco = { .min = 1760000, .max = 3510000 }, - .n = { .min = 1, .max = 3 }, - .m = { .min = 79, .max = 126 }, - .m1 = { .min = 12, .max = 22 }, - .m2 = { .min = 5, .max = 9 }, - .p = { .min = 14, .max = 42 }, - .p1 = { .min = 2, .max = 6 }, - .p2 = { .dot_limit = 225000, - .p2_slow = 7, .p2_fast = 7 }, -}; - -static const intel_limit_t intel_limits_vlv = { - /* - * These are the data rate limits (measured in fast clocks) - * since those are the strictest limits we have. The fast - * clock and actual rate limits are more relaxed, so checking - * them would make no difference. - */ - .dot = { .min = 25000 * 5, .max = 270000 * 5 }, - .vco = { .min = 4000000, .max = 6000000 }, - .n = { .min = 1, .max = 7 }, - .m1 = { .min = 2, .max = 3 }, - .m2 = { .min = 11, .max = 156 }, - .p1 = { .min = 2, .max = 3 }, - .p2 = { .p2_slow = 2, .p2_fast = 20 }, /* slow=min, fast=max */ -}; - -static void vlv_clock(int refclk, intel_clock_t *clock) -{ - clock->m = clock->m1 * clock->m2; - clock->p = clock->p1 * clock->p2; - if (WARN_ON(clock->n == 0 || clock->p == 0)) - return; - clock->vco = DIV_ROUND_CLOSEST(refclk * clock->m, clock->n); - clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p); -} - /** * Returns whether any output on the specified pipe is of the specified type */ @@ -342,384 +73,6 @@ bool intel_pipe_has_type(struct drm_crtc *crtc, int type) return false; } -static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc, - int refclk) -{ - struct drm_device *dev = crtc->dev; - const intel_limit_t *limit; - - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { - if (intel_is_dual_link_lvds(dev)) { - if (refclk == 100000) - limit = &intel_limits_ironlake_dual_lvds_100m; - else - limit = &intel_limits_ironlake_dual_lvds; - } else { - if (refclk == 100000) - limit = &intel_limits_ironlake_single_lvds_100m; - else - limit = &intel_limits_ironlake_single_lvds; - } - } else - limit = &intel_limits_ironlake_dac; - - return limit; -} - -static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - const intel_limit_t *limit; - - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { - if (intel_is_dual_link_lvds(dev)) - limit = &intel_limits_g4x_dual_channel_lvds; - else - 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 /* The option is for other outputs */ - limit = &intel_limits_i9xx_sdvo; - - return limit; -} - -static const intel_limit_t *intel_limit(struct drm_crtc *crtc, int refclk) -{ - struct drm_device *dev = crtc->dev; - const intel_limit_t *limit; - - if (HAS_PCH_SPLIT(dev)) - limit = intel_ironlake_limit(crtc, refclk); - else if (IS_G4X(dev)) { - limit = intel_g4x_limit(crtc); - } else if (IS_PINEVIEW(dev)) { - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) - limit = &intel_limits_pineview_lvds; - else - limit = &intel_limits_pineview_sdvo; - } else if (IS_VALLEYVIEW(dev)) { - limit = &intel_limits_vlv; - } else if (!IS_GEN2(dev)) { - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) - limit = &intel_limits_i9xx_lvds; - else - limit = &intel_limits_i9xx_sdvo; - } else { - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) - limit = &intel_limits_i8xx_lvds; - else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DVO)) - limit = &intel_limits_i8xx_dvo; - else - limit = &intel_limits_i8xx_dac; - } - return limit; -} - -/* m1 is reserved as 0 in Pineview, n is a ring counter */ -static void pineview_clock(int refclk, intel_clock_t *clock) -{ - clock->m = clock->m2 + 2; - clock->p = clock->p1 * clock->p2; - if (WARN_ON(clock->n == 0 || clock->p == 0)) - return; - clock->vco = DIV_ROUND_CLOSEST(refclk * clock->m, clock->n); - clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p); -} - -static uint32_t i9xx_dpll_compute_m(struct dpll *dpll) -{ - return 5 * (dpll->m1 + 2) + (dpll->m2 + 2); -} - -static void i9xx_clock(int refclk, intel_clock_t *clock) -{ - clock->m = i9xx_dpll_compute_m(clock); - clock->p = clock->p1 * clock->p2; - if (WARN_ON(clock->n + 2 == 0 || clock->p == 0)) - return; - clock->vco = DIV_ROUND_CLOSEST(refclk * clock->m, clock->n + 2); - clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p); -} - -#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_device *dev, - const intel_limit_t *limit, - const intel_clock_t *clock) -{ - if (clock->n < limit->n.min || limit->n.max < clock->n) - INTELPllInvalid("n out of range\n"); - if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1) - INTELPllInvalid("p1 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 (!IS_PINEVIEW(dev) && !IS_VALLEYVIEW(dev)) - if (clock->m1 <= clock->m2) - INTELPllInvalid("m1 <= m2\n"); - - if (!IS_VALLEYVIEW(dev)) { - if (clock->p < limit->p.min || limit->p.max < clock->p) - INTELPllInvalid("p out of range\n"); - if (clock->m < limit->m.min || limit->m.max < clock->m) - INTELPllInvalid("m 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 -i9xx_find_best_dpll(const intel_limit_t *limit, struct drm_crtc *crtc, - int target, int refclk, intel_clock_t *match_clock, - intel_clock_t *best_clock) -{ - struct drm_device *dev = crtc->dev; - intel_clock_t clock; - int err = target; - - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { - /* - * For LVDS 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 (intel_is_dual_link_lvds(dev)) - 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.m1 = limit->m1.min; clock.m1 <= limit->m1.max; - clock.m1++) { - for (clock.m2 = limit->m2.min; - clock.m2 <= limit->m2.max; clock.m2++) { - if (clock.m2 >= clock.m1) - 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++) { - int this_err; - - i9xx_clock(refclk, &clock); - if (!intel_PLL_is_valid(dev, limit, - &clock)) - continue; - if (match_clock && - clock.p != match_clock->p) - continue; - - this_err = abs(clock.dot - target); - if (this_err < err) { - *best_clock = clock; - err = this_err; - } - } - } - } - } - - return (err != target); -} - -static bool -pnv_find_best_dpll(const intel_limit_t *limit, struct drm_crtc *crtc, - int target, int refclk, intel_clock_t *match_clock, - intel_clock_t *best_clock) -{ - struct drm_device *dev = crtc->dev; - intel_clock_t clock; - int err = target; - - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { - /* - * For LVDS 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 (intel_is_dual_link_lvds(dev)) - 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.m1 = limit->m1.min; clock.m1 <= limit->m1.max; - clock.m1++) { - for (clock.m2 = limit->m2.min; - clock.m2 <= limit->m2.max; clock.m2++) { - 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++) { - int this_err; - - pineview_clock(refclk, &clock); - if (!intel_PLL_is_valid(dev, limit, - &clock)) - continue; - if (match_clock && - clock.p != match_clock->p) - continue; - - this_err = abs(clock.dot - target); - if (this_err < err) { - *best_clock = clock; - err = this_err; - } - } - } - } - } - - return (err != target); -} - -static bool -g4x_find_best_dpll(const intel_limit_t *limit, struct drm_crtc *crtc, - int target, int refclk, intel_clock_t *match_clock, - intel_clock_t *best_clock) -{ - struct drm_device *dev = crtc->dev; - intel_clock_t clock; - int max_n; - bool found; - /* approximately equals target * 0.00585 */ - int err_most = (target >> 8) + (target >> 9); - found = false; - - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { - if (intel_is_dual_link_lvds(dev)) - 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 requirement, prefer smaller n to precision */ - for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) { - /* based on hardware requirement, 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; - - i9xx_clock(refclk, &clock); - if (!intel_PLL_is_valid(dev, limit, - &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 -vlv_find_best_dpll(const intel_limit_t *limit, struct drm_crtc *crtc, - int target, int refclk, intel_clock_t *match_clock, - intel_clock_t *best_clock) -{ - struct drm_device *dev = crtc->dev; - intel_clock_t clock; - unsigned int bestppm = 1000000; - /* min update 19.2 MHz */ - int max_n = min(limit->n.max, refclk / 19200); - bool found = false; - - target *= 5; /* fast clock */ - - memset(best_clock, 0, sizeof(*best_clock)); - - /* based on hardware requirement, prefer smaller n to precision */ - for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) { - for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) { - for (clock.p2 = limit->p2.p2_fast; clock.p2 >= limit->p2.p2_slow; - clock.p2 -= clock.p2 > 10 ? 2 : 1) { - clock.p = clock.p1 * clock.p2; - /* based on hardware requirement, prefer bigger m1,m2 values */ - for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) { - unsigned int ppm, diff; - - clock.m2 = DIV_ROUND_CLOSEST(target * clock.p * clock.n, - refclk * clock.m1); - - vlv_clock(refclk, &clock); - - if (!intel_PLL_is_valid(dev, limit, - &clock)) - continue; - - diff = abs(clock.dot - target); - ppm = div_u64(1000000ULL * diff, target); - - if (ppm < 100 && clock.p > best_clock->p) { - bestppm = 0; - *best_clock = clock; - found = true; - } - - if (bestppm >= 10 && ppm < bestppm - 10) { - bestppm = ppm; - *best_clock = clock; - found = true; - } - } - } - } - } - - return found; -} - bool intel_crtc_active(struct drm_crtc *crtc) { struct intel_crtc *intel_crtc = to_intel_crtc(crtc); @@ -899,103 +252,6 @@ bool ibx_digital_port_connected(struct drm_i915_private *dev_priv, return I915_READ(SDEISR) & bit; } -/* Only for pre-ILK configs */ -void assert_pll(struct drm_i915_private *dev_priv, - enum pipe pipe, bool state) -{ - int reg; - u32 val; - bool cur_state; - - reg = DPLL(pipe); - val = I915_READ(reg); - cur_state = !!(val & DPLL_VCO_ENABLE); - WARN(cur_state != state, - "PLL state assertion failure (expected %s, current %s)\n", - state_string(state), state_string(cur_state)); -} - -/* XXX: the dsi pll is shared between MIPI DSI ports */ -static void assert_dsi_pll(struct drm_i915_private *dev_priv, bool state) -{ - u32 val; - bool cur_state; - - mutex_lock(&dev_priv->dpio_lock); - val = vlv_cck_read(dev_priv, CCK_REG_DSI_PLL_CONTROL); - mutex_unlock(&dev_priv->dpio_lock); - - cur_state = val & DSI_PLL_VCO_EN; - WARN(cur_state != state, - "DSI PLL state assertion failure (expected %s, current %s)\n", - state_string(state), state_string(cur_state)); -} -#define assert_dsi_pll_enabled(d) assert_dsi_pll(d, true) -#define assert_dsi_pll_disabled(d) assert_dsi_pll(d, false) - -struct intel_shared_dpll * -intel_crtc_to_shared_dpll(struct intel_crtc *crtc) -{ - struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; - - if (crtc->config.shared_dpll < 0) - return NULL; - - return &dev_priv->shared_dplls[crtc->config.shared_dpll]; -} - -/* For ILK+ */ -void assert_shared_dpll(struct drm_i915_private *dev_priv, - struct intel_shared_dpll *pll, - bool state) -{ - bool cur_state; - struct intel_dpll_hw_state hw_state; - - if (HAS_PCH_LPT(dev_priv->dev)) { - DRM_DEBUG_DRIVER("LPT detected: skipping PCH PLL test\n"); - return; - } - - if (WARN (!pll, - "asserting DPLL %s with no DPLL\n", state_string(state))) - return; - - cur_state = pll->get_hw_state(dev_priv, pll, &hw_state); - WARN(cur_state != state, - "%s assertion failure (expected %s, current %s)\n", - pll->name, state_string(state), state_string(cur_state)); -} - -static void assert_panel_unlocked(struct drm_i915_private *dev_priv, - enum pipe pipe) -{ - int pp_reg, lvds_reg; - u32 val; - enum pipe panel_pipe = PIPE_A; - bool locked = true; - - if (HAS_PCH_SPLIT(dev_priv->dev)) { - pp_reg = PCH_PP_CONTROL; - lvds_reg = PCH_LVDS; - } else { - pp_reg = PP_CONTROL; - lvds_reg = LVDS; - } - - val = I915_READ(pp_reg); - if (!(val & PANEL_POWER_ON) || - ((val & PANEL_UNLOCK_REGS) == PANEL_UNLOCK_REGS)) - locked = false; - - if (I915_READ(lvds_reg) & LVDS_PIPEB_SELECT) - panel_pipe = PIPE_B; - - WARN(panel_pipe == pipe && locked, - "panel assertion failure, pipe %c regs locked\n", - pipe_name(pipe)); -} - void assert_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, bool state) { @@ -1101,21 +357,8 @@ static void assert_sprites_disabled(struct drm_i915_private *dev_priv, } } -static void ibx_assert_pch_refclk_enabled(struct drm_i915_private *dev_priv) -{ - u32 val; - bool enabled; - - WARN_ON(!(HAS_PCH_IBX(dev_priv->dev) || HAS_PCH_CPT(dev_priv->dev))); - - val = I915_READ(PCH_DREF_CONTROL); - enabled = !!(val & (DREF_SSC_SOURCE_MASK | DREF_NONSPREAD_SOURCE_MASK | - DREF_SUPERSPREAD_SOURCE_MASK)); - WARN(!enabled, "PCH refclk assertion failure, should be active but is disabled\n"); -} - -static void assert_pch_transcoder_disabled(struct drm_i915_private *dev_priv, - enum pipe pipe) +void assert_pch_transcoder_disabled(struct drm_i915_private *dev_priv, + enum pipe pipe) { int reg; u32 val; @@ -1285,129 +528,6 @@ static void intel_reset_dpio(struct drm_device *dev) I915_WRITE(DPIO_CTL, I915_READ(DPIO_CTL) | DPIO_CMNRST); } -static void vlv_enable_pll(struct intel_crtc *crtc) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - int reg = DPLL(crtc->pipe); - u32 dpll = crtc->config.dpll_hw_state.dpll; - - assert_pipe_disabled(dev_priv, crtc->pipe); - - /* No really, not for ILK+ */ - BUG_ON(!IS_VALLEYVIEW(dev_priv->dev)); - - /* PLL is protected by panel, make sure we can write it */ - if (IS_MOBILE(dev_priv->dev) && !IS_I830(dev_priv->dev)) - assert_panel_unlocked(dev_priv, crtc->pipe); - - I915_WRITE(reg, dpll); - POSTING_READ(reg); - udelay(150); - - if (wait_for(((I915_READ(reg) & DPLL_LOCK_VLV) == DPLL_LOCK_VLV), 1)) - DRM_ERROR("DPLL %d failed to lock\n", crtc->pipe); - - I915_WRITE(DPLL_MD(crtc->pipe), crtc->config.dpll_hw_state.dpll_md); - POSTING_READ(DPLL_MD(crtc->pipe)); - - /* We do this three times for luck */ - I915_WRITE(reg, dpll); - POSTING_READ(reg); - udelay(150); /* wait for warmup */ - I915_WRITE(reg, dpll); - POSTING_READ(reg); - udelay(150); /* wait for warmup */ - I915_WRITE(reg, dpll); - POSTING_READ(reg); - udelay(150); /* wait for warmup */ -} - -static void i9xx_enable_pll(struct intel_crtc *crtc) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - int reg = DPLL(crtc->pipe); - u32 dpll = crtc->config.dpll_hw_state.dpll; - - assert_pipe_disabled(dev_priv, crtc->pipe); - - /* No really, not for ILK+ */ - BUG_ON(INTEL_INFO(dev)->gen >= 5); - - /* PLL is protected by panel, make sure we can write it */ - if (IS_MOBILE(dev) && !IS_I830(dev)) - assert_panel_unlocked(dev_priv, crtc->pipe); - - I915_WRITE(reg, dpll); - - /* Wait for the clocks to stabilize. */ - POSTING_READ(reg); - udelay(150); - - if (INTEL_INFO(dev)->gen >= 4) { - I915_WRITE(DPLL_MD(crtc->pipe), - crtc->config.dpll_hw_state.dpll_md); - } else { - /* The pixel multiplier can only be updated once the - * DPLL is enabled and the clocks are stable. - * - * So write it again. - */ - I915_WRITE(reg, dpll); - } - - /* We do this three times for luck */ - I915_WRITE(reg, dpll); - POSTING_READ(reg); - udelay(150); /* wait for warmup */ - I915_WRITE(reg, dpll); - POSTING_READ(reg); - udelay(150); /* wait for warmup */ - I915_WRITE(reg, dpll); - POSTING_READ(reg); - udelay(150); /* wait for warmup */ -} - -/** - * i9xx_disable_pll - disable a PLL - * @dev_priv: i915 private structure - * @pipe: pipe PLL to disable - * - * Disable the PLL for @pipe, making sure the pipe is off first. - * - * Note! This is for pre-ILK only. - */ -static void i9xx_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) -{ - /* Don't disable pipe A or pipe A PLLs if needed */ - if (pipe == PIPE_A && (dev_priv->quirks & QUIRK_PIPEA_FORCE)) - return; - - /* Make sure the pipe isn't still relying on us */ - assert_pipe_disabled(dev_priv, pipe); - - I915_WRITE(DPLL(pipe), 0); - POSTING_READ(DPLL(pipe)); -} - -static void vlv_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) -{ - u32 val = 0; - - /* Make sure the pipe isn't still relying on us */ - assert_pipe_disabled(dev_priv, pipe); - - /* - * Leave integrated clock source and reference clock enabled for pipe B. - * The latter is needed for VGA hotplug / manual detection. - */ - if (pipe == PIPE_B) - val = DPLL_INTEGRATED_CRI_CLK_VLV | DPLL_REFA_CLK_ENABLE_VLV; - I915_WRITE(DPLL(pipe), val); - POSTING_READ(DPLL(pipe)); -} - void vlv_wait_port_ready(struct drm_i915_private *dev_priv, struct intel_digital_port *dport) { @@ -1429,77 +549,6 @@ void vlv_wait_port_ready(struct drm_i915_private *dev_priv, port_name(dport->port), I915_READ(DPLL(0))); } -/** - * ironlake_enable_shared_dpll - enable PCH PLL - * @dev_priv: i915 private structure - * @pipe: pipe PLL to enable - * - * The PCH PLL needs to be enabled before the PCH transcoder, since it - * drives the transcoder clock. - */ -static void ironlake_enable_shared_dpll(struct intel_crtc *crtc) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc); - - /* PCH PLLs only available on ILK, SNB and IVB */ - BUG_ON(INTEL_INFO(dev)->gen < 5); - if (WARN_ON(pll == NULL)) - return; - - if (WARN_ON(pll->refcount == 0)) - return; - - DRM_DEBUG_KMS("enable %s (active %d, on? %d)for crtc %d\n", - pll->name, pll->active, pll->on, - crtc->base.base.id); - - if (pll->active++) { - WARN_ON(!pll->on); - assert_shared_dpll_enabled(dev_priv, pll); - return; - } - WARN_ON(pll->on); - - DRM_DEBUG_KMS("enabling %s\n", pll->name); - pll->enable(dev_priv, pll); - pll->on = true; -} - -static void intel_disable_shared_dpll(struct intel_crtc *crtc) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc); - - /* PCH only available on ILK+ */ - BUG_ON(INTEL_INFO(dev)->gen < 5); - if (WARN_ON(pll == NULL)) - return; - - if (WARN_ON(pll->refcount == 0)) - return; - - DRM_DEBUG_KMS("disable %s (active %d, on? %d) for crtc %d\n", - pll->name, pll->active, pll->on, - crtc->base.base.id); - - if (WARN_ON(pll->active == 0)) { - assert_shared_dpll_disabled(dev_priv, pll); - return; - } - - assert_shared_dpll_enabled(dev_priv, pll); - WARN_ON(!pll->on); - if (--pll->active) - return; - - DRM_DEBUG_KMS("disabling %s\n", pll->name); - pll->disable(dev_priv, pll); - pll->on = false; -} - static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv, enum pipe pipe) { @@ -2693,98 +1742,6 @@ static void lpt_pch_enable(struct drm_crtc *crtc) lpt_enable_pch_transcoder(dev_priv, cpu_transcoder); } -static void intel_put_shared_dpll(struct intel_crtc *crtc) -{ - struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc); - - if (pll == NULL) - return; - - if (pll->refcount == 0) { - WARN(1, "bad %s refcount\n", pll->name); - return; - } - - if (--pll->refcount == 0) { - WARN_ON(pll->on); - WARN_ON(pll->active); - } - - crtc->config.shared_dpll = DPLL_ID_PRIVATE; -} - -static struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc) -{ - struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; - struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc); - enum intel_dpll_id i; - - if (pll) { - DRM_DEBUG_KMS("CRTC:%d dropping existing %s\n", - crtc->base.base.id, pll->name); - intel_put_shared_dpll(crtc); - } - - if (HAS_PCH_IBX(dev_priv->dev)) { - /* Ironlake PCH has a fixed PLL->PCH pipe mapping. */ - i = (enum intel_dpll_id) crtc->pipe; - pll = &dev_priv->shared_dplls[i]; - - DRM_DEBUG_KMS("CRTC:%d using pre-allocated %s\n", - crtc->base.base.id, pll->name); - - goto found; - } - - for (i = 0; i < dev_priv->num_shared_dpll; i++) { - pll = &dev_priv->shared_dplls[i]; - - /* Only want to check enabled timings first */ - if (pll->refcount == 0) - continue; - - if (memcmp(&crtc->config.dpll_hw_state, &pll->hw_state, - sizeof(pll->hw_state)) == 0) { - DRM_DEBUG_KMS("CRTC:%d sharing existing %s (refcount %d, ative %d)\n", - crtc->base.base.id, - pll->name, pll->refcount, pll->active); - - goto found; - } - } - - /* Ok no matching timings, maybe there's a free one? */ - for (i = 0; i < dev_priv->num_shared_dpll; i++) { - pll = &dev_priv->shared_dplls[i]; - if (pll->refcount == 0) { - DRM_DEBUG_KMS("CRTC:%d allocated %s\n", - crtc->base.base.id, pll->name); - goto found; - } - } - - return NULL; - -found: - crtc->config.shared_dpll = i; - DRM_DEBUG_DRIVER("using %s for pipe %c\n", pll->name, - pipe_name(crtc->pipe)); - - if (pll->active == 0) { - memcpy(&pll->hw_state, &crtc->config.dpll_hw_state, - sizeof(pll->hw_state)); - - DRM_DEBUG_DRIVER("setting up %s\n", pll->name); - WARN_ON(pll->on); - assert_shared_dpll_disabled(dev_priv, pll); - - pll->mode_set(dev_priv, pll); - } - pll->refcount++; - - return pll; -} - static void cpt_verify_modeset(struct drm_device *dev, int pipe) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -4166,388 +3123,65 @@ static void compute_m_n(unsigned int m, unsigned int n, uint32_t *ret_m, uint32_t *ret_n) { *ret_n = min_t(unsigned int, roundup_pow_of_two(n), DATA_LINK_N_MAX); - *ret_m = div_u64((uint64_t) m * *ret_n, n); - intel_reduce_m_n_ratio(ret_m, ret_n); -} - -void -intel_link_compute_m_n(int bits_per_pixel, int nlanes, - int pixel_clock, int link_clock, - struct intel_link_m_n *m_n) -{ - m_n->tu = 64; - - compute_m_n(bits_per_pixel * pixel_clock, - link_clock * nlanes * 8, - &m_n->gmch_m, &m_n->gmch_n); - - compute_m_n(pixel_clock, link_clock, - &m_n->link_m, &m_n->link_n); -} - -static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv) -{ - if (i915.panel_use_ssc >= 0) - return i915.panel_use_ssc != 0; - return dev_priv->vbt.lvds_use_ssc - && !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE); -} - -static int i9xx_get_refclk(struct drm_crtc *crtc, int num_connectors) -{ - struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - int refclk; - - if (IS_VALLEYVIEW(dev)) { - refclk = 100000; - } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && - intel_panel_use_ssc(dev_priv) && num_connectors < 2) { - refclk = dev_priv->vbt.lvds_ssc_freq; - DRM_DEBUG_KMS("using SSC reference clock of %d kHz\n", refclk); - } else if (!IS_GEN2(dev)) { - refclk = 96000; - } else { - refclk = 48000; - } - - return refclk; -} - -static uint32_t pnv_dpll_compute_fp(struct dpll *dpll) -{ - return (1 << dpll->n) << 16 | dpll->m2; -} - -static uint32_t i9xx_dpll_compute_fp(struct dpll *dpll) -{ - return dpll->n << 16 | dpll->m1 << 8 | dpll->m2; -} - -static void i9xx_update_pll_dividers(struct intel_crtc *crtc, - intel_clock_t *reduced_clock) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - int pipe = crtc->pipe; - u32 fp, fp2 = 0; - - if (IS_PINEVIEW(dev)) { - fp = pnv_dpll_compute_fp(&crtc->config.dpll); - if (reduced_clock) - fp2 = pnv_dpll_compute_fp(reduced_clock); - } else { - fp = i9xx_dpll_compute_fp(&crtc->config.dpll); - if (reduced_clock) - fp2 = i9xx_dpll_compute_fp(reduced_clock); - } - - I915_WRITE(FP0(pipe), fp); - crtc->config.dpll_hw_state.fp0 = fp; - - crtc->lowfreq_avail = false; - if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS) && - reduced_clock && i915.powersave) { - I915_WRITE(FP1(pipe), fp2); - crtc->config.dpll_hw_state.fp1 = fp2; - crtc->lowfreq_avail = true; - } else { - I915_WRITE(FP1(pipe), fp); - crtc->config.dpll_hw_state.fp1 = fp; - } -} - -static void vlv_pllb_recal_opamp(struct drm_i915_private *dev_priv, enum pipe - pipe) -{ - u32 reg_val; - - /* - * PLLB opamp always calibrates to max value of 0x3f, force enable it - * and set it to a reasonable value instead. - */ - reg_val = vlv_dpio_read(dev_priv, pipe, VLV_PLL_DW9(1)); - reg_val &= 0xffffff00; - reg_val |= 0x00000030; - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW9(1), reg_val); - - reg_val = vlv_dpio_read(dev_priv, pipe, VLV_REF_DW13); - reg_val &= 0x8cffffff; - reg_val = 0x8c000000; - vlv_dpio_write(dev_priv, pipe, VLV_REF_DW13, reg_val); - - reg_val = vlv_dpio_read(dev_priv, pipe, VLV_PLL_DW9(1)); - reg_val &= 0xffffff00; - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW9(1), reg_val); - - reg_val = vlv_dpio_read(dev_priv, pipe, VLV_REF_DW13); - reg_val &= 0x00ffffff; - reg_val |= 0xb0000000; - vlv_dpio_write(dev_priv, pipe, VLV_REF_DW13, reg_val); -} - -static void intel_pch_transcoder_set_m_n(struct intel_crtc *crtc, - struct intel_link_m_n *m_n) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - int pipe = crtc->pipe; - - I915_WRITE(PCH_TRANS_DATA_M1(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m); - I915_WRITE(PCH_TRANS_DATA_N1(pipe), m_n->gmch_n); - I915_WRITE(PCH_TRANS_LINK_M1(pipe), m_n->link_m); - I915_WRITE(PCH_TRANS_LINK_N1(pipe), m_n->link_n); -} - -static void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc, - struct intel_link_m_n *m_n) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - int pipe = crtc->pipe; - enum transcoder transcoder = crtc->config.cpu_transcoder; - - if (INTEL_INFO(dev)->gen >= 5) { - I915_WRITE(PIPE_DATA_M1(transcoder), TU_SIZE(m_n->tu) | m_n->gmch_m); - I915_WRITE(PIPE_DATA_N1(transcoder), m_n->gmch_n); - I915_WRITE(PIPE_LINK_M1(transcoder), m_n->link_m); - I915_WRITE(PIPE_LINK_N1(transcoder), m_n->link_n); - } else { - I915_WRITE(PIPE_DATA_M_G4X(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m); - I915_WRITE(PIPE_DATA_N_G4X(pipe), m_n->gmch_n); - I915_WRITE(PIPE_LINK_M_G4X(pipe), m_n->link_m); - I915_WRITE(PIPE_LINK_N_G4X(pipe), m_n->link_n); - } -} - -static void intel_dp_set_m_n(struct intel_crtc *crtc) -{ - if (crtc->config.has_pch_encoder) - intel_pch_transcoder_set_m_n(crtc, &crtc->config.dp_m_n); - else - intel_cpu_transcoder_set_m_n(crtc, &crtc->config.dp_m_n); -} - -static void vlv_update_pll(struct intel_crtc *crtc) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - int pipe = crtc->pipe; - u32 dpll, mdiv; - u32 bestn, bestm1, bestm2, bestp1, bestp2; - u32 coreclk, reg_val, dpll_md; - - mutex_lock(&dev_priv->dpio_lock); - - bestn = crtc->config.dpll.n; - bestm1 = crtc->config.dpll.m1; - bestm2 = crtc->config.dpll.m2; - bestp1 = crtc->config.dpll.p1; - bestp2 = crtc->config.dpll.p2; - - /* See eDP HDMI DPIO driver vbios notes doc */ - - /* PLL B needs special handling */ - if (pipe) - vlv_pllb_recal_opamp(dev_priv, pipe); - - /* Set up Tx target for periodic Rcomp update */ - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW9_BCAST, 0x0100000f); - - /* Disable target IRef on PLL */ - reg_val = vlv_dpio_read(dev_priv, pipe, VLV_PLL_DW8(pipe)); - reg_val &= 0x00ffffff; - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW8(pipe), reg_val); - - /* Disable fast lock */ - vlv_dpio_write(dev_priv, pipe, VLV_CMN_DW0, 0x610); - - /* Set idtafcrecal before PLL is enabled */ - mdiv = ((bestm1 << DPIO_M1DIV_SHIFT) | (bestm2 & DPIO_M2DIV_MASK)); - mdiv |= ((bestp1 << DPIO_P1_SHIFT) | (bestp2 << DPIO_P2_SHIFT)); - mdiv |= ((bestn << DPIO_N_SHIFT)); - mdiv |= (1 << DPIO_K_SHIFT); - - /* - * Post divider depends on pixel clock rate, DAC vs digital (and LVDS, - * but we don't support that). - * Note: don't use the DAC post divider as it seems unstable. - */ - mdiv |= (DPIO_POST_DIV_HDMIDP << DPIO_POST_DIV_SHIFT); - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW3(pipe), mdiv); - - mdiv |= DPIO_ENABLE_CALIBRATION; - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW3(pipe), mdiv); - - /* Set HBR and RBR LPF coefficients */ - if (crtc->config.port_clock == 162000 || - intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_ANALOG) || - intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI)) - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW10(pipe), - 0x009f0003); - else - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW10(pipe), - 0x00d0000f); - - if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP) || - intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT)) { - /* Use SSC source */ - if (!pipe) - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW5(pipe), - 0x0df40000); - else - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW5(pipe), - 0x0df70000); - } else { /* HDMI or VGA */ - /* Use bend source */ - if (!pipe) - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW5(pipe), - 0x0df70000); - else - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW5(pipe), - 0x0df40000); - } - - coreclk = vlv_dpio_read(dev_priv, pipe, VLV_PLL_DW7(pipe)); - coreclk = (coreclk & 0x0000ff00) | 0x01c00000; - if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT) || - intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP)) - coreclk |= 0x01000000; - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW7(pipe), coreclk); - - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW11(pipe), 0x87871000); - - /* - * Enable DPIO clock input. We should never disable the reference - * clock for pipe B, since VGA hotplug / manual detection depends - * on it. - */ - dpll = DPLL_EXT_BUFFER_ENABLE_VLV | DPLL_REFA_CLK_ENABLE_VLV | - DPLL_VGA_MODE_DIS | DPLL_INTEGRATED_CLOCK_VLV; - /* We should never disable this, set it here for state tracking */ - if (pipe == PIPE_B) - dpll |= DPLL_INTEGRATED_CRI_CLK_VLV; - dpll |= DPLL_VCO_ENABLE; - crtc->config.dpll_hw_state.dpll = dpll; - - dpll_md = (crtc->config.pixel_multiplier - 1) - << DPLL_MD_UDI_MULTIPLIER_SHIFT; - crtc->config.dpll_hw_state.dpll_md = dpll_md; - - mutex_unlock(&dev_priv->dpio_lock); -} - -static void i9xx_update_pll(struct intel_crtc *crtc, - intel_clock_t *reduced_clock, - int num_connectors) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - u32 dpll; - bool is_sdvo; - struct dpll *clock = &crtc->config.dpll; - - i9xx_update_pll_dividers(crtc, reduced_clock); - - is_sdvo = intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_SDVO) || - intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI); - - dpll = DPLL_VGA_MODE_DIS; - - if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS)) - dpll |= DPLLB_MODE_LVDS; - else - dpll |= DPLLB_MODE_DAC_SERIAL; - - if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) { - dpll |= (crtc->config.pixel_multiplier - 1) - << SDVO_MULTIPLIER_SHIFT_HIRES; - } - - if (is_sdvo) - dpll |= DPLL_SDVO_HIGH_SPEED; - - if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT)) - dpll |= DPLL_SDVO_HIGH_SPEED; - - /* compute bitmask from p1 value */ - if (IS_PINEVIEW(dev)) - dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW; - else { - dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; - if (IS_G4X(dev) && reduced_clock) - dpll |= (1 << (reduced_clock->p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT; - } - switch (clock->p2) { - case 5: - dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5; - break; - case 7: - dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7; - break; - case 10: - dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10; - break; - case 14: - dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14; - break; - } - if (INTEL_INFO(dev)->gen >= 4) - dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT); + *ret_m = div_u64((uint64_t) m * *ret_n, n); + intel_reduce_m_n_ratio(ret_m, ret_n); +} - if (crtc->config.sdvo_tv_clock) - dpll |= PLL_REF_INPUT_TVCLKINBC; - else if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS) && - intel_panel_use_ssc(dev_priv) && num_connectors < 2) - dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; - else - dpll |= PLL_REF_INPUT_DREFCLK; +void +intel_link_compute_m_n(int bits_per_pixel, int nlanes, + int pixel_clock, int link_clock, + struct intel_link_m_n *m_n) +{ + m_n->tu = 64; - dpll |= DPLL_VCO_ENABLE; - crtc->config.dpll_hw_state.dpll = dpll; + compute_m_n(bits_per_pixel * pixel_clock, + link_clock * nlanes * 8, + &m_n->gmch_m, &m_n->gmch_n); - if (INTEL_INFO(dev)->gen >= 4) { - u32 dpll_md = (crtc->config.pixel_multiplier - 1) - << DPLL_MD_UDI_MULTIPLIER_SHIFT; - crtc->config.dpll_hw_state.dpll_md = dpll_md; - } + compute_m_n(pixel_clock, link_clock, + &m_n->link_m, &m_n->link_n); } -static void i8xx_update_pll(struct intel_crtc *crtc, - intel_clock_t *reduced_clock, - int num_connectors) +static void intel_pch_transcoder_set_m_n(struct intel_crtc *crtc, + struct intel_link_m_n *m_n) { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 dpll; - struct dpll *clock = &crtc->config.dpll; + int pipe = crtc->pipe; - i9xx_update_pll_dividers(crtc, reduced_clock); + I915_WRITE(PCH_TRANS_DATA_M1(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m); + I915_WRITE(PCH_TRANS_DATA_N1(pipe), m_n->gmch_n); + I915_WRITE(PCH_TRANS_LINK_M1(pipe), m_n->link_m); + I915_WRITE(PCH_TRANS_LINK_N1(pipe), m_n->link_n); +} - dpll = DPLL_VGA_MODE_DIS; +static void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc, + struct intel_link_m_n *m_n) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int pipe = crtc->pipe; + enum transcoder transcoder = crtc->config.cpu_transcoder; - if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS)) { - dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; + if (INTEL_INFO(dev)->gen >= 5) { + I915_WRITE(PIPE_DATA_M1(transcoder), TU_SIZE(m_n->tu) | m_n->gmch_m); + I915_WRITE(PIPE_DATA_N1(transcoder), m_n->gmch_n); + I915_WRITE(PIPE_LINK_M1(transcoder), m_n->link_m); + I915_WRITE(PIPE_LINK_N1(transcoder), m_n->link_n); } else { - if (clock->p1 == 2) - dpll |= PLL_P1_DIVIDE_BY_TWO; - else - dpll |= (clock->p1 - 2) << DPLL_FPA01_P1_POST_DIV_SHIFT; - if (clock->p2 == 4) - dpll |= PLL_P2_DIVIDE_BY_4; + I915_WRITE(PIPE_DATA_M_G4X(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m); + I915_WRITE(PIPE_DATA_N_G4X(pipe), m_n->gmch_n); + I915_WRITE(PIPE_LINK_M_G4X(pipe), m_n->link_m); + I915_WRITE(PIPE_LINK_N_G4X(pipe), m_n->link_n); } +} - if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DVO)) - dpll |= DPLL_DVO_2X_MODE; - - if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS) && - intel_panel_use_ssc(dev_priv) && num_connectors < 2) - dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; +static void intel_dp_set_m_n(struct intel_crtc *crtc) +{ + if (crtc->config.has_pch_encoder) + intel_pch_transcoder_set_m_n(crtc, &crtc->config.dp_m_n); else - dpll |= PLL_REF_INPUT_DREFCLK; - - dpll |= DPLL_VCO_ENABLE; - crtc->config.dpll_hw_state.dpll = dpll; + intel_cpu_transcoder_set_m_n(crtc, &crtc->config.dp_m_n); } static void intel_set_pipe_timings(struct intel_crtc *intel_crtc) @@ -4892,32 +3526,6 @@ static void i9xx_get_pfit_config(struct intel_crtc *crtc, I915_READ(LVDS) & LVDS_BORDER_ENABLE; } -static void vlv_crtc_clock_get(struct intel_crtc *crtc, - struct intel_crtc_config *pipe_config) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - int pipe = pipe_config->cpu_transcoder; - intel_clock_t clock; - u32 mdiv; - int refclk = 100000; - - mutex_lock(&dev_priv->dpio_lock); - mdiv = vlv_dpio_read(dev_priv, pipe, VLV_PLL_DW3(pipe)); - mutex_unlock(&dev_priv->dpio_lock); - - clock.m1 = (mdiv >> DPIO_M1DIV_SHIFT) & 7; - clock.m2 = mdiv & DPIO_M2DIV_MASK; - clock.n = (mdiv >> DPIO_N_SHIFT) & 0xf; - clock.p1 = (mdiv >> DPIO_P1_SHIFT) & 7; - clock.p2 = (mdiv >> DPIO_P2_SHIFT) & 0x1f; - - vlv_clock(refclk, &clock); - - /* clock.dot is the fast clock */ - pipe_config->port_clock = clock.dot / 5; -} - static void i9xx_get_plane_config(struct intel_crtc *crtc, struct intel_plane_config *plane_config) { @@ -5407,32 +4015,6 @@ void intel_init_pch_refclk(struct drm_device *dev) lpt_init_pch_refclk(dev); } -static int ironlake_get_refclk(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_encoder *encoder; - int num_connectors = 0; - bool is_lvds = false; - - for_each_encoder_on_crtc(dev, crtc, encoder) { - switch (encoder->type) { - case INTEL_OUTPUT_LVDS: - is_lvds = true; - break; - } - num_connectors++; - } - - if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) { - DRM_DEBUG_KMS("using SSC reference clock of %d kHz\n", - dev_priv->vbt.lvds_ssc_freq); - return dev_priv->vbt.lvds_ssc_freq; - } - - return 120000; -} - static void ironlake_set_pipeconf(struct drm_crtc *crtc) { struct drm_i915_private *dev_priv = crtc->dev->dev_private; @@ -5654,96 +4236,6 @@ int ironlake_get_lanes_required(int target_clock, int link_bw, int bpp) return DIV_ROUND_UP(bps, link_bw * 8); } -static bool ironlake_needs_fb_cb_tune(struct dpll *dpll, int factor) -{ - return i9xx_dpll_compute_m(dpll) < factor * dpll->n; -} - -static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, - u32 *fp, - intel_clock_t *reduced_clock, u32 *fp2) -{ - struct drm_crtc *crtc = &intel_crtc->base; - struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_encoder *intel_encoder; - uint32_t dpll; - int factor, num_connectors = 0; - bool is_lvds = false, is_sdvo = false; - - for_each_encoder_on_crtc(dev, crtc, intel_encoder) { - switch (intel_encoder->type) { - case INTEL_OUTPUT_LVDS: - is_lvds = true; - break; - case INTEL_OUTPUT_SDVO: - case INTEL_OUTPUT_HDMI: - is_sdvo = true; - break; - } - - num_connectors++; - } - - /* Enable autotuning of the PLL clock (if permissible) */ - factor = 21; - if (is_lvds) { - if ((intel_panel_use_ssc(dev_priv) && - dev_priv->vbt.lvds_ssc_freq == 100000) || - (HAS_PCH_IBX(dev) && intel_is_dual_link_lvds(dev))) - factor = 25; - } else if (intel_crtc->config.sdvo_tv_clock) - factor = 20; - - if (ironlake_needs_fb_cb_tune(&intel_crtc->config.dpll, factor)) - *fp |= FP_CB_TUNE; - - if (fp2 && (reduced_clock->m < factor * reduced_clock->n)) - *fp2 |= FP_CB_TUNE; - - dpll = 0; - - if (is_lvds) - dpll |= DPLLB_MODE_LVDS; - else - dpll |= DPLLB_MODE_DAC_SERIAL; - - dpll |= (intel_crtc->config.pixel_multiplier - 1) - << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT; - - if (is_sdvo) - dpll |= DPLL_SDVO_HIGH_SPEED; - if (intel_crtc->config.has_dp_encoder) - dpll |= DPLL_SDVO_HIGH_SPEED; - - /* compute bitmask from p1 value */ - dpll |= (1 << (intel_crtc->config.dpll.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; - /* also FPA1 */ - dpll |= (1 << (intel_crtc->config.dpll.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT; - - switch (intel_crtc->config.dpll.p2) { - case 5: - dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5; - break; - case 7: - dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7; - break; - case 10: - dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10; - break; - case 14: - dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14; - break; - } - - if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) - dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; - else - dpll |= PLL_REF_INPUT_DREFCLK; - - return dpll | DPLL_VCO_ENABLE; -} - static int ironlake_crtc_mode_set(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *fb) @@ -6732,111 +5224,6 @@ void intel_release_load_detect_pipe(struct drm_connector *connector, mutex_unlock(&crtc->mutex); } -static int i9xx_pll_refclk(struct drm_device *dev, - const struct intel_crtc_config *pipe_config) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - u32 dpll = pipe_config->dpll_hw_state.dpll; - - if ((dpll & PLL_REF_INPUT_MASK) == PLLB_REF_INPUT_SPREADSPECTRUMIN) - return dev_priv->vbt.lvds_ssc_freq; - else if (HAS_PCH_SPLIT(dev)) - return 120000; - else if (!IS_GEN2(dev)) - return 96000; - else - return 48000; -} - -/* Returns the clock of the currently programmed mode of the given pipe. */ -static void i9xx_crtc_clock_get(struct intel_crtc *crtc, - struct intel_crtc_config *pipe_config) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - int pipe = pipe_config->cpu_transcoder; - u32 dpll = pipe_config->dpll_hw_state.dpll; - u32 fp; - intel_clock_t clock; - int refclk = i9xx_pll_refclk(dev, pipe_config); - - if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) - fp = pipe_config->dpll_hw_state.fp0; - else - fp = pipe_config->dpll_hw_state.fp1; - - clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT; - if (IS_PINEVIEW(dev)) { - clock.n = ffs((fp & FP_N_PINEVIEW_DIV_MASK) >> FP_N_DIV_SHIFT) - 1; - clock.m2 = (fp & FP_M2_PINEVIEW_DIV_MASK) >> FP_M2_DIV_SHIFT; - } else { - clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT; - clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT; - } - - if (!IS_GEN2(dev)) { - if (IS_PINEVIEW(dev)) - clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_PINEVIEW) >> - DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW); - else - clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK) >> - DPLL_FPA01_P1_POST_DIV_SHIFT); - - switch (dpll & DPLL_MODE_MASK) { - case DPLLB_MODE_DAC_SERIAL: - clock.p2 = dpll & DPLL_DAC_SERIAL_P2_CLOCK_DIV_5 ? - 5 : 10; - break; - case DPLLB_MODE_LVDS: - clock.p2 = dpll & DPLLB_LVDS_P2_CLOCK_DIV_7 ? - 7 : 14; - break; - default: - DRM_DEBUG_KMS("Unknown DPLL mode %08x in programmed " - "mode\n", (int)(dpll & DPLL_MODE_MASK)); - return; - } - - if (IS_PINEVIEW(dev)) - pineview_clock(refclk, &clock); - else - i9xx_clock(refclk, &clock); - } else { - u32 lvds = IS_I830(dev) ? 0 : I915_READ(LVDS); - bool is_lvds = (pipe == 1) && (lvds & LVDS_PORT_EN); - - if (is_lvds) { - clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >> - DPLL_FPA01_P1_POST_DIV_SHIFT); - - if (lvds & LVDS_CLKB_POWER_UP) - clock.p2 = 7; - else - clock.p2 = 14; - } else { - if (dpll & PLL_P1_DIVIDE_BY_TWO) - clock.p1 = 2; - else { - clock.p1 = ((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830) >> - DPLL_FPA01_P1_POST_DIV_SHIFT) + 2; - } - if (dpll & PLL_P2_DIVIDE_BY_4) - clock.p2 = 4; - else - clock.p2 = 2; - } - - i9xx_clock(refclk, &clock); - } - - /* - * This value includes pixel_multiplier. We will use - * port_clock to compute adjusted_mode.crtc_clock in the - * encoder's get_config() function. - */ - pipe_config->port_clock = clock.dot; -} - int intel_dotclock_calculate(int link_freq, const struct intel_link_m_n *m_n) { @@ -6856,25 +5243,6 @@ int intel_dotclock_calculate(int link_freq, return div_u64((u64)m_n->link_m * link_freq, m_n->link_n); } -static void ironlake_pch_clock_get(struct intel_crtc *crtc, - struct intel_crtc_config *pipe_config) -{ - struct drm_device *dev = crtc->base.dev; - - /* read out port_clock from the DPLL */ - i9xx_crtc_clock_get(crtc, pipe_config); - - /* - * This value does not include pixel_multiplier. - * We will check that port_clock and adjusted_mode.crtc_clock - * agree once we know their relationship in the encoder's - * get_config() function. - */ - pipe_config->adjusted_mode.crtc_clock = - intel_dotclock_calculate(intel_fdi_link_freq(dev) * 10000, - &pipe_config->fdi_m_n); -} - /** Returns the currently programmed mode of the given pipe. */ struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev, struct drm_crtc *crtc) @@ -6923,73 +5291,6 @@ struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev, return mode; } -static void intel_increase_pllclock(struct drm_crtc *crtc) -{ - 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 dpll_reg = DPLL(pipe); - int dpll; - - if (HAS_PCH_SPLIT(dev)) - return; - - if (!dev_priv->lvds_downclock_avail) - return; - - dpll = I915_READ(dpll_reg); - if (!HAS_PIPE_CXSR(dev) && (dpll & DISPLAY_RATE_SELECT_FPA1)) { - DRM_DEBUG_DRIVER("upclocking LVDS\n"); - - assert_panel_unlocked(dev_priv, pipe); - - dpll &= ~DISPLAY_RATE_SELECT_FPA1; - I915_WRITE(dpll_reg, dpll); - intel_wait_for_vblank(dev, pipe); - - dpll = I915_READ(dpll_reg); - if (dpll & DISPLAY_RATE_SELECT_FPA1) - DRM_DEBUG_DRIVER("failed to upclock LVDS!\n"); - } -} - -static void intel_decrease_pllclock(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - - if (HAS_PCH_SPLIT(dev)) - return; - - if (!dev_priv->lvds_downclock_avail) - 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) { - int pipe = intel_crtc->pipe; - int dpll_reg = DPLL(pipe); - int dpll; - - DRM_DEBUG_DRIVER("downclocking LVDS\n"); - - assert_panel_unlocked(dev_priv, pipe); - - dpll = I915_READ(dpll_reg); - dpll |= DISPLAY_RATE_SELECT_FPA1; - I915_WRITE(dpll_reg, dpll); - intel_wait_for_vblank(dev, pipe); - dpll = I915_READ(dpll_reg); - if (!(dpll & DISPLAY_RATE_SELECT_FPA1)) - DRM_DEBUG_DRIVER("failed to downclock LVDS!\n"); - } - -} - void intel_mark_busy(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -9031,107 +7332,6 @@ static const struct drm_crtc_funcs intel_crtc_funcs = { .page_flip = intel_crtc_page_flip, }; -static void intel_cpu_pll_init(struct drm_device *dev) -{ - if (HAS_DDI(dev)) - intel_ddi_pll_init(dev); -} - -static bool ibx_pch_dpll_get_hw_state(struct drm_i915_private *dev_priv, - struct intel_shared_dpll *pll, - struct intel_dpll_hw_state *hw_state) -{ - uint32_t val; - - val = I915_READ(PCH_DPLL(pll->id)); - hw_state->dpll = val; - hw_state->fp0 = I915_READ(PCH_FP0(pll->id)); - hw_state->fp1 = I915_READ(PCH_FP1(pll->id)); - - return val & DPLL_VCO_ENABLE; -} - -static void ibx_pch_dpll_mode_set(struct drm_i915_private *dev_priv, - struct intel_shared_dpll *pll) -{ - I915_WRITE(PCH_FP0(pll->id), pll->hw_state.fp0); - I915_WRITE(PCH_FP1(pll->id), pll->hw_state.fp1); -} - -static void ibx_pch_dpll_enable(struct drm_i915_private *dev_priv, - struct intel_shared_dpll *pll) -{ - /* PCH refclock must be enabled first */ - ibx_assert_pch_refclk_enabled(dev_priv); - - I915_WRITE(PCH_DPLL(pll->id), pll->hw_state.dpll); - - /* Wait for the clocks to stabilize. */ - POSTING_READ(PCH_DPLL(pll->id)); - udelay(150); - - /* The pixel multiplier can only be updated once the - * DPLL is enabled and the clocks are stable. - * - * So write it again. - */ - I915_WRITE(PCH_DPLL(pll->id), pll->hw_state.dpll); - POSTING_READ(PCH_DPLL(pll->id)); - udelay(200); -} - -static void ibx_pch_dpll_disable(struct drm_i915_private *dev_priv, - struct intel_shared_dpll *pll) -{ - struct drm_device *dev = dev_priv->dev; - struct intel_crtc *crtc; - - /* Make sure no transcoder isn't still depending on us. */ - list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) { - if (intel_crtc_to_shared_dpll(crtc) == pll) - assert_pch_transcoder_disabled(dev_priv, crtc->pipe); - } - - I915_WRITE(PCH_DPLL(pll->id), 0); - POSTING_READ(PCH_DPLL(pll->id)); - udelay(200); -} - -static char *ibx_pch_dpll_names[] = { - "PCH DPLL A", - "PCH DPLL B", -}; - -static void ibx_pch_dpll_init(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - int i; - - dev_priv->num_shared_dpll = 2; - - for (i = 0; i < dev_priv->num_shared_dpll; i++) { - dev_priv->shared_dplls[i].id = i; - dev_priv->shared_dplls[i].name = ibx_pch_dpll_names[i]; - dev_priv->shared_dplls[i].mode_set = ibx_pch_dpll_mode_set; - dev_priv->shared_dplls[i].enable = ibx_pch_dpll_enable; - dev_priv->shared_dplls[i].disable = ibx_pch_dpll_disable; - dev_priv->shared_dplls[i].get_hw_state = - ibx_pch_dpll_get_hw_state; - } -} - -static void intel_shared_dpll_init(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - - if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) - ibx_pch_dpll_init(dev); - else - dev_priv->num_shared_dpll = 0; - - BUG_ON(dev_priv->num_shared_dpll > I915_NUM_PLLS); -} - static void intel_crtc_init(struct drm_device *dev, int pipe) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -9561,15 +7761,6 @@ static void intel_init_display(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - if (HAS_PCH_SPLIT(dev) || IS_G4X(dev)) - dev_priv->display.find_dpll = g4x_find_best_dpll; - else if (IS_VALLEYVIEW(dev)) - dev_priv->display.find_dpll = vlv_find_best_dpll; - else if (IS_PINEVIEW(dev)) - dev_priv->display.find_dpll = pnv_find_best_dpll; - else - dev_priv->display.find_dpll = i9xx_find_best_dpll; - if (HAS_DDI(dev)) { dev_priv->display.get_pipe_config = haswell_get_pipe_config; dev_priv->display.get_plane_config = ironlake_get_plane_config; @@ -9652,6 +7843,7 @@ static void intel_init_display(struct drm_device *dev) valleyview_modeset_global_resources; } + intel_pll_init(dev_priv); intel_fdi_init(dev_priv); intel_eld_init(dev_priv); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 013ddf5..aa93f1a 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -728,16 +728,6 @@ __intel_framebuffer_create(struct drm_device *dev, void intel_prepare_page_flip(struct drm_device *dev, int plane); void intel_finish_page_flip(struct drm_device *dev, int pipe); void intel_finish_page_flip_plane(struct drm_device *dev, int plane); -struct intel_shared_dpll *intel_crtc_to_shared_dpll(struct intel_crtc *crtc); -void assert_shared_dpll(struct drm_i915_private *dev_priv, - struct intel_shared_dpll *pll, - bool state); -#define assert_shared_dpll_enabled(d, p) assert_shared_dpll(d, p, true) -#define assert_shared_dpll_disabled(d, p) assert_shared_dpll(d, p, false) -void assert_pll(struct drm_i915_private *dev_priv, - enum pipe pipe, bool state); -#define assert_pll_enabled(d, p) assert_pll(d, p, true) -#define assert_pll_disabled(d, p) assert_pll(d, p, false) void assert_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, bool state); #define assert_pipe_enabled(d, p) assert_pipe(d, p, true) #define assert_pipe_disabled(d, p) assert_pipe(d, p, false) @@ -770,6 +760,9 @@ void intel_mode_from_pipe_config(struct drm_display_mode *mode, int intel_format_to_fourcc(int format); bool intel_pipe_has_type(struct drm_crtc *crtc, int type); bool need_vtd_wa(struct drm_device *dev); +void assert_pch_transcoder_disabled(struct drm_i915_private *dev_priv, + enum pipe pipe); + /* intel_dp.c */ void intel_dp_init(struct drm_device *dev, int output_reg, enum port port); @@ -926,6 +919,70 @@ extern struct drm_display_mode *intel_find_panel_downclock( struct drm_device *dev, struct drm_display_mode *fixed_mode, struct drm_connector *connector); +bool intel_panel_use_ssc(struct drm_i915_private *dev_priv); +void assert_panel_unlocked(struct drm_i915_private *dev_priv, enum pipe pipe); + + +/* intel_pll.c */ +typedef struct { + int min, max; +} intel_range_t; + +typedef struct { + int dot_limit; + int p2_slow, p2_fast; +} intel_p2_t; + +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; +}; + +const intel_limit_t *intel_limit(struct drm_crtc *crtc, int refclk); +void vlv_crtc_clock_get(struct intel_crtc *crtc, + struct intel_crtc_config *pipe_config); +void i9xx_crtc_clock_get(struct intel_crtc *crtc, + struct intel_crtc_config *pipe_config); +void ironlake_pch_clock_get(struct intel_crtc *crtc, + struct intel_crtc_config *pipe_config); +uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, + u32 *fp, intel_clock_t *reduced_clock, u32 *fp2); +int i9xx_get_refclk(struct drm_crtc *crtc, int num_connectors); +int ironlake_get_refclk(struct drm_crtc *crtc); +void intel_pll_init(struct drm_i915_private *dev_priv); +void assert_pll(struct drm_i915_private *dev_priv, + enum pipe pipe, bool state); +#define assert_pll_enabled(d, p) assert_pll(d, p, true) +#define assert_pll_disabled(d, p) assert_pll(d, p, false) +void assert_dsi_pll(struct drm_i915_private *dev_priv, bool state); +#define assert_dsi_pll_enabled(d) assert_dsi_pll(d, true) +#define assert_dsi_pll_disabled(d) assert_dsi_pll(d, false) +struct intel_shared_dpll *intel_crtc_to_shared_dpll(struct intel_crtc *crtc); +void assert_shared_dpll(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll, + bool state); +#define assert_shared_dpll_enabled(d, p) assert_shared_dpll(d, p, true) +#define assert_shared_dpll_disabled(d, p) assert_shared_dpll(d, p, false) +void ironlake_enable_shared_dpll(struct intel_crtc *crtc); +void intel_disable_shared_dpll(struct intel_crtc *crtc); +void intel_put_shared_dpll(struct intel_crtc *crtc); +struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc); +void intel_increase_pllclock(struct drm_crtc *crtc); +void intel_decrease_pllclock(struct drm_crtc *crtc); +void vlv_enable_pll(struct intel_crtc *crtc); +void i9xx_enable_pll(struct intel_crtc *crtc); +void i9xx_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe); +void vlv_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe); +void vlv_update_pll(struct intel_crtc *crtc); +void i9xx_update_pll(struct intel_crtc *crtc, intel_clock_t *reduced_clock, + int num_connectors); +void i8xx_update_pll(struct intel_crtc *crtc, intel_clock_t *reduced_clock, + int num_connectors); +uint32_t i9xx_dpll_compute_fp(struct dpll *dpll); +void intel_cpu_pll_init(struct drm_device *dev); +void intel_shared_dpll_init(struct drm_device *dev); + /* intel_pm.c */ void intel_init_clock_gating(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index cb05840..ec764e3 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -1189,6 +1189,42 @@ void intel_panel_init_backlight_funcs(struct drm_device *dev) } } +bool intel_panel_use_ssc(struct drm_i915_private *dev_priv) +{ + if (i915.panel_use_ssc >= 0) + return i915.panel_use_ssc != 0; + return dev_priv->vbt.lvds_use_ssc + && !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE); +} + +void assert_panel_unlocked(struct drm_i915_private *dev_priv, enum pipe pipe) +{ + int pp_reg, lvds_reg; + u32 val; + enum pipe panel_pipe = PIPE_A; + bool locked = true; + + if (HAS_PCH_SPLIT(dev_priv->dev)) { + pp_reg = PCH_PP_CONTROL; + lvds_reg = PCH_LVDS; + } else { + pp_reg = PP_CONTROL; + lvds_reg = LVDS; + } + + val = I915_READ(pp_reg); + if (!(val & PANEL_POWER_ON) || + ((val & PANEL_UNLOCK_REGS) == PANEL_UNLOCK_REGS)) + locked = false; + + if (I915_READ(lvds_reg) & LVDS_PIPEB_SELECT) + panel_pipe = PIPE_B; + + WARN(panel_pipe == pipe && locked, + "panel assertion failure, pipe %c regs locked\n", + pipe_name(pipe)); +} + int intel_panel_init(struct intel_panel *panel, struct drm_display_mode *fixed_mode, struct drm_display_mode *downclock_mode) diff --git a/drivers/gpu/drm/i915/intel_pll.c b/drivers/gpu/drm/i915/intel_pll.c new file mode 100644 index 0000000..fed6ba7 --- /dev/null +++ b/drivers/gpu/drm/i915/intel_pll.c @@ -0,0 +1,1779 @@ +/* + * Copyright © 2006-2007, 2014 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 "intel_drv.h" + +static const intel_limit_t intel_limits_i8xx_dac = { + .dot = { .min = 25000, .max = 350000 }, + .vco = { .min = 908000, .max = 1512000 }, + .n = { .min = 2, .max = 16 }, + .m = { .min = 96, .max = 140 }, + .m1 = { .min = 18, .max = 26 }, + .m2 = { .min = 6, .max = 16 }, + .p = { .min = 4, .max = 128 }, + .p1 = { .min = 2, .max = 33 }, + .p2 = { .dot_limit = 165000, + .p2_slow = 4, .p2_fast = 2 }, +}; + +static const intel_limit_t intel_limits_i8xx_dvo = { + .dot = { .min = 25000, .max = 350000 }, + .vco = { .min = 908000, .max = 1512000 }, + .n = { .min = 2, .max = 16 }, + .m = { .min = 96, .max = 140 }, + .m1 = { .min = 18, .max = 26 }, + .m2 = { .min = 6, .max = 16 }, + .p = { .min = 4, .max = 128 }, + .p1 = { .min = 2, .max = 33 }, + .p2 = { .dot_limit = 165000, + .p2_slow = 4, .p2_fast = 4 }, +}; + +static const intel_limit_t intel_limits_i8xx_lvds = { + .dot = { .min = 25000, .max = 350000 }, + .vco = { .min = 908000, .max = 1512000 }, + .n = { .min = 2, .max = 16 }, + .m = { .min = 96, .max = 140 }, + .m1 = { .min = 18, .max = 26 }, + .m2 = { .min = 6, .max = 16 }, + .p = { .min = 4, .max = 128 }, + .p1 = { .min = 1, .max = 6 }, + .p2 = { .dot_limit = 165000, + .p2_slow = 14, .p2_fast = 7 }, +}; + +static const intel_limit_t intel_limits_i9xx_sdvo = { + .dot = { .min = 20000, .max = 400000 }, + .vco = { .min = 1400000, .max = 2800000 }, + .n = { .min = 1, .max = 6 }, + .m = { .min = 70, .max = 120 }, + .m1 = { .min = 8, .max = 18 }, + .m2 = { .min = 3, .max = 7 }, + .p = { .min = 5, .max = 80 }, + .p1 = { .min = 1, .max = 8 }, + .p2 = { .dot_limit = 200000, + .p2_slow = 10, .p2_fast = 5 }, +}; + +static const intel_limit_t intel_limits_i9xx_lvds = { + .dot = { .min = 20000, .max = 400000 }, + .vco = { .min = 1400000, .max = 2800000 }, + .n = { .min = 1, .max = 6 }, + .m = { .min = 70, .max = 120 }, + .m1 = { .min = 8, .max = 18 }, + .m2 = { .min = 3, .max = 7 }, + .p = { .min = 7, .max = 98 }, + .p1 = { .min = 1, .max = 8 }, + .p2 = { .dot_limit = 112000, + .p2_slow = 14, .p2_fast = 7 }, +}; + + +static const intel_limit_t intel_limits_g4x_sdvo = { + .dot = { .min = 25000, .max = 270000 }, + .vco = { .min = 1750000, .max = 3500000}, + .n = { .min = 1, .max = 4 }, + .m = { .min = 104, .max = 138 }, + .m1 = { .min = 17, .max = 23 }, + .m2 = { .min = 5, .max = 11 }, + .p = { .min = 10, .max = 30 }, + .p1 = { .min = 1, .max = 3}, + .p2 = { .dot_limit = 270000, + .p2_slow = 10, + .p2_fast = 10 + }, +}; + +static const intel_limit_t intel_limits_g4x_hdmi = { + .dot = { .min = 22000, .max = 400000 }, + .vco = { .min = 1750000, .max = 3500000}, + .n = { .min = 1, .max = 4 }, + .m = { .min = 104, .max = 138 }, + .m1 = { .min = 16, .max = 23 }, + .m2 = { .min = 5, .max = 11 }, + .p = { .min = 5, .max = 80 }, + .p1 = { .min = 1, .max = 8}, + .p2 = { .dot_limit = 165000, + .p2_slow = 10, .p2_fast = 5 }, +}; + +static const intel_limit_t intel_limits_g4x_single_channel_lvds = { + .dot = { .min = 20000, .max = 115000 }, + .vco = { .min = 1750000, .max = 3500000 }, + .n = { .min = 1, .max = 3 }, + .m = { .min = 104, .max = 138 }, + .m1 = { .min = 17, .max = 23 }, + .m2 = { .min = 5, .max = 11 }, + .p = { .min = 28, .max = 112 }, + .p1 = { .min = 2, .max = 8 }, + .p2 = { .dot_limit = 0, + .p2_slow = 14, .p2_fast = 14 + }, +}; + +static const intel_limit_t intel_limits_g4x_dual_channel_lvds = { + .dot = { .min = 80000, .max = 224000 }, + .vco = { .min = 1750000, .max = 3500000 }, + .n = { .min = 1, .max = 3 }, + .m = { .min = 104, .max = 138 }, + .m1 = { .min = 17, .max = 23 }, + .m2 = { .min = 5, .max = 11 }, + .p = { .min = 14, .max = 42 }, + .p1 = { .min = 2, .max = 6 }, + .p2 = { .dot_limit = 0, + .p2_slow = 7, .p2_fast = 7 + }, +}; + +static const intel_limit_t intel_limits_pineview_sdvo = { + .dot = { .min = 20000, .max = 400000}, + .vco = { .min = 1700000, .max = 3500000 }, + /* Pineview's Ncounter is a ring counter */ + .n = { .min = 3, .max = 6 }, + .m = { .min = 2, .max = 256 }, + /* Pineview only has one combined m divider, which we treat as m2. */ + .m1 = { .min = 0, .max = 0 }, + .m2 = { .min = 0, .max = 254 }, + .p = { .min = 5, .max = 80 }, + .p1 = { .min = 1, .max = 8 }, + .p2 = { .dot_limit = 200000, + .p2_slow = 10, .p2_fast = 5 }, +}; + +static const intel_limit_t intel_limits_pineview_lvds = { + .dot = { .min = 20000, .max = 400000 }, + .vco = { .min = 1700000, .max = 3500000 }, + .n = { .min = 3, .max = 6 }, + .m = { .min = 2, .max = 256 }, + .m1 = { .min = 0, .max = 0 }, + .m2 = { .min = 0, .max = 254 }, + .p = { .min = 7, .max = 112 }, + .p1 = { .min = 1, .max = 8 }, + .p2 = { .dot_limit = 112000, + .p2_slow = 14, .p2_fast = 14 }, +}; + +/* Ironlake / Sandybridge + * + * We calculate clock using (register_value + 2) for N/M1/M2, so here + * the range value for them is (actual_value - 2). + */ +static const intel_limit_t intel_limits_ironlake_dac = { + .dot = { .min = 25000, .max = 350000 }, + .vco = { .min = 1760000, .max = 3510000 }, + .n = { .min = 1, .max = 5 }, + .m = { .min = 79, .max = 127 }, + .m1 = { .min = 12, .max = 22 }, + .m2 = { .min = 5, .max = 9 }, + .p = { .min = 5, .max = 80 }, + .p1 = { .min = 1, .max = 8 }, + .p2 = { .dot_limit = 225000, + .p2_slow = 10, .p2_fast = 5 }, +}; + +static const intel_limit_t intel_limits_ironlake_single_lvds = { + .dot = { .min = 25000, .max = 350000 }, + .vco = { .min = 1760000, .max = 3510000 }, + .n = { .min = 1, .max = 3 }, + .m = { .min = 79, .max = 118 }, + .m1 = { .min = 12, .max = 22 }, + .m2 = { .min = 5, .max = 9 }, + .p = { .min = 28, .max = 112 }, + .p1 = { .min = 2, .max = 8 }, + .p2 = { .dot_limit = 225000, + .p2_slow = 14, .p2_fast = 14 }, +}; + +static const intel_limit_t intel_limits_ironlake_dual_lvds = { + .dot = { .min = 25000, .max = 350000 }, + .vco = { .min = 1760000, .max = 3510000 }, + .n = { .min = 1, .max = 3 }, + .m = { .min = 79, .max = 127 }, + .m1 = { .min = 12, .max = 22 }, + .m2 = { .min = 5, .max = 9 }, + .p = { .min = 14, .max = 56 }, + .p1 = { .min = 2, .max = 8 }, + .p2 = { .dot_limit = 225000, + .p2_slow = 7, .p2_fast = 7 }, +}; + +/* LVDS 100mhz refclk limits. */ +static const intel_limit_t intel_limits_ironlake_single_lvds_100m = { + .dot = { .min = 25000, .max = 350000 }, + .vco = { .min = 1760000, .max = 3510000 }, + .n = { .min = 1, .max = 2 }, + .m = { .min = 79, .max = 126 }, + .m1 = { .min = 12, .max = 22 }, + .m2 = { .min = 5, .max = 9 }, + .p = { .min = 28, .max = 112 }, + .p1 = { .min = 2, .max = 8 }, + .p2 = { .dot_limit = 225000, + .p2_slow = 14, .p2_fast = 14 }, +}; + +static const intel_limit_t intel_limits_ironlake_dual_lvds_100m = { + .dot = { .min = 25000, .max = 350000 }, + .vco = { .min = 1760000, .max = 3510000 }, + .n = { .min = 1, .max = 3 }, + .m = { .min = 79, .max = 126 }, + .m1 = { .min = 12, .max = 22 }, + .m2 = { .min = 5, .max = 9 }, + .p = { .min = 14, .max = 42 }, + .p1 = { .min = 2, .max = 6 }, + .p2 = { .dot_limit = 225000, + .p2_slow = 7, .p2_fast = 7 }, +}; + +static const intel_limit_t intel_limits_vlv = { + /* + * These are the data rate limits (measured in fast clocks) + * since those are the strictest limits we have. The fast + * clock and actual rate limits are more relaxed, so checking + * them would make no difference. + */ + .dot = { .min = 25000 * 5, .max = 270000 * 5 }, + .vco = { .min = 4000000, .max = 6000000 }, + .n = { .min = 1, .max = 7 }, + .m1 = { .min = 2, .max = 3 }, + .m2 = { .min = 11, .max = 156 }, + .p1 = { .min = 2, .max = 3 }, + .p2 = { .p2_slow = 2, .p2_fast = 20 }, /* slow=min, fast=max */ +}; + +static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc, + int refclk) +{ + struct drm_device *dev = crtc->dev; + const intel_limit_t *limit; + + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { + if (intel_is_dual_link_lvds(dev)) { + if (refclk == 100000) + limit = &intel_limits_ironlake_dual_lvds_100m; + else + limit = &intel_limits_ironlake_dual_lvds; + } else { + if (refclk == 100000) + limit = &intel_limits_ironlake_single_lvds_100m; + else + limit = &intel_limits_ironlake_single_lvds; + } + } else + limit = &intel_limits_ironlake_dac; + + return limit; +} + +static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + const intel_limit_t *limit; + + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { + if (intel_is_dual_link_lvds(dev)) + limit = &intel_limits_g4x_dual_channel_lvds; + else + 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 /* The option is for other outputs */ + limit = &intel_limits_i9xx_sdvo; + + return limit; +} + +const intel_limit_t *intel_limit(struct drm_crtc *crtc, int refclk) +{ + struct drm_device *dev = crtc->dev; + const intel_limit_t *limit; + + if (HAS_PCH_SPLIT(dev)) + limit = intel_ironlake_limit(crtc, refclk); + else if (IS_G4X(dev)) { + limit = intel_g4x_limit(crtc); + } else if (IS_PINEVIEW(dev)) { + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) + limit = &intel_limits_pineview_lvds; + else + limit = &intel_limits_pineview_sdvo; + } else if (IS_VALLEYVIEW(dev)) { + limit = &intel_limits_vlv; + } else if (!IS_GEN2(dev)) { + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) + limit = &intel_limits_i9xx_lvds; + else + limit = &intel_limits_i9xx_sdvo; + } else { + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) + limit = &intel_limits_i8xx_lvds; + else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DVO)) + limit = &intel_limits_i8xx_dvo; + else + limit = &intel_limits_i8xx_dac; + } + return limit; +} + +#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_device *dev, + const intel_limit_t *limit, + const intel_clock_t *clock) +{ + if (clock->n < limit->n.min || limit->n.max < clock->n) + INTELPllInvalid("n out of range\n"); + if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1) + INTELPllInvalid("p1 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 (!IS_PINEVIEW(dev) && !IS_VALLEYVIEW(dev)) + if (clock->m1 <= clock->m2) + INTELPllInvalid("m1 <= m2\n"); + + if (!IS_VALLEYVIEW(dev)) { + if (clock->p < limit->p.min || limit->p.max < clock->p) + INTELPllInvalid("p out of range\n"); + if (clock->m < limit->m.min || limit->m.max < clock->m) + INTELPllInvalid("m 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 uint32_t i9xx_dpll_compute_m(struct dpll *dpll) +{ + return 5 * (dpll->m1 + 2) + (dpll->m2 + 2); +} + +static void i9xx_clock(int refclk, intel_clock_t *clock) +{ + clock->m = i9xx_dpll_compute_m(clock); + clock->p = clock->p1 * clock->p2; + if (WARN_ON(clock->n + 2 == 0 || clock->p == 0)) + return; + clock->vco = DIV_ROUND_CLOSEST(refclk * clock->m, clock->n + 2); + clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p); +} + +/* m1 is reserved as 0 in Pineview, n is a ring counter */ +static void pineview_clock(int refclk, intel_clock_t *clock) +{ + clock->m = clock->m2 + 2; + clock->p = clock->p1 * clock->p2; + if (WARN_ON(clock->n == 0 || clock->p == 0)) + return; + clock->vco = DIV_ROUND_CLOSEST(refclk * clock->m, clock->n); + clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p); +} + +static void vlv_clock(int refclk, intel_clock_t *clock) +{ + clock->m = clock->m1 * clock->m2; + clock->p = clock->p1 * clock->p2; + if (WARN_ON(clock->n == 0 || clock->p == 0)) + return; + clock->vco = DIV_ROUND_CLOSEST(refclk * clock->m, clock->n); + clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p); +} + +static bool +g4x_find_best_dpll(const intel_limit_t *limit, struct drm_crtc *crtc, + int target, int refclk, intel_clock_t *match_clock, + intel_clock_t *best_clock) +{ + struct drm_device *dev = crtc->dev; + intel_clock_t clock; + int max_n; + bool found; + /* approximately equals target * 0.00585 */ + int err_most = (target >> 8) + (target >> 9); + found = false; + + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { + if (intel_is_dual_link_lvds(dev)) + 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 requirement, prefer smaller n to precision */ + for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) { + /* based on hardware requirement, 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; + + i9xx_clock(refclk, &clock); + if (!intel_PLL_is_valid(dev, limit, + &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 +vlv_find_best_dpll(const intel_limit_t *limit, struct drm_crtc *crtc, + int target, int refclk, intel_clock_t *match_clock, + intel_clock_t *best_clock) +{ + struct drm_device *dev = crtc->dev; + intel_clock_t clock; + unsigned int bestppm = 1000000; + /* min update 19.2 MHz */ + int max_n = min(limit->n.max, refclk / 19200); + bool found = false; + + target *= 5; /* fast clock */ + + memset(best_clock, 0, sizeof(*best_clock)); + + /* based on hardware requirement, prefer smaller n to precision */ + for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) { + for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) { + for (clock.p2 = limit->p2.p2_fast; clock.p2 >= limit->p2.p2_slow; + clock.p2 -= clock.p2 > 10 ? 2 : 1) { + clock.p = clock.p1 * clock.p2; + /* based on hardware requirement, prefer bigger m1,m2 values */ + for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) { + unsigned int ppm, diff; + + clock.m2 = DIV_ROUND_CLOSEST(target * clock.p * clock.n, + refclk * clock.m1); + + vlv_clock(refclk, &clock); + + if (!intel_PLL_is_valid(dev, limit, + &clock)) + continue; + + diff = abs(clock.dot - target); + ppm = div_u64(1000000ULL * diff, target); + + if (ppm < 100 && clock.p > best_clock->p) { + bestppm = 0; + *best_clock = clock; + found = true; + } + + if (bestppm >= 10 && ppm < bestppm - 10) { + bestppm = ppm; + *best_clock = clock; + found = true; + } + } + } + } + } + + return found; +} + +static bool +pnv_find_best_dpll(const intel_limit_t *limit, struct drm_crtc *crtc, + int target, int refclk, intel_clock_t *match_clock, + intel_clock_t *best_clock) +{ + struct drm_device *dev = crtc->dev; + intel_clock_t clock; + int err = target; + + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { + /* + * For LVDS 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 (intel_is_dual_link_lvds(dev)) + 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.m1 = limit->m1.min; clock.m1 <= limit->m1.max; + clock.m1++) { + for (clock.m2 = limit->m2.min; + clock.m2 <= limit->m2.max; clock.m2++) { + 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++) { + int this_err; + + pineview_clock(refclk, &clock); + if (!intel_PLL_is_valid(dev, limit, + &clock)) + continue; + if (match_clock && + clock.p != match_clock->p) + continue; + + this_err = abs(clock.dot - target); + if (this_err < err) { + *best_clock = clock; + err = this_err; + } + } + } + } + } + + return (err != target); +} + +static bool +i9xx_find_best_dpll(const intel_limit_t *limit, struct drm_crtc *crtc, + int target, int refclk, intel_clock_t *match_clock, + intel_clock_t *best_clock) +{ + struct drm_device *dev = crtc->dev; + intel_clock_t clock; + int err = target; + + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { + /* + * For LVDS 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 (intel_is_dual_link_lvds(dev)) + 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.m1 = limit->m1.min; clock.m1 <= limit->m1.max; + clock.m1++) { + for (clock.m2 = limit->m2.min; + clock.m2 <= limit->m2.max; clock.m2++) { + if (clock.m2 >= clock.m1) + 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++) { + int this_err; + + i9xx_clock(refclk, &clock); + if (!intel_PLL_is_valid(dev, limit, + &clock)) + continue; + if (match_clock && + clock.p != match_clock->p) + continue; + + this_err = abs(clock.dot - target); + if (this_err < err) { + *best_clock = clock; + err = this_err; + } + } + } + } + } + + return (err != target); +} + +static int i9xx_pll_refclk(struct drm_device *dev, + const struct intel_crtc_config *pipe_config) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 dpll = pipe_config->dpll_hw_state.dpll; + + if ((dpll & PLL_REF_INPUT_MASK) == PLLB_REF_INPUT_SPREADSPECTRUMIN) + return dev_priv->vbt.lvds_ssc_freq; + else if (HAS_PCH_SPLIT(dev)) + return 120000; + else if (!IS_GEN2(dev)) + return 96000; + else + return 48000; +} + +/* Returns the clock of the currently programmed mode of the given pipe. */ +void i9xx_crtc_clock_get(struct intel_crtc *crtc, + struct intel_crtc_config *pipe_config) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int pipe = pipe_config->cpu_transcoder; + u32 dpll = pipe_config->dpll_hw_state.dpll; + u32 fp; + intel_clock_t clock; + int refclk = i9xx_pll_refclk(dev, pipe_config); + + if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) + fp = pipe_config->dpll_hw_state.fp0; + else + fp = pipe_config->dpll_hw_state.fp1; + + clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT; + if (IS_PINEVIEW(dev)) { + clock.n = ffs((fp & FP_N_PINEVIEW_DIV_MASK) >> FP_N_DIV_SHIFT) - 1; + clock.m2 = (fp & FP_M2_PINEVIEW_DIV_MASK) >> FP_M2_DIV_SHIFT; + } else { + clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT; + clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT; + } + + if (!IS_GEN2(dev)) { + if (IS_PINEVIEW(dev)) + clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_PINEVIEW) >> + DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW); + else + clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK) >> + DPLL_FPA01_P1_POST_DIV_SHIFT); + + switch (dpll & DPLL_MODE_MASK) { + case DPLLB_MODE_DAC_SERIAL: + clock.p2 = dpll & DPLL_DAC_SERIAL_P2_CLOCK_DIV_5 ? + 5 : 10; + break; + case DPLLB_MODE_LVDS: + clock.p2 = dpll & DPLLB_LVDS_P2_CLOCK_DIV_7 ? + 7 : 14; + break; + default: + DRM_DEBUG_KMS("Unknown DPLL mode %08x in programmed " + "mode\n", (int)(dpll & DPLL_MODE_MASK)); + return; + } + + if (IS_PINEVIEW(dev)) + pineview_clock(refclk, &clock); + else + i9xx_clock(refclk, &clock); + } else { + u32 lvds = IS_I830(dev) ? 0 : I915_READ(LVDS); + bool is_lvds = (pipe == 1) && (lvds & LVDS_PORT_EN); + + if (is_lvds) { + clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >> + DPLL_FPA01_P1_POST_DIV_SHIFT); + + if (lvds & LVDS_CLKB_POWER_UP) + clock.p2 = 7; + else + clock.p2 = 14; + } else { + if (dpll & PLL_P1_DIVIDE_BY_TWO) + clock.p1 = 2; + else { + clock.p1 = ((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830) >> + DPLL_FPA01_P1_POST_DIV_SHIFT) + 2; + } + if (dpll & PLL_P2_DIVIDE_BY_4) + clock.p2 = 4; + else + clock.p2 = 2; + } + + i9xx_clock(refclk, &clock); + } + + /* + * This value includes pixel_multiplier. We will use + * port_clock to compute adjusted_mode.crtc_clock in the + * encoder's get_config() function. + */ + pipe_config->port_clock = clock.dot; +} + +void vlv_crtc_clock_get(struct intel_crtc *crtc, + struct intel_crtc_config *pipe_config) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int pipe = pipe_config->cpu_transcoder; + intel_clock_t clock; + u32 mdiv; + int refclk = 100000; + + mutex_lock(&dev_priv->dpio_lock); + mdiv = vlv_dpio_read(dev_priv, pipe, VLV_PLL_DW3(pipe)); + mutex_unlock(&dev_priv->dpio_lock); + + clock.m1 = (mdiv >> DPIO_M1DIV_SHIFT) & 7; + clock.m2 = mdiv & DPIO_M2DIV_MASK; + clock.n = (mdiv >> DPIO_N_SHIFT) & 0xf; + clock.p1 = (mdiv >> DPIO_P1_SHIFT) & 7; + clock.p2 = (mdiv >> DPIO_P2_SHIFT) & 0x1f; + + vlv_clock(refclk, &clock); + + /* clock.dot is the fast clock */ + pipe_config->port_clock = clock.dot / 5; +} + +void ironlake_pch_clock_get(struct intel_crtc *crtc, + struct intel_crtc_config *pipe_config) +{ + struct drm_device *dev = crtc->base.dev; + + /* read out port_clock from the DPLL */ + i9xx_crtc_clock_get(crtc, pipe_config); + + /* + * This value does not include pixel_multiplier. + * We will check that port_clock and adjusted_mode.crtc_clock + * agree once we know their relationship in the encoder's + * get_config() function. + */ + pipe_config->adjusted_mode.crtc_clock = + intel_dotclock_calculate(intel_fdi_link_freq(dev) * 10000, + &pipe_config->fdi_m_n); +} + +static bool ironlake_needs_fb_cb_tune(struct dpll *dpll, int factor) +{ + return i9xx_dpll_compute_m(dpll) < factor * dpll->n; +} + +uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, + u32 *fp, intel_clock_t *reduced_clock, u32 *fp2) +{ + struct drm_crtc *crtc = &intel_crtc->base; + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_encoder *intel_encoder; + uint32_t dpll; + int factor, num_connectors = 0; + bool is_lvds = false, is_sdvo = false; + + for_each_encoder_on_crtc(dev, crtc, intel_encoder) { + switch (intel_encoder->type) { + case INTEL_OUTPUT_LVDS: + is_lvds = true; + break; + case INTEL_OUTPUT_SDVO: + case INTEL_OUTPUT_HDMI: + is_sdvo = true; + break; + } + + num_connectors++; + } + + /* Enable autotuning of the PLL clock (if permissible) */ + factor = 21; + if (is_lvds) { + if ((intel_panel_use_ssc(dev_priv) && + dev_priv->vbt.lvds_ssc_freq == 100000) || + (HAS_PCH_IBX(dev) && intel_is_dual_link_lvds(dev))) + factor = 25; + } else if (intel_crtc->config.sdvo_tv_clock) + factor = 20; + + if (ironlake_needs_fb_cb_tune(&intel_crtc->config.dpll, factor)) + *fp |= FP_CB_TUNE; + + if (fp2 && (reduced_clock->m < factor * reduced_clock->n)) + *fp2 |= FP_CB_TUNE; + + dpll = 0; + + if (is_lvds) + dpll |= DPLLB_MODE_LVDS; + else + dpll |= DPLLB_MODE_DAC_SERIAL; + + dpll |= (intel_crtc->config.pixel_multiplier - 1) + << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT; + + if (is_sdvo) + dpll |= DPLL_SDVO_HIGH_SPEED; + if (intel_crtc->config.has_dp_encoder) + dpll |= DPLL_SDVO_HIGH_SPEED; + + /* compute bitmask from p1 value */ + dpll |= (1 << (intel_crtc->config.dpll.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; + /* also FPA1 */ + dpll |= (1 << (intel_crtc->config.dpll.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT; + + switch (intel_crtc->config.dpll.p2) { + case 5: + dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5; + break; + case 7: + dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7; + break; + case 10: + dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10; + break; + case 14: + dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14; + break; + } + + if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) + dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; + else + dpll |= PLL_REF_INPUT_DREFCLK; + + return dpll | DPLL_VCO_ENABLE; +} + +int i9xx_get_refclk(struct drm_crtc *crtc, int num_connectors) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int refclk; + + if (IS_VALLEYVIEW(dev)) { + refclk = 100000; + } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && + intel_panel_use_ssc(dev_priv) && num_connectors < 2) { + refclk = dev_priv->vbt.lvds_ssc_freq; + DRM_DEBUG_KMS("using SSC reference clock of %d kHz\n", refclk); + } else if (!IS_GEN2(dev)) { + refclk = 96000; + } else { + refclk = 48000; + } + + return refclk; +} + +int ironlake_get_refclk(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_encoder *encoder; + int num_connectors = 0; + bool is_lvds = false; + + for_each_encoder_on_crtc(dev, crtc, encoder) { + switch (encoder->type) { + case INTEL_OUTPUT_LVDS: + is_lvds = true; + break; + } + num_connectors++; + } + + if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) { + DRM_DEBUG_KMS("using SSC reference clock of %d kHz\n", + dev_priv->vbt.lvds_ssc_freq); + return dev_priv->vbt.lvds_ssc_freq; + } + + return 120000; +} + +/* Only for pre-ILK configs */ +void assert_pll(struct drm_i915_private *dev_priv, + enum pipe pipe, bool state) +{ + int reg; + u32 val; + bool cur_state; + + reg = DPLL(pipe); + val = I915_READ(reg); + cur_state = !!(val & DPLL_VCO_ENABLE); + WARN(cur_state != state, + "PLL state assertion failure (expected %s, current %s)\n", + state_string(state), state_string(cur_state)); +} + +/* XXX: the dsi pll is shared between MIPI DSI ports */ +void assert_dsi_pll(struct drm_i915_private *dev_priv, bool state) +{ + u32 val; + bool cur_state; + + mutex_lock(&dev_priv->dpio_lock); + val = vlv_cck_read(dev_priv, CCK_REG_DSI_PLL_CONTROL); + mutex_unlock(&dev_priv->dpio_lock); + + cur_state = val & DSI_PLL_VCO_EN; + WARN(cur_state != state, + "DSI PLL state assertion failure (expected %s, current %s)\n", + state_string(state), state_string(cur_state)); +} + +/* For ILK+ */ +void assert_shared_dpll(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll, + bool state) +{ + bool cur_state; + struct intel_dpll_hw_state hw_state; + + if (HAS_PCH_LPT(dev_priv->dev)) { + DRM_DEBUG_DRIVER("LPT detected: skipping PCH PLL test\n"); + return; + } + + if (WARN (!pll, + "asserting DPLL %s with no DPLL\n", state_string(state))) + return; + + cur_state = pll->get_hw_state(dev_priv, pll, &hw_state); + WARN(cur_state != state, + "%s assertion failure (expected %s, current %s)\n", + pll->name, state_string(state), state_string(cur_state)); +} + +struct intel_shared_dpll * +intel_crtc_to_shared_dpll(struct intel_crtc *crtc) +{ + struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; + + if (crtc->config.shared_dpll < 0) + return NULL; + + return &dev_priv->shared_dplls[crtc->config.shared_dpll]; +} + +/** + * ironlake_enable_shared_dpll - enable PCH PLL + * @dev_priv: i915 private structure + * @pipe: pipe PLL to enable + * + * The PCH PLL needs to be enabled before the PCH transcoder, since it + * drives the transcoder clock. + */ +void ironlake_enable_shared_dpll(struct intel_crtc *crtc) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc); + + /* PCH PLLs only available on ILK, SNB and IVB */ + BUG_ON(INTEL_INFO(dev)->gen < 5); + if (WARN_ON(pll == NULL)) + return; + + if (WARN_ON(pll->refcount == 0)) + return; + + DRM_DEBUG_KMS("enable %s (active %d, on? %d)for crtc %d\n", + pll->name, pll->active, pll->on, + crtc->base.base.id); + + if (pll->active++) { + WARN_ON(!pll->on); + assert_shared_dpll_enabled(dev_priv, pll); + return; + } + WARN_ON(pll->on); + + DRM_DEBUG_KMS("enabling %s\n", pll->name); + pll->enable(dev_priv, pll); + pll->on = true; +} + +void intel_disable_shared_dpll(struct intel_crtc *crtc) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc); + + /* PCH only available on ILK+ */ + BUG_ON(INTEL_INFO(dev)->gen < 5); + if (WARN_ON(pll == NULL)) + return; + + if (WARN_ON(pll->refcount == 0)) + return; + + DRM_DEBUG_KMS("disable %s (active %d, on? %d) for crtc %d\n", + pll->name, pll->active, pll->on, + crtc->base.base.id); + + if (WARN_ON(pll->active == 0)) { + assert_shared_dpll_disabled(dev_priv, pll); + return; + } + + assert_shared_dpll_enabled(dev_priv, pll); + WARN_ON(!pll->on); + if (--pll->active) + return; + + DRM_DEBUG_KMS("disabling %s\n", pll->name); + pll->disable(dev_priv, pll); + pll->on = false; +} + +void intel_put_shared_dpll(struct intel_crtc *crtc) +{ + struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc); + + if (pll == NULL) + return; + + if (pll->refcount == 0) { + WARN(1, "bad %s refcount\n", pll->name); + return; + } + + if (--pll->refcount == 0) { + WARN_ON(pll->on); + WARN_ON(pll->active); + } + + crtc->config.shared_dpll = DPLL_ID_PRIVATE; +} + +struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc) +{ + struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; + struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc); + enum intel_dpll_id i; + + if (pll) { + DRM_DEBUG_KMS("CRTC:%d dropping existing %s\n", + crtc->base.base.id, pll->name); + intel_put_shared_dpll(crtc); + } + + if (HAS_PCH_IBX(dev_priv->dev)) { + /* Ironlake PCH has a fixed PLL->PCH pipe mapping. */ + i = (enum intel_dpll_id) crtc->pipe; + pll = &dev_priv->shared_dplls[i]; + + DRM_DEBUG_KMS("CRTC:%d using pre-allocated %s\n", + crtc->base.base.id, pll->name); + + goto found; + } + + for (i = 0; i < dev_priv->num_shared_dpll; i++) { + pll = &dev_priv->shared_dplls[i]; + + /* Only want to check enabled timings first */ + if (pll->refcount == 0) + continue; + + if (memcmp(&crtc->config.dpll_hw_state, &pll->hw_state, + sizeof(pll->hw_state)) == 0) { + DRM_DEBUG_KMS("CRTC:%d sharing existing %s (refcount %d, ative %d)\n", + crtc->base.base.id, + pll->name, pll->refcount, pll->active); + + goto found; + } + } + + /* Ok no matching timings, maybe there's a free one? */ + for (i = 0; i < dev_priv->num_shared_dpll; i++) { + pll = &dev_priv->shared_dplls[i]; + if (pll->refcount == 0) { + DRM_DEBUG_KMS("CRTC:%d allocated %s\n", + crtc->base.base.id, pll->name); + goto found; + } + } + + return NULL; + +found: + crtc->config.shared_dpll = i; + DRM_DEBUG_DRIVER("using %s for pipe %c\n", pll->name, + pipe_name(crtc->pipe)); + + if (pll->active == 0) { + memcpy(&pll->hw_state, &crtc->config.dpll_hw_state, + sizeof(pll->hw_state)); + + DRM_DEBUG_DRIVER("setting up %s\n", pll->name); + WARN_ON(pll->on); + assert_shared_dpll_disabled(dev_priv, pll); + + pll->mode_set(dev_priv, pll); + } + pll->refcount++; + + return pll; +} + +void intel_increase_pllclock(struct drm_crtc *crtc) +{ + 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 dpll_reg = DPLL(pipe); + int dpll; + + if (HAS_PCH_SPLIT(dev)) + return; + + if (!dev_priv->lvds_downclock_avail) + return; + + dpll = I915_READ(dpll_reg); + if (!HAS_PIPE_CXSR(dev) && (dpll & DISPLAY_RATE_SELECT_FPA1)) { + DRM_DEBUG_DRIVER("upclocking LVDS\n"); + + assert_panel_unlocked(dev_priv, pipe); + + dpll &= ~DISPLAY_RATE_SELECT_FPA1; + I915_WRITE(dpll_reg, dpll); + intel_wait_for_vblank(dev, pipe); + + dpll = I915_READ(dpll_reg); + if (dpll & DISPLAY_RATE_SELECT_FPA1) + DRM_DEBUG_DRIVER("failed to upclock LVDS!\n"); + } +} + +void intel_decrease_pllclock(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + if (HAS_PCH_SPLIT(dev)) + return; + + if (!dev_priv->lvds_downclock_avail) + 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) { + int pipe = intel_crtc->pipe; + int dpll_reg = DPLL(pipe); + int dpll; + + DRM_DEBUG_DRIVER("downclocking LVDS\n"); + + assert_panel_unlocked(dev_priv, pipe); + + dpll = I915_READ(dpll_reg); + dpll |= DISPLAY_RATE_SELECT_FPA1; + I915_WRITE(dpll_reg, dpll); + intel_wait_for_vblank(dev, pipe); + dpll = I915_READ(dpll_reg); + if (!(dpll & DISPLAY_RATE_SELECT_FPA1)) + DRM_DEBUG_DRIVER("failed to downclock LVDS!\n"); + } + +} + +void vlv_enable_pll(struct intel_crtc *crtc) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int reg = DPLL(crtc->pipe); + u32 dpll = crtc->config.dpll_hw_state.dpll; + + assert_pipe_disabled(dev_priv, crtc->pipe); + + /* No really, not for ILK+ */ + BUG_ON(!IS_VALLEYVIEW(dev_priv->dev)); + + /* PLL is protected by panel, make sure we can write it */ + if (IS_MOBILE(dev_priv->dev) && !IS_I830(dev_priv->dev)) + assert_panel_unlocked(dev_priv, crtc->pipe); + + I915_WRITE(reg, dpll); + POSTING_READ(reg); + udelay(150); + + if (wait_for(((I915_READ(reg) & DPLL_LOCK_VLV) == DPLL_LOCK_VLV), 1)) + DRM_ERROR("DPLL %d failed to lock\n", crtc->pipe); + + I915_WRITE(DPLL_MD(crtc->pipe), crtc->config.dpll_hw_state.dpll_md); + POSTING_READ(DPLL_MD(crtc->pipe)); + + /* We do this three times for luck */ + I915_WRITE(reg, dpll); + POSTING_READ(reg); + udelay(150); /* wait for warmup */ + I915_WRITE(reg, dpll); + POSTING_READ(reg); + udelay(150); /* wait for warmup */ + I915_WRITE(reg, dpll); + POSTING_READ(reg); + udelay(150); /* wait for warmup */ +} + +void i9xx_enable_pll(struct intel_crtc *crtc) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int reg = DPLL(crtc->pipe); + u32 dpll = crtc->config.dpll_hw_state.dpll; + + assert_pipe_disabled(dev_priv, crtc->pipe); + + /* No really, not for ILK+ */ + BUG_ON(INTEL_INFO(dev)->gen >= 5); + + /* PLL is protected by panel, make sure we can write it */ + if (IS_MOBILE(dev) && !IS_I830(dev)) + assert_panel_unlocked(dev_priv, crtc->pipe); + + I915_WRITE(reg, dpll); + + /* Wait for the clocks to stabilize. */ + POSTING_READ(reg); + udelay(150); + + if (INTEL_INFO(dev)->gen >= 4) { + I915_WRITE(DPLL_MD(crtc->pipe), + crtc->config.dpll_hw_state.dpll_md); + } else { + /* The pixel multiplier can only be updated once the + * DPLL is enabled and the clocks are stable. + * + * So write it again. + */ + I915_WRITE(reg, dpll); + } + + /* We do this three times for luck */ + I915_WRITE(reg, dpll); + POSTING_READ(reg); + udelay(150); /* wait for warmup */ + I915_WRITE(reg, dpll); + POSTING_READ(reg); + udelay(150); /* wait for warmup */ + I915_WRITE(reg, dpll); + POSTING_READ(reg); + udelay(150); /* wait for warmup */ +} + +/** + * i9xx_disable_pll - disable a PLL + * @dev_priv: i915 private structure + * @pipe: pipe PLL to disable + * + * Disable the PLL for @pipe, making sure the pipe is off first. + * + * Note! This is for pre-ILK only. + */ +void i9xx_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) +{ + /* Don't disable pipe A or pipe A PLLs if needed */ + if (pipe == PIPE_A && (dev_priv->quirks & QUIRK_PIPEA_FORCE)) + return; + + /* Make sure the pipe isn't still relying on us */ + assert_pipe_disabled(dev_priv, pipe); + + I915_WRITE(DPLL(pipe), 0); + POSTING_READ(DPLL(pipe)); +} + +void vlv_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) +{ + u32 val = 0; + + /* Make sure the pipe isn't still relying on us */ + assert_pipe_disabled(dev_priv, pipe); + + /* + * Leave integrated clock source and reference clock enabled for pipe B. + * The latter is needed for VGA hotplug / manual detection. + */ + if (pipe == PIPE_B) + val = DPLL_INTEGRATED_CRI_CLK_VLV | DPLL_REFA_CLK_ENABLE_VLV; + I915_WRITE(DPLL(pipe), val); + POSTING_READ(DPLL(pipe)); +} + +static uint32_t pnv_dpll_compute_fp(struct dpll *dpll) +{ + return (1 << dpll->n) << 16 | dpll->m2; +} + +uint32_t i9xx_dpll_compute_fp(struct dpll *dpll) +{ + return dpll->n << 16 | dpll->m1 << 8 | dpll->m2; +} + +static void i9xx_update_pll_dividers(struct intel_crtc *crtc, + intel_clock_t *reduced_clock) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int pipe = crtc->pipe; + u32 fp, fp2 = 0; + + if (IS_PINEVIEW(dev)) { + fp = pnv_dpll_compute_fp(&crtc->config.dpll); + if (reduced_clock) + fp2 = pnv_dpll_compute_fp(reduced_clock); + } else { + fp = i9xx_dpll_compute_fp(&crtc->config.dpll); + if (reduced_clock) + fp2 = i9xx_dpll_compute_fp(reduced_clock); + } + + I915_WRITE(FP0(pipe), fp); + crtc->config.dpll_hw_state.fp0 = fp; + + crtc->lowfreq_avail = false; + if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS) && + reduced_clock && i915.powersave) { + I915_WRITE(FP1(pipe), fp2); + crtc->config.dpll_hw_state.fp1 = fp2; + crtc->lowfreq_avail = true; + } else { + I915_WRITE(FP1(pipe), fp); + crtc->config.dpll_hw_state.fp1 = fp; + } +} + +static void vlv_pllb_recal_opamp(struct drm_i915_private *dev_priv, enum pipe + pipe) +{ + u32 reg_val; + + /* + * PLLB opamp always calibrates to max value of 0x3f, force enable it + * and set it to a reasonable value instead. + */ + reg_val = vlv_dpio_read(dev_priv, pipe, VLV_PLL_DW9(1)); + reg_val &= 0xffffff00; + reg_val |= 0x00000030; + vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW9(1), reg_val); + + reg_val = vlv_dpio_read(dev_priv, pipe, VLV_REF_DW13); + reg_val &= 0x8cffffff; + reg_val = 0x8c000000; + vlv_dpio_write(dev_priv, pipe, VLV_REF_DW13, reg_val); + + reg_val = vlv_dpio_read(dev_priv, pipe, VLV_PLL_DW9(1)); + reg_val &= 0xffffff00; + vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW9(1), reg_val); + + reg_val = vlv_dpio_read(dev_priv, pipe, VLV_REF_DW13); + reg_val &= 0x00ffffff; + reg_val |= 0xb0000000; + vlv_dpio_write(dev_priv, pipe, VLV_REF_DW13, reg_val); +} + +void vlv_update_pll(struct intel_crtc *crtc) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int pipe = crtc->pipe; + u32 dpll, mdiv; + u32 bestn, bestm1, bestm2, bestp1, bestp2; + u32 coreclk, reg_val, dpll_md; + + mutex_lock(&dev_priv->dpio_lock); + + bestn = crtc->config.dpll.n; + bestm1 = crtc->config.dpll.m1; + bestm2 = crtc->config.dpll.m2; + bestp1 = crtc->config.dpll.p1; + bestp2 = crtc->config.dpll.p2; + + /* See eDP HDMI DPIO driver vbios notes doc */ + + /* PLL B needs special handling */ + if (pipe) + vlv_pllb_recal_opamp(dev_priv, pipe); + + /* Set up Tx target for periodic Rcomp update */ + vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW9_BCAST, 0x0100000f); + + /* Disable target IRef on PLL */ + reg_val = vlv_dpio_read(dev_priv, pipe, VLV_PLL_DW8(pipe)); + reg_val &= 0x00ffffff; + vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW8(pipe), reg_val); + + /* Disable fast lock */ + vlv_dpio_write(dev_priv, pipe, VLV_CMN_DW0, 0x610); + + /* Set idtafcrecal before PLL is enabled */ + mdiv = ((bestm1 << DPIO_M1DIV_SHIFT) | (bestm2 & DPIO_M2DIV_MASK)); + mdiv |= ((bestp1 << DPIO_P1_SHIFT) | (bestp2 << DPIO_P2_SHIFT)); + mdiv |= ((bestn << DPIO_N_SHIFT)); + mdiv |= (1 << DPIO_K_SHIFT); + + /* + * Post divider depends on pixel clock rate, DAC vs digital (and LVDS, + * but we don't support that). + * Note: don't use the DAC post divider as it seems unstable. + */ + mdiv |= (DPIO_POST_DIV_HDMIDP << DPIO_POST_DIV_SHIFT); + vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW3(pipe), mdiv); + + mdiv |= DPIO_ENABLE_CALIBRATION; + vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW3(pipe), mdiv); + + /* Set HBR and RBR LPF coefficients */ + if (crtc->config.port_clock == 162000 || + intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_ANALOG) || + intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI)) + vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW10(pipe), + 0x009f0003); + else + vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW10(pipe), + 0x00d0000f); + + if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP) || + intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT)) { + /* Use SSC source */ + if (!pipe) + vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW5(pipe), + 0x0df40000); + else + vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW5(pipe), + 0x0df70000); + } else { /* HDMI or VGA */ + /* Use bend source */ + if (!pipe) + vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW5(pipe), + 0x0df70000); + else + vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW5(pipe), + 0x0df40000); + } + + coreclk = vlv_dpio_read(dev_priv, pipe, VLV_PLL_DW7(pipe)); + coreclk = (coreclk & 0x0000ff00) | 0x01c00000; + if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT) || + intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP)) + coreclk |= 0x01000000; + vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW7(pipe), coreclk); + + vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW11(pipe), 0x87871000); + + /* + * Enable DPIO clock input. We should never disable the reference + * clock for pipe B, since VGA hotplug / manual detection depends + * on it. + */ + dpll = DPLL_EXT_BUFFER_ENABLE_VLV | DPLL_REFA_CLK_ENABLE_VLV | + DPLL_VGA_MODE_DIS | DPLL_INTEGRATED_CLOCK_VLV; + /* We should never disable this, set it here for state tracking */ + if (pipe == PIPE_B) + dpll |= DPLL_INTEGRATED_CRI_CLK_VLV; + dpll |= DPLL_VCO_ENABLE; + crtc->config.dpll_hw_state.dpll = dpll; + + dpll_md = (crtc->config.pixel_multiplier - 1) + << DPLL_MD_UDI_MULTIPLIER_SHIFT; + crtc->config.dpll_hw_state.dpll_md = dpll_md; + + mutex_unlock(&dev_priv->dpio_lock); +} + + +void i9xx_update_pll(struct intel_crtc *crtc, intel_clock_t *reduced_clock, + int num_connectors) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 dpll; + bool is_sdvo; + struct dpll *clock = &crtc->config.dpll; + + i9xx_update_pll_dividers(crtc, reduced_clock); + + is_sdvo = intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_SDVO) || + intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI); + + dpll = DPLL_VGA_MODE_DIS; + + if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS)) + dpll |= DPLLB_MODE_LVDS; + else + dpll |= DPLLB_MODE_DAC_SERIAL; + + if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) { + dpll |= (crtc->config.pixel_multiplier - 1) + << SDVO_MULTIPLIER_SHIFT_HIRES; + } + + if (is_sdvo) + dpll |= DPLL_SDVO_HIGH_SPEED; + + if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT)) + dpll |= DPLL_SDVO_HIGH_SPEED; + + /* compute bitmask from p1 value */ + if (IS_PINEVIEW(dev)) + dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW; + else { + dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; + if (IS_G4X(dev) && reduced_clock) + dpll |= (1 << (reduced_clock->p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT; + } + switch (clock->p2) { + case 5: + dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5; + break; + case 7: + dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7; + break; + case 10: + dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10; + break; + case 14: + dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14; + break; + } + if (INTEL_INFO(dev)->gen >= 4) + dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT); + + if (crtc->config.sdvo_tv_clock) + dpll |= PLL_REF_INPUT_TVCLKINBC; + else if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS) && + intel_panel_use_ssc(dev_priv) && num_connectors < 2) + dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; + else + dpll |= PLL_REF_INPUT_DREFCLK; + + dpll |= DPLL_VCO_ENABLE; + crtc->config.dpll_hw_state.dpll = dpll; + + if (INTEL_INFO(dev)->gen >= 4) { + u32 dpll_md = (crtc->config.pixel_multiplier - 1) + << DPLL_MD_UDI_MULTIPLIER_SHIFT; + crtc->config.dpll_hw_state.dpll_md = dpll_md; + } +} + +void i8xx_update_pll(struct intel_crtc *crtc, intel_clock_t *reduced_clock, + int num_connectors) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 dpll; + struct dpll *clock = &crtc->config.dpll; + + i9xx_update_pll_dividers(crtc, reduced_clock); + + dpll = DPLL_VGA_MODE_DIS; + + if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS)) { + dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; + } else { + if (clock->p1 == 2) + dpll |= PLL_P1_DIVIDE_BY_TWO; + else + dpll |= (clock->p1 - 2) << DPLL_FPA01_P1_POST_DIV_SHIFT; + if (clock->p2 == 4) + dpll |= PLL_P2_DIVIDE_BY_4; + } + + if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DVO)) + dpll |= DPLL_DVO_2X_MODE; + + if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS) && + intel_panel_use_ssc(dev_priv) && num_connectors < 2) + dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; + else + dpll |= PLL_REF_INPUT_DREFCLK; + + dpll |= DPLL_VCO_ENABLE; + crtc->config.dpll_hw_state.dpll = dpll; +} + +void intel_cpu_pll_init(struct drm_device *dev) +{ + if (HAS_DDI(dev)) + intel_ddi_pll_init(dev); +} + +static bool ibx_pch_dpll_get_hw_state(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll, + struct intel_dpll_hw_state *hw_state) +{ + uint32_t val; + + val = I915_READ(PCH_DPLL(pll->id)); + hw_state->dpll = val; + hw_state->fp0 = I915_READ(PCH_FP0(pll->id)); + hw_state->fp1 = I915_READ(PCH_FP1(pll->id)); + + return val & DPLL_VCO_ENABLE; +} + +static void ibx_pch_dpll_mode_set(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll) +{ + I915_WRITE(PCH_FP0(pll->id), pll->hw_state.fp0); + I915_WRITE(PCH_FP1(pll->id), pll->hw_state.fp1); +} + +static void ibx_assert_pch_refclk_enabled(struct drm_i915_private *dev_priv) +{ + u32 val; + bool enabled; + + WARN_ON(!(HAS_PCH_IBX(dev_priv->dev) || HAS_PCH_CPT(dev_priv->dev))); + + val = I915_READ(PCH_DREF_CONTROL); + enabled = !!(val & (DREF_SSC_SOURCE_MASK | DREF_NONSPREAD_SOURCE_MASK | + DREF_SUPERSPREAD_SOURCE_MASK)); + WARN(!enabled, "PCH refclk assertion failure, should be active but is disabled\n"); +} + +static void ibx_pch_dpll_enable(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll) +{ + /* PCH refclock must be enabled first */ + ibx_assert_pch_refclk_enabled(dev_priv); + + I915_WRITE(PCH_DPLL(pll->id), pll->hw_state.dpll); + + /* Wait for the clocks to stabilize. */ + POSTING_READ(PCH_DPLL(pll->id)); + udelay(150); + + /* The pixel multiplier can only be updated once the + * DPLL is enabled and the clocks are stable. + * + * So write it again. + */ + I915_WRITE(PCH_DPLL(pll->id), pll->hw_state.dpll); + POSTING_READ(PCH_DPLL(pll->id)); + udelay(200); +} + +static void ibx_pch_dpll_disable(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll) +{ + struct drm_device *dev = dev_priv->dev; + struct intel_crtc *crtc; + + /* Make sure no transcoder isn't still depending on us. */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) { + if (intel_crtc_to_shared_dpll(crtc) == pll) + assert_pch_transcoder_disabled(dev_priv, crtc->pipe); + } + + I915_WRITE(PCH_DPLL(pll->id), 0); + POSTING_READ(PCH_DPLL(pll->id)); + udelay(200); +} + +static char *ibx_pch_dpll_names[] = { + "PCH DPLL A", + "PCH DPLL B", +}; + +static void ibx_pch_dpll_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int i; + + dev_priv->num_shared_dpll = 2; + + for (i = 0; i < dev_priv->num_shared_dpll; i++) { + dev_priv->shared_dplls[i].id = i; + dev_priv->shared_dplls[i].name = ibx_pch_dpll_names[i]; + dev_priv->shared_dplls[i].mode_set = ibx_pch_dpll_mode_set; + dev_priv->shared_dplls[i].enable = ibx_pch_dpll_enable; + dev_priv->shared_dplls[i].disable = ibx_pch_dpll_disable; + dev_priv->shared_dplls[i].get_hw_state = + ibx_pch_dpll_get_hw_state; + } +} + +void intel_shared_dpll_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) + ibx_pch_dpll_init(dev); + else + dev_priv->num_shared_dpll = 0; + + BUG_ON(dev_priv->num_shared_dpll > I915_NUM_PLLS); +} + + +void intel_pll_init(struct drm_i915_private *dev_priv) +{ + struct drm_device *dev = dev_priv->dev; + + if (HAS_PCH_SPLIT(dev) || IS_G4X(dev)) + dev_priv->display.find_dpll = g4x_find_best_dpll; + else if (IS_VALLEYVIEW(dev)) + dev_priv->display.find_dpll = vlv_find_best_dpll; + else if (IS_PINEVIEW(dev)) + dev_priv->display.find_dpll = pnv_find_best_dpll; + else + dev_priv->display.find_dpll = i9xx_find_best_dpll; +}