diff options
author | Terje Bergstrom <tbergstrom@nvidia.com> | 2012-03-16 13:55:24 +0200 |
---|---|---|
committer | Simone Willett <swillett@nvidia.com> | 2012-03-23 17:28:25 -0700 |
commit | e5b5fd672b03e36080c34720b976551f5526d8e9 (patch) | |
tree | bada3aade9eca380ccc8072464d97550ef65c78a | |
parent | 97139a5d89b2a80fb8464dda86fcb1c968be2b63 (diff) |
video: tegra: host: Split submit into subfunctions
Split host1x_channel_submit() into subfunctions.
Bug 926690
Change-Id: I8be55cbc9d25ee76c758a918de4a9bb27e2ea846
Signed-off-by: Terje Bergstrom <tbergstrom@nvidia.com>
Reviewed-on: http://git-master/r/90626
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Mayuresh Kulkarni <mkulkarni@nvidia.com>
Reviewed-by: Juha Tukkinen <jtukkinen@nvidia.com>
-rw-r--r-- | drivers/video/tegra/host/bus_client.c | 1 | ||||
-rw-r--r-- | drivers/video/tegra/host/host1x/host1x_channel.c | 366 | ||||
-rw-r--r-- | drivers/video/tegra/host/nvhost_intr.c | 10 | ||||
-rw-r--r-- | drivers/video/tegra/host/nvhost_intr.h | 6 | ||||
-rw-r--r-- | drivers/video/tegra/host/nvhost_job.c | 21 | ||||
-rw-r--r-- | drivers/video/tegra/host/nvhost_job.h | 9 | ||||
-rw-r--r-- | include/trace/events/nvhost.h | 35 |
7 files changed, 258 insertions, 190 deletions
diff --git a/drivers/video/tegra/host/bus_client.c b/drivers/video/tegra/host/bus_client.c index b9164dc1b02b..736f3da0bdec 100644 --- a/drivers/video/tegra/host/bus_client.c +++ b/drivers/video/tegra/host/bus_client.c @@ -200,6 +200,7 @@ static int set_submit(struct nvhost_channel_userctx *ctx) } ctx->job = nvhost_job_realloc(ctx->job, + ctx->hwctx, &ctx->hdr, ctx->nvmap, ctx->priority, diff --git a/drivers/video/tegra/host/host1x/host1x_channel.c b/drivers/video/tegra/host/host1x/host1x_channel.c index 7fac426107e7..b16a34f416ab 100644 --- a/drivers/video/tegra/host/host1x/host1x_channel.c +++ b/drivers/video/tegra/host/host1x/host1x_channel.c @@ -47,53 +47,193 @@ static void sync_waitbases(struct nvhost_channel *ch, u32 syncpt_val) } } +static void *pre_submit_ctxsave(struct nvhost_job *job, + struct nvhost_hwctx *cur_ctx) +{ + struct nvhost_channel *ch = job->ch; + void *ctxsave_waiter = NULL; + + /* Is a save needed? */ + if (!cur_ctx || ch->cur_ctx == job->hwctx) + return NULL; + + if (cur_ctx->has_timedout) { + dev_dbg(&ch->dev->dev, + "%s: skip save of timed out context (0x%p)\n", + __func__, ch->cur_ctx); + + return NULL; + } + + /* Allocate save waiter if needed */ + if (ch->ctxhandler->save_service) { + ctxsave_waiter = nvhost_intr_alloc_waiter(); + if (!ctxsave_waiter) + return ERR_PTR(-ENOMEM); + } + + return ctxsave_waiter; +} + +static void submit_ctxsave(struct nvhost_job *job, void *ctxsave_waiter, + struct nvhost_hwctx *cur_ctx) +{ + struct nvhost_master *host = nvhost_get_host(job->ch->dev); + struct nvhost_channel *ch = job->ch; + u32 syncval; + int err; + u32 save_thresh = 0; + + /* Is a save needed? */ + if (!cur_ctx || cur_ctx == job->hwctx || cur_ctx->has_timedout) + return; + + /* Retrieve save threshold if we have a waiter */ + if (ctxsave_waiter) + save_thresh = + nvhost_syncpt_read_max(&host->syncpt, job->syncpt_id) + + to_host1x_hwctx(cur_ctx)->save_thresh; + + /* Adjust the syncpoint max */ + job->syncpt_incrs += to_host1x_hwctx(cur_ctx)->save_incrs; + syncval = nvhost_syncpt_incr_max(&host->syncpt, + job->syncpt_id, + to_host1x_hwctx(cur_ctx)->save_incrs); + + /* Send the save to channel */ + cur_ctx->valid = true; + ch->ctxhandler->save_push(cur_ctx, &ch->cdma); + nvhost_job_get_hwctx(job, cur_ctx); + + /* Notify save service */ + if (ctxsave_waiter) { + err = nvhost_intr_add_action(&host->intr, + job->syncpt_id, + save_thresh, + NVHOST_INTR_ACTION_CTXSAVE, cur_ctx, + ctxsave_waiter, + NULL); + ctxsave_waiter = NULL; + WARN(err, "Failed to set ctx save interrupt"); + } + + trace_nvhost_channel_context_save(ch->dev->name, cur_ctx); +} + +static void submit_ctxrestore(struct nvhost_job *job) +{ + struct nvhost_master *host = nvhost_get_host(job->ch->dev); + struct nvhost_channel *ch = job->ch; + u32 syncval; + struct host1x_hwctx *ctx = + job->hwctx ? to_host1x_hwctx(job->hwctx) : NULL; + + /* First check if we have a valid context to restore */ + if(ch->cur_ctx == job->hwctx || !job->hwctx || !job->hwctx->valid) + return; + + /* Increment syncpt max */ + job->syncpt_incrs += ctx->restore_incrs; + syncval = nvhost_syncpt_incr_max(&host->syncpt, + job->syncpt_id, + ctx->restore_incrs); + + /* Send restore buffer to channel */ + nvhost_cdma_push_gather(&ch->cdma, + host->nvmap, + nvmap_ref_to_handle(ctx->restore), + nvhost_opcode_gather(ctx->restore_size), + ctx->restore_phys); + + trace_nvhost_channel_context_restore(ch->dev->name, &ctx->hwctx); +} + +void submit_nullkickoff(struct nvhost_job *job, int user_syncpt_incrs) +{ + struct nvhost_channel *ch = job->ch; + int incr; + u32 op_incr; + + /* push increments that correspond to nulled out commands */ + op_incr = nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_OP_DONE, + job->syncpt_id); + for (incr = 0; incr < (user_syncpt_incrs >> 1); incr++) + nvhost_cdma_push(&ch->cdma, op_incr, op_incr); + if (user_syncpt_incrs & 1) + nvhost_cdma_push(&ch->cdma, op_incr, NVHOST_OPCODE_NOOP); + + /* for 3d, waitbase needs to be incremented after each submit */ + if (ch->dev->class == NV_GRAPHICS_3D_CLASS_ID) { + u32 waitbase = to_host1x_hwctx_handler(job->hwctx->h)->waitbase; + nvhost_cdma_push(&ch->cdma, + nvhost_opcode_setclass( + NV_HOST1X_CLASS_ID, + NV_CLASS_HOST_INCR_SYNCPT_BASE, + 1), + nvhost_class_host_incr_syncpt_base( + waitbase, + user_syncpt_incrs)); + } +} + +void submit_gathers(struct nvhost_job *job) +{ + /* push user gathers */ + int i = 0; + for ( ; i < job->num_gathers; i++) { + u32 op1 = nvhost_opcode_gather(job->gathers[i].words); + u32 op2 = job->gathers[i].mem; + nvhost_cdma_push_gather(&job->ch->cdma, + job->nvmap, job->unpins[i/2], + op1, op2); + } +} + int host1x_channel_submit(struct nvhost_job *job) { - struct host1x_hwctx *hwctx_to_save = NULL; - struct nvhost_channel *channel = job->ch; + struct nvhost_channel *ch = job->ch; struct nvhost_syncpt *sp = &nvhost_get_host(job->ch->dev)->syncpt; u32 user_syncpt_incrs = job->syncpt_incrs; - bool need_restore = false; + u32 prev_max = 0; u32 syncval; int err; - void *ctxrestore_waiter = NULL; - void *ctxsave_waiter, *completed_waiter; + void *completed_waiter = NULL, *ctxsave_waiter = NULL; + /* Bail out on timed out contexts */ if (job->hwctx && job->hwctx->has_timedout) return -ETIMEDOUT; - ctxsave_waiter = nvhost_intr_alloc_waiter(); - completed_waiter = nvhost_intr_alloc_waiter(); - if (!ctxsave_waiter || !completed_waiter) { - err = -ENOMEM; - goto done; - } - - /* keep module powered */ - nvhost_module_busy(channel->dev); - if (channel->dev->busy) - channel->dev->busy(channel->dev); + /* Turn on the client module and host1x */ + nvhost_module_busy(ch->dev); + if (ch->dev->busy) + ch->dev->busy(ch->dev); /* before error checks, return current max */ - job->syncpt_end = nvhost_syncpt_read_max(sp, job->syncpt_id); + prev_max = job->syncpt_end = + nvhost_syncpt_read_max(sp, job->syncpt_id); /* get submit lock */ - err = mutex_lock_interruptible(&channel->submitlock); + err = mutex_lock_interruptible(&ch->submitlock); if (err) { - nvhost_module_idle(channel->dev); - goto done; + nvhost_module_idle(ch->dev); + goto error; } - /* If we are going to need a restore, allocate a waiter for it */ - if (channel->cur_ctx != job->hwctx && job->hwctx && job->hwctx->valid) { - ctxrestore_waiter = nvhost_intr_alloc_waiter(); - if (!ctxrestore_waiter) { - mutex_unlock(&channel->submitlock); - nvhost_module_idle(channel->dev); - err = -ENOMEM; - goto done; - } - need_restore = true; + /* Do the needed allocations */ + ctxsave_waiter = pre_submit_ctxsave(job, ch->cur_ctx); + if (IS_ERR(ctxsave_waiter)) { + err = PTR_ERR(ctxsave_waiter); + nvhost_module_idle(ch->dev); + mutex_unlock(&ch->submitlock); + goto error; + } + + completed_waiter = nvhost_intr_alloc_waiter(); + if (!completed_waiter) { + nvhost_module_idle(ch->dev); + mutex_unlock(&ch->submitlock); + err = -ENOMEM; + goto error; } /* remove stale waits */ @@ -104,166 +244,64 @@ int host1x_channel_submit(struct nvhost_job *job) job->waitchk, job->num_waitchk); if (err) { - dev_warn(&channel->dev->dev, + dev_warn(&ch->dev->dev, "nvhost_syncpt_wait_check failed: %d\n", err); - mutex_unlock(&channel->submitlock); - nvhost_module_idle(channel->dev); - goto done; + mutex_unlock(&ch->submitlock); + nvhost_module_idle(ch->dev); + goto error; } } /* begin a CDMA submit */ - err = nvhost_cdma_begin(&channel->cdma, job); + err = nvhost_cdma_begin(&ch->cdma, job); if (err) { - mutex_unlock(&channel->submitlock); - nvhost_module_idle(channel->dev); - goto done; + mutex_unlock(&ch->submitlock); + nvhost_module_idle(ch->dev); + goto error; } - sync_waitbases(channel, job->syncpt_end); + submit_ctxsave(job, ctxsave_waiter, ch->cur_ctx); + submit_ctxrestore(job); + ch->cur_ctx = job->hwctx; - /* context switch */ - if (channel->cur_ctx != job->hwctx) { - trace_nvhost_channel_context_switch(channel->dev->name, - channel->cur_ctx, job->hwctx); - hwctx_to_save = channel->cur_ctx ? - to_host1x_hwctx(channel->cur_ctx) : NULL; - if (hwctx_to_save && - hwctx_to_save->hwctx.has_timedout) { - hwctx_to_save = NULL; - dev_dbg(&channel->dev->dev, - "%s: skip save of timed out context (0x%p)\n", - __func__, channel->cur_ctx); - } - if (hwctx_to_save) { - job->syncpt_incrs += hwctx_to_save->save_incrs; - hwctx_to_save->hwctx.valid = true; - channel->ctxhandler->get(&hwctx_to_save->hwctx); - } - channel->cur_ctx = job->hwctx; - if (need_restore) - job->syncpt_incrs += - to_host1x_hwctx(job->hwctx)->restore_incrs; - } - - /* get absolute sync value */ - if (BIT(job->syncpt_id) & sp->client_managed) - syncval = nvhost_syncpt_set_max(sp, - job->syncpt_id, job->syncpt_incrs); - else - syncval = nvhost_syncpt_incr_max(sp, - job->syncpt_id, job->syncpt_incrs); + syncval = nvhost_syncpt_incr_max(sp, + job->syncpt_id, user_syncpt_incrs); job->syncpt_end = syncval; - /* push save buffer (pre-gather setup depends on unit) */ - if (hwctx_to_save) - channel->ctxhandler->save_push(&hwctx_to_save->hwctx, - &channel->cdma); - - /* gather restore buffer */ - if (need_restore) { - struct host1x_hwctx *cur_ctx = - to_host1x_hwctx(channel->cur_ctx); - nvhost_cdma_push_gather(&channel->cdma, - nvhost_get_host(channel->dev)->nvmap, - nvmap_ref_to_handle(cur_ctx->restore), - nvhost_opcode_gather(cur_ctx->restore_size), - cur_ctx->restore_phys); - channel->ctxhandler->get(channel->cur_ctx); - } - - /* add a setclass for modules that require it (unless ctxsw added it) */ - if (!hwctx_to_save && !need_restore && channel->dev->class) - nvhost_cdma_push(&channel->cdma, - nvhost_opcode_setclass(channel->dev->class, 0, 0), + /* add a setclass for modules that require it */ + if (ch->dev->class) + nvhost_cdma_push(&ch->cdma, + nvhost_opcode_setclass(ch->dev->class, 0, 0), NVHOST_OPCODE_NOOP); - if (job->null_kickoff) { - int incr; - u32 op_incr; - - /* TODO ideally we'd also perform host waits here */ - - /* push increments that correspond to nulled out commands */ - op_incr = nvhost_opcode_imm(0, 0x100 | job->syncpt_id); - for (incr = 0; incr < (user_syncpt_incrs >> 1); incr++) - nvhost_cdma_push(&channel->cdma, op_incr, op_incr); - if (user_syncpt_incrs & 1) - nvhost_cdma_push(&channel->cdma, - op_incr, NVHOST_OPCODE_NOOP); - - /* for 3d, waitbase needs to be incremented after each submit */ - if (channel->dev->class == NV_GRAPHICS_3D_CLASS_ID) - nvhost_cdma_push(&channel->cdma, - nvhost_opcode_setclass( - NV_HOST1X_CLASS_ID, - NV_CLASS_HOST_INCR_SYNCPT_BASE, - 1), - nvhost_class_host_incr_syncpt_base( - NVWAITBASE_3D, - user_syncpt_incrs)); - } else { - /* push user gathers */ - int i = 0; - for ( ; i < job->num_gathers; i++) { - u32 op1 = nvhost_opcode_gather(job->gathers[i].words); - u32 op2 = job->gathers[i].mem; - nvhost_cdma_push_gather(&channel->cdma, - job->nvmap, job->unpins[i/2], - op1, op2); - } - } - - /* end CDMA submit & stash pinned hMems into sync queue */ - nvhost_cdma_end(&channel->cdma, job); + if (job->null_kickoff) + submit_nullkickoff(job, user_syncpt_incrs); + else + submit_gathers(job); - trace_nvhost_channel_submitted(channel->dev->name, - syncval - job->syncpt_incrs, syncval); + sync_waitbases(ch, job->syncpt_end); - /* - * schedule a context save interrupt (to drain the host FIFO - * if necessary, and to release the restore buffer) - */ - if (hwctx_to_save) { - err = nvhost_intr_add_action( - &nvhost_get_host(channel->dev)->intr, - job->syncpt_id, - syncval - job->syncpt_incrs - + hwctx_to_save->save_thresh, - NVHOST_INTR_ACTION_CTXSAVE, &hwctx_to_save->hwctx, - ctxsave_waiter, - NULL); - ctxsave_waiter = NULL; - WARN(err, "Failed to set ctx save interrupt"); - } + /* end CDMA submit & stash pinned hMems into sync queue */ + nvhost_cdma_end(&ch->cdma, job); - if (need_restore) { - BUG_ON(!ctxrestore_waiter); - err = nvhost_intr_add_action( - &nvhost_get_host(channel->dev)->intr, - job->syncpt_id, - syncval - user_syncpt_incrs, - NVHOST_INTR_ACTION_CTXRESTORE, channel->cur_ctx, - ctxrestore_waiter, - NULL); - ctxrestore_waiter = NULL; - WARN(err, "Failed to set ctx restore interrupt"); - } + trace_nvhost_channel_submitted(ch->dev->name, + prev_max, syncval); /* schedule a submit complete interrupt */ - err = nvhost_intr_add_action(&nvhost_get_host(channel->dev)->intr, + err = nvhost_intr_add_action(&nvhost_get_host(ch->dev)->intr, job->syncpt_id, syncval, - NVHOST_INTR_ACTION_SUBMIT_COMPLETE, channel, + NVHOST_INTR_ACTION_SUBMIT_COMPLETE, ch, completed_waiter, NULL); completed_waiter = NULL; WARN(err, "Failed to set submit complete interrupt"); - mutex_unlock(&channel->submitlock); + mutex_unlock(&ch->submitlock); -done: - kfree(ctxrestore_waiter); + return 0; + +error: kfree(ctxsave_waiter); kfree(completed_waiter); return err; diff --git a/drivers/video/tegra/host/nvhost_intr.c b/drivers/video/tegra/host/nvhost_intr.c index 0fc6652c7c77..7c4bdc7bafb6 100644 --- a/drivers/video/tegra/host/nvhost_intr.c +++ b/drivers/video/tegra/host/nvhost_intr.c @@ -144,15 +144,6 @@ static void action_ctxsave(struct nvhost_waitlist *waiter) if (channel->ctxhandler->save_service) channel->ctxhandler->save_service(hwctx); - channel->ctxhandler->put(hwctx); -} - -static void action_ctxrestore(struct nvhost_waitlist *waiter) -{ - struct nvhost_hwctx *hwctx = waiter->data; - struct nvhost_channel *channel = hwctx->channel; - - channel->ctxhandler->put(hwctx); } static void action_wakeup(struct nvhost_waitlist *waiter) @@ -174,7 +165,6 @@ typedef void (*action_handler)(struct nvhost_waitlist *waiter); static action_handler action_handlers[NVHOST_INTR_ACTION_COUNT] = { action_submit_complete, action_ctxsave, - action_ctxrestore, action_wakeup, action_wakeup_interruptible, }; diff --git a/drivers/video/tegra/host/nvhost_intr.h b/drivers/video/tegra/host/nvhost_intr.h index 08c2a92880e0..26ab04ebd4ab 100644 --- a/drivers/video/tegra/host/nvhost_intr.h +++ b/drivers/video/tegra/host/nvhost_intr.h @@ -41,12 +41,6 @@ enum nvhost_intr_action { NVHOST_INTR_ACTION_CTXSAVE, /** - * Restore a HW context. - * 'data' points to a context - */ - NVHOST_INTR_ACTION_CTXRESTORE, - - /** * Wake up a task. * 'data' points to a wait_queue_head_t */ diff --git a/drivers/video/tegra/host/nvhost_job.c b/drivers/video/tegra/host/nvhost_job.c index e0baea559b19..df7a62d689bc 100644 --- a/drivers/video/tegra/host/nvhost_job.c +++ b/drivers/video/tegra/host/nvhost_job.c @@ -181,6 +181,8 @@ struct nvhost_job *nvhost_job_alloc(struct nvhost_channel *ch, kref_init(&job->ref); job->ch = ch; job->hwctx = hwctx; + if (hwctx) + hwctx->h->get(hwctx); job->nvmap = nvmap ? nvmap_client_get(nvmap) : NULL; err = alloc_gathers(job, num_cmdbufs); @@ -199,6 +201,7 @@ error: struct nvhost_job *nvhost_job_realloc( struct nvhost_job *oldjob, + struct nvhost_hwctx *hwctx, struct nvhost_submit_hdr_ext *hdr, struct nvmap_client *nvmap, int priority, int clientid) @@ -212,7 +215,9 @@ struct nvhost_job *nvhost_job_realloc( goto error; kref_init(&newjob->ref); newjob->ch = oldjob->ch; - newjob->hwctx = oldjob->hwctx; + newjob->hwctx = hwctx; + if (hwctx) + newjob->hwctx->h->get(newjob->hwctx); newjob->timeout = oldjob->timeout; newjob->nvmap = nvmap ? nvmap_client_get(nvmap) : NULL; @@ -243,6 +248,10 @@ static void job_free(struct kref *ref) { struct nvhost_job *job = container_of(ref, struct nvhost_job, ref); + if (job->hwctxref) + job->hwctxref->h->put(job->hwctxref); + if (job->hwctx) + job->hwctx->h->put(job->hwctx); if (job->gathers) nvmap_munmap(job->gather_mem, job->gathers); if (job->gather_mem) @@ -252,6 +261,16 @@ static void job_free(struct kref *ref) vfree(job); } +/* Acquire reference to a hardware context. Used for keeping saved contexts in + * memory. */ +void nvhost_job_get_hwctx(struct nvhost_job *job, struct nvhost_hwctx *hwctx) +{ + BUG_ON(job->hwctxref); + + job->hwctxref = hwctx; + hwctx->h->get(hwctx); +} + void nvhost_job_put(struct nvhost_job *job) { kref_put(&job->ref, job_free); diff --git a/drivers/video/tegra/host/nvhost_job.h b/drivers/video/tegra/host/nvhost_job.h index 96007bd33faa..ad9d1af60da1 100644 --- a/drivers/video/tegra/host/nvhost_job.h +++ b/drivers/video/tegra/host/nvhost_job.h @@ -83,6 +83,9 @@ struct nvhost_job { /* Index and number of slots used in the push buffer */ int first_get; int num_slots; + + /* Context to be freed */ + struct nvhost_hwctx *hwctxref; }; /* @@ -101,6 +104,7 @@ struct nvhost_job *nvhost_job_alloc(struct nvhost_channel *ch, * oldjob will be reused, and nvhost_job_put() will be called to it. */ struct nvhost_job *nvhost_job_realloc(struct nvhost_job *oldjob, + struct nvhost_hwctx *hwctx, struct nvhost_submit_hdr_ext *hdr, struct nvmap_client *nvmap, int priority, int clientid); @@ -117,6 +121,11 @@ void nvhost_job_add_gather(struct nvhost_job *job, void nvhost_job_get(struct nvhost_job *job); /* + * Increment reference for a hardware context. + */ +void nvhost_job_get_hwctx(struct nvhost_job *job, struct nvhost_hwctx *hwctx); + +/* * Decrement reference job, free if goes to zero. */ void nvhost_job_put(struct nvhost_job *job); diff --git a/include/trace/events/nvhost.h b/include/trace/events/nvhost.h index 1e28559c9287..4c44cdc99f98 100644 --- a/include/trace/events/nvhost.h +++ b/include/trace/events/nvhost.h @@ -210,25 +210,42 @@ TRACE_EVENT(nvhost_channel_write_waitchks, __entry->name, __entry->waitchks, __entry->waitmask) ); -TRACE_EVENT(nvhost_channel_context_switch, - TP_PROTO(const char *name, void *old_ctx, void *new_ctx), +TRACE_EVENT(nvhost_channel_context_save, + TP_PROTO(const char *name, void *ctx), - TP_ARGS(name, old_ctx, new_ctx), + TP_ARGS(name, ctx), TP_STRUCT__entry( __field(const char *, name) - __field(void*, old_ctx) - __field(void*, new_ctx) + __field(void*, ctx) ), TP_fast_assign( __entry->name = name; - __entry->old_ctx = old_ctx; - __entry->new_ctx = new_ctx; + __entry->ctx = ctx; ), - TP_printk("name=%s, old=%p, new=%p", - __entry->name, __entry->old_ctx, __entry->new_ctx) + TP_printk("name=%s, ctx=%p", + __entry->name, __entry->ctx) +); + +TRACE_EVENT(nvhost_channel_context_restore, + TP_PROTO(const char *name, void *ctx), + + TP_ARGS(name, ctx), + + TP_STRUCT__entry( + __field(const char *, name) + __field(void*, ctx) + ), + + TP_fast_assign( + __entry->name = name; + __entry->ctx = ctx; + ), + + TP_printk("name=%s, ctx=%p", + __entry->name, __entry->ctx) ); TRACE_EVENT(nvhost_ctrlopen, |