summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/omapdrm/dss/pll.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/omapdrm/dss/pll.c')
-rw-r--r--drivers/gpu/drm/omapdrm/dss/pll.c167
1 files changed, 114 insertions, 53 deletions
diff --git a/drivers/gpu/drm/omapdrm/dss/pll.c b/drivers/gpu/drm/omapdrm/dss/pll.c
index 5e221302768b..4c8246a3ded9 100644
--- a/drivers/gpu/drm/omapdrm/dss/pll.c
+++ b/drivers/gpu/drm/omapdrm/dss/pll.c
@@ -1,21 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) 2014 Texas Instruments Incorporated
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
+ * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com/
*/
#define DSS_SUBSYS_NAME "PLL"
+#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/kernel.h>
@@ -35,15 +25,14 @@
#define PLL_SSC_CONFIGURATION2 0x001C
#define PLL_CONFIGURATION4 0x0020
-static struct dss_pll *dss_plls[4];
-
-int dss_pll_register(struct dss_pll *pll)
+int dss_pll_register(struct dss_device *dss, struct dss_pll *pll)
{
int i;
- for (i = 0; i < ARRAY_SIZE(dss_plls); ++i) {
- if (!dss_plls[i]) {
- dss_plls[i] = pll;
+ for (i = 0; i < ARRAY_SIZE(dss->plls); ++i) {
+ if (!dss->plls[i]) {
+ dss->plls[i] = pll;
+ pll->dss = dss;
return 0;
}
}
@@ -53,29 +42,32 @@ int dss_pll_register(struct dss_pll *pll)
void dss_pll_unregister(struct dss_pll *pll)
{
+ struct dss_device *dss = pll->dss;
int i;
- for (i = 0; i < ARRAY_SIZE(dss_plls); ++i) {
- if (dss_plls[i] == pll) {
- dss_plls[i] = NULL;
+ for (i = 0; i < ARRAY_SIZE(dss->plls); ++i) {
+ if (dss->plls[i] == pll) {
+ dss->plls[i] = NULL;
+ pll->dss = NULL;
return;
}
}
}
-struct dss_pll *dss_pll_find(const char *name)
+struct dss_pll *dss_pll_find(struct dss_device *dss, const char *name)
{
int i;
- for (i = 0; i < ARRAY_SIZE(dss_plls); ++i) {
- if (dss_plls[i] && strcmp(dss_plls[i]->name, name) == 0)
- return dss_plls[i];
+ for (i = 0; i < ARRAY_SIZE(dss->plls); ++i) {
+ if (dss->plls[i] && strcmp(dss->plls[i]->name, name) == 0)
+ return dss->plls[i];
}
return NULL;
}
-struct dss_pll *dss_pll_find_by_src(enum dss_clk_source src)
+struct dss_pll *dss_pll_find_by_src(struct dss_device *dss,
+ enum dss_clk_source src)
{
struct dss_pll *pll;
@@ -85,27 +77,27 @@ struct dss_pll *dss_pll_find_by_src(enum dss_clk_source src)
return NULL;
case DSS_CLK_SRC_HDMI_PLL:
- return dss_pll_find("hdmi");
+ return dss_pll_find(dss, "hdmi");
case DSS_CLK_SRC_PLL1_1:
case DSS_CLK_SRC_PLL1_2:
case DSS_CLK_SRC_PLL1_3:
- pll = dss_pll_find("dsi0");
+ pll = dss_pll_find(dss, "dsi0");
if (!pll)
- pll = dss_pll_find("video0");
+ pll = dss_pll_find(dss, "video0");
return pll;
case DSS_CLK_SRC_PLL2_1:
case DSS_CLK_SRC_PLL2_2:
case DSS_CLK_SRC_PLL2_3:
- pll = dss_pll_find("dsi1");
+ pll = dss_pll_find(dss, "dsi1");
if (!pll)
- pll = dss_pll_find("video1");
+ pll = dss_pll_find(dss, "video1");
return pll;
}
}
-unsigned dss_pll_get_clkout_idx_for_src(enum dss_clk_source src)
+unsigned int dss_pll_get_clkout_idx_for_src(enum dss_clk_source src)
{
switch (src) {
case DSS_CLK_SRC_HDMI_PLL:
@@ -215,8 +207,8 @@ bool dss_pll_calc_a(const struct dss_pll *pll, unsigned long clkin,
dss_pll_calc_func func, void *data)
{
const struct dss_pll_hw *hw = pll->hw;
- int n, n_min, n_max;
- int m, m_min, m_max;
+ int n, n_start, n_stop, n_inc;
+ int m, m_start, m_stop, m_inc;
unsigned long fint, clkdco;
unsigned long pll_hw_max;
unsigned long fint_hw_min, fint_hw_max;
@@ -226,22 +218,39 @@ bool dss_pll_calc_a(const struct dss_pll *pll, unsigned long clkin,
fint_hw_min = hw->fint_min;
fint_hw_max = hw->fint_max;
- n_min = max(DIV_ROUND_UP(clkin, fint_hw_max), 1ul);
- n_max = min((unsigned)(clkin / fint_hw_min), hw->n_max);
+ n_start = max(DIV_ROUND_UP(clkin, fint_hw_max), 1ul);
+ n_stop = min((unsigned)(clkin / fint_hw_min), hw->n_max);
+ n_inc = 1;
+
+ if (n_start > n_stop)
+ return false;
+
+ if (hw->errata_i886) {
+ swap(n_start, n_stop);
+ n_inc = -1;
+ }
pll_max = pll_max ? pll_max : ULONG_MAX;
- /* Try to find high N & M to avoid jitter (DRA7 errata i886) */
- for (n = n_max; n >= n_min; --n) {
+ for (n = n_start; n != n_stop; n += n_inc) {
fint = clkin / n;
- m_min = max(DIV_ROUND_UP(DIV_ROUND_UP(pll_min, fint), 2),
+ m_start = max(DIV_ROUND_UP(DIV_ROUND_UP(pll_min, fint), 2),
1ul);
- m_max = min3((unsigned)(pll_max / fint / 2),
+ m_stop = min3((unsigned)(pll_max / fint / 2),
(unsigned)(pll_hw_max / fint / 2),
hw->m_max);
+ m_inc = 1;
+
+ if (m_start > m_stop)
+ continue;
- for (m = m_max; m >= m_min; --m) {
+ if (hw->errata_i886) {
+ swap(m_start, m_stop);
+ m_inc = -1;
+ }
+
+ for (m = m_start; m != m_stop; m += m_inc) {
clkdco = 2 * m * fint;
if (func(n, m, fint, clkdco, data))
@@ -266,7 +275,7 @@ bool dss_pll_calc_b(const struct dss_pll *pll, unsigned long clkin,
unsigned long fint, clkdco, clkout;
unsigned long target_clkdco;
unsigned long min_dco;
- unsigned n, m, mf, m2, sd;
+ unsigned int n, m, mf, m2, sd;
const struct dss_pll_hw *hw = pll->hw;
DSSDBG("clkin %lu, target clkout %lu\n", clkin, target_clkout);
@@ -368,6 +377,22 @@ static int dss_wait_hsdiv_ack(struct dss_pll *pll, u32 hsdiv_ack_mask)
return -ETIMEDOUT;
}
+static bool pll_is_locked(u32 stat)
+{
+ /*
+ * Required value for each bitfield listed below
+ *
+ * PLL_STATUS[6] = 0 PLL_BYPASS
+ * PLL_STATUS[5] = 0 PLL_HIGHJITTER
+ *
+ * PLL_STATUS[3] = 0 PLL_LOSSREF
+ * PLL_STATUS[2] = 0 PLL_RECAL
+ * PLL_STATUS[1] = 1 PLL_LOCK
+ * PLL_STATUS[0] = 1 PLL_CTRL_RESET_DONE
+ */
+ return ((stat & 0x6f) == 0x3);
+}
+
int dss_pll_write_config_type_a(struct dss_pll *pll,
const struct dss_pll_clock_info *cinfo)
{
@@ -423,18 +448,54 @@ int dss_pll_write_config_type_a(struct dss_pll *pll,
l = FLD_MOD(l, 0, 25, 25); /* M7_CLOCK_EN */
writel_relaxed(l, base + PLL_CONFIGURATION2);
- writel_relaxed(1, base + PLL_GO); /* PLL_GO */
+ if (hw->errata_i932) {
+ int cnt = 0;
+ u32 sleep_time;
+ const u32 max_lock_retries = 20;
- if (wait_for_bit_change(base + PLL_GO, 0, 0) != 0) {
- DSSERR("DSS DPLL GO bit not going down.\n");
- r = -EIO;
- goto err;
- }
+ /*
+ * Calculate wait time for PLL LOCK
+ * 1000 REFCLK cycles in us.
+ */
+ sleep_time = DIV_ROUND_UP(1000*1000*1000, cinfo->fint);
- if (wait_for_bit_change(base + PLL_STATUS, 1, 1) != 1) {
- DSSERR("cannot lock DSS DPLL\n");
- r = -EIO;
- goto err;
+ for (cnt = 0; cnt < max_lock_retries; cnt++) {
+ writel_relaxed(1, base + PLL_GO); /* PLL_GO */
+
+ /**
+ * read the register back to ensure the write is
+ * flushed
+ */
+ readl_relaxed(base + PLL_GO);
+
+ usleep_range(sleep_time, sleep_time + 5);
+ l = readl_relaxed(base + PLL_STATUS);
+
+ if (pll_is_locked(l) &&
+ !(readl_relaxed(base + PLL_GO) & 0x1))
+ break;
+
+ }
+
+ if (cnt == max_lock_retries) {
+ DSSERR("cannot lock PLL\n");
+ r = -EIO;
+ goto err;
+ }
+ } else {
+ writel_relaxed(1, base + PLL_GO); /* PLL_GO */
+
+ if (wait_for_bit_change(base + PLL_GO, 0, 0) != 0) {
+ DSSERR("DSS DPLL GO bit not going down.\n");
+ r = -EIO;
+ goto err;
+ }
+
+ if (wait_for_bit_change(base + PLL_STATUS, 1, 1) != 1) {
+ DSSERR("cannot lock DSS DPLL\n");
+ r = -EIO;
+ goto err;
+ }
}
l = readl_relaxed(base + PLL_CONFIGURATION2);