// 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); }