summaryrefslogtreecommitdiff
path: root/drivers/video/tegra/host
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/tegra/host')
-rw-r--r--drivers/video/tegra/host/nvhost_channel.h10
-rw-r--r--drivers/video/tegra/host/t20/Makefile1
-rw-r--r--drivers/video/tegra/host/t20/channel_t20.c7
-rw-r--r--drivers/video/tegra/host/t20/mpectx_t20.c572
-rw-r--r--drivers/video/tegra/host/t20/mpectx_t20.h (renamed from drivers/video/tegra/host/nvhost_mpectx.c)16
-rw-r--r--drivers/video/tegra/host/t20/t20.c77
-rw-r--r--drivers/video/tegra/host/t20/t20.h1
-rw-r--r--drivers/video/tegra/host/t30/channel_t30.c5
8 files changed, 674 insertions, 15 deletions
diff --git a/drivers/video/tegra/host/nvhost_channel.h b/drivers/video/tegra/host/nvhost_channel.h
index f48fff615401..b21e3bf035a2 100644
--- a/drivers/video/tegra/host/nvhost_channel.h
+++ b/drivers/video/tegra/host/nvhost_channel.h
@@ -74,16 +74,6 @@ struct nvhost_channel {
struct nvhost_cdma cdma;
};
-struct nvhost_op_pair {
- u32 op1;
- u32 op2;
-};
-
-struct nvhost_cpuinterrupt {
- u32 syncpt_val;
- void *intr_data;
-};
-
int nvhost_channel_init(
struct nvhost_channel *ch,
struct nvhost_master *dev, int index);
diff --git a/drivers/video/tegra/host/t20/Makefile b/drivers/video/tegra/host/t20/Makefile
index a17adf71f7d2..7903e1b96de3 100644
--- a/drivers/video/tegra/host/t20/Makefile
+++ b/drivers/video/tegra/host/t20/Makefile
@@ -8,6 +8,7 @@ nvhost-t20-objs = \
intr_t20.o \
cdma_t20.o \
3dctx_t20.o \
+ mpectx_t20.o \
debug_t20.o
obj-$(CONFIG_TEGRA_GRHOST) += nvhost-t20.o
diff --git a/drivers/video/tegra/host/t20/channel_t20.c b/drivers/video/tegra/host/t20/channel_t20.c
index 70fb0289b62e..f78210c7596a 100644
--- a/drivers/video/tegra/host/t20/channel_t20.c
+++ b/drivers/video/tegra/host/t20/channel_t20.c
@@ -32,6 +32,7 @@
#include "../dev.h"
#include "3dctx_t20.h"
#include "../3dctx_common.h"
+#include "mpectx_t20.h"
#define NVHOST_NUMCHANNELS (NV_HOST1X_CHANNELS - 1)
#define NVHOST_CHANNEL_BASE 0
@@ -120,9 +121,11 @@ const struct nvhost_channeldesc nvhost_t20_channelmap[] = {
BIT(NVSYNCPT_MPE_WR_SAFE),
.waitbases = BIT(NVWAITBASE_MPE),
.class = NV_VIDEO_ENCODE_MPEG_CLASS_ID,
+ .waitbasesync = true,
.exclusive = true,
.keepalive = true,
.module = {
+ .prepare_poweroff = nvhost_mpectx_prepare_power_off,
.clocks = {{"mpe", UINT_MAX}, {"emc", UINT_MAX}, {} },
.powergate_ids = {TEGRA_POWERGATE_MPE, -1},
NVHOST_DEFAULT_CLOCKGATE_DELAY,
@@ -153,7 +156,8 @@ static inline int t20_nvhost_hwctx_handler_init(
{
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;
}
@@ -564,6 +568,7 @@ done:
return err;
}
+
int nvhost_init_t20_channel_support(struct nvhost_master *host)
{
diff --git a/drivers/video/tegra/host/t20/mpectx_t20.c b/drivers/video/tegra/host/t20/mpectx_t20.c
new file mode 100644
index 000000000000..21dc579211c0
--- /dev/null
+++ b/drivers/video/tegra/host/t20/mpectx_t20.c
@@ -0,0 +1,572 @@
+/*
+ * drivers/video/tegra/host/nvhost_mpectx.c
+ *
+ * Tegra Graphics Host MPE HW Context
+ *
+ * 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_hwctx.h"
+#include "../dev.h"
+#include "hardware_t20.h"
+#include "syncpt_t20.h"
+#include "t20.h"
+#include <linux/slab.h>
+
+enum {
+ HWCTX_REGINFO_NORMAL = 0,
+ HWCTX_REGINFO_STASH,
+ HWCTX_REGINFO_CALCULATE,
+ HWCTX_REGINFO_WRITEBACK
+};
+
+const struct hwctx_reginfo ctxsave_regs_mpe[] = {
+ HWCTX_REGINFO(0x124, 1, STASH),
+ HWCTX_REGINFO(0x123, 1, STASH),
+ HWCTX_REGINFO(0x103, 1, STASH),
+ HWCTX_REGINFO(0x074, 1, STASH),
+ HWCTX_REGINFO(0x021, 1, NORMAL),
+ HWCTX_REGINFO(0x020, 1, STASH),
+ HWCTX_REGINFO(0x024, 2, NORMAL),
+ HWCTX_REGINFO(0x0e6, 1, NORMAL),
+ HWCTX_REGINFO(0x3fc, 1, NORMAL),
+ HWCTX_REGINFO(0x3d0, 1, NORMAL),
+ HWCTX_REGINFO(0x3d4, 1, NORMAL),
+ HWCTX_REGINFO(0x013, 1, NORMAL),
+ HWCTX_REGINFO(0x022, 1, NORMAL),
+ HWCTX_REGINFO(0x030, 4, NORMAL),
+ HWCTX_REGINFO(0x023, 1, NORMAL),
+ HWCTX_REGINFO(0x070, 1, NORMAL),
+ HWCTX_REGINFO(0x0a0, 9, NORMAL),
+ HWCTX_REGINFO(0x071, 1, NORMAL),
+ HWCTX_REGINFO(0x100, 4, NORMAL),
+ HWCTX_REGINFO(0x104, 2, NORMAL),
+ HWCTX_REGINFO(0x108, 9, NORMAL),
+ HWCTX_REGINFO(0x112, 2, NORMAL),
+ HWCTX_REGINFO(0x114, 1, STASH),
+ HWCTX_REGINFO(0x014, 1, NORMAL),
+ HWCTX_REGINFO(0x072, 1, NORMAL),
+ HWCTX_REGINFO(0x200, 1, NORMAL),
+ HWCTX_REGINFO(0x0d1, 1, NORMAL),
+ HWCTX_REGINFO(0x0d0, 1, NORMAL),
+ HWCTX_REGINFO(0x0c0, 1, NORMAL),
+ HWCTX_REGINFO(0x0c3, 2, NORMAL),
+ HWCTX_REGINFO(0x0d2, 1, NORMAL),
+ HWCTX_REGINFO(0x0d8, 1, NORMAL),
+ HWCTX_REGINFO(0x0e0, 2, NORMAL),
+ HWCTX_REGINFO(0x07f, 2, NORMAL),
+ HWCTX_REGINFO(0x084, 8, NORMAL),
+ HWCTX_REGINFO(0x0d3, 1, NORMAL),
+ HWCTX_REGINFO(0x040, 13, NORMAL),
+ HWCTX_REGINFO(0x050, 6, NORMAL),
+ HWCTX_REGINFO(0x058, 1, NORMAL),
+ HWCTX_REGINFO(0x057, 1, NORMAL),
+ HWCTX_REGINFO(0x111, 1, NORMAL),
+ HWCTX_REGINFO(0x130, 3, NORMAL),
+ HWCTX_REGINFO(0x201, 1, NORMAL),
+ HWCTX_REGINFO(0x068, 2, NORMAL),
+ HWCTX_REGINFO(0x08c, 1, NORMAL),
+ HWCTX_REGINFO(0x0cf, 1, NORMAL),
+ HWCTX_REGINFO(0x082, 2, NORMAL),
+ HWCTX_REGINFO(0x075, 1, NORMAL),
+ HWCTX_REGINFO(0x0e8, 1, NORMAL),
+ HWCTX_REGINFO(0x056, 1, NORMAL),
+ HWCTX_REGINFO(0x057, 1, NORMAL),
+ HWCTX_REGINFO(0x073, 1, CALCULATE),
+ HWCTX_REGINFO(0x074, 1, NORMAL),
+ HWCTX_REGINFO(0x075, 1, NORMAL),
+ HWCTX_REGINFO(0x076, 1, STASH),
+ HWCTX_REGINFO(0x11a, 9, NORMAL),
+ HWCTX_REGINFO(0x123, 1, NORMAL),
+ HWCTX_REGINFO(0x124, 1, NORMAL),
+ HWCTX_REGINFO(0x12a, 5, NORMAL),
+ HWCTX_REGINFO(0x12f, 1, STASH),
+ HWCTX_REGINFO(0x125, 2, NORMAL),
+ HWCTX_REGINFO(0x034, 1, NORMAL),
+ HWCTX_REGINFO(0x133, 2, NORMAL),
+ HWCTX_REGINFO(0x127, 1, NORMAL),
+ HWCTX_REGINFO(0x106, 1, WRITEBACK),
+ HWCTX_REGINFO(0x107, 1, WRITEBACK)
+};
+
+#define NR_STASHES 8
+#define NR_WRITEBACKS 2
+
+#define RC_RAM_LOAD_CMD 0x115
+#define RC_RAM_LOAD_DATA 0x116
+#define RC_RAM_READ_CMD 0x128
+#define RC_RAM_READ_DATA 0x129
+#define RC_RAM_SIZE 692
+
+#define IRFR_RAM_LOAD_CMD 0xc5
+#define IRFR_RAM_LOAD_DATA 0xc6
+#define IRFR_RAM_READ_CMD 0xcd
+#define IRFR_RAM_READ_DATA 0xce
+#define IRFR_RAM_SIZE 408
+
+struct mpe_save_info {
+ u32 in[NR_STASHES];
+ u32 out[NR_WRITEBACKS];
+ unsigned in_pos;
+ unsigned out_pos;
+ u32 h264_mode;
+};
+
+
+/*** restore ***/
+
+static unsigned int restore_size;
+
+static void restore_begin(u32 *ptr)
+{
+ /* set class to host */
+ ptr[0] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID,
+ NV_CLASS_HOST_INCR_SYNCPT_BASE, 1);
+ /* increment sync point base */
+ ptr[1] = nvhost_class_host_incr_syncpt_base(NVWAITBASE_MPE, 1);
+ /* set class to MPE */
+ ptr[2] = nvhost_opcode_setclass(NV_VIDEO_ENCODE_MPEG_CLASS_ID, 0, 0);
+}
+#define RESTORE_BEGIN_SIZE 3
+
+static void restore_ram(u32 *ptr, unsigned words,
+ unsigned cmd_reg, unsigned data_reg)
+{
+ ptr[0] = nvhost_opcode_imm(cmd_reg, words);
+ ptr[1] = nvhost_opcode_nonincr(data_reg, words);
+}
+#define RESTORE_RAM_SIZE 2
+
+static void restore_end(u32 *ptr)
+{
+ /* syncpt increment to track restore gather. */
+ ptr[0] = nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_OP_DONE,
+ NVSYNCPT_MPE);
+}
+#define RESTORE_END_SIZE 1
+
+static u32 *setup_restore_regs(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;
+ *ptr++ = nvhost_opcode_incr(offset, count);
+ ptr += count;
+ }
+ return ptr;
+}
+
+static u32 *setup_restore_ram(u32 *ptr, unsigned words,
+ unsigned cmd_reg, unsigned data_reg)
+{
+ restore_ram(ptr, words, cmd_reg, data_reg);
+ return ptr + (RESTORE_RAM_SIZE + words);
+}
+
+static void setup_restore(u32 *ptr)
+{
+ restore_begin(ptr);
+ ptr += RESTORE_BEGIN_SIZE;
+
+ ptr = setup_restore_regs(ptr, ctxsave_regs_mpe,
+ ARRAY_SIZE(ctxsave_regs_mpe));
+
+ ptr = setup_restore_ram(ptr, RC_RAM_SIZE,
+ RC_RAM_LOAD_CMD, RC_RAM_LOAD_DATA);
+
+ ptr = setup_restore_ram(ptr, IRFR_RAM_SIZE,
+ IRFR_RAM_LOAD_CMD, IRFR_RAM_LOAD_DATA);
+
+ restore_end(ptr);
+
+ wmb();
+}
+
+
+/*** save ***/
+
+/* the same context save command sequence is used for all contexts. */
+static struct nvmap_handle_ref *save_buf;
+static phys_addr_t save_phys;
+static unsigned int save_size;
+
+struct save_info {
+ u32 *ptr;
+ unsigned int save_count;
+ unsigned int restore_count;
+};
+
+static void __init save_begin(u32 *ptr)
+{
+ /* MPE: when done, increment syncpt to base+1 */
+ ptr[0] = nvhost_opcode_setclass(NV_VIDEO_ENCODE_MPEG_CLASS_ID, 0, 0);
+ ptr[1] = nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_OP_DONE,
+ NVSYNCPT_MPE);
+ /* host: wait for syncpt base+1 */
+ ptr[2] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID,
+ NV_CLASS_HOST_WAIT_SYNCPT_BASE, 1);
+ ptr[3] = nvhost_class_host_wait_syncpt_base(NVSYNCPT_MPE,
+ NVWAITBASE_MPE, 1);
+ /* host: signal context read thread to start reading */
+ ptr[4] = nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_IMMEDIATE,
+ NVSYNCPT_MPE);
+}
+#define SAVE_BEGIN_SIZE 5
+
+static void __init save_direct(u32 *ptr, u32 start_reg, u32 count)
+{
+ ptr[0] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID,
+ NV_CLASS_HOST_INDOFF, 1);
+ ptr[1] = nvhost_class_host_indoff_reg_read(NV_HOST_MODULE_MPE,
+ start_reg, true);
+ ptr[2] = nvhost_opcode_nonincr(NV_CLASS_HOST_INDDATA, count);
+}
+#define SAVE_DIRECT_SIZE 3
+
+static void __init save_set_ram_cmd(u32 *ptr, u32 cmd_reg, u32 count)
+{
+ ptr[0] = nvhost_opcode_setclass(NV_VIDEO_ENCODE_MPEG_CLASS_ID,
+ cmd_reg, 1);
+ ptr[1] = count;
+}
+#define SAVE_SET_RAM_CMD_SIZE 2
+
+static void __init save_read_ram_data_nasty(u32 *ptr, u32 data_reg)
+{
+ ptr[0] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID,
+ NV_CLASS_HOST_INDOFF, 1);
+ ptr[1] = nvhost_class_host_indoff_reg_read(NV_HOST_MODULE_MPE,
+ data_reg, false);
+ ptr[2] = nvhost_opcode_imm(NV_CLASS_HOST_INDDATA, 0);
+ /* write junk data to avoid 'cached problem with register memory' */
+ ptr[3] = nvhost_opcode_setclass(NV_VIDEO_ENCODE_MPEG_CLASS_ID,
+ data_reg, 1);
+ ptr[4] = 0x99;
+}
+#define SAVE_READ_RAM_DATA_NASTY_SIZE 5
+
+static void __init save_end(u32 *ptr)
+{
+ /* Wait for context read service to finish (cpu incr 3) */
+ ptr[0] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID,
+ NV_CLASS_HOST_WAIT_SYNCPT_BASE, 1);
+ ptr[1] = nvhost_class_host_wait_syncpt_base(NVSYNCPT_MPE,
+ NVWAITBASE_MPE, 3);
+ /* Advance syncpoint base */
+ ptr[2] = nvhost_opcode_nonincr(NV_CLASS_HOST_INCR_SYNCPT_BASE, 1);
+ ptr[3] = nvhost_class_host_incr_syncpt_base(NVWAITBASE_MPE, 3);
+ /* set class back to the unit */
+ ptr[4] = nvhost_opcode_setclass(NV_VIDEO_ENCODE_MPEG_CLASS_ID, 0, 0);
+}
+#define SAVE_END_SIZE 5
+
+static void __init 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;
+ if (regs->type != HWCTX_REGINFO_WRITEBACK) {
+ if (ptr) {
+ save_direct(ptr, offset, count);
+ ptr += SAVE_DIRECT_SIZE;
+ memset(ptr, 0, count * 4);
+ ptr += count;
+ }
+ save_count += (SAVE_DIRECT_SIZE + count);
+ }
+ restore_count += (1 + count);
+ }
+
+ info->ptr = ptr;
+ info->save_count = save_count;
+ info->restore_count = restore_count;
+}
+
+static void __init setup_save_ram_nasty(struct save_info *info, unsigned words,
+ unsigned cmd_reg, unsigned data_reg)
+{
+ u32 *ptr = info->ptr;
+ unsigned int save_count = info->save_count;
+ unsigned int restore_count = info->restore_count;
+ unsigned i;
+
+ if (ptr) {
+ save_set_ram_cmd(ptr, cmd_reg, words);
+ ptr += SAVE_SET_RAM_CMD_SIZE;
+ for (i = words; i; --i) {
+ save_read_ram_data_nasty(ptr, data_reg);
+ ptr += SAVE_READ_RAM_DATA_NASTY_SIZE;
+ }
+ }
+
+ save_count += SAVE_SET_RAM_CMD_SIZE;
+ save_count += words * SAVE_READ_RAM_DATA_NASTY_SIZE;
+ restore_count += (RESTORE_RAM_SIZE + words);
+
+ info->ptr = ptr;
+ info->save_count = save_count;
+ info->restore_count = restore_count;
+}
+
+static void __init setup_save(u32 *ptr)
+{
+ struct save_info info = {
+ ptr,
+ SAVE_BEGIN_SIZE,
+ RESTORE_BEGIN_SIZE
+ };
+
+ if (info.ptr) {
+ save_begin(info.ptr);
+ info.ptr += SAVE_BEGIN_SIZE;
+ }
+
+ setup_save_regs(&info, ctxsave_regs_mpe,
+ ARRAY_SIZE(ctxsave_regs_mpe));
+
+ setup_save_ram_nasty(&info, RC_RAM_SIZE,
+ RC_RAM_READ_CMD, RC_RAM_READ_DATA);
+
+ setup_save_ram_nasty(&info, IRFR_RAM_SIZE,
+ IRFR_RAM_READ_CMD, IRFR_RAM_READ_DATA);
+
+ if (info.ptr) {
+ save_end(info.ptr);
+ info.ptr += SAVE_END_SIZE;
+ }
+
+ wmb();
+
+ save_size = info.save_count + SAVE_END_SIZE;
+ restore_size = info.restore_count + RESTORE_END_SIZE;
+}
+
+
+static u32 calculate_mpe(u32 word, struct mpe_save_info *msi)
+{
+ u32 buffer_full_read = msi->in[0] & 0x01ffffff;
+ u32 byte_len = msi->in[1];
+ u32 drain = (msi->in[2] >> 2) & 0x007fffff;
+ u32 rep_frame = msi->in[3] & 0x0000ffff;
+ u32 h264_mode = (msi->in[4] >> 11) & 1;
+ int new_buffer_full;
+
+ if (h264_mode)
+ byte_len >>= 3;
+ new_buffer_full = buffer_full_read + byte_len - (drain * 4);
+ msi->out[0] = max(0, new_buffer_full);
+ msi->out[1] = rep_frame;
+ if (rep_frame == 0)
+ word &= 0xffff0000;
+ return word;
+}
+
+static u32 *save_regs(u32 *ptr, unsigned int *pending,
+ struct nvhost_channel *channel,
+ const struct hwctx_reginfo *regs,
+ unsigned int nr_regs,
+ struct mpe_save_info *msi)
+{
+ const struct hwctx_reginfo *rend = regs + nr_regs;
+
+ for ( ; regs != rend; ++regs) {
+ u32 count = regs->count;
+ ++ptr; /* restore incr */
+ if (regs->type == HWCTX_REGINFO_NORMAL) {
+ nvhost_drain_read_fifo(channel->aperture,
+ ptr, count, pending);
+ ptr += count;
+ } else {
+ u32 word;
+ if (regs->type == HWCTX_REGINFO_WRITEBACK) {
+ BUG_ON(msi->out_pos >= NR_WRITEBACKS);
+ word = msi->out[msi->out_pos++];
+ } else {
+ nvhost_drain_read_fifo(channel->aperture,
+ &word, 1, pending);
+ if (regs->type == HWCTX_REGINFO_STASH) {
+ BUG_ON(msi->in_pos >= NR_STASHES);
+ msi->in[msi->in_pos++] = word;
+ } else {
+ word = calculate_mpe(word, msi);
+ }
+ }
+ *ptr++ = word;
+ }
+ }
+ return ptr;
+}
+
+static u32 *save_ram(u32 *ptr, unsigned int *pending,
+ struct nvhost_channel *channel,
+ unsigned words, unsigned cmd_reg, unsigned data_reg)
+{
+ int err = 0;
+ ptr += RESTORE_RAM_SIZE;
+ err = nvhost_drain_read_fifo(channel->aperture, ptr, words, pending);
+ WARN_ON(err);
+ return ptr + words;
+}
+
+
+/*** ctxmpe ***/
+
+static struct nvhost_hwctx *ctxmpe_alloc(struct nvhost_channel *ch)
+{
+ struct nvmap_client *nvmap = ch->dev->nvmap;
+ struct nvhost_hwctx *ctx;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return NULL;
+ ctx->restore = nvmap_alloc(nvmap, restore_size * 4, 32,
+ NVMAP_HANDLE_WRITE_COMBINE);
+ if (IS_ERR_OR_NULL(ctx->restore)) {
+ kfree(ctx);
+ return NULL;
+ }
+
+ ctx->restore_virt = nvmap_mmap(ctx->restore);
+ if (!ctx->restore_virt) {
+ nvmap_free(nvmap, ctx->restore);
+ kfree(ctx);
+ return NULL;
+ }
+
+ kref_init(&ctx->ref);
+ ctx->channel = ch;
+ ctx->valid = false;
+ ctx->save = save_buf;
+ ctx->save_incrs = 3;
+ ctx->save_thresh = 2;
+ ctx->restore_phys = nvmap_pin(nvmap, ctx->restore);
+ ctx->restore_size = restore_size;
+ ctx->restore_incrs = 1;
+
+ setup_restore(ctx->restore_virt);
+
+ return ctx;
+}
+
+static void ctxmpe_get(struct nvhost_hwctx *ctx)
+{
+ kref_get(&ctx->ref);
+}
+
+static void ctxmpe_free(struct kref *ref)
+{
+ struct nvhost_hwctx *ctx = container_of(ref, struct nvhost_hwctx, ref);
+ struct nvmap_client *nvmap = ctx->channel->dev->nvmap;
+
+ if (ctx->restore_virt)
+ nvmap_munmap(ctx->restore, ctx->restore_virt);
+ nvmap_unpin(nvmap, ctx->restore);
+ nvmap_free(nvmap, ctx->restore);
+ kfree(ctx);
+}
+
+static void ctxmpe_put(struct nvhost_hwctx *ctx)
+{
+ kref_put(&ctx->ref, ctxmpe_free);
+}
+
+static void ctxmpe_save_push(struct nvhost_cdma *cdma, struct nvhost_hwctx *ctx)
+{
+ nvhost_cdma_push(cdma,
+ nvhost_opcode_gather(save_size),
+ save_phys);
+}
+
+static void ctxmpe_save_service(struct nvhost_hwctx *ctx)
+{
+ u32 *ptr = (u32 *)ctx->restore_virt + RESTORE_BEGIN_SIZE;
+ unsigned int pending = 0;
+ struct mpe_save_info msi;
+
+ msi.in_pos = 0;
+ msi.out_pos = 0;
+
+ ptr = save_regs(ptr, &pending, ctx->channel,
+ ctxsave_regs_mpe, ARRAY_SIZE(ctxsave_regs_mpe), &msi);
+
+ ptr = save_ram(ptr, &pending, ctx->channel,
+ RC_RAM_SIZE, RC_RAM_READ_CMD, RC_RAM_READ_DATA);
+
+ ptr = save_ram(ptr, &pending, ctx->channel,
+ IRFR_RAM_SIZE, IRFR_RAM_READ_CMD, IRFR_RAM_READ_DATA);
+
+ wmb();
+ nvhost_syncpt_cpu_incr(&ctx->channel->dev->syncpt, NVSYNCPT_MPE);
+}
+
+
+/*** nvhost_mpectx ***/
+
+int __init t20_nvhost_mpectx_handler_init(struct nvhost_hwctx_handler *h)
+{
+ struct nvhost_channel *ch;
+ struct nvmap_client *nvmap;
+ u32 *save_ptr;
+
+ ch = container_of(h, struct nvhost_channel, ctxhandler);
+ nvmap = ch->dev->nvmap;
+
+ setup_save(NULL);
+
+ save_buf = nvmap_alloc(nvmap, save_size * 4, 32,
+ NVMAP_HANDLE_WRITE_COMBINE);
+ if (IS_ERR(save_buf)) {
+ int err = PTR_ERR(save_buf);
+ save_buf = NULL;
+ return err;
+ }
+
+ save_ptr = nvmap_mmap(save_buf);
+ if (!save_ptr) {
+ nvmap_free(nvmap, save_buf);
+ save_buf = NULL;
+ return -ENOMEM;
+ }
+
+ save_phys = nvmap_pin(nvmap, save_buf);
+
+ setup_save(save_ptr);
+
+ h->alloc = ctxmpe_alloc;
+ h->save_push = ctxmpe_save_push;
+ h->save_service = ctxmpe_save_service;
+ h->get = ctxmpe_get;
+ h->put = ctxmpe_put;
+
+ return 0;
+}
+
+int nvhost_mpectx_prepare_power_off(struct nvhost_module *mod)
+{
+ return nvhost_t20_save_context(mod, NVSYNCPT_MPE);
+}
diff --git a/drivers/video/tegra/host/nvhost_mpectx.c b/drivers/video/tegra/host/t20/mpectx_t20.h
index a5812e7469a3..e5e520e51884 100644
--- a/drivers/video/tegra/host/nvhost_mpectx.c
+++ b/drivers/video/tegra/host/t20/mpectx_t20.h
@@ -1,9 +1,9 @@
/*
- * drivers/video/tegra/host/nvhost_mpectx.c
+ * drivers/video/tegra/host/t20/mpectx_t20.h
*
- * Tegra Graphics Host MPE HW Context
+ * Tegra Graphics Host MPE context saving for T20
*
- * Copyright (c) 2010, NVIDIA Corporation.
+ * 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
@@ -20,4 +20,12 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-/* Placeholder */
+#ifndef __NVHOST_MPECTX_T20_H
+#define __NVHOST_MPECTX_T20_H
+
+struct nvhost_hwctx_handler;
+
+int t20_nvhost_mpectx_handler_init(struct nvhost_hwctx_handler *h);
+int nvhost_mpectx_prepare_power_off(struct nvhost_module *mod);
+
+#endif
diff --git a/drivers/video/tegra/host/t20/t20.c b/drivers/video/tegra/host/t20/t20.c
index c846ce7cf289..a663f4598f02 100644
--- a/drivers/video/tegra/host/t20/t20.c
+++ b/drivers/video/tegra/host/t20/t20.c
@@ -20,6 +20,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include <linux/slab.h>
#include "../dev.h"
#include "t20.h"
@@ -49,3 +50,79 @@ int nvhost_init_t20_support(struct nvhost_master *host)
return err;
return 0;
}
+
+int nvhost_t20_save_context(struct nvhost_module *mod, u32 syncpt_id)
+{
+ 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;
+ int err = 0;
+ void *ref;
+ void *ctx_waiter = NULL, *wakeup_waiter = NULL;
+
+ ctx_waiter = nvhost_intr_alloc_waiter();
+ wakeup_waiter = nvhost_intr_alloc_waiter();
+ if (!ctx_waiter || !wakeup_waiter) {
+ err = -ENOMEM;
+ goto done;
+ }
+
+ if (mod->desc->busy)
+ mod->desc->busy(mod);
+
+ mutex_lock(&ch->submitlock);
+ hwctx_to_save = ch->cur_ctx;
+ if (!hwctx_to_save) {
+ mutex_unlock(&ch->submitlock);
+ goto done;
+ }
+
+ err = nvhost_cdma_begin(&ch->cdma, hwctx_to_save->timeout);
+ if (err) {
+ mutex_unlock(&ch->submitlock);
+ goto done;
+ }
+
+ 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,
+ syncpt_id, syncpt_incrs);
+
+ ch->ctxhandler.save_push(&ch->cdma, hwctx_to_save);
+ nvhost_cdma_end(&ch->cdma, ch->dev->nvmap, syncpt_id, syncpt_val,
+ NULL, 0, hwctx_to_save->timeout);
+
+ err = nvhost_intr_add_action(&ch->dev->intr, syncpt_id,
+ syncpt_val - syncpt_incrs + hwctx_to_save->save_thresh,
+ NVHOST_INTR_ACTION_CTXSAVE, hwctx_to_save,
+ ctx_waiter,
+ NULL);
+ ctx_waiter = NULL;
+ WARN(err, "Failed to set context save interrupt");
+
+ err = nvhost_intr_add_action(&ch->dev->intr, syncpt_id, syncpt_val,
+ NVHOST_INTR_ACTION_WAKEUP, &wq,
+ wakeup_waiter,
+ &ref);
+ wakeup_waiter = NULL;
+ WARN(err, "Failed to set wakeup interrupt");
+ wait_event(wq,
+ nvhost_syncpt_min_cmp(&ch->dev->syncpt,
+ syncpt_id, syncpt_val));
+
+ nvhost_intr_put_ref(&ch->dev->intr, ref);
+
+ nvhost_cdma_update(&ch->cdma);
+
+ mutex_unlock(&ch->submitlock);
+
+done:
+ kfree(ctx_waiter);
+ kfree(wakeup_waiter);
+ return err;
+}
diff --git a/drivers/video/tegra/host/t20/t20.h b/drivers/video/tegra/host/t20/t20.h
index 406841092f01..528718cc9c1d 100644
--- a/drivers/video/tegra/host/t20/t20.h
+++ b/drivers/video/tegra/host/t20/t20.h
@@ -30,5 +30,6 @@ 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 *);
+int nvhost_t20_save_context(struct nvhost_module *mod, u32 syncpt_id);
#endif /* _NVHOST_T20_H_ */
diff --git a/drivers/video/tegra/host/t30/channel_t30.c b/drivers/video/tegra/host/t30/channel_t30.c
index 1c529d396457..ef346181c0c7 100644
--- a/drivers/video/tegra/host/t30/channel_t30.c
+++ b/drivers/video/tegra/host/t30/channel_t30.c
@@ -29,6 +29,7 @@
#include "../t20/syncpt_t20.h"
#include "../3dctx_common.h"
#include "3dctx_t30.h"
+#include "../t20/mpectx_t20.h"
#include "scale3d.h"
#define NVMODMUTEX_2D_FULL (1)
@@ -130,9 +131,11 @@ const struct nvhost_channeldesc nvhost_t30_channelmap[] = {
BIT(NVSYNCPT_MPE_WR_SAFE),
.waitbases = BIT(NVWAITBASE_MPE),
.class = NV_VIDEO_ENCODE_MPEG_CLASS_ID,
+ .waitbasesync = true,
.exclusive = true,
.keepalive = true,
.module = {
+ .prepare_poweroff = nvhost_mpectx_prepare_power_off,
.clocks = {{"mpe", UINT_MAX}, {"emc", UINT_MAX}, {} },
.powergate_ids = {TEGRA_POWERGATE_MPE, -1},
NVHOST_DEFAULT_CLOCKGATE_DELAY,
@@ -159,6 +162,8 @@ static inline int t30_nvhost_hwctx_handler_init(
{
if (strcmp(module, "gr3d") == 0)
return t30_nvhost_3dctx_handler_init(h);
+ else if (strcmp(module, "mpe") == 0)
+ return t20_nvhost_mpectx_handler_init(h);
return 0;
}