summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/include/mach/nvhost.h10
-rw-r--r--drivers/video/tegra/host/dev.c22
-rwxr-xr-xdrivers/video/tegra/host/nvhost_acm.c113
-rw-r--r--drivers/video/tegra/host/nvhost_acm.h13
4 files changed, 157 insertions, 1 deletions
diff --git a/arch/arm/mach-tegra/include/mach/nvhost.h b/arch/arm/mach-tegra/include/mach/nvhost.h
index fc18f2e6292e..fd9cdc70ee82 100644
--- a/arch/arm/mach-tegra/include/mach/nvhost.h
+++ b/arch/arm/mach-tegra/include/mach/nvhost.h
@@ -128,6 +128,10 @@ struct nvhost_set_nvmap_fd_args {
__u32 fd;
};
+struct nvhost_clk_rate_args {
+ __u64 rate;
+};
+
#define NVHOST_IOCTL_CHANNEL_FLUSH \
_IOR(NVHOST_IOCTL_MAGIC, 1, struct nvhost_get_param_args)
#define NVHOST_IOCTL_CHANNEL_GET_SYNCPOINTS \
@@ -142,8 +146,12 @@ struct nvhost_set_nvmap_fd_args {
_IOR(NVHOST_IOCTL_MAGIC, 6, struct nvhost_get_param_args)
#define NVHOST_IOCTL_CHANNEL_SUBMIT_EXT \
_IOW(NVHOST_IOCTL_MAGIC, 7, struct nvhost_submit_hdr_ext)
+#define NVHOST_IOCTL_CHANNEL_GET_CLK_RATE \
+ _IOR(NVHOST_IOCTL_MAGIC, 8, struct nvhost_clk_rate_args)
+#define NVHOST_IOCTL_CHANNEL_SET_CLK_RATE \
+ _IOW(NVHOST_IOCTL_MAGIC, 9, struct nvhost_clk_rate_args)
#define NVHOST_IOCTL_CHANNEL_LAST \
- _IOC_NR(NVHOST_IOCTL_CHANNEL_SUBMIT_EXT)
+ _IOC_NR(NVHOST_IOCTL_CHANNEL_SET_CLK_RATE)
#define NVHOST_IOCTL_CHANNEL_MAX_ARG_SIZE sizeof(struct nvhost_submit_hdr_ext)
struct nvhost_ctrl_syncpt_read_args {
diff --git a/drivers/video/tegra/host/dev.c b/drivers/video/tegra/host/dev.c
index fdbf5cc8efc1..8fa5dbca4573 100644
--- a/drivers/video/tegra/host/dev.c
+++ b/drivers/video/tegra/host/dev.c
@@ -69,6 +69,7 @@ static int nvhost_channelrelease(struct inode *inode, struct file *filp)
filp->private_data = NULL;
+ nvhost_module_remove_client(&priv->ch->mod, priv);
nvhost_putchannel(priv->ch, priv->hwctx);
if (priv->hwctx)
@@ -106,6 +107,7 @@ static int nvhost_channelopen(struct inode *inode, struct file *filp)
gather_size = sizeof(struct nvhost_op_pair) * NVHOST_MAX_GATHERS;
priv->gather_mem = nvmap_alloc(ch->dev->nvmap, gather_size, 32,
NVMAP_HANDLE_CACHEABLE);
+ nvhost_module_add_client(&ch->mod, priv);
if (IS_ERR(priv->gather_mem))
goto fail;
@@ -431,6 +433,26 @@ static long nvhost_channelctl(struct file *filp,
priv->nvmap = new_client;
break;
}
+ case NVHOST_IOCTL_CHANNEL_GET_CLK_RATE:
+ {
+ unsigned long rate;
+ struct nvhost_clk_rate_args *arg =
+ (struct nvhost_clk_rate_args *)buf;
+
+ err = nvhost_module_get_rate(&priv->ch->mod, &rate, 0);
+ if (err == 0)
+ arg->rate = rate;
+ break;
+ }
+ case NVHOST_IOCTL_CHANNEL_SET_CLK_RATE:
+ {
+ struct nvhost_clk_rate_args *arg =
+ (struct nvhost_clk_rate_args *)buf;
+ unsigned long rate = (unsigned long)arg->rate;
+
+ err = nvhost_module_set_rate(&priv->ch->mod, priv, rate, 0);
+ break;
+ }
default:
err = -ENOTTY;
break;
diff --git a/drivers/video/tegra/host/nvhost_acm.c b/drivers/video/tegra/host/nvhost_acm.c
index 4fd1820df0b5..a64ae7735e72 100755
--- a/drivers/video/tegra/host/nvhost_acm.c
+++ b/drivers/video/tegra/host/nvhost_acm.c
@@ -21,6 +21,7 @@
*/
#include "dev.h"
+#include <linux/slab.h>
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/err.h>
@@ -121,6 +122,117 @@ static int get_module_powergate_id(const char *module)
return -1;
}
+int nvhost_module_get_rate(struct nvhost_module *mod, unsigned long *rate,
+ int index)
+{
+ struct clk *c;
+
+ c = mod->clk[index];
+ if (IS_ERR_OR_NULL(c))
+ return -EINVAL;
+
+ nvhost_module_busy(mod);
+ *rate = clk_get_rate(c);
+ nvhost_module_idle(mod);
+
+ return 0;
+}
+
+int nvhost_module_update_rate(struct nvhost_module *mod, int index)
+{
+ unsigned long rate = 0;
+ struct nvhost_module_client *m;
+
+ if (list_empty(&mod->client_list))
+ rate = ULONG_MAX;
+
+ list_for_each_entry(m, &mod->client_list, node) {
+ rate = max(m->rate[index], rate);
+ }
+ if (IS_ERR_OR_NULL(mod->clk[index]))
+ return -EINVAL;
+ clk_set_rate(mod->clk[index], rate);
+ return 0;
+}
+
+static unsigned long nvhost_module_round_rate(struct clk *c, unsigned long rate)
+{
+ unsigned long round_rate = 0;
+ unsigned long max_rate;
+ unsigned long delta = 0;
+
+ max_rate = clk_round_rate(c, ULONG_MAX);
+ if (rate >= max_rate)
+ return max_rate;
+
+ max_rate = rate;
+
+ round_rate = clk_round_rate(c,rate);
+ if (round_rate >= max_rate)
+ return round_rate;
+
+ delta = round_rate - clk_round_rate(c, round_rate - 1);
+
+ do {
+ rate = rate + delta;
+ round_rate = clk_round_rate(c, rate);
+ } while(round_rate < max_rate);
+
+ return round_rate;
+}
+
+int nvhost_module_set_rate(struct nvhost_module *mod, void *priv,
+ unsigned long rate, int index)
+{
+ struct nvhost_module_client *m;
+
+ list_for_each_entry(m, &mod->client_list, node) {
+ if (m->priv == priv) {
+ rate = nvhost_module_round_rate(mod->clk[index], rate);
+ m->rate[index] = rate;
+ break;
+ }
+ }
+ return nvhost_module_update_rate(mod, index);
+}
+
+int nvhost_module_add_client(struct nvhost_module *mod, void *priv)
+{
+ int i;
+ unsigned long rate;
+ struct nvhost_module_client *client;
+
+ client = kzalloc(sizeof(*client), GFP_KERNEL);
+ if (!client) {
+ return -ENOMEM;
+ }
+ INIT_LIST_HEAD(&client->node);
+ client->priv = priv;
+
+ for (i = 0; i < mod->num_clks; i++) {
+ rate = clk_round_rate(mod->clk[i], ULONG_MAX);
+ client->rate[i] = rate;
+ }
+ list_add_tail(&client->node, &mod->client_list);
+ return 0;
+}
+
+void nvhost_module_remove_client(struct nvhost_module *mod, void *priv)
+{
+ struct nvhost_module_client *m;
+
+ list_for_each_entry(m, &mod->client_list, node) {
+ if (priv == m->priv) {
+ list_del(&m->node);
+ break;
+ }
+ }
+ m->priv = NULL;
+ kfree(m);
+
+ nvhost_module_update_rate(mod, 0);
+}
+
int nvhost_module_init(struct nvhost_module *mod, const char *name,
nvhost_modulef func, struct nvhost_module *parent,
struct device *dev)
@@ -128,6 +240,7 @@ int nvhost_module_init(struct nvhost_module *mod, const char *name,
int i = 0;
mod->name = name;
+ INIT_LIST_HEAD(&mod->client_list);
while (i < NVHOST_MODULE_MAX_CLOCKS) {
long rate;
mod->clk[i] = clk_get(dev, get_module_clk_id(name, i));
diff --git a/drivers/video/tegra/host/nvhost_acm.h b/drivers/video/tegra/host/nvhost_acm.h
index f7e28af8e9cb..0f402c9a4fdb 100644
--- a/drivers/video/tegra/host/nvhost_acm.h
+++ b/drivers/video/tegra/host/nvhost_acm.h
@@ -39,6 +39,12 @@ enum nvhost_power_action {
typedef void (*nvhost_modulef)(struct nvhost_module *mod, enum nvhost_power_action action);
+struct nvhost_module_client {
+ struct list_head node;
+ unsigned long rate[NVHOST_MODULE_MAX_CLOCKS];
+ void *priv;
+};
+
struct nvhost_module {
const char *name;
nvhost_modulef func;
@@ -51,6 +57,7 @@ struct nvhost_module {
wait_queue_head_t idle;
struct nvhost_module *parent;
int powergate_id;
+ struct list_head client_list;
};
int nvhost_module_init(struct nvhost_module *mod, const char *name,
@@ -61,6 +68,12 @@ void nvhost_module_suspend(struct nvhost_module *mod, bool system_suspend);
void nvhost_module_busy(struct nvhost_module *mod);
void nvhost_module_idle_mult(struct nvhost_module *mod, int refs);
+int nvhost_module_add_client(struct nvhost_module *mod, void *priv);
+void nvhost_module_remove_client(struct nvhost_module *mod, void *priv);
+int nvhost_module_get_rate(struct nvhost_module *mod, unsigned long *rate,
+ int index);
+int nvhost_module_set_rate(struct nvhost_module *mod, void *priv,
+ unsigned long rate, int index);
static inline bool nvhost_module_powered(struct nvhost_module *mod)
{