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: &iter); |
25 | |
26 | msm_disp_state_print(disp_state, p: &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(size: sizeof(struct msm_disp_state), GFP_KERNEL); |
40 | if (!disp_state) |
41 | return ERR_PTR(error: -ENOMEM); |
42 | |
43 | disp_state->dev = drm_dev->dev; |
44 | disp_state->drm_dev = drm_dev; |
45 | |
46 | INIT_LIST_HEAD(list: &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(lock: &kms->dump_mutex); |
63 | |
64 | if (IS_ERR(ptr: disp_state)) |
65 | return; |
66 | |
67 | if (MSM_DISP_SNAPSHOT_DUMP_IN_CONSOLE) { |
68 | p = drm_info_printer(dev: disp_state->drm_dev->dev); |
69 | msm_disp_state_print(disp_state, p: &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(dev: disp_state->dev, THIS_MODULE, data: disp_state, datalen: 0, GFP_KERNEL, |
78 | read: disp_devcoredump_read, free: 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(worker: kms->dump_worker, work: &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(flags: 0, namefmt: "%s" , "disp_snapshot" ); |
113 | if (IS_ERR(ptr: 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(worker: kms->dump_worker); |
136 | |
137 | mutex_destroy(lock: &kms->dump_mutex); |
138 | } |
139 | |