summaryrefslogtreecommitdiff
path: root/drivers/staging/psb/psb_schedule.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/psb/psb_schedule.c')
-rw-r--r--drivers/staging/psb/psb_schedule.c1539
1 files changed, 1539 insertions, 0 deletions
diff --git a/drivers/staging/psb/psb_schedule.c b/drivers/staging/psb/psb_schedule.c
new file mode 100644
index 000000000000..a9e9637729d9
--- /dev/null
+++ b/drivers/staging/psb/psb_schedule.c
@@ -0,0 +1,1539 @@
+/**************************************************************************
+ * Copyright (c) 2007, Intel 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,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to
+ * develop this driver.
+ *
+ **************************************************************************/
+/*
+ * Authors: Thomas Hellstrom <thomas-at-tungstengraphics.com>
+ */
+
+#include <drm/drmP.h>
+#include "psb_drm.h"
+#include "psb_drv.h"
+#include "psb_reg.h"
+#include "psb_scene.h"
+#include "ttm/ttm_execbuf_util.h"
+
+#define PSB_ALLOWED_RASTER_RUNTIME (DRM_HZ * 30)
+#define PSB_ALLOWED_TA_RUNTIME (DRM_HZ * 30)
+#define PSB_RASTER_TIMEOUT (DRM_HZ / 10)
+#define PSB_TA_TIMEOUT (DRM_HZ / 10)
+
+#undef PSB_SOFTWARE_WORKAHEAD
+
+#ifdef PSB_STABLE_SETTING
+
+/*
+ * Software blocks completely while the engines are working so there can be no
+ * overlap.
+ */
+
+#define PSB_WAIT_FOR_RASTER_COMPLETION
+#define PSB_WAIT_FOR_TA_COMPLETION
+
+#elif defined(PSB_PARANOID_SETTING)
+/*
+ * Software blocks "almost" while the engines are working so there can be no
+ * overlap.
+ */
+
+#define PSB_WAIT_FOR_RASTER_COMPLETION
+#define PSB_WAIT_FOR_TA_COMPLETION
+#define PSB_BE_PARANOID
+
+#elif defined(PSB_SOME_OVERLAP_BUT_LOCKUP)
+/*
+ * Software leaps ahead while the rasterizer is running and prepares
+ * a new ta job that can be scheduled before the rasterizer has
+ * finished.
+ */
+
+#define PSB_WAIT_FOR_TA_COMPLETION
+
+#elif defined(PSB_SOFTWARE_WORKAHEAD)
+/*
+ * Don't sync, but allow software to work ahead. and queue a number of jobs.
+ * But block overlapping in the scheduler.
+ */
+
+#define PSB_BLOCK_OVERLAP
+#define ONLY_ONE_JOB_IN_RASTER_QUEUE
+
+#endif
+
+/*
+ * Avoid pixelbe pagefaults on C0.
+ */
+#if 0
+#define PSB_BLOCK_OVERLAP
+#endif
+
+static void psb_dispatch_ta(struct drm_psb_private *dev_priv,
+ struct psb_scheduler *scheduler,
+ uint32_t reply_flag);
+static void psb_dispatch_raster(struct drm_psb_private *dev_priv,
+ struct psb_scheduler *scheduler,
+ uint32_t reply_flag);
+
+#ifdef FIX_TG_16
+
+void psb_2d_atomic_unlock(struct drm_psb_private *dev_priv);
+static int psb_check_2d_idle(struct drm_psb_private *dev_priv);
+
+#endif
+
+void psb_scheduler_lockup(struct drm_psb_private *dev_priv,
+ int *lockup, int *idle)
+{
+ unsigned long irq_flags;
+ struct psb_scheduler *scheduler = &dev_priv->scheduler;
+
+ *lockup = 0;
+ *idle = 1;
+
+ spin_lock_irqsave(&scheduler->lock, irq_flags);
+
+ if (scheduler->current_task[PSB_SCENE_ENGINE_TA] != NULL &&
+ time_after_eq(jiffies, scheduler->ta_end_jiffies)) {
+ *lockup = 1;
+ }
+ if (!*lockup
+ && (scheduler->current_task[PSB_SCENE_ENGINE_RASTER] != NULL)
+ && time_after_eq(jiffies, scheduler->raster_end_jiffies)) {
+ *lockup = 1;
+ }
+ if (!*lockup)
+ *idle = scheduler->idle;
+
+ spin_unlock_irqrestore(&scheduler->lock, irq_flags);
+}
+
+static inline void psb_set_idle(struct psb_scheduler *scheduler)
+{
+ scheduler->idle =
+ (scheduler->current_task[PSB_SCENE_ENGINE_RASTER] == NULL) &&
+ (scheduler->current_task[PSB_SCENE_ENGINE_TA] == NULL);
+ if (scheduler->idle)
+ wake_up(&scheduler->idle_queue);
+}
+
+/*
+ * Call with the scheduler spinlock held.
+ * Assigns a scene context to either the ta or the rasterizer,
+ * flushing out other scenes to memory if necessary.
+ */
+
+static int psb_set_scene_fire(struct psb_scheduler *scheduler,
+ struct psb_scene *scene,
+ int engine, struct psb_task *task)
+{
+ uint32_t flags = 0;
+ struct psb_hw_scene *hw_scene;
+ struct drm_device *dev = scene->dev;
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *) dev->dev_private;
+
+ hw_scene = scene->hw_scene;
+ if (hw_scene && hw_scene->last_scene == scene) {
+
+ /*
+ * Reuse the last hw scene context and delete it from the
+ * free list.
+ */
+
+ PSB_DEBUG_RENDER("Reusing hw scene %d.\n",
+ hw_scene->context_number);
+ if (scene->flags & PSB_SCENE_FLAG_DIRTY) {
+
+ /*
+ * No hw context initialization to be done.
+ */
+
+ flags |= PSB_SCENE_FLAG_SETUP_ONLY;
+ }
+
+ list_del_init(&hw_scene->head);
+
+ } else {
+ struct list_head *list;
+ hw_scene = NULL;
+
+ /*
+ * Grab a new hw scene context.
+ */
+
+ list_for_each(list, &scheduler->hw_scenes) {
+ hw_scene =
+ list_entry(list, struct psb_hw_scene, head);
+ break;
+ }
+ BUG_ON(!hw_scene);
+ PSB_DEBUG_RENDER("New hw scene %d.\n",
+ hw_scene->context_number);
+
+ list_del_init(list);
+ }
+ scene->hw_scene = hw_scene;
+ hw_scene->last_scene = scene;
+
+ flags |= PSB_SCENE_FLAG_SETUP;
+
+ /*
+ * Switch context and setup the engine.
+ */
+
+ return psb_xhw_scene_bind_fire(dev_priv,
+ &task->buf,
+ task->flags,
+ hw_scene->context_number,
+ scene->hw_cookie,
+ task->oom_cmds,
+ task->oom_cmd_size,
+ scene->hw_data->offset,
+ engine, flags | scene->flags);
+}
+
+static inline void psb_report_fence(struct drm_psb_private *dev_priv,
+ struct psb_scheduler *scheduler,
+ uint32_t class,
+ uint32_t sequence,
+ uint32_t type, int call_handler)
+{
+ struct psb_scheduler_seq *seq = &scheduler->seq[type];
+ struct ttm_fence_device *fdev = &dev_priv->fdev;
+ struct ttm_fence_class_manager *fc = &fdev->fence_class[PSB_ENGINE_TA];
+ unsigned long irq_flags;
+
+ /**
+ * Block racing poll_ta calls, that take the lock in write mode.
+ */
+
+ read_lock_irqsave(&fc->lock, irq_flags);
+ seq->sequence = sequence;
+ seq->reported = 0;
+ read_unlock_irqrestore(&fc->lock, irq_flags);
+
+ if (call_handler)
+ psb_fence_handler(scheduler->dev, class);
+}
+
+static void psb_schedule_raster(struct drm_psb_private *dev_priv,
+ struct psb_scheduler *scheduler);
+
+static void psb_schedule_ta(struct drm_psb_private *dev_priv,
+ struct psb_scheduler *scheduler)
+{
+ struct psb_task *task = NULL;
+ struct list_head *list, *next;
+ int pushed_raster_task = 0;
+
+ PSB_DEBUG_RENDER("schedule ta\n");
+
+ if (scheduler->idle_count != 0)
+ return;
+
+ if (scheduler->current_task[PSB_SCENE_ENGINE_TA] != NULL)
+ return;
+
+ if (scheduler->ta_state)
+ return;
+
+ /*
+ * Skip the ta stage for rasterization-only
+ * tasks. They arrive here to make sure we're rasterizing
+ * tasks in the correct order.
+ */
+
+ list_for_each_safe(list, next, &scheduler->ta_queue) {
+ task = list_entry(list, struct psb_task, head);
+ if (task->task_type != psb_raster_task)
+ break;
+
+ list_del_init(list);
+ list_add_tail(list, &scheduler->raster_queue);
+ psb_report_fence(dev_priv, scheduler, task->engine,
+ task->sequence,
+ _PSB_FENCE_TA_DONE_SHIFT, 1);
+ task = NULL;
+ pushed_raster_task = 1;
+ }
+
+ if (pushed_raster_task)
+ psb_schedule_raster(dev_priv, scheduler);
+
+ if (!task)
+ return;
+
+ /*
+ * Still waiting for a vistest?
+ */
+
+ if (scheduler->feedback_task == task)
+ return;
+
+#ifdef ONLY_ONE_JOB_IN_RASTER_QUEUE
+
+ /*
+ * Block ta from trying to use both hardware contexts
+ * without the rasterizer starting to render from one of them.
+ */
+
+ if (!list_empty(&scheduler->raster_queue))
+ return;
+
+#endif
+
+#ifdef PSB_BLOCK_OVERLAP
+ /*
+ * Make sure rasterizer isn't doing anything.
+ */
+ if (scheduler->current_task[PSB_SCENE_ENGINE_RASTER] != NULL)
+ return;
+#endif
+ if (list_empty(&scheduler->hw_scenes))
+ return;
+
+#ifdef FIX_TG_16
+ if (psb_check_2d_idle(dev_priv))
+ return;
+#endif
+
+ list_del_init(&task->head);
+ if (task->flags & PSB_FIRE_FLAG_XHW_OOM)
+ scheduler->ta_state = 1;
+
+ scheduler->current_task[PSB_SCENE_ENGINE_TA] = task;
+ scheduler->idle = 0;
+ scheduler->ta_end_jiffies = jiffies + PSB_TA_TIMEOUT;
+ scheduler->total_ta_jiffies = 0;
+
+ task->reply_flags = (task->flags & PSB_FIRE_FLAG_XHW_OOM) ?
+ 0x00000000 : PSB_RF_FIRE_TA;
+
+ (void) psb_reg_submit(dev_priv, task->ta_cmds, task->ta_cmd_size);
+ psb_set_scene_fire(scheduler, task->scene, PSB_SCENE_ENGINE_TA,
+ task);
+ psb_schedule_watchdog(dev_priv);
+}
+
+static int psb_fire_raster(struct psb_scheduler *scheduler,
+ struct psb_task *task)
+{
+ struct drm_device *dev = scheduler->dev;
+ struct drm_psb_private *dev_priv = (struct drm_psb_private *)
+ dev->dev_private;
+
+ PSB_DEBUG_RENDER("Fire raster %d\n", task->sequence);
+
+ return psb_xhw_fire_raster(dev_priv, &task->buf, task->flags);
+}
+
+/*
+ * Take the first rasterization task from the hp raster queue or from the
+ * raster queue and fire the rasterizer.
+ */
+
+static void psb_schedule_raster(struct drm_psb_private *dev_priv,
+ struct psb_scheduler *scheduler)
+{
+ struct psb_task *task;
+ struct list_head *list;
+
+ if (scheduler->idle_count != 0)
+ return;
+
+ if (scheduler->current_task[PSB_SCENE_ENGINE_RASTER] != NULL) {
+ PSB_DEBUG_RENDER("Raster busy.\n");
+ return;
+ }
+#ifdef PSB_BLOCK_OVERLAP
+ if (scheduler->current_task[PSB_SCENE_ENGINE_TA] != NULL) {
+ PSB_DEBUG_RENDER("TA busy.\n");
+ return;
+ }
+#endif
+
+ if (!list_empty(&scheduler->hp_raster_queue))
+ list = scheduler->hp_raster_queue.next;
+ else if (!list_empty(&scheduler->raster_queue))
+ list = scheduler->raster_queue.next;
+ else {
+ PSB_DEBUG_RENDER("Nothing in list\n");
+ return;
+ }
+
+ task = list_entry(list, struct psb_task, head);
+
+ /*
+ * Sometimes changing ZLS format requires an ISP reset.
+ * Doesn't seem to consume too much time.
+ */
+
+ if (task->scene)
+ PSB_WSGX32(_PSB_CS_RESET_ISP_RESET, PSB_CR_SOFT_RESET);
+
+ scheduler->current_task[PSB_SCENE_ENGINE_RASTER] = task;
+
+ list_del_init(list);
+ scheduler->idle = 0;
+ scheduler->raster_end_jiffies = jiffies + PSB_RASTER_TIMEOUT;
+ scheduler->total_raster_jiffies = 0;
+
+ if (task->scene)
+ PSB_WSGX32(0, PSB_CR_SOFT_RESET);
+
+ (void) psb_reg_submit(dev_priv, task->raster_cmds,
+ task->raster_cmd_size);
+
+ if (task->scene) {
+ task->reply_flags = (task->flags & PSB_FIRE_FLAG_XHW_OOM) ?
+ 0x00000000 : PSB_RF_FIRE_RASTER;
+ psb_set_scene_fire(scheduler,
+ task->scene, PSB_SCENE_ENGINE_RASTER,
+ task);
+ } else {
+ task->reply_flags = PSB_RF_DEALLOC | PSB_RF_FIRE_RASTER;
+ psb_fire_raster(scheduler, task);
+ }
+ psb_schedule_watchdog(dev_priv);
+}
+
+int psb_extend_timeout(struct drm_psb_private *dev_priv,
+ uint32_t xhw_lockup)
+{
+ struct psb_scheduler *scheduler = &dev_priv->scheduler;
+ unsigned long irq_flags;
+ int ret = -EBUSY;
+
+ spin_lock_irqsave(&scheduler->lock, irq_flags);
+
+ if (scheduler->current_task[PSB_SCENE_ENGINE_TA] != NULL &&
+ time_after_eq(jiffies, scheduler->ta_end_jiffies)) {
+ if (xhw_lockup & PSB_LOCKUP_TA) {
+ goto out_unlock;
+ } else {
+ scheduler->total_ta_jiffies +=
+ jiffies - scheduler->ta_end_jiffies +
+ PSB_TA_TIMEOUT;
+ if (scheduler->total_ta_jiffies >
+ PSB_ALLOWED_TA_RUNTIME)
+ goto out_unlock;
+ scheduler->ta_end_jiffies = jiffies + PSB_TA_TIMEOUT;
+ }
+ }
+ if (scheduler->current_task[PSB_SCENE_ENGINE_RASTER] != NULL &&
+ time_after_eq(jiffies, scheduler->raster_end_jiffies)) {
+ if (xhw_lockup & PSB_LOCKUP_RASTER) {
+ goto out_unlock;
+ } else {
+ scheduler->total_raster_jiffies +=
+ jiffies - scheduler->raster_end_jiffies +
+ PSB_RASTER_TIMEOUT;
+ if (scheduler->total_raster_jiffies >
+ PSB_ALLOWED_RASTER_RUNTIME)
+ goto out_unlock;
+ scheduler->raster_end_jiffies =
+ jiffies + PSB_RASTER_TIMEOUT;
+ }
+ }
+
+ ret = 0;
+
+out_unlock:
+ spin_unlock_irqrestore(&scheduler->lock, irq_flags);
+ return ret;
+}
+
+/*
+ * TA done handler.
+ */
+
+static void psb_ta_done(struct drm_psb_private *dev_priv,
+ struct psb_scheduler *scheduler)
+{
+ struct psb_task *task =
+ scheduler->current_task[PSB_SCENE_ENGINE_TA];
+ struct psb_scene *scene = task->scene;
+
+ PSB_DEBUG_RENDER("TA done %u\n", task->sequence);
+
+ switch (task->ta_complete_action) {
+ case PSB_RASTER_BLOCK:
+ scheduler->ta_state = 1;
+ scene->flags |=
+ (PSB_SCENE_FLAG_DIRTY | PSB_SCENE_FLAG_COMPLETE);
+ list_add_tail(&task->head, &scheduler->raster_queue);
+ break;
+ case PSB_RASTER:
+ scene->flags |=
+ (PSB_SCENE_FLAG_DIRTY | PSB_SCENE_FLAG_COMPLETE);
+ list_add_tail(&task->head, &scheduler->raster_queue);
+ break;
+ case PSB_RETURN:
+ scheduler->ta_state = 0;
+ scene->flags |= PSB_SCENE_FLAG_DIRTY;
+ list_add_tail(&scene->hw_scene->head,
+ &scheduler->hw_scenes);
+
+ break;
+ }
+
+ scheduler->current_task[PSB_SCENE_ENGINE_TA] = NULL;
+
+#ifdef FIX_TG_16
+ psb_2d_atomic_unlock(dev_priv);
+#endif
+
+ if (task->ta_complete_action != PSB_RASTER_BLOCK)
+ psb_report_fence(dev_priv, scheduler, task->engine,
+ task->sequence,
+ _PSB_FENCE_TA_DONE_SHIFT, 1);
+
+ psb_schedule_raster(dev_priv, scheduler);
+ psb_schedule_ta(dev_priv, scheduler);
+ psb_set_idle(scheduler);
+
+ if (task->ta_complete_action != PSB_RETURN)
+ return;
+
+ list_add_tail(&task->head, &scheduler->task_done_queue);
+ schedule_delayed_work(&scheduler->wq, 1);
+}
+
+/*
+ * Rasterizer done handler.
+ */
+
+static void psb_raster_done(struct drm_psb_private *dev_priv,
+ struct psb_scheduler *scheduler)
+{
+ struct psb_task *task =
+ scheduler->current_task[PSB_SCENE_ENGINE_RASTER];
+ struct psb_scene *scene = task->scene;
+ uint32_t complete_action = task->raster_complete_action;
+
+ PSB_DEBUG_RENDER("Raster done %u\n", task->sequence);
+
+ scheduler->current_task[PSB_SCENE_ENGINE_RASTER] = NULL;
+
+ if (complete_action != PSB_RASTER)
+ psb_schedule_raster(dev_priv, scheduler);
+
+ if (scene) {
+ if (task->feedback.page) {
+ if (unlikely(scheduler->feedback_task)) {
+ /*
+ * This should never happen, since the previous
+ * feedback query will return before the next
+ * raster task is fired.
+ */
+ DRM_ERROR("Feedback task busy.\n");
+ }
+ scheduler->feedback_task = task;
+ psb_xhw_vistest(dev_priv, &task->buf);
+ }
+ switch (complete_action) {
+ case PSB_RETURN:
+ scene->flags &=
+ ~(PSB_SCENE_FLAG_DIRTY |
+ PSB_SCENE_FLAG_COMPLETE);
+ list_add_tail(&scene->hw_scene->head,
+ &scheduler->hw_scenes);
+ psb_report_fence(dev_priv, scheduler, task->engine,
+ task->sequence,
+ _PSB_FENCE_SCENE_DONE_SHIFT, 1);
+ if (task->flags & PSB_FIRE_FLAG_XHW_OOM)
+ scheduler->ta_state = 0;
+
+ break;
+ case PSB_RASTER:
+ list_add(&task->head, &scheduler->raster_queue);
+ task->raster_complete_action = PSB_RETURN;
+ psb_schedule_raster(dev_priv, scheduler);
+ break;
+ case PSB_TA:
+ list_add(&task->head, &scheduler->ta_queue);
+ scheduler->ta_state = 0;
+ task->raster_complete_action = PSB_RETURN;
+ task->ta_complete_action = PSB_RASTER;
+ break;
+
+ }
+ }
+ psb_schedule_ta(dev_priv, scheduler);
+ psb_set_idle(scheduler);
+
+ if (complete_action == PSB_RETURN) {
+ if (task->scene == NULL) {
+ psb_report_fence(dev_priv, scheduler, task->engine,
+ task->sequence,
+ _PSB_FENCE_RASTER_DONE_SHIFT, 1);
+ }
+ if (!task->feedback.page) {
+ list_add_tail(&task->head,
+ &scheduler->task_done_queue);
+ schedule_delayed_work(&scheduler->wq, 1);
+ }
+ }
+}
+
+void psb_scheduler_pause(struct drm_psb_private *dev_priv)
+{
+ struct psb_scheduler *scheduler = &dev_priv->scheduler;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&scheduler->lock, irq_flags);
+ scheduler->idle_count++;
+ spin_unlock_irqrestore(&scheduler->lock, irq_flags);
+}
+
+void psb_scheduler_restart(struct drm_psb_private *dev_priv)
+{
+ struct psb_scheduler *scheduler = &dev_priv->scheduler;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&scheduler->lock, irq_flags);
+ if (--scheduler->idle_count == 0) {
+ psb_schedule_ta(dev_priv, scheduler);
+ psb_schedule_raster(dev_priv, scheduler);
+ }
+ spin_unlock_irqrestore(&scheduler->lock, irq_flags);
+}
+
+int psb_scheduler_idle(struct drm_psb_private *dev_priv)
+{
+ struct psb_scheduler *scheduler = &dev_priv->scheduler;
+ unsigned long irq_flags;
+ int ret;
+ spin_lock_irqsave(&scheduler->lock, irq_flags);
+ ret = scheduler->idle_count != 0 && scheduler->idle;
+ spin_unlock_irqrestore(&scheduler->lock, irq_flags);
+ return ret;
+}
+
+int psb_scheduler_finished(struct drm_psb_private *dev_priv)
+{
+ struct psb_scheduler *scheduler = &dev_priv->scheduler;
+ unsigned long irq_flags;
+ int ret;
+ spin_lock_irqsave(&scheduler->lock, irq_flags);
+ ret = (scheduler->idle &&
+ list_empty(&scheduler->raster_queue) &&
+ list_empty(&scheduler->ta_queue) &&
+ list_empty(&scheduler->hp_raster_queue));
+ spin_unlock_irqrestore(&scheduler->lock, irq_flags);
+ return ret;
+}
+
+static void psb_ta_oom(struct drm_psb_private *dev_priv,
+ struct psb_scheduler *scheduler)
+{
+
+ struct psb_task *task =
+ scheduler->current_task[PSB_SCENE_ENGINE_TA];
+ if (!task)
+ return;
+
+ if (task->aborting)
+ return;
+ task->aborting = 1;
+
+ DRM_INFO("Info: TA out of parameter memory.\n");
+
+ (void) psb_xhw_ta_oom(dev_priv, &task->buf,
+ task->scene->hw_cookie);
+}
+
+static void psb_ta_oom_reply(struct drm_psb_private *dev_priv,
+ struct psb_scheduler *scheduler)
+{
+
+ struct psb_task *task =
+ scheduler->current_task[PSB_SCENE_ENGINE_TA];
+ uint32_t flags;
+ if (!task)
+ return;
+
+ psb_xhw_ta_oom_reply(dev_priv, &task->buf,
+ task->scene->hw_cookie,
+ &task->ta_complete_action,
+ &task->raster_complete_action, &flags);
+ task->flags |= flags;
+ task->aborting = 0;
+ psb_dispatch_ta(dev_priv, scheduler, PSB_RF_OOM_REPLY);
+}
+
+static void psb_ta_hw_scene_freed(struct drm_psb_private *dev_priv,
+ struct psb_scheduler *scheduler)
+{
+ DRM_ERROR("TA hw scene freed.\n");
+}
+
+static void psb_vistest_reply(struct drm_psb_private *dev_priv,
+ struct psb_scheduler *scheduler)
+{
+ struct psb_task *task = scheduler->feedback_task;
+ uint8_t *feedback_map;
+ uint32_t add;
+ uint32_t cur;
+ struct drm_psb_vistest *vistest;
+ int i;
+
+ scheduler->feedback_task = NULL;
+ if (!task) {
+ DRM_ERROR("No Poulsbo feedback task.\n");
+ return;
+ }
+ if (!task->feedback.page) {
+ DRM_ERROR("No Poulsbo feedback page.\n");
+ goto out;
+ }
+
+ if (in_irq())
+ feedback_map = kmap_atomic(task->feedback.page, KM_IRQ0);
+ else
+ feedback_map = kmap_atomic(task->feedback.page, KM_USER0);
+
+ /*
+ * Loop over all requested vistest components here.
+ * Only one (vistest) currently.
+ */
+
+ vistest = (struct drm_psb_vistest *)
+ (feedback_map + task->feedback.offset);
+
+ for (i = 0; i < PSB_HW_FEEDBACK_SIZE; ++i) {
+ add = task->buf.arg.arg.feedback[i];
+ cur = vistest->vt[i];
+
+ /*
+ * Vistest saturates.
+ */
+
+ vistest->vt[i] = (cur + add < cur) ? ~0 : cur + add;
+ }
+ if (in_irq())
+ kunmap_atomic(feedback_map, KM_IRQ0);
+ else
+ kunmap_atomic(feedback_map, KM_USER0);
+out:
+ psb_report_fence(dev_priv, scheduler, task->engine, task->sequence,
+ _PSB_FENCE_FEEDBACK_SHIFT, 1);
+
+ if (list_empty(&task->head)) {
+ list_add_tail(&task->head, &scheduler->task_done_queue);
+ schedule_delayed_work(&scheduler->wq, 1);
+ } else
+ psb_schedule_ta(dev_priv, scheduler);
+}
+
+static void psb_ta_fire_reply(struct drm_psb_private *dev_priv,
+ struct psb_scheduler *scheduler)
+{
+ struct psb_task *task =
+ scheduler->current_task[PSB_SCENE_ENGINE_TA];
+
+ psb_xhw_fire_reply(dev_priv, &task->buf, task->scene->hw_cookie);
+
+ psb_dispatch_ta(dev_priv, scheduler, PSB_RF_FIRE_TA);
+}
+
+static void psb_raster_fire_reply(struct drm_psb_private *dev_priv,
+ struct psb_scheduler *scheduler)
+{
+ struct psb_task *task =
+ scheduler->current_task[PSB_SCENE_ENGINE_RASTER];
+ uint32_t reply_flags;
+
+ if (!task) {
+ DRM_ERROR("Null task.\n");
+ return;
+ }
+
+ task->raster_complete_action = task->buf.arg.arg.sb.rca;
+ psb_xhw_fire_reply(dev_priv, &task->buf, task->scene->hw_cookie);
+
+ reply_flags = PSB_RF_FIRE_RASTER;
+ if (task->raster_complete_action == PSB_RASTER)
+ reply_flags |= PSB_RF_DEALLOC;
+
+ psb_dispatch_raster(dev_priv, scheduler, reply_flags);
+}
+
+static int psb_user_interrupt(struct drm_psb_private *dev_priv,
+ struct psb_scheduler *scheduler)
+{
+ uint32_t type;
+ int ret;
+ unsigned long irq_flags;
+
+ /*
+ * Xhw cannot write directly to the comm page, so
+ * do it here. Firmware would have written directly.
+ */
+
+ ret = psb_xhw_handler(dev_priv);
+ if (unlikely(ret))
+ return ret;
+
+ spin_lock_irqsave(&dev_priv->xhw_lock, irq_flags);
+ type = dev_priv->comm[PSB_COMM_USER_IRQ];
+ dev_priv->comm[PSB_COMM_USER_IRQ] = 0;
+ if (dev_priv->comm[PSB_COMM_USER_IRQ_LOST]) {
+ dev_priv->comm[PSB_COMM_USER_IRQ_LOST] = 0;
+ DRM_ERROR("Lost Poulsbo hardware event.\n");
+ }
+ spin_unlock_irqrestore(&dev_priv->xhw_lock, irq_flags);
+
+ if (type == 0)
+ return 0;
+
+ switch (type) {
+ case PSB_UIRQ_VISTEST:
+ psb_vistest_reply(dev_priv, scheduler);
+ break;
+ case PSB_UIRQ_OOM_REPLY:
+ psb_ta_oom_reply(dev_priv, scheduler);
+ break;
+ case PSB_UIRQ_FIRE_TA_REPLY:
+ psb_ta_fire_reply(dev_priv, scheduler);
+ break;
+ case PSB_UIRQ_FIRE_RASTER_REPLY:
+ psb_raster_fire_reply(dev_priv, scheduler);
+ break;
+ default:
+ DRM_ERROR("Unknown Poulsbo hardware event. %d\n", type);
+ }
+ return 0;
+}
+
+int psb_forced_user_interrupt(struct drm_psb_private *dev_priv)
+{
+ struct psb_scheduler *scheduler = &dev_priv->scheduler;
+ unsigned long irq_flags;
+ int ret;
+
+ spin_lock_irqsave(&scheduler->lock, irq_flags);
+ ret = psb_user_interrupt(dev_priv, scheduler);
+ spin_unlock_irqrestore(&scheduler->lock, irq_flags);
+ return ret;
+}
+
+static void psb_dispatch_ta(struct drm_psb_private *dev_priv,
+ struct psb_scheduler *scheduler,
+ uint32_t reply_flag)
+{
+ struct psb_task *task =
+ scheduler->current_task[PSB_SCENE_ENGINE_TA];
+ uint32_t flags;
+ uint32_t mask;
+
+ task->reply_flags |= reply_flag;
+ flags = task->reply_flags;
+ mask = PSB_RF_FIRE_TA;
+
+ if (!(flags & mask))
+ return;
+
+ mask = PSB_RF_TA_DONE;
+ if ((flags & mask) == mask) {
+ task->reply_flags &= ~mask;
+ psb_ta_done(dev_priv, scheduler);
+ }
+
+ mask = PSB_RF_OOM;
+ if ((flags & mask) == mask) {
+ task->reply_flags &= ~mask;
+ psb_ta_oom(dev_priv, scheduler);
+ }
+
+ mask = (PSB_RF_OOM_REPLY | PSB_RF_TERMINATE);
+ if ((flags & mask) == mask) {
+ task->reply_flags &= ~mask;
+ psb_ta_done(dev_priv, scheduler);
+ }
+}
+
+static void psb_dispatch_raster(struct drm_psb_private *dev_priv,
+ struct psb_scheduler *scheduler,
+ uint32_t reply_flag)
+{
+ struct psb_task *task =
+ scheduler->current_task[PSB_SCENE_ENGINE_RASTER];
+ uint32_t flags;
+ uint32_t mask;
+
+ task->reply_flags |= reply_flag;
+ flags = task->reply_flags;
+ mask = PSB_RF_FIRE_RASTER;
+
+ if (!(flags & mask))
+ return;
+
+ /*
+ * For rasterizer-only tasks, don't report fence done here,
+ * as this is time consuming and the rasterizer wants a new
+ * task immediately. For other tasks, the hardware is probably
+ * still busy deallocating TA memory, so we can report
+ * fence done in parallel.
+ */
+
+ if (task->raster_complete_action == PSB_RETURN &&
+ (reply_flag & PSB_RF_RASTER_DONE) && task->scene != NULL) {
+ psb_report_fence(dev_priv, scheduler, task->engine,
+ task->sequence,
+ _PSB_FENCE_RASTER_DONE_SHIFT, 1);
+ }
+
+ mask = PSB_RF_RASTER_DONE | PSB_RF_DEALLOC;
+ if ((flags & mask) == mask) {
+ task->reply_flags &= ~mask;
+ psb_raster_done(dev_priv, scheduler);
+ }
+}
+
+void psb_scheduler_handler(struct drm_psb_private *dev_priv,
+ uint32_t status)
+{
+ struct psb_scheduler *scheduler = &dev_priv->scheduler;
+
+ spin_lock(&scheduler->lock);
+
+ if (status & _PSB_CE_PIXELBE_END_RENDER) {
+ psb_dispatch_raster(dev_priv, scheduler,
+ PSB_RF_RASTER_DONE);
+ }
+ if (status & _PSB_CE_DPM_3D_MEM_FREE)
+ psb_dispatch_raster(dev_priv, scheduler, PSB_RF_DEALLOC);
+
+ if (status & _PSB_CE_TA_FINISHED)
+ psb_dispatch_ta(dev_priv, scheduler, PSB_RF_TA_DONE);
+
+ if (status & _PSB_CE_TA_TERMINATE)
+ psb_dispatch_ta(dev_priv, scheduler, PSB_RF_TERMINATE);
+
+ if (status & (_PSB_CE_DPM_REACHED_MEM_THRESH |
+ _PSB_CE_DPM_OUT_OF_MEMORY_GBL |
+ _PSB_CE_DPM_OUT_OF_MEMORY_MT)) {
+ psb_dispatch_ta(dev_priv, scheduler, PSB_RF_OOM);
+ }
+ if (status & _PSB_CE_DPM_TA_MEM_FREE)
+ psb_ta_hw_scene_freed(dev_priv, scheduler);
+
+ if (status & _PSB_CE_SW_EVENT)
+ psb_user_interrupt(dev_priv, scheduler);
+
+ spin_unlock(&scheduler->lock);
+}
+
+static void psb_free_task_wq(struct work_struct *work)
+{
+ struct psb_scheduler *scheduler =
+ container_of(work, struct psb_scheduler, wq.work);
+
+ struct list_head *list, *next;
+ unsigned long irq_flags;
+ struct psb_task *task;
+
+ if (!mutex_trylock(&scheduler->task_wq_mutex))
+ return;
+
+ spin_lock_irqsave(&scheduler->lock, irq_flags);
+ list_for_each_safe(list, next, &scheduler->task_done_queue) {
+ task = list_entry(list, struct psb_task, head);
+ list_del_init(list);
+ spin_unlock_irqrestore(&scheduler->lock, irq_flags);
+
+ PSB_DEBUG_RENDER("Checking Task %d: Scene 0x%08lx, "
+ "Feedback bo 0x%08lx, done %d\n",
+ task->sequence,
+ (unsigned long) task->scene,
+ (unsigned long) task->feedback.bo,
+ atomic_read(&task->buf.done));
+
+ if (task->scene) {
+ PSB_DEBUG_RENDER("Unref scene %d\n",
+ task->sequence);
+ psb_scene_unref(&task->scene);
+ if (task->feedback.bo) {
+ PSB_DEBUG_RENDER("Unref feedback bo %d\n",
+ task->sequence);
+ ttm_bo_unref(&task->feedback.bo);
+ }
+ }
+
+ if (atomic_read(&task->buf.done)) {
+ PSB_DEBUG_RENDER("Deleting task %d\n",
+ task->sequence);
+ drm_free(task, sizeof(*task), DRM_MEM_DRIVER);
+ task = NULL;
+ }
+ spin_lock_irqsave(&scheduler->lock, irq_flags);
+ if (task != NULL)
+ list_add(list, &scheduler->task_done_queue);
+ }
+ if (!list_empty(&scheduler->task_done_queue)) {
+ PSB_DEBUG_RENDER("Rescheduling wq\n");
+ schedule_delayed_work(&scheduler->wq, 1);
+ }
+ spin_unlock_irqrestore(&scheduler->lock, irq_flags);
+
+ if (list_empty(&scheduler->task_done_queue) &&
+ drm_psb_ospm && IS_MRST(scheduler->dev)) {
+ psb_try_power_down_sgx(scheduler->dev);
+ }
+ mutex_unlock(&scheduler->task_wq_mutex);
+}
+
+/*
+ * Check if any of the tasks in the queues is using a scene.
+ * In that case we know the TA memory buffer objects are
+ * fenced and will not be evicted until that fence is signaled.
+ */
+
+void psb_scheduler_ta_mem_check(struct drm_psb_private *dev_priv)
+{
+ struct psb_scheduler *scheduler = &dev_priv->scheduler;
+ unsigned long irq_flags;
+ struct psb_task *task;
+ struct psb_task *next_task;
+
+ dev_priv->force_ta_mem_load = 1;
+ spin_lock_irqsave(&scheduler->lock, irq_flags);
+ list_for_each_entry_safe(task, next_task, &scheduler->ta_queue,
+ head) {
+ if (task->scene) {
+ dev_priv->force_ta_mem_load = 0;
+ break;
+ }
+ }
+ list_for_each_entry_safe(task, next_task, &scheduler->raster_queue,
+ head) {
+ if (task->scene) {
+ dev_priv->force_ta_mem_load = 0;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&scheduler->lock, irq_flags);
+}
+
+void psb_scheduler_reset(struct drm_psb_private *dev_priv,
+ int error_condition)
+{
+ struct psb_scheduler *scheduler = &dev_priv->scheduler;
+ unsigned long wait_jiffies;
+ unsigned long cur_jiffies;
+ struct psb_task *task;
+ struct psb_task *next_task;
+ unsigned long irq_flags;
+
+ psb_scheduler_pause(dev_priv);
+ if (!psb_scheduler_idle(dev_priv)) {
+ spin_lock_irqsave(&scheduler->lock, irq_flags);
+
+ cur_jiffies = jiffies;
+ wait_jiffies = cur_jiffies;
+ if (scheduler->current_task[PSB_SCENE_ENGINE_TA] &&
+ time_after_eq(scheduler->ta_end_jiffies, wait_jiffies))
+ wait_jiffies = scheduler->ta_end_jiffies;
+ if (scheduler->current_task[PSB_SCENE_ENGINE_RASTER] &&
+ time_after_eq(scheduler->raster_end_jiffies,
+ wait_jiffies))
+ wait_jiffies = scheduler->raster_end_jiffies;
+
+ wait_jiffies -= cur_jiffies;
+ spin_unlock_irqrestore(&scheduler->lock, irq_flags);
+
+ (void) wait_event_timeout(scheduler->idle_queue,
+ psb_scheduler_idle(dev_priv),
+ wait_jiffies);
+ }
+
+ if (!psb_scheduler_idle(dev_priv)) {
+ spin_lock_irqsave(&scheduler->lock, irq_flags);
+ task = scheduler->current_task[PSB_SCENE_ENGINE_RASTER];
+ if (task) {
+ DRM_ERROR("Detected Poulsbo rasterizer lockup.\n");
+ if (task->engine == PSB_ENGINE_HPRAST) {
+ psb_fence_error(scheduler->dev,
+ PSB_ENGINE_HPRAST,
+ task->sequence,
+ _PSB_FENCE_TYPE_RASTER_DONE,
+ error_condition);
+
+ list_del(&task->head);
+ psb_xhw_clean_buf(dev_priv, &task->buf);
+ list_add_tail(&task->head,
+ &scheduler->task_done_queue);
+ } else {
+ list_add(&task->head,
+ &scheduler->raster_queue);
+ }
+ }
+ scheduler->current_task[PSB_SCENE_ENGINE_RASTER] = NULL;
+ task = scheduler->current_task[PSB_SCENE_ENGINE_TA];
+ if (task) {
+ DRM_ERROR("Detected Poulsbo ta lockup.\n");
+ list_add_tail(&task->head,
+ &scheduler->raster_queue);
+#ifdef FIX_TG_16
+ psb_2d_atomic_unlock(dev_priv);
+#endif
+ }
+ scheduler->current_task[PSB_SCENE_ENGINE_TA] = NULL;
+ scheduler->ta_state = 0;
+
+#ifdef FIX_TG_16
+ atomic_set(&dev_priv->ta_wait_2d, 0);
+ atomic_set(&dev_priv->ta_wait_2d_irq, 0);
+ wake_up(&dev_priv->queue_2d);
+#endif
+ spin_unlock_irqrestore(&scheduler->lock, irq_flags);
+ }
+
+ /*
+ * Empty raster queue.
+ */
+
+ spin_lock_irqsave(&scheduler->lock, irq_flags);
+ list_for_each_entry_safe(task, next_task, &scheduler->raster_queue,
+ head) {
+ struct psb_scene *scene = task->scene;
+
+ DRM_INFO("Signaling fence sequence %u\n",
+ task->sequence);
+
+ psb_fence_error(scheduler->dev,
+ task->engine,
+ task->sequence,
+ _PSB_FENCE_TYPE_TA_DONE |
+ _PSB_FENCE_TYPE_RASTER_DONE |
+ _PSB_FENCE_TYPE_SCENE_DONE |
+ _PSB_FENCE_TYPE_FEEDBACK, error_condition);
+ if (scene) {
+ scene->flags = 0;
+ if (scene->hw_scene) {
+ list_add_tail(&scene->hw_scene->head,
+ &scheduler->hw_scenes);
+ scene->hw_scene = NULL;
+ }
+ }
+
+ psb_xhw_clean_buf(dev_priv, &task->buf);
+ list_del(&task->head);
+ list_add_tail(&task->head, &scheduler->task_done_queue);
+ }
+
+ schedule_delayed_work(&scheduler->wq, 1);
+ scheduler->idle = 1;
+ wake_up(&scheduler->idle_queue);
+
+ spin_unlock_irqrestore(&scheduler->lock, irq_flags);
+ psb_scheduler_restart(dev_priv);
+
+}
+
+int psb_scheduler_init(struct drm_device *dev,
+ struct psb_scheduler *scheduler)
+{
+ struct psb_hw_scene *hw_scene;
+ int i;
+
+ memset(scheduler, 0, sizeof(*scheduler));
+ scheduler->dev = dev;
+ mutex_init(&scheduler->task_wq_mutex);
+ spin_lock_init(&scheduler->lock);
+ scheduler->idle = 1;
+
+ INIT_LIST_HEAD(&scheduler->ta_queue);
+ INIT_LIST_HEAD(&scheduler->raster_queue);
+ INIT_LIST_HEAD(&scheduler->hp_raster_queue);
+ INIT_LIST_HEAD(&scheduler->hw_scenes);
+ INIT_LIST_HEAD(&scheduler->task_done_queue);
+ INIT_DELAYED_WORK(&scheduler->wq, &psb_free_task_wq);
+ init_waitqueue_head(&scheduler->idle_queue);
+
+ for (i = 0; i < PSB_NUM_HW_SCENES; ++i) {
+ hw_scene = &scheduler->hs[i];
+ hw_scene->context_number = i;
+ list_add_tail(&hw_scene->head, &scheduler->hw_scenes);
+ }
+
+ for (i = 0; i < _PSB_ENGINE_TA_FENCE_TYPES; ++i)
+ scheduler->seq[i].reported = 0;
+ return 0;
+}
+
+/*
+ * Scene references maintained by the scheduler are not refcounted.
+ * Remove all references to a particular scene here.
+ */
+
+void psb_scheduler_remove_scene_refs(struct psb_scene *scene)
+{
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *) scene->dev->dev_private;
+ struct psb_scheduler *scheduler = &dev_priv->scheduler;
+ struct psb_hw_scene *hw_scene;
+ unsigned long irq_flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&scheduler->lock, irq_flags);
+ for (i = 0; i < PSB_NUM_HW_SCENES; ++i) {
+ hw_scene = &scheduler->hs[i];
+ if (hw_scene->last_scene == scene) {
+ BUG_ON(list_empty(&hw_scene->head));
+ hw_scene->last_scene = NULL;
+ }
+ }
+ spin_unlock_irqrestore(&scheduler->lock, irq_flags);
+}
+
+void psb_scheduler_takedown(struct psb_scheduler *scheduler)
+{
+ flush_scheduled_work();
+}
+
+static int psb_setup_task(struct drm_device *dev,
+ struct drm_psb_cmdbuf_arg *arg,
+ struct ttm_buffer_object *raster_cmd_buffer,
+ struct ttm_buffer_object *ta_cmd_buffer,
+ struct ttm_buffer_object *oom_cmd_buffer,
+ struct psb_scene *scene,
+ enum psb_task_type task_type,
+ uint32_t engine,
+ uint32_t flags, struct psb_task **task_p)
+{
+ struct psb_task *task;
+ int ret;
+
+ if (ta_cmd_buffer && arg->ta_size > PSB_MAX_TA_CMDS) {
+ DRM_ERROR("Too many ta cmds %d.\n", arg->ta_size);
+ return -EINVAL;
+ }
+ if (raster_cmd_buffer && arg->cmdbuf_size > PSB_MAX_RASTER_CMDS) {
+ DRM_ERROR("Too many raster cmds %d.\n", arg->cmdbuf_size);
+ return -EINVAL;
+ }
+ if (oom_cmd_buffer && arg->oom_size > PSB_MAX_OOM_CMDS) {
+ DRM_ERROR("Too many oom cmds %d.\n", arg->oom_size);
+ return -EINVAL;
+ }
+
+ task = drm_calloc(1, sizeof(*task), DRM_MEM_DRIVER);
+ if (!task)
+ return -ENOMEM;
+
+ atomic_set(&task->buf.done, 1);
+ task->engine = engine;
+ INIT_LIST_HEAD(&task->head);
+ INIT_LIST_HEAD(&task->buf.head);
+ if (ta_cmd_buffer && arg->ta_size != 0) {
+ task->ta_cmd_size = arg->ta_size;
+ ret = psb_submit_copy_cmdbuf(dev, ta_cmd_buffer,
+ arg->ta_offset,
+ arg->ta_size,
+ PSB_ENGINE_TA, task->ta_cmds);
+ if (ret)
+ goto out_err;
+ }
+ if (raster_cmd_buffer) {
+ task->raster_cmd_size = arg->cmdbuf_size;
+ ret = psb_submit_copy_cmdbuf(dev, raster_cmd_buffer,
+ arg->cmdbuf_offset,
+ arg->cmdbuf_size,
+ PSB_ENGINE_TA,
+ task->raster_cmds);
+ if (ret)
+ goto out_err;
+ }
+ if (oom_cmd_buffer && arg->oom_size != 0) {
+ task->oom_cmd_size = arg->oom_size;
+ ret = psb_submit_copy_cmdbuf(dev, oom_cmd_buffer,
+ arg->oom_offset,
+ arg->oom_size,
+ PSB_ENGINE_TA,
+ task->oom_cmds);
+ if (ret)
+ goto out_err;
+ }
+ task->task_type = task_type;
+ task->flags = flags;
+ if (scene)
+ task->scene = psb_scene_ref(scene);
+
+ *task_p = task;
+ return 0;
+out_err:
+ drm_free(task, sizeof(*task), DRM_MEM_DRIVER);
+ *task_p = NULL;
+ return ret;
+}
+
+int psb_cmdbuf_ta(struct drm_file *priv,
+ struct psb_context *context,
+ struct drm_psb_cmdbuf_arg *arg,
+ struct ttm_buffer_object *cmd_buffer,
+ struct ttm_buffer_object *ta_buffer,
+ struct ttm_buffer_object *oom_buffer,
+ struct psb_scene *scene,
+ struct psb_feedback_info *feedback,
+ struct psb_ttm_fence_rep *fence_arg)
+{
+ struct drm_device *dev = priv->minor->dev;
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ struct ttm_fence_object *fence = NULL;
+ struct psb_task *task = NULL;
+ int ret;
+ struct psb_scheduler *scheduler = &dev_priv->scheduler;
+ uint32_t sequence;
+
+ PSB_DEBUG_RENDER("Cmdbuf ta\n");
+
+ ret = psb_setup_task(dev, arg, cmd_buffer, ta_buffer,
+ oom_buffer, scene,
+ psb_ta_task, PSB_ENGINE_TA,
+ PSB_FIRE_FLAG_RASTER_DEALLOC, &task);
+
+ if (ret)
+ goto out_err;
+
+ task->feedback = *feedback;
+ mutex_lock(&dev_priv->reset_mutex);
+
+ /*
+ * Hand the task over to the scheduler.
+ */
+
+ task->sequence = psb_fence_advance_sequence(dev, PSB_ENGINE_TA);
+
+ task->ta_complete_action = PSB_RASTER;
+ task->raster_complete_action = PSB_RETURN;
+ sequence = task->sequence;
+
+ spin_lock_irq(&scheduler->lock);
+
+ list_add_tail(&task->head, &scheduler->ta_queue);
+ PSB_DEBUG_RENDER("queued ta %u\n", task->sequence);
+
+ psb_schedule_ta(dev_priv, scheduler);
+
+ /**
+ * From this point we may no longer dereference task,
+ * as the object it points to may be freed by another thread.
+ */
+
+ task = NULL;
+ spin_unlock_irq(&scheduler->lock);
+ mutex_unlock(&dev_priv->reset_mutex);
+
+ psb_fence_or_sync(priv, PSB_ENGINE_TA, context->fence_types,
+ arg->fence_flags,
+ &context->validate_list, fence_arg, &fence);
+ ttm_eu_fence_buffer_objects(&context->kern_validate_list, fence);
+
+ if (fence) {
+ spin_lock_irq(&scheduler->lock);
+ psb_report_fence(dev_priv, scheduler, PSB_ENGINE_TA,
+ sequence, _PSB_FENCE_EXE_SHIFT, 1);
+ spin_unlock_irq(&scheduler->lock);
+ fence_arg->signaled_types |= _PSB_FENCE_TYPE_EXE;
+ }
+
+out_err:
+ if (ret && ret != -ERESTART)
+ DRM_ERROR("TA task queue job failed.\n");
+
+ if (fence) {
+#ifdef PSB_WAIT_FOR_TA_COMPLETION
+ ttm_fence_object_wait(fence, 1, 1, DRM_FENCE_TYPE_EXE |
+ _PSB_FENCE_TYPE_TA_DONE);
+#ifdef PSB_BE_PARANOID
+ ttm_fence_object_wait(fence, 1, 1, DRM_FENCE_TYPE_EXE |
+ _PSB_FENCE_TYPE_SCENE_DONE);
+#endif
+#endif
+ ttm_fence_object_unref(&fence);
+ }
+ return ret;
+}
+
+int psb_cmdbuf_raster(struct drm_file *priv,
+ struct psb_context *context,
+ struct drm_psb_cmdbuf_arg *arg,
+ struct ttm_buffer_object *cmd_buffer,
+ struct psb_ttm_fence_rep *fence_arg)
+{
+ struct drm_device *dev = priv->minor->dev;
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ struct ttm_fence_object *fence = NULL;
+ struct psb_task *task = NULL;
+ int ret;
+ uint32_t sequence;
+ struct psb_scheduler *scheduler = &dev_priv->scheduler;
+
+ PSB_DEBUG_RENDER("Cmdbuf Raster\n");
+
+ ret = psb_setup_task(dev, arg, cmd_buffer, NULL, NULL,
+ NULL, psb_raster_task,
+ PSB_ENGINE_TA, 0, &task);
+
+ if (ret)
+ goto out_err;
+
+ /*
+ * Hand the task over to the scheduler.
+ */
+
+ mutex_lock(&dev_priv->reset_mutex);
+ task->sequence = psb_fence_advance_sequence(dev, PSB_ENGINE_TA);
+ task->ta_complete_action = PSB_RASTER;
+ task->raster_complete_action = PSB_RETURN;
+ sequence = task->sequence;
+
+ spin_lock_irq(&scheduler->lock);
+ list_add_tail(&task->head, &scheduler->ta_queue);
+ PSB_DEBUG_RENDER("queued raster %u\n", task->sequence);
+ psb_schedule_ta(dev_priv, scheduler);
+
+ /**
+ * From this point we may no longer dereference task,
+ * as the object it points to may be freed by another thread.
+ */
+
+ task = NULL;
+ spin_unlock_irq(&scheduler->lock);
+ mutex_unlock(&dev_priv->reset_mutex);
+
+ psb_fence_or_sync(priv, PSB_ENGINE_TA, context->fence_types,
+ arg->fence_flags,
+ &context->validate_list, fence_arg, &fence);
+
+ ttm_eu_fence_buffer_objects(&context->kern_validate_list, fence);
+ if (fence) {
+ spin_lock_irq(&scheduler->lock);
+ psb_report_fence(dev_priv, scheduler, PSB_ENGINE_TA, sequence,
+ _PSB_FENCE_EXE_SHIFT, 1);
+ spin_unlock_irq(&scheduler->lock);
+ fence_arg->signaled_types |= _PSB_FENCE_TYPE_EXE;
+ }
+out_err:
+ if (ret && ret != -ERESTART)
+ DRM_ERROR("Raster task queue job failed.\n");
+
+ if (fence) {
+#ifdef PSB_WAIT_FOR_RASTER_COMPLETION
+ ttm_fence_object_wait(fence, 1, 1, fence->type);
+#endif
+ ttm_fence_object_unref(&fence);
+ }
+
+ return ret;
+}
+
+#ifdef FIX_TG_16
+
+static int psb_check_2d_idle(struct drm_psb_private *dev_priv)
+{
+ if (psb_2d_trylock(dev_priv)) {
+ if ((PSB_RSGX32(PSB_CR_2D_SOCIF) == _PSB_C2_SOCIF_EMPTY) &&
+ !((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) &
+ _PSB_C2B_STATUS_BUSY))) {
+ return 0;
+ }
+ if (atomic_cmpxchg(&dev_priv->ta_wait_2d_irq, 0, 1) == 0)
+ psb_2D_irq_on(dev_priv);
+
+ PSB_WSGX32(PSB_2D_FENCE_BH, PSB_SGX_2D_SLAVE_PORT);
+ PSB_WSGX32(PSB_2D_FLUSH_BH, PSB_SGX_2D_SLAVE_PORT);
+ (void) PSB_RSGX32(PSB_SGX_2D_SLAVE_PORT);
+
+ psb_2d_atomic_unlock(dev_priv);
+ }
+
+ atomic_set(&dev_priv->ta_wait_2d, 1);
+ return -EBUSY;
+}
+
+static void psb_atomic_resume_ta_2d_idle(struct drm_psb_private *dev_priv)
+{
+ struct psb_scheduler *scheduler = &dev_priv->scheduler;
+
+ if (atomic_cmpxchg(&dev_priv->ta_wait_2d, 1, 0) == 1) {
+ psb_schedule_ta(dev_priv, scheduler);
+ if (atomic_read(&dev_priv->waiters_2d) != 0)
+ wake_up(&dev_priv->queue_2d);
+ }
+}
+
+void psb_resume_ta_2d_idle(struct drm_psb_private *dev_priv)
+{
+ struct psb_scheduler *scheduler = &dev_priv->scheduler;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&scheduler->lock, irq_flags);
+ if (atomic_cmpxchg(&dev_priv->ta_wait_2d_irq, 1, 0) == 1) {
+ atomic_set(&dev_priv->ta_wait_2d, 0);
+ psb_2D_irq_off(dev_priv);
+ psb_schedule_ta(dev_priv, scheduler);
+ if (atomic_read(&dev_priv->waiters_2d) != 0)
+ wake_up(&dev_priv->queue_2d);
+ }
+ spin_unlock_irqrestore(&scheduler->lock, irq_flags);
+}
+
+/*
+ * 2D locking functions. Can't use a mutex since the trylock() and
+ * unlock() methods need to be accessible from interrupt context.
+ */
+
+int psb_2d_trylock(struct drm_psb_private *dev_priv)
+{
+ return atomic_cmpxchg(&dev_priv->lock_2d, 0, 1) == 0;
+}
+
+void psb_2d_atomic_unlock(struct drm_psb_private *dev_priv)
+{
+ atomic_set(&dev_priv->lock_2d, 0);
+ if (atomic_read(&dev_priv->waiters_2d) != 0)
+ wake_up(&dev_priv->queue_2d);
+}
+
+void psb_2d_unlock(struct drm_psb_private *dev_priv)
+{
+ struct psb_scheduler *scheduler = &dev_priv->scheduler;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&scheduler->lock, irq_flags);
+ psb_2d_atomic_unlock(dev_priv);
+ if (atomic_read(&dev_priv->ta_wait_2d) != 0)
+ psb_atomic_resume_ta_2d_idle(dev_priv);
+ spin_unlock_irqrestore(&scheduler->lock, irq_flags);
+}
+
+void psb_2d_lock(struct drm_psb_private *dev_priv)
+{
+ atomic_inc(&dev_priv->waiters_2d);
+ wait_event(dev_priv->queue_2d,
+ atomic_read(&dev_priv->ta_wait_2d) == 0);
+ wait_event(dev_priv->queue_2d, psb_2d_trylock(dev_priv));
+ atomic_dec(&dev_priv->waiters_2d);
+}
+
+#endif