video: tegra: host: Do not access cmdbuf_ext if NULL
[linux-3.10.git] / drivers / video / tegra / host / bus_client.c
index a673562..030e28c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * 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,
@@ -54,6 +54,7 @@
 #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)
 {
@@ -85,6 +86,23 @@ static __iomem void *get_aperture(struct platform_device *pdev)
        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)
 {
@@ -137,22 +155,6 @@ int nvhost_write_module_regs(struct platform_device *ndev,
        return 0;
 }
 
-bool nvhost_client_can_writel(struct platform_device *pdev)
-{
-       return !!get_aperture(pdev);
-}
-EXPORT_SYMBOL(nvhost_client_can_writel);
-
-void nvhost_client_writel(struct platform_device *pdev, u32 val, u32 reg)
-{
-       writel(val, get_aperture(pdev) + reg * 4);
-}
-
-u32 nvhost_client_readl(struct platform_device *pdev, u32 reg)
-{
-       return readl(get_aperture(pdev) + reg * 4);
-}
-
 struct nvhost_channel_userctx {
        struct nvhost_channel *ch;
        struct nvhost_hwctx *hwctx;
@@ -233,7 +235,6 @@ static int nvhost_channelopen(struct inode *inode, struct file *filp)
        priv->timeout_debug_dump = true;
        if (!tegra_platform_is_silicon())
                priv->timeout = 0;
-
        return 0;
 fail:
        nvhost_channelrelease(inode, filp);
@@ -336,6 +337,19 @@ static int nvhost_ioctl_channel_wait(
        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)
@@ -359,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 =
@@ -412,14 +428,23 @@ static int nvhost_ioctl_channel_submit(struct nvhost_channel_userctx *ctx,
 
        for (i = 0; i < num_cmdbufs; ++i) {
                struct nvhost_cmdbuf 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;
 
+               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.offset, class_id,
+                                     cmdbuf_ext.pre_fence);
        }
 
        kfree(local_class_ids);
@@ -457,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;
 
        /*
@@ -518,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;
 
@@ -545,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;
@@ -685,7 +725,7 @@ 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)
@@ -935,7 +975,7 @@ static long nvhost_channelctl(struct file *filp,
                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;
@@ -961,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;
@@ -1155,11 +1251,13 @@ 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) {
@@ -1198,6 +1296,43 @@ 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;
@@ -1217,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)
@@ -1244,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;
 }
@@ -1260,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);
 
@@ -1273,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);
@@ -1283,33 +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_module_suspend(dev);
-       if (ret)
-               return ret;
-
-       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)
-{
-       nvhost_module_resume(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;