1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. |
4 | */ |
5 | |
6 | #define pr_fmt(fmt)"[drm-dp] %s: " fmt, __func__ |
7 | |
8 | #include <linux/debugfs.h> |
9 | #include <drm/drm_connector.h> |
10 | #include <drm/drm_file.h> |
11 | |
12 | #include "dp_catalog.h" |
13 | #include "dp_aux.h" |
14 | #include "dp_ctrl.h" |
15 | #include "dp_debug.h" |
16 | #include "dp_display.h" |
17 | |
18 | #define DEBUG_NAME "msm_dp" |
19 | |
20 | struct dp_debug_private { |
21 | struct dp_link *link; |
22 | struct dp_panel *panel; |
23 | struct drm_connector *connector; |
24 | |
25 | struct dp_debug dp_debug; |
26 | }; |
27 | |
28 | static int dp_debug_show(struct seq_file *seq, void *p) |
29 | { |
30 | struct dp_debug_private *debug = seq->private; |
31 | u64 lclk = 0; |
32 | u32 link_params_rate; |
33 | const struct drm_display_mode *drm_mode; |
34 | |
35 | if (!debug) |
36 | return -ENODEV; |
37 | |
38 | drm_mode = &debug->panel->dp_mode.drm_mode; |
39 | |
40 | seq_printf(m: seq, fmt: "\tname = %s\n" , DEBUG_NAME); |
41 | seq_printf(m: seq, fmt: "\tdrm_dp_link\n\t\trate = %u\n" , |
42 | debug->panel->link_info.rate); |
43 | seq_printf(m: seq, fmt: "\t\tnum_lanes = %u\n" , |
44 | debug->panel->link_info.num_lanes); |
45 | seq_printf(m: seq, fmt: "\t\tcapabilities = %lu\n" , |
46 | debug->panel->link_info.capabilities); |
47 | seq_printf(m: seq, fmt: "\tdp_panel_info:\n\t\tactive = %dx%d\n" , |
48 | drm_mode->hdisplay, |
49 | drm_mode->vdisplay); |
50 | seq_printf(m: seq, fmt: "\t\tback_porch = %dx%d\n" , |
51 | drm_mode->htotal - drm_mode->hsync_end, |
52 | drm_mode->vtotal - drm_mode->vsync_end); |
53 | seq_printf(m: seq, fmt: "\t\tfront_porch = %dx%d\n" , |
54 | drm_mode->hsync_start - drm_mode->hdisplay, |
55 | drm_mode->vsync_start - drm_mode->vdisplay); |
56 | seq_printf(m: seq, fmt: "\t\tsync_width = %dx%d\n" , |
57 | drm_mode->hsync_end - drm_mode->hsync_start, |
58 | drm_mode->vsync_end - drm_mode->vsync_start); |
59 | seq_printf(m: seq, fmt: "\t\tactive_low = %dx%d\n" , |
60 | debug->panel->dp_mode.h_active_low, |
61 | debug->panel->dp_mode.v_active_low); |
62 | seq_printf(m: seq, fmt: "\t\th_skew = %d\n" , |
63 | drm_mode->hskew); |
64 | seq_printf(m: seq, fmt: "\t\trefresh rate = %d\n" , |
65 | drm_mode_vrefresh(mode: drm_mode)); |
66 | seq_printf(m: seq, fmt: "\t\tpixel clock khz = %d\n" , |
67 | drm_mode->clock); |
68 | seq_printf(m: seq, fmt: "\t\tbpp = %d\n" , |
69 | debug->panel->dp_mode.bpp); |
70 | |
71 | /* Link Information */ |
72 | seq_printf(m: seq, fmt: "\tdp_link:\n\t\ttest_requested = %d\n" , |
73 | debug->link->sink_request); |
74 | seq_printf(m: seq, fmt: "\t\tnum_lanes = %d\n" , |
75 | debug->link->link_params.num_lanes); |
76 | link_params_rate = debug->link->link_params.rate; |
77 | seq_printf(m: seq, fmt: "\t\tbw_code = %d\n" , |
78 | drm_dp_link_rate_to_bw_code(link_rate: link_params_rate)); |
79 | lclk = debug->link->link_params.rate * 1000; |
80 | seq_printf(m: seq, fmt: "\t\tlclk = %lld\n" , lclk); |
81 | seq_printf(m: seq, fmt: "\t\tv_level = %d\n" , |
82 | debug->link->phy_params.v_level); |
83 | seq_printf(m: seq, fmt: "\t\tp_level = %d\n" , |
84 | debug->link->phy_params.p_level); |
85 | |
86 | return 0; |
87 | } |
88 | DEFINE_SHOW_ATTRIBUTE(dp_debug); |
89 | |
90 | static int dp_test_data_show(struct seq_file *m, void *data) |
91 | { |
92 | const struct dp_debug_private *debug = m->private; |
93 | const struct drm_connector *connector = debug->connector; |
94 | u32 bpc; |
95 | |
96 | if (connector->status == connector_status_connected) { |
97 | bpc = debug->link->test_video.test_bit_depth; |
98 | seq_printf(m, fmt: "hdisplay: %d\n" , |
99 | debug->link->test_video.test_h_width); |
100 | seq_printf(m, fmt: "vdisplay: %d\n" , |
101 | debug->link->test_video.test_v_height); |
102 | seq_printf(m, fmt: "bpc: %u\n" , |
103 | dp_link_bit_depth_to_bpp(tbd: bpc) / 3); |
104 | } else { |
105 | seq_puts(m, s: "0" ); |
106 | } |
107 | |
108 | return 0; |
109 | } |
110 | DEFINE_SHOW_ATTRIBUTE(dp_test_data); |
111 | |
112 | static int dp_test_type_show(struct seq_file *m, void *data) |
113 | { |
114 | const struct dp_debug_private *debug = m->private; |
115 | const struct drm_connector *connector = debug->connector; |
116 | |
117 | if (connector->status == connector_status_connected) |
118 | seq_printf(m, fmt: "%02x" , DP_TEST_LINK_VIDEO_PATTERN); |
119 | else |
120 | seq_puts(m, s: "0" ); |
121 | |
122 | return 0; |
123 | } |
124 | DEFINE_SHOW_ATTRIBUTE(dp_test_type); |
125 | |
126 | static ssize_t dp_test_active_write(struct file *file, |
127 | const char __user *ubuf, |
128 | size_t len, loff_t *offp) |
129 | { |
130 | char *input_buffer; |
131 | int status = 0; |
132 | const struct dp_debug_private *debug; |
133 | const struct drm_connector *connector; |
134 | int val = 0; |
135 | |
136 | debug = ((struct seq_file *)file->private_data)->private; |
137 | connector = debug->connector; |
138 | |
139 | if (len == 0) |
140 | return 0; |
141 | |
142 | input_buffer = memdup_user_nul(ubuf, len); |
143 | if (IS_ERR(ptr: input_buffer)) |
144 | return PTR_ERR(ptr: input_buffer); |
145 | |
146 | DRM_DEBUG_DRIVER("Copied %d bytes from user\n" , (unsigned int)len); |
147 | |
148 | if (connector->status == connector_status_connected) { |
149 | status = kstrtoint(s: input_buffer, base: 10, res: &val); |
150 | if (status < 0) { |
151 | kfree(objp: input_buffer); |
152 | return status; |
153 | } |
154 | DRM_DEBUG_DRIVER("Got %d for test active\n" , val); |
155 | /* To prevent erroneous activation of the compliance |
156 | * testing code, only accept an actual value of 1 here |
157 | */ |
158 | if (val == 1) |
159 | debug->panel->video_test = true; |
160 | else |
161 | debug->panel->video_test = false; |
162 | } |
163 | kfree(objp: input_buffer); |
164 | |
165 | *offp += len; |
166 | return len; |
167 | } |
168 | |
169 | static int dp_test_active_show(struct seq_file *m, void *data) |
170 | { |
171 | struct dp_debug_private *debug = m->private; |
172 | struct drm_connector *connector = debug->connector; |
173 | |
174 | if (connector->status == connector_status_connected) { |
175 | if (debug->panel->video_test) |
176 | seq_puts(m, s: "1" ); |
177 | else |
178 | seq_puts(m, s: "0" ); |
179 | } else { |
180 | seq_puts(m, s: "0" ); |
181 | } |
182 | |
183 | return 0; |
184 | } |
185 | |
186 | static int dp_test_active_open(struct inode *inode, |
187 | struct file *file) |
188 | { |
189 | return single_open(file, dp_test_active_show, |
190 | inode->i_private); |
191 | } |
192 | |
193 | static const struct file_operations test_active_fops = { |
194 | .owner = THIS_MODULE, |
195 | .open = dp_test_active_open, |
196 | .read = seq_read, |
197 | .llseek = seq_lseek, |
198 | .release = single_release, |
199 | .write = dp_test_active_write |
200 | }; |
201 | |
202 | static void dp_debug_init(struct dp_debug *dp_debug, struct dentry *root, bool is_edp) |
203 | { |
204 | struct dp_debug_private *debug = container_of(dp_debug, |
205 | struct dp_debug_private, dp_debug); |
206 | |
207 | debugfs_create_file(name: "dp_debug" , mode: 0444, parent: root, |
208 | data: debug, fops: &dp_debug_fops); |
209 | |
210 | if (!is_edp) { |
211 | debugfs_create_file(name: "msm_dp_test_active" , mode: 0444, |
212 | parent: root, |
213 | data: debug, fops: &test_active_fops); |
214 | |
215 | debugfs_create_file(name: "msm_dp_test_data" , mode: 0444, |
216 | parent: root, |
217 | data: debug, fops: &dp_test_data_fops); |
218 | |
219 | debugfs_create_file(name: "msm_dp_test_type" , mode: 0444, |
220 | parent: root, |
221 | data: debug, fops: &dp_test_type_fops); |
222 | } |
223 | } |
224 | |
225 | struct dp_debug *dp_debug_get(struct device *dev, struct dp_panel *panel, |
226 | struct dp_link *link, |
227 | struct drm_connector *connector, |
228 | struct dentry *root, bool is_edp) |
229 | { |
230 | struct dp_debug_private *debug; |
231 | struct dp_debug *dp_debug; |
232 | int rc; |
233 | |
234 | if (!dev || !panel || !link) { |
235 | DRM_ERROR("invalid input\n" ); |
236 | rc = -EINVAL; |
237 | goto error; |
238 | } |
239 | |
240 | debug = devm_kzalloc(dev, size: sizeof(*debug), GFP_KERNEL); |
241 | if (!debug) { |
242 | rc = -ENOMEM; |
243 | goto error; |
244 | } |
245 | |
246 | debug->dp_debug.debug_en = false; |
247 | debug->link = link; |
248 | debug->panel = panel; |
249 | |
250 | dp_debug = &debug->dp_debug; |
251 | dp_debug->vdisplay = 0; |
252 | dp_debug->hdisplay = 0; |
253 | dp_debug->vrefresh = 0; |
254 | |
255 | dp_debug_init(dp_debug, root, is_edp); |
256 | |
257 | return dp_debug; |
258 | error: |
259 | return ERR_PTR(error: rc); |
260 | } |
261 | |