--- /dev/null
+/*
+ * linux/drivers/gpu/exynos/g2d/g2d_debug.c
+ *
+ * Copyright (C) 2017 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+
+#include "g2d.h"
+#include "g2d_task.h"
+#include "g2d_uapi.h"
+#include "g2d_debug.h"
+
+static unsigned int g2d_debug;
+
+#define G2D_MAX_STAMP_SIZE 1024
+
+static struct g2d_stamp {
+ ktime_t time;
+ struct g2d_task *task;
+ u32 state;
+ u32 job_id;
+ u32 val;
+ u8 cpu;
+} g2d_stamp_list[G2D_MAX_STAMP_SIZE];
+
+static atomic_t p_stamp;
+
+static int g2d_stamp_show(struct seq_file *s, void *unused)
+{
+ int ptr = atomic_read(&p_stamp);
+ struct g2d_stamp *stamp;
+ int i;
+
+ if (ptr < 0)
+ return 0;
+
+ /* in chronological order */
+ ptr = (ptr + 1) & (G2D_MAX_STAMP_SIZE - 1);
+ i = ptr;
+
+ while (1) {
+ stamp = &g2d_stamp_list[i];
+
+ seq_printf(s, "[%d] %u:%u@%u (0x%x) %06llu\n", i++,
+ stamp->cpu, stamp->job_id, stamp->val, stamp->state,
+ ktime_to_us(stamp->time));
+
+ i &= (G2D_MAX_STAMP_SIZE - 1);
+
+ if (i == ptr)
+ break;
+ }
+
+ return 0;
+}
+
+static int g2d_debug_logs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, g2d_stamp_show, inode->i_private);
+}
+
+static const struct file_operations g2d_debug_logs_fops = {
+ .open = g2d_debug_logs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+void g2d_init_debug(struct g2d_device *g2d_dev)
+{
+ atomic_set(&p_stamp, -1);
+
+ g2d_dev->debug_root = debugfs_create_dir("g2d", NULL);
+ if (!g2d_dev->debug_root) {
+ dev_err(g2d_dev->dev, "debugfs : failed to create root directory\n");
+ return;
+ }
+
+ g2d_dev->debug = debugfs_create_u32("debug",
+ 0644, g2d_dev->debug_root, &g2d_debug);
+ if (!g2d_dev->debug) {
+ dev_err(g2d_dev->dev, "debugfs : failed to create debug file\n");
+ return;
+ }
+
+ g2d_dev->debug_logs = debugfs_create_file("logs",
+ 0444, g2d_dev->debug_root, g2d_dev, &g2d_debug_logs_fops);
+ if (!g2d_dev->debug_logs) {
+ dev_err(g2d_dev->dev, "debugfs : failed to create debug logs file\n");
+ return;
+ }
+}
+
+void g2d_destroy_debug(struct g2d_device *g2d_dev)
+{
+ debugfs_remove_recursive(g2d_dev->debug_root);
+}
+
+static struct regs_info g2d_reg_info[] = {
+ /* Start, Size, Name */
+ { 0x0, 0x20, "General" },
+ { 0x34, 0x10, "Secure Layer" },
+ { 0xF0, 0x10, "AFBC debugging" },
+ { 0x80, 0x70, "Job manager" },
+ { 0x8000, 0x100, "HW flow control" },
+ { 0x2000, 0x120, "Layer CSC Coefficient" },
+ { 0x120, 0xE0, "Destination" },
+ { 0x200, 0x100, "Layer0" },
+ { 0x300, 0x100, "Layer1" },
+ { 0x400, 0x100, "Layer2" },
+ { 0x500, 0x100, "Layer3" },
+ { 0x600, 0x100, "Layer4" },
+ { 0x700, 0x100, "Layer5" },
+ { 0x800, 0x100, "Layer6" },
+ { 0x900, 0x100, "Layer7" },
+ { 0xA00, 0x100, "Layer8" },
+ { 0xB00, 0x100, "Layer9" },
+ { 0xC00, 0x100, "Layer10" },
+ { 0xD00, 0x100, "Layer11" },
+ { 0xE00, 0x100, "Layer12" },
+ { 0xF00, 0x100, "Layer13" },
+ { 0x1000, 0x100, "Layer14" },
+ { 0x1100, 0x100, "Layer15" },
+};
+
+static void g2d_dump_task(struct g2d_task *task)
+{
+ struct g2d_device *g2d_dev = task->g2d_dev;
+ unsigned int i, num_array;
+ struct g2d_reg *regs;
+
+ num_array = ARRAY_SIZE(g2d_reg_info) - G2D_MAX_IMAGES + task->num_source;
+
+ for (i = 0; i < num_array; i++) {
+ pr_info("[%s: %04X .. %04X]\n",
+ g2d_reg_info[i].name, g2d_reg_info[i].start,
+ g2d_reg_info[i].start + g2d_reg_info[i].size);
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4,
+ g2d_dev->reg + g2d_reg_info[i].start,
+ g2d_reg_info[i].size, false);
+ }
+
+ regs = page_address(task->cmd_page);
+
+ for (i = 0; i < task->cmd_count; i++)
+ pr_info("G2D: CMD[%03d] %#06x, %#010x\n",
+ i, regs[i].offset, regs[i].value);
+}
+
+void g2d_stamp_task(struct g2d_task *task, u32 val)
+{
+ int ptr = atomic_inc_return(&p_stamp) & (G2D_MAX_STAMP_SIZE - 1);
+ struct g2d_stamp *stamp = &g2d_stamp_list[ptr];
+
+ if (task) {
+ stamp->state = task->state;
+ stamp->job_id = task->job_id;
+ stamp->task = task;
+ } else {
+ stamp->task = NULL;
+ }
+
+ stamp->time = ktime_get();
+ stamp->val = val;
+ stamp->cpu = smp_processor_id();
+
+ /* when error status, dump the task */
+ if ((stamp->val == G2D_STAMP_STATE_TIMEOUT_HW) ||
+ (stamp->val == G2D_STAMP_STATE_ERR_INT) ||
+ (stamp->val == G2D_STAMP_STATE_MMUFAULT))
+ g2d_dump_task(task);
+
+ if (stamp->val == G2D_STAMP_STATE_DONE) {
+ if (g2d_debug == 1) {
+ pr_info("Job #%x took %06llu to H/W process\n",
+ task->job_id, ktime_us_delta(task->ktime_end, task->ktime_begin));
+ } else if (g2d_debug == 2) {
+ g2d_dump_task(task);
+ }
+ }
+}
--- /dev/null
+/*
+ * linux/drivers/gpu/exynos/g2d/g2d_debug.h
+ *
+ * Copyright (C) 2017 Samsung Electronics Co., Ltd.
+ *
+ * Contact: Hyesoo Yu <hyesoo.yu@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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.
+ */
+
+#ifndef __EXYNOS_G2D_DEBUG_H_
+#define __EXYNOS_G2D_DEBUG_H_
+
+struct regs_info {
+ int start;
+ int size;
+ const char *name;
+};
+
+enum g2d_stamp_id {
+ G2D_STAMP_STATE_ALLOC,
+ G2D_STAMP_STATE_BEGIN,
+ G2D_STAMP_STATE_PM_RESUME,
+ G2D_STAMP_STATE_PUSH,
+ G2D_STAMP_STATE_DONE,
+ G2D_STAMP_STATE_PM_SUSPEND,
+ G2D_STAMP_STATE_FREE,
+ G2D_STAMP_STATE_TIMEOUT_FENCE,
+ G2D_STAMP_STATE_TIMEOUT_HW,
+ G2D_STAMP_STATE_ERR_INT,
+ G2D_STAMP_STATE_MMUFAULT,
+ G2D_STAMP_STATE_SHUTDOWN_S,
+ G2D_STAMP_STATE_SHUTDOWN_E,
+ G2D_STAMP_STATE_SUSPEND_S,
+ G2D_STAMP_STATE_SUSPEND_E,
+ G2D_STAMP_STATE_RESUME_S,
+ G2D_STAMP_STATE_RESUME_E,
+};
+
+void g2d_init_debug(struct g2d_device *dev);
+void g2d_destroy_debug(struct g2d_device *dev);
+void g2d_stamp_task(struct g2d_task *task, u32 val);
+#endif /* __EXYNOS_G2D_HELPER_H_ */
#include "g2d_regs.h"
#include "g2d_task.h"
#include "g2d_uapi_process.h"
+#include "g2d_debug.h"
#define MODULE_NAME "exynos-g2d"
/* Time out is not caused by this task */
goto out;
+ g2d_stamp_task(task, G2D_STAMP_STATE_TIMEOUT_HW);
+
mark_task_state_killed(task);
g2d_hw_kill_task(g2d_dev, task->job_id);
{
g2d_hw_push_task(g2d_dev, task);
+ g2d_stamp_task(task, G2D_STAMP_STATE_PUSH);
+
return 0;
}
"%s: Error occurred during running job %d\n",
__func__, job_id);
- g2d_dump_task(g2d_dev, job_id);
+ g2d_stamp_task(task, G2D_STAMP_STATE_ERR_INT);
}
g2d_flush_all_tasks(g2d_dev);
int fault_flags, void *token)
{
struct g2d_device *g2d_dev = token;
+ struct g2d_task *task;
int job_id = g2d_hw_get_current_task(g2d_dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&g2d_dev->lock_task, flags);
+ task = g2d_get_active_task_from_id(g2d_dev, job_id);
+ spin_unlock_irqrestore(&g2d_dev->lock_task, flags);
- g2d_dump_task(g2d_dev, job_id);
+ g2d_stamp_task(task, G2D_STAMP_STATE_MMUFAULT);
return 0;
}
break;
}
+ g2d_stamp_task(task, G2D_STAMP_STATE_BEGIN);
+
g2d_start_task(task);
if (!(task->flags & G2D_FLAG_NONBLOCK))
dev_info(&pdev->dev, "Probed FIMG2D version %#010x\n", version);
+ g2d_init_debug(g2d_dev);
+
return 0;
err_pm:
g2d_destroy_tasks(g2d_dev);
{
struct g2d_device *g2d_dev = platform_get_drvdata(pdev);
+ g2d_stamp_task(NULL, G2D_STAMP_STATE_SHUTDOWN_S);
g2d_prepare_suspend(g2d_dev);
wait_event(g2d_dev->freeze_wait, list_empty(&g2d_dev->tasks_active));
if (test_and_set_bit(G2D_DEVICE_STATE_IOVMM_DISABLED, &g2d_dev->state))
iovmm_deactivate(g2d_dev->dev);
+
+ g2d_stamp_task(NULL, G2D_STAMP_STATE_SHUTDOWN_E);
}
static int g2d_remove(struct platform_device *pdev)
{
struct g2d_device *g2d_dev = platform_get_drvdata(pdev);
+ g2d_destroy_debug(g2d_dev);
+
g2d_shutdown(pdev);
g2d_destroy_tasks(g2d_dev);
#ifdef CONFIG_PM
static int g2d_runtime_resume(struct device *dev)
{
+ g2d_stamp_task(NULL, G2D_STAMP_STATE_PM_RESUME);
+
return 0;
}
struct g2d_device *g2d_dev = dev_get_drvdata(dev);
clk_unprepare(g2d_dev->clock);
+ g2d_stamp_task(NULL, G2D_STAMP_STATE_PM_SUSPEND);
return 0;
}
#include "g2d_uapi_process.h"
#include "g2d_command.h"
#include "g2d_fence.h"
+#include "g2d_debug.h"
#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION
#include <linux/smc.h>
{
list_del_init(&task->node);
+ g2d_stamp_task(task, G2D_STAMP_STATE_DONE);
+
del_timer(&task->timer);
g2d_secure_disable();
set_bit(G2D_DEVICE_STATE_SUSPEND, &g2d_dev->state);
spin_unlock_irq(&g2d_dev->lock_task);
+ g2d_stamp_task(NULL, G2D_STAMP_STATE_SUSPEND_S);
wait_event(g2d_dev->freeze_wait, list_empty(&g2d_dev->tasks_active));
+ g2d_stamp_task(NULL, G2D_STAMP_STATE_SUSPEND_E);
}
void g2d_suspend_finish(struct g2d_device *g2d_dev)
spin_lock_irq(&g2d_dev->lock_task);
+ g2d_stamp_task(NULL, G2D_STAMP_STATE_RESUME_S);
clear_bit(G2D_DEVICE_STATE_SUSPEND, &g2d_dev->state);
while (!list_empty(&g2d_dev->tasks_prepared)) {
}
spin_unlock_irq(&g2d_dev->lock_task);
+ g2d_stamp_task(NULL, G2D_STAMP_STATE_RESUME_E);
}
static void g2d_schedule_task(struct g2d_task *task)
g2d_init_commands(task);
+ g2d_stamp_task(task, G2D_STAMP_STATE_ALLOC);
+
spin_unlock_irqrestore(&g2d_dev->lock_task, flags);
return task;
list_add(&task->node, &g2d_dev->tasks_free);
+ g2d_stamp_task(task, G2D_STAMP_STATE_FREE);
+
spin_unlock_irqrestore(&g2d_dev->lock_task, flags);
}
return 0;
}
-
-void g2d_dump_task(struct g2d_device *g2d_dev, unsigned int job_id)
-{
- struct g2d_task *task;
- unsigned long flags;
- unsigned int i;
- struct g2d_reg *regs;
-
- spin_lock_irqsave(&g2d_dev->lock_task, flags);
-
- list_for_each_entry(task, &g2d_dev->tasks_active, node) {
- if (task->job_id == job_id)
- break;
- }
-
- /* TODO: more dump task */
-
- regs = page_address(task->cmd_page);
-
- for (i = 0; i < task->cmd_count; i++)
- pr_info("G2D: CMD[%03d] %#06x, %#010x\n",
- i, regs[i].offset, regs[i].value);
-
- spin_unlock_irqrestore(&g2d_dev->lock_task, flags);
-}