#define g2d_job_empty(id, job_mask) ((id & job_mask) == 0)
int g2d_device_run(struct g2d_device *g2d_dev, struct g2d_task *task);
+void g2d_hw_timeout_handler(unsigned long arg);
#endif /* __EXYNOS_G2D_H__ */
#define MODULE_NAME "exynos-g2d"
+void g2d_hw_timeout_handler(unsigned long arg)
+{
+ struct g2d_task *task = (struct g2d_task *)arg;
+ struct g2d_device *g2d_dev = task->g2d_dev;
+ unsigned long flags;
+ u32 job_state;
+
+ /* TODO: Dump of internal state of G2D */
+
+ dev_err(g2d_dev->dev, "%s: Time is up: %d msec for job %d\n",
+ __func__, G2D_HW_TIMEOUT_MSEC, task->job_id);
+
+ spin_lock_irqsave(&g2d_dev->lock_task, flags);
+
+ if (!is_task_state_active(task))
+ /*
+ * The task timed out is not currently running in H/W.
+ * It might be just finished by interrupt.
+ */
+ goto out;
+
+ job_state = g2d_hw_get_job_state(g2d_dev, task->job_id);
+ if (job_state == G2D_JOB_STATE_DONE)
+ /*
+ * The task timed out is not currently running in H/W.
+ * It will be processed in the interrupt handler.
+ */
+ goto out;
+
+ if (is_task_state_killed(task)) {
+ /* The killed task is not died in the time out priod. */
+ g2d_hw_global_reset(g2d_dev);
+
+ g2d_flush_all_tasks(g2d_dev);
+
+ dev_err(g2d_dev->dev,
+ "GLOBAL RESET: killed task not dead in %d msec.\n",
+ G2D_HW_TIMEOUT_MSEC);
+ goto out;
+ }
+
+ mod_timer(&task->hw_timer,
+ jiffies + msecs_to_jiffies(G2D_HW_TIMEOUT_MSEC));
+
+ if (job_state != G2D_JOB_STATE_RUNNING)
+ /* G2D_JOB_STATE_QUEUEING or G2D_JOB_STATE_SUSPENDING */
+ /* Time out is not caused by this task */
+ goto out;
+
+ mark_task_state_killed(task);
+
+ g2d_hw_kill_task(g2d_dev, task->job_id);
+
+out:
+ spin_unlock_irqrestore(&g2d_dev->lock_task, flags);
+}
+
int g2d_device_run(struct g2d_device *g2d_dev, struct g2d_task *task)
{
g2d_hw_push_task(g2d_dev, task);
return -1;
}
+
+void g2d_hw_kill_task(struct g2d_device *g2d_dev, unsigned int job_id)
+{
+ int retry_count = 120;
+
+ writel((0 << 4) | job_id, g2d_dev->reg + G2D_JOB_KILL_REG);
+
+ while (retry_count-- > 0) {
+ if (!(readl(g2d_dev->reg + G2D_JOB_PUSHKILL_STATE_REG) & 0x2)) {
+ dev_err(g2d_dev->dev,
+ "%s: Killed JOB %d\n", __func__, job_id);
+ return;
+ }
+ }
+
+ dev_err(g2d_dev->dev, "%s: Failed to kill job %d\n", __func__, job_id);
+}
void g2d_hw_push_task(struct g2d_device *g2d_dev, struct g2d_task *task);
int g2d_hw_get_current_task(struct g2d_device *g2d_dev);
+void g2d_hw_kill_task(struct g2d_device *g2d_dev, unsigned int job_id);
static inline u32 g2d_hw_finished_job_ids(struct g2d_device *g2d_dev)
{
{
list_del_init(&task->node);
+ del_timer(&task->hw_timer);
+
clk_disable(g2d_dev->clock);
pm_runtime_put(g2d_dev->dev);
if (!task)
return;
+ if (is_task_state_killed(task)) {
+ dev_err(g2d_dev->dev, "%s: Killed task ID %d is completed\n",
+ __func__, job_id);
+ success = false;
+ }
+
task->ktime_end = ktime_get();
g2d_finish_task(g2d_dev, task, success);
goto err_run;
}
+ mod_timer(&task->hw_timer,
+ jiffies + msecs_to_jiffies(G2D_HW_TIMEOUT_MSEC));
+
spin_unlock_irqrestore(&g2d_dev->lock_task, flags);
return;
err_run:
if (!task->cmd_page)
goto err_page;
+ setup_timer(&task->hw_timer,
+ g2d_hw_timeout_handler, (unsigned long)task);
+
/* mapping the command data */
sg_init_table(&sgl, 1);
sg_set_page(&sgl, task->cmd_page, G2D_CMD_LIST_SIZE, 0);
#include <linux/ktime.h>
#include <linux/dma-buf.h>
#include <linux/workqueue.h>
+#include <linux/timer.h>
#include "g2d_format.h"
unsigned int job_id;
unsigned long state;
struct kref starter;
+ struct timer_list hw_timer;
struct g2d_layer source[G2D_MAX_IMAGES];
struct g2d_layer target;
task->state = 0;
}
+#define is_task_state_active(task) (((task)->state & G2D_TASKSTATE_ACTIVE) != 0)
+#define is_task_state_killed(task) (((task)->state & G2D_TASKSTATE_KILLED) != 0)
+
+#define G2D_HW_TIMEOUT_MSEC 500
+
struct g2d_task *g2d_get_active_task_from_id(struct g2d_device *g2d_dev,
unsigned int id);
void g2d_destroy_tasks(struct g2d_device *g2d_dev);