video: tegra: host: Do not access cmdbuf_ext if NULL
[linux-3.10.git] / drivers / video / tegra / host / bus_client.c
index e10809b..030e28c 100644 (file)
@@ -1,9 +1,7 @@
 /*
- * drivers/video/tegra/host/bus_client.c
- *
  * Tegra Graphics Host Client Module
  *
- * Copyright (c) 2010-2013, NVIDIA Corporation. All rights reserved.
+ * Copyright (c) 2010-2014, NVIDIA Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -30,6 +28,7 @@
 #include <linux/export.h>
 #include <linux/firmware.h>
 #include <linux/dma-mapping.h>
+#include <linux/tegra-soc.h>
 
 #include <trace/events/nvhost.h>
 
@@ -40,7 +39,6 @@
 #include <linux/nvhost_ioctl.h>
 
 #include <mach/gpufuse.h>
-#include <mach/hardware.h>
 
 #include "debug.h"
 #include "bus_client.h"
 #include "nvhost_job.h"
 #include "nvhost_hwctx.h"
 #include "user_hwctx.h"
+#include "nvhost_sync.h"
 
 static int validate_reg(struct platform_device *ndev, u32 offset, int count)
 {
        int err = 0;
-       struct resource *r = platform_get_resource(ndev, IORESOURCE_MEM, 0);
+       struct resource *r;
+       struct nvhost_device_data *pdata = platform_get_drvdata(ndev);
+
+       r = platform_get_resource(pdata->master ? pdata->master : ndev,
+                       IORESOURCE_MEM, 0);
        if (!r) {
                dev_err(&ndev->dev, "failed to get memory resource\n");
                return -ENODEV;
@@ -73,14 +76,40 @@ static int validate_reg(struct platform_device *ndev, u32 offset, int count)
        return err;
 }
 
+static __iomem void *get_aperture(struct platform_device *pdev)
+{
+       struct nvhost_device_data *pdata = platform_get_drvdata(pdev);
+
+       if (pdata->master)
+               pdata = platform_get_drvdata(pdata->master);
+
+       return pdata->aperture[0];
+}
+
+void host1x_writel(struct platform_device *pdev, u32 r, u32 v)
+{
+       void __iomem *addr = get_aperture(pdev) + r;
+       nvhost_dbg(dbg_reg, " d=%s r=0x%x v=0x%x", pdev->name, r, v);
+       writel(v, addr);
+}
+EXPORT_SYMBOL_GPL(host1x_writel);
+
+u32 host1x_readl(struct platform_device *pdev, u32 r)
+{
+       void __iomem *addr = get_aperture(pdev) + r;
+       u32 v = readl(addr);
+       nvhost_dbg(dbg_reg, " d=%s r=0x%x v=0x%x", pdev->name, r, v);
+       return v;
+}
+EXPORT_SYMBOL_GPL(host1x_readl);
+
 int nvhost_read_module_regs(struct platform_device *ndev,
                        u32 offset, int count, u32 *values)
 {
-       struct nvhost_device_data *pdata = platform_get_drvdata(ndev);
-       void __iomem *p = pdata->aperture[0] + offset;
+       void __iomem *p = get_aperture(ndev);
        int err;
 
-       if (!pdata->aperture[0])
+       if (!p)
                return -ENODEV;
 
        /* verify offset */
