summaryrefslogtreecommitdiff
path: root/drivers/video/tegra
diff options
context:
space:
mode:
authorTerje Bergstrom <tbergstrom@nvidia.com>2012-02-20 14:50:26 +0200
committerSimone Willett <swillett@nvidia.com>2012-03-20 13:46:33 -0700
commit419f21cd5826a38a1759fde2a4daba92a4f9a7bc (patch)
tree376c4da30a3c31496255dcd57f4020d80db43cda /drivers/video/tegra
parent590fd1f9ad46649ede95348e73e4b0cc246f6390 (diff)
video: tegra: host: Fix sync point comparison
Fix sync point comparison to take into account old expired values, and do proper comparison taking into account wrapping. Bug 941327 Change-Id: I70724637ba870b2e29bac695abc0ea2b968394d7 Signed-off-by: Terje Bergstrom <tbergstrom@nvidia.com> Reviewed-on: http://git-master/r/84808 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Ilan Aelion <iaelion@nvidia.com> Reviewed-by: Acorn Pooley <apooley@nvidia.com>
Diffstat (limited to 'drivers/video/tegra')
-rw-r--r--drivers/video/tegra/host/host1x/host1x_channel.c4
-rw-r--r--drivers/video/tegra/host/nvhost_cdma.c2
-rw-r--r--drivers/video/tegra/host/nvhost_syncpt.c81
-rw-r--r--drivers/video/tegra/host/nvhost_syncpt.h13
4 files changed, 75 insertions, 25 deletions
diff --git a/drivers/video/tegra/host/host1x/host1x_channel.c b/drivers/video/tegra/host/host1x/host1x_channel.c
index 34ae8879ba18..7fac426107e7 100644
--- a/drivers/video/tegra/host/host1x/host1x_channel.c
+++ b/drivers/video/tegra/host/host1x/host1x_channel.c
@@ -424,7 +424,7 @@ int host1x_channel_read_3d_reg(
read_waiter = NULL;
WARN(err, "Failed to set wakeup interrupt");
wait_event(wq,
- nvhost_syncpt_min_cmp(&nvhost_get_host(channel->dev)->syncpt,
+ nvhost_syncpt_is_expired(&nvhost_get_host(channel->dev)->syncpt,
p->syncpt, syncval - 2));
nvhost_intr_put_ref(&nvhost_get_host(channel->dev)->intr, ref);
@@ -573,7 +573,7 @@ int host1x_save_context(struct nvhost_device *dev, u32 syncpt_id)
wakeup_waiter = NULL;
WARN(err, "Failed to set wakeup interrupt");
wait_event(wq,
- nvhost_syncpt_min_cmp(&nvhost_get_host(ch->dev)->syncpt,
+ nvhost_syncpt_is_expired(&nvhost_get_host(ch->dev)->syncpt,
syncpt_id, syncpt_val));
nvhost_intr_put_ref(&nvhost_get_host(ch->dev)->intr, ref);
diff --git a/drivers/video/tegra/host/nvhost_cdma.c b/drivers/video/tegra/host/nvhost_cdma.c
index 390497759020..775d761e65c9 100644
--- a/drivers/video/tegra/host/nvhost_cdma.c
+++ b/drivers/video/tegra/host/nvhost_cdma.c
@@ -163,7 +163,7 @@ static void update_cdma_locked(struct nvhost_cdma *cdma)
BUG_ON(job->syncpt_id == NVSYNCPT_INVALID);
/* Check whether this syncpt has completed, and bail if not */
- if (!nvhost_syncpt_min_cmp(sp,
+ if (!nvhost_syncpt_is_expired(sp,
job->syncpt_id, job->syncpt_end)) {
/* Start timer on next pending syncpt */
if (job->timeout)
diff --git a/drivers/video/tegra/host/nvhost_syncpt.c b/drivers/video/tegra/host/nvhost_syncpt.c
index 4e2bec77da65..c41ecd99d909 100644
--- a/drivers/video/tegra/host/nvhost_syncpt.c
+++ b/drivers/video/tegra/host/nvhost_syncpt.c
@@ -143,7 +143,7 @@ int nvhost_syncpt_wait_timeout(struct nvhost_syncpt *sp, u32 id,
}
/* first check cache */
- if (nvhost_syncpt_min_cmp(sp, id, thresh)) {
+ if (nvhost_syncpt_is_expired(sp, id, thresh)) {
if (value)
*value = nvhost_syncpt_read_min(sp, id);
return 0;
@@ -182,13 +182,17 @@ int nvhost_syncpt_wait_timeout(struct nvhost_syncpt *sp, u32 id,
goto done;
err = -EAGAIN;
+ /* Caller-specified timeout may be impractically low */
+ if (timeout < SYNCPT_CHECK_PERIOD)
+ low_timeout = timeout;
+
/* wait for the syncpoint, or timeout, or signal */
while (timeout) {
u32 check = min_t(u32, SYNCPT_CHECK_PERIOD, timeout);
int remain = wait_event_interruptible_timeout(wq,
- nvhost_syncpt_min_cmp(sp, id, thresh),
- check);
- if (remain > 0 || nvhost_syncpt_min_cmp(sp, id, thresh)) {
+ nvhost_syncpt_is_expired(sp, id, thresh),
+ check);
+ if (remain > 0 || nvhost_syncpt_is_expired(sp, id, thresh)) {
if (value)
*value = nvhost_syncpt_read_min(sp, id);
err = 0;
@@ -198,13 +202,8 @@ int nvhost_syncpt_wait_timeout(struct nvhost_syncpt *sp, u32 id,
err = remain;
break;
}
- if (timeout != NVHOST_NO_TIMEOUT) {
- if (timeout < SYNCPT_CHECK_PERIOD) {
- /* Caller-specified timeout may be impractically low */
- low_timeout = timeout;
- }
+ if (timeout != NVHOST_NO_TIMEOUT)
timeout -= check;
- }
if (timeout) {
dev_warn(&syncpt_to_dev(sp)->dev->dev,
"%s: syncpoint id %d (%s) stuck waiting %d, timeout=%d\n",
@@ -230,6 +229,68 @@ done:
return err;
}
+/**
+ * Returns true if syncpoint is expired, false if we may need to wait
+ */
+bool nvhost_syncpt_is_expired(
+ struct nvhost_syncpt *sp,
+ u32 id,
+ u32 thresh)
+{
+ u32 current_val;
+ u32 future_val;
+ smp_rmb();
+ current_val = (u32)atomic_read(&sp->min_val[id]);
+ future_val = (u32)atomic_read(&sp->max_val[id]);
+
+ /* Note the use of unsigned arithmetic here (mod 1<<32).
+ *
+ * c = current_val = min_val = the current value of the syncpoint.
+ * t = thresh = the value we are checking
+ * f = future_val = max_val = the value c will reach when all
+ * outstanding increments have completed.
+ *
+ * Note that c always chases f until it reaches f.
+ *
+ * Dtf = (f - t)
+ * Dtc = (c - t)
+ *
+ * Consider all cases:
+ *
+ * A) .....c..t..f..... Dtf < Dtc need to wait
+ * B) .....c.....f..t.. Dtf > Dtc expired
+ * C) ..t..c.....f..... Dtf > Dtc expired (Dct very large)
+ *
+ * Any case where f==c: always expired (for any t). Dtf == Dcf
+ * Any case where t==c: always expired (for any f). Dtf >= Dtc (because Dtc==0)
+ * Any case where t==f!=c: always wait. Dtf < Dtc (because Dtf==0,
+ * Dtc!=0)
+ *
+ * Other cases:
+ *
+ * A) .....t..f..c..... Dtf < Dtc need to wait
+ * A) .....f..c..t..... Dtf < Dtc need to wait
+ * A) .....f..t..c..... Dtf > Dtc expired
+ *
+ * So:
+ * Dtf >= Dtc implies EXPIRED (return true)
+ * Dtf < Dtc implies WAIT (return false)
+ *
+ * Note: If t is expired then we *cannot* wait on it. We would wait
+ * forever (hang the system).
+ *
+ * Note: do NOT get clever and remove the -thresh from both sides. It
+ * is NOT the same.
+ *
+ * If future valueis zero, we have a client managed sync point. In that
+ * case we do a direct comparison.
+ */
+ if (!client_managed(id))
+ return future_val - thresh >= current_val - thresh;
+ else
+ return (s32)(current_val - thresh) >= 0;
+}
+
void nvhost_syncpt_debug(struct nvhost_syncpt *sp)
{
syncpt_op(sp).debug(sp);
diff --git a/drivers/video/tegra/host/nvhost_syncpt.h b/drivers/video/tegra/host/nvhost_syncpt.h
index 595d95e29240..5b339178d1e1 100644
--- a/drivers/video/tegra/host/nvhost_syncpt.h
+++ b/drivers/video/tegra/host/nvhost_syncpt.h
@@ -95,18 +95,6 @@ static inline bool nvhost_syncpt_check_max(struct nvhost_syncpt *sp,
}
/**
- * Returns true if syncpoint has reached threshold
- */
-static inline bool nvhost_syncpt_min_cmp(struct nvhost_syncpt *sp,
- u32 id, u32 thresh)
-{
- u32 cur;
- smp_rmb();
- cur = (u32)atomic_read(&sp->min_val[id]);
- return ((s32)(cur - thresh) >= 0);
-}
-
-/**
* Returns true if syncpoint min == max
*/
static inline bool nvhost_syncpt_min_eq_max(struct nvhost_syncpt *sp, u32 id)
@@ -121,6 +109,7 @@ static inline bool nvhost_syncpt_min_eq_max(struct nvhost_syncpt *sp, u32 id)
void nvhost_syncpt_cpu_incr(struct nvhost_syncpt *sp, u32 id);
u32 nvhost_syncpt_update_min(struct nvhost_syncpt *sp, u32 id);
+bool nvhost_syncpt_is_expired(struct nvhost_syncpt *sp, u32 id, u32 thresh);
void nvhost_syncpt_save(struct nvhost_syncpt *sp);