blob: f9dd522781692fb469dc6002e0bbe0311c194129 [file] [log] [blame]
From b28d1f89d8978c568e6a08e020adbe650af07f8a Mon Sep 17 00:00:00 2001
From: Rob Clark <robdclark@chromium.org>
Date: Wed, 27 Jan 2021 12:42:00 -0800
Subject: [PATCH] CHROMIUM: drm/msm: Boost/wake gpu in response to input events
Pipeline the GPU "boot up" sequence with userspace handling of input
events.
The long-term solution should be a userspace component triggering
CPU/GPU boost. In particular, the kernel doesn't properly know which
keys to boost on keypress vs keyrelease. (See b/178726466, b/165621854)
BUG=b:193221885
TEST=test latency from window switcher keypress to first frame of
animation
Change-Id: Ic8724139b480acaf5618c1caddb12f59b7db627c
Signed-off-by: Rob Clark <robdclark@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/2654074
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Commit-Queue: Douglas Anderson <dianders@chromium.org>
(cherry picked from commit 0e4c14d5dca879e2b7e335e5d7f3c43dcf585858)
Signed-off-by: Matt Turner <msturner@google.com>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/3564090
[rebase61(tzungbi):
Squashed:
FIXUP: CHROMIUM: drm/msm: Boost/wake gpu in response to input events
]
Signed-off-by: Tzung-Bi Shih <tzungbi@chromium.org>
---
drivers/gpu/drm/msm/Kconfig | 1 +
drivers/gpu/drm/msm/msm_gpu.c | 149 ++++++++++++++++++++++++++++
drivers/gpu/drm/msm/msm_gpu.h | 5 +
drivers/gpu/drm/msm/msm_gpu_trace.h | 17 ++++
4 files changed, 172 insertions(+)
diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
index 85f5ab1d552c4caf3cd2b50b5c768a8439fb89c7..32ae301607fd3533ace3329c08b441bd979d4b78 100644
--- a/drivers/gpu/drm/msm/Kconfig
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -10,6 +10,7 @@ config DRM_MSM
depends on QCOM_LLCC || QCOM_LLCC=n
depends on QCOM_COMMAND_DB || QCOM_COMMAND_DB=n
depends on PM
+ depends on INPUT
select IOMMU_IO_PGTABLE
select QCOM_MDT_LOADER if ARCH_QCOM
select REGULATOR
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index 26ebda40be4f0c93574a9a21fef9be1c25d0a425..725ce93d351b7bf5de2747e1476d2d00488605d5 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -18,6 +18,150 @@
#include <linux/devcoredump.h>
#include <linux/sched/task.h>
+/*
+ * gpu-boost, get notified of input events to get a head start on booting
+ * up the GPU
+ */
+
+static bool gpuboost = true;
+MODULE_PARM_DESC(gpuboost, "Enable GPU boost");
+module_param(gpuboost, bool, 0600);
+
+static void boost_worker(struct kthread_work *work)
+{
+ struct msm_gpu *gpu = container_of(work, struct msm_gpu, boost_work);
+
+ pm_runtime_get_sync(&gpu->pdev->dev);
+ pm_runtime_mark_last_busy(&gpu->pdev->dev);
+ pm_runtime_put_autosuspend(&gpu->pdev->dev);
+}
+
+static void msm_gpuboost_input_event(struct input_handle *handle,
+ unsigned int type, unsigned int code,
+ int value)
+{
+ struct input_handler *handler = handle->handler;
+ struct msm_gpu *gpu = container_of(handler, struct msm_gpu, boost_handler);
+ int ret;
+
+ if (!gpuboost)
+ return;
+
+ /* This is something we can do from irq context, to avoid scheduling
+ * a worker if the GPU is already ticking
+ */
+ ret = pm_runtime_get_if_in_use(&gpu->pdev->dev);
+ if (ret == 0) {
+ trace_msm_gpu_boost(type, code, value);
+ kthread_queue_work(gpu->worker, &gpu->boost_work);
+ return;
+ }
+
+ pm_runtime_mark_last_busy(&gpu->pdev->dev);
+ pm_runtime_put_autosuspend(&gpu->pdev->dev);
+}
+
+static int msm_gpuboost_input_connect(struct input_handler *handler,
+ struct input_dev *dev,
+ const struct input_device_id *id)
+{
+ struct input_handle *handle;
+ int error;
+
+ handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
+ if (!handle)
+ return -ENOMEM;
+
+ handle->dev = dev;
+ handle->handler = handler;
+ handle->name = "msm-gpu-boost";
+
+ error = input_register_handle(handle);
+ if (error)
+ goto err2;
+
+ error = input_open_device(handle);
+ if (error)
+ goto err1;
+
+ return 0;
+
+err1:
+ input_unregister_handle(handle);
+err2:
+ kfree(handle);
+ return error;
+}
+
+static void msm_gpuboost_input_disconnect(struct input_handle *handle)
+{
+ input_close_device(handle);
+ input_unregister_handle(handle);
+ kfree(handle);
+}
+
+static const struct input_device_id msm_gpuboost_ids[] = {
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+ INPUT_DEVICE_ID_MATCH_ABSBIT,
+ .evbit = { BIT_MASK(EV_ABS) },
+ .absbit = { [BIT_WORD(ABS_MT_POSITION_X)] =
+ BIT_MASK(ABS_MT_POSITION_X) |
+ BIT_MASK(ABS_MT_POSITION_Y) },
+ }, /* multi-touch touchscreen */
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+ .evbit = { BIT_MASK(EV_ABS) },
+ .absbit = { [BIT_WORD(ABS_X)] = BIT_MASK(ABS_X) }
+
+ }, /* stylus or joystick device */
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+ .evbit = { BIT_MASK(EV_KEY) },
+ .keybit = { [BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) },
+ }, /* pointer (e.g. trackpad, mouse) */
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+ .evbit = { BIT_MASK(EV_KEY) },
+ .keybit = { [BIT_WORD(KEY_ESC)] = BIT_MASK(KEY_ESC) },
+ }, /* keyboard */
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+ INPUT_DEVICE_ID_MATCH_KEYBIT,
+ .evbit = { BIT_MASK(EV_KEY) },
+ .keybit = {[BIT_WORD(BTN_JOYSTICK)] = BIT_MASK(BTN_JOYSTICK) },
+ }, /* joysticks not caught by ABS_X above */
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+ INPUT_DEVICE_ID_MATCH_KEYBIT,
+ .evbit = { BIT_MASK(EV_KEY) },
+ .keybit = { [BIT_WORD(BTN_GAMEPAD)] = BIT_MASK(BTN_GAMEPAD) },
+ }, /* gamepad */
+ { },
+};
+
+static void msm_gpuboost_init(struct msm_gpu *gpu)
+{
+ struct input_handler *handler = &gpu->boost_handler;
+ int ret;
+
+ handler->event = msm_gpuboost_input_event;
+ handler->connect = msm_gpuboost_input_connect;
+ handler->disconnect = msm_gpuboost_input_disconnect;
+ handler->name = "msm-gpu-boost";
+ handler->id_table = msm_gpuboost_ids;
+
+ ret = input_register_handler(handler);
+ if (ret) {
+ DRM_DEV_ERROR(gpu->dev->dev, "failed to register input handler: %d\n", ret);
+ }
+}
+
+static void msm_gpuboost_cleanup(struct msm_gpu *gpu)
+{
+ input_unregister_handler(&gpu->boost_handler);
+}
+
/*
* Power Management:
*/
@@ -873,6 +1017,7 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
mutex_init(&gpu->active_lock);
mutex_init(&gpu->lock);
init_waitqueue_head(&gpu->retire_event);
+ kthread_init_work(&gpu->boost_work, boost_worker);
kthread_init_work(&gpu->retire_work, retire_worker);
kthread_init_work(&gpu->recover_work, recover_worker);
kthread_init_work(&gpu->fault_work, fault_worker);
@@ -987,6 +1132,8 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
refcount_set(&gpu->sysprof_active, 1);
+ msm_gpuboost_init(gpu);
+
return 0;
fail:
@@ -1007,6 +1154,8 @@ void msm_gpu_cleanup(struct msm_gpu *gpu)
DBG("%s", gpu->name);
+ msm_gpuboost_cleanup(gpu);
+
for (i = 0; i < ARRAY_SIZE(gpu->rb); i++) {
msm_ringbuffer_destroy(gpu->rb[i]);
gpu->rb[i] = NULL;
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index 7a4fa1b8655b38a80c39a236b62adadfbf497e52..4404b270eb5810595689cba6a0caad9d852d3c88 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -10,6 +10,7 @@
#include <linux/adreno-smmu-priv.h>
#include <linux/clk.h>
#include <linux/devfreq.h>
+#include <linux/input.h>
#include <linux/interconnect.h>
#include <linux/pm_opp.h>
#include <linux/regulator/consumer.h>
@@ -256,6 +257,10 @@ struct msm_gpu {
#define DRM_MSM_HANGCHECK_PROGRESS_RETRIES 3
struct timer_list hangcheck_timer;
+ /* work for waking GPU on input: */
+ struct kthread_work boost_work;
+ struct input_handler boost_handler;
+
/* Fault info for most recent iova fault: */
struct msm_gpu_fault_info fault_info;
diff --git a/drivers/gpu/drm/msm/msm_gpu_trace.h b/drivers/gpu/drm/msm/msm_gpu_trace.h
index ac40d857bc4578377b03b4cedd138c87144997e4..c6032215ae505fc2ca1a4f94d1cd80a22a0a3229 100644
--- a/drivers/gpu/drm/msm/msm_gpu_trace.h
+++ b/drivers/gpu/drm/msm/msm_gpu_trace.h
@@ -177,6 +177,23 @@ TRACE_EVENT(msm_gpu_resume,
TP_printk("%u", __entry->dummy)
);
+
+TRACE_EVENT(msm_gpu_boost,
+ TP_PROTO(unsigned int type, unsigned int code, int value),
+ TP_ARGS(type, code, value),
+ TP_STRUCT__entry(
+ __field(u32, type)
+ __field(u32, code)
+ __field(int, value)
+ ),
+ TP_fast_assign(
+ __entry->type = type;
+ __entry->code = code;
+ __entry->value = value;
+ ),
+ TP_printk("type=%u, code=%u, value=%d", __entry->type, __entry->code, __entry->value)
+);
+
#endif
#undef TRACE_INCLUDE_PATH
--
2.38.3