diff options
Diffstat (limited to 'drivers/gpu/drm/msm/disp/msm_disp_snapshot.c')
-rw-r--r-- | drivers/gpu/drm/msm/disp/msm_disp_snapshot.c | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/drivers/gpu/drm/msm/disp/msm_disp_snapshot.c b/drivers/gpu/drm/msm/disp/msm_disp_snapshot.c new file mode 100644 index 000000000..e75b97127 --- /dev/null +++ b/drivers/gpu/drm/msm/disp/msm_disp_snapshot.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + */ + +#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ + +#include "msm_disp_snapshot.h" + +static ssize_t __maybe_unused disp_devcoredump_read(char *buffer, loff_t offset, + size_t count, void *data, size_t datalen) +{ + struct drm_print_iterator iter; + struct drm_printer p; + struct msm_disp_state *disp_state; + + disp_state = data; + + iter.data = buffer; + iter.offset = 0; + iter.start = offset; + iter.remain = count; + + p = drm_coredump_printer(&iter); + + msm_disp_state_print(disp_state, &p); + + return count - iter.remain; +} + +struct msm_disp_state * +msm_disp_snapshot_state_sync(struct msm_kms *kms) +{ + struct drm_device *drm_dev = kms->dev; + struct msm_disp_state *disp_state; + + WARN_ON(!mutex_is_locked(&kms->dump_mutex)); + + disp_state = kzalloc(sizeof(struct msm_disp_state), GFP_KERNEL); + if (!disp_state) + return ERR_PTR(-ENOMEM); + + disp_state->dev = drm_dev->dev; + disp_state->drm_dev = drm_dev; + + INIT_LIST_HEAD(&disp_state->blocks); + + msm_disp_snapshot_capture_state(disp_state); + + return disp_state; +} + +static void _msm_disp_snapshot_work(struct kthread_work *work) +{ + struct msm_kms *kms = container_of(work, struct msm_kms, dump_work); + struct msm_disp_state *disp_state; + struct drm_printer p; + + /* Serialize dumping here */ + mutex_lock(&kms->dump_mutex); + disp_state = msm_disp_snapshot_state_sync(kms); + mutex_unlock(&kms->dump_mutex); + + if (IS_ERR(disp_state)) + return; + + if (MSM_DISP_SNAPSHOT_DUMP_IN_CONSOLE) { + p = drm_info_printer(disp_state->drm_dev->dev); + msm_disp_state_print(disp_state, &p); + } + + /* + * If COREDUMP is disabled, the stub will call the free function. + * If there is a codedump pending for the device, the dev_coredumpm() + * will also free new coredump state. + */ + dev_coredumpm(disp_state->dev, THIS_MODULE, disp_state, 0, GFP_KERNEL, + disp_devcoredump_read, msm_disp_state_free); +} + +void msm_disp_snapshot_state(struct drm_device *drm_dev) +{ + struct msm_drm_private *priv; + struct msm_kms *kms; + + if (!drm_dev) { + DRM_ERROR("invalid params\n"); + return; + } + + priv = drm_dev->dev_private; + kms = priv->kms; + + kthread_queue_work(kms->dump_worker, &kms->dump_work); +} + +int msm_disp_snapshot_init(struct drm_device *drm_dev) +{ + struct msm_drm_private *priv; + struct msm_kms *kms; + + if (!drm_dev) { + DRM_ERROR("invalid params\n"); + return -EINVAL; + } + + priv = drm_dev->dev_private; + kms = priv->kms; + + mutex_init(&kms->dump_mutex); + + kms->dump_worker = kthread_create_worker(0, "%s", "disp_snapshot"); + if (IS_ERR(kms->dump_worker)) + DRM_ERROR("failed to create disp state task\n"); + + kthread_init_work(&kms->dump_work, _msm_disp_snapshot_work); + + return 0; +} + +void msm_disp_snapshot_destroy(struct drm_device *drm_dev) +{ + struct msm_kms *kms; + struct msm_drm_private *priv; + + if (!drm_dev) { + DRM_ERROR("invalid params\n"); + return; + } + + priv = drm_dev->dev_private; + kms = priv->kms; + + if (kms->dump_worker) + kthread_destroy_worker(kms->dump_worker); + + mutex_destroy(&kms->dump_mutex); +} |