summaryrefslogtreecommitdiff
path: root/drivers/video/tegra/host/gr3d/gr3d_t20.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/tegra/host/gr3d/gr3d_t20.c')
-rw-r--r--drivers/video/tegra/host/gr3d/gr3d_t20.c399
1 files changed, 399 insertions, 0 deletions
diff --git a/drivers/video/tegra/host/gr3d/gr3d_t20.c b/drivers/video/tegra/host/gr3d/gr3d_t20.c
new file mode 100644
index 000000000000..694b00527790
--- /dev/null
+++ b/drivers/video/tegra/host/gr3d/gr3d_t20.c
@@ -0,0 +1,399 @@
+/*
+ * drivers/video/tegra/host/gr3d/gr3d_t20.c
+ *
+ * Tegra Graphics Host 3D for Tegra2
+ *
+ * Copyright (c) 2010-2012, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "nvhost_hwctx.h"
+#include "nvhost_channel.h"
+#include "host1x/host1x.h"
+#include "host1x/host1x01_hardware.h"
+#include "gr3d.h"
+#include "chip_support.h"
+#include "nvhost_memmgr.h"
+
+#include <linux/slab.h>
+
+static const struct hwctx_reginfo ctxsave_regs_3d_global[] = {
+ HWCTX_REGINFO(0xe00, 4, DIRECT),
+ HWCTX_REGINFO(0xe05, 30, DIRECT),
+ HWCTX_REGINFO(0xe25, 2, DIRECT),
+ HWCTX_REGINFO(0xe28, 2, DIRECT),
+ HWCTX_REGINFO(0x001, 2, DIRECT),
+ HWCTX_REGINFO(0x00c, 10, DIRECT),
+ HWCTX_REGINFO(0x100, 34, DIRECT),
+ HWCTX_REGINFO(0x124, 2, DIRECT),
+ HWCTX_REGINFO(0x200, 5, DIRECT),
+ HWCTX_REGINFO(0x205, 1024, INDIRECT),
+ HWCTX_REGINFO(0x207, 1024, INDIRECT),
+ HWCTX_REGINFO(0x209, 1, DIRECT),
+ HWCTX_REGINFO(0x300, 64, DIRECT),
+ HWCTX_REGINFO(0x343, 25, DIRECT),
+ HWCTX_REGINFO(0x363, 2, DIRECT),
+ HWCTX_REGINFO(0x400, 16, DIRECT),
+ HWCTX_REGINFO(0x411, 1, DIRECT),
+ HWCTX_REGINFO(0x500, 4, DIRECT),
+ HWCTX_REGINFO(0x520, 32, DIRECT),
+ HWCTX_REGINFO(0x540, 64, INDIRECT),
+ HWCTX_REGINFO(0x600, 16, INDIRECT_4X),
+ HWCTX_REGINFO(0x603, 128, INDIRECT),
+ HWCTX_REGINFO(0x608, 4, DIRECT),
+ HWCTX_REGINFO(0x60e, 1, DIRECT),
+ HWCTX_REGINFO(0x700, 64, INDIRECT),
+ HWCTX_REGINFO(0x710, 50, DIRECT),
+ HWCTX_REGINFO(0x800, 16, INDIRECT_4X),
+ HWCTX_REGINFO(0x803, 512, INDIRECT),
+ HWCTX_REGINFO(0x805, 64, INDIRECT),
+ HWCTX_REGINFO(0x820, 32, DIRECT),
+ HWCTX_REGINFO(0x900, 64, INDIRECT),
+ HWCTX_REGINFO(0x902, 2, DIRECT),
+ HWCTX_REGINFO(0xa02, 10, DIRECT),
+ HWCTX_REGINFO(0xe04, 1, DIRECT),
+ HWCTX_REGINFO(0xe2a, 1, DIRECT),
+};
+
+/* the same context save command sequence is used for all contexts. */
+#define SAVE_BEGIN_V0_SIZE 5
+#define SAVE_DIRECT_V0_SIZE 3
+#define SAVE_INDIRECT_V0_SIZE 5
+#define SAVE_END_V0_SIZE 5
+#define SAVE_INCRS 3
+#define SAVE_THRESH_OFFSET 1
+#define RESTORE_BEGIN_SIZE 4
+#define RESTORE_DIRECT_SIZE 1
+#define RESTORE_INDIRECT_SIZE 2
+#define RESTORE_END_SIZE 1
+
+struct save_info {
+ u32 *ptr;
+ unsigned int save_count;
+ unsigned int restore_count;
+ unsigned int save_incrs;
+ unsigned int restore_incrs;
+};
+
+static u32 *setup_restore_regs_v0(u32 *ptr,
+ const struct hwctx_reginfo *regs,
+ unsigned int nr_regs)
+{
+ const struct hwctx_reginfo *rend = regs + nr_regs;
+
+ for ( ; regs != rend; ++regs) {
+ u32 offset = regs->offset;
+ u32 count = regs->count;
+ u32 indoff = offset + 1;
+ switch (regs->type) {
+ case HWCTX_REGINFO_DIRECT:
+ nvhost_3dctx_restore_direct(ptr, offset, count);
+ ptr += RESTORE_DIRECT_SIZE;
+ break;
+ case HWCTX_REGINFO_INDIRECT_4X:
+ ++indoff;
+ /* fall through */
+ case HWCTX_REGINFO_INDIRECT:
+ nvhost_3dctx_restore_indirect(ptr,
+ offset, 0, indoff, count);
+ ptr += RESTORE_INDIRECT_SIZE;
+ break;
+ }
+ ptr += count;
+ }
+ return ptr;
+}
+
+static void setup_restore_v0(struct host1x_hwctx_handler *h, u32 *ptr)
+{
+ nvhost_3dctx_restore_begin(h, ptr);
+ ptr += RESTORE_BEGIN_SIZE;
+
+ ptr = setup_restore_regs_v0(ptr,
+ ctxsave_regs_3d_global,
+ ARRAY_SIZE(ctxsave_regs_3d_global));
+
+ nvhost_3dctx_restore_end(h, ptr);
+
+ wmb();
+}
+
+/*** v0 saver ***/
+
+static void save_push_v0(struct nvhost_hwctx *nctx, struct nvhost_cdma *cdma)
+{
+ struct host1x_hwctx *ctx = to_host1x_hwctx(nctx);
+ struct host1x_hwctx_handler *p = host1x_hwctx_handler(ctx);
+
+ nvhost_cdma_push_gather(cdma,
+ nvhost_get_host(nctx->channel->dev)->memmgr,
+ p->save_buf,
+ 0,
+ nvhost_opcode_gather(p->save_size),
+ p->save_phys);
+}
+
+static void save_begin_v0(struct host1x_hwctx_handler *h, u32 *ptr)
+{
+ /* 3d: when done, increment syncpt to base+1 */
+ ptr[0] = nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0);
+ ptr[1] = nvhost_opcode_imm_incr_syncpt(
+ host1x_uclass_incr_syncpt_cond_op_done_v(),
+ h->syncpt); /* incr 1 */
+ /* host: wait for syncpt base+1 */
+ ptr[2] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID,
+ host1x_uclass_wait_syncpt_base_r(), 1);
+ ptr[3] = nvhost_class_host_wait_syncpt_base(h->syncpt,
+ h->waitbase, 1);
+ /* host: signal context read thread to start reading */
+ ptr[4] = nvhost_opcode_imm_incr_syncpt(
+ host1x_uclass_incr_syncpt_cond_immediate_v(),
+ h->syncpt); /* incr 2 */
+}
+
+static void save_direct_v0(u32 *ptr, u32 start_reg, u32 count)
+{
+ ptr[0] = nvhost_opcode_nonincr(host1x_uclass_indoff_r(), 1);
+ ptr[1] = nvhost_class_host_indoff_reg_read(NV_HOST_MODULE_GR3D,
+ start_reg, true);
+ ptr[2] = nvhost_opcode_nonincr(host1x_uclass_inddata_r(), count);
+}
+
+static void save_indirect_v0(u32 *ptr, u32 offset_reg, u32 offset,
+ u32 data_reg, u32 count)
+{
+ ptr[0] = nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID,
+ offset_reg, 1);
+ ptr[1] = offset;
+ ptr[2] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID,
+ host1x_uclass_indoff_r(), 1);
+ ptr[3] = nvhost_class_host_indoff_reg_read(NV_HOST_MODULE_GR3D,
+ data_reg, false);
+ ptr[4] = nvhost_opcode_nonincr(host1x_uclass_inddata_r(), count);
+}
+
+static void save_end_v0(struct host1x_hwctx_handler *h, u32 *ptr)
+{
+ /* Wait for context read service to finish (cpu incr 3) */
+ ptr[0] = nvhost_opcode_nonincr(host1x_uclass_wait_syncpt_base_r(), 1);
+ ptr[1] = nvhost_class_host_wait_syncpt_base(h->syncpt,
+ h->waitbase, h->save_incrs);
+ /* Advance syncpoint base */
+ ptr[2] = nvhost_opcode_nonincr(host1x_uclass_incr_syncpt_base_r(), 1);
+ ptr[3] = nvhost_class_host_incr_syncpt_base(h->waitbase,
+ h->save_incrs);
+ /* set class back to the unit */
+ ptr[4] = nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0);
+}
+
+static u32 *save_regs_v0(u32 *ptr, unsigned int *pending,
+ struct nvhost_channel *ch,
+ const struct hwctx_reginfo *regs,
+ unsigned int nr_regs)
+{
+ const struct hwctx_reginfo *rend = regs + nr_regs;
+ int drain_result = 0;
+
+ for ( ; regs != rend; ++regs) {
+ u32 count = regs->count;
+ switch (regs->type) {
+ case HWCTX_REGINFO_DIRECT:
+ ptr += RESTORE_DIRECT_SIZE;
+ break;
+ case HWCTX_REGINFO_INDIRECT:
+ case HWCTX_REGINFO_INDIRECT_4X:
+ ptr += RESTORE_INDIRECT_SIZE;
+ break;
+ }
+ drain_result = nvhost_channel_drain_read_fifo(ch,
+ ptr, count, pending);
+ BUG_ON(drain_result < 0);
+ ptr += count;
+ }
+ return ptr;
+}
+
+/*** save ***/
+
+static void setup_save_regs(struct save_info *info,
+ const struct hwctx_reginfo *regs,
+ unsigned int nr_regs)
+{
+ const struct hwctx_reginfo *rend = regs + nr_regs;
+ u32 *ptr = info->ptr;
+ unsigned int save_count = info->save_count;
+ unsigned int restore_count = info->restore_count;
+
+ for ( ; regs != rend; ++regs) {
+ u32 offset = regs->offset;
+ u32 count = regs->count;
+ u32 indoff = offset + 1;
+ switch (regs->type) {
+ case HWCTX_REGINFO_DIRECT:
+ if (ptr) {
+ save_direct_v0(ptr, offset, count);
+ ptr += SAVE_DIRECT_V0_SIZE;
+ }
+ save_count += SAVE_DIRECT_V0_SIZE;
+ restore_count += RESTORE_DIRECT_SIZE;
+ break;
+ case HWCTX_REGINFO_INDIRECT_4X:
+ ++indoff;
+ /* fall through */
+ case HWCTX_REGINFO_INDIRECT:
+ if (ptr) {
+ save_indirect_v0(ptr, offset, 0,
+ indoff, count);
+ ptr += SAVE_INDIRECT_V0_SIZE;
+ }
+ save_count += SAVE_INDIRECT_V0_SIZE;
+ restore_count += RESTORE_INDIRECT_SIZE;
+ break;
+ }
+ if (ptr) {
+ /* SAVE cases only: reserve room for incoming data */
+ u32 k = 0;
+ /*
+ * Create a signature pattern for indirect data (which
+ * will be overwritten by true incoming data) for
+ * better deducing where we are in a long command
+ * sequence, when given only a FIFO snapshot for debug
+ * purposes.
+ */
+ for (k = 0; k < count; k++)
+ *(ptr + k) = 0xd000d000 | (offset << 16) | k;
+ ptr += count;
+ }
+ save_count += count;
+ restore_count += count;
+ }
+
+ info->ptr = ptr;
+ info->save_count = save_count;
+ info->restore_count = restore_count;
+}
+
+static void setup_save(struct host1x_hwctx_handler *h, u32 *ptr)
+{
+ struct save_info info = {
+ ptr,
+ SAVE_BEGIN_V0_SIZE,
+ RESTORE_BEGIN_SIZE,
+ SAVE_INCRS,
+ 1
+ };
+
+ if (info.ptr) {
+ save_begin_v0(h, info.ptr);
+ info.ptr += SAVE_BEGIN_V0_SIZE;
+ }
+
+ /* save regs */
+ setup_save_regs(&info,
+ ctxsave_regs_3d_global,
+ ARRAY_SIZE(ctxsave_regs_3d_global));
+
+ if (info.ptr) {
+ save_end_v0(h, info.ptr);
+ info.ptr += SAVE_END_V0_SIZE;
+ }
+
+ wmb();
+
+ h->save_size = info.save_count + SAVE_END_V0_SIZE;
+ h->restore_size = info.restore_count + RESTORE_END_SIZE;
+ h->save_incrs = info.save_incrs;
+ h->save_thresh = h->save_incrs - SAVE_THRESH_OFFSET;
+ h->restore_incrs = info.restore_incrs;
+}
+
+
+
+/*** ctx3d ***/
+
+static struct nvhost_hwctx *ctx3d_alloc_v0(struct nvhost_hwctx_handler *h,
+ struct nvhost_channel *ch)
+{
+ struct host1x_hwctx_handler *p = to_host1x_hwctx_handler(h);
+ struct host1x_hwctx *ctx =
+ nvhost_3dctx_alloc_common(p, ch, true);
+ if (ctx) {
+ setup_restore_v0(p, ctx->restore_virt);
+ return &ctx->hwctx;
+ } else
+ return NULL;
+}
+
+static void ctx3d_save_service(struct nvhost_hwctx *nctx)
+{
+ struct host1x_hwctx *ctx = to_host1x_hwctx(nctx);
+
+ u32 *ptr = (u32 *)ctx->restore_virt + RESTORE_BEGIN_SIZE;
+ unsigned int pending = 0;
+
+ ptr = save_regs_v0(ptr, &pending, nctx->channel,
+ ctxsave_regs_3d_global,
+ ARRAY_SIZE(ctxsave_regs_3d_global));
+
+ wmb();
+ nvhost_syncpt_cpu_incr(&nvhost_get_host(nctx->channel->dev)->syncpt,
+ host1x_hwctx_handler(ctx)->syncpt);
+}
+
+struct nvhost_hwctx_handler *nvhost_gr3d_t20_ctxhandler_init(
+ u32 syncpt, u32 waitbase,
+ struct nvhost_channel *ch)
+{
+ struct mem_mgr *memmgr;
+ u32 *save_ptr;
+ struct host1x_hwctx_handler *p;
+
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return NULL;
+ memmgr = nvhost_get_host(ch->dev)->memmgr;
+
+ p->syncpt = syncpt;
+ p->waitbase = waitbase;
+
+ setup_save(p, NULL);
+
+ p->save_buf = mem_op().alloc(memmgr, p->save_size * sizeof(u32), 32,
+ mem_mgr_flag_write_combine);
+ if (IS_ERR_OR_NULL(p->save_buf)) {
+ p->save_buf = NULL;
+ return NULL;
+ }
+
+ p->save_slots = 1;
+
+ save_ptr = mem_op().mmap(p->save_buf);
+ if (!save_ptr) {
+ mem_op().put(memmgr, p->save_buf);
+ p->save_buf = NULL;
+ return NULL;
+ }
+
+ p->save_phys = mem_op().pin(memmgr, p->save_buf);
+
+ setup_save(p, save_ptr);
+
+ p->h.alloc = ctx3d_alloc_v0;
+ p->h.save_push = save_push_v0;
+ p->h.save_service = ctx3d_save_service;
+ p->h.get = nvhost_3dctx_get;
+ p->h.put = nvhost_3dctx_put;
+
+ return &p->h;
+}