diff options
Diffstat (limited to 'drivers/video/tegra/host/dev.c')
-rw-r--r-- | drivers/video/tegra/host/dev.c | 119 |
1 files changed, 90 insertions, 29 deletions
diff --git a/drivers/video/tegra/host/dev.c b/drivers/video/tegra/host/dev.c index 8b577cc9045f..fdbf5cc8efc1 100644 --- a/drivers/video/tegra/host/dev.c +++ b/drivers/video/tegra/host/dev.c @@ -46,10 +46,7 @@ static int nvhost_minor = NVHOST_CHANNEL_BASE; struct nvhost_channel_userctx { struct nvhost_channel *ch; struct nvhost_hwctx *hwctx; - u32 syncpt_id; - u32 syncpt_incrs; - u32 cmdbufs_pending; - u32 relocs_pending; + struct nvhost_submit_hdr_ext hdr; struct nvmap_handle_ref *gather_mem; struct nvhost_op_pair *gathers; int num_gathers; @@ -57,6 +54,8 @@ struct nvhost_channel_userctx { struct nvmap_pinarray_elem pinarray[NVHOST_MAX_HANDLES]; struct nvmap_handle *unpinarray[NVHOST_MAX_HANDLES]; struct nvmap_client *nvmap; + struct nvhost_waitchk waitchks[NVHOST_MAX_WAIT_CHECKS]; + u32 num_waitchks; }; struct nvhost_ctrl_userctx { @@ -137,10 +136,25 @@ static void add_gather(struct nvhost_channel_userctx *ctx, int idx, ctx->gathers[idx].op1 = nvhost_opcode_gather(0, words); } +static int set_submit(struct nvhost_channel_userctx *ctx) +{ + int err = 0; + + /* submit should have at least 1 cmdbuf */ + if (!ctx->hdr.num_cmdbufs) + return -EFAULT; + + /* leave room for ctx switch */ + ctx->num_gathers = 2; + ctx->pinarray_size = 0; + return err; +} + static void reset_submit(struct nvhost_channel_userctx *ctx) { - ctx->cmdbufs_pending = 0; - ctx->relocs_pending = 0; + ctx->hdr.num_cmdbufs = 0; + ctx->hdr.num_relocs = 0; + ctx->hdr.num_waitchks = 0; } static ssize_t nvhost_channelwrite(struct file *filp, const char __user *buf, @@ -152,22 +166,19 @@ static ssize_t nvhost_channelwrite(struct file *filp, const char __user *buf, while (remaining) { size_t consumed; - if (!priv->relocs_pending && !priv->cmdbufs_pending) { + if (!priv->hdr.num_relocs && !priv->hdr.num_cmdbufs && !priv->hdr.num_waitchks) { consumed = sizeof(struct nvhost_submit_hdr); if (remaining < consumed) break; - if (copy_from_user(&priv->syncpt_id, buf, consumed)) { + if (copy_from_user(&priv->hdr, buf, consumed)) { err = -EFAULT; break; } - if (!priv->cmdbufs_pending) { - err = -EFAULT; + BUG_ON(priv->hdr.submit_version != NVHOST_SUBMIT_VERSION_V0); + err = set_submit(priv); + if (err) break; - } - /* leave room for ctx switch */ - priv->num_gathers = 2; - priv->pinarray_size = 0; - } else if (priv->cmdbufs_pending) { + } else if (priv->hdr.num_cmdbufs) { struct nvhost_cmdbuf cmdbuf; consumed = sizeof(cmdbuf); if (remaining < consumed) @@ -178,12 +189,12 @@ static ssize_t nvhost_channelwrite(struct file *filp, const char __user *buf, } add_gather(priv, priv->num_gathers++, cmdbuf.mem, cmdbuf.words, cmdbuf.offset); - priv->cmdbufs_pending--; - } else if (priv->relocs_pending) { + priv->hdr.num_cmdbufs--; + } else if (priv->hdr.num_relocs) { int numrelocs = remaining / sizeof(struct nvhost_reloc); if (!numrelocs) break; - numrelocs = min_t(int, numrelocs, priv->relocs_pending); + numrelocs = min_t(int, numrelocs, priv->hdr.num_relocs); consumed = numrelocs * sizeof(struct nvhost_reloc); if (copy_from_user(&priv->pinarray[priv->pinarray_size], buf, consumed)) { @@ -191,7 +202,19 @@ static ssize_t nvhost_channelwrite(struct file *filp, const char __user *buf, break; } priv->pinarray_size += numrelocs; - priv->relocs_pending -= numrelocs; + priv->hdr.num_relocs -= numrelocs; + } else if (priv->hdr.num_waitchks) { + struct nvhost_waitchk *waitp; + consumed = sizeof(struct nvhost_waitchk); + if (remaining < consumed) + break; + waitp = &priv->waitchks[priv->num_waitchks]; + if (copy_from_user(waitp, buf, consumed)) { + err = -EFAULT; + break; + } + priv->num_waitchks++; + priv->hdr.num_waitchks--; } else { err = -EFAULT; break; @@ -219,9 +242,9 @@ static int nvhost_ioctl_channel_flush(struct nvhost_channel_userctx *ctx, u32 syncval; int num_unpin; int err; - int nulled_incrs = null_kickoff ? ctx->syncpt_incrs : 0; + int nulled_incrs = null_kickoff ? ctx->hdr.syncpt_incrs : 0; - if (ctx->relocs_pending || ctx->cmdbufs_pending) { + if (ctx->hdr.num_relocs || ctx->hdr.num_cmdbufs || ctx->hdr.num_waitchks) { reset_submit(ctx); dev_err(&ctx->ch->dev->pdev->dev, "channel submit out of sync\n"); return -EFAULT; @@ -256,6 +279,22 @@ static int nvhost_ioctl_channel_flush(struct nvhost_channel_userctx *ctx, return err; } + /* remove stale waits */ + if (ctx->num_waitchks) { + err = nvhost_syncpt_wait_check(ctx->nvmap, + &ctx->ch->dev->syncpt, ctx->hdr.waitchk_mask, + ctx->waitchks, ctx->num_waitchks); + if (err) { + dev_warn(&ctx->ch->dev->pdev->dev, + "nvhost_syncpt_wait_check failed: %d\n", err); + mutex_unlock(&ctx->ch->submitlock); + nvmap_unpin_handles(ctx->nvmap, ctx->unpinarray, num_unpin); + nvhost_module_idle(&ctx->ch->mod); + return err; + } + ctx->num_waitchks = 0; + } + /* context switch */ if (ctx->ch->cur_ctx != ctx->hwctx) { struct nvhost_hwctx *hw = ctx->hwctx; @@ -264,7 +303,7 @@ static int nvhost_ioctl_channel_flush(struct nvhost_channel_userctx *ctx, ctx->gathers[gather_idx].op1 = nvhost_opcode_gather(0, hw->restore_size); ctx->gathers[gather_idx].op2 = hw->restore_phys; - ctx->syncpt_incrs += hw->restore_incrs; + ctx->hdr.syncpt_incrs += hw->restore_incrs; } hw = ctx->ch->cur_ctx; if (hw) { @@ -272,7 +311,7 @@ static int nvhost_ioctl_channel_flush(struct nvhost_channel_userctx *ctx, ctx->gathers[gather_idx].op1 = nvhost_opcode_gather(0, hw->save_size); ctx->gathers[gather_idx].op2 = hw->save_phys; - ctx->syncpt_incrs += hw->save_incrs; + ctx->hdr.syncpt_incrs += hw->save_incrs; num_intrs = 1; ctxsw.syncpt_val = hw->save_incrs - 1; ctxsw.intr_data = hw; @@ -291,24 +330,24 @@ static int nvhost_ioctl_channel_flush(struct nvhost_channel_userctx *ctx, } /* get absolute sync value */ - if (BIT(ctx->syncpt_id) & NVSYNCPTS_CLIENT_MANAGED) + if (BIT(ctx->hdr.syncpt_id) & NVSYNCPTS_CLIENT_MANAGED) syncval = nvhost_syncpt_set_max(&ctx->ch->dev->syncpt, - ctx->syncpt_id, ctx->syncpt_incrs); + ctx->hdr.syncpt_id, ctx->hdr.syncpt_incrs); else syncval = nvhost_syncpt_incr_max(&ctx->ch->dev->syncpt, - ctx->syncpt_id, ctx->syncpt_incrs); + ctx->hdr.syncpt_id, ctx->hdr.syncpt_incrs); /* patch absolute syncpt value into interrupt triggers */ - ctxsw.syncpt_val += syncval - ctx->syncpt_incrs; + ctxsw.syncpt_val += syncval - ctx->hdr.syncpt_incrs; nvhost_channel_submit(ctx->ch, ctx->nvmap, &ctx->gathers[gather_idx], (null_kickoff ? 2 : ctx->num_gathers) - gather_idx, &ctxsw, num_intrs, ctx->unpinarray, num_unpin, - ctx->syncpt_id, syncval, + ctx->hdr.syncpt_id, syncval, nulled_incrs); /* schedule a submit complete interrupt */ - nvhost_intr_add_action(&ctx->ch->dev->intr, ctx->syncpt_id, syncval, + nvhost_intr_add_action(&ctx->ch->dev->intr, ctx->hdr.syncpt_id, syncval, NVHOST_INTR_ACTION_SUBMIT_COMPLETE, ctx->ch, NULL); mutex_unlock(&ctx->ch->submitlock); @@ -342,7 +381,29 @@ static long nvhost_channelctl(struct file *filp, 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 = (struct nvhost_submit_hdr_ext *)buf; + + if (priv->hdr.num_relocs || priv->hdr.num_cmdbufs || priv->hdr.num_waitchks) { + reset_submit(priv); + dev_err(&priv->ch->dev->pdev->dev, "channel submit out of sync\n"); + err = -EFAULT; + break; + } + if (hdr->submit_version > NVHOST_SUBMIT_VERSION_MAX_SUPPORTED) { + dev_err(&priv->ch->dev->pdev->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); + break; + } case NVHOST_IOCTL_CHANNEL_GET_SYNCPOINTS: + /* host syncpt ID is used by the RM (and never be given out) */ + BUG_ON(priv->ch->desc->syncpts & (1 << NVSYNCPT_GRAPHICS_HOST)); ((struct nvhost_get_param_args *)buf)->value = priv->ch->desc->syncpts; break; |