From patchwork Fri Oct 31 22:54:14 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steve Longerbeam X-Patchwork-Id: 5207771 Return-Path: X-Original-To: patchwork-dri-devel@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 74B24C11AC for ; Fri, 31 Oct 2014 23:57:33 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 6F56520173 for ; Fri, 31 Oct 2014 23:57:32 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id 2284920172 for ; Fri, 31 Oct 2014 23:57:31 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 78D0D6E86B; Fri, 31 Oct 2014 16:56:58 -0700 (PDT) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mail-pa0-f44.google.com (mail-pa0-f44.google.com [209.85.220.44]) by gabe.freedesktop.org (Postfix) with ESMTP id EB7EA6E84B for ; Fri, 31 Oct 2014 15:57:13 -0700 (PDT) Received: by mail-pa0-f44.google.com with SMTP id bj1so8581930pad.17 for ; Fri, 31 Oct 2014 15:57:13 -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; bh=QTmUZ71k/1lbe/G46UdSe6gFzKpi6QsEFGW2m4JylYI=; b=sJeY5Cx/Rrieje4xcqSHsFUAcQ5OruuF/QDmvWIQfBIScZtXrQ4Okbq3hhynLcVtV0 rK4Gu++d9HW5Uyk4eYaPqsbI8yva99Uw0wz16STMVMig1eNlbC8pudQKr/NlEY7/lU48 iHDX51I7swB4ipmE/KhNsFuUmhIG7C9R/+BqgMhNqu52/cPWgQt/hpal9w1lAb9wbZlK g4HVFEGpNLLvGcLXMkELwCRXNk87trAlAvz1bccYKb7kW9jwjHqXvWTyHLzH39Y2I13o VqpQ6zudbPF9DCMrHQeIuYkWALvTOtxySzNlCr1aDotIqe4S2i5MU0ChgE2kRicSoUZQ G4gQ== X-Received: by 10.66.65.233 with SMTP id a9mr27061402pat.107.1414796233840; Fri, 31 Oct 2014 15:57:13 -0700 (PDT) Received: from mothership.mgc.mentorg.com (c-50-152-159-227.hsd1.ca.comcast.net. [50.152.159.227]) by mx.google.com with ESMTPSA id ev8sm10870656pdb.28.2014.10.31.15.57.13 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Fri, 31 Oct 2014 15:57:13 -0700 (PDT) From: Steve Longerbeam X-Google-Original-From: Steve Longerbeam To: dri-devel@lists.freedesktop.org Subject: [PATCH 31/72] gpu: ipu-di: Set rate of DI pre clock Date: Fri, 31 Oct 2014 15:54:14 -0700 Message-Id: <1414796095-10107-32-git-send-email-steve_longerbeam@mentor.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1414796095-10107-1-git-send-email-steve_longerbeam@mentor.com> References: <1414796095-10107-1-git-send-email-steve_longerbeam@mentor.com> X-Mailman-Approved-At: Fri, 31 Oct 2014 16:56:31 -0700 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Spam-Status: No, score=-4.7 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 This patch sets the rate of the DI pre clock to support a much wider range of pixel clock frequencies. It does this by calculating two values for the pre-clk rate: a rate that is a whole integer multiple of the pixel clock, and a rate that is a half-integer multiple. It then programs whichever rate comes closest to generating the desired pixel clock, and uses the corresponding integer or half-integer divider. The reason only whole or half integer DI dividers are used is because of a chip bug in the fractional part of DI Base Sync Clock Gen 0 register (see discussion in FSL community site at https://community.freescale.com/thread/308577). Quoting from that thread: "Pixel IPU clock for Display has high jitter and does not have 50% duty cycle. This is expected behavior, when the clock divider is set to a value that is not an integer. When the divider is set to a non-integer value, the average frequency of the clock will be correct, but the clock will jitter." According to our experimentation, only 0x8 for the fractional part seems to generate clock waveforms that are tolerated by most displays (this hasn't been proved officially). Signed-off-by: Steve Longerbeam Signed-off-by: Jiada Wang --- drivers/gpu/ipu-v3/ipu-di.c | 145 +++++++++++++++++++++++++++++++++---------- 1 file changed, 111 insertions(+), 34 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-di.c b/drivers/gpu/ipu-v3/ipu-di.c index b306f07..8da9c9f 100644 --- a/drivers/gpu/ipu-v3/ipu-di.c +++ b/drivers/gpu/ipu-v3/ipu-di.c @@ -398,12 +398,105 @@ static void ipu_di_sync_config_noninterlaced(struct ipu_di *di, ipu_di_sync_config(di, cfg_vga, 0, ARRAY_SIZE(cfg_vga)); } -static void ipu_di_config_clock(struct ipu_di *di, - const struct ipu_di_signal_cfg *sig) + +/* + * We need to use the DI divider. We should really have a flag here + * indicating whether the bridge can cope with a fractional divider + * or not. + * + * For now, assume the chip bug exists in the fractional divider + * which causes bad DI pixel clock waveforms for all values of the + * fractional part other than 0 (no fraction) or 0x8 (0.5). So this + * function returns a divider with only 0x0 or 0x8 in the fractional + * part. + * + * This function reprograms the clock rate of the parent-parent of + * the DI clock in order to get as close as possible to the requested + * pixel clock. It calculates two values for the parent-parent rate: a + * rate that is a whole integer multiple of the pixel clock, and a rate + * that is a half-integer multiple. It then programs whichever rate + * comes closest to generating the desired rate, and returns the + * corresponding integer or half-integer divider (times 16). + */ +static int set_di_pre_clk_rate(struct ipu_di *di, + const struct ipu_di_signal_cfg *sig) +{ + struct clk *di_pre_clk; + unsigned long pre_clk_N_round, pre_clk_N_0_5_round; + unsigned long pre_clk_N, pre_clk_N_0_5; + unsigned long pixclk_N, pixclk_N_0_5; + unsigned div_N, div_N_0_5, error_N, error_N_0_5; + int ret; + + di_pre_clk = clk_get_parent(clk_get_parent(di->clk_di)); + if (IS_ERR(di_pre_clk)) { + dev_err(di->ipu->dev, "failed to get di_pre clock\n"); + return PTR_ERR(di_pre_clk); + } + + /* + * calc pre_clk_N and pre_clk_N_0_5, which are an integer multiple + * and half-integer multiple of sig->pixelclock, respectively. + */ + pre_clk_N = sig->pixelclock; + pre_clk_N_round = clk_round_rate(di_pre_clk, pre_clk_N); + pre_clk_N *= (pre_clk_N_round / pre_clk_N); + pre_clk_N_0_5 = pre_clk_N + sig->pixelclock / 2; + + if (pre_clk_N < pre_clk_N_round) + pre_clk_N += sig->pixelclock; + if (pre_clk_N_0_5 < pre_clk_N_round) + pre_clk_N_0_5 += sig->pixelclock; + + /* + * now get the rounded pre_clk_N and pre_clk_N_0_5, i.e. what the + * pre-clk can actually generate. + */ + pre_clk_N_round = clk_round_rate(di_pre_clk, pre_clk_N); + pre_clk_N_0_5_round = clk_round_rate(di_pre_clk, pre_clk_N_0_5); + + /* + * finally we can determine whether the integer multiple or + * half-integer multiple pre-clk comes closest to generating + * the desired pixel clock. Set pre-clk rate to whichever comes + * closest. + */ + div_N = (pre_clk_N_round << 4) / sig->pixelclock; + div_N = (div_N + 0x8) & ~0xf; /* round to nearest int */ + pixclk_N = (pre_clk_N_round << 4) / div_N; + + div_N_0_5 = (pre_clk_N_0_5_round << 4) / sig->pixelclock; + div_N_0_5 = (div_N_0_5 + 0x4) & ~0x7; /* round to nearest half-int */ + pixclk_N_0_5 = (pre_clk_N_0_5_round << 4) / div_N_0_5; + + error_N = pixclk_N / (sig->pixelclock / 1000); + error_N_0_5 = pixclk_N_0_5 / (sig->pixelclock / 1000); + + if (abs(error_N - 1000) < abs(error_N_0_5 - 1000)) { + ret = div_N; + clk_set_rate(di_pre_clk, pre_clk_N_round); + } else { + ret = div_N_0_5; + clk_set_rate(di_pre_clk, pre_clk_N_0_5_round); + } + + dev_dbg(di->ipu->dev, + "di%d: pixclk want %lu, can do %lu / %lu, div 0x%02x / 0x%02x, error %d.%u%% / %d.%u%%\n", + di->id, sig->pixelclock, pixclk_N, pixclk_N_0_5, + div_N, div_N_0_5, + (signed)(error_N - 1000) / 10, error_N % 10, + (signed)(error_N_0_5 - 1000) / 10, error_N_0_5 % 10); + + return ret; +} + +static int ipu_di_config_clock(struct ipu_di *di, + const struct ipu_di_signal_cfg *sig) { struct clk *clk; unsigned clkgen0; uint32_t val; + int ret; if (sig->clkflags & IPU_DI_CLKMODE_EXT) { /* @@ -423,24 +516,10 @@ static void ipu_di_config_clock(struct ipu_di *di, */ clkgen0 = 1 << 4; } else { - /* - * We can use the divider. We should really have - * a flag here indicating whether the bridge can - * cope with a fractional divider or not. For the - * time being, let's go for simplicitly and - * reliability. - */ - unsigned long in_rate; - unsigned div; - - clk_set_rate(clk, sig->pixelclock); - - in_rate = clk_get_rate(clk); - div = (in_rate + sig->pixelclock / 2) / sig->pixelclock; - if (div == 0) - div = 1; - - clkgen0 = div << 4; + ret = set_di_pre_clk_rate(di, sig); + if (ret < 0) + return ret; + clkgen0 = ret; } } else { /* @@ -470,19 +549,12 @@ static void ipu_di_config_clock(struct ipu_di *di, clkgen0 = div << 4; } else { - unsigned long in_rate; - unsigned div; - clk = di->clk_di; - clk_set_rate(clk, sig->pixelclock); - - in_rate = clk_get_rate(clk); - div = (in_rate + sig->pixelclock / 2) / sig->pixelclock; - if (div == 0) - div = 1; - - clkgen0 = div << 4; + ret = set_di_pre_clk_rate(di, sig); + if (ret < 0) + return ret; + clkgen0 = ret; } } @@ -513,6 +585,8 @@ static void ipu_di_config_clock(struct ipu_di *di, clk_get_rate(di->clk_di), clk == di->clk_di ? "DI" : "IPU", clk_get_rate(di->clk_di_pixel) / (clkgen0 >> 4)); + + return 0; } /* @@ -546,6 +620,7 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) u32 reg; u32 di_gen, vsync_cnt; u32 div; + int ret = 0; dev_dbg(di->ipu->dev, "di%d: panel size = %d x %d\n", di->id, sig->width, sig->height); @@ -564,7 +639,9 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) mutex_lock(&di_mutex); - ipu_di_config_clock(di, sig); + ret = ipu_di_config_clock(di, sig); + if (ret) + goto unlock; div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff; div = div / 16; /* Now divider is integer portion */ @@ -641,9 +718,9 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) ipu_di_write(di, reg, DI_POL); +unlock: mutex_unlock(&di_mutex); - - return 0; + return ret; } EXPORT_SYMBOL_GPL(ipu_di_init_sync_panel);