1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. 4 */ 5 6 #define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ 7 8 #include "msm_disp_snapshot.h" 9 10 static ssize_t __maybe_unused disp_devcoredump_read(char *buffer, loff_t offset, 11 size_t count, void *data, size_t datalen) 12 { 13 struct drm_print_iterator iter; 14 struct drm_printer p; 15 struct msm_disp_state *disp_state; 16 17 disp_state = data; 18 19 iter.data = buffer; 20 iter.offset = 0; 21 iter.start = offset; 22 iter.remain = count; 23 24 p = drm_coredump_printer(&iter); 25 26 msm_disp_state_print(disp_state, &p); 27 28 return count - iter.remain; 29 } 30 31 struct msm_disp_state * 32 msm_disp_snapshot_state_sync(struct msm_kms *kms) 33 { 34 struct drm_device *drm_dev = kms->dev; 35 struct msm_disp_state *disp_state; 36 37 WARN_ON(!mutex_is_locked(&kms->dump_mutex)); 38 39 disp_state = kzalloc(sizeof(struct msm_disp_state), GFP_KERNEL); 40 if (!disp_state) 41 return ERR_PTR(-ENOMEM); 42 43 disp_state->dev = drm_dev->dev; 44 disp_state->drm_dev = drm_dev; 45 46 INIT_LIST_HEAD(&disp_state->blocks); 47 48 msm_disp_snapshot_capture_state(disp_state); 49 50 return disp_state; 51 } 52 53 static void _msm_disp_snapshot_work(struct kthread_work *work) 54 { 55 struct msm_kms *kms = container_of(work, struct msm_kms, dump_work); 56 struct msm_disp_state *disp_state; 57 struct drm_printer p; 58 59 /* Serialize dumping here */ 60 mutex_lock(&kms->dump_mutex); 61 disp_state = msm_disp_snapshot_state_sync(kms); 62 mutex_unlock(&kms->dump_mutex); 63 64 if (IS_ERR(disp_state)) 65 return; 66 67 if (MSM_DISP_SNAPSHOT_DUMP_IN_CONSOLE) { 68 p = drm_info_printer(disp_state->drm_dev->dev); 69 msm_disp_state_print(disp_state, &p); 70 } 71 72 /* 73 * If COREDUMP is disabled, the stub will call the free function. 74 * If there is a codedump pending for the device, the dev_coredumpm() 75 * will also free new coredump state. 76 */ 77 dev_coredumpm(disp_state->dev, THIS_MODULE, disp_state, 0, GFP_KERNEL, 78 disp_devcoredump_read, msm_disp_state_free); 79 } 80 81 void msm_disp_snapshot_state(struct drm_device *drm_dev) 82 { 83 struct msm_drm_private *priv; 84 struct msm_kms *kms; 85 86 if (!drm_dev) { 87 DRM_ERROR("invalid params\n"); 88 return; 89 } 90 91 priv = drm_dev->dev_private; 92 kms = priv->kms; 93 94 kthread_queue_work(kms->dump_worker, &kms->dump_work); 95 } 96 97 int msm_disp_snapshot_init(struct drm_device *drm_dev) 98 { 99 struct msm_drm_private *priv; 100 struct msm_kms *kms; 101 102 if (!drm_dev) { 103 DRM_ERROR("invalid params\n"); 104 return -EINVAL; 105 } 106 107 priv = drm_dev->dev_private; 108 kms = priv->kms; 109 110 mutex_init(&kms->dump_mutex); 111 112 kms->dump_worker = kthread_create_worker(0, "%s", "disp_snapshot"); 113 if (IS_ERR(kms->dump_worker)) 114 DRM_ERROR("failed to create disp state task\n"); 115 116 kthread_init_work(&kms->dump_work, _msm_disp_snapshot_work); 117 118 return 0; 119 } 120 121 void msm_disp_snapshot_destroy(struct drm_device *drm_dev) 122 { 123 struct msm_kms *kms; 124 struct msm_drm_private *priv; 125 126 if (!drm_dev) { 127 DRM_ERROR("invalid params\n"); 128 return; 129 } 130 131 priv = drm_dev->dev_private; 132 kms = priv->kms; 133 134 if (kms->dump_worker) 135 kthread_destroy_worker(kms->dump_worker); 136 137 mutex_destroy(&kms->dump_mutex); 138 } 139