@@ -89,6 +118,7 @@ int nvhost_read_module_regs(struct platform_device *ndev,
                return err;
 
        nvhost_module_busy(ndev);
+       p += offset;
        while (count--) {
                *(values++) = readl(p);
                p += 4;
@@ -102,21 +132,19 @@ int nvhost_read_module_regs(struct platform_device *ndev,
 int nvhost_write_module_regs(struct platform_device *ndev,
                        u32 offset, int count, const u32 *values)
 {
-       void __iomem *p;
        int err;
-       struct nvhost_device_data *pdata = platform_get_drvdata(ndev);
+       void __iomem *p = get_aperture(ndev);
 
-       if (!pdata->aperture[0])
+       if (!p)
                return -ENODEV;
 
-       p = pdata->aperture[0] + offset;
-
        /* verify offset */
        err = validate_reg(ndev, offset, count);
        if (err)
                return err;
 
        nvhost_module_busy(ndev);
+       p += offset;
        while (count--) {
                writel(*(values++), p);
                p += 4;
@@ -130,8 +158,6 @@ int nvhost_write_module_regs(struct platform_device *ndev,
 struct nvhost_channel_userctx {
        struct nvhost_channel *ch;
        struct nvhost_hwctx *hwctx;
-       struct nvhost_submit_hdr_ext hdr;
-       int num_relocshifts;
        struct nvhost_job *job;
        struct mem_mgr *memmgr;
        u32 timeout;
@@ -179,7 +205,7 @@ static int nvhost_channelopen(struct inode *inode, struct file *filp)
        struct nvhost_device_data *pdata;
 
        ch = container_of(inode->i_cdev, struct nvhost_channel, cdev);
-       ch = nvhost_getchannel(ch);
+       ch = nvhost_getchannel(ch, false);
        if (!ch)
                return -ENOMEM;
        trace_nvhost_channel_open(dev_name(&ch->dev->dev));
@@ -191,7 +217,7 @@ static int nvhost_channelopen(struct inode *inode, struct file *filp)
        }
        filp->private_data = priv;
        priv->ch = ch;
-       if(nvhost_module_add_client(ch->dev, priv))
+       if (nvhost_module_add_client(ch->dev, priv))
                goto fail;
 
        if (ch->ctxhandler && ch->ctxhandler->alloc) {
@@ -204,304 +230,66 @@ static int nvhost_channelopen(struct inode *inode, struct file *filp)
        priv->priority = NVHOST_PRIORITY_MEDIUM;
        priv->clientid = atomic_add_return(1,
                        &nvhost_get_host(ch->dev)->clientid);
-       pdata = platform_get_drvdata(ch->dev);
+       pdata = dev_get_drvdata(ch->dev->dev.parent);
        priv->timeout = pdata->nvhost_timeout_default;
        priv->timeout_debug_dump = true;
        if (!tegra_platform_is_silicon())
                priv->timeout = 0;
-
        return 0;
 fail:
        nvhost_channelrelease(inode, filp);
        return -ENOMEM;
 }
 
-static int set_submit(struct nvhost_channel_userctx *ctx)
-{
-       struct platform_device *ndev = ctx->ch->dev;
-       struct nvhost_master *host = nvhost_get_host(ndev);
-
-       /* submit should have at least 1 cmdbuf */
-       if (!ctx->hdr.num_cmdbufs ||
-                       !nvhost_syncpt_is_valid(&host->syncpt,
-                               ctx->hdr.syncpt_id))
-               return -EIO;
-
-       if (!ctx->memmgr) {
-               dev_err(&ndev->dev, "no nvmap context set\n");
-               return -EFAULT;
-       }
-
-       if (ctx->job) {
-               dev_warn(&ndev->dev, "performing channel submit when a job already exists\n");
-               nvhost_job_put(ctx->job);
-       }
-       ctx->job = nvhost_job_alloc(ctx->ch,
-                       ctx->hwctx,
-                       ctx->hdr.num_cmdbufs,
-                       ctx->hdr.num_relocs,
-                       ctx->hdr.num_waitchks,
-                       1,
-                       ctx->memmgr);
-       if (!ctx->job)
-               return -ENOMEM;
-       ctx->job->timeout = ctx->timeout;
-       ctx->job->sp->id = ctx->hdr.syncpt_id;
-       ctx->job->sp->incrs = ctx->hdr.syncpt_incrs;
-       ctx->job->hwctx_syncpt_idx = 0;
-       ctx->job->num_syncpts = 1;
-       ctx->job->priority = ctx->priority;
-       ctx->job->clientid = ctx->clientid;
-       ctx->job->timeout_debug_dump = ctx->timeout_debug_dump;
-
-       if (ctx->hdr.submit_version >= NVHOST_SUBMIT_VERSION_V2)
-               ctx->num_relocshifts = ctx->hdr.num_relocs;
-
-       return 0;
-}
-
-static void reset_submit(struct nvhost_channel_userctx *ctx)
-{
-       ctx->hdr.num_cmdbufs = 0;
-       ctx->hdr.num_relocs = 0;
-       ctx->num_relocshifts = 0;
-       ctx->hdr.num_waitchks = 0;
-
-       if (ctx->job) {
-               nvhost_job_put(ctx->job);
-               ctx->job = NULL;
-       }
-}
-
-static ssize_t nvhost_channelwrite(struct file *filp, const char __user *buf,
-                               size_t count, loff_t *offp)
-{
-       struct nvhost_channel_userctx *priv = filp->private_data;
-       size_t remaining = count;
-       int err = 0;
-       struct nvhost_job *job = priv->job;
-       struct nvhost_submit_hdr_ext *hdr = &priv->hdr;
-       const char *chname = priv->ch->dev->name;
-
-       if (!job)
-               return -EIO;
-
-       while (remaining) {
-               size_t consumed;
-               if (!hdr->num_relocs &&
-                   !priv->num_relocshifts &&
-                   !hdr->num_cmdbufs &&
-                   !hdr->num_waitchks) {
-                       consumed = sizeof(struct nvhost_submit_hdr);
-                       if (remaining < consumed)
-                               break;
-                       if (copy_from_user(hdr, buf, consumed)) {
-                               err = -EFAULT;
-                               break;
-                       }
-                       hdr->submit_version = NVHOST_SUBMIT_VERSION_V0;
-                       err = set_submit(priv);
-                       if (err)
-                               break;
-                       trace_nvhost_channel_write_submit(chname,
-                         count, hdr->num_cmdbufs, hdr->num_relocs,
-                         hdr->syncpt_id, hdr->syncpt_incrs);
-               } else if (hdr->num_cmdbufs) {
-                       struct nvhost_cmdbuf cmdbuf;
-                       consumed = sizeof(cmdbuf);
-                       if (remaining < consumed)
-                               break;
-                       if (copy_from_user(&cmdbuf, buf, consumed)) {
-                               err = -EFAULT;
-                               break;
-                       }
-                       trace_nvhost_channel_write_cmdbuf(chname,
-                               cmdbuf.mem, cmdbuf.words, cmdbuf.offset);
-                       nvhost_job_add_gather(job,
-                               cmdbuf.mem, cmdbuf.words, cmdbuf.offset);
-                       hdr->num_cmdbufs--;
-               } else if (hdr->num_relocs) {
-                       int numrelocs = remaining / sizeof(struct nvhost_reloc);
-                       if (!numrelocs)
-                               break;
-                       numrelocs = min_t(int, numrelocs, priv->hdr.num_relocs);
-                       consumed = numrelocs * sizeof(struct nvhost_reloc);
-                       if (copy_from_user(&job->relocarray[job->num_relocs],
-                                       buf, consumed)) {
-                               err = -EFAULT;
-                               break;
-                       }
-                       while (numrelocs) {
-                               struct nvhost_reloc *reloc =
-                                       &job->relocarray[job->num_relocs];
-                               trace_nvhost_channel_write_reloc(chname,
-                                       reloc->cmdbuf_mem,
-                                       reloc->cmdbuf_offset,
-                                       reloc->target,
-                                       reloc->target_offset);
-                               job->num_relocs++;
-                               hdr->num_relocs--;
-                               numrelocs--;
-                       }
-               } else if (hdr->num_waitchks) {
-                       int numwaitchks =
-                               (remaining / sizeof(struct nvhost_waitchk));
-                       if (!numwaitchks)
-                               break;
-                       numwaitchks = min_t(int,
-                               numwaitchks, hdr->num_waitchks);
-                       consumed = numwaitchks * sizeof(struct nvhost_waitchk);
-                       if (copy_from_user(&job->waitchk[job->num_waitchk],
-                                       buf, consumed)) {
-                               err = -EFAULT;
-                               break;
-                       }
-                       trace_nvhost_channel_write_waitchks(
-                         chname, numwaitchks);
-                       job->num_waitchk += numwaitchks;
-                       hdr->num_waitchks -= numwaitchks;
-               } else if (priv->num_relocshifts) {
-                       int next_shift =
-                               job->num_relocs - priv->num_relocshifts;
-                       int num =
-                               (remaining / sizeof(struct nvhost_reloc_shift));
-                       if (!num)
-                               break;
-                       num = min_t(int, num, priv->num_relocshifts);
-                       consumed = num * sizeof(struct nvhost_reloc_shift);
-                       if (copy_from_user(&job->relocshiftarray[next_shift],
-                                       buf, consumed)) {
-                               err = -EFAULT;
-                               break;
-                       }
-                       priv->num_relocshifts -= num;
-               } else {
-                       err = -EFAULT;
-                       break;
-               }
-               remaining -= consumed;
-               buf += consumed;
-       }
-
-       if (err < 0) {
-               dev_err(&priv->ch->dev->dev, "channel write error\n");
-               reset_submit(priv);
-               return err;
-       }
-
-       return count - remaining;
-}
-
-static int nvhost_ioctl_channel_flush(
-       struct nvhost_channel_userctx *ctx,
-       struct nvhost_get_param_args *args,
-       int null_kickoff)
-{
-       struct platform_device *ndev = to_platform_device(&ctx->ch->dev->dev);
-       int err;
-
-       trace_nvhost_ioctl_channel_flush(ctx->ch->dev->name);
-
-       if (!ctx->job ||
-           ctx->hdr.num_relocs ||
-           ctx->hdr.num_cmdbufs ||
-           ctx->hdr.num_waitchks) {
-               reset_submit(ctx);
-               dev_err(&ndev->dev, "channel submit out of sync\n");
-               return -EFAULT;
-       }
-
-       err = nvhost_job_pin(ctx->job, &nvhost_get_host(ndev)->syncpt);
-       if (err) {
-               dev_warn(&ndev->dev, "nvhost_job_pin failed: %d\n", err);
-               goto fail;
-       }
-
-       if (nvhost_debug_null_kickoff_pid == current->tgid)
-               null_kickoff = 1;
-       ctx->job->null_kickoff = null_kickoff;
-
-       if ((nvhost_debug_force_timeout_pid == current->tgid) &&
-           (nvhost_debug_force_timeout_channel == ctx->ch->chid)) {
-               ctx->timeout = nvhost_debug_force_timeout_val;
-       }
-
-       /* context switch if needed, and submit user's gathers to the channel */
-       err = nvhost_channel_submit(ctx->job);
-       args->value = ctx->job->sp->fence;
-
-fail:
-       if (err)
-               nvhost_job_unpin(ctx->job);
-
-       nvhost_job_put(ctx->job);
-       ctx->job = NULL;
-
-       return err;
-}
-
 static int nvhost_ioctl_channel_alloc_obj_ctx(
        struct nvhost_channel_userctx *ctx,
        struct nvhost_alloc_obj_ctx_args *args)
 {
        int ret;
 
-       BUG_ON(!channel_op().alloc_obj);
+       BUG_ON(!channel_op(ctx->ch).alloc_obj);
        nvhost_module_busy(ctx->ch->dev);
-       ret = channel_op().alloc_obj(ctx->hwctx, args);
+       ret = channel_op(ctx->ch).alloc_obj(ctx->hwctx, args);
        nvhost_module_idle(ctx->ch->dev);
        return ret;
 }
 
-static int nvhost_ioctl_channel_alloc_obj_ctx_old(
-       struct nvhost_channel_userctx *ctx,
-       struct nvhost_alloc_obj_ctx_old_args *args)
-{
-       struct nvhost_alloc_obj_ctx_args new_args;
-       int err;
-
-       new_args.class_num = args->class_num;
-       err = nvhost_ioctl_channel_alloc_obj_ctx(ctx, &new_args);
-       if (!err)
-               args->obj_id = new_args.obj_id;
-       return err;
-}
-
 static int nvhost_ioctl_channel_free_obj_ctx(
        struct nvhost_channel_userctx *ctx,
        struct nvhost_free_obj_ctx_args *args)
 {
        int ret;
 
-       BUG_ON(!channel_op().free_obj);
+       BUG_ON(!channel_op(ctx->ch).free_obj);
        nvhost_module_busy(ctx->ch->dev);
-       ret = channel_op().free_obj(ctx->hwctx, args);
+       ret = channel_op(ctx->ch).free_obj(ctx->hwctx, args);
        nvhost_module_idle(ctx->ch->dev);
        return ret;
 }
 
-static int nvhost_ioctl_channel_free_obj_ctx_old(
-       struct nvhost_channel_userctx *ctx,
-       struct nvhost_free_obj_ctx_old_args *args)
-{
-       struct nvhost_free_obj_ctx_args new_args;
-       new_args.obj_id = args->obj_id;
-       return nvhost_ioctl_channel_free_obj_ctx(ctx, &new_args);
-}
-
 static int nvhost_ioctl_channel_alloc_gpfifo(
        struct nvhost_channel_userctx *ctx,
        struct nvhost_alloc_gpfifo_args *args)
 {
        int ret;
 
-       BUG_ON(!channel_op().alloc_gpfifo);
+       BUG_ON(!channel_op(ctx->ch).alloc_gpfifo);
        nvhost_module_busy(ctx->ch->dev);
-       ret = channel_op().alloc_gpfifo(ctx->hwctx, args);
+       ret = channel_op(ctx->ch).alloc_gpfifo(ctx->hwctx, args);
        nvhost_module_idle(ctx->ch->dev);
        return ret;
 }
 
+static int nvhost_ioctl_channel_set_error_notifier(
+       struct nvhost_channel_userctx *ctx,
+       struct nvhost_set_error_notifier *args)
+{
+       int ret;
+       BUG_ON(!channel_op(ctx->ch).set_error_notifier);
+       ret = channel_op(ctx->ch).set_error_notifier(ctx->hwctx, args);
+       return ret;
+}
+
 static int nvhost_ioctl_channel_submit_gpfifo(
        struct nvhost_channel_userctx *ctx,
        struct nvhost_submit_gpfifo_args *args)
@@ -510,10 +298,13 @@ static int nvhost_ioctl_channel_submit_gpfifo(
        u32 size;
        int ret = 0;
 
+       if (!ctx->hwctx || ctx->hwctx->has_timedout)
+               return -ETIMEDOUT;
+
        size = args->num_entries * sizeof(struct nvhost_gpfifo);
 
        gpfifo = kzalloc(size, GFP_KERNEL);
-       if (IS_ERR_OR_NULL(gpfifo))
+       if (!gpfifo)
                return -ENOMEM;
 
        if (copy_from_user(gpfifo,
@@ -522,10 +313,10 @@ static int nvhost_ioctl_channel_submit_gpfifo(
                goto clean_up;
        }
 
-       BUG_ON(!channel_op().submit_gpfifo);
+       BUG_ON(!channel_op(ctx->ch).submit_gpfifo);
 
        nvhost_module_busy(ctx->ch->dev);
-       ret = channel_op().submit_gpfifo(ctx->hwctx, gpfifo,
+       ret = channel_op(ctx->ch).submit_gpfifo(ctx->hwctx, gpfifo,
                        args->num_entries, &args->fence, args->flags);
        nvhost_module_idle(ctx->ch->dev);
 clean_up:
@@ -533,60 +324,45 @@ clean_up:
        return ret;
 }
 
-static int nvhost_ioctl_channel_submit_gpfifo_old(
-       struct nvhost_channel_userctx *ctx,
-       struct nvhost_submit_gpfifo_old_args *args)
-{
-       int ret;
-       struct nvhost_submit_gpfifo_args new_args;
-
-       new_args.gpfifo = (u64)(uintptr_t)args->gpfifo;
-       new_args.num_entries = args->num_entries;
-       new_args.fence = args->fence;
-       new_args.flags = args->flags;
-       ret = nvhost_ioctl_channel_submit_gpfifo(ctx, &new_args);
-       if (!ret)
-               args->fence = new_args.fence;
-       return ret;
-}
-
 static int nvhost_ioctl_channel_wait(
        struct nvhost_channel_userctx *ctx,
        struct nvhost_wait_args *args)
 {
        int ret;
 
-       BUG_ON(!channel_op().wait);
+       BUG_ON(!channel_op(ctx->ch).wait);
        nvhost_module_busy(ctx->ch->dev);
-       ret = channel_op().wait(ctx->hwctx, args);
+       ret = channel_op(ctx->ch).wait(ctx->hwctx, args);
        nvhost_module_idle(ctx->ch->dev);
        return ret;
 }
 
+static int nvhost_ioctl_channel_set_priority(
+       struct nvhost_channel_userctx *ctx,
+       struct nvhost_set_priority_args *args)
+{
+       int ret = 0;
+       if (channel_op(ctx->ch).set_priority) {
+               nvhost_module_busy(ctx->ch->dev);
+               ret = channel_op(ctx->ch).set_priority(ctx->hwctx, args);
+               nvhost_module_idle(ctx->ch->dev);
+       }
+       return ret;
+}
+
 static int nvhost_ioctl_channel_zcull_bind(
        struct nvhost_channel_userctx *ctx,
        struct nvhost_zcull_bind_args *args)
 {
        int ret;
 
-       BUG_ON(!channel_zcull_op().bind);
+       BUG_ON(!channel_zcull_op(ctx->ch).bind);
        nvhost_module_busy(ctx->ch->dev);
-       ret = channel_zcull_op().bind(ctx->hwctx, args);
+       ret = channel_zcull_op(ctx->ch).bind(ctx->hwctx, args);
        nvhost_module_idle(ctx->ch->dev);
        return ret;
 }
 
-static int nvhost_ioctl_channel_zcull_bind_old(
-       struct nvhost_channel_userctx *ctx,
-       struct nvhost_zcull_bind_old_args *args)
-{
-       struct nvhost_zcull_bind_args new_args;
-
-       new_args.gpu_va = args->gpu_va;
-       new_args.mode = args->mode;
-       return nvhost_ioctl_channel_zcull_bind(ctx, &new_args);
-}
-
 static int nvhost_ioctl_channel_submit(struct nvhost_channel_userctx *ctx,
                struct nvhost_submit_args *args)
 {
@@ -597,6 +373,8 @@ static int nvhost_ioctl_channel_submit(struct nvhost_channel_userctx *ctx,
        int num_syncpt_incrs = args->num_syncpt_incrs;
        struct nvhost_cmdbuf __user *cmdbufs =
                (struct nvhost_cmdbuf *)(uintptr_t)args->cmdbufs;
+       struct nvhost_cmdbuf __user *cmdbuf_exts =
+               (struct nvhost_cmdbuf *)(uintptr_t)args->cmdbuf_exts;
        struct nvhost_reloc __user *relocs =
                (struct nvhost_reloc *)(uintptr_t)args->relocs;
        struct nvhost_reloc_shift __user *reloc_shifts =
@@ -607,9 +385,10 @@ static int nvhost_ioctl_channel_submit(struct nvhost_channel_userctx *ctx,
                (struct nvhost_syncpt_incr *)(uintptr_t)args->syncpt_incrs;
        u32 __user *waitbases = (u32 *)(uintptr_t)args->waitbases;
        u32 __user *fences = (u32 *)(uintptr_t)args->fences;
+       u32 __user *class_ids = (u32 *)(uintptr_t)args->class_ids;
 
        struct nvhost_master *host = nvhost_get_host(ctx->ch->dev);
-       u32 *local_waitbases = NULL;
+       u32 *local_waitbases = NULL, *local_class_ids = NULL;
        int err, i, hwctx_syncpt_idx = -1;
 
        if (num_syncpt_incrs > host->info.nb_pts)
@@ -631,17 +410,46 @@ static int nvhost_ioctl_channel_submit(struct nvhost_channel_userctx *ctx,
        job->priority = ctx->priority;
        job->clientid = ctx->clientid;
 
-       while (num_cmdbufs) {
+       /* mass copy class_ids */
+       if (args->class_ids) {
+               local_class_ids = kzalloc(sizeof(u32) * num_cmdbufs,
+                       GFP_KERNEL);
+               if (!local_class_ids) {
+                       err = -ENOMEM;
+                       goto fail;
+               }
+               err = copy_from_user(local_class_ids, class_ids,
+                       sizeof(u32) * num_cmdbufs);
+               if (err) {
+                       err = -EINVAL;
+                       goto fail;
+               }
+       }
+
+       for (i = 0; i < num_cmdbufs; ++i) {
                struct nvhost_cmdbuf cmdbuf;
-               err = copy_from_user(&cmdbuf, cmdbufs, sizeof(cmdbuf));
+               struct nvhost_cmdbuf_ext cmdbuf_ext;
+               u32 class_id = class_ids ? local_class_ids[i] : 0;
+
+               err = copy_from_user(&cmdbuf, cmdbufs + i, sizeof(cmdbuf));
                if (err)
                        goto fail;
-               nvhost_job_add_gather(job,
-                               cmdbuf.mem, cmdbuf.words, cmdbuf.offset);
-               num_cmdbufs--;
-               cmdbufs++;
+
+               cmdbuf_ext.pre_fence = -1;
+               if (cmdbuf_exts)
+                       err = copy_from_user(&cmdbuf_ext,
+                                       cmdbuf_exts + i, sizeof(cmdbuf_ext));
+               if (err)
+                       cmdbuf_ext.pre_fence = -1;
+
+               nvhost_job_add_gather(job, cmdbuf.mem, cmdbuf.words,
+                                     cmdbuf.offset, class_id,
+                                     cmdbuf_ext.pre_fence);
        }
 
+       kfree(local_class_ids);
+       local_class_ids = NULL;
+
        err = copy_from_user(job->relocarray,
                        relocs, sizeof(*relocs) * num_relocs);
        if (err)
@@ -661,6 +469,11 @@ static int nvhost_ioctl_channel_submit(struct nvhost_channel_userctx *ctx,
        if (args->waitbases) {
                local_waitbases = kzalloc(sizeof(u32) * num_syncpt_incrs,
                        GFP_KERNEL);
+               if (!local_waitbases) {
+                       err = -ENOMEM;
+                       goto fail;
+               }
+
                err = copy_from_user(local_waitbases, waitbases,
                        sizeof(u32) * num_syncpt_incrs);
                if (err) {
@@ -669,8 +482,8 @@ static int nvhost_ioctl_channel_submit(struct nvhost_channel_userctx *ctx,
                }
        }
 
-       /* set valid id for hwctx_syncpt_idx if no hwctx is present */
-       if (!ctx->hwctx)
+       /* set valid id for hwctx_syncpt_idx if hwctx does not provide one */
+       if (!ctx->hwctx || ctx->hwctx->h->syncpt == NVSYNCPT_INVALID)
                hwctx_syncpt_idx = 0;
 
        /*
@@ -730,7 +543,9 @@ static int nvhost_ioctl_channel_submit(struct nvhost_channel_userctx *ctx,
                job->sp[job->hwctx_syncpt_idx].id,
                job->sp[job->hwctx_syncpt_idx].incrs);
 
+       nvhost_module_busy(ctx->ch->dev);
        err = nvhost_job_pin(job, &nvhost_get_host(ctx->ch->dev)->syncpt);
+       nvhost_module_idle(ctx->ch->dev);
        if (err)
                goto fail;
 
@@ -757,7 +572,20 @@ static int nvhost_ioctl_channel_submit(struct nvhost_channel_userctx *ctx,
        /* Deliver the fence using the old mechanism _only_ if a single
         * syncpoint is used. */
 
-       if (num_syncpt_incrs == 1)
+       if (args->flags & BIT(NVHOST_SUBMIT_FLAG_SYNC_FENCE_FD)) {
+               struct nvhost_ctrl_sync_fence_info pts[num_syncpt_incrs];
+
+               for (i = 0; i < num_syncpt_incrs; i++) {
+                       pts[i].id = job->sp[i].id;
+                       pts[i].thresh = job->sp[i].fence;
+               }
+
+               err = nvhost_sync_create_fence(
+                               &nvhost_get_host(ctx->ch->dev)->syncpt,
+                               pts, num_syncpt_incrs, "fence", &args->fence);
+               if (err)
+                       goto fail;
+       } else if (num_syncpt_incrs == 1)
                args->fence = job->sp[job->hwctx_syncpt_idx].fence;
        else
                args->fence = 0;
@@ -770,6 +598,7 @@ fail_submit:
        nvhost_job_unpin(job);
 fail:
        nvhost_job_put(job);
+       kfree(local_class_ids);
        kfree(local_waitbases);
        return err;
 }
@@ -865,6 +694,9 @@ static int nvhost_ioctl_channel_set_ctxswitch(
                        save_incr.syncpt_incrs, restore_incr.syncpt_incrs);
 
        nhwctx->memmgr = nvhost_memmgr_get_mgr(ctx->memmgr);
+       if (!nhwctx->memmgr)
+               goto fail_set_restore;
+
        err = user_hwctx_set_restore(hwctx, cmdbuf_restore.mem,
                        cmdbuf_restore.offset, cmdbuf_restore.words);
        if (err)
@@ -893,14 +725,14 @@ fail:
        return err;
 }
 
-#if defined(CONFIG_TEGRA_GPU_CYCLE_STATS)
+#if defined(CONFIG_GK20A_CYCLE_STATS)
 static int nvhost_ioctl_channel_cycle_stats(
        struct nvhost_channel_userctx *ctx,
        struct nvhost_cycle_stats_args *args)
 {
        int ret;
-       BUG_ON(!channel_op().cycle_stats);
-       ret = channel_op().cycle_stats(ctx->hwctx, args);
+       BUG_ON(!channel_op(ctx->ch).cycle_stats);
+       ret = channel_op(ctx->ch).cycle_stats(ctx->hwctx, args);
        return ret;
 }
 #endif
@@ -1014,7 +846,7 @@ static u32 create_mask(u32 *words, int num)
 {
        int i;
        u32 word = 0;
-       for (i = 0; i < num && words[i] && words[i] < BITS_PER_LONG; i++)
+       for (i = 0; i < num && words[i] && words[i] < 32; i++)
                word |= BIT(words[i]);
 
        return word;
@@ -1040,45 +872,6 @@ static long nvhost_channelctl(struct file *filp,
        }
 
        switch (cmd) {
-       case NVHOST_IOCTL_CHANNEL_FLUSH:
-               err = nvhost_ioctl_channel_flush(priv, (void *)buf, 0);
-               break;
-       case NVHOST_IOCTL_CHANNEL_NULL_KICKOFF:
-               err = nvhost_ioctl_channel_flush(priv, (void *)buf, 1);
-               break;
-       case NVHOST_IOCTL_CHANNEL_SUBMIT_EXT:
-       {
-               struct nvhost_submit_hdr_ext *hdr;
-
-               if (priv->hdr.num_relocs ||
-                   priv->num_relocshifts ||
-                   priv->hdr.num_cmdbufs ||
-                   priv->hdr.num_waitchks) {
-                       reset_submit(priv);
-                       dev_err(&priv->ch->dev->dev,
-                               "channel submit out of sync\n");
-                       err = -EIO;
-                       break;
-               }
-
-               hdr = (struct nvhost_submit_hdr_ext *)buf;
-               if (hdr->submit_version > NVHOST_SUBMIT_VERSION_MAX_SUPPORTED) {
-                       dev_err(&priv->ch->dev->dev,
-                               "submit version %d > max supported %d\n",
-                               hdr->submit_version,
-                               NVHOST_SUBMIT_VERSION_MAX_SUPPORTED);
-                       err = -EINVAL;
-                       break;
-               }
-               memcpy(&priv->hdr, hdr, sizeof(struct nvhost_submit_hdr_ext));
-               err = set_submit(priv);
-               trace_nvhost_ioctl_channel_submit(priv->ch->dev->name,
-                       priv->hdr.submit_version,
-                       priv->hdr.num_cmdbufs, priv->hdr.num_relocs,
-                       priv->hdr.num_waitchks,
-                       priv->hdr.syncpt_id, priv->hdr.syncpt_incrs);
-               break;
-       }
        case NVHOST_IOCTL_CHANNEL_GET_SYNCPOINTS:
        {
                struct nvhost_device_data *pdata = \
@@ -1163,35 +956,26 @@ static long nvhost_channelctl(struct file *filp,
        case NVHOST_IOCTL_CHANNEL_ALLOC_OBJ_CTX:
                err = nvhost_ioctl_channel_alloc_obj_ctx(priv, (void *)buf);
                break;
-       case NVHOST_IOCTL_CHANNEL_ALLOC_OBJ_CTX_OLD:
-               err = nvhost_ioctl_channel_alloc_obj_ctx_old(priv, (void *)buf);
-               break;
        case NVHOST_IOCTL_CHANNEL_FREE_OBJ_CTX:
                err = nvhost_ioctl_channel_free_obj_ctx(priv, (void *)buf);
                break;
-       case NVHOST_IOCTL_CHANNEL_FREE_OBJ_CTX_OLD:
-               err = nvhost_ioctl_channel_free_obj_ctx_old(priv, (void *)buf);
-               break;
        case NVHOST_IOCTL_CHANNEL_ALLOC_GPFIFO:
                err = nvhost_ioctl_channel_alloc_gpfifo(priv, (void *)buf);
                break;
        case NVHOST_IOCTL_CHANNEL_SUBMIT_GPFIFO:
                err = nvhost_ioctl_channel_submit_gpfifo(priv, (void *)buf);
                break;
-       case NVHOST_IOCTL_CHANNEL_SUBMIT_GPFIFO_OLD:
-               err = nvhost_ioctl_channel_submit_gpfifo_old(priv, (void *)buf);
-               break;
        case NVHOST_IOCTL_CHANNEL_WAIT:
                err = nvhost_ioctl_channel_wait(priv, (void *)buf);
                break;
        case NVHOST_IOCTL_CHANNEL_ZCULL_BIND:
                err = nvhost_ioctl_channel_zcull_bind(priv, (void *)buf);
                break;
-       case NVHOST_IOCTL_CHANNEL_ZCULL_BIND_OLD:
-               err = nvhost_ioctl_channel_zcull_bind_old(priv, (void *)buf);
+       case NVHOST_IOCTL_CHANNEL_SET_ERROR_NOTIFIER:
+               err = nvhost_ioctl_channel_set_error_notifier(priv,
+                       (void *)buf);
                break;
-
-#if defined(CONFIG_TEGRA_GPU_CYCLE_STATS)
+#if defined(CONFIG_GK20A_CYCLE_STATS)
        case NVHOST_IOCTL_CHANNEL_CYCLE_STATS:
                err = nvhost_ioctl_channel_cycle_stats(priv, (void *)buf);
                break;
@@ -1217,36 +1001,92 @@ static long nvhost_channelctl(struct file *filp,
                break;
        }
        case NVHOST_IOCTL_CHANNEL_SET_TIMEOUT:
-               priv->timeout =
+       {
+               u32 timeout =
                        (u32)((struct nvhost_set_timeout_args *)buf)->timeout;
+
+               priv->timeout = timeout;
                dev_dbg(&priv->ch->dev->dev,
                        "%s: setting buffer timeout (%d ms) for userctx 0x%p\n",
                        __func__, priv->timeout, priv);
+               if (priv->hwctx)
+                       priv->hwctx->timeout_ms_max = timeout;
                break;
+       }
        case NVHOST_IOCTL_CHANNEL_GET_TIMEDOUT:
                ((struct nvhost_get_param_args *)buf)->value =
                                priv->hwctx->has_timedout;
                break;
        case NVHOST_IOCTL_CHANNEL_SET_PRIORITY:
+               nvhost_ioctl_channel_set_priority(priv, (void *)buf);
                priv->priority =
                        (u32)((struct nvhost_set_priority_args *)buf)->priority;
                break;
+       case NVHOST32_IOCTL_CHANNEL_MODULE_REGRDWR:
+       {
+               struct nvhost32_ctrl_module_regrdwr_args *args32 =
+                       (struct nvhost32_ctrl_module_regrdwr_args *)buf;
+               struct nvhost_ctrl_module_regrdwr_args args;
+               args.id = args32->id;
+               args.num_offsets = args32->num_offsets;
+               args.block_size = args32->block_size;
+               args.offsets = args32->offsets;
+               args.values = args32->values;
+               args.write = args32->write;
+               err = nvhost_ioctl_channel_module_regrdwr(priv, &args);
+               break;
+       }
        case NVHOST_IOCTL_CHANNEL_MODULE_REGRDWR:
                err = nvhost_ioctl_channel_module_regrdwr(priv, (void *)buf);
                break;
+       case NVHOST32_IOCTL_CHANNEL_SUBMIT:
+       {
+               struct nvhost32_submit_args *args32 = (void *)buf;
+               struct nvhost_submit_args args;
+
+               memset(&args, 0, sizeof(args));
+               args.submit_version = args32->submit_version;
+               args.num_syncpt_incrs = args32->num_syncpt_incrs;
+               args.num_cmdbufs = args32->num_cmdbufs;
+               args.num_relocs = args32->num_relocs;
+               args.num_waitchks = args32->num_waitchks;
+               args.timeout = args32->timeout;
+               args.syncpt_incrs = args32->syncpt_incrs;
+               args.fence = args32->fence;
+
+               args.cmdbufs = args32->cmdbufs;
+               args.relocs = args32->relocs;
+               args.reloc_shifts = args32->reloc_shifts;
+               args.waitchks = args32->waitchks;
+               args.waitbases = args32->waitbases;
+               args.class_ids = args32->class_ids;
+               args.fences = args32->fences;
+
+               err = nvhost_ioctl_channel_submit(priv, &args);
+               args32->fence = args.fence;
+               break;
+       }
        case NVHOST_IOCTL_CHANNEL_SUBMIT:
                err = nvhost_ioctl_channel_submit(priv, (void *)buf);
                break;
        case NVHOST_IOCTL_CHANNEL_SET_TIMEOUT_EX:
-               priv->timeout = (u32)
-                       ((struct nvhost_set_timeout_ex_args *)buf)->timeout;
-               priv->timeout_debug_dump = !((u32)
+       {
+               u32 timeout =
+                       (u32)((struct nvhost_set_timeout_args *)buf)->timeout;
+               bool timeout_debug_dump = !((u32)
                        ((struct nvhost_set_timeout_ex_args *)buf)->flags &
                        (1 << NVHOST_TIMEOUT_FLAG_DISABLE_DUMP));
+               priv->timeout = timeout;
+               priv->timeout_debug_dump = timeout_debug_dump;
                dev_dbg(&priv->ch->dev->dev,
                        "%s: setting buffer timeout (%d ms) for userctx 0x%p\n",
                        __func__, priv->timeout, priv);
+               if (priv->hwctx) {
+                       priv->hwctx->timeout_ms_max = timeout;
+                       priv->hwctx->timeout_debug_dump = timeout_debug_dump;
+               }
                break;
+       }
        case NVHOST_IOCTL_CHANNEL_SET_CTXSWITCH:
                err = nvhost_ioctl_channel_set_ctxswitch(priv, (void *)buf);
                break;
@@ -1266,7 +1106,9 @@ static const struct file_operations nvhost_channelops = {
        .owner = THIS_MODULE,
        .release = nvhost_channelrelease,
        .open = nvhost_channelopen,
-       .write = nvhost_channelwrite,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = nvhost_channelctl,
+#endif
        .unlocked_ioctl = nvhost_channelctl
 };
 
@@ -1292,6 +1134,9 @@ static const struct file_operations nvhost_asops = {
        .owner = THIS_MODULE,
        .release = nvhost_as_dev_release,
        .open = nvhost_as_dev_open,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = nvhost_as_dev_ctl,
+#endif
        .unlocked_ioctl = nvhost_as_dev_ctl,
 };
 
@@ -1393,9 +1238,10 @@ int nvhost_client_user_init(struct platform_device *dev)
        struct nvhost_channel *ch = pdata->channel;
 
        BUG_ON(!ch);
-       // reserve 3 minor #s for <dev> and as-<dev> and ctrl-<dev>
+       /* reserve 5 minor #s for <dev> and as-<dev>, ctrl-<dev>,
+        * dbg-<dev> and prof-<dev> */
 
-       err = alloc_chrdev_region(&devno, 0, 3, IFACE_NAME);
+       err = alloc_chrdev_region(&devno, 0, 5, IFACE_NAME);
        if (err < 0) {
                dev_err(&dev->dev, "failed to allocate devno\n");
                goto fail;
@@ -1405,12 +1251,15 @@ int nvhost_client_user_init(struct platform_device *dev)
                                "", devno, &nvhost_channelops);
        if (ch->node == NULL)
                goto fail;
-       ++devno;
-       ch->as_node = nvhost_client_device_create(dev, &ch->as_cdev,
-                               "as-", devno, &nvhost_asops);
-       if (ch->as_node == NULL)
-               goto fail;
+       if (pdata->as_ops) {
+               ++devno;
+               ch->as_node = nvhost_client_device_create(dev, &ch->as_cdev,
+                                       "as-", devno, &nvhost_asops);
+               if (ch->as_node == NULL)
+                       goto fail;
+       }
 
+       /* module control (npn-channel based, global) interface */
        if (pdata->ctrl_ops) {
                ++devno;
                pdata->ctrl_node = nvhost_client_device_create(dev,
@@ -1420,11 +1269,70 @@ int nvhost_client_user_init(struct platform_device *dev)
                        goto fail;
        }
 
+       /* module debugger interface (per channel and global) */
+       if (pdata->dbg_ops) {
+               ++devno;
+               pdata->dbg_node = nvhost_client_device_create(dev,
+                                       &pdata->dbg_cdev, "dbg-",
+                                       devno, pdata->dbg_ops);
+               if (pdata->dbg_node == NULL)
+                       goto fail;
+       }
+
+       /* module profiler interface (per channel and global) */
+       if (pdata->prof_ops) {
+               ++devno;
+               pdata->prof_node = nvhost_client_device_create(dev,
+                                       &pdata->prof_cdev, "prof-",
+                                       devno, pdata->prof_ops);
+               if (pdata->prof_node == NULL)
+                       goto fail;
+       }
+
+
+
        return 0;
 fail:
        return err;
 }
 
+void nvhost_client_user_deinit(struct platform_device *dev)
+{
+       struct nvhost_master *nvhost_master = nvhost_get_host(dev);
+       struct nvhost_device_data *pdata = platform_get_drvdata(dev);
+       struct nvhost_channel *ch = pdata->channel;
+
+       BUG_ON(!ch);
+
+       if (ch->node) {
+               device_destroy(nvhost_master->nvhost_class, ch->cdev.dev);
+               cdev_del(&ch->cdev);
+       }
+
+       if (ch->as_node) {
+               device_destroy(nvhost_master->nvhost_class, ch->as_cdev.dev);
+               cdev_del(&ch->as_cdev);
+       }
+
+       if (pdata->ctrl_node) {
+               device_destroy(nvhost_master->nvhost_class,
+                              pdata->ctrl_cdev.dev);
+               cdev_del(&pdata->ctrl_cdev);
+       }
+
+       if (pdata->dbg_node) {
+               device_destroy(nvhost_master->nvhost_class,
+                              pdata->dbg_cdev.dev);
+               cdev_del(&pdata->dbg_cdev);
+       }
+
+       if (pdata->prof_node) {
+               device_destroy(nvhost_master->nvhost_class,
+                              pdata->prof_cdev.dev);
+               cdev_del(&pdata->prof_cdev);
+       }
+}
+
 int nvhost_client_device_init(struct platform_device *dev)
 {
        int err;
@@ -1444,7 +1352,7 @@ int nvhost_client_device_init(struct platform_device *dev)
 
        err = nvhost_channel_init(ch, nvhost_master, pdata->index);
        if (err)
-               goto fail;
+               goto fail1;
 
        err = nvhost_client_user_init(dev);
        if (err)
@@ -1471,15 +1379,23 @@ int nvhost_client_device_init(struct platform_device *dev)
 
        dev_info(&dev->dev, "initialized\n");
 
-       if (pdata->slave) {
+       if (pdata->slave && !pdata->slave_initialized) {
+               struct nvhost_device_data *slave_pdata =
+                                       pdata->slave->dev.platform_data;
+               slave_pdata->master = dev;
                pdata->slave->dev.parent = dev->dev.parent;
                platform_device_register(pdata->slave);
+               pdata->slave_initialized = 1;
        }
 
        return 0;
 
 fail:
        /* Add clean-up */
+       dev_err(&dev->dev, "failed to init client device\n");
+       nvhost_client_user_deinit(dev);
+fail1:
+       nvhost_device_debug_deinit(dev);
        nvhost_free_channel(ch);
        return err;
 }
@@ -1487,7 +1403,6 @@ EXPORT_SYMBOL(nvhost_client_device_init);
 
 int nvhost_client_device_release(struct platform_device *dev)
 {
-       struct nvhost_master *nvhost_master = nvhost_get_host(dev);
        struct nvhost_channel *ch;
        struct nvhost_device_data *pdata = platform_get_drvdata(dev);
 
@@ -1500,8 +1415,10 @@ int nvhost_client_device_release(struct platform_device *dev)
        nvhost_device_list_remove(dev);
 
        /* Release chardev and device node for user space */
-       device_destroy(nvhost_master->nvhost_class, ch->cdev.dev);
-       cdev_del(&ch->cdev);
+       nvhost_client_user_deinit(dev);
+
+       /* Remove debugFS */
+       nvhost_device_debug_deinit(dev);
 
        /* Free nvhost channel */
        nvhost_free_channel(ch);
@@ -1510,28 +1427,6 @@ int nvhost_client_device_release(struct platform_device *dev)
 }
 EXPORT_SYMBOL(nvhost_client_device_release);
 
-int nvhost_client_device_suspend(struct device *dev)
-{
-       int ret = 0;
-       struct nvhost_device_data *pdata = dev_get_drvdata(dev);
-
-       ret = nvhost_channel_suspend(pdata->channel);
-       if (ret)
-               return ret;
-
-       dev_info(dev, "suspend status: %d\n", ret);
-
-       return ret;
-}
-EXPORT_SYMBOL(nvhost_client_device_suspend);
-
-int nvhost_client_device_resume(struct device *dev)
-{
-       dev_info(dev, "resuming\n");
-       return 0;
-}
-EXPORT_SYMBOL(nvhost_client_device_resume);
-
 int nvhost_client_device_get_resources(struct platform_device *dev)
 {
        int i;