From patchwork Tue Sep 4 12:10:16 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 10587289 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 7BEF6920 for ; Tue, 4 Sep 2018 12:10:47 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 66503292E0 for ; Tue, 4 Sep 2018 12:10:47 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 59E6C292EA; Tue, 4 Sep 2018 12:10: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 49F16292E0 for ; Tue, 4 Sep 2018 12:10:46 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 01A6A6E3AB; Tue, 4 Sep 2018 12:10:39 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by gabe.freedesktop.org (Postfix) with ESMTPS id 8A1326E3AB for ; Tue, 4 Sep 2018 12:10:37 +0000 (UTC) Received: from localhost.localdomain (unknown [IPv6:2a02:a03f:4407:cd00:1953:677d:5909:a7be]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 13A6B1C49; Tue, 4 Sep 2018 14:10:32 +0200 (CEST) From: Laurent Pinchart To: dri-devel@lists.freedesktop.org Subject: [PATCH 05/16] drm: rcar-du: lvds: D3/E3 support Date: Tue, 4 Sep 2018 15:10:16 +0300 Message-Id: <20180904121027.24031-6-laurent.pinchart+renesas@ideasonboard.com> X-Mailer: git-send-email 2.16.4 In-Reply-To: <20180904121027.24031-1-laurent.pinchart+renesas@ideasonboard.com> References: <20180904121027.24031-1-laurent.pinchart+renesas@ideasonboard.com> 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: linux-renesas-soc@vger.kernel.org MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP The LVDS encoders in the D3 and E3 SoCs differ significantly from those in the other R-Car Gen3 family members: - The LVDS PLL architecture is more complex and requires computing PLL parameters manually. - The PLL uses external clocks as inputs, which need to be retrieved from DT. - In addition to the different PLL setup, the startup sequence has changed *again* (seems someone had trouble making his/her mind). Supporting all this requires DT bindings extensions for external clocks, brand new PLL setup code, and a few quirks to handle the differences in the startup sequence. The implementation doesn't support all hardware features yet, namely - Using the LV[01] clocks generated by the CPG as PLL input. - Providing the LVDS PLL clock to the DU for use with the RGB output. Those features can be added later when the need will arise. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/rcar-du/rcar_lvds.c | 365 +++++++++++++++++++++++++++---- drivers/gpu/drm/rcar-du/rcar_lvds_regs.h | 43 +++- 2 files changed, 361 insertions(+), 47 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c index ce0eb68c3416..aac4acbcfbfc 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c @@ -24,6 +24,8 @@ #include "rcar_lvds_regs.h" +struct rcar_lvds; + /* Keep in sync with the LVDCR0.LVMD hardware register values. */ enum rcar_lvds_mode { RCAR_LVDS_MODE_JEIDA = 0, @@ -31,14 +33,16 @@ enum rcar_lvds_mode { RCAR_LVDS_MODE_VESA = 4, }; -#define RCAR_LVDS_QUIRK_LANES (1 << 0) /* LVDS lanes 1 and 3 inverted */ -#define RCAR_LVDS_QUIRK_GEN2_PLLCR (1 << 1) /* LVDPLLCR has gen2 layout */ -#define RCAR_LVDS_QUIRK_GEN3_LVEN (1 << 2) /* LVEN bit needs to be set */ - /* on R8A77970/R8A7799x */ +#define RCAR_LVDS_QUIRK_LANES BIT(0) /* LVDS lanes 1 and 3 inverted */ +#define RCAR_LVDS_QUIRK_GEN3_LVEN BIT(1) /* LVEN bit needs to be set on R8A77970/R8A7799x */ +#define RCAR_LVDS_QUIRK_PWD BIT(2) /* PWD bit available (all of Gen3 but E3) */ +#define RCAR_LVDS_QUIRK_EXT_PLL BIT(3) /* Has extended PLL */ +#define RCAR_LVDS_QUIRK_DUAL_LINK BIT(4) /* Supports dual-link operation */ struct rcar_lvds_device_info { unsigned int gen; unsigned int quirks; + void (*pll_setup)(struct rcar_lvds *lvds, unsigned int freq); }; struct rcar_lvds { @@ -52,7 +56,11 @@ struct rcar_lvds { struct drm_panel *panel; void __iomem *mmio; - struct clk *clock; + struct { + struct clk *mod; /* CPG module clock */ + struct clk *extal; /* External clock */ + struct clk *dotclkin[2]; /* External DU clocks */ + } clocks; bool enabled; struct drm_display_mode display_mode; @@ -128,33 +136,222 @@ static const struct drm_connector_funcs rcar_lvds_conn_funcs = { }; /* ----------------------------------------------------------------------------- - * Bridge + * PLL Setup */ -static u32 rcar_lvds_lvdpllcr_gen2(unsigned int freq) +static void rcar_lvds_pll_setup_gen2(struct rcar_lvds *lvds, unsigned int freq) +{ + u32 val; + + if (freq < 39000000) + val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M; + else if (freq < 61000000) + val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M; + else if (freq < 121000000) + val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M; + else + val = LVDPLLCR_PLLDLYCNT_150M; + + rcar_lvds_write(lvds, LVDPLLCR, val); +} + +static void rcar_lvds_pll_setup_gen3(struct rcar_lvds *lvds, unsigned int freq) { - if (freq < 39000) - return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M; - else if (freq < 61000) - return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M; - else if (freq < 121000) - return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M; + u32 val; + + if (freq < 42000000) + val = LVDPLLCR_PLLDIVCNT_42M; + else if (freq < 85000000) + val = LVDPLLCR_PLLDIVCNT_85M; + else if (freq < 128000000) + val = LVDPLLCR_PLLDIVCNT_128M; else - return LVDPLLCR_PLLDLYCNT_150M; + val = LVDPLLCR_PLLDIVCNT_148M; + + rcar_lvds_write(lvds, LVDPLLCR, val); +} + +struct pll_info { + struct clk *clk; + unsigned long diff; + unsigned int pll_m; + unsigned int pll_n; + unsigned int pll_e; + unsigned int div; +}; + +static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk *clk, + unsigned long target, struct pll_info *pll) +{ + unsigned long fin; + unsigned int m_min; + unsigned int m_max; + unsigned int m; + + if (!clk) + return; + + /* + * The LVDS PLL is made of a pre-divider and a multiplier (strangerly + * enough called M and N respectively), followed by a post-divider E. + * + * ,-----. ,-----. ,-----. ,-----. + * Fin --> | 1/M | -Fpdf-> | PFD | --> | VCO | -Fvco-> | 1/E | --> Fout + * `-----' ,-> | | `-----' | `-----' + * | `-----' | + * | ,-----. | + * `-------- | 1/N | <-------' + * `-----' + * + * The clock output by the PLL is then further divided by a programmable + * divider DIV to achieve the desired target frequency. Finally, an + * optional fixed /7 divider is used to convert the bit clock to a pixel + * clock (as LVDS transmits 7 bits per lane per clock sample). + * + * ,-------. ,-----. |\ + * Fout --> | 1/DIV | --> | 1/7 | --> | | + * `-------' | `-----' | | --> dot clock + * `------------> | | + * |/ + * + * The /7 divider is optional when the LVDS PLL is used to generate a + * dot clock for the DU RGB output, without using the LVDS encoder. We + * don't support this configuration yet. + * + * The PLL allowed input frequency range is 12 MHz to 192 MHz. + */ + + fin = clk_get_rate(clk); + if (fin < 12000000 || fin > 192000000) + return; + + /* + * The comparison frequency range is 12 MHz to 24 MHz, which limits the + * allowed values for the pre-divider M (normal range 1-8). + * + * Fpfd = Fin / M + */ + m_min = max_t(unsigned int, 1, DIV_ROUND_UP(fin, 24000000)); + m_max = min_t(unsigned int, 8, fin / 12000000); + + for (m = m_min; m <= m_max; ++m) { + unsigned long fpfd; + unsigned int n_min; + unsigned int n_max; + unsigned int n; + + /* + * The VCO operating range is 900 Mhz to 1800 MHz, which limits + * the allowed values for the multiplier N (normal range + * 60-120). + * + * Fvco = Fin * N / M + */ + fpfd = fin / m; + n_min = max_t(unsigned int, 60, DIV_ROUND_UP(900000000, fpfd)); + n_max = min_t(unsigned int, 120, 1800000000 / fpfd); + + for (n = n_min; n < n_max; ++n) { + unsigned long fvco; + unsigned int e_min; + unsigned int e; + + /* + * The output frequency is limited to 1039.5 MHz, + * limiting again the allowed values for the + * post-divider E (normal value 1, 2 or 4). + * + * Fout = Fvco / E + */ + fvco = fpfd * n; + e_min = fvco > 1039500000 ? 1 : 0; + + for (e = e_min; e < 3; ++e) { + unsigned long fout; + unsigned long diff; + unsigned int div; + + /* + * Finally we have a programable divider after + * the PLL, followed by a an optional fixed /7 + * divider. + */ + fout = fvco / (1 << e) / 7; + div = DIV_ROUND_CLOSEST(fout, target); + diff = abs(fout / div - target); + + if (diff < pll->diff) { + pll->clk = clk; + pll->diff = diff; + pll->pll_m = m; + pll->pll_n = n; + pll->pll_e = e; + pll->div = div; + + if (diff == 0) + goto done; + } + } + } + } + +done: +#if defined(CONFIG_DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) + { + unsigned long output = fin * pll->pll_n / pll->pll_m + / (1 << pll->pll_e) / 7 / pll->div; + int error = (long)(output - target) * 10000 / (long)target; + + dev_dbg(lvds->dev, + "%pC %lu Hz -> Fout %lu Hz (target %lu Hz, error %d.%02u%%), PLL M/N/E/DIV %u/%u/%u/%u\n", + clk, fin, output, target, error / 100, + error < 0 ? -error % 100 : error % 100, + pll->pll_m, pll->pll_n, pll->pll_e, pll->div); + } +#endif } -static u32 rcar_lvds_lvdpllcr_gen3(unsigned int freq) +static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, unsigned int freq) { - if (freq < 42000) - return LVDPLLCR_PLLDIVCNT_42M; - else if (freq < 85000) - return LVDPLLCR_PLLDIVCNT_85M; - else if (freq < 128000) - return LVDPLLCR_PLLDIVCNT_128M; + struct drm_crtc *crtc = lvds->bridge.encoder->crtc; + struct pll_info pll = { .diff = (unsigned long)-1 }; + u32 lvdpllcr; + + if (lvds->clocks.dotclkin[0] || lvds->clocks.dotclkin[1]) { + rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.dotclkin[0], + freq, &pll); + rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.dotclkin[1], + freq, &pll); + } else if (lvds->clocks.extal) { + rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.extal, + freq, &pll); + } + + lvdpllcr = LVDPLLCR_PLLON | LVDPLLCR_CLKOUT + | LVDPLLCR_PLLN(pll.pll_n - 1) | LVDPLLCR_PLLM(pll.pll_m - 1); + + if (pll.clk == lvds->clocks.extal) + lvdpllcr |= LVDPLLCR_CKSEL_EXTAL; + else + lvdpllcr |= LVDPLLCR_CKSEL_DU_DOTCLKIN(drm_crtc_index(crtc)); + + if (pll.pll_e > 0) + lvdpllcr |= LVDPLLCR_STP_CLKOUTE | LVDPLLCR_OUTCLKSEL + | LVDPLLCR_PLLE(pll.pll_e - 1); + + rcar_lvds_write(lvds, LVDPLLCR, lvdpllcr); + + if (pll.div > 1) + rcar_lvds_write(lvds, LVDDIV, LVDDIV_DIVSEL | + LVDDIV_DIVRESET | LVDDIV_DIV(pll.div - 1)); else - return LVDPLLCR_PLLDIVCNT_148M; + rcar_lvds_write(lvds, LVDDIV, 0); } +/* ----------------------------------------------------------------------------- + * Bridge + */ + static void rcar_lvds_enable(struct drm_bridge *bridge) { struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); @@ -164,14 +361,13 @@ static void rcar_lvds_enable(struct drm_bridge *bridge) * do we get a state pointer? */ struct drm_crtc *crtc = lvds->bridge.encoder->crtc; - u32 lvdpllcr; u32 lvdhcr; u32 lvdcr0; int ret; WARN_ON(lvds->enabled); - ret = clk_prepare_enable(lvds->clock); + ret = clk_prepare_enable(lvds->clocks.mod); if (ret < 0) return; @@ -196,12 +392,13 @@ static void rcar_lvds_enable(struct drm_bridge *bridge) rcar_lvds_write(lvds, LVDCHCR, lvdhcr); + if (lvds->info->quirks & RCAR_LVDS_QUIRK_DUAL_LINK) { + /* Disable dual-link mode. */ + rcar_lvds_write(lvds, LVDSTRIPE, 0); + } + /* PLL clock configuration. */ - if (lvds->info->quirks & RCAR_LVDS_QUIRK_GEN2_PLLCR) - lvdpllcr = rcar_lvds_lvdpllcr_gen2(mode->clock); - else - lvdpllcr = rcar_lvds_lvdpllcr_gen3(mode->clock); - rcar_lvds_write(lvds, LVDPLLCR, lvdpllcr); + lvds->info->pll_setup(lvds, mode->clock * 1000); /* Set the LVDS mode and select the input. */ lvdcr0 = lvds->mode << LVDCR0_LVMD_SHIFT; @@ -220,11 +417,16 @@ static void rcar_lvds_enable(struct drm_bridge *bridge) rcar_lvds_write(lvds, LVDCR0, lvdcr0); } - /* Turn the PLL on. */ - lvdcr0 |= LVDCR0_PLLON; - rcar_lvds_write(lvds, LVDCR0, lvdcr0); + if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) { + /* + * Turn the PLL on (simple PLL only, extended PLL is fully + * controlled through LVDPLLCR). + */ + lvdcr0 |= LVDCR0_PLLON; + rcar_lvds_write(lvds, LVDCR0, lvdcr0); + } - if (lvds->info->gen > 2) { + if (lvds->info->quirks & RCAR_LVDS_QUIRK_PWD) { /* Set LVDS normal mode. */ lvdcr0 |= LVDCR0_PWD; rcar_lvds_write(lvds, LVDCR0, lvdcr0); @@ -236,8 +438,10 @@ static void rcar_lvds_enable(struct drm_bridge *bridge) rcar_lvds_write(lvds, LVDCR0, lvdcr0); } - /* Wait for the startup delay. */ - usleep_range(100, 150); + if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) { + /* Wait for the PLL startup delay (simple PLL only). */ + usleep_range(100, 150); + } /* Turn the output on. */ lvdcr0 |= LVDCR0_LVRES; @@ -264,8 +468,9 @@ static void rcar_lvds_disable(struct drm_bridge *bridge) rcar_lvds_write(lvds, LVDCR0, 0); rcar_lvds_write(lvds, LVDCR1, 0); + rcar_lvds_write(lvds, LVDPLLCR, 0); - clk_disable_unprepare(lvds->clock); + clk_disable_unprepare(lvds->clocks.mod); lvds->enabled = false; } @@ -446,6 +651,60 @@ static int rcar_lvds_parse_dt(struct rcar_lvds *lvds) return ret; } +static struct clk *rcar_lvds_get_clock(struct rcar_lvds *lvds, const char *name, + bool optional) +{ + struct clk *clk; + + clk = devm_clk_get(lvds->dev, name); + if (!IS_ERR(clk)) + return clk; + + if (PTR_ERR(clk) == -ENOENT && optional) + return NULL; + + if (PTR_ERR(clk) != -EPROBE_DEFER) + dev_err(lvds->dev, "failed to get %s clock\n", + name ? name : "module"); + + return clk; +} + +static int rcar_lvds_get_clocks(struct rcar_lvds *lvds) +{ + lvds->clocks.mod = rcar_lvds_get_clock(lvds, NULL, false); + if (IS_ERR(lvds->clocks.mod)) + return PTR_ERR(lvds->clocks.mod); + + /* + * LVDS encoders without an extended PLL have no external clock inputs. + */ + if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) + return 0; + + lvds->clocks.extal = rcar_lvds_get_clock(lvds, "extal", true); + if (IS_ERR(lvds->clocks.extal)) + return PTR_ERR(lvds->clocks.extal); + + lvds->clocks.dotclkin[0] = rcar_lvds_get_clock(lvds, "dclkin.0", true); + if (IS_ERR(lvds->clocks.dotclkin[0])) + return PTR_ERR(lvds->clocks.dotclkin[0]); + + lvds->clocks.dotclkin[1] = rcar_lvds_get_clock(lvds, "dclkin.1", true); + if (IS_ERR(lvds->clocks.dotclkin[1])) + return PTR_ERR(lvds->clocks.dotclkin[1]); + + /* At least one input to the PLL must be available. */ + if (!lvds->clocks.extal && !lvds->clocks.dotclkin[0] && + !lvds->clocks.dotclkin[1]) { + dev_err(lvds->dev, + "no input clock (extal, dclkin.0 or dclkin.1)\n"); + return -EINVAL; + } + + return 0; +} + static int rcar_lvds_probe(struct platform_device *pdev) { struct rcar_lvds *lvds; @@ -475,11 +734,9 @@ static int rcar_lvds_probe(struct platform_device *pdev) if (IS_ERR(lvds->mmio)) return PTR_ERR(lvds->mmio); - lvds->clock = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(lvds->clock)) { - dev_err(&pdev->dev, "failed to get clock\n"); - return PTR_ERR(lvds->clock); - } + ret = rcar_lvds_get_clocks(lvds); + if (ret < 0) + return ret; drm_bridge_add(&lvds->bridge); @@ -497,21 +754,39 @@ static int rcar_lvds_remove(struct platform_device *pdev) static const struct rcar_lvds_device_info rcar_lvds_gen2_info = { .gen = 2, - .quirks = RCAR_LVDS_QUIRK_GEN2_PLLCR, + .pll_setup = rcar_lvds_pll_setup_gen2, }; static const struct rcar_lvds_device_info rcar_lvds_r8a7790_info = { .gen = 2, - .quirks = RCAR_LVDS_QUIRK_GEN2_PLLCR | RCAR_LVDS_QUIRK_LANES, + .quirks = RCAR_LVDS_QUIRK_LANES, + .pll_setup = rcar_lvds_pll_setup_gen2, }; static const struct rcar_lvds_device_info rcar_lvds_gen3_info = { .gen = 3, + .quirks = RCAR_LVDS_QUIRK_PWD, + .pll_setup = rcar_lvds_pll_setup_gen3, }; static const struct rcar_lvds_device_info rcar_lvds_r8a77970_info = { .gen = 3, - .quirks = RCAR_LVDS_QUIRK_GEN2_PLLCR | RCAR_LVDS_QUIRK_GEN3_LVEN, + .quirks = RCAR_LVDS_QUIRK_PWD | RCAR_LVDS_QUIRK_GEN3_LVEN, + .pll_setup = rcar_lvds_pll_setup_gen2, +}; + +static const struct rcar_lvds_device_info rcar_lvds_r8a77990_info = { + .gen = 3, + .quirks = RCAR_LVDS_QUIRK_GEN3_LVEN | RCAR_LVDS_QUIRK_EXT_PLL + | RCAR_LVDS_QUIRK_DUAL_LINK, + .pll_setup = rcar_lvds_pll_setup_d3_e3, +}; + +static const struct rcar_lvds_device_info rcar_lvds_r8a77995_info = { + .gen = 3, + .quirks = RCAR_LVDS_QUIRK_GEN3_LVEN | RCAR_LVDS_QUIRK_PWD + | RCAR_LVDS_QUIRK_EXT_PLL | RCAR_LVDS_QUIRK_DUAL_LINK, + .pll_setup = rcar_lvds_pll_setup_d3_e3, }; static const struct of_device_id rcar_lvds_of_table[] = { @@ -523,6 +798,8 @@ static const struct of_device_id rcar_lvds_of_table[] = { { .compatible = "renesas,r8a7796-lvds", .data = &rcar_lvds_gen3_info }, { .compatible = "renesas,r8a77970-lvds", .data = &rcar_lvds_r8a77970_info }, { .compatible = "renesas,r8a77980-lvds", .data = &rcar_lvds_gen3_info }, + { .compatible = "renesas,r8a77990-lvds", .data = &rcar_lvds_r8a77990_info }, + { .compatible = "renesas,r8a77995-lvds", .data = &rcar_lvds_r8a77995_info }, { } }; diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h index 4870f50d9bec..87149f2f8056 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h +++ b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h @@ -18,7 +18,7 @@ #define LVDCR0_PLLON (1 << 4) #define LVDCR0_PWD (1 << 2) /* Gen3 only */ #define LVDCR0_BEN (1 << 2) /* Gen2 only */ -#define LVDCR0_LVEN (1 << 1) /* Gen2 only */ +#define LVDCR0_LVEN (1 << 1) #define LVDCR0_LVRES (1 << 0) #define LVDCR1 0x0004 @@ -27,21 +27,36 @@ #define LVDCR1_CLKSTBY (3 << 0) #define LVDPLLCR 0x0008 +/* Gen2 & V3M */ #define LVDPLLCR_CEEN (1 << 14) #define LVDPLLCR_FBEN (1 << 13) #define LVDPLLCR_COSEL (1 << 12) -/* Gen2 */ #define LVDPLLCR_PLLDLYCNT_150M (0x1bf << 0) #define LVDPLLCR_PLLDLYCNT_121M (0x22c << 0) #define LVDPLLCR_PLLDLYCNT_60M (0x77b << 0) #define LVDPLLCR_PLLDLYCNT_38M (0x69a << 0) #define LVDPLLCR_PLLDLYCNT_MASK (0x7ff << 0) -/* Gen3 */ +/* Gen3 but V3M,D3 and E3 */ #define LVDPLLCR_PLLDIVCNT_42M (0x014cb << 0) #define LVDPLLCR_PLLDIVCNT_85M (0x00a45 << 0) #define LVDPLLCR_PLLDIVCNT_128M (0x006c3 << 0) #define LVDPLLCR_PLLDIVCNT_148M (0x046c1 << 0) #define LVDPLLCR_PLLDIVCNT_MASK (0x7ffff << 0) +/* D3 and E3 */ +#define LVDPLLCR_PLLON (1 << 22) +#define LVDPLLCR_PLLSEL_PLL0 (0 << 20) +#define LVDPLLCR_PLLSEL_LVX (1 << 20) +#define LVDPLLCR_PLLSEL_PLL1 (2 << 20) +#define LVDPLLCR_CKSEL_LVX (1 << 17) +#define LVDPLLCR_CKSEL_EXTAL (3 << 17) +#define LVDPLLCR_CKSEL_DU_DOTCLKIN(n) ((5 + (n) * 2) << 17) +#define LVDPLLCR_OCKSEL (1 << 16) +#define LVDPLLCR_STP_CLKOUTE (1 << 14) +#define LVDPLLCR_OUTCLKSEL (1 << 12) +#define LVDPLLCR_CLKOUT (1 << 11) +#define LVDPLLCR_PLLE(n) ((n) << 10) +#define LVDPLLCR_PLLN(n) ((n) << 3) +#define LVDPLLCR_PLLM(n) ((n) << 0) #define LVDCTRCR 0x000c #define LVDCTRCR_CTR3SEL_ZERO (0 << 12) @@ -71,4 +86,26 @@ #define LVDCHCR_CHSEL_CH(n, c) ((((c) - (n)) & 3) << ((n) * 4)) #define LVDCHCR_CHSEL_MASK(n) (3 << ((n) * 4)) +/* All registers below are specific to D3 and E3 */ +#define LVDSTRIPE 0x0014 +#define LVDSTRIPE_ST_TRGSEL_DISP (0 << 2) +#define LVDSTRIPE_ST_TRGSEL_HSYNC_R (1 << 2) +#define LVDSTRIPE_ST_TRGSEL_HSYNC_F (2 << 2) +#define LVDSTRIPE_ST_SWAP (1 << 1) +#define LVDSTRIPE_ST_ON (1 << 0) + +#define LVDSCR 0x0018 +#define LVDSCR_DEPTH(n) (((n) - 1) << 29) +#define LVDSCR_BANDSET (1 << 28) +#define LVDSCR_TWGCNT(n) ((((n) - 256) / 16) << 24) +#define LVDSCR_SDIV(n) ((n) << 22) +#define LVDSCR_MODE (1 << 21) +#define LVDSCR_RSTN (1 << 20) + +#define LVDDIV 0x001c +#define LVDDIV_DIVSEL (1 << 8) +#define LVDDIV_DIVRESET (1 << 7) +#define LVDDIV_DIVSTP (1 << 6) +#define LVDDIV_DIV(n) ((n) << 0) + #endif /* __RCAR_LVDS_REGS_H__ */