summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPrashant Gaikwad <pgaikwad@nvidia.com>2011-08-02 21:03:59 +0530
committerVarun Colbert <vcolbert@nvidia.com>2011-08-08 11:24:43 -0700
commit621b21120db2d8894a564701637fe0a95aae6442 (patch)
treef75187ccad820cf1d57e20745f7e7b6b4ff87354
parent5728079a265ce38e782a9766a6ee6e0cfbe45fef (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 Change-Id: I1c7a8dfd159460e7c5a27813e3a08a992a20c132 Signed-off-by: Prashant Gaikwad <pgaikwad@nvidia.com> Reviewed-on: http://git-master/r/44579 Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com> Tested-by: Bharat Nihalani <bnihalani@nvidia.com> Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com>
-rw-r--r--arch/arm/mach-tegra/include/mach/nvhost.h10
-rw-r--r--drivers/video/tegra/host/dev.c22
-rw-r--r--drivers/video/tegra/host/nvhost_acm.c105
-rw-r--r--drivers/video/tegra/host/nvhost_acm.h13
4 files changed, 149 insertions, 1 deletions
diff --git a/arch/arm/mach-tegra/include/mach/nvhost.h b/arch/arm/mach-tegra/include/mach/nvhost.h
index 01cb1cff390e..8d7031791eab 100644
--- a/arch/arm/mach-tegra/include/mach/nvhost.h
+++ b/arch/arm/mach-tegra/include/mach/nvhost.h
@@ -133,6 +133,10 @@ struct nvhost_read_3d_reg_args {
__u32 value;
};
+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 \
@@ -149,8 +153,12 @@ struct nvhost_read_3d_reg_args {
_IOW(NVHOST_IOCTL_MAGIC, 7, struct nvhost_submit_hdr_ext)
#define NVHOST_IOCTL_CHANNEL_READ_3D_REG \
_IOWR(NVHOST_IOCTL_MAGIC, 8, struct nvhost_read_3d_reg_args)
+#define NVHOST_IOCTL_CHANNEL_GET_CLK_RATE \
+ _IOR(NVHOST_IOCTL_MAGIC, 9, struct nvhost_clk_rate_args)
+#define NVHOST_IOCTL_CHANNEL_SET_CLK_RATE \
+ _IOW(NVHOST_IOCTL_MAGIC, 10, struct nvhost_clk_rate_args)
#define NVHOST_IOCTL_CHANNEL_LAST \
- _IOC_NR(NVHOST_IOCTL_CHANNEL_READ_3D_REG)
+ _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 c9e478ab8412..41b3493ce59e 100644
--- a/drivers/video/tegra/host/dev.c
+++ b/drivers/video/tegra/host/dev.c
@@ -76,6 +76,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)
@@ -111,6 +112,7 @@ static int nvhost_channelopen(struct inode *inode, struct file *filp)
}
filp->private_data = priv;
priv->ch = ch;
+ nvhost_module_add_client(&ch->mod, priv);
priv->gather_mem = nvmap_alloc(ch->dev->nvmap,
sizeof(u32) * 2 * NVHOST_MAX_GATHERS, 32,
NVMAP_HANDLE_CACHEABLE);
@@ -422,6 +424,26 @@ static long nvhost_channelctl(struct file *filp,
case NVHOST_IOCTL_CHANNEL_READ_3D_REG:
err = nvhost_ioctl_channel_read_3d_reg(priv, (void *)buf);
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 4d14dc1093dd..e477d5a6ad85 100644
--- 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>
@@ -128,6 +129,109 @@ static bool _3d_powergating_disabled(void)
return 1;
}
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+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;
+
+ *rate = clk_get_rate(c);
+ return 0;
+}
+
+int nvhost_module_update_rate(struct nvhost_module *mod, int index)
+{
+ unsigned long rate = 0;
+ struct nvhost_module_client *m;
+
+ list_for_each_entry(m, &mod->client_list, node) {
+ rate = max(m->rate[index], rate);
+ }
+ if (!mod->clk[index])
+ return -EINVAL;
+ clk_set_rate(mod->clk[index], rate);
+ return 0;
+}
+
+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 = clk_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], 0);
+ 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)
+{
+ int i;
+ 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);
+ for (i = 0; i < mod->num_clks; i++)
+ nvhost_module_update_rate(mod, i);
+}
+#else
+int nvhost_module_get_rate(struct nvhost_module *mod, unsigned long *rate,
+ int index)
+{
+ return 0;
+}
+
+int nvhost_module_set_rate(struct nvhost_module *mod, void *priv,
+ unsigned long rate, int index)
+{
+ return 0;
+}
+
+int nvhost_module_add_client(struct nvhost_module *mod, void *priv)
+{
+ return 0;
+}
+
+void nvhost_module_remove_client(struct nvhost_module *mod, void *priv)
+{
+}
+#endif
+
int nvhost_module_init(struct nvhost_module *mod, const char *name,
nvhost_modulef func, struct nvhost_module *parent,
struct device *dev)
@@ -136,6 +240,7 @@ int nvhost_module_init(struct nvhost_module *mod, const char *name,
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 9736eb64c633..2efdadf29723 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;
@@ -53,6 +59,7 @@ struct nvhost_module {
int powergate_id;
int powergate_id2;
int powerdown_delay;
+ struct list_head client_list;
};
int nvhost_module_init(struct nvhost_module *mod, const char *name,
@@ -63,6 +70,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)
{