diff options
author | Ken Adams <kadams@nvidia.com> | 2011-06-18 10:14:42 -0400 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:47:24 -0800 |
commit | 74460b7effdcd931f287da336e664f5819ee12c5 (patch) | |
tree | c08687063d197438eaa40725bb864866078eae66 | |
parent | dc83973f99b5567af802f70f40bcf602952f43ca (diff) |
video: tegra: refactor for multiple chip support
Original-Change-Id: Ia203886a3b013612b4159393ff43a25a313d1ece
Reviewed-on: http://git-master/r/35911
Reviewed-by: Varun Colbert <vcolbert@nvidia.com>
Tested-by: Varun Colbert <vcolbert@nvidia.com>
Rebase-Id: R01b763362c13e09111f60700c3d3a7d2a9a3fc1c
33 files changed, 2196 insertions, 1286 deletions
diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h index 31706dee5c47..df03d171203e 100644 --- a/drivers/video/tegra/dc/dc_priv.h +++ b/drivers/video/tegra/dc/dc_priv.h @@ -25,6 +25,7 @@ #include <linux/completion.h> #include "../host/dev.h" +#include "../host/t20/syncpt_t20.h" #define WIN_IS_TILED(win) ((win)->flags & TEGRA_WIN_FLAG_TILED) #define WIN_IS_ENABLED(win) ((win)->flags & TEGRA_WIN_FLAG_ENABLED) diff --git a/drivers/video/tegra/host/Makefile b/drivers/video/tegra/host/Makefile index c13f8348ed9c..e6b7edfa917f 100644 --- a/drivers/video/tegra/host/Makefile +++ b/drivers/video/tegra/host/Makefile @@ -5,9 +5,10 @@ nvhost-objs = \ nvhost_cpuaccess.o \ nvhost_intr.o \ nvhost_channel.o \ - nvhost_3dctx.o \ dev.o \ bus.o \ debug.o +obj-$(CONFIG_TEGRA_GRHOST) += t20/ +obj-$(CONFIG_TEGRA_GRHOST) += t30/ obj-$(CONFIG_TEGRA_GRHOST) += nvhost.o diff --git a/drivers/video/tegra/host/chip_support.h b/drivers/video/tegra/host/chip_support.h new file mode 100644 index 000000000000..0f44770b6263 --- /dev/null +++ b/drivers/video/tegra/host/chip_support.h @@ -0,0 +1,149 @@ +/* + * drivers/video/tegra/host/chip_support.h + * + * Tegra Graphics Host Chip Support + * + * Copyright (c) 2011, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _NVHOST_CHIP_SUPPORT_H_ +#define _NVHOST_CHIP_SUPPORT_H_ + +struct output; + +struct nvhost_chip_support { + struct { + int (*init)(struct nvhost_channel *, + struct nvhost_master *, + int chid); + int (*submit)(struct nvhost_channel *, + struct nvhost_hwctx *, + struct nvmap_client *, + u32 *gather, + u32 *gather_end, + struct nvhost_waitchk *waitchk, + struct nvhost_waitchk *waitchk_end, + u32 waitchk_mask, + struct nvmap_handle **unpins, + int nr_unpins, + u32 syncpt_id, + u32 syncpt_incrs, + u32 *syncpt_value, + bool null_kickoff); + } channel; + + struct { + void (*start)(struct nvhost_cdma *); + void (*stop)(struct nvhost_cdma *); + void (*kick)(struct nvhost_cdma *); + } cdma; + + struct { + void (*reset)(struct push_buffer *); + int (*init)(struct push_buffer *); + void (*destroy)(struct push_buffer *); + void (*push_to)(struct push_buffer *, + u32 op1, u32 op2); + void (*pop_from)(struct push_buffer *, + unsigned int slots); + u32 (*space)(struct push_buffer *); + u32 (*putptr)(struct push_buffer *); + } push_buffer; + + struct { + void (*show_channel_cdma)(struct nvhost_master *, + struct output *, + int chid); + void (*show_channel_fifo)(struct nvhost_master *, + struct output *, + int chid); + void (*show_mlocks)(struct nvhost_master *m, + struct output *o); + + } debug; + + struct { + void (*reset)(struct nvhost_syncpt *, u32 id); + void (*reset_wait_base)(struct nvhost_syncpt *, u32 id); + void (*read_wait_base)(struct nvhost_syncpt *, u32 id); + u32 (*update_min)(struct nvhost_syncpt *, u32 id); + void (*cpu_incr)(struct nvhost_syncpt *, u32 id); + int (*wait_check)(struct nvhost_syncpt *sp, + struct nvmap_client *nvmap, + u32 waitchk_mask, + struct nvhost_waitchk *wait, + struct nvhost_waitchk *waitend); + void (*debug)(struct nvhost_syncpt *); + const char * (*name)(struct nvhost_syncpt *, u32 id); + } syncpt; + + struct { + void (*init_host_sync)(struct nvhost_intr *); + void (*set_host_clocks_per_usec)( + struct nvhost_intr *, u32 clocks); + void (*set_syncpt_threshold)( + struct nvhost_intr *, u32 id, u32 thresh); + void (*enable_syncpt_intr)(struct nvhost_intr *, u32 id); + void (*disable_all_syncpt_intrs)(struct nvhost_intr *); + int (*request_host_general_irq)(struct nvhost_intr *); + void (*free_host_general_irq)(struct nvhost_intr *); + } intr; + + struct { + int (*mutex_try_lock)(struct nvhost_cpuaccess *, + unsigned int idx); + void (*mutex_unlock)(struct nvhost_cpuaccess *, + unsigned int idx); + } cpuaccess; +}; + + +int nvhost_init_t20_support(struct nvhost_master *host); +int nvhost_init_t30_support(struct nvhost_master *host); + + +/* place holder for chip id assumed to live in kernel/arch/arm/mach-tegra */ +struct tegra_chip_info { +#define TEGRA_SOC_CHIP_ARCH_T20 0 +#define TEGRA_SOC_CHIP_IMPL_T20 0 + u16 arch; +#define TEGRA_SOC_CHIP_ARCH_T30 1 +#define TEGRA_SOC_CHIP_IMPL_T30 0 + u16 impl; +}; + +#if 0 +extern int tegra_get_chip_info(struct tegra_chip_info *); +#else +static inline int tegra_get_chip_info(struct tegra_chip_info *ci) +{ +#if defined(CONFIG_ARCH_TEGRA_3x_SOC) + ci->arch = TEGRA_SOC_CHIP_ARCH_T30; + ci->impl = TEGRA_SOC_CHIP_IMPL_T30; + +#elif defined(CONFIG_ARCH_TEGRA_2x_SOC) + ci->arch = TEGRA_SOC_CHIP_ARCH_T20; + ci->impl = TEGRA_SOC_CHIP_IMPL_T20; + +#else + return -ENODEV; +#endif + + return 0; +} +#endif + +#endif /* _NVHOST_CHIP_SUPPORT_H_ */ diff --git a/drivers/video/tegra/host/debug.c b/drivers/video/tegra/host/debug.c index f4c6562a9b43..c0cdf65ccd18 100644 --- a/drivers/video/tegra/host/debug.c +++ b/drivers/video/tegra/host/debug.c @@ -23,26 +23,11 @@ #include <asm/io.h> #include "dev.h" - -struct output { - void (*fn)(void *ctx, const char* str, size_t len); - void *ctx; - char buf[256]; -}; +#include "debug.h" pid_t nvhost_debug_null_kickoff_pid; -static void write_to_seqfile(void *ctx, const char* str, size_t len) -{ - seq_write((struct seq_file *)ctx, str, len); -} - -static void write_to_printk(void *ctx, const char* str, size_t len) -{ - printk("%s", str); -} - -static void output(struct output *o, const char* fmt, ...) +void nvhost_debug_output(struct output *o, const char* fmt, ...) { va_list args; int len; @@ -53,294 +38,40 @@ static void output(struct output *o, const char* fmt, ...) o->fn(o->ctx, o->buf, len); } -enum { - NVHOST_DBG_STATE_CMD = 0, - NVHOST_DBG_STATE_DATA = 1, - NVHOST_DBG_STATE_GATHER = 2 -}; - -static int show_channel_command(struct output *o, u32 val, int *count) -{ - unsigned mask; - unsigned subop; - - switch (val >> 28) { - case 0x0: - mask = val & 0x3f; - if (mask) { - output(o, "SETCL(class=%03x, offset=%03x, mask=%02x, [", - val >> 6 & 0x3ff, val >> 16 & 0xfff, mask); - *count = hweight8(mask); - return NVHOST_DBG_STATE_DATA; - } else { - output(o, "SETCL(class=%03x)\n", val >> 6 & 0x3ff); - return NVHOST_DBG_STATE_CMD; - } - - case 0x1: - output(o, "INCR(offset=%03x, [", val >> 16 & 0xfff); - *count = val & 0xffff; - return NVHOST_DBG_STATE_DATA; - - case 0x2: - output(o, "NONINCR(offset=%03x, [", val >> 16 & 0xfff); - *count = val & 0xffff; - return NVHOST_DBG_STATE_DATA; - - case 0x3: - mask = val & 0xffff; - output(o, "MASK(offset=%03x, mask=%03x, [", - val >> 16 & 0xfff, mask); - *count = hweight16(mask); - return NVHOST_DBG_STATE_DATA; - - case 0x4: - output(o, "IMM(offset=%03x, data=%03x)\n", - val >> 16 & 0xfff, val & 0xffff); - return NVHOST_DBG_STATE_CMD; - - case 0x5: - output(o, "RESTART(offset=%08x)\n", val << 4); - return NVHOST_DBG_STATE_CMD; - - case 0x6: - output(o, "GATHER(offset=%03x, insert=%d, type=%d, count=%04x, addr=[", - val >> 16 & 0xfff, val >> 15 & 0x1, val >> 14 & 0x1, - val & 0x3fff); - *count = val & 0x3fff; // TODO: insert - return NVHOST_DBG_STATE_GATHER; - - case 0xe: - subop = val >> 24 & 0xf; - if (subop == 0) - output(o, "ACQUIRE_MLOCK(index=%d)\n", val & 0xff); - else if (subop == 1) - output(o, "RELEASE_MLOCK(index=%d)\n", val & 0xff); - else - output(o, "EXTEND_UNKNOWN(%08x)\n", val); - return NVHOST_DBG_STATE_CMD; - - default: - return NVHOST_DBG_STATE_CMD; - } -} - -static void show_channel_gather(struct output *o, phys_addr_t phys_addr, - u32 words); - -static void show_channel_word(struct output *o, int *state, int *count, - u32 addr, u32 val) -{ - switch (*state) { - case NVHOST_DBG_STATE_CMD: - if (addr) - output(o, "%08x: %08x:", addr, val); - else - output(o, "%08x:", val); - - *state = show_channel_command(o, val, count); - if (*state == NVHOST_DBG_STATE_DATA && *count == 0) { - *state = NVHOST_DBG_STATE_CMD; - output(o, "])\n"); - } - break; - - case NVHOST_DBG_STATE_DATA: - (*count)--; - output(o, "%08x%s", val, *count > 0 ? ", " : "])\n"); - if (*count == 0) - *state = NVHOST_DBG_STATE_CMD; - break; - - case NVHOST_DBG_STATE_GATHER: - *state = NVHOST_DBG_STATE_CMD; - output(o, "%08x]):\n", val); - show_channel_gather(o, val, *count); - break; - } -} - -/* - * TODO: This uses ioremap_xxx on memory which is deprecated. - * Also, it won't work properly with SMMU. - */ -static void show_channel_gather(struct output *o, phys_addr_t phys_addr, - u32 words) -{ - phys_addr_t map_base = phys_addr & PAGE_MASK; - phys_addr_t map_end = (phys_addr + words * 4 + PAGE_SIZE - 1) & PAGE_MASK; - phys_addr_t map_size = map_end - map_base; - phys_addr_t map_offset = phys_addr - map_base; - void *map_addr = ioremap_nocache(map_base, map_size); - int state = NVHOST_DBG_STATE_CMD; - int count, i; - - if (!map_addr) - return; - for (i = 0; i < words; i++) - show_channel_word(o, &state, &count, phys_addr + i * 4, - readl(map_addr + map_offset + i * 4)); - iounmap(map_addr); -} - -static void show_channel_pair(struct output *o, u32 addr, - u32 w0, u32 w1) -{ - int state = NVHOST_DBG_STATE_CMD; - int count; - - show_channel_word(o, &state, &count, addr, w0); - show_channel_word(o, &state, &count, addr, w1); -} - -static void show_channel_cdma(struct nvhost_master *m, - struct output *o, int chid) -{ - struct nvhost_channel *channel = m->channels + chid; - u32 dmaput, dmaget, dmactrl; - u32 cbstat, cbread; - u32 val, base, baseval; - u32 pbw[2]; - - dmaput = readl(channel->aperture + HOST1X_CHANNEL_DMAPUT); - dmaget = readl(channel->aperture + HOST1X_CHANNEL_DMAGET); - dmactrl = readl(channel->aperture + HOST1X_CHANNEL_DMACTRL); - cbread = readl(m->aperture + HOST1X_SYNC_CBREAD(chid)); - cbstat = readl(m->aperture + HOST1X_SYNC_CBSTAT(chid)); - - output(o, "%d-%s (%d): ", chid, - channel->mod.name, atomic_read(&channel->mod.refcount)); - - if ((dmactrl & 1) || !channel->cdma.push_buffer.mapped) { - output(o, "inactive\n\n"); - return; - } - - switch (cbstat) { - case 0x00010008: - output(o, "waiting on syncpt %d val %d\n", - cbread >> 24, cbread & 0xffffff); - break; - - case 0x00010009: - base = (cbread >> 16) & 0xff; - val = readl(m->aperture + HOST1X_SYNC_SYNCPT_BASE(base)); - baseval = val & 0xffff; - val = cbread & 0xffff; - output(o, "waiting on syncpt %d val %d " - "(base %d = %d; offset = %d)\n", - cbread >> 24, baseval + val, - base, baseval, val); - break; - - default: - output(o, "active class %02x, offset %04x, val %08x\n", - cbstat >> 16, cbstat & 0xffff, cbread); - break; - } - - nvhost_cdma_peek(&channel->cdma, dmaget, -1, pbw); - show_channel_pair(o, chid, pbw[0], pbw[1]); - output(o, "\n"); -} - -void show_channel_fifo(struct nvhost_master *m, - struct output *o, int chid) -{ - u32 val, rd_ptr, wr_ptr, start, end; - int state, count; - - val = readl(m->aperture + HOST1X_CHANNEL_FIFOSTAT); - if (val & (1 << 10)) - return; - - writel(0x0, m->aperture + HOST1X_SYNC_CFPEEK_CTRL); - writel((1 << 31) | (chid << 16), - m->aperture + HOST1X_SYNC_CFPEEK_CTRL); - - val = readl(m->aperture + HOST1X_SYNC_CFPEEK_PTRS); - rd_ptr = val & 0x1ff; - wr_ptr = (val >> 16) & 0x1ff; - - val = readl(m->aperture + HOST1X_SYNC_CF_SETUP(chid)); - start = val & 0x1ff; - end = (val >> 16) & 0x1ff; - - state = NVHOST_DBG_STATE_CMD; - output(o, "%d: fifo:\n", chid); - - do { - writel(0x0, m->aperture + HOST1X_SYNC_CFPEEK_CTRL); - writel((1 << 31) | (chid << 16) | rd_ptr, - m->aperture + HOST1X_SYNC_CFPEEK_CTRL); - val = readl(m->aperture + HOST1X_SYNC_CFPEEK_READ); - - show_channel_word(o, &state, &count, 0, val); - - if (rd_ptr == end) - rd_ptr = start; - else - rd_ptr++; - } while (rd_ptr != wr_ptr); - - if (state == NVHOST_DBG_STATE_DATA) - output(o, ", ...])\n"); - output(o, "\n"); - - writel(0x0, m->aperture + HOST1X_SYNC_CFPEEK_CTRL); -} static void show_channels(struct nvhost_master *m, struct output *o) { int i; - output(o, "---- channels ----\n"); - for (i = 0; i < NVHOST_NUMCHANNELS; i++) { - show_channel_cdma(m, o, i); - show_channel_fifo(m, o, i); + nvhost_debug_output(o, "---- channels ----\n"); + for (i = 0; i < m->nb_channels; i++) { + m->op.debug.show_channel_cdma(m, o, i); + m->op.debug.show_channel_fifo(m, o, i); } } -static void show_mlocks(struct nvhost_master *m, struct output *o) -{ - u32 __iomem *mlo_regs = m->sync_aperture + HOST1X_SYNC_MLOCK_OWNER_0; - int i; - - output(o, "---- mlocks ----\n"); - for (i = 0; i < NV_HOST1X_NB_MLOCKS; i++) { - u32 owner = readl(mlo_regs + i); - if (owner & 0x1) - output(o, "%d: locked by channel %d\n", - i, (owner >> 8) & 0xf); - else if (owner & 0x2) - output(o, "%d: locked by cpu\n", i); - else - output(o, "%d: unlocked\n", i); - } - output(o, "\n"); -} static void show_syncpts(struct nvhost_master *m, struct output *o) { int i; - - output(o, "---- syncpts ----\n"); - for (i = 0; i < NV_HOST1X_SYNCPT_NB_PTS; i++) { + BUG_ON(!m->op.syncpt.name); + nvhost_debug_output(o, "---- syncpts ----\n"); + for (i = 0; i < m->syncpt.nb_pts; i++) { u32 max = nvhost_syncpt_read_max(&m->syncpt, i); if (!max) continue; - output(o, "id %d (%s) min %d max %d\n", - i, nvhost_syncpt_name(i), + nvhost_debug_output(o, "id %d (%s) min %d max %d\n", + i, m->op.syncpt.name(&m->syncpt, i), nvhost_syncpt_update_min(&m->syncpt, i), max); } - output(o, "\n"); + nvhost_debug_output(o, "\n"); } static void show_all(struct nvhost_master *m, struct output *o) { nvhost_module_busy(&m->mod); - show_mlocks(m, o); + m->op.debug.show_mlocks(m, o); show_syncpts(m, o); show_channels(m, o); diff --git a/drivers/video/tegra/host/debug.h b/drivers/video/tegra/host/debug.h new file mode 100644 index 000000000000..829c29ecda2a --- /dev/null +++ b/drivers/video/tegra/host/debug.h @@ -0,0 +1,43 @@ +/* + * drivers/video/tegra/host/debug.h + * + * Tegra Graphics Host Debug + * + * Copyright (c) 2011 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef __NVHOST_DEBUG_H +#define __NVHOST_DEBUG_H + +struct output { + void (*fn)(void *ctx, const char* str, size_t len); + void *ctx; + char buf[256]; +}; + +static inline void write_to_seqfile(void *ctx, const char* str, size_t len) +{ + seq_write((struct seq_file *)ctx, str, len); +} + +static inline void write_to_printk(void *ctx, const char* str, size_t len) +{ + printk("%s", str); +} + +void nvhost_debug_output(struct output *o, const char* fmt, ...); + +#endif /*__NVHOST_DEBUG_H */ diff --git a/drivers/video/tegra/host/dev.c b/drivers/video/tegra/host/dev.c index 09ab33ff94d2..bfb7e111221b 100644 --- a/drivers/video/tegra/host/dev.c +++ b/drivers/video/tegra/host/dev.c @@ -45,7 +45,7 @@ #define IFACE_NAME "nvhost" static int nvhost_major = NVHOST_MAJOR; -static int nvhost_minor = NVHOST_CHANNEL_BASE; +static int nvhost_minor; static unsigned int register_sets; struct nvhost_channel_userctx { @@ -65,7 +65,7 @@ struct nvhost_channel_userctx { struct nvhost_ctrl_userctx { struct nvhost_master *dev; - u32 mod_locks[NV_HOST1X_NB_MLOCKS]; + u32 *mod_locks; }; static int nvhost_channelrelease(struct inode *inode, struct file *filp) @@ -308,7 +308,8 @@ static int nvhost_ioctl_channel_flush( null_kickoff = 1; /* context switch if needed, and submit user's gathers to the channel */ - err = nvhost_channel_submit(ctx->ch, ctx->hwctx, ctx->nvmap, + BUG_ON(!channel_op(ctx->ch).submit); + err = channel_op(ctx->ch).submit(ctx->ch, ctx->hwctx, ctx->nvmap, ctx->gathers, ctx->cur_gather, ctx->waitchks, ctx->cur_waitchk, ctx->hdr.waitchk_mask, @@ -438,9 +439,10 @@ static int nvhost_ctrlrelease(struct inode *inode, struct file *filp) filp->private_data = NULL; if (priv->mod_locks[0]) nvhost_module_idle(&priv->dev->mod); - for (i = 1; i < NV_HOST1X_NB_MLOCKS; i++) + for (i = 1; i < priv->dev->nb_mlocks; i++) if (priv->mod_locks[i]) nvhost_mutex_unlock(&priv->dev->cpuaccess, i); + kfree(priv->mod_locks); kfree(priv); return 0; } @@ -449,14 +451,21 @@ static int nvhost_ctrlopen(struct inode *inode, struct file *filp) { struct nvhost_master *host = container_of(inode->i_cdev, struct nvhost_master, cdev); struct nvhost_ctrl_userctx *priv; + u32 *mod_locks; trace_nvhost_ctrlopen(host->mod.name); priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) + mod_locks = kzalloc(sizeof(u32)*host->nb_mlocks, GFP_KERNEL); + + if (!(priv && mod_locks)) { + kfree(priv); + kfree(mod_locks); return -ENOMEM; + } priv->dev = host; + priv->mod_locks = mod_locks; filp->private_data = priv; return 0; } @@ -465,7 +474,7 @@ static int nvhost_ioctl_ctrl_syncpt_read( struct nvhost_ctrl_userctx *ctx, struct nvhost_ctrl_syncpt_read_args *args) { - if (args->id >= NV_HOST1X_SYNCPT_NB_PTS) + if (args->id >= ctx->dev->syncpt.nb_pts) return -EINVAL; trace_nvhost_ioctl_ctrl_syncpt_read(args->id); args->value = nvhost_syncpt_read(&ctx->dev->syncpt, args->id); @@ -476,7 +485,7 @@ static int nvhost_ioctl_ctrl_syncpt_incr( struct nvhost_ctrl_userctx *ctx, struct nvhost_ctrl_syncpt_incr_args *args) { - if (args->id >= NV_HOST1X_SYNCPT_NB_PTS) + if (args->id >= ctx->dev->syncpt.nb_pts) return -EINVAL; trace_nvhost_ioctl_ctrl_syncpt_incr(args->id); nvhost_syncpt_incr(&ctx->dev->syncpt, args->id); @@ -488,7 +497,7 @@ static int nvhost_ioctl_ctrl_syncpt_waitex( struct nvhost_ctrl_syncpt_waitex_args *args) { u32 timeout; - if (args->id >= NV_HOST1X_SYNCPT_NB_PTS) + if (args->id >= ctx->dev->syncpt.nb_pts) return -EINVAL; if (args->timeout == NVHOST_NO_TIMEOUT) timeout = MAX_SCHEDULE_TIMEOUT; @@ -506,7 +515,7 @@ static int nvhost_ioctl_ctrl_module_mutex( struct nvhost_ctrl_module_mutex_args *args) { int err = 0; - if (args->id >= NV_HOST1X_NB_MLOCKS || + if (args->id >= ctx->dev->nb_mlocks || args->lock > 1) return -EINVAL; @@ -538,7 +547,7 @@ static int nvhost_ioctl_ctrl_module_regrdwr( void *values = args->values; u32 vals[64]; - if (!nvhost_access_module_regs(&ctx->dev->cpuaccess, args->id) || + if (!(args->id < ctx->dev->nb_modules) || (num_offsets == 0)) return -EINVAL; @@ -638,7 +647,7 @@ static void power_host(struct nvhost_module *mod, enum nvhost_power_action actio */ } else if (action == NVHOST_POWER_ACTION_OFF) { int i; - for (i = 0; i < NVHOST_NUMCHANNELS; i++) + for (i = 0; i < dev->nb_channels; i++) nvhost_channel_suspend(&dev->channels[i]); nvhost_syncpt_save(&dev->syncpt); nvhost_intr_stop(&dev->intr); @@ -658,10 +667,11 @@ static int __devinit nvhost_user_init(struct nvhost_master *host) if (nvhost_major) { devno = MKDEV(nvhost_major, nvhost_minor); - err = register_chrdev_region(devno, NVHOST_NUMCHANNELS + 1, IFACE_NAME); + err = register_chrdev_region(devno, host->nb_channels + 1, + IFACE_NAME); } else { err = alloc_chrdev_region(&devno, nvhost_minor, - NVHOST_NUMCHANNELS + 1, IFACE_NAME); + host->nb_channels + 1, IFACE_NAME); nvhost_major = MAJOR(devno); } if (err < 0) { @@ -669,14 +679,9 @@ static int __devinit nvhost_user_init(struct nvhost_master *host) goto fail; } - for (i = 0; i < NVHOST_NUMCHANNELS; i++) { + for (i = 0; i < host->nb_channels; i++) { struct nvhost_channel *ch = &host->channels[i]; - if (!strcmp(ch->desc->name, "display") && - !nvhost_access_module_regs(&host->cpuaccess, - NVHOST_MODULE_DISPLAY_A)) - continue; - cdev_init(&ch->cdev, &nvhost_channelops); ch->cdev.owner = THIS_MODULE; @@ -697,7 +702,7 @@ static int __devinit nvhost_user_init(struct nvhost_master *host) cdev_init(&host->cdev, &nvhost_ctrlops); host->cdev.owner = THIS_MODULE; - devno = MKDEV(nvhost_major, nvhost_minor + NVHOST_NUMCHANNELS); + devno = MKDEV(nvhost_major, nvhost_minor + host->nb_channels); err = cdev_add(&host->cdev, devno, 1); if (err < 0) goto fail; @@ -714,6 +719,91 @@ fail: return err; } +static void nvhost_remove_chip_support(struct nvhost_master *host) +{ + + kfree(host->channels); + host->channels = 0; + + kfree(host->syncpt.min_val); + host->syncpt.min_val = 0; + + kfree(host->syncpt.max_val); + host->syncpt.max_val = 0; + + kfree(host->syncpt.base_val); + host->syncpt.base_val = 0; + + kfree(host->intr.syncpt); + host->intr.syncpt = 0; + + kfree(host->cpuaccess.regs); + host->cpuaccess.regs = 0; + + kfree(host->cpuaccess.reg_mem); + host->cpuaccess.reg_mem = 0; + + kfree(host->cpuaccess.lock_counts); + host->cpuaccess.lock_counts = 0; +} + +static int __devinit nvhost_init_chip_support(struct nvhost_master *host) +{ + int err; + err = tegra_get_chip_info(&host->chip_info); + if (err) + return err; + + switch (host->chip_info.arch) { + case TEGRA_SOC_CHIP_ARCH_T20: + err = nvhost_init_t20_support(host); + break; + + case TEGRA_SOC_CHIP_ARCH_T30: + err = nvhost_init_t30_support(host); + break; + default: + return -ENODEV; + } + + if (err) + return err; + + /* allocate items sized in chip specific support init */ + host->channels = kzalloc(sizeof(struct nvhost_channel) * + host->nb_channels, GFP_KERNEL); + + host->syncpt.min_val = kzalloc(sizeof(atomic_t) * + host->syncpt.nb_pts, GFP_KERNEL); + + host->syncpt.max_val = kzalloc(sizeof(atomic_t) * + host->syncpt.nb_pts, GFP_KERNEL); + + host->syncpt.base_val = kzalloc(sizeof(u32) * + host->syncpt.nb_bases, GFP_KERNEL); + + host->intr.syncpt = kzalloc(sizeof(struct nvhost_intr_syncpt) * + host->syncpt.nb_pts, GFP_KERNEL); + + host->cpuaccess.reg_mem = kzalloc(sizeof(struct resource *) * + host->nb_modules, GFP_KERNEL); + + host->cpuaccess.regs = kzalloc(sizeof(void __iomem *) * + host->nb_modules, GFP_KERNEL); + + host->cpuaccess.lock_counts = kzalloc(sizeof(atomic_t) * + host->nb_mlocks, GFP_KERNEL); + + if (!(host->channels && host->syncpt.min_val && + host->syncpt.max_val && host->syncpt.base_val && + host->intr.syncpt && host->cpuaccess.reg_mem && + host->cpuaccess.regs && host->cpuaccess.lock_counts)) { + /* frees happen in the support removal phase */ + return -ENOMEM; + } + + return 0; +} static int __devinit nvhost_probe(struct platform_device *pdev) { struct nvhost_master *host; @@ -755,27 +845,40 @@ static int __devinit nvhost_probe(struct platform_device *pdev) err = -ENXIO; goto fail; } - host->sync_aperture = host->aperture + - (NV_HOST1X_CHANNEL0_BASE + - HOST1X_CHANNEL_SYNC_REG_BASE); - for (i = 0; i < NVHOST_NUMCHANNELS; i++) { + err = nvhost_init_chip_support(host); + if (err) { + dev_err(&pdev->dev, "failed to init chip support\n"); + goto fail; + } + + for (i = 0; i < host->nb_channels; i++) { struct nvhost_channel *ch = &host->channels[i]; - err = nvhost_channel_init(ch, host, i); + BUG_ON(!host_channel_op(host).init); + err = host_channel_op(host).init(ch, host, i); if (err < 0) { dev_err(&pdev->dev, "failed to init channel %d\n", i); goto fail; } } + err = nvhost_cpuaccess_init(&host->cpuaccess, pdev); - if (err) goto fail; + if (err) + goto fail; + err = nvhost_intr_init(&host->intr, intr1->start, intr0->start); - if (err) goto fail; + if (err) + goto fail; + err = nvhost_user_init(host); - if (err) goto fail; + if (err) + goto fail; + err = nvhost_module_init(&host->mod, "host1x", power_host, NULL, &pdev->dev); - if (err) goto fail; + if (err) + goto fail; + platform_set_drvdata(pdev, host); @@ -791,6 +894,7 @@ static int __devinit nvhost_probe(struct platform_device *pdev) return 0; fail: + nvhost_remove_chip_support(host); if (host->nvmap) nvmap_client_put(host->nvmap); /* TODO: [ahatala 2010-05-04] */ @@ -800,6 +904,9 @@ fail: static int __exit nvhost_remove(struct platform_device *pdev) { + struct nvhost_master *host = platform_get_drvdata(pdev); + nvhost_remove_chip_support(host); + /*kfree(host);?*/ return 0; } diff --git a/drivers/video/tegra/host/dev.h b/drivers/video/tegra/host/dev.h index 688fa431a616..ee79bddf05d5 100644 --- a/drivers/video/tegra/host/dev.h +++ b/drivers/video/tegra/host/dev.h @@ -28,7 +28,7 @@ #include "nvhost_intr.h" #include "nvhost_cpuaccess.h" #include "nvhost_channel.h" -#include "nvhost_hardware.h" +#include "chip_support.h" #define NVHOST_MAJOR 0 /* dynamic */ @@ -43,9 +43,17 @@ struct nvhost_master { struct nvhost_syncpt syncpt; struct nvmap_client *nvmap; struct nvhost_cpuaccess cpuaccess; + u32 nb_mlocks; struct nvhost_intr intr; struct nvhost_module mod; - struct nvhost_channel channels[NVHOST_NUMCHANNELS]; + struct nvhost_channel *channels; + u32 nb_channels; + u32 nb_modules; + + u32 sync_queue_size; + + struct tegra_chip_info chip_info; + struct nvhost_chip_support op; }; void nvhost_debug_init(struct nvhost_master *master); diff --git a/drivers/video/tegra/host/nvhost_acm.c b/drivers/video/tegra/host/nvhost_acm.c index 8d06ee731dca..725aebfc0c9a 100644 --- a/drivers/video/tegra/host/nvhost_acm.c +++ b/drivers/video/tegra/host/nvhost_acm.c @@ -205,14 +205,14 @@ static void debug_not_idle(struct nvhost_master *dev) int i; bool lock_released = true; - for (i = 0; i < NVHOST_NUMCHANNELS; i++) { + for (i = 0; i < dev->nb_channels; i++) { struct nvhost_module *m = &dev->channels[i].mod; if (m->name) printk("tegra_grhost: %s: refcnt %d\n", m->name, atomic_read(&m->refcount)); } - for (i = 0; i < NV_HOST1X_SYNC_MLOCK_NUM; i++) { + for (i = 0; i < dev->nb_mlocks; i++) { int c = atomic_read(&dev->cpuaccess.lock_counts[i]); if (c) { printk("tegra_grhost: lock id %d: refcnt %d\n", i, c); diff --git a/drivers/video/tegra/host/nvhost_cdma.c b/drivers/video/tegra/host/nvhost_cdma.c index 05d563c31c8f..edebeeb5ea7b 100644 --- a/drivers/video/tegra/host/nvhost_cdma.c +++ b/drivers/video/tegra/host/nvhost_cdma.c @@ -23,7 +23,7 @@ #include "nvhost_cdma.h" #include "dev.h" #include <asm/cacheflush.h> - +#include <linux/slab.h> /* * TODO: * stats @@ -32,132 +32,6 @@ * - some channels hardly need any, some channels (3d) could use more */ -#define cdma_to_channel(cdma) container_of(cdma, struct nvhost_channel, cdma) -#define cdma_to_dev(cdma) ((cdma_to_channel(cdma))->dev) -#define cdma_to_nvmap(cdma) ((cdma_to_dev(cdma))->nvmap) -#define pb_to_cdma(pb) container_of(pb, struct nvhost_cdma, push_buffer) - -/* - * push_buffer - * - * The push buffer is a circular array of words to be fetched by command DMA. - * Note that it works slightly differently to the sync queue; fence == cur - * means that the push buffer is full, not empty. - */ - -// 8 bytes per slot. (This number does not include the final RESTART.) -#define PUSH_BUFFER_SIZE (NVHOST_GATHER_QUEUE_SIZE * 8) - -static void destroy_push_buffer(struct push_buffer *pb); - -/** - * Reset to empty push buffer - */ -static void reset_push_buffer(struct push_buffer *pb) -{ - pb->fence = PUSH_BUFFER_SIZE - 8; - pb->cur = 0; -} - -/** - * Init push buffer resources - */ -static int init_push_buffer(struct push_buffer *pb) -{ - struct nvhost_cdma *cdma = pb_to_cdma(pb); - struct nvmap_client *nvmap = cdma_to_nvmap(cdma); - pb->mem = NULL; - pb->mapped = NULL; - pb->phys = 0; - reset_push_buffer(pb); - - /* allocate and map pushbuffer memory */ - pb->mem = nvmap_alloc(nvmap, PUSH_BUFFER_SIZE + 4, 32, - NVMAP_HANDLE_WRITE_COMBINE); - if (IS_ERR_OR_NULL(pb->mem)) { - pb->mem = NULL; - goto fail; - } - pb->mapped = nvmap_mmap(pb->mem); - if (pb->mapped == NULL) - goto fail; - - /* pin pushbuffer and get physical address */ - pb->phys = nvmap_pin(nvmap, pb->mem); - if (pb->phys >= 0xfffff000) { - pb->phys = 0; - goto fail; - } - - /* put the restart at the end of pushbuffer memory */ - *(pb->mapped + (PUSH_BUFFER_SIZE >> 2)) = nvhost_opcode_restart(pb->phys); - - return 0; - -fail: - destroy_push_buffer(pb); - return -ENOMEM; -} - -/** - * Clean up push buffer resources - */ -static void destroy_push_buffer(struct push_buffer *pb) -{ - struct nvhost_cdma *cdma = pb_to_cdma(pb); - struct nvmap_client *nvmap = cdma_to_nvmap(cdma); - if (pb->mapped) - nvmap_munmap(pb->mem, pb->mapped); - - if (pb->phys != 0) - nvmap_unpin(nvmap, pb->mem); - - if (pb->mem) - nvmap_free(nvmap, pb->mem); - - pb->mem = NULL; - pb->mapped = NULL; - pb->phys = 0; -} - -/** - * Push two words to the push buffer - * Caller must ensure push buffer is not full - */ -static void push_to_push_buffer(struct push_buffer *pb, u32 op1, u32 op2) -{ - u32 cur = pb->cur; - u32 *p = (u32*)((u32)pb->mapped + cur); - BUG_ON(cur == pb->fence); - *(p++) = op1; - *(p++) = op2; - pb->cur = (cur + 8) & (PUSH_BUFFER_SIZE - 1); - /* printk("push_to_push_buffer: op1=%08x; op2=%08x; cur=%x\n", op1, op2, pb->cur); */ -} - -/** - * Pop a number of two word slots from the push buffer - * Caller must ensure push buffer is not empty - */ -static void pop_from_push_buffer(struct push_buffer *pb, unsigned int slots) -{ - pb->fence = (pb->fence + slots * 8) & (PUSH_BUFFER_SIZE - 1); -} - -/** - * Return the number of two word slots free in the push buffer - */ -static u32 push_buffer_space(struct push_buffer *pb) -{ - return ((pb->fence - pb->cur) & (PUSH_BUFFER_SIZE - 1)) / 8; -} - -static u32 push_buffer_putptr(struct push_buffer *pb) -{ - return pb->phys + pb->cur; -} - - /* Sync Queue * * The sync queue is a circular buffer of u32s interpreted as: @@ -198,12 +72,18 @@ static void reset_sync_queue(struct sync_queue *queue) */ static unsigned int sync_queue_space(struct sync_queue *queue) { + struct nvhost_cdma *cdma; + struct nvhost_master *host; + unsigned int read = queue->read; unsigned int write = queue->write; u32 size; - BUG_ON(read > (NVHOST_SYNC_QUEUE_SIZE - SYNC_QUEUE_MIN_ENTRY)); - BUG_ON(write > (NVHOST_SYNC_QUEUE_SIZE - SYNC_QUEUE_MIN_ENTRY)); + cdma = container_of(queue, struct nvhost_cdma, sync_queue); + host = cdma_to_dev(cdma); + + BUG_ON(read > (host->sync_queue_size - SYNC_QUEUE_MIN_ENTRY)); + BUG_ON(write > (host->sync_queue_size - SYNC_QUEUE_MIN_ENTRY)); /* * We can use all of the space up to the end of the buffer, unless the @@ -213,7 +93,7 @@ static unsigned int sync_queue_space(struct sync_queue *queue) if (read > write) { size = (read - 1) - write; } else { - size = NVHOST_SYNC_QUEUE_SIZE - write; + size = host->sync_queue_size - write; /* * If the read position is zero, it gets complicated. We can't @@ -248,15 +128,20 @@ static void add_to_sync_queue(struct sync_queue *queue, u32 nr_slots, struct nvmap_client *user_nvmap, struct nvmap_handle **handles, u32 nr_handles) { + struct nvhost_cdma *cdma; + struct nvhost_master *host; u32 write = queue->write; u32 *p = queue->buffer + write; u32 size = 4 + (entry_size(nr_handles)); + cdma = container_of(queue, struct nvhost_cdma, sync_queue); + host = cdma_to_dev(cdma); + BUG_ON(sync_point_id == NVSYNCPT_INVALID); BUG_ON(sync_queue_space(queue) < nr_handles); write += size; - BUG_ON(write > NVHOST_SYNC_QUEUE_SIZE); + BUG_ON(write > host->sync_queue_size); *p++ = sync_point_id; *p++ = sync_point_value; @@ -271,7 +156,7 @@ static void add_to_sync_queue(struct sync_queue *queue, memcpy(p, handles, nr_handles * sizeof(struct nvmap_handle *)); /* If there's not enough room for another entry, wrap to the start. */ - if ((write + SYNC_QUEUE_MIN_ENTRY) > NVHOST_SYNC_QUEUE_SIZE) { + if ((write + SYNC_QUEUE_MIN_ENTRY) > host->sync_queue_size) { /* * It's an error for the read position to be zero, as that * would mean we emptied the queue while adding something. @@ -289,11 +174,15 @@ static void add_to_sync_queue(struct sync_queue *queue, */ static u32 *sync_queue_head(struct sync_queue *queue) { + struct nvhost_cdma *cdma = container_of(queue, + struct nvhost_cdma, + sync_queue); + struct nvhost_master *host = cdma_to_dev(cdma); u32 read = queue->read; u32 write = queue->write; - BUG_ON(read > (NVHOST_SYNC_QUEUE_SIZE - SYNC_QUEUE_MIN_ENTRY)); - BUG_ON(write > (NVHOST_SYNC_QUEUE_SIZE - SYNC_QUEUE_MIN_ENTRY)); + BUG_ON(read > (host->sync_queue_size - SYNC_QUEUE_MIN_ENTRY)); + BUG_ON(write > (host->sync_queue_size - SYNC_QUEUE_MIN_ENTRY)); if (read == write) return NULL; @@ -306,6 +195,10 @@ static u32 *sync_queue_head(struct sync_queue *queue) static void dequeue_sync_queue_head(struct sync_queue *queue) { + struct nvhost_cdma *cdma = container_of(queue, + struct nvhost_cdma, + sync_queue); + struct nvhost_master *host = cdma_to_dev(cdma); u32 read = queue->read; u32 size; @@ -314,62 +207,16 @@ dequeue_sync_queue_head(struct sync_queue *queue) size = 4 + entry_size(queue->buffer[read + 3]); read += size; - BUG_ON(read > NVHOST_SYNC_QUEUE_SIZE); + BUG_ON(read > host->sync_queue_size); /* If there's not enough room for another entry, wrap to the start. */ - if ((read + SYNC_QUEUE_MIN_ENTRY) > NVHOST_SYNC_QUEUE_SIZE) + if ((read + SYNC_QUEUE_MIN_ENTRY) > host->sync_queue_size) read = 0; queue->read = read; } -/*** Cdma internal stuff ***/ - -/** - * Start channel DMA - */ -static void start_cdma(struct nvhost_cdma *cdma) -{ - void __iomem *chan_regs = cdma_to_channel(cdma)->aperture; - - if (cdma->running) - return; - - cdma->last_put = push_buffer_putptr(&cdma->push_buffer); - - writel(nvhost_channel_dmactrl(true, false, false), - chan_regs + HOST1X_CHANNEL_DMACTRL); - - /* set base, put, end pointer (all of memory) */ - writel(0, chan_regs + HOST1X_CHANNEL_DMASTART); - writel(cdma->last_put, chan_regs + HOST1X_CHANNEL_DMAPUT); - writel(0xFFFFFFFF, chan_regs + HOST1X_CHANNEL_DMAEND); - - /* reset GET */ - writel(nvhost_channel_dmactrl(true, true, true), - chan_regs + HOST1X_CHANNEL_DMACTRL); - - /* start the command DMA */ - writel(nvhost_channel_dmactrl(false, false, false), - chan_regs + HOST1X_CHANNEL_DMACTRL); - - cdma->running = true; -} - -/** - * Kick channel DMA into action by writing its PUT offset (if it has changed) - */ -static void kick_cdma(struct nvhost_cdma *cdma) -{ - u32 put = push_buffer_putptr(&cdma->push_buffer); - if (put != cdma->last_put) { - void __iomem *chan_regs = cdma_to_channel(cdma)->aperture; - wmb(); - writel(put, chan_regs + HOST1X_CHANNEL_DMAPUT); - cdma->last_put = put; - } -} /** * Return the status of the cdma's sync queue or push buffer for the given event @@ -385,8 +232,11 @@ static unsigned int cdma_status(struct nvhost_cdma *cdma, enum cdma_event event) return sync_queue_head(&cdma->sync_queue) ? 0 : 1; case CDMA_EVENT_SYNC_QUEUE_SPACE: return sync_queue_space(&cdma->sync_queue); - case CDMA_EVENT_PUSH_BUFFER_SPACE: - return push_buffer_space(&cdma->push_buffer); + case CDMA_EVENT_PUSH_BUFFER_SPACE: { + struct push_buffer *pb = &cdma->push_buffer; + BUG_ON(!cdma_pb_op(cdma).space); + return cdma_pb_op(cdma).space(pb); + } default: return 0; } @@ -401,7 +251,7 @@ static unsigned int cdma_status(struct nvhost_cdma *cdma, enum cdma_event event) * - Return the amount of space (> 0) * Must be called with the cdma lock held. */ -static unsigned int wait_cdma(struct nvhost_cdma *cdma, enum cdma_event event) +unsigned int nvhost_cdma_wait(struct nvhost_cdma *cdma, enum cdma_event event) { for (;;) { unsigned int space = cdma_status(cdma, event); @@ -476,7 +326,9 @@ static void update_cdma(struct nvhost_cdma *cdma) /* Pop push buffer slots */ if (nr_slots) { - pop_from_push_buffer(&cdma->push_buffer, nr_slots); + struct push_buffer *pb = &cdma->push_buffer; + BUG_ON(!cdma_pb_op(cdma).pop_from); + cdma_pb_op(cdma).pop_from(pb, nr_slots); if (cdma->event == CDMA_EVENT_PUSH_BUFFER_SPACE) signal = true; } @@ -499,12 +351,20 @@ static void update_cdma(struct nvhost_cdma *cdma) int nvhost_cdma_init(struct nvhost_cdma *cdma) { int err; - + struct push_buffer *pb = &cdma->push_buffer; + BUG_ON(!cdma_pb_op(cdma).init); mutex_init(&cdma->lock); sema_init(&cdma->sem, 0); cdma->event = CDMA_EVENT_NONE; cdma->running = false; - err = init_push_buffer(&cdma->push_buffer); + + /* allocate sync queue memory */ + cdma->sync_queue.buffer = kzalloc(cdma_to_dev(cdma)->sync_queue_size + * sizeof(u32), GFP_KERNEL); + if (!cdma->sync_queue.buffer) + return -ENOMEM; + + err = cdma_pb_op(cdma).init(pb); if (err) return err; reset_sync_queue(&cdma->sync_queue); @@ -516,32 +376,24 @@ int nvhost_cdma_init(struct nvhost_cdma *cdma) */ void nvhost_cdma_deinit(struct nvhost_cdma *cdma) { + struct push_buffer *pb = &cdma->push_buffer; + BUG_ON(!cdma_pb_op(cdma).destroy); BUG_ON(cdma->running); - destroy_push_buffer(&cdma->push_buffer); + kfree(cdma->sync_queue.buffer); + cdma->sync_queue.buffer = 0; + cdma_pb_op(cdma).destroy(pb); } -void nvhost_cdma_stop(struct nvhost_cdma *cdma) -{ - void __iomem *chan_regs = cdma_to_channel(cdma)->aperture; - - mutex_lock(&cdma->lock); - if (cdma->running) { - wait_cdma(cdma, CDMA_EVENT_SYNC_QUEUE_EMPTY); - writel(nvhost_channel_dmactrl(true, false, false), - chan_regs + HOST1X_CHANNEL_DMACTRL); - cdma->running = false; - } - mutex_unlock(&cdma->lock); -} /** * Begin a cdma submit */ void nvhost_cdma_begin(struct nvhost_cdma *cdma) { + BUG_ON(!cdma_op(cdma).start); mutex_lock(&cdma->lock); if (!cdma->running) - start_cdma(cdma); + cdma_op(cdma).start(cdma); cdma->slots_free = 0; cdma->slots_used = 0; } @@ -553,13 +405,16 @@ void nvhost_cdma_begin(struct nvhost_cdma *cdma) void nvhost_cdma_push(struct nvhost_cdma *cdma, u32 op1, u32 op2) { u32 slots_free = cdma->slots_free; + struct push_buffer *pb = &cdma->push_buffer; + BUG_ON(!cdma_pb_op(cdma).push_to); + BUG_ON(!cdma_op(cdma).kick); if (slots_free == 0) { - kick_cdma(cdma); - slots_free = wait_cdma(cdma, CDMA_EVENT_PUSH_BUFFER_SPACE); + cdma_op(cdma).kick(cdma); + slots_free = nvhost_cdma_wait(cdma, CDMA_EVENT_PUSH_BUFFER_SPACE); } cdma->slots_free = slots_free - 1; cdma->slots_used++; - push_to_push_buffer(&cdma->push_buffer, op1, op2); + cdma_pb_op(cdma).push_to(pb, op1, op2); } /** @@ -575,7 +430,8 @@ void nvhost_cdma_end(struct nvhost_cdma *cdma, u32 sync_point_id, u32 sync_point_value, struct nvmap_handle **handles, unsigned int nr_handles) { - kick_cdma(cdma); + BUG_ON(!cdma_op(cdma).kick); + cdma_op(cdma).kick(cdma); while (nr_handles || cdma->slots_used) { unsigned int count; @@ -583,7 +439,7 @@ void nvhost_cdma_end(struct nvhost_cdma *cdma, * Wait until there's enough room in the * sync queue to write something. */ - count = wait_cdma(cdma, CDMA_EVENT_SYNC_QUEUE_SPACE); + count = nvhost_cdma_wait(cdma, CDMA_EVENT_SYNC_QUEUE_SPACE); /* Add reloc entries to sync queue (as many as will fit) */ if (count > nr_handles) @@ -626,16 +482,3 @@ void nvhost_cdma_flush(struct nvhost_cdma *cdma) mutex_unlock(&cdma->lock); } -/** - * Retrieve the op pair at a slot offset from a DMA address - */ -void nvhost_cdma_peek(struct nvhost_cdma *cdma, - u32 dmaget, int slot, u32 *out) -{ - u32 offset = dmaget - cdma->push_buffer.phys; - u32 *p = cdma->push_buffer.mapped; - - offset = ((offset + slot * 8) & (PUSH_BUFFER_SIZE - 1)) >> 2; - out[0] = p[offset]; - out[1] = p[offset + 1]; -} diff --git a/drivers/video/tegra/host/nvhost_cdma.h b/drivers/video/tegra/host/nvhost_cdma.h index a5876670a20a..64d17249f6af 100644 --- a/drivers/video/tegra/host/nvhost_cdma.h +++ b/drivers/video/tegra/host/nvhost_cdma.h @@ -45,13 +45,6 @@ * update - call to update sync queue and push buffer, unpin memory */ -/* Size of the sync queue. If it is too small, we won't be able to queue up - * many command buffers. If it is too large, we waste memory. */ -#define NVHOST_SYNC_QUEUE_SIZE 8192 - -/* Number of gathers we allow to be queued up per channel. Must be a - power of two. Currently sized such that pushbuffer is 4KB (512*8B). */ -#define NVHOST_GATHER_QUEUE_SIZE 512 struct push_buffer { struct nvmap_handle_ref *mem; /* handle to pushbuffer memory */ @@ -64,7 +57,7 @@ struct push_buffer { struct sync_queue { unsigned int read; /* read position within buffer */ unsigned int write; /* write position within buffer */ - u32 buffer[NVHOST_SYNC_QUEUE_SIZE]; /* queue data */ + u32 *buffer; /* queue data */ }; enum cdma_event { @@ -84,8 +77,17 @@ struct nvhost_cdma { struct push_buffer push_buffer; /* channel's push buffer */ struct sync_queue sync_queue; /* channel's sync queue */ bool running; + }; +#define cdma_to_channel(cdma) container_of(cdma, struct nvhost_channel, cdma) +#define cdma_to_dev(cdma) ((cdma_to_channel(cdma))->dev) +#define cdma_op(cdma) (cdma_to_dev(cdma)->op.cdma) +#define cdma_to_nvmap(cdma) ((cdma_to_dev(cdma))->nvmap) +#define pb_to_cdma(pb) container_of(pb, struct nvhost_cdma, push_buffer) +#define cdma_pb_op(cdma) (cdma_to_dev(cdma)->op.push_buffer) + + int nvhost_cdma_init(struct nvhost_cdma *cdma); void nvhost_cdma_deinit(struct nvhost_cdma *cdma); void nvhost_cdma_stop(struct nvhost_cdma *cdma); @@ -100,4 +102,5 @@ void nvhost_cdma_flush(struct nvhost_cdma *cdma); void nvhost_cdma_peek(struct nvhost_cdma *cdma, u32 dmaget, int slot, u32 *out); +unsigned int nvhost_cdma_wait(struct nvhost_cdma *cdma, enum cdma_event event); #endif diff --git a/drivers/video/tegra/host/nvhost_channel.c b/drivers/video/tegra/host/nvhost_channel.c index f7776e63e68d..ad8d403df0f7 100644 --- a/drivers/video/tegra/host/nvhost_channel.c +++ b/drivers/video/tegra/host/nvhost_channel.c @@ -27,100 +27,7 @@ #include <linux/platform_device.h> -#define NVMODMUTEX_2D_FULL (1) -#define NVMODMUTEX_2D_SIMPLE (2) -#define NVMODMUTEX_2D_SB_A (3) -#define NVMODMUTEX_2D_SB_B (4) -#define NVMODMUTEX_3D (5) -#define NVMODMUTEX_DISPLAYA (6) -#define NVMODMUTEX_DISPLAYB (7) -#define NVMODMUTEX_VI (8) -#define NVMODMUTEX_DSI (9) -static void power_2d(struct nvhost_module *mod, enum nvhost_power_action action); -static void power_3d(struct nvhost_module *mod, enum nvhost_power_action action); -static void power_mpe(struct nvhost_module *mod, enum nvhost_power_action action); - -static const struct nvhost_channeldesc channelmap[] = { -{ - /* channel 0 */ - .name = "display", - .syncpts = BIT(NVSYNCPT_DISP0) | BIT(NVSYNCPT_DISP1) | - BIT(NVSYNCPT_VBLANK0) | BIT(NVSYNCPT_VBLANK1), - .modulemutexes = BIT(NVMODMUTEX_DISPLAYA) | BIT(NVMODMUTEX_DISPLAYB), -}, -{ - /* channel 1 */ - .name = "gr3d", - .syncpts = BIT(NVSYNCPT_3D), - .waitbases = BIT(NVWAITBASE_3D), - .modulemutexes = BIT(NVMODMUTEX_3D), - .class = NV_GRAPHICS_3D_CLASS_ID, - .power = power_3d, -}, -{ - /* channel 2 */ - .name = "gr2d", - .syncpts = BIT(NVSYNCPT_2D_0) | BIT(NVSYNCPT_2D_1), - .waitbases = BIT(NVWAITBASE_2D_0) | BIT(NVWAITBASE_2D_1), - .modulemutexes = BIT(NVMODMUTEX_2D_FULL) | BIT(NVMODMUTEX_2D_SIMPLE) | - BIT(NVMODMUTEX_2D_SB_A) | BIT(NVMODMUTEX_2D_SB_B), - .power = power_2d, -}, -{ - /* channel 3 */ - .name = "isp", - .syncpts = 0, -}, -{ - /* channel 4 */ - .name = "vi", - .syncpts = BIT(NVSYNCPT_CSI_VI_0) | BIT(NVSYNCPT_CSI_VI_1) | - BIT(NVSYNCPT_VI_ISP_0) | BIT(NVSYNCPT_VI_ISP_1) | - BIT(NVSYNCPT_VI_ISP_2) | BIT(NVSYNCPT_VI_ISP_3) | - BIT(NVSYNCPT_VI_ISP_4), - .modulemutexes = BIT(NVMODMUTEX_VI), - .exclusive = true, -}, -{ - /* channel 5 */ - .name = "mpe", - .syncpts = BIT(NVSYNCPT_MPE) | BIT(NVSYNCPT_MPE_EBM_EOF) | - BIT(NVSYNCPT_MPE_WR_SAFE), - .waitbases = BIT(NVWAITBASE_MPE), - .class = NV_VIDEO_ENCODE_MPEG_CLASS_ID, - .power = power_mpe, - .exclusive = true, - .keepalive = true, -}, -{ - /* channel 6 */ - .name = "dsi", - .syncpts = BIT(NVSYNCPT_DSI), - .modulemutexes = BIT(NVMODMUTEX_DSI), -}}; - -static inline void __iomem *channel_aperture(void __iomem *p, int ndx) -{ - ndx += NVHOST_CHANNEL_BASE; - p += NV_HOST1X_CHANNEL0_BASE; - p += ndx * NV_HOST1X_CHANNEL_MAP_SIZE_BYTES; - return p; -} - -int __init nvhost_channel_init(struct nvhost_channel *ch, - struct nvhost_master *dev, int index) -{ - BUILD_BUG_ON(NVHOST_NUMCHANNELS != ARRAY_SIZE(channelmap)); - - ch->dev = dev; - ch->desc = &channelmap[index]; - ch->aperture = channel_aperture(dev->aperture, index); - mutex_init(&ch->reflock); - mutex_init(&ch->submitlock); - - return nvhost_hwctx_handler_init(&ch->ctxhandler, ch->desc->name); -} struct nvhost_channel *nvhost_getchannel(struct nvhost_channel *ch) { @@ -152,6 +59,8 @@ struct nvhost_channel *nvhost_getchannel(struct nvhost_channel *ch) void nvhost_putchannel(struct nvhost_channel *ch, struct nvhost_hwctx *ctx) { + BUG_ON(!channel_cdma_op(ch).stop); + if (ctx) { mutex_lock(&ch->submitlock); if (ch->cur_ctx == ctx) @@ -167,7 +76,8 @@ void nvhost_putchannel(struct nvhost_channel *ch, struct nvhost_hwctx *ctx) if (ch->refcount == 1) { nvhost_module_deinit(&ch->mod); /* cdma may already be stopped, that's ok */ - nvhost_cdma_stop(&ch->cdma); + + channel_cdma_op(ch).stop(&ch->cdma); nvhost_cdma_deinit(&ch->cdma); } ch->refcount--; @@ -178,211 +88,9 @@ void nvhost_channel_suspend(struct nvhost_channel *ch) { mutex_lock(&ch->reflock); BUG_ON(nvhost_module_powered(&ch->mod)); + BUG_ON(!channel_cdma_op(ch).stop); + if (ch->refcount) - nvhost_cdma_stop(&ch->cdma); + channel_cdma_op(ch).stop(&ch->cdma); mutex_unlock(&ch->reflock); } - -int nvhost_channel_submit(struct nvhost_channel *channel, - struct nvhost_hwctx *hwctx, - struct nvmap_client *user_nvmap, - u32 *gather, - u32 *gather_end, - struct nvhost_waitchk *waitchk, - struct nvhost_waitchk *waitchk_end, - u32 waitchk_mask, - struct nvmap_handle **unpins, - int nr_unpins, - u32 syncpt_id, - u32 syncpt_incrs, - u32 *syncpt_value, - bool null_kickoff) -{ - struct nvhost_hwctx *hwctx_to_save = NULL; - u32 user_syncpt_incrs = syncpt_incrs; - bool need_restore = false; - u32 syncval; - int err; - - /* keep module powered */ - nvhost_module_busy(&channel->mod); - - /* get submit lock */ - err = mutex_lock_interruptible(&channel->submitlock); - if (err) { - nvhost_module_idle(&channel->mod); - return err; - } - - /* remove stale waits */ - if (waitchk != waitchk_end) { - err = nvhost_syncpt_wait_check(user_nvmap, - &channel->dev->syncpt, waitchk_mask, - waitchk, waitchk_end); - if (err) { - dev_warn(&channel->dev->pdev->dev, - "nvhost_syncpt_wait_check failed: %d\n", err); - mutex_unlock(&channel->submitlock); - nvhost_module_idle(&channel->mod); - return err; - } - } - - /* context switch */ - if (channel->cur_ctx != hwctx) { - trace_nvhost_channel_context_switch(channel->desc->name, - channel->cur_ctx, hwctx); - hwctx_to_save = channel->cur_ctx; - if (hwctx_to_save) { - syncpt_incrs += hwctx_to_save->save_incrs; - hwctx_to_save->valid = true; - channel->ctxhandler.get(hwctx_to_save); - } - channel->cur_ctx = hwctx; - if (channel->cur_ctx && channel->cur_ctx->valid) { - need_restore = true; - syncpt_incrs += channel->cur_ctx->restore_incrs; - } - } - - /* get absolute sync value */ - if (BIT(syncpt_id) & NVSYNCPTS_CLIENT_MANAGED) - syncval = nvhost_syncpt_set_max(&channel->dev->syncpt, - syncpt_id, syncpt_incrs); - else - syncval = nvhost_syncpt_incr_max(&channel->dev->syncpt, - syncpt_id, syncpt_incrs); - - /* begin a CDMA submit */ - nvhost_cdma_begin(&channel->cdma); - - /* push save buffer (pre-gather setup depends on unit) */ - if (hwctx_to_save) - channel->ctxhandler.save_push(&channel->cdma, hwctx_to_save); - - /* gather restore buffer */ - if (need_restore) - nvhost_cdma_push(&channel->cdma, - nvhost_opcode_gather(channel->cur_ctx->restore_size), - channel->cur_ctx->restore_phys); - - /* add a setclass for modules that require it (unless ctxsw added it) */ - if (!hwctx_to_save && !need_restore && channel->desc->class) - nvhost_cdma_push(&channel->cdma, - nvhost_opcode_setclass(channel->desc->class, 0, 0), - NVHOST_OPCODE_NOOP); - - if (null_kickoff) { - int incr; - u32 op_incr; - - /* TODO ideally we'd also perform host waits here */ - - /* push increments that correspond to nulled out commands */ - op_incr = nvhost_opcode_imm(0, 0x100 | syncpt_id); - for (incr = 0; incr < (user_syncpt_incrs >> 1); incr++) - nvhost_cdma_push(&channel->cdma, op_incr, op_incr); - if (user_syncpt_incrs & 1) - nvhost_cdma_push(&channel->cdma, - op_incr, NVHOST_OPCODE_NOOP); - - /* for 3d, waitbase needs to be incremented after each submit */ - if (channel->desc->class == NV_GRAPHICS_3D_CLASS_ID) - nvhost_cdma_push(&channel->cdma, - nvhost_opcode_setclass( - NV_HOST1X_CLASS_ID, - NV_CLASS_HOST_INCR_SYNCPT_BASE, - 1), - nvhost_class_host_incr_syncpt_base( - NVWAITBASE_3D, - user_syncpt_incrs)); - } - else { - /* push user gathers */ - for ( ; gather != gather_end; gather += 2) - nvhost_cdma_push(&channel->cdma, - nvhost_opcode_gather(gather[0]), - gather[1]); - } - - /* end CDMA submit & stash pinned hMems into sync queue */ - nvhost_cdma_end(&channel->cdma, user_nvmap, - syncpt_id, syncval, unpins, nr_unpins); - - /* - * schedule a context save interrupt (to drain the host FIFO - * if necessary, and to release the restore buffer) - */ - if (hwctx_to_save) - nvhost_intr_add_action(&channel->dev->intr, syncpt_id, - syncval - syncpt_incrs + hwctx_to_save->save_thresh, - NVHOST_INTR_ACTION_CTXSAVE, hwctx_to_save, NULL); - - /* schedule a submit complete interrupt */ - nvhost_intr_add_action(&channel->dev->intr, syncpt_id, syncval, - NVHOST_INTR_ACTION_SUBMIT_COMPLETE, channel, NULL); - - mutex_unlock(&channel->submitlock); - - *syncpt_value = syncval; - return 0; -} - -static void power_2d(struct nvhost_module *mod, enum nvhost_power_action action) -{ - /* TODO: [ahatala 2010-06-17] reimplement EPP hang war */ - if (action == NVHOST_POWER_ACTION_OFF) { - /* TODO: [ahatala 2010-06-17] reset EPP */ - } -} - -static void power_3d(struct nvhost_module *mod, enum nvhost_power_action action) -{ - struct nvhost_channel *ch = container_of(mod, struct nvhost_channel, mod); - struct nvhost_hwctx *hwctx_to_save; - DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); - u32 syncpt_incrs, syncpt_val; - void *ref; - - if (action != NVHOST_POWER_ACTION_OFF) - return; - - mutex_lock(&ch->submitlock); - hwctx_to_save = ch->cur_ctx; - if (!hwctx_to_save) { - mutex_unlock(&ch->submitlock); - return; - } - - hwctx_to_save->valid = true; - ch->ctxhandler.get(hwctx_to_save); - ch->cur_ctx = NULL; - - syncpt_incrs = hwctx_to_save->save_incrs; - syncpt_val = nvhost_syncpt_incr_max(&ch->dev->syncpt, - NVSYNCPT_3D, syncpt_incrs); - - nvhost_cdma_begin(&ch->cdma); - ch->ctxhandler.save_push(&ch->cdma, hwctx_to_save); - nvhost_cdma_end(&ch->cdma, ch->dev->nvmap, NVSYNCPT_3D, syncpt_val, NULL, 0); - - nvhost_intr_add_action(&ch->dev->intr, NVSYNCPT_3D, - syncpt_val - syncpt_incrs + hwctx_to_save->save_thresh, - NVHOST_INTR_ACTION_CTXSAVE, hwctx_to_save, NULL); - - nvhost_intr_add_action(&ch->dev->intr, NVSYNCPT_3D, syncpt_val, - NVHOST_INTR_ACTION_WAKEUP, &wq, &ref); - wait_event(wq, - nvhost_syncpt_min_cmp(&ch->dev->syncpt, - NVSYNCPT_3D, syncpt_val)); - - nvhost_intr_put_ref(&ch->dev->intr, ref); - - nvhost_cdma_update(&ch->cdma); - - mutex_unlock(&ch->submitlock); -} - -static void power_mpe(struct nvhost_module *mod, enum nvhost_power_action action) -{ -} diff --git a/drivers/video/tegra/host/nvhost_channel.h b/drivers/video/tegra/host/nvhost_channel.h index b1515c4db6d9..2afd65d54b17 100644 --- a/drivers/video/tegra/host/nvhost_channel.h +++ b/drivers/video/tegra/host/nvhost_channel.h @@ -30,8 +30,6 @@ #include <linux/cdev.h> #include <linux/io.h> -#define NVHOST_CHANNEL_BASE 0 -#define NVHOST_NUMCHANNELS (NV_HOST1X_CHANNELS - 1) #define NVHOST_MAX_WAIT_CHECKS 256 #define NVHOST_MAX_GATHERS 512 #define NVHOST_MAX_HANDLES 1280 @@ -98,4 +96,7 @@ struct nvhost_channel *nvhost_getchannel(struct nvhost_channel *ch); void nvhost_putchannel(struct nvhost_channel *ch, struct nvhost_hwctx *ctx); void nvhost_channel_suspend(struct nvhost_channel *ch); +#define channel_cdma_op(ch) (ch->dev->op.cdma) +#define channel_op(ch) (ch->dev->op.channel) +#define host_channel_op(host) (host->op.channel) #endif diff --git a/drivers/video/tegra/host/nvhost_cpuaccess.c b/drivers/video/tegra/host/nvhost_cpuaccess.c index 4a5c34d593fc..dd585fbe8814 100644 --- a/drivers/video/tegra/host/nvhost_cpuaccess.c +++ b/drivers/video/tegra/host/nvhost_cpuaccess.c @@ -24,20 +24,20 @@ #include "dev.h" #include <linux/string.h> -#define cpuaccess_to_dev(ctx) container_of(ctx, struct nvhost_master, cpuaccess) - int nvhost_cpuaccess_init(struct nvhost_cpuaccess *ctx, struct platform_device *pdev) { + struct nvhost_master *host = cpuaccess_to_dev(ctx); int i; - for (i = 0; i < NVHOST_MODULE_NUM; i++) { + + for (i = 0; i < host->nb_modules; i++) { struct resource *mem; mem = platform_get_resource(pdev, IORESOURCE_MEM, i+1); if (!mem) { dev_err(&pdev->dev, "missing module memory resource\n"); return -ENXIO; } - + ctx->reg_mem[i] = mem; ctx->regs[i] = ioremap(mem->start, resource_size(mem)); if (!ctx->regs[i]) { dev_err(&pdev->dev, "failed to map module registers\n"); @@ -50,8 +50,10 @@ int nvhost_cpuaccess_init(struct nvhost_cpuaccess *ctx, void nvhost_cpuaccess_deinit(struct nvhost_cpuaccess *ctx) { + struct nvhost_master *host = cpuaccess_to_dev(ctx); int i; - for (i = 0; i < NVHOST_MODULE_NUM; i++) { + + for (i = 0; i < host->nb_modules; i++) { iounmap(ctx->regs[i]); release_resource(ctx->reg_mem[i]); } @@ -60,13 +62,11 @@ void nvhost_cpuaccess_deinit(struct nvhost_cpuaccess *ctx) int nvhost_mutex_try_lock(struct nvhost_cpuaccess *ctx, unsigned int idx) { struct nvhost_master *dev = cpuaccess_to_dev(ctx); - void __iomem *sync_regs = dev->sync_aperture; u32 reg; + BUG_ON(!cpuaccess_op(ctx).mutex_try_lock); - /* mlock registers returns 0 when the lock is aquired. - * writing 0 clears the lock. */ nvhost_module_busy(&dev->mod); - reg = readl(sync_regs + (HOST1X_SYNC_MLOCK_0 + idx * 4)); + reg = cpuaccess_op(ctx).mutex_try_lock(ctx, idx); if (reg) { nvhost_module_idle(&dev->mod); return -ERESTARTSYS; @@ -78,8 +78,9 @@ int nvhost_mutex_try_lock(struct nvhost_cpuaccess *ctx, unsigned int idx) void nvhost_mutex_unlock(struct nvhost_cpuaccess *ctx, unsigned int idx) { struct nvhost_master *dev = cpuaccess_to_dev(ctx); - void __iomem *sync_regs = dev->sync_aperture; - writel(0, sync_regs + (HOST1X_SYNC_MLOCK_0 + idx * 4)); + BUG_ON(!cpuaccess_op(ctx).mutex_unlock); + + cpuaccess_op(ctx).mutex_unlock(ctx, idx); nvhost_module_idle(&dev->mod); atomic_dec(&ctx->lock_counts[idx]); } diff --git a/drivers/video/tegra/host/nvhost_cpuaccess.h b/drivers/video/tegra/host/nvhost_cpuaccess.h index 98ea1e1e1f8f..2e210b7477af 100644 --- a/drivers/video/tegra/host/nvhost_cpuaccess.h +++ b/drivers/video/tegra/host/nvhost_cpuaccess.h @@ -23,7 +23,6 @@ #ifndef __NVHOST_CPUACCESS_H #define __NVHOST_CPUACCESS_H -#include "nvhost_hardware.h" #include <linux/platform_device.h> #include <linux/io.h> @@ -43,26 +42,20 @@ enum nvhost_module_id { }; struct nvhost_cpuaccess { - struct resource *reg_mem[NVHOST_MODULE_NUM]; - void __iomem *regs[NVHOST_MODULE_NUM]; - atomic_t lock_counts[NV_HOST1X_SYNC_MLOCK_NUM]; + struct resource **reg_mem; + void __iomem **regs; + atomic_t *lock_counts; }; +#define cpuaccess_to_dev(ctx) container_of(ctx, struct nvhost_master, cpuaccess) +#define cpuaccess_op(ctx) (cpuaccess_to_dev(ctx)->op.cpuaccess) int nvhost_cpuaccess_init(struct nvhost_cpuaccess *ctx, struct platform_device *pdev); -void nvhost_cpuaccess_deinit(struct nvhost_cpuaccess *ctx); - int nvhost_mutex_try_lock(struct nvhost_cpuaccess *ctx, unsigned int idx); void nvhost_mutex_unlock(struct nvhost_cpuaccess *ctx, unsigned int idx); -static inline bool nvhost_access_module_regs( - struct nvhost_cpuaccess *ctx, u32 module) -{ - return (module < NVHOST_MODULE_NUM); -} - void nvhost_read_module_regs(struct nvhost_cpuaccess *ctx, u32 module, u32 offset, size_t size, void *values); diff --git a/drivers/video/tegra/host/nvhost_hwctx.h b/drivers/video/tegra/host/nvhost_hwctx.h index 67c3c7f8d0ce..83d93bd871eb 100644 --- a/drivers/video/tegra/host/nvhost_hwctx.h +++ b/drivers/video/tegra/host/nvhost_hwctx.h @@ -57,20 +57,6 @@ struct nvhost_hwctx_handler { void (*save_service) (struct nvhost_hwctx *ctx); }; -int nvhost_3dctx_handler_init(struct nvhost_hwctx_handler *h); -int nvhost_mpectx_handler_init(struct nvhost_hwctx_handler *h); - -static inline int nvhost_hwctx_handler_init( - struct nvhost_hwctx_handler *h, - const char *module) -{ - if (strcmp(module, "gr3d") == 0) - return nvhost_3dctx_handler_init(h); - else if (strcmp(module, "mpe") == 0) - return nvhost_mpectx_handler_init(h); - - return 0; -} struct hwctx_reginfo { unsigned int offset:12; diff --git a/drivers/video/tegra/host/nvhost_intr.c b/drivers/video/tegra/host/nvhost_intr.c index 729a0e351453..ef511392154b 100644 --- a/drivers/video/tegra/host/nvhost_intr.c +++ b/drivers/video/tegra/host/nvhost_intr.c @@ -26,52 +26,8 @@ #include <linux/slab.h> #include <linux/irq.h> -#define intr_to_dev(x) container_of(x, struct nvhost_master, intr) -/*** HW host sync management ***/ - -void init_host_sync(void __iomem *sync_regs) -{ - /* disable the ip_busy_timeout. this prevents write drops, etc. - * there's no real way to recover from a hung client anyway. - */ - writel(0, sync_regs + HOST1X_SYNC_IP_BUSY_TIMEOUT); - - /* increase the auto-ack timout to the maximum value. 2d will hang - * otherwise on ap20. - */ - writel(0xff, sync_regs + HOST1X_SYNC_CTXSW_TIMEOUT_CFG); -} - -void set_host_clocks_per_microsecond(void __iomem *sync_regs, u32 cpm) -{ - /* write microsecond clock register */ - writel(cpm, sync_regs + HOST1X_SYNC_USEC_CLK); -} - -static void set_syncpt_threshold(void __iomem *sync_regs, u32 id, u32 thresh) -{ - thresh &= 0xffff; - writel(thresh, sync_regs + (HOST1X_SYNC_SYNCPT_INT_THRESH_0 + id * 4)); -} - -static void enable_syncpt_interrupt(void __iomem *sync_regs, u32 id) -{ - writel(BIT(id), sync_regs + HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0); -} - -void disable_all_syncpt_interrupts(void __iomem *sync_regs) -{ - /* disable interrupts for both cpu's */ - writel(0, sync_regs + HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE); - - /* clear status for both cpu's */ - writel(0xfffffffful, sync_regs + - HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS); - writel(0xfffffffful, sync_regs + - HOST1X_SYNC_SYNCPT_THRESH_CPU1_INT_STATUS); -} /*** Wait list management ***/ @@ -156,14 +112,17 @@ static void remove_completed_waiters(struct list_head *head, u32 sync, } } -void reset_threshold_interrupt(struct list_head *head, - unsigned int id, void __iomem *sync_regs) +void reset_threshold_interrupt(struct nvhost_intr *intr, + struct list_head *head, + unsigned int id) { u32 thresh = list_first_entry(head, struct nvhost_waitlist, list)->thresh; + BUG_ON(!(intr_op(intr).set_syncpt_threshold && + intr_op(intr).enable_syncpt_intr)); - set_syncpt_threshold(sync_regs, id, thresh); - enable_syncpt_interrupt(sync_regs, id); + intr_op(intr).set_syncpt_threshold(intr, id, thresh); + intr_op(intr).enable_syncpt_intr(intr, id); } @@ -230,8 +189,9 @@ static void run_handlers(struct list_head completed[NVHOST_INTR_ACTION_COUNT]) /** * Remove & handle all waiters that have completed for the given syncpt */ -static int process_wait_list(struct nvhost_intr_syncpt *syncpt, - u32 threshold, void __iomem *sync_regs) +static int process_wait_list(struct nvhost_intr *intr, + struct nvhost_intr_syncpt *syncpt, + u32 threshold) { struct list_head completed[NVHOST_INTR_ACTION_COUNT]; unsigned int i; @@ -246,8 +206,8 @@ static int process_wait_list(struct nvhost_intr_syncpt *syncpt, empty = list_empty(&syncpt->wait_head); if (!empty) - reset_threshold_interrupt(&syncpt->wait_head, - syncpt->id, sync_regs); + reset_threshold_interrupt(intr, &syncpt->wait_head, + syncpt->id); spin_unlock(&syncpt->lock); @@ -256,29 +216,7 @@ static int process_wait_list(struct nvhost_intr_syncpt *syncpt, return empty; } - /*** host syncpt interrupt service functions ***/ - -/** - * Sync point threshold interrupt service function - * Handles sync point threshold triggers, in interrupt context - */ -static irqreturn_t syncpt_thresh_isr(int irq, void *dev_id) -{ - struct nvhost_intr_syncpt *syncpt = dev_id; - unsigned int id = syncpt->id; - struct nvhost_intr *intr = container_of(syncpt, struct nvhost_intr, - syncpt[id]); - void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture; - - writel(BIT(id), - sync_regs + HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE); - writel(BIT(id), - sync_regs + HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS); - - return IRQ_WAKE_THREAD; -} - /** * Sync point threshold interrupt service thread function * Handles sync point threshold triggers, in thread context @@ -287,13 +225,11 @@ static irqreturn_t syncpt_thresh_fn(int irq, void *dev_id) { struct nvhost_intr_syncpt *syncpt = dev_id; unsigned int id = syncpt->id; - struct nvhost_intr *intr = container_of(syncpt, struct nvhost_intr, - syncpt[id]); + struct nvhost_intr *intr = intr_syncpt_to_intr(syncpt); struct nvhost_master *dev = intr_to_dev(intr); - (void)process_wait_list(syncpt, - nvhost_syncpt_update_min(&dev->syncpt, id), - dev->sync_aperture); + (void)process_wait_list(intr, syncpt, + nvhost_syncpt_update_min(&dev->syncpt, id)); return IRQ_HANDLED; } @@ -304,12 +240,12 @@ static irqreturn_t syncpt_thresh_fn(int irq, void *dev_id) static int request_syncpt_irq(struct nvhost_intr_syncpt *syncpt) { int err; - + extern irqreturn_t t20_intr_syncpt_thresh_isr(int irq, void *dev_id); if (syncpt->irq_requested) return 0; err = request_threaded_irq(syncpt->irq, - syncpt_thresh_isr, syncpt_thresh_fn, + t20_intr_syncpt_thresh_isr, syncpt_thresh_fn, 0, syncpt->thresh_irq_name, syncpt); if (err) return err; @@ -332,87 +268,6 @@ static void free_syncpt_irq(struct nvhost_intr_syncpt *syncpt) /*** host general interrupt service functions ***/ -/** - * Host general interrupt service function - * Handles read / write failures - */ -static irqreturn_t host1x_isr(int irq, void *dev_id) -{ - struct nvhost_intr *intr = dev_id; - void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture; - u32 stat; - u32 ext_stat; - u32 addr; - - stat = readl(sync_regs + HOST1X_SYNC_HINTSTATUS); - ext_stat = readl(sync_regs + HOST1X_SYNC_HINTSTATUS_EXT); - - if (nvhost_sync_hintstatus_ext_ip_read_int(ext_stat)) { - addr = readl(sync_regs + HOST1X_SYNC_IP_READ_TIMEOUT_ADDR); - pr_err("Host read timeout at address %x\n", addr); - } - - if (nvhost_sync_hintstatus_ext_ip_write_int(ext_stat)) { - addr = readl(sync_regs + HOST1X_SYNC_IP_WRITE_TIMEOUT_ADDR); - pr_err("Host write timeout at address %x\n", addr); - } - - writel(ext_stat, sync_regs + HOST1X_SYNC_HINTSTATUS_EXT); - writel(stat, sync_regs + HOST1X_SYNC_HINTSTATUS); - - return IRQ_HANDLED; -} - -static int request_host_general_irq(struct nvhost_intr *intr) -{ - void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture; - int err; - - if (intr->host_general_irq_requested) - return 0; - - /* master disable for general (not syncpt) host interrupts */ - writel(0, sync_regs + HOST1X_SYNC_INTMASK); - - /* clear status & extstatus */ - writel(0xfffffffful, sync_regs + HOST1X_SYNC_HINTSTATUS_EXT); - writel(0xfffffffful, sync_regs + HOST1X_SYNC_HINTSTATUS); - - err = request_irq(intr->host_general_irq, host1x_isr, 0, - "host_status", intr); - if (err) - return err; - - /* enable extra interrupt sources IP_READ_INT and IP_WRITE_INT */ - writel(BIT(30) | BIT(31), sync_regs + HOST1X_SYNC_HINTMASK_EXT); - - /* enable extra interrupt sources */ - writel(BIT(31), sync_regs + HOST1X_SYNC_HINTMASK); - - /* enable host module interrupt to CPU0 */ - writel(BIT(0), sync_regs + HOST1X_SYNC_INTC0MASK); - - /* master enable for general (not syncpt) host interrupts */ - writel(BIT(0), sync_regs + HOST1X_SYNC_INTMASK); - - intr->host_general_irq_requested = true; - - return err; -} - -static void free_host_general_irq(struct nvhost_intr *intr) -{ - if (intr->host_general_irq_requested) { - void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture; - - /* master disable for general (not syncpt) host interrupts */ - writel(0, sync_regs + HOST1X_SYNC_INTMASK); - - free_irq(intr->host_general_irq, intr); - intr->host_general_irq_requested = false; - } -} - /*** Main API ***/ @@ -422,10 +277,12 @@ int nvhost_intr_add_action(struct nvhost_intr *intr, u32 id, u32 thresh, { struct nvhost_waitlist *waiter; struct nvhost_intr_syncpt *syncpt; - void __iomem *sync_regs; int queue_was_empty; int err; + BUG_ON(!(intr_op(intr).set_syncpt_threshold && + intr_op(intr).enable_syncpt_intr)); + /* create and initialize a new waiter */ waiter = kmalloc(sizeof(*waiter), GFP_KERNEL); if (!waiter) @@ -440,9 +297,8 @@ int nvhost_intr_add_action(struct nvhost_intr *intr, u32 id, u32 thresh, waiter->data = data; waiter->count = 1; - BUG_ON(id >= NV_HOST1X_SYNCPT_NB_PTS); + BUG_ON(id >= intr_to_dev(intr)->syncpt.nb_pts); syncpt = intr->syncpt + id; - sync_regs = intr_to_dev(intr)->sync_aperture; spin_lock(&syncpt->lock); @@ -466,11 +322,11 @@ int nvhost_intr_add_action(struct nvhost_intr *intr, u32 id, u32 thresh, if (add_waiter_to_queue(waiter, &syncpt->wait_head)) { /* added at head of list - new threshold value */ - set_syncpt_threshold(sync_regs, id, thresh); + intr_op(intr).set_syncpt_threshold(intr, id, thresh); /* added as first waiter - enable interrupt */ if (queue_was_empty) - enable_syncpt_interrupt(sync_regs, id); + intr_op(intr).enable_syncpt_intr(intr, id); } spin_unlock(&syncpt->lock); @@ -498,14 +354,18 @@ int nvhost_intr_init(struct nvhost_intr *intr, u32 irq_gen, u32 irq_sync) { unsigned int id; struct nvhost_intr_syncpt *syncpt; + struct nvhost_master *host = + container_of(intr, struct nvhost_master, intr); + u32 nb_pts = host->syncpt.nb_pts; mutex_init(&intr->mutex); intr->host_general_irq = irq_gen; intr->host_general_irq_requested = false; for (id = 0, syncpt = intr->syncpt; - id < NV_HOST1X_SYNCPT_NB_PTS; + id < nb_pts; ++id, ++syncpt) { + syncpt->intr = &host->intr; syncpt->id = id; syncpt->irq = irq_sync + id; syncpt->irq_requested = 0; @@ -526,31 +386,36 @@ void nvhost_intr_deinit(struct nvhost_intr *intr) void nvhost_intr_start(struct nvhost_intr *intr, u32 hz) { - struct nvhost_master *dev = intr_to_dev(intr); - void __iomem *sync_regs = dev->sync_aperture; + BUG_ON(!(intr_op(intr).init_host_sync && + intr_op(intr).set_host_clocks_per_usec && + intr_op(intr).request_host_general_irq)); mutex_lock(&intr->mutex); - init_host_sync(sync_regs); - set_host_clocks_per_microsecond(sync_regs, (hz + 1000000 - 1)/1000000); + intr_op(intr).init_host_sync(intr); + intr_op(intr).set_host_clocks_per_usec(intr, + (hz + 1000000 - 1)/1000000); - request_host_general_irq(intr); + intr_op(intr).request_host_general_irq(intr); mutex_unlock(&intr->mutex); } void nvhost_intr_stop(struct nvhost_intr *intr) { - void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture; unsigned int id; struct nvhost_intr_syncpt *syncpt; + u32 nb_pts = intr_to_dev(intr)->syncpt.nb_pts; + + BUG_ON(!(intr_op(intr).disable_all_syncpt_intrs && + intr_op(intr).free_host_general_irq)); mutex_lock(&intr->mutex); - disable_all_syncpt_interrupts(sync_regs); + intr_op(intr).disable_all_syncpt_intrs(intr); for (id = 0, syncpt = intr->syncpt; - id < NV_HOST1X_SYNCPT_NB_PTS; + id < nb_pts; ++id, ++syncpt) { struct nvhost_waitlist *waiter, *next; list_for_each_entry_safe(waiter, next, &syncpt->wait_head, list) { @@ -569,7 +434,7 @@ void nvhost_intr_stop(struct nvhost_intr *intr) free_syncpt_irq(syncpt); } - free_host_general_irq(intr); + intr_op(intr).free_host_general_irq(intr); mutex_unlock(&intr->mutex); } diff --git a/drivers/video/tegra/host/nvhost_intr.h b/drivers/video/tegra/host/nvhost_intr.h index fb3e613d70da..150fd6c5694c 100644 --- a/drivers/video/tegra/host/nvhost_intr.h +++ b/drivers/video/tegra/host/nvhost_intr.h @@ -26,8 +26,6 @@ #include <linux/kthread.h> #include <linux/semaphore.h> -#include "nvhost_hardware.h" - struct nvhost_channel; enum nvhost_intr_action { @@ -58,7 +56,10 @@ enum nvhost_intr_action { NVHOST_INTR_ACTION_COUNT }; +struct nvhost_intr; + struct nvhost_intr_syncpt { + struct nvhost_intr *intr; u8 id; u8 irq_requested; u16 irq; @@ -68,11 +69,14 @@ struct nvhost_intr_syncpt { }; struct nvhost_intr { - struct nvhost_intr_syncpt syncpt[NV_HOST1X_SYNCPT_NB_PTS]; + struct nvhost_intr_syncpt *syncpt; struct mutex mutex; int host_general_irq; bool host_general_irq_requested; }; +#define intr_to_dev(x) container_of(x, struct nvhost_master, intr) +#define intr_op(intr) (intr_to_dev(intr)->op.intr) +#define intr_syncpt_to_intr(is) is->intr /** * Schedule an action to be taken when a sync point reaches the given threshold. diff --git a/drivers/video/tegra/host/nvhost_syncpt.c b/drivers/video/tegra/host/nvhost_syncpt.c index 942030865f4b..88059b0f182d 100644 --- a/drivers/video/tegra/host/nvhost_syncpt.c +++ b/drivers/video/tegra/host/nvhost_syncpt.c @@ -23,51 +23,6 @@ #include "nvhost_syncpt.h" #include "dev.h" -#define client_managed(id) (BIT(id) & NVSYNCPTS_CLIENT_MANAGED) -#define syncpt_to_dev(sp) container_of(sp, struct nvhost_master, syncpt) -#define SYNCPT_CHECK_PERIOD 2*HZ - -static bool check_max(struct nvhost_syncpt *sp, u32 id, u32 real) -{ - u32 max; - if (client_managed(id)) - return true; - smp_rmb(); - max = (u32)atomic_read(&sp->max_val[id]); - return ((s32)(max - real) >= 0); -} - -/** - * Write the current syncpoint value back to hw. - */ -static void reset_syncpt(struct nvhost_syncpt *sp, u32 id) -{ - struct nvhost_master *dev = syncpt_to_dev(sp); - int min; - smp_rmb(); - min = atomic_read(&sp->min_val[id]); - writel(min, dev->sync_aperture + (HOST1X_SYNC_SYNCPT_0 + id * 4)); -} - -/** - * Write the current waitbase value back to hw. - */ -static void reset_syncpt_wait_base(struct nvhost_syncpt *sp, u32 id) -{ - struct nvhost_master *dev = syncpt_to_dev(sp); - writel(sp->base_val[id], - dev->sync_aperture + (HOST1X_SYNC_SYNCPT_BASE_0 + id * 4)); -} - -/** - * Read waitbase value from hw. - */ -static void read_syncpt_wait_base(struct nvhost_syncpt *sp, u32 id) -{ - struct nvhost_master *dev = syncpt_to_dev(sp); - sp->base_val[id] = readl(dev->sync_aperture + - (HOST1X_SYNC_SYNCPT_BASE_0 + id * 4)); -} /** * Resets syncpoint and waitbase values to sw shadows @@ -75,10 +30,12 @@ static void read_syncpt_wait_base(struct nvhost_syncpt *sp, u32 id) void nvhost_syncpt_reset(struct nvhost_syncpt *sp) { u32 i; - for (i = 0; i < NV_HOST1X_SYNCPT_NB_PTS; i++) - reset_syncpt(sp, i); - for (i = 0; i < NV_HOST1X_SYNCPT_NB_BASES; i++) - reset_syncpt_wait_base(sp, i); + BUG_ON(!(syncpt_op(sp).reset && syncpt_op(sp).reset_wait_base)); + + for (i = 0; i < sp->nb_pts; i++) + syncpt_op(sp).reset(sp, i); + for (i = 0; i < sp->nb_bases; i++) + syncpt_op(sp).reset_wait_base(sp, i); wmb(); } @@ -88,16 +45,17 @@ void nvhost_syncpt_reset(struct nvhost_syncpt *sp) void nvhost_syncpt_save(struct nvhost_syncpt *sp) { u32 i; + BUG_ON(!(syncpt_op(sp).update_min && syncpt_op(sp).read_wait_base)); - for (i = 0; i < NV_HOST1X_SYNCPT_NB_PTS; i++) { + for (i = 0; i < sp->nb_pts; i++) { if (client_managed(i)) - nvhost_syncpt_update_min(sp, i); + syncpt_op(sp).update_min(sp, i); else BUG_ON(!nvhost_syncpt_min_eq_max(sp, i)); } - for (i = 0; i < NV_HOST1X_SYNCPT_NB_BASES; i++) - read_syncpt_wait_base(sp, i); + for (i = 0; i < sp->nb_bases; i++) + syncpt_op(sp).read_wait_base(sp, i); } /** @@ -105,19 +63,9 @@ void nvhost_syncpt_save(struct nvhost_syncpt *sp) */ u32 nvhost_syncpt_update_min(struct nvhost_syncpt *sp, u32 id) { - struct nvhost_master *dev = syncpt_to_dev(sp); - void __iomem *sync_regs = dev->sync_aperture; - u32 old, live; - - do { - smp_rmb(); - old = (u32)atomic_read(&sp->min_val[id]); - live = readl(sync_regs + (HOST1X_SYNC_SYNCPT_0 + id * 4)); - } while ((u32)atomic_cmpxchg(&sp->min_val[id], old, live) != old); - - BUG_ON(!check_max(sp, id, live)); + BUG_ON(!syncpt_op(sp).update_min); - return live; + return syncpt_op(sp).update_min(sp, id); } /** @@ -126,9 +74,9 @@ u32 nvhost_syncpt_update_min(struct nvhost_syncpt *sp, u32 id) u32 nvhost_syncpt_read(struct nvhost_syncpt *sp, u32 id) { u32 val; - + BUG_ON(!syncpt_op(sp).update_min); nvhost_module_busy(&syncpt_to_dev(sp)->mod); - val = nvhost_syncpt_update_min(sp, id); + val = syncpt_op(sp).update_min(sp, id); nvhost_module_idle(&syncpt_to_dev(sp)->mod); return val; } @@ -139,11 +87,8 @@ u32 nvhost_syncpt_read(struct nvhost_syncpt *sp, u32 id) */ void nvhost_syncpt_cpu_incr(struct nvhost_syncpt *sp, u32 id) { - struct nvhost_master *dev = syncpt_to_dev(sp); - BUG_ON(!nvhost_module_powered(&dev->mod)); - BUG_ON(!client_managed(id) && nvhost_syncpt_min_eq_max(sp, id)); - writel(BIT(id), dev->sync_aperture + HOST1X_SYNC_SYNCPT_CPU_INCR); - wmb(); + BUG_ON(!syncpt_op(sp).cpu_incr); + syncpt_op(sp).cpu_incr(sp, id); } /** @@ -169,8 +114,8 @@ int nvhost_syncpt_wait_timeout(struct nvhost_syncpt *sp, u32 id, if (value) *value = 0; - - BUG_ON(!check_max(sp, id, thresh)); + BUG_ON(!syncpt_op(sp).update_min); + BUG_ON(!nvhost_syncpt_check_max(sp, id, thresh)); /* first check cache */ if (nvhost_syncpt_min_cmp(sp, id, thresh)) { @@ -184,7 +129,7 @@ int nvhost_syncpt_wait_timeout(struct nvhost_syncpt *sp, u32 id, if (client_managed(id) || !nvhost_syncpt_min_eq_max(sp, id)) { /* try to read from register */ - u32 val = nvhost_syncpt_update_min(sp, id); + u32 val = syncpt_op(sp).update_min(sp, id); if ((s32)(val - thresh) >= 0) { if (value) *value = val; @@ -225,8 +170,8 @@ int nvhost_syncpt_wait_timeout(struct nvhost_syncpt *sp, u32 id, if (timeout) { dev_warn(&syncpt_to_dev(sp)->pdev->dev, "syncpoint id %d (%s) stuck waiting %d\n", - id, nvhost_syncpt_name(id), thresh); - nvhost_syncpt_debug(sp); + id, syncpt_op(sp).name(sp, id), thresh); + syncpt_op(sp).debug(sp); } } nvhost_intr_put_ref(&(syncpt_to_dev(sp)->intr), ref); @@ -236,98 +181,17 @@ done: return err; } -static const char *s_syncpt_names[32] = { - "gfx_host", "", "", "", "", "", "", "", "", "", "", - "csi_vi_0", "csi_vi_1", "vi_isp_0", "vi_isp_1", "vi_isp_2", "vi_isp_3", "vi_isp_4", - "2d_0", "2d_1", - "", "", - "3d", "mpe", "disp0", "disp1", "vblank0", "vblank1", "mpe_ebm_eof", "mpe_wr_safe", - "2d_tinyblt", "dsi" -}; - -const char *nvhost_syncpt_name(u32 id) -{ - BUG_ON(id > ARRAY_SIZE(s_syncpt_names)); - return s_syncpt_names[id]; -} - void nvhost_syncpt_debug(struct nvhost_syncpt *sp) { - u32 i; - for (i = 0; i < NV_HOST1X_SYNCPT_NB_PTS; i++) { - u32 max = nvhost_syncpt_read_max(sp, i); - if (!max) - continue; - dev_info(&syncpt_to_dev(sp)->pdev->dev, - "id %d (%s) min %d max %d\n", - i, nvhost_syncpt_name(i), - nvhost_syncpt_update_min(sp, i), max); - - } -} - -/* returns true, if a <= b < c using wrapping comparison */ -static inline bool nvhost_syncpt_is_between(u32 a, u32 b, u32 c) -{ - return b-a < c-a; -} - -/* returns true, if syncpt >= threshold (mod 1 << 32) */ -static bool nvhost_syncpt_wrapping_comparison(u32 syncpt, u32 threshold) -{ - return nvhost_syncpt_is_between(threshold, syncpt, - (1UL<<31UL)+threshold); + syncpt_op(sp).debug(sp); } /* check for old WAITs to be removed (avoiding a wrap) */ -int nvhost_syncpt_wait_check(struct nvmap_client *nvmap, - struct nvhost_syncpt *sp, u32 waitchk_mask, - struct nvhost_waitchk *wait, - struct nvhost_waitchk *waitend) +int nvhost_syncpt_wait_check(struct nvhost_syncpt *sp, + struct nvmap_client *nvmap, + u32 waitchk_mask, + struct nvhost_waitchk *wait, + struct nvhost_waitchk *waitend) { - u32 idx; - int err = 0; - - /* get current syncpt values */ - for (idx = 0; idx < NV_HOST1X_SYNCPT_NB_PTS; idx++) { - if (BIT(idx) & waitchk_mask) - nvhost_syncpt_update_min(sp, idx); - } - - BUG_ON(!wait && !waitend); - - /* compare syncpt vs wait threshold */ - while (wait != waitend) { - u32 syncpt, override; - - BUG_ON(wait->syncpt_id > NV_HOST1X_SYNCPT_NB_PTS); - - syncpt = atomic_read(&sp->min_val[wait->syncpt_id]); - if (nvhost_syncpt_wrapping_comparison(syncpt, wait->thresh)) { - /* - * NULL an already satisfied WAIT_SYNCPT host method, - * by patching its args in the command stream. The - * method data is changed to reference a reserved - * (never given out or incr) NVSYNCPT_GRAPHICS_HOST - * syncpt with a matching threshold value of 0, so - * is guaranteed to be popped by the host HW. - */ - dev_dbg(&syncpt_to_dev(sp)->pdev->dev, - "drop WAIT id %d (%s) thresh 0x%x, syncpt 0x%x\n", - wait->syncpt_id, - nvhost_syncpt_name(wait->syncpt_id), - wait->thresh, syncpt); - - /* patch the wait */ - override = nvhost_class_host_wait_syncpt( - NVSYNCPT_GRAPHICS_HOST, 0); - err = nvmap_patch_word(nvmap, - (struct nvmap_handle *)wait->mem, - wait->offset, override); - if (err) - break; - } - wait++; - } - return err; + return syncpt_op(sp).wait_check(sp, nvmap, waitchk_mask, wait, waitend); } diff --git a/drivers/video/tegra/host/nvhost_syncpt.h b/drivers/video/tegra/host/nvhost_syncpt.h index 564cc83a954f..b6db72e915a9 100644 --- a/drivers/video/tegra/host/nvhost_syncpt.h +++ b/drivers/video/tegra/host/nvhost_syncpt.h @@ -29,54 +29,35 @@ #include <mach/nvmap.h> #include <asm/atomic.h> -#include "nvhost_hardware.h" - +/* host managed and invalid syncpt id */ #define NVSYNCPT_GRAPHICS_HOST (0) -#define NVSYNCPT_CSI_VI_0 (11) -#define NVSYNCPT_CSI_VI_1 (12) -#define NVSYNCPT_VI_ISP_0 (13) -#define NVSYNCPT_VI_ISP_1 (14) -#define NVSYNCPT_VI_ISP_2 (15) -#define NVSYNCPT_VI_ISP_3 (16) -#define NVSYNCPT_VI_ISP_4 (17) -#define NVSYNCPT_2D_0 (18) -#define NVSYNCPT_2D_1 (19) -#define NVSYNCPT_3D (22) -#define NVSYNCPT_MPE (23) -#define NVSYNCPT_DISP0 (24) -#define NVSYNCPT_DISP1 (25) -#define NVSYNCPT_VBLANK0 (26) -#define NVSYNCPT_VBLANK1 (27) -#define NVSYNCPT_MPE_EBM_EOF (28) -#define NVSYNCPT_MPE_WR_SAFE (29) -#define NVSYNCPT_DSI (31) #define NVSYNCPT_INVALID (-1) -/*#define NVSYNCPT_2D_CHANNEL2_0 (20) */ -/*#define NVSYNCPT_2D_CHANNEL2_1 (21) */ -/*#define NVSYNCPT_2D_TINYBLT_WAR (30)*/ -/*#define NVSYNCPT_2D_TINYBLT_RESTORE_CLASS_ID (30)*/ - -/* sync points that are wholly managed by the client */ -#define NVSYNCPTS_CLIENT_MANAGED ( \ - BIT(NVSYNCPT_DISP0) | BIT(NVSYNCPT_DISP1) | BIT(NVSYNCPT_DSI) | \ - BIT(NVSYNCPT_CSI_VI_0) | BIT(NVSYNCPT_CSI_VI_1) | \ - BIT(NVSYNCPT_VI_ISP_1) | BIT(NVSYNCPT_VI_ISP_2) | \ - BIT(NVSYNCPT_VI_ISP_3) | BIT(NVSYNCPT_VI_ISP_4) | \ - BIT(NVSYNCPT_MPE_EBM_EOF) | BIT(NVSYNCPT_MPE_WR_SAFE) | \ - BIT(NVSYNCPT_2D_1)) - -#define NVWAITBASE_2D_0 (1) -#define NVWAITBASE_2D_1 (2) -#define NVWAITBASE_3D (3) -#define NVWAITBASE_MPE (4) - struct nvhost_syncpt { - atomic_t min_val[NV_HOST1X_SYNCPT_NB_PTS]; - atomic_t max_val[NV_HOST1X_SYNCPT_NB_PTS]; - u32 base_val[NV_HOST1X_SYNCPT_NB_BASES]; + atomic_t *min_val; + atomic_t *max_val; + u32 *base_val; + u32 nb_pts; + u32 nb_bases; + u32 client_managed; }; +int nvhost_syncpt_init(struct nvhost_syncpt *); +#define client_managed(id) (BIT(id) & sp->client_managed) +#define syncpt_to_dev(sp) container_of(sp, struct nvhost_master, syncpt) +#define syncpt_op(sp) (syncpt_to_dev(sp)->op.syncpt) +#define SYNCPT_CHECK_PERIOD 2*HZ +static inline bool nvhost_syncpt_check_max(struct nvhost_syncpt *sp, u32 id, u32 real) +{ + u32 max; + if (client_managed(id)) + return true; + smp_rmb(); + max = (u32)atomic_read(&sp->max_val[id]); + return ((s32)(max - real) >= 0); +} + + /** * Updates the value sent to hardware. */ @@ -159,14 +140,15 @@ static inline int nvhost_syncpt_wait(struct nvhost_syncpt *sp, u32 id, u32 thres * that have already been satisfied and NULL the comparison (to * avoid a wrap condition in the HW). * - * @param: nvmap - needed to access command buffer * @param: sp - global shadowed syncpt struct + * @param: nvmap - needed to access command buffer * @param: mask - bit mask of syncpt IDs referenced in WAITs * @param: wait - start of filled in array of waitchk structs * @param: waitend - end ptr (one beyond last valid waitchk) */ -int nvhost_syncpt_wait_check(struct nvmap_client *nvmap, - struct nvhost_syncpt *sp, u32 mask, +int nvhost_syncpt_wait_check(struct nvhost_syncpt *sp, + struct nvmap_client *nvmap, + u32 mask, struct nvhost_waitchk *wait, struct nvhost_waitchk *waitend); diff --git a/drivers/video/tegra/host/nvhost_3dctx.c b/drivers/video/tegra/host/t20/3dctx_t20.c index ecbca73454aa..b9068b03f78e 100644 --- a/drivers/video/tegra/host/nvhost_3dctx.c +++ b/drivers/video/tegra/host/t20/3dctx_t20.c @@ -1,5 +1,5 @@ /* - * drivers/video/tegra/host/nvhost_3dctx.c + * drivers/video/tegra/host/t20/3dctx_t20.c * * Tegra Graphics Host 3d hardware context * @@ -20,13 +20,16 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "nvhost_hwctx.h" -#include "dev.h" +#include "../nvhost_hwctx.h" +#include "../dev.h" +#include "hardware_t20.h" +#include "syncpt_t20.h" #include <mach/gpufuse.h> #include <mach/hardware.h> #include <linux/slab.h> + #define NV_WAR_789194 1 /* 99 > 2, which makes kernel panic if register set is incorrect */ @@ -38,6 +41,8 @@ static bool s_is_v1 = true; #endif static bool s_war_insert_syncpoints; + + const struct hwctx_reginfo ctxsave_regs_3d_global[] = { HWCTX_REGINFO(0, 0xe00, 4, DIRECT), HWCTX_REGINFO(0, 0xe05, 30, DIRECT), @@ -769,10 +774,7 @@ static const struct ctx_saver v1_saver __initconst = { .ctx3d_save_service = NULL }; - -/*** nvhost_3dctx ***/ - -int __init nvhost_3dctx_handler_init(struct nvhost_hwctx_handler *h) +int __init t20_nvhost_3dctx_handler_init(struct nvhost_hwctx_handler *h) { const struct ctx_saver *saver = s_is_v1 ? &v1_saver : &v0_saver; struct nvhost_channel *ch; @@ -819,7 +821,7 @@ int __init nvhost_3dctx_handler_init(struct nvhost_hwctx_handler *h) } /* TODO: [ahatala 2010-05-27] */ -int __init nvhost_mpectx_handler_init(struct nvhost_hwctx_handler *h) +int __init t20_nvhost_mpectx_handler_init(struct nvhost_hwctx_handler *h) { return 0; } diff --git a/drivers/video/tegra/host/t20/Makefile b/drivers/video/tegra/host/t20/Makefile new file mode 100644 index 000000000000..a4d404074d0c --- /dev/null +++ b/drivers/video/tegra/host/t20/Makefile @@ -0,0 +1,11 @@ +nvhost-t20-objs = \ + t20.o \ + syncpt_t20.o \ + cpuaccess_t20.o \ + channel_t20.o \ + intr_t20.o \ + cdma_t20.o \ + 3dctx_t20.o \ + debug_t20.o + +obj-$(CONFIG_TEGRA_GRHOST) += nvhost-t20.o diff --git a/drivers/video/tegra/host/t20/cdma_t20.c b/drivers/video/tegra/host/t20/cdma_t20.c new file mode 100644 index 000000000000..77f514eb4e01 --- /dev/null +++ b/drivers/video/tegra/host/t20/cdma_t20.c @@ -0,0 +1,243 @@ +/* + * drivers/video/tegra/host/t20/cdma_t20.c + * + * Tegra Graphics Host Command DMA + * + * Copyright (c) 2010-2011, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "../nvhost_cdma.h" +#include "../dev.h" + +#include "hardware_t20.h" + +/* + * push_buffer + * + * The push buffer is a circular array of words to be fetched by command DMA. + * Note that it works slightly differently to the sync queue; fence == cur + * means that the push buffer is full, not empty. + */ + + +/** + * Reset to empty push buffer + */ +static void t20_push_buffer_reset(struct push_buffer *pb) +{ + pb->fence = PUSH_BUFFER_SIZE - 8; + pb->cur = 0; +} + +/** + * Init push buffer resources + */ +static int t20_push_buffer_init(struct push_buffer *pb) +{ + struct nvhost_cdma *cdma = pb_to_cdma(pb); + struct nvmap_client *nvmap = cdma_to_nvmap(cdma); + pb->mem = NULL; + pb->mapped = NULL; + pb->phys = 0; + + BUG_ON(!cdma_pb_op(cdma).reset); + cdma_pb_op(cdma).reset(pb); + + /* allocate and map pushbuffer memory */ + pb->mem = nvmap_alloc(nvmap, PUSH_BUFFER_SIZE + 4, 32, + NVMAP_HANDLE_WRITE_COMBINE); + if (IS_ERR_OR_NULL(pb->mem)) { + pb->mem = NULL; + goto fail; + } + pb->mapped = nvmap_mmap(pb->mem); + if (pb->mapped == NULL) + goto fail; + + /* pin pushbuffer and get physical address */ + pb->phys = nvmap_pin(nvmap, pb->mem); + if (pb->phys >= 0xfffff000) { + pb->phys = 0; + goto fail; + } + + /* put the restart at the end of pushbuffer memory */ + *(pb->mapped + (PUSH_BUFFER_SIZE >> 2)) = nvhost_opcode_restart(pb->phys); + + return 0; + +fail: + cdma_pb_op(cdma).destroy(pb); + return -ENOMEM; +} + +/** + * Clean up push buffer resources + */ +static void t20_push_buffer_destroy(struct push_buffer *pb) +{ + struct nvhost_cdma *cdma = pb_to_cdma(pb); + struct nvmap_client *nvmap = cdma_to_nvmap(cdma); + if (pb->mapped) + nvmap_munmap(pb->mem, pb->mapped); + + if (pb->phys != 0) + nvmap_unpin(nvmap, pb->mem); + + if (pb->mem) + nvmap_free(nvmap, pb->mem); + + pb->mem = NULL; + pb->mapped = NULL; + pb->phys = 0; +} + +/** + * Push two words to the push buffer + * Caller must ensure push buffer is not full + */ +static void t20_push_buffer_push_to(struct push_buffer *pb, u32 op1, u32 op2) +{ + u32 cur = pb->cur; + u32 *p = (u32*)((u32)pb->mapped + cur); + BUG_ON(cur == pb->fence); + *(p++) = op1; + *(p++) = op2; + pb->cur = (cur + 8) & (PUSH_BUFFER_SIZE - 1); + /* printk("push_to_push_buffer: op1=%08x; op2=%08x; cur=%x\n", op1, op2, pb->cur); */ +} + +/** + * Pop a number of two word slots from the push buffer + * Caller must ensure push buffer is not empty + */ +static void t20_push_buffer_pop_from(struct push_buffer *pb, unsigned int slots) +{ + pb->fence = (pb->fence + slots * 8) & (PUSH_BUFFER_SIZE - 1); +} + +/** + * Return the number of two word slots free in the push buffer + */ +static u32 t20_push_buffer_space(struct push_buffer *pb) +{ + return ((pb->fence - pb->cur) & (PUSH_BUFFER_SIZE - 1)) / 8; +} + +static u32 t20_push_buffer_putptr(struct push_buffer *pb) +{ + return pb->phys + pb->cur; +} + + +/** + * Start channel DMA + */ +static void t20_cdma_start(struct nvhost_cdma *cdma) +{ + void __iomem *chan_regs = cdma_to_channel(cdma)->aperture; + + if (cdma->running) + return; + + BUG_ON(!cdma_pb_op(cdma).putptr); + + cdma->last_put = cdma_pb_op(cdma).putptr(&cdma->push_buffer); + + writel(nvhost_channel_dmactrl(true, false, false), + chan_regs + HOST1X_CHANNEL_DMACTRL); + + /* set base, put, end pointer (all of memory) */ + writel(0, chan_regs + HOST1X_CHANNEL_DMASTART); + writel(cdma->last_put, chan_regs + HOST1X_CHANNEL_DMAPUT); + writel(0xFFFFFFFF, chan_regs + HOST1X_CHANNEL_DMAEND); + + /* reset GET */ + writel(nvhost_channel_dmactrl(true, true, true), + chan_regs + HOST1X_CHANNEL_DMACTRL); + + /* start the command DMA */ + writel(nvhost_channel_dmactrl(false, false, false), + chan_regs + HOST1X_CHANNEL_DMACTRL); + + cdma->running = true; +} + +/** + * Kick channel DMA into action by writing its PUT offset (if it has changed) + */ +static void t20_cdma_kick(struct nvhost_cdma *cdma) +{ + u32 put; + BUG_ON(!cdma_pb_op(cdma).putptr); + + put = cdma_pb_op(cdma).putptr(&cdma->push_buffer); + + if (put != cdma->last_put) { + void __iomem *chan_regs = cdma_to_channel(cdma)->aperture; + wmb(); + writel(put, chan_regs + HOST1X_CHANNEL_DMAPUT); + cdma->last_put = put; + } +} + +static void t20_cdma_stop(struct nvhost_cdma *cdma) +{ + void __iomem *chan_regs = cdma_to_channel(cdma)->aperture; + + mutex_lock(&cdma->lock); + if (cdma->running) { + nvhost_cdma_wait(cdma, CDMA_EVENT_SYNC_QUEUE_EMPTY); + writel(nvhost_channel_dmactrl(true, false, false), + chan_regs + HOST1X_CHANNEL_DMACTRL); + cdma->running = false; + } + mutex_unlock(&cdma->lock); +} + +/** + * Retrieve the op pair at a slot offset from a DMA address + */ +void t20_cdma_peek(struct nvhost_cdma *cdma, + u32 dmaget, int slot, u32 *out) +{ + u32 offset = dmaget - cdma->push_buffer.phys; + u32 *p = cdma->push_buffer.mapped; + + offset = ((offset + slot * 8) & (PUSH_BUFFER_SIZE - 1)) >> 2; + out[0] = p[offset]; + out[1] = p[offset + 1]; +} + +int nvhost_init_t20_cdma_support(struct nvhost_master *host) +{ + host->op.cdma.start = t20_cdma_start; + host->op.cdma.stop = t20_cdma_stop; + host->op.cdma.kick = t20_cdma_kick; + + host->sync_queue_size = NVHOST_SYNC_QUEUE_SIZE; + + host->op.push_buffer.reset = t20_push_buffer_reset; + host->op.push_buffer.init = t20_push_buffer_init; + host->op.push_buffer.destroy = t20_push_buffer_destroy; + host->op.push_buffer.push_to = t20_push_buffer_push_to; + host->op.push_buffer.pop_from = t20_push_buffer_pop_from; + host->op.push_buffer.space = t20_push_buffer_space; + host->op.push_buffer.putptr = t20_push_buffer_putptr; + + return 0; +} diff --git a/drivers/video/tegra/host/t20/channel_t20.c b/drivers/video/tegra/host/t20/channel_t20.c new file mode 100644 index 000000000000..68cffd71f7fd --- /dev/null +++ b/drivers/video/tegra/host/t20/channel_t20.c @@ -0,0 +1,367 @@ +/* + * drivers/video/tegra/host/t20/channel_t20.c + * + * Tegra Graphics Host Channel + * + * Copyright (c) 2010-2011, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "../nvhost_channel.h" +#include "../dev.h" +#include "../nvhost_hwctx.h" +#include <trace/events/nvhost.h> + +#include "hardware_t20.h" +#include "syncpt_t20.h" + +#define NVHOST_NUMCHANNELS (NV_HOST1X_CHANNELS - 1) +#define NVHOST_CHANNEL_BASE 0 + +#define NVMODMUTEX_2D_FULL (1) +#define NVMODMUTEX_2D_SIMPLE (2) +#define NVMODMUTEX_2D_SB_A (3) +#define NVMODMUTEX_2D_SB_B (4) +#define NVMODMUTEX_3D (5) +#define NVMODMUTEX_DISPLAYA (6) +#define NVMODMUTEX_DISPLAYB (7) +#define NVMODMUTEX_VI (8) +#define NVMODMUTEX_DSI (9) + +static void power_2d(struct nvhost_module *mod, enum nvhost_power_action action); +static void power_3d(struct nvhost_module *mod, enum nvhost_power_action action); +static void power_mpe(struct nvhost_module *mod, enum nvhost_power_action action); + + + +static const struct nvhost_channeldesc channelmap[] = { +{ + /* channel 0 */ + .name = "display", + .syncpts = BIT(NVSYNCPT_DISP0) | BIT(NVSYNCPT_DISP1) | + BIT(NVSYNCPT_VBLANK0) | BIT(NVSYNCPT_VBLANK1), + .modulemutexes = BIT(NVMODMUTEX_DISPLAYA) | BIT(NVMODMUTEX_DISPLAYB), +}, +{ + /* channel 1 */ + .name = "gr3d", + .syncpts = BIT(NVSYNCPT_3D), + .waitbases = BIT(NVWAITBASE_3D), + .modulemutexes = BIT(NVMODMUTEX_3D), + .class = NV_GRAPHICS_3D_CLASS_ID, + .power = power_3d, +}, +{ + /* channel 2 */ + .name = "gr2d", + .syncpts = BIT(NVSYNCPT_2D_0) | BIT(NVSYNCPT_2D_1), + .waitbases = BIT(NVWAITBASE_2D_0) | BIT(NVWAITBASE_2D_1), + .modulemutexes = BIT(NVMODMUTEX_2D_FULL) | BIT(NVMODMUTEX_2D_SIMPLE) | + BIT(NVMODMUTEX_2D_SB_A) | BIT(NVMODMUTEX_2D_SB_B), + .power = power_2d, +}, +{ + /* channel 3 */ + .name = "isp", + .syncpts = 0, +}, +{ + /* channel 4 */ + .name = "vi", + .syncpts = BIT(NVSYNCPT_CSI_VI_0) | BIT(NVSYNCPT_CSI_VI_1) | + BIT(NVSYNCPT_VI_ISP_0) | BIT(NVSYNCPT_VI_ISP_1) | + BIT(NVSYNCPT_VI_ISP_2) | BIT(NVSYNCPT_VI_ISP_3) | + BIT(NVSYNCPT_VI_ISP_4), + .modulemutexes = BIT(NVMODMUTEX_VI), + .exclusive = true, +}, +{ + /* channel 5 */ + .name = "mpe", + .syncpts = BIT(NVSYNCPT_MPE) | BIT(NVSYNCPT_MPE_EBM_EOF) | + BIT(NVSYNCPT_MPE_WR_SAFE), + .waitbases = BIT(NVWAITBASE_MPE), + .class = NV_VIDEO_ENCODE_MPEG_CLASS_ID, + .power = power_mpe, + .exclusive = true, + .keepalive = true, +}, +{ + /* channel 6 */ + .name = "dsi", + .syncpts = BIT(NVSYNCPT_DSI), + .modulemutexes = BIT(NVMODMUTEX_DSI), +}}; + +static inline void __iomem *t20_channel_aperture(void __iomem *p, int ndx) +{ + ndx += NVHOST_CHANNEL_BASE; + p += NV_HOST1X_CHANNEL0_BASE; + p += ndx * NV_HOST1X_CHANNEL_MAP_SIZE_BYTES; + return p; +} + + +int t20_nvhost_3dctx_handler_init(struct nvhost_hwctx_handler *h); +int t20_nvhost_mpectx_handler_init(struct nvhost_hwctx_handler *h); + +static inline int t20_nvhost_hwctx_handler_init( + struct nvhost_hwctx_handler *h, + const char *module) +{ + if (strcmp(module, "gr3d") == 0) + return t20_nvhost_3dctx_handler_init(h); + else if (strcmp(module, "mpe") == 0) + return t20_nvhost_mpectx_handler_init(h); + + return 0; +} + + +static int t20_channel_init(struct nvhost_channel *ch, + struct nvhost_master *dev, int index) +{ + ch->dev = dev; + ch->desc = channelmap + index; + mutex_init(&ch->reflock); + mutex_init(&ch->submitlock); + + ch->aperture = t20_channel_aperture(dev->aperture, index); + + return t20_nvhost_hwctx_handler_init(&ch->ctxhandler, ch->desc->name); +} + + + +static int t20_channel_submit(struct nvhost_channel *channel, + struct nvhost_hwctx *hwctx, + struct nvmap_client *user_nvmap, + u32 *gather, + u32 *gather_end, + struct nvhost_waitchk *waitchk, + struct nvhost_waitchk *waitchk_end, + u32 waitchk_mask, + struct nvmap_handle **unpins, + int nr_unpins, + u32 syncpt_id, + u32 syncpt_incrs, + u32 *syncpt_value, + bool null_kickoff) +{ + struct nvhost_hwctx *hwctx_to_save = NULL; + struct nvhost_syncpt *sp = &channel->dev->syncpt; + u32 user_syncpt_incrs = syncpt_incrs; + bool need_restore = false; + u32 syncval; + int err; + + /* keep module powered */ + nvhost_module_busy(&channel->mod); + + /* get submit lock */ + err = mutex_lock_interruptible(&channel->submitlock); + if (err) { + nvhost_module_idle(&channel->mod); + return err; + } + + /* remove stale waits */ + if (waitchk != waitchk_end) { + err = nvhost_syncpt_wait_check(sp, + user_nvmap, + waitchk_mask, + waitchk, waitchk_end); + if (err) { + dev_warn(&channel->dev->pdev->dev, + "nvhost_syncpt_wait_check failed: %d\n", err); + mutex_unlock(&channel->submitlock); + nvhost_module_idle(&channel->mod); + return err; + } + } + + /* context switch */ + if (channel->cur_ctx != hwctx) { + trace_nvhost_channel_context_switch(channel->desc->name, + channel->cur_ctx, hwctx); + hwctx_to_save = channel->cur_ctx; + if (hwctx_to_save) { + syncpt_incrs += hwctx_to_save->save_incrs; + hwctx_to_save->valid = true; + channel->ctxhandler.get(hwctx_to_save); + } + channel->cur_ctx = hwctx; + if (channel->cur_ctx && channel->cur_ctx->valid) { + need_restore = true; + syncpt_incrs += channel->cur_ctx->restore_incrs; + } + } + + /* get absolute sync value */ + if (BIT(syncpt_id) & sp->client_managed) + syncval = nvhost_syncpt_set_max(sp, + syncpt_id, syncpt_incrs); + else + syncval = nvhost_syncpt_incr_max(sp, + syncpt_id, syncpt_incrs); + + /* begin a CDMA submit */ + nvhost_cdma_begin(&channel->cdma); + + /* push save buffer (pre-gather setup depends on unit) */ + if (hwctx_to_save) + channel->ctxhandler.save_push(&channel->cdma, hwctx_to_save); + + /* gather restore buffer */ + if (need_restore) + nvhost_cdma_push(&channel->cdma, + nvhost_opcode_gather(channel->cur_ctx->restore_size), + channel->cur_ctx->restore_phys); + + /* add a setclass for modules that require it (unless ctxsw added it) */ + if (!hwctx_to_save && !need_restore && channel->desc->class) + nvhost_cdma_push(&channel->cdma, + nvhost_opcode_setclass(channel->desc->class, 0, 0), + NVHOST_OPCODE_NOOP); + + if (null_kickoff) { + int incr; + u32 op_incr; + + /* TODO ideally we'd also perform host waits here */ + + /* push increments that correspond to nulled out commands */ + op_incr = nvhost_opcode_imm(0, 0x100 | syncpt_id); + for (incr = 0; incr < (user_syncpt_incrs >> 1); incr++) + nvhost_cdma_push(&channel->cdma, op_incr, op_incr); + if (user_syncpt_incrs & 1) + nvhost_cdma_push(&channel->cdma, + op_incr, NVHOST_OPCODE_NOOP); + + /* for 3d, waitbase needs to be incremented after each submit */ + if (channel->desc->class == NV_GRAPHICS_3D_CLASS_ID) + nvhost_cdma_push(&channel->cdma, + nvhost_opcode_setclass( + NV_HOST1X_CLASS_ID, + NV_CLASS_HOST_INCR_SYNCPT_BASE, + 1), + nvhost_class_host_incr_syncpt_base( + NVWAITBASE_3D, + user_syncpt_incrs)); + } + else { + /* push user gathers */ + for ( ; gather != gather_end; gather += 2) + nvhost_cdma_push(&channel->cdma, + nvhost_opcode_gather(gather[0]), + gather[1]); + } + + /* end CDMA submit & stash pinned hMems into sync queue */ + nvhost_cdma_end(&channel->cdma, user_nvmap, + syncpt_id, syncval, unpins, nr_unpins); + + /* + * schedule a context save interrupt (to drain the host FIFO + * if necessary, and to release the restore buffer) + */ + if (hwctx_to_save) + nvhost_intr_add_action(&channel->dev->intr, syncpt_id, + syncval - syncpt_incrs + hwctx_to_save->save_thresh, + NVHOST_INTR_ACTION_CTXSAVE, hwctx_to_save, NULL); + + /* schedule a submit complete interrupt */ + nvhost_intr_add_action(&channel->dev->intr, syncpt_id, syncval, + NVHOST_INTR_ACTION_SUBMIT_COMPLETE, channel, NULL); + + mutex_unlock(&channel->submitlock); + + *syncpt_value = syncval; + return 0; +} + +static void power_2d(struct nvhost_module *mod, enum nvhost_power_action action) +{ + /* TODO: [ahatala 2010-06-17] reimplement EPP hang war */ + if (action == NVHOST_POWER_ACTION_OFF) { + /* TODO: [ahatala 2010-06-17] reset EPP */ + } +} + +static void power_3d(struct nvhost_module *mod, enum nvhost_power_action action) +{ + struct nvhost_channel *ch = container_of(mod, struct nvhost_channel, mod); + struct nvhost_hwctx *hwctx_to_save; + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); + u32 syncpt_incrs, syncpt_val; + void *ref; + + if (action != NVHOST_POWER_ACTION_OFF) + return; + + mutex_lock(&ch->submitlock); + hwctx_to_save = ch->cur_ctx; + if (!hwctx_to_save) { + mutex_unlock(&ch->submitlock); + return; + } + + hwctx_to_save->valid = true; + ch->ctxhandler.get(hwctx_to_save); + ch->cur_ctx = NULL; + + syncpt_incrs = hwctx_to_save->save_incrs; + syncpt_val = nvhost_syncpt_incr_max(&ch->dev->syncpt, + NVSYNCPT_3D, syncpt_incrs); + + nvhost_cdma_begin(&ch->cdma); + ch->ctxhandler.save_push(&ch->cdma, hwctx_to_save); + nvhost_cdma_end(&ch->cdma, ch->dev->nvmap, NVSYNCPT_3D, syncpt_val, NULL, 0); + + nvhost_intr_add_action(&ch->dev->intr, NVSYNCPT_3D, + syncpt_val - syncpt_incrs + hwctx_to_save->save_thresh, + NVHOST_INTR_ACTION_CTXSAVE, hwctx_to_save, NULL); + + nvhost_intr_add_action(&ch->dev->intr, NVSYNCPT_3D, syncpt_val, + NVHOST_INTR_ACTION_WAKEUP, &wq, &ref); + wait_event(wq, + nvhost_syncpt_min_cmp(&ch->dev->syncpt, + NVSYNCPT_3D, syncpt_val)); + + nvhost_intr_put_ref(&ch->dev->intr, ref); + + nvhost_cdma_update(&ch->cdma); + + mutex_unlock(&ch->submitlock); +} + +static void power_mpe(struct nvhost_module *mod, enum nvhost_power_action action) +{ +} + +int nvhost_init_t20_channel_support(struct nvhost_master *host) +{ + + BUILD_BUG_ON(NVHOST_NUMCHANNELS != ARRAY_SIZE(channelmap)); + + host->nb_mlocks = NV_HOST1X_SYNC_MLOCK_NUM; + host->nb_channels = NVHOST_NUMCHANNELS; + + host->op.channel.init = t20_channel_init; + host->op.channel.submit = t20_channel_submit; + + return 0; +} diff --git a/drivers/video/tegra/host/t20/cpuaccess_t20.c b/drivers/video/tegra/host/t20/cpuaccess_t20.c new file mode 100644 index 000000000000..f6bb76f4165a --- /dev/null +++ b/drivers/video/tegra/host/t20/cpuaccess_t20.c @@ -0,0 +1,55 @@ +/* + * drivers/video/tegra/host/cpuaccess_t20.c + * + * Tegra Graphics Host Cpu Register Access + * + * Copyright (c) 2010-2011, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "../nvhost_cpuaccess.h" +#include "../dev.h" + +#include "hardware_t20.h" + +static int t20_cpuaccess_mutex_try_lock(struct nvhost_cpuaccess *ctx, + unsigned int idx) +{ + struct nvhost_master *dev = cpuaccess_to_dev(ctx); + void __iomem *sync_regs = dev->sync_aperture; + /* mlock registers returns 0 when the lock is aquired. + * writing 0 clears the lock. */ + return !!readl(sync_regs + (HOST1X_SYNC_MLOCK_0 + idx * 4)); +} + +static void t20_cpuaccess_mutex_unlock(struct nvhost_cpuaccess *ctx, + unsigned int idx) +{ + struct nvhost_master *dev = cpuaccess_to_dev(ctx); + void __iomem *sync_regs = dev->sync_aperture; + + writel(0, sync_regs + (HOST1X_SYNC_MLOCK_0 + idx * 4)); +} + +int nvhost_init_t20_cpuaccess_support(struct nvhost_master *host) +{ + host->nb_modules = NVHOST_MODULE_NUM; + + host->op.cpuaccess.mutex_try_lock = t20_cpuaccess_mutex_try_lock; + host->op.cpuaccess.mutex_unlock = t20_cpuaccess_mutex_unlock; + + return 0; +} diff --git a/drivers/video/tegra/host/t20/debug_t20.c b/drivers/video/tegra/host/t20/debug_t20.c new file mode 100644 index 000000000000..0c77745b8c41 --- /dev/null +++ b/drivers/video/tegra/host/t20/debug_t20.c @@ -0,0 +1,308 @@ +/* + * drivers/video/tegra/host/t20/debug_t20.c + * + * Copyright (C) 2010 Google, Inc. + * Author: Erik Gilling <konkers@android.com> + * + * Copyright (C) 2011 NVIDIA Corporation + * + * 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/debugfs.h> +#include <linux/seq_file.h> + +#include <asm/io.h> + +#include "../dev.h" +#include "../debug.h" +#include "../nvhost_cdma.h" + +#include "hardware_t20.h" + +enum { + NVHOST_DBG_STATE_CMD = 0, + NVHOST_DBG_STATE_DATA = 1, + NVHOST_DBG_STATE_GATHER = 2 +}; + +static int show_channel_command(struct output *o, u32 val, int *count) +{ + unsigned mask; + unsigned subop; + + switch (val >> 28) { + case 0x0: + mask = val & 0x3f; + if (mask) { + nvhost_debug_output(o, "SETCL(class=%03x, offset=%03x, mask=%02x, [", + val >> 6 & 0x3ff, val >> 16 & 0xfff, mask); + *count = hweight8(mask); + return NVHOST_DBG_STATE_DATA; + } else { + nvhost_debug_output(o, "SETCL(class=%03x)\n", val >> 6 & 0x3ff); + return NVHOST_DBG_STATE_CMD; + } + + case 0x1: + nvhost_debug_output(o, "INCR(offset=%03x, [", val >> 16 & 0xfff); + *count = val & 0xffff; + return NVHOST_DBG_STATE_DATA; + + case 0x2: + nvhost_debug_output(o, "NONINCR(offset=%03x, [", val >> 16 & 0xfff); + *count = val & 0xffff; + return NVHOST_DBG_STATE_DATA; + + case 0x3: + mask = val & 0xffff; + nvhost_debug_output(o, "MASK(offset=%03x, mask=%03x, [", + val >> 16 & 0xfff, mask); + *count = hweight16(mask); + return NVHOST_DBG_STATE_DATA; + + case 0x4: + nvhost_debug_output(o, "IMM(offset=%03x, data=%03x)\n", + val >> 16 & 0xfff, val & 0xffff); + return NVHOST_DBG_STATE_CMD; + + case 0x5: + nvhost_debug_output(o, "RESTART(offset=%08x)\n", val << 4); + return NVHOST_DBG_STATE_CMD; + + case 0x6: + nvhost_debug_output(o, "GATHER(offset=%03x, insert=%d, type=%d, count=%04x, addr=[", + val >> 16 & 0xfff, val >> 15 & 0x1, val >> 14 & 0x1, + val & 0x3fff); + *count = val & 0x3fff; // TODO: insert + return NVHOST_DBG_STATE_GATHER; + + case 0xe: + subop = val >> 24 & 0xf; + if (subop == 0) + nvhost_debug_output(o, "ACQUIRE_MLOCK(index=%d)\n", val & 0xff); + else if (subop == 1) + nvhost_debug_output(o, "RELEASE_MLOCK(index=%d)\n", val & 0xff); + else + nvhost_debug_output(o, "EXTEND_UNKNOWN(%08x)\n", val); + return NVHOST_DBG_STATE_CMD; + + default: + return NVHOST_DBG_STATE_CMD; + } +} + +/* + * TODO: This uses ioremap_xxx on memory which is deprecated. + * Also, it won't work properly with SMMU. + */ +static void show_channel_gather(struct output *o, u32 phys_addr, + phys_addr_t words); + +static void show_channel_word(struct output *o, int *state, int *count, + u32 addr, u32 val) +{ + switch (*state) { + case NVHOST_DBG_STATE_CMD: + if (addr) + nvhost_debug_output(o, "%08x: %08x:", addr, val); + else + nvhost_debug_output(o, "%08x:", val); + + *state = show_channel_command(o, val, count); + if (*state == NVHOST_DBG_STATE_DATA && *count == 0) { + *state = NVHOST_DBG_STATE_CMD; + nvhost_debug_output(o, "])\n"); + } + break; + + case NVHOST_DBG_STATE_DATA: + (*count)--; + nvhost_debug_output(o, "%08x%s", val, *count > 0 ? ", " : "])\n"); + if (*count == 0) + *state = NVHOST_DBG_STATE_CMD; + break; + + case NVHOST_DBG_STATE_GATHER: + *state = NVHOST_DBG_STATE_CMD; + nvhost_debug_output(o, "%08x]):\n", val); + show_channel_gather(o, val, *count); + break; + } +} + +static void show_channel_gather(struct output *o, phys_addr_t phys_addr, + u32 words) +{ + phys_addr_t map_base = phys_addr & PAGE_MASK; + phys_addr_t map_end = (phys_addr + words * 4 + PAGE_SIZE - 1) & PAGE_MASK; + phys_addr_t map_size = map_end - map_base; + phys_addr_t map_offset = phys_addr - map_base; + void *map_addr = ioremap_nocache(map_base, map_size); + int state = NVHOST_DBG_STATE_CMD; + int count, i; + + if (!map_addr) + return; + for (i = 0; i < words; i++) + show_channel_word(o, &state, &count, phys_addr + i * 4, + readl(map_addr + map_offset + i * 4)); + iounmap(map_addr); +} + +static void show_channel_pair(struct output *o, u32 addr, + u32 w0, u32 w1) +{ + int state = NVHOST_DBG_STATE_CMD; + int count; + + show_channel_word(o, &state, &count, addr, w0); + show_channel_word(o, &state, &count, addr, w1); +} + +/** + * Retrieve the op pair at a slot offset from a DMA address + */ +static void cdma_peek(struct nvhost_cdma *cdma, + u32 dmaget, int slot, u32 *out) +{ + u32 offset = dmaget - cdma->push_buffer.phys; + u32 *p = cdma->push_buffer.mapped; + + offset = ((offset + slot * 8) & (PUSH_BUFFER_SIZE - 1)) >> 2; + out[0] = p[offset]; + out[1] = p[offset + 1]; +} + +static void t20_debug_show_channel_cdma(struct nvhost_master *m, + struct output *o, int chid) +{ + struct nvhost_channel *channel = m->channels + chid; + u32 dmaput, dmaget, dmactrl; + u32 cbstat, cbread; + u32 val, base, baseval; + u32 pbw[2]; + + dmaput = readl(channel->aperture + HOST1X_CHANNEL_DMAPUT); + dmaget = readl(channel->aperture + HOST1X_CHANNEL_DMAGET); + dmactrl = readl(channel->aperture + HOST1X_CHANNEL_DMACTRL); + cbread = readl(m->aperture + HOST1X_SYNC_CBREAD(chid)); + cbstat = readl(m->aperture + HOST1X_SYNC_CBSTAT(chid)); + + nvhost_debug_output(o, "%d-%s (%d): ", chid, + channel->mod.name, atomic_read(&channel->mod.refcount)); + + if ((dmactrl & 1) || !channel->cdma.push_buffer.mapped) { + nvhost_debug_output(o, "inactive\n\n"); + return; + } + + switch (cbstat) { + case 0x00010008: + nvhost_debug_output(o, "waiting on syncpt %d val %d\n", + cbread >> 24, cbread & 0xffffff); + break; + + case 0x00010009: + base = (cbread >> 16) & 0xff; + val = readl(m->aperture + HOST1X_SYNC_SYNCPT_BASE(base)); + baseval = val & 0xffff; + val = cbread & 0xffff; + nvhost_debug_output(o, "waiting on syncpt %d val %d " + "(base %d = %d; offset = %d)\n", + cbread >> 24, baseval + val, + base, baseval, val); + break; + + default: + nvhost_debug_output(o, "active class %02x, offset %04x, val %08x\n", + cbstat >> 16, cbstat & 0xffff, cbread); + break; + } + + cdma_peek(&channel->cdma, dmaget, -1, pbw); + show_channel_pair(o, chid, pbw[0], pbw[1]); + nvhost_debug_output(o, "\n"); +} + +void t20_debug_show_channel_fifo(struct nvhost_master *m, + struct output *o, int chid) +{ + u32 val, rd_ptr, wr_ptr, start, end; + int state, count; + + val = readl(m->aperture + HOST1X_CHANNEL_FIFOSTAT); + if (val & (1 << 10)) + return; + + writel(0x0, m->aperture + HOST1X_SYNC_CFPEEK_CTRL); + writel((1 << 31) | (chid << 16), + m->aperture + HOST1X_SYNC_CFPEEK_CTRL); + + val = readl(m->aperture + HOST1X_SYNC_CFPEEK_PTRS); + rd_ptr = val & 0x1ff; + wr_ptr = (val >> 16) & 0x1ff; + + val = readl(m->aperture + HOST1X_SYNC_CF_SETUP(chid)); + start = val & 0x1ff; + end = (val >> 16) & 0x1ff; + + state = NVHOST_DBG_STATE_CMD; + nvhost_debug_output(o, "%d: fifo:\n", chid); + + do { + writel(0x0, m->aperture + HOST1X_SYNC_CFPEEK_CTRL); + writel((1 << 31) | (chid << 16) | rd_ptr, + m->aperture + HOST1X_SYNC_CFPEEK_CTRL); + val = readl(m->aperture + HOST1X_SYNC_CFPEEK_READ); + + show_channel_word(o, &state, &count, 0, val); + + if (rd_ptr == end) + rd_ptr = start; + else + rd_ptr++; + } while (rd_ptr != wr_ptr); + + if (state == NVHOST_DBG_STATE_DATA) + nvhost_debug_output(o, ", ...])\n"); + nvhost_debug_output(o, "\n"); + + writel(0x0, m->aperture + HOST1X_SYNC_CFPEEK_CTRL); +} + +static void t20_debug_show_mlocks(struct nvhost_master *m, struct output *o) +{ + u32 __iomem *mlo_regs = m->sync_aperture + HOST1X_SYNC_MLOCK_OWNER_0; + int i; + + nvhost_debug_output(o, "---- mlocks ----\n"); + for (i = 0; i < NV_HOST1X_NB_MLOCKS; i++) { + u32 owner = readl(mlo_regs + i); + if (owner & 0x1) + nvhost_debug_output(o, "%d: locked by channel %d\n", + i, (owner >> 8) & 0xf); + else if (owner & 0x2) + nvhost_debug_output(o, "%d: locked by cpu\n", i); + else + nvhost_debug_output(o, "%d: unlocked\n", i); + } + nvhost_debug_output(o, "\n"); +} + +int nvhost_init_t20_debug_support(struct nvhost_master *host) +{ + host->op.debug.show_channel_cdma = t20_debug_show_channel_cdma; + host->op.debug.show_channel_fifo = t20_debug_show_channel_fifo; + host->op.debug.show_mlocks = t20_debug_show_mlocks; + + return 0; +} diff --git a/drivers/video/tegra/host/nvhost_hardware.h b/drivers/video/tegra/host/t20/hardware_t20.h index 8fa219595789..f94137cdafe4 100644 --- a/drivers/video/tegra/host/nvhost_hardware.h +++ b/drivers/video/tegra/host/t20/hardware_t20.h @@ -1,9 +1,9 @@ /* - * drivers/video/tegra/host/nvhost_hardware.h + * drivers/video/tegra/host/t20/hardware_t20.h * * Tegra Graphics Host Register Offsets * - * Copyright (c) 2010, NVIDIA Corporation. + * Copyright (c) 2010,2011 NVIDIA Corporation. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,8 +20,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef __NVHOST_HARDWARE_H -#define __NVHOST_HARDWARE_H +#ifndef __NVHOST_HARDWARE_T20_H +#define __NVHOST_HARDWARE_T20_H #include <linux/types.h> #include <linux/bitops.h> @@ -256,5 +256,15 @@ static inline u32 nvhost_mask2(unsigned x, unsigned y) return 1 | (1 << (y - x)); } -#endif /* __NVHOST_HARDWARE_H */ +/* Size of the sync queue. If it is too small, we won't be able to queue up + * many command buffers. If it is too large, we waste memory. */ +#define NVHOST_SYNC_QUEUE_SIZE 8192 +/* Number of gathers we allow to be queued up per channel. Must be a + * power of two. Currently sized such that pushbuffer is 4KB (512*8B). */ +#define NVHOST_GATHER_QUEUE_SIZE 512 + +/* 8 bytes per slot. (This number does not include the final RESTART.) */ +#define PUSH_BUFFER_SIZE (NVHOST_GATHER_QUEUE_SIZE * 8) + +#endif /* __NVHOST_HARDWARE_T20_H */ diff --git a/drivers/video/tegra/host/t20/intr_t20.c b/drivers/video/tegra/host/t20/intr_t20.c new file mode 100644 index 000000000000..94bdae4bf392 --- /dev/null +++ b/drivers/video/tegra/host/t20/intr_t20.c @@ -0,0 +1,201 @@ +/* + * drivers/video/tegra/host/t20/intr_t20.c + * + * Tegra Graphics Host Interrupt Management + * + * Copyright (c) 2010-2011, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> + +#include "../nvhost_intr.h" +#include "../dev.h" + +#include "hardware_t20.h" + + +/*** HW host sync management ***/ + +static void t20_intr_init_host_sync(struct nvhost_intr *intr) +{ + struct nvhost_master *dev = intr_to_dev(intr); + void __iomem *sync_regs = dev->sync_aperture; + /* disable the ip_busy_timeout. this prevents write drops, etc. + * there's no real way to recover from a hung client anyway. + */ + writel(0, sync_regs + HOST1X_SYNC_IP_BUSY_TIMEOUT); + + /* increase the auto-ack timout to the maximum value. 2d will hang + * otherwise on ap20. + */ + writel(0xff, sync_regs + HOST1X_SYNC_CTXSW_TIMEOUT_CFG); +} + +static void t20_intr_set_host_clocks_per_usec(struct nvhost_intr *intr, u32 cpm) +{ + struct nvhost_master *dev = intr_to_dev(intr); + void __iomem *sync_regs = dev->sync_aperture; + /* write microsecond clock register */ + writel(cpm, sync_regs + HOST1X_SYNC_USEC_CLK); +} + +static void t20_intr_set_syncpt_threshold(struct nvhost_intr *intr, u32 id, u32 thresh) +{ + struct nvhost_master *dev = intr_to_dev(intr); + void __iomem *sync_regs = dev->sync_aperture; + thresh &= 0xffff; + writel(thresh, sync_regs + (HOST1X_SYNC_SYNCPT_INT_THRESH_0 + id * 4)); +} + +static void t20_intr_enable_syncpt_intr(struct nvhost_intr *intr, u32 id) +{ + struct nvhost_master *dev = intr_to_dev(intr); + void __iomem *sync_regs = dev->sync_aperture; + writel(BIT(id), sync_regs + HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0); +} + +static void t20_intr_disable_all_syncpt_intrs(struct nvhost_intr *intr) +{ + struct nvhost_master *dev = intr_to_dev(intr); + void __iomem *sync_regs = dev->sync_aperture; + /* disable interrupts for both cpu's */ + writel(0, sync_regs + HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE); + + /* clear status for both cpu's */ + writel(0xfffffffful, sync_regs + + HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS); + writel(0xfffffffful, sync_regs + + HOST1X_SYNC_SYNCPT_THRESH_CPU1_INT_STATUS); +} + +/** + * Sync point threshold interrupt service function + * Handles sync point threshold triggers, in interrupt context + */ +irqreturn_t t20_intr_syncpt_thresh_isr(int irq, void *dev_id) +{ + struct nvhost_intr_syncpt *syncpt = dev_id; + unsigned int id = syncpt->id; + struct nvhost_intr *intr = intr_syncpt_to_intr(syncpt); + + void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture; + + writel(BIT(id), + sync_regs + HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE); + writel(BIT(id), + sync_regs + HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS); + + return IRQ_WAKE_THREAD; +} + +/** + * Host general interrupt service function + * Handles read / write failures + */ +static irqreturn_t t20_intr_host1x_isr(int irq, void *dev_id) +{ + struct nvhost_intr *intr = dev_id; + void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture; + u32 stat; + u32 ext_stat; + u32 addr; + + stat = readl(sync_regs + HOST1X_SYNC_HINTSTATUS); + ext_stat = readl(sync_regs + HOST1X_SYNC_HINTSTATUS_EXT); + + if (nvhost_sync_hintstatus_ext_ip_read_int(ext_stat)) { + addr = readl(sync_regs + HOST1X_SYNC_IP_READ_TIMEOUT_ADDR); + pr_err("Host read timeout at address %x\n", addr); + } + + if (nvhost_sync_hintstatus_ext_ip_write_int(ext_stat)) { + addr = readl(sync_regs + HOST1X_SYNC_IP_WRITE_TIMEOUT_ADDR); + pr_err("Host write timeout at address %x\n", addr); + } + + writel(ext_stat, sync_regs + HOST1X_SYNC_HINTSTATUS_EXT); + writel(stat, sync_regs + HOST1X_SYNC_HINTSTATUS); + + return IRQ_HANDLED; +} +static int t20_intr_request_host_general_irq(struct nvhost_intr *intr) +{ + void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture; + int err; + + if (intr->host_general_irq_requested) + return 0; + + /* master disable for general (not syncpt) host interrupts */ + writel(0, sync_regs + HOST1X_SYNC_INTMASK); + + /* clear status & extstatus */ + writel(0xfffffffful, sync_regs + HOST1X_SYNC_HINTSTATUS_EXT); + writel(0xfffffffful, sync_regs + HOST1X_SYNC_HINTSTATUS); + + err = request_irq(intr->host_general_irq, t20_intr_host1x_isr, 0, + "host_status", intr); + if (err) + return err; + + /* enable extra interrupt sources IP_READ_INT and IP_WRITE_INT */ + writel(BIT(30) | BIT(31), sync_regs + HOST1X_SYNC_HINTMASK_EXT); + + /* enable extra interrupt sources */ + writel(BIT(31), sync_regs + HOST1X_SYNC_HINTMASK); + + /* enable host module interrupt to CPU0 */ + writel(BIT(0), sync_regs + HOST1X_SYNC_INTC0MASK); + + /* master enable for general (not syncpt) host interrupts */ + writel(BIT(0), sync_regs + HOST1X_SYNC_INTMASK); + + intr->host_general_irq_requested = true; + + return err; +} + +static void t20_intr_free_host_general_irq(struct nvhost_intr *intr) +{ + if (intr->host_general_irq_requested) { + void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture; + + /* master disable for general (not syncpt) host interrupts */ + writel(0, sync_regs + HOST1X_SYNC_INTMASK); + + free_irq(intr->host_general_irq, intr); + intr->host_general_irq_requested = false; + } +} + +int nvhost_init_t20_intr_support(struct nvhost_master *host) +{ + host->op.intr.init_host_sync = t20_intr_init_host_sync; + host->op.intr.set_host_clocks_per_usec = + t20_intr_set_host_clocks_per_usec; + host->op.intr.set_syncpt_threshold = t20_intr_set_syncpt_threshold; + host->op.intr.enable_syncpt_intr = t20_intr_enable_syncpt_intr; + host->op.intr.disable_all_syncpt_intrs = + t20_intr_disable_all_syncpt_intrs; + host->op.intr.request_host_general_irq = + t20_intr_request_host_general_irq; + host->op.intr.free_host_general_irq = + t20_intr_free_host_general_irq; + + return 0; +} diff --git a/drivers/video/tegra/host/t20/syncpt_t20.c b/drivers/video/tegra/host/t20/syncpt_t20.c new file mode 100644 index 000000000000..07fdb029e742 --- /dev/null +++ b/drivers/video/tegra/host/t20/syncpt_t20.c @@ -0,0 +1,214 @@ +/* + * drivers/video/tegra/host/t20/syncpt_t20.c + * + * Tegra Graphics Host Syncpoints for T20 + * + * Copyright (c) 2010-2011, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "../nvhost_syncpt.h" +#include "../dev.h" + +#include "syncpt_t20.h" +#include "hardware_t20.h" + +/** + * Write the current syncpoint value back to hw. + */ +static void t20_syncpt_reset(struct nvhost_syncpt *sp, u32 id) +{ + struct nvhost_master *dev = syncpt_to_dev(sp); + int min; + smp_rmb(); + min = atomic_read(&sp->min_val[id]); + writel(min, dev->sync_aperture + (HOST1X_SYNC_SYNCPT_0 + id * 4)); +} + +/** + * Write the current waitbase value back to hw. + */ +static void t20_syncpt_reset_wait_base(struct nvhost_syncpt *sp, u32 id) +{ + struct nvhost_master *dev = syncpt_to_dev(sp); + writel(sp->base_val[id], + dev->sync_aperture + (HOST1X_SYNC_SYNCPT_BASE_0 + id * 4)); +} + +/** + * Read waitbase value from hw. + */ +static void t20_syncpt_read_wait_base(struct nvhost_syncpt *sp, u32 id) +{ + struct nvhost_master *dev = syncpt_to_dev(sp); + sp->base_val[id] = readl(dev->sync_aperture + + (HOST1X_SYNC_SYNCPT_BASE_0 + id * 4)); +} + +/** + * Updates the last value read from hardware. + * (was nvhost_syncpt_update_min) + */ +static u32 t20_syncpt_update_min(struct nvhost_syncpt *sp, u32 id) +{ + struct nvhost_master *dev = syncpt_to_dev(sp); + void __iomem *sync_regs = dev->sync_aperture; + u32 old, live; + + do { + smp_rmb(); + old = (u32)atomic_read(&sp->min_val[id]); + live = readl(sync_regs + (HOST1X_SYNC_SYNCPT_0 + id * 4)); + } while ((u32)atomic_cmpxchg(&sp->min_val[id], old, live) != old); + + BUG_ON(!nvhost_syncpt_check_max(sp, id, live)); + + return live; +} + +/** + * Write a cpu syncpoint increment to the hardware, without touching + * the cache. Caller is responsible for host being powered. + */ +static void t20_syncpt_cpu_incr(struct nvhost_syncpt *sp, u32 id) +{ + struct nvhost_master *dev = syncpt_to_dev(sp); + BUG_ON(!nvhost_module_powered(&dev->mod)); + BUG_ON(!client_managed(id) && nvhost_syncpt_min_eq_max(sp, id)); + writel(BIT(id), dev->sync_aperture + HOST1X_SYNC_SYNCPT_CPU_INCR); + wmb(); +} + +/* returns true, if a <= b < c using wrapping comparison */ +static inline bool nvhost_syncpt_is_between(u32 a, u32 b, u32 c) +{ + return b-a < c-a; +} + +/* returns true, if syncpt >= threshold (mod 1 << 32) */ +static bool nvhost_syncpt_wrapping_comparison(u32 syncpt, u32 threshold) +{ + return nvhost_syncpt_is_between(threshold, syncpt, + (1UL<<31UL)+threshold); +} + +/* check for old WAITs to be removed (avoiding a wrap) */ +static int t20_syncpt_wait_check(struct nvhost_syncpt *sp, + struct nvmap_client *nvmap, + u32 waitchk_mask, + struct nvhost_waitchk *wait, + struct nvhost_waitchk *waitend) +{ + u32 idx; + int err = 0; + + /* get current syncpt values */ + for (idx = 0; idx < NV_HOST1X_SYNCPT_NB_PTS; idx++) { + if (BIT(idx) & waitchk_mask) + nvhost_syncpt_update_min(sp, idx); + } + + BUG_ON(!wait && !waitend); + + /* compare syncpt vs wait threshold */ + while (wait != waitend) { + u32 syncpt, override; + + BUG_ON(wait->syncpt_id > NV_HOST1X_SYNCPT_NB_PTS); + + syncpt = atomic_read(&sp->min_val[wait->syncpt_id]); + if (nvhost_syncpt_wrapping_comparison(syncpt, wait->thresh)) { + /* + * NULL an already satisfied WAIT_SYNCPT host method, + * by patching its args in the command stream. The + * method data is changed to reference a reserved + * (never given out or incr) NVSYNCPT_GRAPHICS_HOST + * syncpt with a matching threshold value of 0, so + * is guaranteed to be popped by the host HW. + */ + dev_dbg(&syncpt_to_dev(sp)->pdev->dev, + "drop WAIT id %d (%s) thresh 0x%x, syncpt 0x%x\n", + wait->syncpt_id, + nvhost_syncpt_name(wait->syncpt_id), + wait->thresh, syncpt); + + /* patch the wait */ + override = nvhost_class_host_wait_syncpt( + NVSYNCPT_GRAPHICS_HOST, 0); + err = nvmap_patch_word(nvmap, + (struct nvmap_handle *)wait->mem, + wait->offset, override); + if (err) + break; + } + wait++; + } + return err; +} + + +static const char *s_syncpt_names[32] = { + "gfx_host", "", "", "", "", "", "", "", "", "", "", + "csi_vi_0", "csi_vi_1", "vi_isp_0", "vi_isp_1", "vi_isp_2", "vi_isp_3", "vi_isp_4", + "2d_0", "2d_1", + "", "", + "3d", "mpe", "disp0", "disp1", "vblank0", "vblank1", "mpe_ebm_eof", "mpe_wr_safe", + "2d_tinyblt", "dsi" +}; + +static const char *t20_syncpt_name(struct nvhost_syncpt *s, u32 id) +{ + BUG_ON(id > ARRAY_SIZE(s_syncpt_names)); + return s_syncpt_names[id]; +} + +static void t20_syncpt_debug(struct nvhost_syncpt *sp) +{ + u32 i; + for (i = 0; i < NV_HOST1X_SYNCPT_NB_PTS; i++) { + u32 max = nvhost_syncpt_read_max(sp, i); + if (!max) + continue; + dev_info(&syncpt_to_dev(sp)->pdev->dev, + "id %d (%s) min %d max %d\n", + i, syncpt_op(sp).name(sp, i), + nvhost_syncpt_update_min(sp, i), max); + + } +} + +int nvhost_init_t20_syncpt_support(struct nvhost_master *host) +{ + + host->sync_aperture = host->aperture + + (NV_HOST1X_CHANNEL0_BASE + + HOST1X_CHANNEL_SYNC_REG_BASE); + + host->op.syncpt.reset = t20_syncpt_reset; + host->op.syncpt.reset_wait_base = t20_syncpt_reset_wait_base; + host->op.syncpt.read_wait_base = t20_syncpt_read_wait_base; + host->op.syncpt.update_min = t20_syncpt_update_min; + host->op.syncpt.cpu_incr = t20_syncpt_cpu_incr; + host->op.syncpt.wait_check = t20_syncpt_wait_check; + host->op.syncpt.debug = t20_syncpt_debug; + host->op.syncpt.name = t20_syncpt_name; + + host->syncpt.nb_pts = NV_HOST1X_SYNCPT_NB_PTS; + host->syncpt.nb_bases = NV_HOST1X_SYNCPT_NB_BASES; + host->syncpt.client_managed = NVSYNCPTS_CLIENT_MANAGED; + + return 0; +} diff --git a/drivers/video/tegra/host/t20/syncpt_t20.h b/drivers/video/tegra/host/t20/syncpt_t20.h new file mode 100644 index 000000000000..5c7c04cad91e --- /dev/null +++ b/drivers/video/tegra/host/t20/syncpt_t20.h @@ -0,0 +1,68 @@ +/* + * drivers/video/tegra/host/t20/syncpt_t20.h + * + * Tegra Graphics Host Syncpoints for T20 + * + * Copyright (c) 2010-2011, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __NVHOST_SYNCPT_T20_H +#define __NVHOST_SYNCPT_T20_H + +#define NVSYNCPT_CSI_VI_0 (11) +#define NVSYNCPT_CSI_VI_1 (12) +#define NVSYNCPT_VI_ISP_0 (13) +#define NVSYNCPT_VI_ISP_1 (14) +#define NVSYNCPT_VI_ISP_2 (15) +#define NVSYNCPT_VI_ISP_3 (16) +#define NVSYNCPT_VI_ISP_4 (17) +#define NVSYNCPT_2D_0 (18) +#define NVSYNCPT_2D_1 (19) +#define NVSYNCPT_3D (22) +#define NVSYNCPT_MPE (23) +#define NVSYNCPT_DISP0 (24) +#define NVSYNCPT_DISP1 (25) +#define NVSYNCPT_VBLANK0 (26) +#define NVSYNCPT_VBLANK1 (27) +#define NVSYNCPT_MPE_EBM_EOF (28) +#define NVSYNCPT_MPE_WR_SAFE (29) +#define NVSYNCPT_DSI (31) + + +/*#define NVSYNCPT_2D_CHANNEL2_0 (20) */ +/*#define NVSYNCPT_2D_CHANNEL2_1 (21) */ +/*#define NVSYNCPT_2D_TINYBLT_WAR (30)*/ +/*#define NVSYNCPT_2D_TINYBLT_RESTORE_CLASS_ID (30)*/ + +/* sync points that are wholly managed by the client */ +#define NVSYNCPTS_CLIENT_MANAGED ( \ + BIT(NVSYNCPT_DISP0) | BIT(NVSYNCPT_DISP1) | BIT(NVSYNCPT_DSI) | \ + BIT(NVSYNCPT_CSI_VI_0) | BIT(NVSYNCPT_CSI_VI_1) | \ + BIT(NVSYNCPT_VI_ISP_1) | BIT(NVSYNCPT_VI_ISP_2) | \ + BIT(NVSYNCPT_VI_ISP_3) | BIT(NVSYNCPT_VI_ISP_4) | \ + BIT(NVSYNCPT_MPE_EBM_EOF) | BIT(NVSYNCPT_MPE_WR_SAFE) | \ + BIT(NVSYNCPT_2D_1)) + + +#define NVWAITBASE_2D_0 (1) +#define NVWAITBASE_2D_1 (2) +#define NVWAITBASE_3D (3) +#define NVWAITBASE_MPE (4) + +int nvhost_t20_init_syncpt(struct nvhost_master *host); + +#endif /* __NVHOST_SYNCPT_T20_H */ diff --git a/drivers/video/tegra/host/t20/t20.c b/drivers/video/tegra/host/t20/t20.c new file mode 100644 index 000000000000..c846ce7cf289 --- /dev/null +++ b/drivers/video/tegra/host/t20/t20.c @@ -0,0 +1,51 @@ +/* + * drivers/video/tegra/host/t20/t20.c + * + * Tegra Graphics Init for T20 Architecture Chips + * + * Copyright (c) 2011, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "../dev.h" + +#include "t20.h" + +int nvhost_init_t20_support(struct nvhost_master *host) +{ + int err; + + /* don't worry about cleaning up on failure... "remove" does it. */ + err = nvhost_init_t20_channel_support(host); + if (err) + return err; + err = nvhost_init_t20_cdma_support(host); + if (err) + return err; + err = nvhost_init_t20_debug_support(host); + if (err) + return err; + err = nvhost_init_t20_syncpt_support(host); + if (err) + return err; + err = nvhost_init_t20_intr_support(host); + if (err) + return err; + err = nvhost_init_t20_cpuaccess_support(host); + if (err) + return err; + return 0; +} diff --git a/drivers/video/tegra/host/t20/t20.h b/drivers/video/tegra/host/t20/t20.h new file mode 100644 index 000000000000..8a8426c740d1 --- /dev/null +++ b/drivers/video/tegra/host/t20/t20.h @@ -0,0 +1,32 @@ +/* + * drivers/video/tegra/host/t20/t20.h + * + * Tegra Graphics Chip support for T20 + * + * Copyright (c) 2011, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _NVHOST_T20_H_ +#define _NVHOST_T20_H_ + +int nvhost_init_t20_channel_support(struct nvhost_master *); +int nvhost_init_t20_cdma_support(struct nvhost_master *); +int nvhost_init_t20_debug_support(struct nvhost_master *); +int nvhost_init_t20_syncpt_support(struct nvhost_master *); +int nvhost_init_t20_intr_support(struct nvhost_master *); +int nvhost_init_t20_cpuaccess_support(struct nvhost_master *); + +#endif /* _NVHOST_T20_H_ */ diff --git a/drivers/video/tegra/host/t30/Makefile b/drivers/video/tegra/host/t30/Makefile new file mode 100644 index 000000000000..f394706ac134 --- /dev/null +++ b/drivers/video/tegra/host/t30/Makefile @@ -0,0 +1,4 @@ +nvhost-t30-objs = \ + t30.o + +obj-$(CONFIG_TEGRA_GRHOST) += nvhost-t30.o diff --git a/drivers/video/tegra/host/t30/t30.c b/drivers/video/tegra/host/t30/t30.c new file mode 100644 index 000000000000..5185541f029a --- /dev/null +++ b/drivers/video/tegra/host/t30/t30.c @@ -0,0 +1,54 @@ +/* + * drivers/video/tegra/host/t30/t30.c + * + * Tegra Graphics Init for T30 Architecture Chips + * + * Copyright (c) 2011, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "../dev.h" + +/* t30 uses t20 entry points */ +#include "../t20/t20.h" + +#if defined(CONFIG_ARCH_TEGRA_3x_SOC) +int nvhost_init_t30_support(struct nvhost_master *host) +{ + int err; + + /* don't worry about cleaning up on failure... "remove" does it. */ + err = nvhost_init_t20_channel_support(host); + if (err) + return err; + err = nvhost_init_t20_cdma_support(host); + if (err) + return err; + err = nvhost_init_t20_debug_support(host); + if (err) + return err; + err = nvhost_init_t20_syncpt_support(host); + if (err) + return err; + err = nvhost_init_t20_intr_support(host); + if (err) + return err; + err = nvhost_init_t20_cpuaccess_support(host); + if (err) + return err; + return 0; +} +#endif |