summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2010-12-08 19:54:37 -0800
committerColin Cross <ccross@android.com>2010-12-08 19:54:37 -0800
commit42907f1736fe39cdf39b5f583fcd6b9e4e257b18 (patch)
tree8f13b9c7990bb6f5c39b2869e6829c72153d0ee0
parent2bc50e6e810abc002ccb32e46f36d20114d0b249 (diff)
parent85f7f645fb1c386b7b01044f2402587d9beda517 (diff)
Merge branch 'linux-tegra-2.6.36' into android-tegra-2.6.36
-rw-r--r--arch/arm/mach-tegra/Kconfig4
-rw-r--r--arch/arm/mach-tegra/Makefile1
-rw-r--r--arch/arm/mach-tegra/clock.c12
-rw-r--r--arch/arm/mach-tegra/cpu-tegra.c21
-rw-r--r--arch/arm/mach-tegra/tegra2_clocks.c70
-rw-r--r--arch/arm/mach-tegra/tegra2_dvfs.c1
-rw-r--r--arch/arm/mach-tegra/tegra2_emc.c172
-rw-r--r--arch/arm/mach-tegra/tegra2_emc.h27
-rw-r--r--drivers/media/video/tegra/avp/avp_svc.c24
-rw-r--r--drivers/video/tegra/dc/dc.c23
-rw-r--r--drivers/video/tegra/dc/dc_priv.h1
11 files changed, 352 insertions, 4 deletions
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
index ef028cd62734..8269eea7ee77 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -75,6 +75,10 @@ config TEGRA_FIQ_DEBUGGER
endif
+config TEGRA_EMC_SCALING_ENABLE
+ bool "Enable scaling the memory frequency"
+ default n
+
config TEGRA_CPU_DVFS
bool "Enable voltage scaling on Tegra CPU"
default y
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 6de6bc4c762e..9b8bdf9f5625 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_dvfs.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_fuse.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += suspend-t2.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_save.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o
obj-$(CONFIG_CPU_V7) += cortex-a9.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pinmux-t2-tables.o
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c
index 124af0f78782..e3936af38356 100644
--- a/arch/arm/mach-tegra/clock.c
+++ b/arch/arm/mach-tegra/clock.c
@@ -350,6 +350,7 @@ int clk_set_rate(struct clk *c, unsigned long rate)
int ret = 0;
unsigned long flags;
unsigned long old_rate;
+ long new_rate;
clk_lock_save(c, flags);
@@ -363,6 +364,17 @@ int clk_set_rate(struct clk *c, unsigned long rate)
if (rate > c->max_rate)
rate = c->max_rate;
+ if (c->ops && c->ops->round_rate) {
+ new_rate = c->ops->round_rate(c, rate);
+
+ if (new_rate < 0) {
+ ret = new_rate;
+ goto out;
+ }
+
+ rate = new_rate;
+ }
+
if (clk_is_auto_dvfs(c) && rate > old_rate && c->refcnt > 0) {
ret = tegra_dvfs_set_rate(c, rate);
if (ret)
diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c
index 1be794d015cf..d8c103e8964c 100644
--- a/arch/arm/mach-tegra/cpu-tegra.c
+++ b/arch/arm/mach-tegra/cpu-tegra.c
@@ -59,6 +59,7 @@ static struct cpufreq_frequency_table freq_table[] = {
#define NUM_CPUS 2
static struct clk *cpu_clk;
+static struct clk *emc_clk;
static struct workqueue_struct *workqueue;
@@ -115,6 +116,17 @@ static int tegra_update_cpu_speed(unsigned long rate)
if (freqs.old == freqs.new)
return ret;
+ /*
+ * Vote on memory bus frequency based on cpu frequency
+ * This sets the minimum frequency, display or avp may request higher
+ */
+ if (rate >= 816000)
+ clk_set_rate(emc_clk, 600000000); /* cpu 816 MHz, emc max */
+ else if (rate >= 456000)
+ clk_set_rate(emc_clk, 300000000); /* cpu 456 MHz, emc 150Mhz */
+ else
+ clk_set_rate(emc_clk, 100000000); /* emc 50Mhz */
+
for_each_online_cpu(freqs.cpu)
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
@@ -336,6 +348,13 @@ static int tegra_cpu_init(struct cpufreq_policy *policy)
if (IS_ERR(cpu_clk))
return PTR_ERR(cpu_clk);
+ emc_clk = clk_get_sys("cpu", "emc");
+ if (IS_ERR(emc_clk)) {
+ clk_put(cpu_clk);
+ return PTR_ERR(emc_clk);
+ }
+ clk_enable(emc_clk);
+
cpufreq_frequency_table_cpuinfo(policy, freq_table);
cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
policy->cur = tegra_getspeed(policy->cpu);
@@ -358,6 +377,8 @@ static int tegra_cpu_init(struct cpufreq_policy *policy)
static int tegra_cpu_exit(struct cpufreq_policy *policy)
{
cpufreq_frequency_table_cpuinfo(policy, freq_table);
+ clk_disable(emc_clk);
+ clk_put(emc_clk);
clk_put(cpu_clk);
return 0;
}
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index d3bd446289dd..eef598e456f1 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -32,6 +32,7 @@
#include "clock.h"
#include "fuse.h"
+#include "tegra2_emc.h"
#define RST_DEVICES 0x004
#define RST_DEVICES_SET 0x300
@@ -1021,6 +1022,53 @@ static struct clk_ops tegra_periph_clk_ops = {
.reset = &tegra2_periph_clk_reset,
};
+/* External memory controller clock ops */
+static void tegra2_emc_clk_init(struct clk *c)
+{
+ tegra2_periph_clk_init(c);
+ c->max_rate = clk_get_rate_locked(c);
+}
+
+static long tegra2_emc_clk_round_rate(struct clk *c, unsigned long rate)
+{
+ long new_rate = rate;
+
+ new_rate = tegra_emc_round_rate(new_rate);
+ if (new_rate < 0)
+ return c->max_rate;
+
+ BUG_ON(new_rate != tegra2_periph_clk_round_rate(c, new_rate));
+
+ return new_rate;
+}
+
+static int tegra2_emc_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ int ret;
+ /* The Tegra2 memory controller has an interlock with the clock
+ * block that allows memory shadowed registers to be updated,
+ * and then transfer them to the main registers at the same
+ * time as the clock update without glitches. */
+ ret = tegra_emc_set_rate(rate);
+ if (ret < 0)
+ return ret;
+
+ ret = tegra2_periph_clk_set_rate(c, rate);
+ udelay(1);
+
+ return ret;
+}
+
+static struct clk_ops tegra_emc_clk_ops = {
+ .init = &tegra2_emc_clk_init,
+ .enable = &tegra2_periph_clk_enable,
+ .disable = &tegra2_periph_clk_disable,
+ .set_parent = &tegra2_periph_clk_set_parent,
+ .set_rate = &tegra2_emc_clk_set_rate,
+ .round_rate = &tegra2_emc_clk_round_rate,
+ .reset = &tegra2_periph_clk_reset,
+};
+
/* Clock doubler ops */
static void tegra2_clk_double_init(struct clk *c)
{
@@ -1157,7 +1205,8 @@ static void tegra_clk_shared_bus_update(struct clk *bus)
rate = max(c->u.shared_bus_user.rate, rate);
}
- clk_set_rate(bus, rate);
+ if (rate != clk_get_rate(bus))
+ clk_set_rate(bus, rate);
};
static void tegra_clk_shared_bus_init(struct clk *c)
@@ -1839,6 +1888,18 @@ static struct clk_mux_sel mux_clk_32k[] = {
{ 0, 0},
};
+static struct clk tegra_clk_emc = {
+ .name = "emc",
+ .ops = &tegra_emc_clk_ops,
+ .reg = 0x19c,
+ .max_rate = 800000000,
+ .inputs = mux_pllm_pllc_pllp_clkm,
+ .flags = MUX | DIV_U71 | PERIPH_EMC_ENB,
+ .u.periph = {
+ .clk_num = 57,
+ },
+};
+
#define PERIPH_CLK(_name, _dev, _con, _clk_num, _reg, _max, _inputs, _flags) \
{ \
.name = _name, \
@@ -1927,13 +1988,17 @@ struct clk tegra_list_clks[] = {
PERIPH_CLK("usbd", "fsl-tegra-udc", NULL, 22, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
PERIPH_CLK("usb2", "tegra-ehci.1", NULL, 58, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
PERIPH_CLK("usb3", "tegra-ehci.2", NULL, 59, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
- PERIPH_CLK("emc", "emc", NULL, 57, 0x19c, 800000000, mux_pllm_pllc_pllp_clkm, MUX | DIV_U71 | PERIPH_EMC_ENB),
PERIPH_CLK("dsi", "dsi", NULL, 48, 0, 500000000, mux_plld, 0), /* scales with voltage */
PERIPH_CLK("csi", "tegra_camera", "csi", 52, 0, 72000000, mux_pllp_out3, 0),
PERIPH_CLK("isp", "tegra_camera", "isp", 23, 0, 150000000, mux_clk_m, 0), /* same frequency as VI */
PERIPH_CLK("csus", "tegra_camera", "csus", 92, 0, 150000000, mux_clk_m, PERIPH_NO_RESET),
SHARED_CLK("avp.sclk", "tegra-avp", "sclk", &tegra_clk_sclk),
+ SHARED_CLK("avp.emc", "tegra-avp", "emc", &tegra_clk_emc),
+ SHARED_CLK("cpu.emc", "cpu", "emc", &tegra_clk_emc),
+ SHARED_CLK("disp1.emc", "tegradc.0", "emc", &tegra_clk_emc),
+ SHARED_CLK("disp2.emc", "tegradc.1", "emc", &tegra_clk_emc),
+ SHARED_CLK("hdmi.emc", "hdmi", "emc", &tegra_clk_emc),
};
#define CLK_DUPLICATE(_name, _dev, _con) \
@@ -2008,6 +2073,7 @@ struct clk *tegra_ptr_clks[] = {
&tegra_clk_virtual_cpu,
&tegra_clk_blink,
&tegra_clk_cop,
+ &tegra_clk_emc,
};
static void tegra2_init_one_clock(struct clk *c)
diff --git a/arch/arm/mach-tegra/tegra2_dvfs.c b/arch/arm/mach-tegra/tegra2_dvfs.c
index 1bc1c4dce0d2..b58a7d2ef92d 100644
--- a/arch/arm/mach-tegra/tegra2_dvfs.c
+++ b/arch/arm/mach-tegra/tegra2_dvfs.c
@@ -150,6 +150,7 @@ static struct dvfs dvfs_init[] = {
CPU_DVFS("cpu", 3, MHZ, 730, 760, 845, 845, 1000),
/* Core voltages (mV): 950, 1000, 1100, 1200, 1275 */
+ CORE_DVFS("emc", 1, KHZ, 57000, 333000, 333000, 666000, 666000),
#if 0
/*
diff --git a/arch/arm/mach-tegra/tegra2_emc.c b/arch/arm/mach-tegra/tegra2_emc.c
new file mode 100644
index 000000000000..bd4fa27b2086
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra2_emc.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Colin Cross <ccross@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+
+#include <mach/iomap.h>
+
+#include "tegra2_emc.h"
+
+#ifdef CONFIG_TEGRA_EMC_SCALING_ENABLE
+static bool emc_enable = true;
+#else
+static bool emc_enable;
+#endif
+module_param(emc_enable, bool, 0644);
+
+static void __iomem *emc = IO_ADDRESS(TEGRA_EMC_BASE);
+static const struct tegra_emc_table *tegra_emc_table;
+static int tegra_emc_table_size;
+
+static inline void emc_writel(u32 val, unsigned long addr)
+{
+ writel(val, emc + addr);
+}
+
+static inline u32 emc_readl(unsigned long addr)
+{
+ return readl(emc + addr);
+}
+
+static const unsigned long emc_reg_addr[TEGRA_EMC_NUM_REGS] = {
+ 0x2c, /* RC */
+ 0x30, /* RFC */
+ 0x34, /* RAS */
+ 0x38, /* RP */
+ 0x3c, /* R2W */
+ 0x40, /* W2R */
+ 0x44, /* R2P */
+ 0x48, /* W2P */
+ 0x4c, /* RD_RCD */
+ 0x50, /* WR_RCD */
+ 0x54, /* RRD */
+ 0x58, /* REXT */
+ 0x5c, /* WDV */
+ 0x60, /* QUSE */
+ 0x64, /* QRST */
+ 0x68, /* QSAFE */
+ 0x6c, /* RDV */
+ 0x70, /* REFRESH */
+ 0x74, /* BURST_REFRESH_NUM */
+ 0x78, /* PDEX2WR */
+ 0x7c, /* PDEX2RD */
+ 0x80, /* PCHG2PDEN */
+ 0x84, /* ACT2PDEN */
+ 0x88, /* AR2PDEN */
+ 0x8c, /* RW2PDEN */
+ 0x90, /* TXSR */
+ 0x94, /* TCKE */
+ 0x98, /* TFAW */
+ 0x9c, /* TRPAB */
+ 0xa0, /* TCLKSTABLE */
+ 0xa4, /* TCLKSTOP */
+ 0xa8, /* TREFBW */
+ 0xac, /* QUSE_EXTRA */
+ 0x114, /* FBIO_CFG6 */
+ 0xb0, /* ODT_WRITE */
+ 0xb4, /* ODT_READ */
+ 0x104, /* FBIO_CFG5 */
+ 0x2bc, /* CFG_DIG_DLL */
+ 0x2c0, /* DLL_XFORM_DQS */
+ 0x2c4, /* DLL_XFORM_QUSE */
+ 0x2e0, /* ZCAL_REF_CNT */
+ 0x2e4, /* ZCAL_WAIT_CNT */
+ 0x2a8, /* AUTO_CAL_INTERVAL */
+ 0x2d0, /* CFG_CLKTRIM_0 */
+ 0x2d4, /* CFG_CLKTRIM_1 */
+ 0x2d8, /* CFG_CLKTRIM_2 */
+};
+
+/* Select the closest EMC rate that is higher than the requested rate */
+long tegra_emc_round_rate(unsigned long rate)
+{
+ int i;
+ int best = -1;
+ unsigned long distance = ULONG_MAX;
+
+ if (!tegra_emc_table)
+ return -EINVAL;
+
+ if (!emc_enable)
+ return -EINVAL;
+
+ pr_debug("%s: %lu\n", __func__, rate);
+
+ /* The EMC clock rate is twice the bus rate, and the bus rate is
+ * measured in kHz */
+ rate = rate / 2 / 1000;
+
+ for (i = 0; i < tegra_emc_table_size; i++) {
+ if (tegra_emc_table[i].rate >= rate &&
+ (tegra_emc_table[i].rate - rate) < distance) {
+ distance = tegra_emc_table[i].rate - rate;
+ best = i;
+ }
+ }
+
+ if (best < 0)
+ return -EINVAL;
+
+ pr_debug("%s: using %lu\n", __func__, tegra_emc_table[best].rate);
+
+ return tegra_emc_table[best].rate * 2 * 1000;
+}
+
+/* The EMC registers have shadow registers. When the EMC clock is updated
+ * in the clock controller, the shadow registers are copied to the active
+ * registers, allowing glitchless memory bus frequency changes.
+ * This function updates the shadow registers for a new clock frequency,
+ * and relies on the clock lock on the emc clock to avoid races between
+ * multiple frequency changes */
+int tegra_emc_set_rate(unsigned long rate)
+{
+ int i;
+ int j;
+
+ if (!tegra_emc_table)
+ return -EINVAL;
+
+ /* The EMC clock rate is twice the bus rate, and the bus rate is
+ * measured in kHz */
+ rate = rate / 2 / 1000;
+
+ for (i = 0; i < tegra_emc_table_size; i++)
+ if (tegra_emc_table[i].rate == rate)
+ break;
+
+ if (i >= tegra_emc_table_size)
+ return -EINVAL;
+
+ pr_debug("%s: setting to %lu\n", __func__, rate);
+
+ for (j = 0; j < TEGRA_EMC_NUM_REGS; j++)
+ emc_writel(tegra_emc_table[i].regs[j], emc_reg_addr[j]);
+
+ emc_readl(tegra_emc_table[i].regs[TEGRA_EMC_NUM_REGS - 1]);
+
+ return 0;
+}
+
+void tegra_init_emc(const struct tegra_emc_table *table, int table_size)
+{
+ tegra_emc_table = table;
+ tegra_emc_table_size = table_size;
+}
diff --git a/arch/arm/mach-tegra/tegra2_emc.h b/arch/arm/mach-tegra/tegra2_emc.h
new file mode 100644
index 000000000000..3515e57fd0d9
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra2_emc.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Colin Cross <ccross@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#define TEGRA_EMC_NUM_REGS 46
+
+struct tegra_emc_table {
+ unsigned long rate;
+ u32 regs[TEGRA_EMC_NUM_REGS];
+};
+
+int tegra_emc_set_rate(unsigned long rate);
+long tegra_emc_round_rate(unsigned long rate);
+void tegra_init_emc(const struct tegra_emc_table *table, int table_size);
diff --git a/drivers/media/video/tegra/avp/avp_svc.c b/drivers/media/video/tegra/avp/avp_svc.c
index 57cd8019c305..2eed2891e556 100644
--- a/drivers/media/video/tegra/avp/avp_svc.c
+++ b/drivers/media/video/tegra/avp/avp_svc.c
@@ -82,6 +82,7 @@ struct avp_svc_info {
struct avp_clk clks[NUM_CLK_REQUESTS];
/* used for dvfs */
struct clk *sclk;
+ struct clk *emcclk;
struct mutex clk_lock;
@@ -352,6 +353,7 @@ static void do_svc_module_clock(struct avp_svc_info *avp_svc,
aclk = &avp_svc->clks[mod->clk_req];
if (msg->enable) {
if (aclk->refcnt++ == 0) {
+ clk_enable(avp_svc->emcclk);
clk_enable(avp_svc->sclk);
clk_enable(aclk->clk);
}
@@ -362,6 +364,7 @@ static void do_svc_module_clock(struct avp_svc_info *avp_svc,
} else if (--aclk->refcnt == 0) {
clk_disable(aclk->clk);
clk_disable(avp_svc->sclk);
+ clk_disable(avp_svc->emcclk);
}
}
mutex_unlock(&avp_svc->clk_lock);
@@ -631,8 +634,9 @@ void avp_svc_stop(struct avp_svc_info *avp_svc)
pr_info("%s: remote left clock '%s' on\n", __func__,
aclk->mod->name);
clk_disable(aclk->clk);
- /* sclk was enabled once for every clock */
+ /* sclk/emcclk was enabled once for every clock */
clk_disable(avp_svc->sclk);
+ clk_disable(avp_svc->emcclk);
}
aclk->refcnt = 0;
}
@@ -682,6 +686,21 @@ struct avp_svc_info *avp_svc_init(struct platform_device *pdev,
ret = -ENOENT;
goto err_get_clks;
}
+
+ avp_svc->emcclk = clk_get(&pdev->dev, "emc");
+ if (IS_ERR(avp_svc->emcclk)) {
+ pr_err("avp_svc: Couldn't get emcclk for dvfs\n");
+ ret = -ENOENT;
+ goto err_get_clks;
+ }
+
+ /*
+ * The emc is a shared clock, it will be set to the highest
+ * requested rate from any user. Set the rate to ULONG_MAX to
+ * always request the max rate whenever this request is enabled
+ */
+ clk_set_rate(avp_svc->emcclk, ULONG_MAX);
+
avp_svc->rpc_node = rpc_node;
mutex_init(&avp_svc->clk_lock);
@@ -694,6 +713,8 @@ err_get_clks:
clk_put(avp_svc->clks[i].clk);
if (!IS_ERR_OR_NULL(avp_svc->sclk))
clk_put(avp_svc->sclk);
+ if (!IS_ERR_OR_NULL(avp_svc->emcclk))
+ clk_put(avp_svc->emcclk);
err_alloc:
return ERR_PTR(ret);
}
@@ -705,6 +726,7 @@ void avp_svc_destroy(struct avp_svc_info *avp_svc)
for (i = 0; i < NUM_CLK_REQUESTS; i++)
clk_put(avp_svc->clks[i].clk);
clk_put(avp_svc->sclk);
+ clk_put(avp_svc->emcclk);
kfree(avp_svc);
}
diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c
index 5c65e3c1a61a..3c3a4754b7dc 100644
--- a/drivers/video/tegra/dc/dc.c
+++ b/drivers/video/tegra/dc/dc.c
@@ -967,6 +967,7 @@ static bool _tegra_dc_enable(struct tegra_dc *dc)
tegra_dc_setup_clk(dc, dc->clk);
clk_enable(dc->clk);
+ clk_enable(dc->emc_clk);
enable_irq(dc->irq);
tegra_dc_init(dc);
@@ -997,6 +998,7 @@ static void _tegra_dc_disable(struct tegra_dc *dc)
if (dc->out_ops && dc->out_ops->disable)
dc->out_ops->disable(dc);
+ clk_disable(dc->emc_clk);
clk_disable(dc->clk);
tegra_dvfs_set_rate(dc->clk, 0);
@@ -1029,6 +1031,7 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
{
struct tegra_dc *dc;
struct clk *clk;
+ struct clk *emc_clk;
struct resource *res;
struct resource *base_res;
struct resource *fb_mem = NULL;
@@ -1085,7 +1088,22 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
goto err_iounmap_reg;
}
+ emc_clk = clk_get(&ndev->dev, "emc");
+ if (IS_ERR_OR_NULL(emc_clk)) {
+ dev_err(&ndev->dev, "can't get emc clock\n");
+ ret = -ENOENT;
+ goto err_put_clk;
+ }
+
+ /*
+ * The emc is a shared clock, it will be set to the highest
+ * requested rate from any user. Set the rate to ULONG_MAX to
+ * always request the max rate whenever this request is enabled
+ */
+ clk_set_rate(emc_clk, ULONG_MAX);
+
dc->clk = clk;
+ dc->emc_clk = emc_clk;
dc->base_res = base_res;
dc->base = base;
dc->irq = irq;
@@ -1108,7 +1126,7 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
dev_name(&ndev->dev), dc)) {
dev_err(&ndev->dev, "request_irq %d failed\n", irq);
ret = -EBUSY;
- goto err_put_clk;
+ goto err_put_emc_clk;
}
/* hack to ballence enable_irq calls in _tegra_dc_enable() */
@@ -1158,6 +1176,8 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
err_free_irq:
free_irq(irq, dc);
+err_put_emc_clk:
+ clk_put(emc_clk);
err_put_clk:
clk_put(clk);
err_iounmap_reg:
@@ -1187,6 +1207,7 @@ static int tegra_dc_remove(struct nvhost_device *ndev)
_tegra_dc_disable(dc);
free_irq(dc->irq, dc);
+ clk_put(dc->emc_clk);
clk_put(dc->clk);
iounmap(dc->base);
if (dc->fb_mem)
diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h
index 7d0e340a6ee2..253d03f057d7 100644
--- a/drivers/video/tegra/dc/dc_priv.h
+++ b/drivers/video/tegra/dc/dc_priv.h
@@ -60,6 +60,7 @@ struct tegra_dc {
int irq;
struct clk *clk;
+ struct clk *emc_clk;
bool enabled;