From patchwork Mon Jul 30 17:20:12 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 10549543 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 302E215E2 for ; Mon, 30 Jul 2018 18:01:47 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 213E02A4E2 for ; Mon, 30 Jul 2018 18:01:47 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 14F292A63B; Mon, 30 Jul 2018 18:01:47 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 93A5E2A4E2 for ; Mon, 30 Jul 2018 18:01:46 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id BFB4389BF0; Mon, 30 Jul 2018 18:01:43 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from slow1-d.mail.gandi.net (mslow2.mail.gandi.net [217.70.178.242]) by gabe.freedesktop.org (Postfix) with ESMTPS id B258189C0D for ; Mon, 30 Jul 2018 18:01:42 +0000 (UTC) Received: from relay4-d.mail.gandi.net (unknown [217.70.183.196]) by slow1-d.mail.gandi.net (Postfix) with ESMTP id DBEC13A91D9 for ; Mon, 30 Jul 2018 19:20:50 +0200 (CEST) X-Originating-IP: 2.224.242.101 Received: from w540.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay4-d.mail.gandi.net (Postfix) with ESMTPSA id C727EE0004; Mon, 30 Jul 2018 17:20:27 +0000 (UTC) From: Jacopo Mondi To: Laurent Pinchart , David Airlie Subject: [PATCH 1/3] drm: rcar-du: Rework clock configuration based on hardware limits Date: Mon, 30 Jul 2018 19:20:12 +0200 Message-Id: <1532971214-17962-2-git-send-email-jacopo@jmondi.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1532971214-17962-1-git-send-email-jacopo@jmondi.org> References: <1532971214-17962-1-git-send-email-jacopo@jmondi.org> X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "open list:DRM DRIVERS FOR RENESAS" , Jacopo Mondi , Laurent Pinchart , open list , "open list:DRM DRIVERS FOR RENESAS" MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP From: Laurent Pinchart The DU channels that have a display PLL (DPLL) can only use external clock sources, and don't have an internal clock divider (with the exception of H3 ES1.x where the post-divider is present and needs to be used as a workaround for a DPLL silicon issue). Rework the clock configuration to take this into account, avoiding selection of non-existing clock sources or usage of a missing post-divider. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 122 ++++++++++++++++++--------------- 1 file changed, 67 insertions(+), 55 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index b52b3e8..6d55cec 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -208,78 +208,90 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode; struct rcar_du_device *rcdu = rcrtc->group->dev; unsigned long mode_clock = mode->clock * 1000; - unsigned long clk; u32 value; u32 escr; - u32 div; - /* - * Compute the clock divisor and select the internal or external dot - * clock based on the requested frequency. - */ - clk = clk_get_rate(rcrtc->clock); - div = DIV_ROUND_CLOSEST(clk, mode_clock); - div = clamp(div, 1U, 64U) - 1; - escr = div | ESCR_DCLKSEL_CLKS; - - if (rcrtc->extclock) { + if (rcdu->info->dpll_ch & (1 << rcrtc->index)) { + unsigned long target = mode_clock; struct dpll_info dpll = { 0 }; unsigned long extclk; - unsigned long extrate; - unsigned long rate; - u32 extdiv; + u32 dpllcr; + u32 div = 0; - extclk = clk_get_rate(rcrtc->extclock); - if (rcdu->info->dpll_ch & (1 << rcrtc->index)) { - unsigned long target = mode_clock; + /* + * DU channels that have a display PLL can't use the internal + * system clock, and have no internal clock divider. + */ - /* - * The H3 ES1.x exhibits dot clock duty cycle stability - * issues. We can work around them by configuring the - * DPLL to twice the desired frequency, coupled with a - * /2 post-divider. This isn't needed on other SoCs and - * breaks HDMI output on M3-W for a currently unknown - * reason, so restrict the workaround to H3 ES1.x. - */ - if (soc_device_match(rcar_du_r8a7795_es1)) - target *= 2; + if (WARN_ON(!rcrtc->extclock)) + return; - rcar_du_dpll_divider(rcrtc, &dpll, extclk, target); - extclk = dpll.output; + /* + * The H3 ES1.x exhibits dot clock duty cycle stability issues. + * We can work around them by configuring the DPLL to twice the + * desired frequency, coupled with a /2 post-divider. Restrict + * the workaround to H3 ES1.x as ES2.0 and all other SoCs have + * no post-divider when a display PLL is present (as shown by + * the workaround breaking HDMI output on M3-W during testing). + */ + if (soc_device_match(rcar_du_r8a7795_es1)) { + target *= 2; + div = 1; } - extdiv = DIV_ROUND_CLOSEST(extclk, mode_clock); - extdiv = clamp(extdiv, 1U, 64U) - 1; + extclk = clk_get_rate(rcrtc->extclock); + rcar_du_dpll_divider(rcrtc, &dpll, extclk, target); - rate = clk / (div + 1); - extrate = extclk / (extdiv + 1); + dpllcr = DPLLCR_CODE | DPLLCR_CLKE + | DPLLCR_FDPLL(dpll.fdpll) + | DPLLCR_N(dpll.n) | DPLLCR_M(dpll.m) + | DPLLCR_STBY; - if (abs((long)extrate - (long)mode_clock) < - abs((long)rate - (long)mode_clock)) { + if (rcrtc->index == 1) + dpllcr |= DPLLCR_PLCS1 + | DPLLCR_INCS_DOTCLKIN1; + else + dpllcr |= DPLLCR_PLCS0 + | DPLLCR_INCS_DOTCLKIN0; - if (rcdu->info->dpll_ch & (1 << rcrtc->index)) { - u32 dpllcr = DPLLCR_CODE | DPLLCR_CLKE - | DPLLCR_FDPLL(dpll.fdpll) - | DPLLCR_N(dpll.n) | DPLLCR_M(dpll.m) - | DPLLCR_STBY; + rcar_du_group_write(rcrtc->group, DPLLCR, dpllcr); - if (rcrtc->index == 1) - dpllcr |= DPLLCR_PLCS1 - | DPLLCR_INCS_DOTCLKIN1; - else - dpllcr |= DPLLCR_PLCS0 - | DPLLCR_INCS_DOTCLKIN0; + escr = ESCR_DCLKSEL_DCLKIN | div; + } else { + unsigned long clk; + u32 div; - rcar_du_group_write(rcrtc->group, DPLLCR, - dpllcr); - } + /* + * Compute the clock divisor and select the internal or external + * dot clock based on the requested frequency. + */ + clk = clk_get_rate(rcrtc->clock); + div = DIV_ROUND_CLOSEST(clk, mode_clock); + div = clamp(div, 1U, 64U) - 1; - escr = ESCR_DCLKSEL_DCLKIN | extdiv; - } + escr = ESCR_DCLKSEL_CLKS | div; - dev_dbg(rcrtc->group->dev->dev, - "mode clock %lu extrate %lu rate %lu ESCR 0x%08x\n", - mode_clock, extrate, rate, escr); + if (rcrtc->extclock) { + unsigned long extclk; + unsigned long extrate; + unsigned long rate; + u32 extdiv; + + extclk = clk_get_rate(rcrtc->extclock); + extdiv = DIV_ROUND_CLOSEST(extclk, mode_clock); + extdiv = clamp(extdiv, 1U, 64U) - 1; + + extrate = extclk / (extdiv + 1); + rate = clk / (div + 1); + + if (abs((long)extrate - (long)mode_clock) < + abs((long)rate - (long)mode_clock)) + escr = ESCR_DCLKSEL_DCLKIN | extdiv; + + dev_dbg(rcrtc->group->dev->dev, + "mode clock %lu extrate %lu rate %lu ESCR 0x%08x\n", + mode_clock, extrate, rate, escr); + } } rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR, From patchwork Mon Jul 30 17:20:13 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 10549545 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 05DA515E2 for ; Mon, 30 Jul 2018 18:01:49 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EAA4A2A419 for ; Mon, 30 Jul 2018 18:01:48 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id DEC1E2A5FD; Mon, 30 Jul 2018 18:01:48 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 945982A419 for ; Mon, 30 Jul 2018 18:01:48 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 587F389CD7; Mon, 30 Jul 2018 18:01:44 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from slow1-d.mail.gandi.net (mslow2.mail.gandi.net [217.70.178.242]) by gabe.freedesktop.org (Postfix) with ESMTPS id AF5B689BF0 for ; Mon, 30 Jul 2018 18:01:42 +0000 (UTC) Received: from relay4-d.mail.gandi.net (unknown [217.70.183.196]) by slow1-d.mail.gandi.net (Postfix) with ESMTP id 66AF13A91F6 for ; Mon, 30 Jul 2018 19:20:52 +0200 (CEST) X-Originating-IP: 2.224.242.101 Received: from w540.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay4-d.mail.gandi.net (Postfix) with ESMTPSA id 61861E000A; Mon, 30 Jul 2018 17:20:29 +0000 (UTC) From: Jacopo Mondi To: Laurent Pinchart , David Airlie Subject: [PATCH 2/3] drm: rcar-du: Rename var to a more precise name Date: Mon, 30 Jul 2018 19:20:13 +0200 Message-Id: <1532971214-17962-3-git-send-email-jacopo@jmondi.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1532971214-17962-1-git-send-email-jacopo@jmondi.org> References: <1532971214-17962-1-git-send-email-jacopo@jmondi.org> X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "open list:DRM DRIVERS FOR RENESAS" , Jacopo Mondi , Laurent Pinchart , open list , "open list:DRM DRIVERS FOR RENESAS" MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP Rename the 'value' variable, only used to for writing to DMSR register to a more precise 'dmsr' name. Signed-off-by: Laurent Pinchart Signed-off-by: Jacopo Mondi --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 6d55cec..4d7907c 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -208,7 +208,7 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode; struct rcar_du_device *rcdu = rcrtc->group->dev; unsigned long mode_clock = mode->clock * 1000; - u32 value; + u32 dsmr; u32 escr; if (rcdu->info->dpll_ch & (1 << rcrtc->index)) { @@ -299,11 +299,11 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0); /* Signal polarities */ - value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? DSMR_VSL : 0) - | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? DSMR_HSL : 0) - | ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? DSMR_ODEV : 0) - | DSMR_DIPM_DISP | DSMR_CSPM; - rcar_du_crtc_write(rcrtc, DSMR, value); + dsmr = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? DSMR_VSL : 0) + | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? DSMR_HSL : 0) + | ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? DSMR_ODEV : 0) + | DSMR_DIPM_DISP | DSMR_CSPM; + rcar_du_crtc_write(rcrtc, DSMR, dsmr); /* Display timings */ rcar_du_crtc_write(rcrtc, HDSR, mode->htotal - mode->hsync_start - 19); From patchwork Mon Jul 30 17:20:14 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 10549553 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D2C69174A for ; Mon, 30 Jul 2018 18:01:57 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C34C12A419 for ; Mon, 30 Jul 2018 18:01:57 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B76E42A5FD; Mon, 30 Jul 2018 18:01:57 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 47B4E2A419 for ; Mon, 30 Jul 2018 18:01:57 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 3E22589B12; Mon, 30 Jul 2018 18:01:56 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from slow1-d.mail.gandi.net (mslow2.mail.gandi.net [217.70.178.242]) by gabe.freedesktop.org (Postfix) with ESMTPS id DD91989CD7 for ; Mon, 30 Jul 2018 18:01:42 +0000 (UTC) Received: from relay4-d.mail.gandi.net (unknown [217.70.183.196]) by slow1-d.mail.gandi.net (Postfix) with ESMTP id ED5833A9216 for ; Mon, 30 Jul 2018 19:20:53 +0200 (CEST) X-Originating-IP: 2.224.242.101 Received: from w540.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay4-d.mail.gandi.net (Postfix) with ESMTPSA id 0E285E0006; Mon, 30 Jul 2018 17:20:30 +0000 (UTC) From: Jacopo Mondi To: Laurent Pinchart , David Airlie Subject: [PATCH 3/3] drm: rcar-du: Improve non-DPLL clock selection Date: Mon, 30 Jul 2018 19:20:14 +0200 Message-Id: <1532971214-17962-4-git-send-email-jacopo@jmondi.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1532971214-17962-1-git-send-email-jacopo@jmondi.org> References: <1532971214-17962-1-git-send-email-jacopo@jmondi.org> X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "open list:DRM DRIVERS FOR RENESAS" , Jacopo Mondi , open list , "open list:DRM DRIVERS FOR RENESAS" MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP DU channels not equipped with a DPLL use an internal (aka SoC provided) or external clock source combined with an internal divider to generate the desired output dot clock frequency. The current clock selection procedure does not fully exploit the ability of external clock sources to generate the exact dot clock frequency by themselves, but relies instead on tuning the internal DU clock divider only, resulting in a less precise clock generation process. When possible, and desirable, ask the external clock source for the exact output dot clock frequency, and test all available clock sources (internally generated clock, externally generated clock rounded to the closest possible frequency, and not rounded externally generated clock) to better approximate the desired output dot clock. This patch specifically targets platforms (like Salvator-X[S] and ULCBs) where the DU's input dotclock.in is generated by the versaclock VC5 clock source, which is capable of generating the exact rate the DU needs as pixel clock output. This patch fixes higher resolution modes wich requires an high pixel clock output currently not working on non-HDMI DU channel (as VGA 1920x1080@60Hz). Fixes: 1b30dbde8 "drm: rcar-du: Add support for external pixel clock" Signed-off-by: Jacopo Mondi --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 96 ++++++++++++++++++++++++++-------- 1 file changed, 73 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 4d7907c..0dfb28ff 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -207,12 +207,12 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) { const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode; struct rcar_du_device *rcdu = rcrtc->group->dev; - unsigned long mode_clock = mode->clock * 1000; + unsigned long mode_pixelclock = mode->clock * 1000; u32 dsmr; u32 escr; if (rcdu->info->dpll_ch & (1 << rcrtc->index)) { - unsigned long target = mode_clock; + unsigned long target = mode_pixelclock; struct dpll_info dpll = { 0 }; unsigned long extclk; u32 dpllcr; @@ -258,42 +258,92 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) escr = ESCR_DCLKSEL_DCLKIN | div; } else { - unsigned long clk; + struct du_clkin_desc { + unsigned long dist; + unsigned long rate; + struct clk *clk; + u32 escr; + } du_clkins[3]; + struct du_clkin_desc *du_clkin = du_clkins; + struct du_clkin_desc *clkin_best; + unsigned long best = -1UL; + unsigned long pixelclock; + unsigned long cpgrate; u32 div; /* * Compute the clock divisor and select the internal or external * dot clock based on the requested frequency. */ - clk = clk_get_rate(rcrtc->clock); - div = DIV_ROUND_CLOSEST(clk, mode_clock); - div = clamp(div, 1U, 64U) - 1; + cpgrate = clk_get_rate(rcrtc->clock); + div = clamp(DIV_ROUND_CLOSEST(cpgrate, mode_pixelclock), + 1UL, 64UL) - 1; + pixelclock = cpgrate / (div + 1); - escr = ESCR_DCLKSEL_CLKS | div; + du_clkin->dist = abs(pixelclock - mode_pixelclock); + du_clkin->escr = ESCR_DCLKSEL_CLKS | div; + du_clkin->clk = rcrtc->clock; + du_clkin->rate = cpgrate; + clkin_best = du_clkin; if (rcrtc->extclock) { - unsigned long extclk; + unsigned long roundrate; unsigned long extrate; - unsigned long rate; - u32 extdiv; - - extclk = clk_get_rate(rcrtc->extclock); - extdiv = DIV_ROUND_CLOSEST(extclk, mode_clock); - extdiv = clamp(extdiv, 1U, 64U) - 1; - extrate = extclk / (extdiv + 1); - rate = clk / (div + 1); - - if (abs((long)extrate - (long)mode_clock) < - abs((long)rate - (long)mode_clock)) - escr = ESCR_DCLKSEL_DCLKIN | extdiv; + /* + * If an external clock source is present ask it + * for the exact dot clock rate, and test all possible + * combinations (internal, external rounded, external + * not rounded) to achieve the best possible clock + * accuracy. + */ + extrate = clk_get_rate(rcrtc->extclock); + roundrate = clk_round_rate(rcrtc->extclock, + mode_pixelclock); + + if (roundrate == mode_pixelclock) { + /* We can't do better than this... */ + clk_set_rate(rcrtc->extclock, roundrate); + escr = ESCR_DCLKSEL_DCLKIN; + goto set_escr; + } - dev_dbg(rcrtc->group->dev->dev, - "mode clock %lu extrate %lu rate %lu ESCR 0x%08x\n", - mode_clock, extrate, rate, escr); + /* Test the external clock 'plain' rate. */ + du_clkin++; + div = clamp(DIV_ROUND_CLOSEST(extrate, mode_pixelclock), + 1UL, 64UL) - 1; + pixelclock = extrate / (div + 1); + du_clkin->dist = abs(pixelclock - mode_pixelclock); + du_clkin->escr = ESCR_DCLKSEL_DCLKIN | div; + du_clkin->clk = rcrtc->extclock; + du_clkin->rate = extrate; + + /* Test the external clock 'rounded' rate. */ + du_clkin++; + div = clamp(DIV_ROUND_CLOSEST(roundrate, mode_pixelclock), + 1UL, 64UL) - 1; + pixelclock = roundrate / (div + 1); + du_clkin->dist = abs(pixelclock - mode_pixelclock); + du_clkin->escr = ESCR_DCLKSEL_DCLKIN | div; + du_clkin->clk = rcrtc->extclock; + du_clkin->rate = roundrate; + + /* Find out the best approximation we got. */ + for (; du_clkin >= du_clkins; --du_clkin) { + if (du_clkin->dist < best) { + best = du_clkin->dist; + clkin_best = du_clkin; + } + } + clk_set_rate(clkin_best->clk, clkin_best->rate); } + + escr = clkin_best->escr; } +set_escr: + dev_err(rcrtc->group->dev->dev, "%s: ESCR 0x%08x\n", __func__, escr); + rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR, escr); rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0);