summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPrashant Gaikwad <pgaikwad@nvidia.com>2011-09-08 19:50:05 +0530
committerRohan Somvanshi <rsomvanshi@nvidia.com>2011-09-19 23:57:49 -0700
commit04d485c9911b245195b62a8131613ff77158b3d4 (patch)
tree4a4b3dd3c5a9bdecb515613684d0e0aead175373
parent090834aee810f66e0d332f499b4da3739ce243ea (diff)
video: tegra: host: Add ioctl to set/get clk rate
Host modules are initialized to max rate. Not all use cases require clocks at max rate, which increases the power consumption. Modules from user space can request for the lower clk rate using this ioctl. Bug 850467 Signed-off-by: Prashant Gaikwad <pgaikwad@nvidia.com> Reviewed-on: http://git-master/r/44579 (cherry picked from commit 621b21120db2d8894a564701637fe0a95aae6442) Change-Id: Ib676bd3607a09cc2303980465eac2d5505bd2ded Reviewed-on: http://git-master/r/51374 Reviewed-by: Prashant Gaikwad <pgaikwad@nvidia.com> Tested-by: Prashant Gaikwad <pgaikwad@nvidia.com> Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com> Tested-by: Gerrit_Virtual_Submit
-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)
{