From patchwork Thu Jan 2 21:26:06 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Russell King X-Patchwork-Id: 3431881 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id E72399F2E9 for ; Fri, 3 Jan 2014 15:25:24 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id A4C712010C for ; Fri, 3 Jan 2014 15:25:23 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id 77A3520108 for ; Fri, 3 Jan 2014 15:25:22 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id DD252FA2EE; Fri, 3 Jan 2014 07:25:17 -0800 (PST) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from pandora.arm.linux.org.uk (gw-1.arm.linux.org.uk [78.32.30.217]) by gabe.freedesktop.org (Postfix) with ESMTP id A0535FB8F9 for ; Thu, 2 Jan 2014 13:30:56 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=arm.linux.org.uk; s=pandora; h=Date:Sender:Message-Id:Subject:Cc:To:From:References:In-Reply-To; bh=cKznOMB3KWYZGEpb4n+d+476928Uw6wLA6HoQYjQsnY=; b=D9o/42Q3KO4HHtw534DIK0oPfzhSMRyfKpAlU6A6c/LcElextUCcFt/vYvx7QEG9l9Ec82gGRSOnpS65/zm+yKrfxU9QaDe34Eg2tvj3ZDf0KNpu7y+mEwEeqVcZf5cBDvbG2uFyHZ9OafbRegJ6J8vBr4KJL7Q+5bKynflJJRU=; Received: from [2001:4d48:ad52:3201:222:68ff:fe15:37dd] (port=58575 helo=rmk-PC.arm.linux.org.uk) by pandora.arm.linux.org.uk with esmtpsa (TLSv1:AES256-SHA:256) (Exim 4.76) (envelope-from ) id 1VypmI-00049E-KE; Thu, 02 Jan 2014 21:26:06 +0000 Received: from rmk by rmk-PC.arm.linux.org.uk with local (Exim 4.76) (envelope-from ) id 1VypmI-0007Dl-82; Thu, 02 Jan 2014 21:26:06 +0000 In-Reply-To: <20140102212528.GD7383@n2100.arm.linux.org.uk> References: <20140102212528.GD7383@n2100.arm.linux.org.uk> From: Russell King To: David Airlie , Greg Kroah-Hartman , Sascha Hauer , Shawn Guo Subject: [PATCH RFC 04/46] imx-drm: ipu-v3: more inteligent DI clock selection Message-Id: Date: Thu, 02 Jan 2014 21:26:06 +0000 X-Mailman-Approved-At: Fri, 03 Jan 2014 07:21:54 -0800 Cc: devel@driverdev.osuosl.org, dri-devel@lists.freedesktop.org, linux-arm-kernel@lists.infradead.org X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: dri-devel-bounces@lists.freedesktop.org Errors-To: dri-devel-bounces@lists.freedesktop.org X-Spam-Status: No, score=-4.6 required=5.0 tests=BAYES_00,DKIM_SIGNED, 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 The DI clock selection was very rudimentary: it would statically use either the IPU internal clock or the DI external clock depending on which "encoder" was being used. In the case of HDMI, it would always use the IPU clock. Moreover, using the IPU clock resulted in fractional divisors, which are achieved by skipping clock pulses. This can result in the HDMI PHY PLL being frequency modulated, and the attached device is then unable to properly lock on to the TMDS clock. We need at least 1% accurate and stable clocks for HDMI. Arrange for the DI clock to be sourced from the IPU internal clock if it can satisfy our requirements, otherwise switch to the DI external clock and try and set the external clock to our desired pixel clock rate. Signed-off-by: Russell King --- drivers/staging/imx-drm/ipu-v3/ipu-di.c | 54 +++++++++++++++++++++++++++++- 1 files changed, 52 insertions(+), 2 deletions(-) diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-di.c b/drivers/staging/imx-drm/ipu-v3/ipu-di.c index 948a49b289ef..8c7241bb435c 100644 --- a/drivers/staging/imx-drm/ipu-v3/ipu-di.c +++ b/drivers/staging/imx-drm/ipu-v3/ipu-di.c @@ -544,10 +544,48 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) if ((sig->v_sync_width == 0) || (sig->h_sync_width == 0)) return -EINVAL; + dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n", + clk_get_rate(di->clk_ipu), + clk_get_rate(di->clk_di), + sig->pixelclock); + + /* + * CLKMODE_EXT means we must use the DI clock: this is needed + * for things like LVDS which needs to feed the DI and LDB with + * the same pixel clock. + * + * For other interfaces, we can arbitarily select between the DI + * specific clock and the internal IPU clock. See DI_GENERAL + * bit 20. We select the IPU clock if it can give us a clock + * rate within 1% of the requested frequency, otherwise we use + * the DI clock. + */ if (sig->clkflags & IPU_DI_CLKMODE_EXT) parent = di->clk_di; - else - parent = di->clk_ipu; + else { + unsigned long rate, clkrate; + unsigned div, error; + + clkrate = clk_get_rate(di->clk_ipu); + div = (clkrate + sig->pixelclock / 2) / sig->pixelclock; + rate = clkrate / div; + + error = rate / (sig->pixelclock / 1000); + + dev_dbg(di->ipu->dev, " IPU clock can give %lu with divider %u, error %d.%u%%\n", + rate, div, (signed)(error - 1000) / 10, error % 10); + + /* Allow a 1% error */ + if (error < 1010 && error >= 990) { + parent = di->clk_ipu; + } else { + parent = di->clk_di; + + ret = clk_set_rate(parent, sig->pixelclock); + if (ret) + dev_err(di->ipu->dev, "Setting of DI clock failed: %d\n", ret); + } + } ret = clk_set_parent(di->clk_di_pixel, parent); if (ret) { @@ -557,6 +595,11 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) return ret; } + /* + * CLKMODE_SYNC means that we want the DI to be clocked at the + * same rate as the parent clock. This is needed (eg) for LDB + * which needs to be fed with the same pixel clock. + */ if (sig->clkflags & IPU_DI_CLKMODE_SYNC) round = clk_get_rate(parent); else @@ -564,6 +607,13 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) ret = clk_set_rate(di->clk_di_pixel, round); + dev_dbg(di->ipu->dev, "Want %luHz IPU %luHz DI %luHz using %s, got %luHz\n", + sig->pixelclock, + clk_get_rate(di->clk_ipu), + clk_get_rate(di->clk_di), + parent == di->clk_di ? "DI" : "IPU", + clk_get_rate(di->clk_di_pixel)); + h_total = sig->width + sig->h_sync_width + sig->h_start_width + sig->h_end_width; v_total = sig->height + sig->v_sync_width + sig->v_start_width +