diff options
-rw-r--r-- | arch/arm/mach-tegra/include/mach/nvhost.h | 10 | ||||
-rw-r--r-- | drivers/video/tegra/host/dev.c | 22 | ||||
-rwxr-xr-x | drivers/video/tegra/host/nvhost_acm.c | 113 | ||||
-rw-r--r-- | drivers/video/tegra/host/nvhost_acm.h | 13 |
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) { |