summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJiun Yu <jiun.yu@samsung.com>2021-02-10 07:45:01 +0900
committerTreeHugger Robot <treehugger-gerrit@google.com>2021-07-17 01:48:00 +0000
commitbd27e22c0ac5cd0a1d56e0e1e63d979797e29d97 (patch)
tree745f73a87c8df94868769f128647426dc7558912
parent591915c591202b76c415ed1b48b6b5cc7298b97a (diff)
downloaddisplay-bd27e22c0ac5cd0a1d56e0e1e63d979797e29d97.tar.gz
drm: samsung: support recovery solution
Display hw including both DPU and panel can go into an abnormal state by various reasons. Therefore, recovery solution would work when it is detected. This patch only supports DPU side in recovery solution. Bug: 143292884 Signed-off-by: Jiun Yu <jiun.yu@samsung.com> Change-Id: I2186e6ce8f14c79b9f1c213a192c6c9a4105dc3a
-rw-r--r--samsung/Kbuild1
-rw-r--r--samsung/exynos_drm_debug.c40
-rw-r--r--samsung/exynos_drm_decon.c1
-rw-r--r--samsung/exynos_drm_decon.h10
-rw-r--r--samsung/exynos_drm_fb.c10
-rw-r--r--samsung/exynos_drm_recovery.c141
-rw-r--r--samsung/exynos_drm_recovery.h29
7 files changed, 228 insertions, 4 deletions
diff --git a/samsung/Kbuild b/samsung/Kbuild
index 8c63cea..fc29f00 100644
--- a/samsung/Kbuild
+++ b/samsung/Kbuild
@@ -24,6 +24,7 @@ exynos-drm-y += exynos_drm_debug.o
exynos-drm-y += exynos_drm_dqe.o
exynos-drm-y += exynos_drm_hibernation.o
exynos-drm-y += exynos_drm_partial.o
+exynos-drm-y += exynos_drm_recovery.o
exynos-drm-$(CONFIG_DRM_SAMSUNG_DECON) += exynos_drm_decon.o
exynos-drm-$(CONFIG_DRM_SAMSUNG_DPP) += exynos_drm_dpp.o
diff --git a/samsung/exynos_drm_debug.c b/samsung/exynos_drm_debug.c
index f492a79..1be4c60 100644
--- a/samsung/exynos_drm_debug.c
+++ b/samsung/exynos_drm_debug.c
@@ -1312,6 +1312,39 @@ static const struct file_operations hibernation_fops = {
.release = seq_release,
};
+static int recovery_show(struct seq_file *s, void *unused)
+{
+ struct decon_device *decon = s->private;
+
+ seq_printf(s, "%d\n", decon->recovery.count);
+
+ return 0;
+}
+
+static int recovery_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, recovery_show, inode->i_private);
+}
+
+static ssize_t recovery_write(struct file *file, const char *user_buf,
+ size_t count, loff_t *f_pos)
+{
+ struct seq_file *s = file->private_data;
+ struct decon_device *decon = s->private;
+
+ decon_trigger_recovery(decon);
+
+ return count;
+}
+
+static const struct file_operations recovery_fops = {
+ .open = recovery_open,
+ .read = seq_read,
+ .write = recovery_write,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
int dpu_init_debug(struct decon_device *decon)
{
int i;
@@ -1356,6 +1389,13 @@ int dpu_init_debug(struct decon_device *decon)
if (decon->hibernation)
debugfs_create_file("hibernation", 0664, crtc->debugfs_entry, decon,
&hibernation_fops);
+
+ if (!debugfs_create_file("recovery", 0644, crtc->debugfs_entry, decon,
+ &recovery_fops)) {
+ DRM_ERROR("failed to create debugfs recovery file\n");
+ goto err_debugfs;
+ }
+
debugfs_create_u32("underrun_cnt", 0664, crtc->debugfs_entry, &decon->d.underrun_cnt);
debugfs_create_u32("crc_cnt", 0444, crtc->debugfs_entry, &decon->d.crc_cnt);
debugfs_create_u32("ecc_cnt", 0444, crtc->debugfs_entry, &decon->d.ecc_cnt);
diff --git a/samsung/exynos_drm_decon.c b/samsung/exynos_drm_decon.c
index 522344b..bb1c99f 100644
--- a/samsung/exynos_drm_decon.c
+++ b/samsung/exynos_drm_decon.c
@@ -1715,6 +1715,7 @@ static int decon_probe(struct platform_device *pdev)
sched_setscheduler_nocheck(decon->thread, SCHED_FIFO, &param);
decon->hibernation = exynos_hibernation_register(decon);
+ exynos_recovery_register(decon);
decon->dqe = exynos_dqe_register(decon);
diff --git a/samsung/exynos_drm_decon.h b/samsung/exynos_drm_decon.h
index a94e581..55fc20e 100644
--- a/samsung/exynos_drm_decon.h
+++ b/samsung/exynos_drm_decon.h
@@ -40,6 +40,7 @@
#include "exynos_drm_fb.h"
#include "exynos_drm_hibernation.h"
+#include "exynos_drm_recovery.h"
#include "exynos_drm_writeback.h"
#include "exynos_drm_partial.h"
@@ -427,6 +428,7 @@ struct decon_device {
struct task_struct *thread;
struct kthread_worker worker;
struct kthread_work early_wakeup_work;
+ struct exynos_recovery recovery;
u32 irq_fs; /* frame start irq number*/
u32 irq_fd; /* frame done irq number*/
@@ -554,4 +556,12 @@ static inline bool is_power_on(struct drm_device *drm_dev)
return ret;
}
+static inline void decon_trigger_recovery(struct decon_device *decon)
+{
+ struct exynos_recovery *recovery = &decon->recovery;
+
+ atomic_inc(&recovery->recovering);
+ kthread_queue_work(&decon->worker, &recovery->work);
+}
+
#endif /* __EXYNOS_DRM_DECON_H__ */
diff --git a/samsung/exynos_drm_fb.c b/samsung/exynos_drm_fb.c
index c7b9392..c0801ac 100644
--- a/samsung/exynos_drm_fb.c
+++ b/samsung/exynos_drm_fb.c
@@ -555,7 +555,7 @@ static void exynos_atomic_commit_tail(struct drm_atomic_state *old_state)
for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
struct decon_mode *mode;
struct drm_crtc_commit *commit = new_crtc_state->commit;
- int fps;
+ int fps, recovering;
decon = crtc_to_decon(crtc);
@@ -572,9 +572,11 @@ static void exynos_atomic_commit_tail(struct drm_atomic_state *old_state)
DPU_ATRACE_BEGIN("wait_for_crtc_flip");
if (!wait_for_completion_timeout(&commit->flip_done, fps_timeout(fps))) {
DPU_EVENT_LOG(DPU_EVT_FRAMESTART_TIMEOUT, decon->id, NULL);
- pr_warn("decon%u framestart timeout (%d fps)\n",
- decon->id, fps);
- decon_dump_all(decon);
+ recovering = atomic_read(&decon->recovery.recovering);
+ pr_warn("decon%u framestart timeout (%d fps). recovering(%d)\n",
+ decon->id, fps, recovering);
+ if (!recovering)
+ decon_dump_all(decon);
decon_force_vblank_event(decon);
}
diff --git a/samsung/exynos_drm_recovery.c b/samsung/exynos_drm_recovery.c
new file mode 100644
index 0000000..e56b753
--- /dev/null
+++ b/samsung/exynos_drm_recovery.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* exynos_drm_recovery.c
+ *
+ * Copyright (C) 2021 Samsung Electronics Co.Ltd
+ * Authors:
+ * Jiun Yu <jiun.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 as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#define pr_fmt(fmt) "[RECOVERY] %s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/printk.h>
+#include <linux/device.h>
+#include <linux/kthread.h>
+#include <linux/export.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_device.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_atomic_uapi.h>
+#include <drm/drm_modeset_lock.h>
+#include "exynos_drm_decon.h"
+#include "exynos_drm_recovery.h"
+
+static void exynos_recovery_handler(struct kthread_work *work)
+{
+ struct exynos_recovery *recovery = container_of(work,
+ struct exynos_recovery, work);
+ struct decon_device *decon = container_of(recovery, struct decon_device,
+ recovery);
+ struct drm_device *dev = decon->drm_dev;
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_atomic_state *state, *rcv_state;
+ struct drm_crtc_state *crtc_state;
+ struct drm_crtc *crtc = &decon->crtc->base;
+ struct drm_connector *conn;
+ struct drm_connector_state *conn_state;
+ struct drm_plane *plane;
+ struct drm_plane_state *plane_state;
+ int ret, i;
+
+ pr_debug("starting recovery...\n");
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+ rcv_state = drm_atomic_helper_duplicate_state(dev, &ctx);
+ if (IS_ERR(rcv_state)) {
+ ret = PTR_ERR(rcv_state);
+ goto out_drop_locks;
+ }
+
+ state = drm_atomic_state_alloc(dev);
+ if (!state) {
+ ret = -ENOMEM;
+ goto out_drop_locks;
+ }
+
+retry:
+ state->acquire_ctx = &ctx;
+
+ crtc_state = drm_atomic_get_crtc_state(state, crtc);
+ if (IS_ERR(crtc_state)) {
+ ret = PTR_ERR(crtc_state);
+ goto out;
+ }
+
+ crtc_state->active = false;
+
+ ret = drm_atomic_set_mode_prop_for_crtc(crtc_state, NULL);
+ if (ret)
+ goto out;
+
+ ret = drm_atomic_add_affected_planes(state, crtc);
+ if (ret)
+ goto out;
+
+ ret = drm_atomic_add_affected_connectors(state, crtc);
+ if (ret)
+ goto out;
+
+ for_each_new_connector_in_state(state, conn, conn_state, i) {
+ ret = drm_atomic_set_crtc_for_connector(conn_state, NULL);
+ if (ret)
+ goto out;
+ }
+
+ for_each_new_plane_in_state(state, plane, plane_state, i) {
+ ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);
+ if (ret)
+ goto out;
+
+ drm_atomic_set_fb_for_plane(plane_state, NULL);
+ }
+
+ ret = drm_atomic_commit(state);
+ if (ret)
+ goto out;
+
+ drm_mode_config_reset(dev);
+ ret = drm_atomic_helper_commit_duplicated_state(rcv_state, &ctx);
+ if (ret)
+ goto out;
+
+ recovery->count++;
+ pr_debug("recovery is successfully finished(%d)\n", recovery->count);
+
+out:
+ if (ret == -EDEADLK) {
+ drm_atomic_state_clear(state);
+ drm_atomic_state_clear(rcv_state);
+ ret = drm_modeset_backoff(&ctx);
+ if (!ret)
+ goto retry;
+ }
+
+ drm_atomic_state_put(state);
+ drm_atomic_state_put(rcv_state);
+
+out_drop_locks:
+ atomic_set(&recovery->recovering, 0);
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+}
+
+void exynos_recovery_register(struct decon_device *decon)
+{
+ struct exynos_recovery *recovery = &decon->recovery;
+
+ kthread_init_work(&recovery->work, exynos_recovery_handler);
+ recovery->count = 0;
+ atomic_set(&recovery->recovering, 0);
+
+ pr_info("ESD recovery is supported\n");
+}
+EXPORT_SYMBOL(exynos_recovery_register);
diff --git a/samsung/exynos_drm_recovery.h b/samsung/exynos_drm_recovery.h
new file mode 100644
index 0000000..6d27b2c
--- /dev/null
+++ b/samsung/exynos_drm_recovery.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * linux/drivers/gpu/drm/samsung/exynos_drm_recovery.h
+ *
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Headef file for recovery Feature.
+ *
+ * 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.
+ */
+
+#ifndef __EXYNOS_DRM_RECOVERY__
+#define __EXYNOS_DRM_RECOVERY__
+
+#include <linux/kthread.h>
+
+struct decon_device;
+struct exynos_recovery {
+ struct kthread_work work;
+ int count;
+ atomic_t recovering;
+};
+
+void exynos_recovery_register(struct decon_device *decon);
+
+#endif /* __EXYNOS_DRM_RECOVERY__ */