1 | /* |
2 | * Copyright 2018 Advanced Micro Devices, Inc. |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the "Software"), |
6 | * to deal in the Software without restriction, including without limitation |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
8 | * and/or sell copies of the Software, and to permit persons to whom the |
9 | * Software is furnished to do so, subject to the following conditions: |
10 | * |
11 | * The above copyright notice and this permission notice shall be included in |
12 | * all copies or substantial portions of the Software. |
13 | * |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
20 | * OTHER DEALINGS IN THE SOFTWARE. |
21 | * |
22 | * Authors: AMD |
23 | * |
24 | */ |
25 | |
26 | #include <linux/string_helpers.h> |
27 | #include <linux/uaccess.h> |
28 | |
29 | #include "dc.h" |
30 | #include "amdgpu.h" |
31 | #include "amdgpu_dm.h" |
32 | #include "amdgpu_dm_debugfs.h" |
33 | #include "dm_helpers.h" |
34 | #include "dmub/dmub_srv.h" |
35 | #include "resource.h" |
36 | #include "dsc.h" |
37 | #include "link_hwss.h" |
38 | #include "dc/dc_dmub_srv.h" |
39 | #include "link/protocols/link_dp_capability.h" |
40 | #include "inc/hw/dchubbub.h" |
41 | |
42 | #ifdef CONFIG_DRM_AMD_SECURE_DISPLAY |
43 | #include "amdgpu_dm_psr.h" |
44 | #endif |
45 | |
46 | struct { |
47 | uint32_t ; |
48 | uint32_t [3]; |
49 | }; |
50 | |
51 | struct dmub_debugfs_trace_entry { |
52 | uint32_t trace_code; |
53 | uint32_t tick_count; |
54 | uint32_t param0; |
55 | uint32_t param1; |
56 | }; |
57 | |
58 | static const char *const mst_progress_status[] = { |
59 | "probe" , |
60 | "remote_edid" , |
61 | "allocate_new_payload" , |
62 | "clear_allocated_payload" , |
63 | }; |
64 | |
65 | /* parse_write_buffer_into_params - Helper function to parse debugfs write buffer into an array |
66 | * |
67 | * Function takes in attributes passed to debugfs write entry |
68 | * and writes into param array. |
69 | * The user passes max_param_num to identify maximum number of |
70 | * parameters that could be parsed. |
71 | * |
72 | */ |
73 | static int parse_write_buffer_into_params(char *wr_buf, uint32_t wr_buf_size, |
74 | long *param, const char __user *buf, |
75 | int max_param_num, |
76 | uint8_t *param_nums) |
77 | { |
78 | char *wr_buf_ptr = NULL; |
79 | uint32_t wr_buf_count = 0; |
80 | int r; |
81 | char *sub_str = NULL; |
82 | const char delimiter[3] = {' ', '\n', '\0'}; |
83 | uint8_t param_index = 0; |
84 | |
85 | *param_nums = 0; |
86 | |
87 | wr_buf_ptr = wr_buf; |
88 | |
89 | /* r is bytes not be copied */ |
90 | if (copy_from_user(to: wr_buf_ptr, from: buf, n: wr_buf_size)) { |
91 | DRM_DEBUG_DRIVER("user data could not be read successfully\n" ); |
92 | return -EFAULT; |
93 | } |
94 | |
95 | /* check number of parameters. isspace could not differ space and \n */ |
96 | while ((*wr_buf_ptr != 0xa) && (wr_buf_count < wr_buf_size)) { |
97 | /* skip space*/ |
98 | while (isspace(*wr_buf_ptr) && (wr_buf_count < wr_buf_size)) { |
99 | wr_buf_ptr++; |
100 | wr_buf_count++; |
101 | } |
102 | |
103 | if (wr_buf_count == wr_buf_size) |
104 | break; |
105 | |
106 | /* skip non-space*/ |
107 | while ((!isspace(*wr_buf_ptr)) && (wr_buf_count < wr_buf_size)) { |
108 | wr_buf_ptr++; |
109 | wr_buf_count++; |
110 | } |
111 | |
112 | (*param_nums)++; |
113 | |
114 | if (wr_buf_count == wr_buf_size) |
115 | break; |
116 | } |
117 | |
118 | if (*param_nums > max_param_num) |
119 | *param_nums = max_param_num; |
120 | |
121 | wr_buf_ptr = wr_buf; /* reset buf pointer */ |
122 | wr_buf_count = 0; /* number of char already checked */ |
123 | |
124 | while (isspace(*wr_buf_ptr) && (wr_buf_count < wr_buf_size)) { |
125 | wr_buf_ptr++; |
126 | wr_buf_count++; |
127 | } |
128 | |
129 | while (param_index < *param_nums) { |
130 | /* after strsep, wr_buf_ptr will be moved to after space */ |
131 | sub_str = strsep(&wr_buf_ptr, delimiter); |
132 | |
133 | r = kstrtol(s: sub_str, base: 16, res: &(param[param_index])); |
134 | |
135 | if (r) |
136 | DRM_DEBUG_DRIVER("string to int convert error code: %d\n" , r); |
137 | |
138 | param_index++; |
139 | } |
140 | |
141 | return 0; |
142 | } |
143 | |
144 | /* function description |
145 | * get/ set DP configuration: lane_count, link_rate, spread_spectrum |
146 | * |
147 | * valid lane count value: 1, 2, 4 |
148 | * valid link rate value: |
149 | * 06h = 1.62Gbps per lane |
150 | * 0Ah = 2.7Gbps per lane |
151 | * 0Ch = 3.24Gbps per lane |
152 | * 14h = 5.4Gbps per lane |
153 | * 1Eh = 8.1Gbps per lane |
154 | * |
155 | * debugfs is located at /sys/kernel/debug/dri/0/DP-x/link_settings |
156 | * |
157 | * --- to get dp configuration |
158 | * |
159 | * cat /sys/kernel/debug/dri/0/DP-x/link_settings |
160 | * |
161 | * It will list current, verified, reported, preferred dp configuration. |
162 | * current -- for current video mode |
163 | * verified --- maximum configuration which pass link training |
164 | * reported --- DP rx report caps (DPCD register offset 0, 1 2) |
165 | * preferred --- user force settings |
166 | * |
167 | * --- set (or force) dp configuration |
168 | * |
169 | * echo <lane_count> <link_rate> > link_settings |
170 | * |
171 | * for example, to force to 2 lane, 2.7GHz, |
172 | * echo 4 0xa > /sys/kernel/debug/dri/0/DP-x/link_settings |
173 | * |
174 | * spread_spectrum could not be changed dynamically. |
175 | * |
176 | * in case invalid lane count, link rate are force, no hw programming will be |
177 | * done. please check link settings after force operation to see if HW get |
178 | * programming. |
179 | * |
180 | * cat /sys/kernel/debug/dri/0/DP-x/link_settings |
181 | * |
182 | * check current and preferred settings. |
183 | * |
184 | */ |
185 | static ssize_t dp_link_settings_read(struct file *f, char __user *buf, |
186 | size_t size, loff_t *pos) |
187 | { |
188 | struct amdgpu_dm_connector *connector = file_inode(f)->i_private; |
189 | struct dc_link *link = connector->dc_link; |
190 | char *rd_buf = NULL; |
191 | char *rd_buf_ptr = NULL; |
192 | const uint32_t rd_buf_size = 100; |
193 | uint32_t result = 0; |
194 | uint8_t str_len = 0; |
195 | int r; |
196 | |
197 | if (*pos & 3 || size & 3) |
198 | return -EINVAL; |
199 | |
200 | rd_buf = kcalloc(n: rd_buf_size, size: sizeof(char), GFP_KERNEL); |
201 | if (!rd_buf) |
202 | return 0; |
203 | |
204 | rd_buf_ptr = rd_buf; |
205 | |
206 | str_len = strlen("Current: %d 0x%x %d " ); |
207 | snprintf(buf: rd_buf_ptr, size: str_len, fmt: "Current: %d 0x%x %d " , |
208 | link->cur_link_settings.lane_count, |
209 | link->cur_link_settings.link_rate, |
210 | link->cur_link_settings.link_spread); |
211 | rd_buf_ptr += str_len; |
212 | |
213 | str_len = strlen("Verified: %d 0x%x %d " ); |
214 | snprintf(buf: rd_buf_ptr, size: str_len, fmt: "Verified: %d 0x%x %d " , |
215 | link->verified_link_cap.lane_count, |
216 | link->verified_link_cap.link_rate, |
217 | link->verified_link_cap.link_spread); |
218 | rd_buf_ptr += str_len; |
219 | |
220 | str_len = strlen("Reported: %d 0x%x %d " ); |
221 | snprintf(buf: rd_buf_ptr, size: str_len, fmt: "Reported: %d 0x%x %d " , |
222 | link->reported_link_cap.lane_count, |
223 | link->reported_link_cap.link_rate, |
224 | link->reported_link_cap.link_spread); |
225 | rd_buf_ptr += str_len; |
226 | |
227 | str_len = strlen("Preferred: %d 0x%x %d " ); |
228 | snprintf(buf: rd_buf_ptr, size: str_len, fmt: "Preferred: %d 0x%x %d\n" , |
229 | link->preferred_link_setting.lane_count, |
230 | link->preferred_link_setting.link_rate, |
231 | link->preferred_link_setting.link_spread); |
232 | |
233 | while (size) { |
234 | if (*pos >= rd_buf_size) |
235 | break; |
236 | |
237 | r = put_user(*(rd_buf + result), buf); |
238 | if (r) { |
239 | kfree(objp: rd_buf); |
240 | return r; /* r = -EFAULT */ |
241 | } |
242 | |
243 | buf += 1; |
244 | size -= 1; |
245 | *pos += 1; |
246 | result += 1; |
247 | } |
248 | |
249 | kfree(objp: rd_buf); |
250 | return result; |
251 | } |
252 | |
253 | static ssize_t dp_link_settings_write(struct file *f, const char __user *buf, |
254 | size_t size, loff_t *pos) |
255 | { |
256 | struct amdgpu_dm_connector *connector = file_inode(f)->i_private; |
257 | struct dc_link *link = connector->dc_link; |
258 | struct amdgpu_device *adev = drm_to_adev(ddev: connector->base.dev); |
259 | struct dc *dc = (struct dc *)link->dc; |
260 | struct dc_link_settings prefer_link_settings; |
261 | char *wr_buf = NULL; |
262 | const uint32_t wr_buf_size = 40; |
263 | /* 0: lane_count; 1: link_rate */ |
264 | int max_param_num = 2; |
265 | uint8_t param_nums = 0; |
266 | long param[2]; |
267 | bool valid_input = true; |
268 | |
269 | if (size == 0) |
270 | return -EINVAL; |
271 | |
272 | wr_buf = kcalloc(n: wr_buf_size, size: sizeof(char), GFP_KERNEL); |
273 | if (!wr_buf) |
274 | return -ENOSPC; |
275 | |
276 | if (parse_write_buffer_into_params(wr_buf, wr_buf_size, |
277 | param: (long *)param, buf, |
278 | max_param_num, |
279 | param_nums: ¶m_nums)) { |
280 | kfree(objp: wr_buf); |
281 | return -EINVAL; |
282 | } |
283 | |
284 | if (param_nums <= 0) { |
285 | kfree(objp: wr_buf); |
286 | DRM_DEBUG_DRIVER("user data not be read\n" ); |
287 | return -EINVAL; |
288 | } |
289 | |
290 | switch (param[0]) { |
291 | case LANE_COUNT_ONE: |
292 | case LANE_COUNT_TWO: |
293 | case LANE_COUNT_FOUR: |
294 | break; |
295 | default: |
296 | valid_input = false; |
297 | break; |
298 | } |
299 | |
300 | switch (param[1]) { |
301 | case LINK_RATE_LOW: |
302 | case LINK_RATE_HIGH: |
303 | case LINK_RATE_RBR2: |
304 | case LINK_RATE_HIGH2: |
305 | case LINK_RATE_HIGH3: |
306 | case LINK_RATE_UHBR10: |
307 | case LINK_RATE_UHBR13_5: |
308 | case LINK_RATE_UHBR20: |
309 | break; |
310 | default: |
311 | valid_input = false; |
312 | break; |
313 | } |
314 | |
315 | if (!valid_input) { |
316 | kfree(objp: wr_buf); |
317 | DRM_DEBUG_DRIVER("Invalid Input value No HW will be programmed\n" ); |
318 | mutex_lock(&adev->dm.dc_lock); |
319 | dc_link_set_preferred_training_settings(dc, NULL, NULL, link, skip_immediate_retrain: false); |
320 | mutex_unlock(lock: &adev->dm.dc_lock); |
321 | return size; |
322 | } |
323 | |
324 | /* save user force lane_count, link_rate to preferred settings |
325 | * spread spectrum will not be changed |
326 | */ |
327 | prefer_link_settings.link_spread = link->cur_link_settings.link_spread; |
328 | prefer_link_settings.use_link_rate_set = false; |
329 | prefer_link_settings.lane_count = param[0]; |
330 | prefer_link_settings.link_rate = param[1]; |
331 | |
332 | mutex_lock(&adev->dm.dc_lock); |
333 | dc_link_set_preferred_training_settings(dc, link_setting: &prefer_link_settings, NULL, link, skip_immediate_retrain: false); |
334 | mutex_unlock(lock: &adev->dm.dc_lock); |
335 | |
336 | kfree(objp: wr_buf); |
337 | return size; |
338 | } |
339 | |
340 | static bool dp_mst_is_end_device(struct amdgpu_dm_connector *aconnector) |
341 | { |
342 | bool is_end_device = false; |
343 | struct drm_dp_mst_topology_mgr *mgr = NULL; |
344 | struct drm_dp_mst_port *port = NULL; |
345 | |
346 | if (aconnector->mst_root && aconnector->mst_root->mst_mgr.mst_state) { |
347 | mgr = &aconnector->mst_root->mst_mgr; |
348 | port = aconnector->mst_output_port; |
349 | |
350 | drm_modeset_lock(lock: &mgr->base.lock, NULL); |
351 | if (port->pdt == DP_PEER_DEVICE_SST_SINK || |
352 | port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV) |
353 | is_end_device = true; |
354 | drm_modeset_unlock(lock: &mgr->base.lock); |
355 | } |
356 | |
357 | return is_end_device; |
358 | } |
359 | |
360 | /* Change MST link setting |
361 | * |
362 | * valid lane count value: 1, 2, 4 |
363 | * valid link rate value: |
364 | * 06h = 1.62Gbps per lane |
365 | * 0Ah = 2.7Gbps per lane |
366 | * 0Ch = 3.24Gbps per lane |
367 | * 14h = 5.4Gbps per lane |
368 | * 1Eh = 8.1Gbps per lane |
369 | * 3E8h = 10.0Gbps per lane |
370 | * 546h = 13.5Gbps per lane |
371 | * 7D0h = 20.0Gbps per lane |
372 | * |
373 | * debugfs is located at /sys/kernel/debug/dri/0/DP-x/mst_link_settings |
374 | * |
375 | * for example, to force to 2 lane, 10.0GHz, |
376 | * echo 2 0x3e8 > /sys/kernel/debug/dri/0/DP-x/mst_link_settings |
377 | * |
378 | * Valid input will trigger hotplug event to get new link setting applied |
379 | * Invalid input will trigger training setting reset |
380 | * |
381 | * The usage can be referred to link_settings entry |
382 | * |
383 | */ |
384 | static ssize_t dp_mst_link_setting(struct file *f, const char __user *buf, |
385 | size_t size, loff_t *pos) |
386 | { |
387 | struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private; |
388 | struct dc_link *link = aconnector->dc_link; |
389 | struct amdgpu_device *adev = drm_to_adev(ddev: aconnector->base.dev); |
390 | struct dc *dc = (struct dc *)link->dc; |
391 | struct dc_link_settings prefer_link_settings; |
392 | char *wr_buf = NULL; |
393 | const uint32_t wr_buf_size = 40; |
394 | /* 0: lane_count; 1: link_rate */ |
395 | int max_param_num = 2; |
396 | uint8_t param_nums = 0; |
397 | long param[2]; |
398 | bool valid_input = true; |
399 | |
400 | if (!dp_mst_is_end_device(aconnector)) |
401 | return -EINVAL; |
402 | |
403 | if (size == 0) |
404 | return -EINVAL; |
405 | |
406 | wr_buf = kcalloc(n: wr_buf_size, size: sizeof(char), GFP_KERNEL); |
407 | if (!wr_buf) |
408 | return -ENOSPC; |
409 | |
410 | if (parse_write_buffer_into_params(wr_buf, wr_buf_size, |
411 | param: (long *)param, buf, |
412 | max_param_num, |
413 | param_nums: ¶m_nums)) { |
414 | kfree(objp: wr_buf); |
415 | return -EINVAL; |
416 | } |
417 | |
418 | if (param_nums <= 0) { |
419 | kfree(objp: wr_buf); |
420 | DRM_DEBUG_DRIVER("user data not be read\n" ); |
421 | return -EINVAL; |
422 | } |
423 | |
424 | switch (param[0]) { |
425 | case LANE_COUNT_ONE: |
426 | case LANE_COUNT_TWO: |
427 | case LANE_COUNT_FOUR: |
428 | break; |
429 | default: |
430 | valid_input = false; |
431 | break; |
432 | } |
433 | |
434 | switch (param[1]) { |
435 | case LINK_RATE_LOW: |
436 | case LINK_RATE_HIGH: |
437 | case LINK_RATE_RBR2: |
438 | case LINK_RATE_HIGH2: |
439 | case LINK_RATE_HIGH3: |
440 | case LINK_RATE_UHBR10: |
441 | case LINK_RATE_UHBR13_5: |
442 | case LINK_RATE_UHBR20: |
443 | break; |
444 | default: |
445 | valid_input = false; |
446 | break; |
447 | } |
448 | |
449 | if (!valid_input) { |
450 | kfree(objp: wr_buf); |
451 | DRM_DEBUG_DRIVER("Invalid Input value No HW will be programmed\n" ); |
452 | mutex_lock(&adev->dm.dc_lock); |
453 | dc_link_set_preferred_training_settings(dc, NULL, NULL, link, skip_immediate_retrain: false); |
454 | mutex_unlock(lock: &adev->dm.dc_lock); |
455 | return -EINVAL; |
456 | } |
457 | |
458 | /* save user force lane_count, link_rate to preferred settings |
459 | * spread spectrum will not be changed |
460 | */ |
461 | prefer_link_settings.link_spread = link->cur_link_settings.link_spread; |
462 | prefer_link_settings.use_link_rate_set = false; |
463 | prefer_link_settings.lane_count = param[0]; |
464 | prefer_link_settings.link_rate = param[1]; |
465 | |
466 | /* skip immediate retrain, and train to new link setting after hotplug event triggered */ |
467 | mutex_lock(&adev->dm.dc_lock); |
468 | dc_link_set_preferred_training_settings(dc, link_setting: &prefer_link_settings, NULL, link, skip_immediate_retrain: true); |
469 | mutex_unlock(lock: &adev->dm.dc_lock); |
470 | |
471 | mutex_lock(&aconnector->base.dev->mode_config.mutex); |
472 | aconnector->base.force = DRM_FORCE_OFF; |
473 | mutex_unlock(lock: &aconnector->base.dev->mode_config.mutex); |
474 | drm_kms_helper_hotplug_event(dev: aconnector->base.dev); |
475 | |
476 | msleep(msecs: 100); |
477 | |
478 | mutex_lock(&aconnector->base.dev->mode_config.mutex); |
479 | aconnector->base.force = DRM_FORCE_UNSPECIFIED; |
480 | mutex_unlock(lock: &aconnector->base.dev->mode_config.mutex); |
481 | drm_kms_helper_hotplug_event(dev: aconnector->base.dev); |
482 | |
483 | kfree(objp: wr_buf); |
484 | return size; |
485 | } |
486 | |
487 | /* function: get current DP PHY settings: voltage swing, pre-emphasis, |
488 | * post-cursor2 (defined by VESA DP specification) |
489 | * |
490 | * valid values |
491 | * voltage swing: 0,1,2,3 |
492 | * pre-emphasis : 0,1,2,3 |
493 | * post cursor2 : 0,1,2,3 |
494 | * |
495 | * |
496 | * how to use this debugfs |
497 | * |
498 | * debugfs is located at /sys/kernel/debug/dri/0/DP-x |
499 | * |
500 | * there will be directories, like DP-1, DP-2,DP-3, etc. for DP display |
501 | * |
502 | * To figure out which DP-x is the display for DP to be check, |
503 | * cd DP-x |
504 | * ls -ll |
505 | * There should be debugfs file, like link_settings, phy_settings. |
506 | * cat link_settings |
507 | * from lane_count, link_rate to figure which DP-x is for display to be worked |
508 | * on |
509 | * |
510 | * To get current DP PHY settings, |
511 | * cat phy_settings |
512 | * |
513 | * To change DP PHY settings, |
514 | * echo <voltage_swing> <pre-emphasis> <post_cursor2> > phy_settings |
515 | * for examle, to change voltage swing to 2, pre-emphasis to 3, post_cursor2 to |
516 | * 0, |
517 | * echo 2 3 0 > phy_settings |
518 | * |
519 | * To check if change be applied, get current phy settings by |
520 | * cat phy_settings |
521 | * |
522 | * In case invalid values are set by user, like |
523 | * echo 1 4 0 > phy_settings |
524 | * |
525 | * HW will NOT be programmed by these settings. |
526 | * cat phy_settings will show the previous valid settings. |
527 | */ |
528 | static ssize_t dp_phy_settings_read(struct file *f, char __user *buf, |
529 | size_t size, loff_t *pos) |
530 | { |
531 | struct amdgpu_dm_connector *connector = file_inode(f)->i_private; |
532 | struct dc_link *link = connector->dc_link; |
533 | char *rd_buf = NULL; |
534 | const uint32_t rd_buf_size = 20; |
535 | uint32_t result = 0; |
536 | int r; |
537 | |
538 | if (*pos & 3 || size & 3) |
539 | return -EINVAL; |
540 | |
541 | rd_buf = kcalloc(n: rd_buf_size, size: sizeof(char), GFP_KERNEL); |
542 | if (!rd_buf) |
543 | return -EINVAL; |
544 | |
545 | snprintf(buf: rd_buf, size: rd_buf_size, fmt: " %d %d %d\n" , |
546 | link->cur_lane_setting[0].VOLTAGE_SWING, |
547 | link->cur_lane_setting[0].PRE_EMPHASIS, |
548 | link->cur_lane_setting[0].POST_CURSOR2); |
549 | |
550 | while (size) { |
551 | if (*pos >= rd_buf_size) |
552 | break; |
553 | |
554 | r = put_user((*(rd_buf + result)), buf); |
555 | if (r) { |
556 | kfree(objp: rd_buf); |
557 | return r; /* r = -EFAULT */ |
558 | } |
559 | |
560 | buf += 1; |
561 | size -= 1; |
562 | *pos += 1; |
563 | result += 1; |
564 | } |
565 | |
566 | kfree(objp: rd_buf); |
567 | return result; |
568 | } |
569 | |
570 | static int dp_lttpr_status_show(struct seq_file *m, void *unused) |
571 | { |
572 | struct drm_connector *connector = m->private; |
573 | struct amdgpu_dm_connector *aconnector = |
574 | to_amdgpu_dm_connector(connector); |
575 | struct dc_lttpr_caps caps = aconnector->dc_link->dpcd_caps.lttpr_caps; |
576 | |
577 | if (connector->status != connector_status_connected) |
578 | return -ENODEV; |
579 | |
580 | seq_printf(m, fmt: "phy repeater count: %u (raw: 0x%x)\n" , |
581 | dp_parse_lttpr_repeater_count(lttpr_repeater_count: caps.phy_repeater_cnt), |
582 | caps.phy_repeater_cnt); |
583 | |
584 | seq_puts(m, s: "phy repeater mode: " ); |
585 | |
586 | switch (caps.mode) { |
587 | case DP_PHY_REPEATER_MODE_TRANSPARENT: |
588 | seq_puts(m, s: "transparent" ); |
589 | break; |
590 | case DP_PHY_REPEATER_MODE_NON_TRANSPARENT: |
591 | seq_puts(m, s: "non-transparent" ); |
592 | break; |
593 | case 0x00: |
594 | seq_puts(m, s: "non lttpr" ); |
595 | break; |
596 | default: |
597 | seq_printf(m, fmt: "read error (raw: 0x%x)" , caps.mode); |
598 | break; |
599 | } |
600 | |
601 | seq_puts(m, s: "\n" ); |
602 | return 0; |
603 | } |
604 | |
605 | static ssize_t dp_phy_settings_write(struct file *f, const char __user *buf, |
606 | size_t size, loff_t *pos) |
607 | { |
608 | struct amdgpu_dm_connector *connector = file_inode(f)->i_private; |
609 | struct dc_link *link = connector->dc_link; |
610 | struct dc *dc = (struct dc *)link->dc; |
611 | char *wr_buf = NULL; |
612 | uint32_t wr_buf_size = 40; |
613 | long param[3]; |
614 | bool use_prefer_link_setting; |
615 | struct link_training_settings link_lane_settings; |
616 | int max_param_num = 3; |
617 | uint8_t param_nums = 0; |
618 | int r = 0; |
619 | |
620 | |
621 | if (size == 0) |
622 | return -EINVAL; |
623 | |
624 | wr_buf = kcalloc(n: wr_buf_size, size: sizeof(char), GFP_KERNEL); |
625 | if (!wr_buf) |
626 | return -ENOSPC; |
627 | |
628 | if (parse_write_buffer_into_params(wr_buf, wr_buf_size, |
629 | param: (long *)param, buf, |
630 | max_param_num, |
631 | param_nums: ¶m_nums)) { |
632 | kfree(objp: wr_buf); |
633 | return -EINVAL; |
634 | } |
635 | |
636 | if (param_nums <= 0) { |
637 | kfree(objp: wr_buf); |
638 | DRM_DEBUG_DRIVER("user data not be read\n" ); |
639 | return -EINVAL; |
640 | } |
641 | |
642 | if ((param[0] > VOLTAGE_SWING_MAX_LEVEL) || |
643 | (param[1] > PRE_EMPHASIS_MAX_LEVEL) || |
644 | (param[2] > POST_CURSOR2_MAX_LEVEL)) { |
645 | kfree(objp: wr_buf); |
646 | DRM_DEBUG_DRIVER("Invalid Input No HW will be programmed\n" ); |
647 | return size; |
648 | } |
649 | |
650 | /* get link settings: lane count, link rate */ |
651 | use_prefer_link_setting = |
652 | ((link->preferred_link_setting.link_rate != LINK_RATE_UNKNOWN) && |
653 | (link->test_pattern_enabled)); |
654 | |
655 | memset(&link_lane_settings, 0, sizeof(link_lane_settings)); |
656 | |
657 | if (use_prefer_link_setting) { |
658 | link_lane_settings.link_settings.lane_count = |
659 | link->preferred_link_setting.lane_count; |
660 | link_lane_settings.link_settings.link_rate = |
661 | link->preferred_link_setting.link_rate; |
662 | link_lane_settings.link_settings.link_spread = |
663 | link->preferred_link_setting.link_spread; |
664 | } else { |
665 | link_lane_settings.link_settings.lane_count = |
666 | link->cur_link_settings.lane_count; |
667 | link_lane_settings.link_settings.link_rate = |
668 | link->cur_link_settings.link_rate; |
669 | link_lane_settings.link_settings.link_spread = |
670 | link->cur_link_settings.link_spread; |
671 | } |
672 | |
673 | /* apply phy settings from user */ |
674 | for (r = 0; r < link_lane_settings.link_settings.lane_count; r++) { |
675 | link_lane_settings.hw_lane_settings[r].VOLTAGE_SWING = |
676 | (enum dc_voltage_swing) (param[0]); |
677 | link_lane_settings.hw_lane_settings[r].PRE_EMPHASIS = |
678 | (enum dc_pre_emphasis) (param[1]); |
679 | link_lane_settings.hw_lane_settings[r].POST_CURSOR2 = |
680 | (enum dc_post_cursor2) (param[2]); |
681 | } |
682 | |
683 | /* program ASIC registers and DPCD registers */ |
684 | dc_link_set_drive_settings(dc, lt_settings: &link_lane_settings, link); |
685 | |
686 | kfree(objp: wr_buf); |
687 | return size; |
688 | } |
689 | |
690 | /* function description |
691 | * |
692 | * set PHY layer or Link layer test pattern |
693 | * PHY test pattern is used for PHY SI check. |
694 | * Link layer test will not affect PHY SI. |
695 | * |
696 | * Reset Test Pattern: |
697 | * 0 = DP_TEST_PATTERN_VIDEO_MODE |
698 | * |
699 | * PHY test pattern supported: |
700 | * 1 = DP_TEST_PATTERN_D102 |
701 | * 2 = DP_TEST_PATTERN_SYMBOL_ERROR |
702 | * 3 = DP_TEST_PATTERN_PRBS7 |
703 | * 4 = DP_TEST_PATTERN_80BIT_CUSTOM |
704 | * 5 = DP_TEST_PATTERN_CP2520_1 |
705 | * 6 = DP_TEST_PATTERN_CP2520_2 = DP_TEST_PATTERN_HBR2_COMPLIANCE_EYE |
706 | * 7 = DP_TEST_PATTERN_CP2520_3 |
707 | * |
708 | * DP PHY Link Training Patterns |
709 | * 8 = DP_TEST_PATTERN_TRAINING_PATTERN1 |
710 | * 9 = DP_TEST_PATTERN_TRAINING_PATTERN2 |
711 | * a = DP_TEST_PATTERN_TRAINING_PATTERN3 |
712 | * b = DP_TEST_PATTERN_TRAINING_PATTERN4 |
713 | * |
714 | * DP Link Layer Test pattern |
715 | * c = DP_TEST_PATTERN_COLOR_SQUARES |
716 | * d = DP_TEST_PATTERN_COLOR_SQUARES_CEA |
717 | * e = DP_TEST_PATTERN_VERTICAL_BARS |
718 | * f = DP_TEST_PATTERN_HORIZONTAL_BARS |
719 | * 10= DP_TEST_PATTERN_COLOR_RAMP |
720 | * |
721 | * debugfs phy_test_pattern is located at /syskernel/debug/dri/0/DP-x |
722 | * |
723 | * --- set test pattern |
724 | * echo <test pattern #> > test_pattern |
725 | * |
726 | * If test pattern # is not supported, NO HW programming will be done. |
727 | * for DP_TEST_PATTERN_80BIT_CUSTOM, it needs extra 10 bytes of data |
728 | * for the user pattern. input 10 bytes data are separated by space |
729 | * |
730 | * echo 0x4 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 0x99 0xaa > test_pattern |
731 | * |
732 | * --- reset test pattern |
733 | * echo 0 > test_pattern |
734 | * |
735 | * --- HPD detection is disabled when set PHY test pattern |
736 | * |
737 | * when PHY test pattern (pattern # within [1,7]) is set, HPD pin of HW ASIC |
738 | * is disable. User could unplug DP display from DP connected and plug scope to |
739 | * check test pattern PHY SI. |
740 | * If there is need unplug scope and plug DP display back, do steps below: |
741 | * echo 0 > phy_test_pattern |
742 | * unplug scope |
743 | * plug DP display. |
744 | * |
745 | * "echo 0 > phy_test_pattern" will re-enable HPD pin again so that video sw |
746 | * driver could detect "unplug scope" and "plug DP display" |
747 | */ |
748 | static ssize_t dp_phy_test_pattern_debugfs_write(struct file *f, const char __user *buf, |
749 | size_t size, loff_t *pos) |
750 | { |
751 | struct amdgpu_dm_connector *connector = file_inode(f)->i_private; |
752 | struct dc_link *link = connector->dc_link; |
753 | char *wr_buf = NULL; |
754 | uint32_t wr_buf_size = 100; |
755 | long param[11] = {0x0}; |
756 | int max_param_num = 11; |
757 | enum dp_test_pattern test_pattern = DP_TEST_PATTERN_UNSUPPORTED; |
758 | bool disable_hpd = false; |
759 | bool valid_test_pattern = false; |
760 | uint8_t param_nums = 0; |
761 | /* init with default 80bit custom pattern */ |
762 | uint8_t custom_pattern[10] = { |
763 | 0x1f, 0x7c, 0xf0, 0xc1, 0x07, |
764 | 0x1f, 0x7c, 0xf0, 0xc1, 0x07 |
765 | }; |
766 | struct dc_link_settings prefer_link_settings = {LANE_COUNT_UNKNOWN, |
767 | LINK_RATE_UNKNOWN, LINK_SPREAD_DISABLED}; |
768 | struct dc_link_settings cur_link_settings = {LANE_COUNT_UNKNOWN, |
769 | LINK_RATE_UNKNOWN, LINK_SPREAD_DISABLED}; |
770 | struct link_training_settings link_training_settings; |
771 | int i; |
772 | |
773 | if (size == 0) |
774 | return -EINVAL; |
775 | |
776 | wr_buf = kcalloc(n: wr_buf_size, size: sizeof(char), GFP_KERNEL); |
777 | if (!wr_buf) |
778 | return -ENOSPC; |
779 | |
780 | if (parse_write_buffer_into_params(wr_buf, wr_buf_size, |
781 | param: (long *)param, buf, |
782 | max_param_num, |
783 | param_nums: ¶m_nums)) { |
784 | kfree(objp: wr_buf); |
785 | return -EINVAL; |
786 | } |
787 | |
788 | if (param_nums <= 0) { |
789 | kfree(objp: wr_buf); |
790 | DRM_DEBUG_DRIVER("user data not be read\n" ); |
791 | return -EINVAL; |
792 | } |
793 | |
794 | |
795 | test_pattern = param[0]; |
796 | |
797 | switch (test_pattern) { |
798 | case DP_TEST_PATTERN_VIDEO_MODE: |
799 | case DP_TEST_PATTERN_COLOR_SQUARES: |
800 | case DP_TEST_PATTERN_COLOR_SQUARES_CEA: |
801 | case DP_TEST_PATTERN_VERTICAL_BARS: |
802 | case DP_TEST_PATTERN_HORIZONTAL_BARS: |
803 | case DP_TEST_PATTERN_COLOR_RAMP: |
804 | valid_test_pattern = true; |
805 | break; |
806 | |
807 | case DP_TEST_PATTERN_D102: |
808 | case DP_TEST_PATTERN_SYMBOL_ERROR: |
809 | case DP_TEST_PATTERN_PRBS7: |
810 | case DP_TEST_PATTERN_80BIT_CUSTOM: |
811 | case DP_TEST_PATTERN_HBR2_COMPLIANCE_EYE: |
812 | case DP_TEST_PATTERN_TRAINING_PATTERN4: |
813 | disable_hpd = true; |
814 | valid_test_pattern = true; |
815 | break; |
816 | |
817 | default: |
818 | valid_test_pattern = false; |
819 | test_pattern = DP_TEST_PATTERN_UNSUPPORTED; |
820 | break; |
821 | } |
822 | |
823 | if (!valid_test_pattern) { |
824 | kfree(objp: wr_buf); |
825 | DRM_DEBUG_DRIVER("Invalid Test Pattern Parameters\n" ); |
826 | return size; |
827 | } |
828 | |
829 | if (test_pattern == DP_TEST_PATTERN_80BIT_CUSTOM) { |
830 | for (i = 0; i < 10; i++) { |
831 | if ((uint8_t) param[i + 1] != 0x0) |
832 | break; |
833 | } |
834 | |
835 | if (i < 10) { |
836 | /* not use default value */ |
837 | for (i = 0; i < 10; i++) |
838 | custom_pattern[i] = (uint8_t) param[i + 1]; |
839 | } |
840 | } |
841 | |
842 | /* Usage: set DP physical test pattern using debugfs with normal DP |
843 | * panel. Then plug out DP panel and connect a scope to measure |
844 | * For normal video mode and test pattern generated from CRCT, |
845 | * they are visibile to user. So do not disable HPD. |
846 | * Video Mode is also set to clear the test pattern, so enable HPD |
847 | * because it might have been disabled after a test pattern was set. |
848 | * AUX depends on HPD * sequence dependent, do not move! |
849 | */ |
850 | if (!disable_hpd) |
851 | dc_link_enable_hpd(link); |
852 | |
853 | prefer_link_settings.lane_count = link->verified_link_cap.lane_count; |
854 | prefer_link_settings.link_rate = link->verified_link_cap.link_rate; |
855 | prefer_link_settings.link_spread = link->verified_link_cap.link_spread; |
856 | |
857 | cur_link_settings.lane_count = link->cur_link_settings.lane_count; |
858 | cur_link_settings.link_rate = link->cur_link_settings.link_rate; |
859 | cur_link_settings.link_spread = link->cur_link_settings.link_spread; |
860 | |
861 | link_training_settings.link_settings = cur_link_settings; |
862 | |
863 | |
864 | if (test_pattern != DP_TEST_PATTERN_VIDEO_MODE) { |
865 | if (prefer_link_settings.lane_count != LANE_COUNT_UNKNOWN && |
866 | prefer_link_settings.link_rate != LINK_RATE_UNKNOWN && |
867 | (prefer_link_settings.lane_count != cur_link_settings.lane_count || |
868 | prefer_link_settings.link_rate != cur_link_settings.link_rate)) |
869 | link_training_settings.link_settings = prefer_link_settings; |
870 | } |
871 | |
872 | for (i = 0; i < (unsigned int)(link_training_settings.link_settings.lane_count); i++) |
873 | link_training_settings.hw_lane_settings[i] = link->cur_lane_setting[i]; |
874 | |
875 | dc_link_dp_set_test_pattern( |
876 | link, |
877 | test_pattern, |
878 | test_pattern_color_space: DP_TEST_PATTERN_COLOR_SPACE_RGB, |
879 | p_link_settings: &link_training_settings, |
880 | p_custom_pattern: custom_pattern, |
881 | cust_pattern_size: 10); |
882 | |
883 | /* Usage: Set DP physical test pattern using AMDDP with normal DP panel |
884 | * Then plug out DP panel and connect a scope to measure DP PHY signal. |
885 | * Need disable interrupt to avoid SW driver disable DP output. This is |
886 | * done after the test pattern is set. |
887 | */ |
888 | if (valid_test_pattern && disable_hpd) |
889 | dc_link_disable_hpd(link); |
890 | |
891 | kfree(objp: wr_buf); |
892 | |
893 | return size; |
894 | } |
895 | |
896 | /* |
897 | * Returns the DMCUB tracebuffer contents. |
898 | * Example usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_dmub_tracebuffer |
899 | */ |
900 | static int dmub_tracebuffer_show(struct seq_file *m, void *data) |
901 | { |
902 | struct amdgpu_device *adev = m->private; |
903 | struct dmub_srv_fb_info *fb_info = adev->dm.dmub_fb_info; |
904 | struct dmub_debugfs_trace_entry *entries; |
905 | uint8_t *tbuf_base; |
906 | uint32_t tbuf_size, max_entries, num_entries, i; |
907 | |
908 | if (!fb_info) |
909 | return 0; |
910 | |
911 | tbuf_base = (uint8_t *)fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].cpu_addr; |
912 | if (!tbuf_base) |
913 | return 0; |
914 | |
915 | tbuf_size = fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].size; |
916 | max_entries = (tbuf_size - sizeof(struct dmub_debugfs_trace_header)) / |
917 | sizeof(struct dmub_debugfs_trace_entry); |
918 | |
919 | num_entries = |
920 | ((struct dmub_debugfs_trace_header *)tbuf_base)->entry_count; |
921 | |
922 | num_entries = min(num_entries, max_entries); |
923 | |
924 | entries = (struct dmub_debugfs_trace_entry |
925 | *)(tbuf_base + |
926 | sizeof(struct dmub_debugfs_trace_header)); |
927 | |
928 | for (i = 0; i < num_entries; ++i) { |
929 | struct dmub_debugfs_trace_entry *entry = &entries[i]; |
930 | |
931 | seq_printf(m, |
932 | fmt: "trace_code=%u tick_count=%u param0=%u param1=%u\n" , |
933 | entry->trace_code, entry->tick_count, entry->param0, |
934 | entry->param1); |
935 | } |
936 | |
937 | return 0; |
938 | } |
939 | |
940 | /* |
941 | * Returns the DMCUB firmware state contents. |
942 | * Example usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_dmub_fw_state |
943 | */ |
944 | static int dmub_fw_state_show(struct seq_file *m, void *data) |
945 | { |
946 | struct amdgpu_device *adev = m->private; |
947 | struct dmub_srv_fb_info *fb_info = adev->dm.dmub_fb_info; |
948 | uint8_t *state_base; |
949 | uint32_t state_size; |
950 | |
951 | if (!fb_info) |
952 | return 0; |
953 | |
954 | state_base = (uint8_t *)fb_info->fb[DMUB_WINDOW_6_FW_STATE].cpu_addr; |
955 | if (!state_base) |
956 | return 0; |
957 | |
958 | state_size = fb_info->fb[DMUB_WINDOW_6_FW_STATE].size; |
959 | |
960 | return seq_write(seq: m, data: state_base, len: state_size); |
961 | } |
962 | |
963 | /* psr_capability_show() - show eDP panel PSR capability |
964 | * |
965 | * The read function: sink_psr_capability_show |
966 | * Shows if sink has PSR capability or not. |
967 | * If yes - the PSR version is appended |
968 | * |
969 | * cat /sys/kernel/debug/dri/0/eDP-X/psr_capability |
970 | * |
971 | * Expected output: |
972 | * "Sink support: no\n" - if panel doesn't support PSR |
973 | * "Sink support: yes [0x01]\n" - if panel supports PSR1 |
974 | * "Driver support: no\n" - if driver doesn't support PSR |
975 | * "Driver support: yes [0x01]\n" - if driver supports PSR1 |
976 | */ |
977 | static int psr_capability_show(struct seq_file *m, void *data) |
978 | { |
979 | struct drm_connector *connector = m->private; |
980 | struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); |
981 | struct dc_link *link = aconnector->dc_link; |
982 | |
983 | if (!link) |
984 | return -ENODEV; |
985 | |
986 | if (link->type == dc_connection_none) |
987 | return -ENODEV; |
988 | |
989 | if (!(link->connector_signal & SIGNAL_TYPE_EDP)) |
990 | return -ENODEV; |
991 | |
992 | seq_printf(m, fmt: "Sink support: %s" , str_yes_no(v: link->dpcd_caps.psr_info.psr_version != 0)); |
993 | if (link->dpcd_caps.psr_info.psr_version) |
994 | seq_printf(m, fmt: " [0x%02x]" , link->dpcd_caps.psr_info.psr_version); |
995 | seq_puts(m, s: "\n" ); |
996 | |
997 | seq_printf(m, fmt: "Driver support: %s" , str_yes_no(v: link->psr_settings.psr_feature_enabled)); |
998 | if (link->psr_settings.psr_version) |
999 | seq_printf(m, fmt: " [0x%02x]" , link->psr_settings.psr_version); |
1000 | seq_puts(m, s: "\n" ); |
1001 | |
1002 | return 0; |
1003 | } |
1004 | |
1005 | /* |
1006 | * Returns the current bpc for the crtc. |
1007 | * Example usage: cat /sys/kernel/debug/dri/0/crtc-0/amdgpu_current_bpc |
1008 | */ |
1009 | static int amdgpu_current_bpc_show(struct seq_file *m, void *data) |
1010 | { |
1011 | struct drm_crtc *crtc = m->private; |
1012 | struct drm_device *dev = crtc->dev; |
1013 | struct dm_crtc_state *dm_crtc_state = NULL; |
1014 | int res = -ENODEV; |
1015 | unsigned int bpc; |
1016 | |
1017 | mutex_lock(&dev->mode_config.mutex); |
1018 | drm_modeset_lock(lock: &crtc->mutex, NULL); |
1019 | if (crtc->state == NULL) |
1020 | goto unlock; |
1021 | |
1022 | dm_crtc_state = to_dm_crtc_state(crtc->state); |
1023 | if (dm_crtc_state->stream == NULL) |
1024 | goto unlock; |
1025 | |
1026 | switch (dm_crtc_state->stream->timing.display_color_depth) { |
1027 | case COLOR_DEPTH_666: |
1028 | bpc = 6; |
1029 | break; |
1030 | case COLOR_DEPTH_888: |
1031 | bpc = 8; |
1032 | break; |
1033 | case COLOR_DEPTH_101010: |
1034 | bpc = 10; |
1035 | break; |
1036 | case COLOR_DEPTH_121212: |
1037 | bpc = 12; |
1038 | break; |
1039 | case COLOR_DEPTH_161616: |
1040 | bpc = 16; |
1041 | break; |
1042 | default: |
1043 | goto unlock; |
1044 | } |
1045 | |
1046 | seq_printf(m, fmt: "Current: %u\n" , bpc); |
1047 | res = 0; |
1048 | |
1049 | unlock: |
1050 | drm_modeset_unlock(lock: &crtc->mutex); |
1051 | mutex_unlock(lock: &dev->mode_config.mutex); |
1052 | |
1053 | return res; |
1054 | } |
1055 | DEFINE_SHOW_ATTRIBUTE(amdgpu_current_bpc); |
1056 | |
1057 | /* |
1058 | * Returns the current colorspace for the crtc. |
1059 | * Example usage: cat /sys/kernel/debug/dri/0/crtc-0/amdgpu_current_colorspace |
1060 | */ |
1061 | static int amdgpu_current_colorspace_show(struct seq_file *m, void *data) |
1062 | { |
1063 | struct drm_crtc *crtc = m->private; |
1064 | struct drm_device *dev = crtc->dev; |
1065 | struct dm_crtc_state *dm_crtc_state = NULL; |
1066 | int res = -ENODEV; |
1067 | |
1068 | mutex_lock(&dev->mode_config.mutex); |
1069 | drm_modeset_lock(lock: &crtc->mutex, NULL); |
1070 | if (crtc->state == NULL) |
1071 | goto unlock; |
1072 | |
1073 | dm_crtc_state = to_dm_crtc_state(crtc->state); |
1074 | if (dm_crtc_state->stream == NULL) |
1075 | goto unlock; |
1076 | |
1077 | switch (dm_crtc_state->stream->output_color_space) { |
1078 | case COLOR_SPACE_SRGB: |
1079 | seq_puts(m, s: "sRGB" ); |
1080 | break; |
1081 | case COLOR_SPACE_YCBCR601: |
1082 | case COLOR_SPACE_YCBCR601_LIMITED: |
1083 | seq_puts(m, s: "BT601_YCC" ); |
1084 | break; |
1085 | case COLOR_SPACE_YCBCR709: |
1086 | case COLOR_SPACE_YCBCR709_LIMITED: |
1087 | seq_puts(m, s: "BT709_YCC" ); |
1088 | break; |
1089 | case COLOR_SPACE_ADOBERGB: |
1090 | seq_puts(m, s: "opRGB" ); |
1091 | break; |
1092 | case COLOR_SPACE_2020_RGB_FULLRANGE: |
1093 | seq_puts(m, s: "BT2020_RGB" ); |
1094 | break; |
1095 | case COLOR_SPACE_2020_YCBCR: |
1096 | seq_puts(m, s: "BT2020_YCC" ); |
1097 | break; |
1098 | default: |
1099 | goto unlock; |
1100 | } |
1101 | res = 0; |
1102 | |
1103 | unlock: |
1104 | drm_modeset_unlock(lock: &crtc->mutex); |
1105 | mutex_unlock(lock: &dev->mode_config.mutex); |
1106 | |
1107 | return res; |
1108 | } |
1109 | DEFINE_SHOW_ATTRIBUTE(amdgpu_current_colorspace); |
1110 | |
1111 | |
1112 | /* |
1113 | * Example usage: |
1114 | * Disable dsc passthrough, i.e.,: have dsc decoding at converver, not external RX |
1115 | * echo 1 /sys/kernel/debug/dri/0/DP-1/dsc_disable_passthrough |
1116 | * Enable dsc passthrough, i.e.,: have dsc passthrough to external RX |
1117 | * echo 0 /sys/kernel/debug/dri/0/DP-1/dsc_disable_passthrough |
1118 | */ |
1119 | static ssize_t dp_dsc_passthrough_set(struct file *f, const char __user *buf, |
1120 | size_t size, loff_t *pos) |
1121 | { |
1122 | struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private; |
1123 | char *wr_buf = NULL; |
1124 | uint32_t wr_buf_size = 42; |
1125 | int max_param_num = 1; |
1126 | long param; |
1127 | uint8_t param_nums = 0; |
1128 | |
1129 | if (size == 0) |
1130 | return -EINVAL; |
1131 | |
1132 | wr_buf = kcalloc(n: wr_buf_size, size: sizeof(char), GFP_KERNEL); |
1133 | |
1134 | if (!wr_buf) { |
1135 | DRM_DEBUG_DRIVER("no memory to allocate write buffer\n" ); |
1136 | return -ENOSPC; |
1137 | } |
1138 | |
1139 | if (parse_write_buffer_into_params(wr_buf, wr_buf_size, |
1140 | param: ¶m, buf, |
1141 | max_param_num, |
1142 | param_nums: ¶m_nums)) { |
1143 | kfree(objp: wr_buf); |
1144 | return -EINVAL; |
1145 | } |
1146 | |
1147 | aconnector->dsc_settings.dsc_force_disable_passthrough = param; |
1148 | |
1149 | kfree(objp: wr_buf); |
1150 | return 0; |
1151 | } |
1152 | |
1153 | /* |
1154 | * Returns the HDCP capability of the Display (1.4 for now). |
1155 | * |
1156 | * NOTE* Not all HDMI displays report their HDCP caps even when they are capable. |
1157 | * Since its rare for a display to not be HDCP 1.4 capable, we set HDMI as always capable. |
1158 | * |
1159 | * Example usage: cat /sys/kernel/debug/dri/0/DP-1/hdcp_sink_capability |
1160 | * or cat /sys/kernel/debug/dri/0/HDMI-A-1/hdcp_sink_capability |
1161 | */ |
1162 | static int hdcp_sink_capability_show(struct seq_file *m, void *data) |
1163 | { |
1164 | struct drm_connector *connector = m->private; |
1165 | struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); |
1166 | bool hdcp_cap, hdcp2_cap; |
1167 | |
1168 | if (connector->status != connector_status_connected) |
1169 | return -ENODEV; |
1170 | |
1171 | seq_printf(m, fmt: "%s:%d HDCP version: " , connector->name, connector->base.id); |
1172 | |
1173 | hdcp_cap = dc_link_is_hdcp14(link: aconnector->dc_link, signal: aconnector->dc_sink->sink_signal); |
1174 | hdcp2_cap = dc_link_is_hdcp22(link: aconnector->dc_link, signal: aconnector->dc_sink->sink_signal); |
1175 | |
1176 | |
1177 | if (hdcp_cap) |
1178 | seq_printf(m, fmt: "%s " , "HDCP1.4" ); |
1179 | if (hdcp2_cap) |
1180 | seq_printf(m, fmt: "%s " , "HDCP2.2" ); |
1181 | |
1182 | if (!hdcp_cap && !hdcp2_cap) |
1183 | seq_printf(m, fmt: "%s " , "None" ); |
1184 | |
1185 | seq_puts(m, s: "\n" ); |
1186 | |
1187 | return 0; |
1188 | } |
1189 | |
1190 | /* |
1191 | * Returns whether the connected display is internal and not hotpluggable. |
1192 | * Example usage: cat /sys/kernel/debug/dri/0/DP-1/internal_display |
1193 | */ |
1194 | static int internal_display_show(struct seq_file *m, void *data) |
1195 | { |
1196 | struct drm_connector *connector = m->private; |
1197 | struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); |
1198 | struct dc_link *link = aconnector->dc_link; |
1199 | |
1200 | seq_printf(m, fmt: "Internal: %u\n" , link->is_internal_display); |
1201 | |
1202 | return 0; |
1203 | } |
1204 | |
1205 | /* |
1206 | * Returns the number of segments used if ODM Combine mode is enabled. |
1207 | * Example usage: cat /sys/kernel/debug/dri/0/DP-1/odm_combine_segments |
1208 | */ |
1209 | static int odm_combine_segments_show(struct seq_file *m, void *unused) |
1210 | { |
1211 | struct drm_connector *connector = m->private; |
1212 | struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); |
1213 | struct dc_link *link = aconnector->dc_link; |
1214 | struct pipe_ctx *pipe_ctx = NULL; |
1215 | int i, segments = -EOPNOTSUPP; |
1216 | |
1217 | for (i = 0; i < MAX_PIPES; i++) { |
1218 | pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i]; |
1219 | if (pipe_ctx->stream && |
1220 | pipe_ctx->stream->link == link) |
1221 | break; |
1222 | } |
1223 | |
1224 | if (connector->status != connector_status_connected) |
1225 | return -ENODEV; |
1226 | |
1227 | if (pipe_ctx != NULL && pipe_ctx->stream_res.tg->funcs->get_odm_combine_segments) |
1228 | pipe_ctx->stream_res.tg->funcs->get_odm_combine_segments(pipe_ctx->stream_res.tg, &segments); |
1229 | |
1230 | seq_printf(m, fmt: "%d\n" , segments); |
1231 | return 0; |
1232 | } |
1233 | |
1234 | /* function description |
1235 | * |
1236 | * generic SDP message access for testing |
1237 | * |
1238 | * debugfs sdp_message is located at /syskernel/debug/dri/0/DP-x |
1239 | * |
1240 | * SDP header |
1241 | * Hb0 : Secondary-Data Packet ID |
1242 | * Hb1 : Secondary-Data Packet type |
1243 | * Hb2 : Secondary-Data-packet-specific header, Byte 0 |
1244 | * Hb3 : Secondary-Data-packet-specific header, Byte 1 |
1245 | * |
1246 | * for using custom sdp message: input 4 bytes SDP header and 32 bytes raw data |
1247 | */ |
1248 | static ssize_t dp_sdp_message_debugfs_write(struct file *f, const char __user *buf, |
1249 | size_t size, loff_t *pos) |
1250 | { |
1251 | int r; |
1252 | uint8_t data[36]; |
1253 | struct amdgpu_dm_connector *connector = file_inode(f)->i_private; |
1254 | struct dm_crtc_state *acrtc_state; |
1255 | uint32_t write_size = 36; |
1256 | |
1257 | if (connector->base.status != connector_status_connected) |
1258 | return -ENODEV; |
1259 | |
1260 | if (size == 0) |
1261 | return 0; |
1262 | |
1263 | acrtc_state = to_dm_crtc_state(connector->base.state->crtc->state); |
1264 | |
1265 | r = copy_from_user(to: data, from: buf, n: write_size); |
1266 | |
1267 | write_size -= r; |
1268 | |
1269 | dc_stream_send_dp_sdp(stream: acrtc_state->stream, custom_sdp_message: data, sdp_message_size: write_size); |
1270 | |
1271 | return write_size; |
1272 | } |
1273 | |
1274 | /* function: Read link's DSC & FEC capabilities |
1275 | * |
1276 | * |
1277 | * Access it with the following command (you need to specify |
1278 | * connector like DP-1): |
1279 | * |
1280 | * cat /sys/kernel/debug/dri/0/DP-X/dp_dsc_fec_support |
1281 | * |
1282 | */ |
1283 | static int dp_dsc_fec_support_show(struct seq_file *m, void *data) |
1284 | { |
1285 | struct drm_connector *connector = m->private; |
1286 | struct drm_modeset_acquire_ctx ctx; |
1287 | struct drm_device *dev = connector->dev; |
1288 | struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); |
1289 | int ret = 0; |
1290 | bool try_again = false; |
1291 | bool is_fec_supported = false; |
1292 | bool is_dsc_supported = false; |
1293 | struct dpcd_caps dpcd_caps; |
1294 | |
1295 | drm_modeset_acquire_init(ctx: &ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE); |
1296 | do { |
1297 | try_again = false; |
1298 | ret = drm_modeset_lock(lock: &dev->mode_config.connection_mutex, ctx: &ctx); |
1299 | if (ret) { |
1300 | if (ret == -EDEADLK) { |
1301 | ret = drm_modeset_backoff(ctx: &ctx); |
1302 | if (!ret) { |
1303 | try_again = true; |
1304 | continue; |
1305 | } |
1306 | } |
1307 | break; |
1308 | } |
1309 | if (connector->status != connector_status_connected) { |
1310 | ret = -ENODEV; |
1311 | break; |
1312 | } |
1313 | dpcd_caps = aconnector->dc_link->dpcd_caps; |
1314 | if (aconnector->mst_output_port) { |
1315 | /* aconnector sets dsc_aux during get_modes call |
1316 | * if MST connector has it means it can either |
1317 | * enable DSC on the sink device or on MST branch |
1318 | * its connected to. |
1319 | */ |
1320 | if (aconnector->dsc_aux) { |
1321 | is_fec_supported = true; |
1322 | is_dsc_supported = true; |
1323 | } |
1324 | } else { |
1325 | is_fec_supported = dpcd_caps.fec_cap.raw & 0x1; |
1326 | is_dsc_supported = dpcd_caps.dsc_caps.dsc_basic_caps.raw[0] & 0x1; |
1327 | } |
1328 | } while (try_again); |
1329 | |
1330 | drm_modeset_drop_locks(ctx: &ctx); |
1331 | drm_modeset_acquire_fini(ctx: &ctx); |
1332 | |
1333 | seq_printf(m, fmt: "FEC_Sink_Support: %s\n" , str_yes_no(v: is_fec_supported)); |
1334 | seq_printf(m, fmt: "DSC_Sink_Support: %s\n" , str_yes_no(v: is_dsc_supported)); |
1335 | |
1336 | return ret; |
1337 | } |
1338 | |
1339 | /* function: Trigger virtual HPD redetection on connector |
1340 | * |
1341 | * This function will perform link rediscovery, link disable |
1342 | * and enable, and dm connector state update. |
1343 | * |
1344 | * Retrigger HPD on an existing connector by echoing 1 into |
1345 | * its respectful "trigger_hotplug" debugfs entry: |
1346 | * |
1347 | * echo 1 > /sys/kernel/debug/dri/0/DP-X/trigger_hotplug |
1348 | * |
1349 | * This function can perform HPD unplug: |
1350 | * |
1351 | * echo 0 > /sys/kernel/debug/dri/0/DP-X/trigger_hotplug |
1352 | * |
1353 | */ |
1354 | static ssize_t trigger_hotplug(struct file *f, const char __user *buf, |
1355 | size_t size, loff_t *pos) |
1356 | { |
1357 | struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private; |
1358 | struct drm_connector *connector = &aconnector->base; |
1359 | struct dc_link *link = NULL; |
1360 | struct drm_device *dev = connector->dev; |
1361 | struct amdgpu_device *adev = drm_to_adev(ddev: dev); |
1362 | enum dc_connection_type new_connection_type = dc_connection_none; |
1363 | char *wr_buf = NULL; |
1364 | uint32_t wr_buf_size = 42; |
1365 | int max_param_num = 1; |
1366 | long param[1] = {0}; |
1367 | uint8_t param_nums = 0; |
1368 | bool ret = false; |
1369 | |
1370 | if (!aconnector || !aconnector->dc_link) |
1371 | return -EINVAL; |
1372 | |
1373 | if (size == 0) |
1374 | return -EINVAL; |
1375 | |
1376 | wr_buf = kcalloc(n: wr_buf_size, size: sizeof(char), GFP_KERNEL); |
1377 | |
1378 | if (!wr_buf) { |
1379 | DRM_DEBUG_DRIVER("no memory to allocate write buffer\n" ); |
1380 | return -ENOSPC; |
1381 | } |
1382 | |
1383 | if (parse_write_buffer_into_params(wr_buf, wr_buf_size, |
1384 | param: (long *)param, buf, |
1385 | max_param_num, |
1386 | param_nums: ¶m_nums)) { |
1387 | kfree(objp: wr_buf); |
1388 | return -EINVAL; |
1389 | } |
1390 | |
1391 | kfree(objp: wr_buf); |
1392 | |
1393 | if (param_nums <= 0) { |
1394 | DRM_DEBUG_DRIVER("user data not be read\n" ); |
1395 | return -EINVAL; |
1396 | } |
1397 | |
1398 | mutex_lock(&aconnector->hpd_lock); |
1399 | |
1400 | /* Don't support for mst end device*/ |
1401 | if (aconnector->mst_root) { |
1402 | mutex_unlock(lock: &aconnector->hpd_lock); |
1403 | return -EINVAL; |
1404 | } |
1405 | |
1406 | if (param[0] == 1) { |
1407 | |
1408 | if (!dc_link_detect_connection_type(link: aconnector->dc_link, type: &new_connection_type) && |
1409 | new_connection_type != dc_connection_none) |
1410 | goto unlock; |
1411 | |
1412 | mutex_lock(&adev->dm.dc_lock); |
1413 | ret = dc_link_detect(link: aconnector->dc_link, reason: DETECT_REASON_HPD); |
1414 | mutex_unlock(lock: &adev->dm.dc_lock); |
1415 | |
1416 | if (!ret) |
1417 | goto unlock; |
1418 | |
1419 | amdgpu_dm_update_connector_after_detect(aconnector); |
1420 | |
1421 | drm_modeset_lock_all(dev); |
1422 | dm_restore_drm_connector_state(dev, connector); |
1423 | drm_modeset_unlock_all(dev); |
1424 | |
1425 | drm_kms_helper_connector_hotplug_event(connector); |
1426 | } else if (param[0] == 0) { |
1427 | if (!aconnector->dc_link) |
1428 | goto unlock; |
1429 | |
1430 | link = aconnector->dc_link; |
1431 | |
1432 | if (link->local_sink) { |
1433 | dc_sink_release(sink: link->local_sink); |
1434 | link->local_sink = NULL; |
1435 | } |
1436 | |
1437 | link->dpcd_sink_count = 0; |
1438 | link->type = dc_connection_none; |
1439 | link->dongle_max_pix_clk = 0; |
1440 | |
1441 | amdgpu_dm_update_connector_after_detect(aconnector); |
1442 | |
1443 | /* If the aconnector is the root node in mst topology */ |
1444 | if (aconnector->mst_mgr.mst_state == true) |
1445 | dc_link_reset_cur_dp_mst_topology(link); |
1446 | |
1447 | drm_modeset_lock_all(dev); |
1448 | dm_restore_drm_connector_state(dev, connector); |
1449 | drm_modeset_unlock_all(dev); |
1450 | |
1451 | drm_kms_helper_connector_hotplug_event(connector); |
1452 | } |
1453 | |
1454 | unlock: |
1455 | mutex_unlock(lock: &aconnector->hpd_lock); |
1456 | |
1457 | return size; |
1458 | } |
1459 | |
1460 | /* function: read DSC status on the connector |
1461 | * |
1462 | * The read function: dp_dsc_clock_en_read |
1463 | * returns current status of DSC clock on the connector. |
1464 | * The return is a boolean flag: 1 or 0. |
1465 | * |
1466 | * Access it with the following command (you need to specify |
1467 | * connector like DP-1): |
1468 | * |
1469 | * cat /sys/kernel/debug/dri/0/DP-X/dsc_clock_en |
1470 | * |
1471 | * Expected output: |
1472 | * 1 - means that DSC is currently enabled |
1473 | * 0 - means that DSC is disabled |
1474 | */ |
1475 | static ssize_t dp_dsc_clock_en_read(struct file *f, char __user *buf, |
1476 | size_t size, loff_t *pos) |
1477 | { |
1478 | char *rd_buf = NULL; |
1479 | char *rd_buf_ptr = NULL; |
1480 | struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private; |
1481 | struct display_stream_compressor *dsc; |
1482 | struct dcn_dsc_state dsc_state = {0}; |
1483 | const uint32_t rd_buf_size = 10; |
1484 | struct pipe_ctx *pipe_ctx; |
1485 | ssize_t result = 0; |
1486 | int i, r, str_len = 30; |
1487 | |
1488 | rd_buf = kcalloc(n: rd_buf_size, size: sizeof(char), GFP_KERNEL); |
1489 | |
1490 | if (!rd_buf) |
1491 | return -ENOMEM; |
1492 | |
1493 | rd_buf_ptr = rd_buf; |
1494 | |
1495 | for (i = 0; i < MAX_PIPES; i++) { |
1496 | pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i]; |
1497 | if (pipe_ctx->stream && |
1498 | pipe_ctx->stream->link == aconnector->dc_link) |
1499 | break; |
1500 | } |
1501 | |
1502 | dsc = pipe_ctx->stream_res.dsc; |
1503 | if (dsc) |
1504 | dsc->funcs->dsc_read_state(dsc, &dsc_state); |
1505 | |
1506 | snprintf(buf: rd_buf_ptr, size: str_len, |
1507 | fmt: "%d\n" , |
1508 | dsc_state.dsc_clock_en); |
1509 | rd_buf_ptr += str_len; |
1510 | |
1511 | while (size) { |
1512 | if (*pos >= rd_buf_size) |
1513 | break; |
1514 | |
1515 | r = put_user(*(rd_buf + result), buf); |
1516 | if (r) { |
1517 | kfree(objp: rd_buf); |
1518 | return r; /* r = -EFAULT */ |
1519 | } |
1520 | |
1521 | buf += 1; |
1522 | size -= 1; |
1523 | *pos += 1; |
1524 | result += 1; |
1525 | } |
1526 | |
1527 | kfree(objp: rd_buf); |
1528 | return result; |
1529 | } |
1530 | |
1531 | /* function: write force DSC on the connector |
1532 | * |
1533 | * The write function: dp_dsc_clock_en_write |
1534 | * enables to force DSC on the connector. |
1535 | * User can write to either force enable or force disable DSC |
1536 | * on the next modeset or set it to driver default |
1537 | * |
1538 | * Accepted inputs: |
1539 | * 0 - default DSC enablement policy |
1540 | * 1 - force enable DSC on the connector |
1541 | * 2 - force disable DSC on the connector (might cause fail in atomic_check) |
1542 | * |
1543 | * Writing DSC settings is done with the following command: |
1544 | * - To force enable DSC (you need to specify |
1545 | * connector like DP-1): |
1546 | * |
1547 | * echo 0x1 > /sys/kernel/debug/dri/0/DP-X/dsc_clock_en |
1548 | * |
1549 | * - To return to default state set the flag to zero and |
1550 | * let driver deal with DSC automatically |
1551 | * (you need to specify connector like DP-1): |
1552 | * |
1553 | * echo 0x0 > /sys/kernel/debug/dri/0/DP-X/dsc_clock_en |
1554 | * |
1555 | */ |
1556 | static ssize_t dp_dsc_clock_en_write(struct file *f, const char __user *buf, |
1557 | size_t size, loff_t *pos) |
1558 | { |
1559 | struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private; |
1560 | struct drm_connector *connector = &aconnector->base; |
1561 | struct drm_device *dev = connector->dev; |
1562 | struct drm_crtc *crtc = NULL; |
1563 | struct dm_crtc_state *dm_crtc_state = NULL; |
1564 | struct pipe_ctx *pipe_ctx; |
1565 | int i; |
1566 | char *wr_buf = NULL; |
1567 | uint32_t wr_buf_size = 42; |
1568 | int max_param_num = 1; |
1569 | long param[1] = {0}; |
1570 | uint8_t param_nums = 0; |
1571 | |
1572 | if (size == 0) |
1573 | return -EINVAL; |
1574 | |
1575 | wr_buf = kcalloc(n: wr_buf_size, size: sizeof(char), GFP_KERNEL); |
1576 | |
1577 | if (!wr_buf) { |
1578 | DRM_DEBUG_DRIVER("no memory to allocate write buffer\n" ); |
1579 | return -ENOSPC; |
1580 | } |
1581 | |
1582 | if (parse_write_buffer_into_params(wr_buf, wr_buf_size, |
1583 | param: (long *)param, buf, |
1584 | max_param_num, |
1585 | param_nums: ¶m_nums)) { |
1586 | kfree(objp: wr_buf); |
1587 | return -EINVAL; |
1588 | } |
1589 | |
1590 | if (param_nums <= 0) { |
1591 | DRM_DEBUG_DRIVER("user data not be read\n" ); |
1592 | kfree(objp: wr_buf); |
1593 | return -EINVAL; |
1594 | } |
1595 | |
1596 | for (i = 0; i < MAX_PIPES; i++) { |
1597 | pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i]; |
1598 | if (pipe_ctx->stream && |
1599 | pipe_ctx->stream->link == aconnector->dc_link) |
1600 | break; |
1601 | } |
1602 | |
1603 | if (!pipe_ctx->stream) |
1604 | goto done; |
1605 | |
1606 | // Get CRTC state |
1607 | mutex_lock(&dev->mode_config.mutex); |
1608 | drm_modeset_lock(lock: &dev->mode_config.connection_mutex, NULL); |
1609 | |
1610 | if (connector->state == NULL) |
1611 | goto unlock; |
1612 | |
1613 | crtc = connector->state->crtc; |
1614 | if (crtc == NULL) |
1615 | goto unlock; |
1616 | |
1617 | drm_modeset_lock(lock: &crtc->mutex, NULL); |
1618 | if (crtc->state == NULL) |
1619 | goto unlock; |
1620 | |
1621 | dm_crtc_state = to_dm_crtc_state(crtc->state); |
1622 | if (dm_crtc_state->stream == NULL) |
1623 | goto unlock; |
1624 | |
1625 | if (param[0] == 1) |
1626 | aconnector->dsc_settings.dsc_force_enable = DSC_CLK_FORCE_ENABLE; |
1627 | else if (param[0] == 2) |
1628 | aconnector->dsc_settings.dsc_force_enable = DSC_CLK_FORCE_DISABLE; |
1629 | else |
1630 | aconnector->dsc_settings.dsc_force_enable = DSC_CLK_FORCE_DEFAULT; |
1631 | |
1632 | dm_crtc_state->dsc_force_changed = true; |
1633 | |
1634 | unlock: |
1635 | if (crtc) |
1636 | drm_modeset_unlock(lock: &crtc->mutex); |
1637 | drm_modeset_unlock(lock: &dev->mode_config.connection_mutex); |
1638 | mutex_unlock(lock: &dev->mode_config.mutex); |
1639 | |
1640 | done: |
1641 | kfree(objp: wr_buf); |
1642 | return size; |
1643 | } |
1644 | |
1645 | /* function: read DSC slice width parameter on the connector |
1646 | * |
1647 | * The read function: dp_dsc_slice_width_read |
1648 | * returns dsc slice width used in the current configuration |
1649 | * The return is an integer: 0 or other positive number |
1650 | * |
1651 | * Access the status with the following command: |
1652 | * |
1653 | * cat /sys/kernel/debug/dri/0/DP-X/dsc_slice_width |
1654 | * |
1655 | * 0 - means that DSC is disabled |
1656 | * |
1657 | * Any other number more than zero represents the |
1658 | * slice width currently used by DSC in pixels |
1659 | * |
1660 | */ |
1661 | static ssize_t dp_dsc_slice_width_read(struct file *f, char __user *buf, |
1662 | size_t size, loff_t *pos) |
1663 | { |
1664 | char *rd_buf = NULL; |
1665 | char *rd_buf_ptr = NULL; |
1666 | struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private; |
1667 | struct display_stream_compressor *dsc; |
1668 | struct dcn_dsc_state dsc_state = {0}; |
1669 | const uint32_t rd_buf_size = 100; |
1670 | struct pipe_ctx *pipe_ctx; |
1671 | ssize_t result = 0; |
1672 | int i, r, str_len = 30; |
1673 | |
1674 | rd_buf = kcalloc(n: rd_buf_size, size: sizeof(char), GFP_KERNEL); |
1675 | |
1676 | if (!rd_buf) |
1677 | return -ENOMEM; |
1678 | |
1679 | rd_buf_ptr = rd_buf; |
1680 | |
1681 | for (i = 0; i < MAX_PIPES; i++) { |
1682 | pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i]; |
1683 | if (pipe_ctx->stream && |
1684 | pipe_ctx->stream->link == aconnector->dc_link) |
1685 | break; |
1686 | } |
1687 | |
1688 | dsc = pipe_ctx->stream_res.dsc; |
1689 | if (dsc) |
1690 | dsc->funcs->dsc_read_state(dsc, &dsc_state); |
1691 | |
1692 | snprintf(buf: rd_buf_ptr, size: str_len, |
1693 | fmt: "%d\n" , |
1694 | dsc_state.dsc_slice_width); |
1695 | rd_buf_ptr += str_len; |
1696 | |
1697 | while (size) { |
1698 | if (*pos >= rd_buf_size) |
1699 | break; |
1700 | |
1701 | r = put_user(*(rd_buf + result), buf); |
1702 | if (r) { |
1703 | kfree(objp: rd_buf); |
1704 | return r; /* r = -EFAULT */ |
1705 | } |
1706 | |
1707 | buf += 1; |
1708 | size -= 1; |
1709 | *pos += 1; |
1710 | result += 1; |
1711 | } |
1712 | |
1713 | kfree(objp: rd_buf); |
1714 | return result; |
1715 | } |
1716 | |
1717 | /* function: write DSC slice width parameter |
1718 | * |
1719 | * The write function: dp_dsc_slice_width_write |
1720 | * overwrites automatically generated DSC configuration |
1721 | * of slice width. |
1722 | * |
1723 | * The user has to write the slice width divisible by the |
1724 | * picture width. |
1725 | * |
1726 | * Also the user has to write width in hexidecimal |
1727 | * rather than in decimal. |
1728 | * |
1729 | * Writing DSC settings is done with the following command: |
1730 | * - To force overwrite slice width: (example sets to 1920 pixels) |
1731 | * |
1732 | * echo 0x780 > /sys/kernel/debug/dri/0/DP-X/dsc_slice_width |
1733 | * |
1734 | * - To stop overwriting and let driver find the optimal size, |
1735 | * set the width to zero: |
1736 | * |
1737 | * echo 0x0 > /sys/kernel/debug/dri/0/DP-X/dsc_slice_width |
1738 | * |
1739 | */ |
1740 | static ssize_t dp_dsc_slice_width_write(struct file *f, const char __user *buf, |
1741 | size_t size, loff_t *pos) |
1742 | { |
1743 | struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private; |
1744 | struct pipe_ctx *pipe_ctx; |
1745 | struct drm_connector *connector = &aconnector->base; |
1746 | struct drm_device *dev = connector->dev; |
1747 | struct drm_crtc *crtc = NULL; |
1748 | struct dm_crtc_state *dm_crtc_state = NULL; |
1749 | int i; |
1750 | char *wr_buf = NULL; |
1751 | uint32_t wr_buf_size = 42; |
1752 | int max_param_num = 1; |
1753 | long param[1] = {0}; |
1754 | uint8_t param_nums = 0; |
1755 | |
1756 | if (size == 0) |
1757 | return -EINVAL; |
1758 | |
1759 | wr_buf = kcalloc(n: wr_buf_size, size: sizeof(char), GFP_KERNEL); |
1760 | |
1761 | if (!wr_buf) { |
1762 | DRM_DEBUG_DRIVER("no memory to allocate write buffer\n" ); |
1763 | return -ENOSPC; |
1764 | } |
1765 | |
1766 | if (parse_write_buffer_into_params(wr_buf, wr_buf_size, |
1767 | param: (long *)param, buf, |
1768 | max_param_num, |
1769 | param_nums: ¶m_nums)) { |
1770 | kfree(objp: wr_buf); |
1771 | return -EINVAL; |
1772 | } |
1773 | |
1774 | if (param_nums <= 0) { |
1775 | DRM_DEBUG_DRIVER("user data not be read\n" ); |
1776 | kfree(objp: wr_buf); |
1777 | return -EINVAL; |
1778 | } |
1779 | |
1780 | for (i = 0; i < MAX_PIPES; i++) { |
1781 | pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i]; |
1782 | if (pipe_ctx->stream && |
1783 | pipe_ctx->stream->link == aconnector->dc_link) |
1784 | break; |
1785 | } |
1786 | |
1787 | if (!pipe_ctx->stream) |
1788 | goto done; |
1789 | |
1790 | // Safely get CRTC state |
1791 | mutex_lock(&dev->mode_config.mutex); |
1792 | drm_modeset_lock(lock: &dev->mode_config.connection_mutex, NULL); |
1793 | |
1794 | if (connector->state == NULL) |
1795 | goto unlock; |
1796 | |
1797 | crtc = connector->state->crtc; |
1798 | if (crtc == NULL) |
1799 | goto unlock; |
1800 | |
1801 | drm_modeset_lock(lock: &crtc->mutex, NULL); |
1802 | if (crtc->state == NULL) |
1803 | goto unlock; |
1804 | |
1805 | dm_crtc_state = to_dm_crtc_state(crtc->state); |
1806 | if (dm_crtc_state->stream == NULL) |
1807 | goto unlock; |
1808 | |
1809 | if (param[0] > 0) |
1810 | aconnector->dsc_settings.dsc_num_slices_h = DIV_ROUND_UP( |
1811 | pipe_ctx->stream->timing.h_addressable, |
1812 | param[0]); |
1813 | else |
1814 | aconnector->dsc_settings.dsc_num_slices_h = 0; |
1815 | |
1816 | dm_crtc_state->dsc_force_changed = true; |
1817 | |
1818 | unlock: |
1819 | if (crtc) |
1820 | drm_modeset_unlock(lock: &crtc->mutex); |
1821 | drm_modeset_unlock(lock: &dev->mode_config.connection_mutex); |
1822 | mutex_unlock(lock: &dev->mode_config.mutex); |
1823 | |
1824 | done: |
1825 | kfree(objp: wr_buf); |
1826 | return size; |
1827 | } |
1828 | |
1829 | /* function: read DSC slice height parameter on the connector |
1830 | * |
1831 | * The read function: dp_dsc_slice_height_read |
1832 | * returns dsc slice height used in the current configuration |
1833 | * The return is an integer: 0 or other positive number |
1834 | * |
1835 | * Access the status with the following command: |
1836 | * |
1837 | * cat /sys/kernel/debug/dri/0/DP-X/dsc_slice_height |
1838 | * |
1839 | * 0 - means that DSC is disabled |
1840 | * |
1841 | * Any other number more than zero represents the |
1842 | * slice height currently used by DSC in pixels |
1843 | * |
1844 | */ |
1845 | static ssize_t dp_dsc_slice_height_read(struct file *f, char __user *buf, |
1846 | size_t size, loff_t *pos) |
1847 | { |
1848 | char *rd_buf = NULL; |
1849 | char *rd_buf_ptr = NULL; |
1850 | struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private; |
1851 | struct display_stream_compressor *dsc; |
1852 | struct dcn_dsc_state dsc_state = {0}; |
1853 | const uint32_t rd_buf_size = 100; |
1854 | struct pipe_ctx *pipe_ctx; |
1855 | ssize_t result = 0; |
1856 | int i, r, str_len = 30; |
1857 | |
1858 | rd_buf = kcalloc(n: rd_buf_size, size: sizeof(char), GFP_KERNEL); |
1859 | |
1860 | if (!rd_buf) |
1861 | return -ENOMEM; |
1862 | |
1863 | rd_buf_ptr = rd_buf; |
1864 | |
1865 | for (i = 0; i < MAX_PIPES; i++) { |
1866 | pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i]; |
1867 | if (pipe_ctx->stream && |
1868 | pipe_ctx->stream->link == aconnector->dc_link) |
1869 | break; |
1870 | } |
1871 | |
1872 | dsc = pipe_ctx->stream_res.dsc; |
1873 | if (dsc) |
1874 | dsc->funcs->dsc_read_state(dsc, &dsc_state); |
1875 | |
1876 | snprintf(buf: rd_buf_ptr, size: str_len, |
1877 | fmt: "%d\n" , |
1878 | dsc_state.dsc_slice_height); |
1879 | rd_buf_ptr += str_len; |
1880 | |
1881 | while (size) { |
1882 | if (*pos >= rd_buf_size) |
1883 | break; |
1884 | |
1885 | r = put_user(*(rd_buf + result), buf); |
1886 | if (r) { |
1887 | kfree(objp: rd_buf); |
1888 | return r; /* r = -EFAULT */ |
1889 | } |
1890 | |
1891 | buf += 1; |
1892 | size -= 1; |
1893 | *pos += 1; |
1894 | result += 1; |
1895 | } |
1896 | |
1897 | kfree(objp: rd_buf); |
1898 | return result; |
1899 | } |
1900 | |
1901 | /* function: write DSC slice height parameter |
1902 | * |
1903 | * The write function: dp_dsc_slice_height_write |
1904 | * overwrites automatically generated DSC configuration |
1905 | * of slice height. |
1906 | * |
1907 | * The user has to write the slice height divisible by the |
1908 | * picture height. |
1909 | * |
1910 | * Also the user has to write height in hexidecimal |
1911 | * rather than in decimal. |
1912 | * |
1913 | * Writing DSC settings is done with the following command: |
1914 | * - To force overwrite slice height (example sets to 128 pixels): |
1915 | * |
1916 | * echo 0x80 > /sys/kernel/debug/dri/0/DP-X/dsc_slice_height |
1917 | * |
1918 | * - To stop overwriting and let driver find the optimal size, |
1919 | * set the height to zero: |
1920 | * |
1921 | * echo 0x0 > /sys/kernel/debug/dri/0/DP-X/dsc_slice_height |
1922 | * |
1923 | */ |
1924 | static ssize_t dp_dsc_slice_height_write(struct file *f, const char __user *buf, |
1925 | size_t size, loff_t *pos) |
1926 | { |
1927 | struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private; |
1928 | struct drm_connector *connector = &aconnector->base; |
1929 | struct drm_device *dev = connector->dev; |
1930 | struct drm_crtc *crtc = NULL; |
1931 | struct dm_crtc_state *dm_crtc_state = NULL; |
1932 | struct pipe_ctx *pipe_ctx; |
1933 | int i; |
1934 | char *wr_buf = NULL; |
1935 | uint32_t wr_buf_size = 42; |
1936 | int max_param_num = 1; |
1937 | uint8_t param_nums = 0; |
1938 | long param[1] = {0}; |
1939 | |
1940 | if (size == 0) |
1941 | return -EINVAL; |
1942 | |
1943 | wr_buf = kcalloc(n: wr_buf_size, size: sizeof(char), GFP_KERNEL); |
1944 | |
1945 | if (!wr_buf) { |
1946 | DRM_DEBUG_DRIVER("no memory to allocate write buffer\n" ); |
1947 | return -ENOSPC; |
1948 | } |
1949 | |
1950 | if (parse_write_buffer_into_params(wr_buf, wr_buf_size, |
1951 | param: (long *)param, buf, |
1952 | max_param_num, |
1953 | param_nums: ¶m_nums)) { |
1954 | kfree(objp: wr_buf); |
1955 | return -EINVAL; |
1956 | } |
1957 | |
1958 | if (param_nums <= 0) { |
1959 | DRM_DEBUG_DRIVER("user data not be read\n" ); |
1960 | kfree(objp: wr_buf); |
1961 | return -EINVAL; |
1962 | } |
1963 | |
1964 | for (i = 0; i < MAX_PIPES; i++) { |
1965 | pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i]; |
1966 | if (pipe_ctx->stream && |
1967 | pipe_ctx->stream->link == aconnector->dc_link) |
1968 | break; |
1969 | } |
1970 | |
1971 | if (!pipe_ctx->stream) |
1972 | goto done; |
1973 | |
1974 | // Get CRTC state |
1975 | mutex_lock(&dev->mode_config.mutex); |
1976 | drm_modeset_lock(lock: &dev->mode_config.connection_mutex, NULL); |
1977 | |
1978 | if (connector->state == NULL) |
1979 | goto unlock; |
1980 | |
1981 | crtc = connector->state->crtc; |
1982 | if (crtc == NULL) |
1983 | goto unlock; |
1984 | |
1985 | drm_modeset_lock(lock: &crtc->mutex, NULL); |
1986 | if (crtc->state == NULL) |
1987 | goto unlock; |
1988 | |
1989 | dm_crtc_state = to_dm_crtc_state(crtc->state); |
1990 | if (dm_crtc_state->stream == NULL) |
1991 | goto unlock; |
1992 | |
1993 | if (param[0] > 0) |
1994 | aconnector->dsc_settings.dsc_num_slices_v = DIV_ROUND_UP( |
1995 | pipe_ctx->stream->timing.v_addressable, |
1996 | param[0]); |
1997 | else |
1998 | aconnector->dsc_settings.dsc_num_slices_v = 0; |
1999 | |
2000 | dm_crtc_state->dsc_force_changed = true; |
2001 | |
2002 | unlock: |
2003 | if (crtc) |
2004 | drm_modeset_unlock(lock: &crtc->mutex); |
2005 | drm_modeset_unlock(lock: &dev->mode_config.connection_mutex); |
2006 | mutex_unlock(lock: &dev->mode_config.mutex); |
2007 | |
2008 | done: |
2009 | kfree(objp: wr_buf); |
2010 | return size; |
2011 | } |
2012 | |
2013 | /* function: read DSC target rate on the connector in bits per pixel |
2014 | * |
2015 | * The read function: dp_dsc_bits_per_pixel_read |
2016 | * returns target rate of compression in bits per pixel |
2017 | * The return is an integer: 0 or other positive integer |
2018 | * |
2019 | * Access it with the following command: |
2020 | * |
2021 | * cat /sys/kernel/debug/dri/0/DP-X/dsc_bits_per_pixel |
2022 | * |
2023 | * 0 - means that DSC is disabled |
2024 | */ |
2025 | static ssize_t dp_dsc_bits_per_pixel_read(struct file *f, char __user *buf, |
2026 | size_t size, loff_t *pos) |
2027 | { |
2028 | char *rd_buf = NULL; |
2029 | char *rd_buf_ptr = NULL; |
2030 | struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private; |
2031 | struct display_stream_compressor *dsc; |
2032 | struct dcn_dsc_state dsc_state = {0}; |
2033 | const uint32_t rd_buf_size = 100; |
2034 | struct pipe_ctx *pipe_ctx; |
2035 | ssize_t result = 0; |
2036 | int i, r, str_len = 30; |
2037 | |
2038 | rd_buf = kcalloc(n: rd_buf_size, size: sizeof(char), GFP_KERNEL); |
2039 | |
2040 | if (!rd_buf) |
2041 | return -ENOMEM; |
2042 | |
2043 | rd_buf_ptr = rd_buf; |
2044 | |
2045 | for (i = 0; i < MAX_PIPES; i++) { |
2046 | pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i]; |
2047 | if (pipe_ctx->stream && |
2048 | pipe_ctx->stream->link == aconnector->dc_link) |
2049 | break; |
2050 | } |
2051 | |
2052 | dsc = pipe_ctx->stream_res.dsc; |
2053 | if (dsc) |
2054 | dsc->funcs->dsc_read_state(dsc, &dsc_state); |
2055 | |
2056 | snprintf(buf: rd_buf_ptr, size: str_len, |
2057 | fmt: "%d\n" , |
2058 | dsc_state.dsc_bits_per_pixel); |
2059 | rd_buf_ptr += str_len; |
2060 | |
2061 | while (size) { |
2062 | if (*pos >= rd_buf_size) |
2063 | break; |
2064 | |
2065 | r = put_user(*(rd_buf + result), buf); |
2066 | if (r) { |
2067 | kfree(objp: rd_buf); |
2068 | return r; /* r = -EFAULT */ |
2069 | } |
2070 | |
2071 | buf += 1; |
2072 | size -= 1; |
2073 | *pos += 1; |
2074 | result += 1; |
2075 | } |
2076 | |
2077 | kfree(objp: rd_buf); |
2078 | return result; |
2079 | } |
2080 | |
2081 | /* function: write DSC target rate in bits per pixel |
2082 | * |
2083 | * The write function: dp_dsc_bits_per_pixel_write |
2084 | * overwrites automatically generated DSC configuration |
2085 | * of DSC target bit rate. |
2086 | * |
2087 | * Also the user has to write bpp in hexidecimal |
2088 | * rather than in decimal. |
2089 | * |
2090 | * Writing DSC settings is done with the following command: |
2091 | * - To force overwrite rate (example sets to 256 bpp x 1/16): |
2092 | * |
2093 | * echo 0x100 > /sys/kernel/debug/dri/0/DP-X/dsc_bits_per_pixel |
2094 | * |
2095 | * - To stop overwriting and let driver find the optimal rate, |
2096 | * set the rate to zero: |
2097 | * |
2098 | * echo 0x0 > /sys/kernel/debug/dri/0/DP-X/dsc_bits_per_pixel |
2099 | * |
2100 | */ |
2101 | static ssize_t dp_dsc_bits_per_pixel_write(struct file *f, const char __user *buf, |
2102 | size_t size, loff_t *pos) |
2103 | { |
2104 | struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private; |
2105 | struct drm_connector *connector = &aconnector->base; |
2106 | struct drm_device *dev = connector->dev; |
2107 | struct drm_crtc *crtc = NULL; |
2108 | struct dm_crtc_state *dm_crtc_state = NULL; |
2109 | struct pipe_ctx *pipe_ctx; |
2110 | int i; |
2111 | char *wr_buf = NULL; |
2112 | uint32_t wr_buf_size = 42; |
2113 | int max_param_num = 1; |
2114 | uint8_t param_nums = 0; |
2115 | long param[1] = {0}; |
2116 | |
2117 | if (size == 0) |
2118 | return -EINVAL; |
2119 | |
2120 | wr_buf = kcalloc(n: wr_buf_size, size: sizeof(char), GFP_KERNEL); |
2121 | |
2122 | if (!wr_buf) { |
2123 | DRM_DEBUG_DRIVER("no memory to allocate write buffer\n" ); |
2124 | return -ENOSPC; |
2125 | } |
2126 | |
2127 | if (parse_write_buffer_into_params(wr_buf, wr_buf_size, |
2128 | param: (long *)param, buf, |
2129 | max_param_num, |
2130 | param_nums: ¶m_nums)) { |
2131 | kfree(objp: wr_buf); |
2132 | return -EINVAL; |
2133 | } |
2134 | |
2135 | if (param_nums <= 0) { |
2136 | DRM_DEBUG_DRIVER("user data not be read\n" ); |
2137 | kfree(objp: wr_buf); |
2138 | return -EINVAL; |
2139 | } |
2140 | |
2141 | for (i = 0; i < MAX_PIPES; i++) { |
2142 | pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i]; |
2143 | if (pipe_ctx->stream && |
2144 | pipe_ctx->stream->link == aconnector->dc_link) |
2145 | break; |
2146 | } |
2147 | |
2148 | if (!pipe_ctx->stream) |
2149 | goto done; |
2150 | |
2151 | // Get CRTC state |
2152 | mutex_lock(&dev->mode_config.mutex); |
2153 | drm_modeset_lock(lock: &dev->mode_config.connection_mutex, NULL); |
2154 | |
2155 | if (connector->state == NULL) |
2156 | goto unlock; |
2157 | |
2158 | crtc = connector->state->crtc; |
2159 | if (crtc == NULL) |
2160 | goto unlock; |
2161 | |
2162 | drm_modeset_lock(lock: &crtc->mutex, NULL); |
2163 | if (crtc->state == NULL) |
2164 | goto unlock; |
2165 | |
2166 | dm_crtc_state = to_dm_crtc_state(crtc->state); |
2167 | if (dm_crtc_state->stream == NULL) |
2168 | goto unlock; |
2169 | |
2170 | aconnector->dsc_settings.dsc_bits_per_pixel = param[0]; |
2171 | |
2172 | dm_crtc_state->dsc_force_changed = true; |
2173 | |
2174 | unlock: |
2175 | if (crtc) |
2176 | drm_modeset_unlock(lock: &crtc->mutex); |
2177 | drm_modeset_unlock(lock: &dev->mode_config.connection_mutex); |
2178 | mutex_unlock(lock: &dev->mode_config.mutex); |
2179 | |
2180 | done: |
2181 | kfree(objp: wr_buf); |
2182 | return size; |
2183 | } |
2184 | |
2185 | /* function: read DSC picture width parameter on the connector |
2186 | * |
2187 | * The read function: dp_dsc_pic_width_read |
2188 | * returns dsc picture width used in the current configuration |
2189 | * It is the same as h_addressable of the current |
2190 | * display's timing |
2191 | * The return is an integer: 0 or other positive integer |
2192 | * If 0 then DSC is disabled. |
2193 | * |
2194 | * Access it with the following command: |
2195 | * |
2196 | * cat /sys/kernel/debug/dri/0/DP-X/dsc_pic_width |
2197 | * |
2198 | * 0 - means that DSC is disabled |
2199 | */ |
2200 | static ssize_t dp_dsc_pic_width_read(struct file *f, char __user *buf, |
2201 | size_t size, loff_t *pos) |
2202 | { |
2203 | char *rd_buf = NULL; |
2204 | char *rd_buf_ptr = NULL; |
2205 | struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private; |
2206 | struct display_stream_compressor *dsc; |
2207 | struct dcn_dsc_state dsc_state = {0}; |
2208 | const uint32_t rd_buf_size = 100; |
2209 | struct pipe_ctx *pipe_ctx; |
2210 | ssize_t result = 0; |
2211 | int i, r, str_len = 30; |
2212 | |
2213 | rd_buf = kcalloc(n: rd_buf_size, size: sizeof(char), GFP_KERNEL); |
2214 | |
2215 | if (!rd_buf) |
2216 | return -ENOMEM; |
2217 | |
2218 | rd_buf_ptr = rd_buf; |
2219 | |
2220 | for (i = 0; i < MAX_PIPES; i++) { |
2221 | pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i]; |
2222 | if (pipe_ctx->stream && |
2223 | pipe_ctx->stream->link == aconnector->dc_link) |
2224 | break; |
2225 | } |
2226 | |
2227 | dsc = pipe_ctx->stream_res.dsc; |
2228 | if (dsc) |
2229 | dsc->funcs->dsc_read_state(dsc, &dsc_state); |
2230 | |
2231 | snprintf(buf: rd_buf_ptr, size: str_len, |
2232 | fmt: "%d\n" , |
2233 | dsc_state.dsc_pic_width); |
2234 | rd_buf_ptr += str_len; |
2235 | |
2236 | while (size) { |
2237 | if (*pos >= rd_buf_size) |
2238 | break; |
2239 | |
2240 | r = put_user(*(rd_buf + result), buf); |
2241 | if (r) { |
2242 | kfree(objp: rd_buf); |
2243 | return r; /* r = -EFAULT */ |
2244 | } |
2245 | |
2246 | buf += 1; |
2247 | size -= 1; |
2248 | *pos += 1; |
2249 | result += 1; |
2250 | } |
2251 | |
2252 | kfree(objp: rd_buf); |
2253 | return result; |
2254 | } |
2255 | |
2256 | static ssize_t dp_dsc_pic_height_read(struct file *f, char __user *buf, |
2257 | size_t size, loff_t *pos) |
2258 | { |
2259 | char *rd_buf = NULL; |
2260 | char *rd_buf_ptr = NULL; |
2261 | struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private; |
2262 | struct display_stream_compressor *dsc; |
2263 | struct dcn_dsc_state dsc_state = {0}; |
2264 | const uint32_t rd_buf_size = 100; |
2265 | struct pipe_ctx *pipe_ctx; |
2266 | ssize_t result = 0; |
2267 | int i, r, str_len = 30; |
2268 | |
2269 | rd_buf = kcalloc(n: rd_buf_size, size: sizeof(char), GFP_KERNEL); |
2270 | |
2271 | if (!rd_buf) |
2272 | return -ENOMEM; |
2273 | |
2274 | rd_buf_ptr = rd_buf; |
2275 | |
2276 | for (i = 0; i < MAX_PIPES; i++) { |
2277 | pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i]; |
2278 | if (pipe_ctx->stream && |
2279 | pipe_ctx->stream->link == aconnector->dc_link) |
2280 | break; |
2281 | } |
2282 | |
2283 | dsc = pipe_ctx->stream_res.dsc; |
2284 | if (dsc) |
2285 | dsc->funcs->dsc_read_state(dsc, &dsc_state); |
2286 | |
2287 | snprintf(buf: rd_buf_ptr, size: str_len, |
2288 | fmt: "%d\n" , |
2289 | dsc_state.dsc_pic_height); |
2290 | rd_buf_ptr += str_len; |
2291 | |
2292 | while (size) { |
2293 | if (*pos >= rd_buf_size) |
2294 | break; |
2295 | |
2296 | r = put_user(*(rd_buf + result), buf); |
2297 | if (r) { |
2298 | kfree(objp: rd_buf); |
2299 | return r; /* r = -EFAULT */ |
2300 | } |
2301 | |
2302 | buf += 1; |
2303 | size -= 1; |
2304 | *pos += 1; |
2305 | result += 1; |
2306 | } |
2307 | |
2308 | kfree(objp: rd_buf); |
2309 | return result; |
2310 | } |
2311 | |
2312 | /* function: read DSC chunk size parameter on the connector |
2313 | * |
2314 | * The read function: dp_dsc_chunk_size_read |
2315 | * returns dsc chunk size set in the current configuration |
2316 | * The value is calculated automatically by DSC code |
2317 | * and depends on slice parameters and bpp target rate |
2318 | * The return is an integer: 0 or other positive integer |
2319 | * If 0 then DSC is disabled. |
2320 | * |
2321 | * Access it with the following command: |
2322 | * |
2323 | * cat /sys/kernel/debug/dri/0/DP-X/dsc_chunk_size |
2324 | * |
2325 | * 0 - means that DSC is disabled |
2326 | */ |
2327 | static ssize_t dp_dsc_chunk_size_read(struct file *f, char __user *buf, |
2328 | size_t size, loff_t *pos) |
2329 | { |
2330 | char *rd_buf = NULL; |
2331 | char *rd_buf_ptr = NULL; |
2332 | struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private; |
2333 | struct display_stream_compressor *dsc; |
2334 | struct dcn_dsc_state dsc_state = {0}; |
2335 | const uint32_t rd_buf_size = 100; |
2336 | struct pipe_ctx *pipe_ctx; |
2337 | ssize_t result = 0; |
2338 | int i, r, str_len = 30; |
2339 | |
2340 | rd_buf = kcalloc(n: rd_buf_size, size: sizeof(char), GFP_KERNEL); |
2341 | |
2342 | if (!rd_buf) |
2343 | return -ENOMEM; |
2344 | |
2345 | rd_buf_ptr = rd_buf; |
2346 | |
2347 | for (i = 0; i < MAX_PIPES; i++) { |
2348 | pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i]; |
2349 | if (pipe_ctx->stream && |
2350 | pipe_ctx->stream->link == aconnector->dc_link) |
2351 | break; |
2352 | } |
2353 | |
2354 | dsc = pipe_ctx->stream_res.dsc; |
2355 | if (dsc) |
2356 | dsc->funcs->dsc_read_state(dsc, &dsc_state); |
2357 | |
2358 | snprintf(buf: rd_buf_ptr, size: str_len, |
2359 | fmt: "%d\n" , |
2360 | dsc_state.dsc_chunk_size); |
2361 | rd_buf_ptr += str_len; |
2362 | |
2363 | while (size) { |
2364 | if (*pos >= rd_buf_size) |
2365 | break; |
2366 | |
2367 | r = put_user(*(rd_buf + result), buf); |
2368 | if (r) { |
2369 | kfree(objp: rd_buf); |
2370 | return r; /* r = -EFAULT */ |
2371 | } |
2372 | |
2373 | buf += 1; |
2374 | size -= 1; |
2375 | *pos += 1; |
2376 | result += 1; |
2377 | } |
2378 | |
2379 | kfree(objp: rd_buf); |
2380 | return result; |
2381 | } |
2382 | |
2383 | /* function: read DSC slice bpg offset on the connector |
2384 | * |
2385 | * The read function: dp_dsc_slice_bpg_offset_read |
2386 | * returns dsc bpg slice offset set in the current configuration |
2387 | * The value is calculated automatically by DSC code |
2388 | * and depends on slice parameters and bpp target rate |
2389 | * The return is an integer: 0 or other positive integer |
2390 | * If 0 then DSC is disabled. |
2391 | * |
2392 | * Access it with the following command: |
2393 | * |
2394 | * cat /sys/kernel/debug/dri/0/DP-X/dsc_slice_bpg_offset |
2395 | * |
2396 | * 0 - means that DSC is disabled |
2397 | */ |
2398 | static ssize_t dp_dsc_slice_bpg_offset_read(struct file *f, char __user *buf, |
2399 | size_t size, loff_t *pos) |
2400 | { |
2401 | char *rd_buf = NULL; |
2402 | char *rd_buf_ptr = NULL; |
2403 | struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private; |
2404 | struct display_stream_compressor *dsc; |
2405 | struct dcn_dsc_state dsc_state = {0}; |
2406 | const uint32_t rd_buf_size = 100; |
2407 | struct pipe_ctx *pipe_ctx; |
2408 | ssize_t result = 0; |
2409 | int i, r, str_len = 30; |
2410 | |
2411 | rd_buf = kcalloc(n: rd_buf_size, size: sizeof(char), GFP_KERNEL); |
2412 | |
2413 | if (!rd_buf) |
2414 | return -ENOMEM; |
2415 | |
2416 | rd_buf_ptr = rd_buf; |
2417 | |
2418 | for (i = 0; i < MAX_PIPES; i++) { |
2419 | pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i]; |
2420 | if (pipe_ctx->stream && |
2421 | pipe_ctx->stream->link == aconnector->dc_link) |
2422 | break; |
2423 | } |
2424 | |
2425 | dsc = pipe_ctx->stream_res.dsc; |
2426 | if (dsc) |
2427 | dsc->funcs->dsc_read_state(dsc, &dsc_state); |
2428 | |
2429 | snprintf(buf: rd_buf_ptr, size: str_len, |
2430 | fmt: "%d\n" , |
2431 | dsc_state.dsc_slice_bpg_offset); |
2432 | rd_buf_ptr += str_len; |
2433 | |
2434 | while (size) { |
2435 | if (*pos >= rd_buf_size) |
2436 | break; |
2437 | |
2438 | r = put_user(*(rd_buf + result), buf); |
2439 | if (r) { |
2440 | kfree(objp: rd_buf); |
2441 | return r; /* r = -EFAULT */ |
2442 | } |
2443 | |
2444 | buf += 1; |
2445 | size -= 1; |
2446 | *pos += 1; |
2447 | result += 1; |
2448 | } |
2449 | |
2450 | kfree(objp: rd_buf); |
2451 | return result; |
2452 | } |
2453 | |
2454 | |
2455 | /* |
2456 | * function description: Read max_requested_bpc property from the connector |
2457 | * |
2458 | * Access it with the following command: |
2459 | * |
2460 | * cat /sys/kernel/debug/dri/0/DP-X/max_bpc |
2461 | * |
2462 | */ |
2463 | static ssize_t dp_max_bpc_read(struct file *f, char __user *buf, |
2464 | size_t size, loff_t *pos) |
2465 | { |
2466 | struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private; |
2467 | struct drm_connector *connector = &aconnector->base; |
2468 | struct drm_device *dev = connector->dev; |
2469 | struct dm_connector_state *state; |
2470 | ssize_t result = 0; |
2471 | char *rd_buf = NULL; |
2472 | char *rd_buf_ptr = NULL; |
2473 | const uint32_t rd_buf_size = 10; |
2474 | int r; |
2475 | |
2476 | rd_buf = kcalloc(n: rd_buf_size, size: sizeof(char), GFP_KERNEL); |
2477 | |
2478 | if (!rd_buf) |
2479 | return -ENOMEM; |
2480 | |
2481 | mutex_lock(&dev->mode_config.mutex); |
2482 | drm_modeset_lock(lock: &dev->mode_config.connection_mutex, NULL); |
2483 | |
2484 | if (connector->state == NULL) |
2485 | goto unlock; |
2486 | |
2487 | state = to_dm_connector_state(connector->state); |
2488 | |
2489 | rd_buf_ptr = rd_buf; |
2490 | snprintf(buf: rd_buf_ptr, size: rd_buf_size, |
2491 | fmt: "%u\n" , |
2492 | state->base.max_requested_bpc); |
2493 | |
2494 | while (size) { |
2495 | if (*pos >= rd_buf_size) |
2496 | break; |
2497 | |
2498 | r = put_user(*(rd_buf + result), buf); |
2499 | if (r) { |
2500 | result = r; /* r = -EFAULT */ |
2501 | goto unlock; |
2502 | } |
2503 | buf += 1; |
2504 | size -= 1; |
2505 | *pos += 1; |
2506 | result += 1; |
2507 | } |
2508 | unlock: |
2509 | drm_modeset_unlock(lock: &dev->mode_config.connection_mutex); |
2510 | mutex_unlock(lock: &dev->mode_config.mutex); |
2511 | kfree(objp: rd_buf); |
2512 | return result; |
2513 | } |
2514 | |
2515 | |
2516 | /* |
2517 | * function description: Set max_requested_bpc property on the connector |
2518 | * |
2519 | * This function will not force the input BPC on connector, it will only |
2520 | * change the max value. This is equivalent to setting max_bpc through |
2521 | * xrandr. |
2522 | * |
2523 | * The BPC value written must be >= 6 and <= 16. Values outside of this |
2524 | * range will result in errors. |
2525 | * |
2526 | * BPC values: |
2527 | * 0x6 - 6 BPC |
2528 | * 0x8 - 8 BPC |
2529 | * 0xa - 10 BPC |
2530 | * 0xc - 12 BPC |
2531 | * 0x10 - 16 BPC |
2532 | * |
2533 | * Write the max_bpc in the following way: |
2534 | * |
2535 | * echo 0x6 > /sys/kernel/debug/dri/0/DP-X/max_bpc |
2536 | * |
2537 | */ |
2538 | static ssize_t dp_max_bpc_write(struct file *f, const char __user *buf, |
2539 | size_t size, loff_t *pos) |
2540 | { |
2541 | struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private; |
2542 | struct drm_connector *connector = &aconnector->base; |
2543 | struct dm_connector_state *state; |
2544 | struct drm_device *dev = connector->dev; |
2545 | char *wr_buf = NULL; |
2546 | uint32_t wr_buf_size = 42; |
2547 | int max_param_num = 1; |
2548 | long param[1] = {0}; |
2549 | uint8_t param_nums = 0; |
2550 | |
2551 | if (size == 0) |
2552 | return -EINVAL; |
2553 | |
2554 | wr_buf = kcalloc(n: wr_buf_size, size: sizeof(char), GFP_KERNEL); |
2555 | |
2556 | if (!wr_buf) { |
2557 | DRM_DEBUG_DRIVER("no memory to allocate write buffer\n" ); |
2558 | return -ENOSPC; |
2559 | } |
2560 | |
2561 | if (parse_write_buffer_into_params(wr_buf, wr_buf_size, |
2562 | param: (long *)param, buf, |
2563 | max_param_num, |
2564 | param_nums: ¶m_nums)) { |
2565 | kfree(objp: wr_buf); |
2566 | return -EINVAL; |
2567 | } |
2568 | |
2569 | if (param_nums <= 0) { |
2570 | DRM_DEBUG_DRIVER("user data not be read\n" ); |
2571 | kfree(objp: wr_buf); |
2572 | return -EINVAL; |
2573 | } |
2574 | |
2575 | if (param[0] < 6 || param[0] > 16) { |
2576 | DRM_DEBUG_DRIVER("bad max_bpc value\n" ); |
2577 | kfree(objp: wr_buf); |
2578 | return -EINVAL; |
2579 | } |
2580 | |
2581 | mutex_lock(&dev->mode_config.mutex); |
2582 | drm_modeset_lock(lock: &dev->mode_config.connection_mutex, NULL); |
2583 | |
2584 | if (connector->state == NULL) |
2585 | goto unlock; |
2586 | |
2587 | state = to_dm_connector_state(connector->state); |
2588 | state->base.max_requested_bpc = param[0]; |
2589 | unlock: |
2590 | drm_modeset_unlock(lock: &dev->mode_config.connection_mutex); |
2591 | mutex_unlock(lock: &dev->mode_config.mutex); |
2592 | |
2593 | kfree(objp: wr_buf); |
2594 | return size; |
2595 | } |
2596 | |
2597 | /* |
2598 | * Backlight at this moment. Read only. |
2599 | * As written to display, taking ABM and backlight lut into account. |
2600 | * Ranges from 0x0 to 0x10000 (= 100% PWM) |
2601 | * |
2602 | * Example usage: cat /sys/kernel/debug/dri/0/eDP-1/current_backlight |
2603 | */ |
2604 | static int current_backlight_show(struct seq_file *m, void *unused) |
2605 | { |
2606 | struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(m->private); |
2607 | struct dc_link *link = aconnector->dc_link; |
2608 | unsigned int backlight; |
2609 | |
2610 | backlight = dc_link_get_backlight_level(dc_link: link); |
2611 | seq_printf(m, fmt: "0x%x\n" , backlight); |
2612 | |
2613 | return 0; |
2614 | } |
2615 | |
2616 | /* |
2617 | * Backlight value that is being approached. Read only. |
2618 | * As written to display, taking ABM and backlight lut into account. |
2619 | * Ranges from 0x0 to 0x10000 (= 100% PWM) |
2620 | * |
2621 | * Example usage: cat /sys/kernel/debug/dri/0/eDP-1/target_backlight |
2622 | */ |
2623 | static int target_backlight_show(struct seq_file *m, void *unused) |
2624 | { |
2625 | struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(m->private); |
2626 | struct dc_link *link = aconnector->dc_link; |
2627 | unsigned int backlight; |
2628 | |
2629 | backlight = dc_link_get_target_backlight_pwm(link); |
2630 | seq_printf(m, fmt: "0x%x\n" , backlight); |
2631 | |
2632 | return 0; |
2633 | } |
2634 | |
2635 | /* |
2636 | * function description: Determine if the connector is mst connector |
2637 | * |
2638 | * This function helps to determine whether a connector is a mst connector. |
2639 | * - "root" stands for the root connector of the topology |
2640 | * - "branch" stands for branch device of the topology |
2641 | * - "end" stands for leaf node connector of the topology |
2642 | * - "no" stands for the connector is not a device of a mst topology |
2643 | * Access it with the following command: |
2644 | * |
2645 | * cat /sys/kernel/debug/dri/0/DP-X/is_mst_connector |
2646 | * |
2647 | */ |
2648 | static int dp_is_mst_connector_show(struct seq_file *m, void *unused) |
2649 | { |
2650 | struct drm_connector *connector = m->private; |
2651 | struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); |
2652 | struct drm_dp_mst_topology_mgr *mgr = NULL; |
2653 | struct drm_dp_mst_port *port = NULL; |
2654 | char *role = NULL; |
2655 | |
2656 | mutex_lock(&aconnector->hpd_lock); |
2657 | |
2658 | if (aconnector->mst_mgr.mst_state) { |
2659 | role = "root" ; |
2660 | } else if (aconnector->mst_root && |
2661 | aconnector->mst_root->mst_mgr.mst_state) { |
2662 | |
2663 | role = "end" ; |
2664 | |
2665 | mgr = &aconnector->mst_root->mst_mgr; |
2666 | port = aconnector->mst_output_port; |
2667 | |
2668 | drm_modeset_lock(lock: &mgr->base.lock, NULL); |
2669 | if (port->pdt == DP_PEER_DEVICE_MST_BRANCHING && |
2670 | port->mcs) |
2671 | role = "branch" ; |
2672 | drm_modeset_unlock(lock: &mgr->base.lock); |
2673 | |
2674 | } else { |
2675 | role = "no" ; |
2676 | } |
2677 | |
2678 | seq_printf(m, fmt: "%s\n" , role); |
2679 | |
2680 | mutex_unlock(lock: &aconnector->hpd_lock); |
2681 | |
2682 | return 0; |
2683 | } |
2684 | |
2685 | /* |
2686 | * function description: Read out the mst progress status |
2687 | * |
2688 | * This function helps to determine the mst progress status of |
2689 | * a mst connector. |
2690 | * |
2691 | * Access it with the following command: |
2692 | * |
2693 | * cat /sys/kernel/debug/dri/0/DP-X/mst_progress_status |
2694 | * |
2695 | */ |
2696 | static int dp_mst_progress_status_show(struct seq_file *m, void *unused) |
2697 | { |
2698 | struct drm_connector *connector = m->private; |
2699 | struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); |
2700 | struct amdgpu_device *adev = drm_to_adev(ddev: connector->dev); |
2701 | int i; |
2702 | |
2703 | mutex_lock(&aconnector->hpd_lock); |
2704 | mutex_lock(&adev->dm.dc_lock); |
2705 | |
2706 | if (aconnector->mst_status == MST_STATUS_DEFAULT) { |
2707 | seq_puts(m, s: "disabled\n" ); |
2708 | } else { |
2709 | for (i = 0; i < sizeof(mst_progress_status)/sizeof(char *); i++) |
2710 | seq_printf(m, fmt: "%s:%s\n" , |
2711 | mst_progress_status[i], |
2712 | aconnector->mst_status & BIT(i) ? "done" : "not_done" ); |
2713 | } |
2714 | |
2715 | mutex_unlock(lock: &adev->dm.dc_lock); |
2716 | mutex_unlock(lock: &aconnector->hpd_lock); |
2717 | |
2718 | return 0; |
2719 | } |
2720 | |
2721 | /* |
2722 | * Reports whether the connected display is a USB4 DPIA tunneled display |
2723 | * Example usage: cat /sys/kernel/debug/dri/0/DP-8/is_dpia_link |
2724 | */ |
2725 | static int is_dpia_link_show(struct seq_file *m, void *data) |
2726 | { |
2727 | struct drm_connector *connector = m->private; |
2728 | struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); |
2729 | struct dc_link *link = aconnector->dc_link; |
2730 | |
2731 | if (connector->status != connector_status_connected) |
2732 | return -ENODEV; |
2733 | |
2734 | seq_printf(m, fmt: "%s\n" , (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) ? "yes" : |
2735 | (link->ep_type == DISPLAY_ENDPOINT_PHY) ? "no" : "unknown" ); |
2736 | |
2737 | return 0; |
2738 | } |
2739 | |
2740 | DEFINE_SHOW_ATTRIBUTE(dp_dsc_fec_support); |
2741 | DEFINE_SHOW_ATTRIBUTE(dmub_fw_state); |
2742 | DEFINE_SHOW_ATTRIBUTE(dmub_tracebuffer); |
2743 | DEFINE_SHOW_ATTRIBUTE(dp_lttpr_status); |
2744 | DEFINE_SHOW_ATTRIBUTE(hdcp_sink_capability); |
2745 | DEFINE_SHOW_ATTRIBUTE(internal_display); |
2746 | DEFINE_SHOW_ATTRIBUTE(odm_combine_segments); |
2747 | DEFINE_SHOW_ATTRIBUTE(psr_capability); |
2748 | DEFINE_SHOW_ATTRIBUTE(dp_is_mst_connector); |
2749 | DEFINE_SHOW_ATTRIBUTE(dp_mst_progress_status); |
2750 | DEFINE_SHOW_ATTRIBUTE(is_dpia_link); |
2751 | |
2752 | static const struct file_operations dp_dsc_clock_en_debugfs_fops = { |
2753 | .owner = THIS_MODULE, |
2754 | .read = dp_dsc_clock_en_read, |
2755 | .write = dp_dsc_clock_en_write, |
2756 | .llseek = default_llseek |
2757 | }; |
2758 | |
2759 | static const struct file_operations dp_dsc_slice_width_debugfs_fops = { |
2760 | .owner = THIS_MODULE, |
2761 | .read = dp_dsc_slice_width_read, |
2762 | .write = dp_dsc_slice_width_write, |
2763 | .llseek = default_llseek |
2764 | }; |
2765 | |
2766 | static const struct file_operations dp_dsc_slice_height_debugfs_fops = { |
2767 | .owner = THIS_MODULE, |
2768 | .read = dp_dsc_slice_height_read, |
2769 | .write = dp_dsc_slice_height_write, |
2770 | .llseek = default_llseek |
2771 | }; |
2772 | |
2773 | static const struct file_operations dp_dsc_bits_per_pixel_debugfs_fops = { |
2774 | .owner = THIS_MODULE, |
2775 | .read = dp_dsc_bits_per_pixel_read, |
2776 | .write = dp_dsc_bits_per_pixel_write, |
2777 | .llseek = default_llseek |
2778 | }; |
2779 | |
2780 | static const struct file_operations dp_dsc_pic_width_debugfs_fops = { |
2781 | .owner = THIS_MODULE, |
2782 | .read = dp_dsc_pic_width_read, |
2783 | .llseek = default_llseek |
2784 | }; |
2785 | |
2786 | static const struct file_operations dp_dsc_pic_height_debugfs_fops = { |
2787 | .owner = THIS_MODULE, |
2788 | .read = dp_dsc_pic_height_read, |
2789 | .llseek = default_llseek |
2790 | }; |
2791 | |
2792 | static const struct file_operations dp_dsc_chunk_size_debugfs_fops = { |
2793 | .owner = THIS_MODULE, |
2794 | .read = dp_dsc_chunk_size_read, |
2795 | .llseek = default_llseek |
2796 | }; |
2797 | |
2798 | static const struct file_operations dp_dsc_slice_bpg_offset_debugfs_fops = { |
2799 | .owner = THIS_MODULE, |
2800 | .read = dp_dsc_slice_bpg_offset_read, |
2801 | .llseek = default_llseek |
2802 | }; |
2803 | |
2804 | static const struct file_operations trigger_hotplug_debugfs_fops = { |
2805 | .owner = THIS_MODULE, |
2806 | .write = trigger_hotplug, |
2807 | .llseek = default_llseek |
2808 | }; |
2809 | |
2810 | static const struct file_operations dp_link_settings_debugfs_fops = { |
2811 | .owner = THIS_MODULE, |
2812 | .read = dp_link_settings_read, |
2813 | .write = dp_link_settings_write, |
2814 | .llseek = default_llseek |
2815 | }; |
2816 | |
2817 | static const struct file_operations dp_phy_settings_debugfs_fop = { |
2818 | .owner = THIS_MODULE, |
2819 | .read = dp_phy_settings_read, |
2820 | .write = dp_phy_settings_write, |
2821 | .llseek = default_llseek |
2822 | }; |
2823 | |
2824 | static const struct file_operations dp_phy_test_pattern_fops = { |
2825 | .owner = THIS_MODULE, |
2826 | .write = dp_phy_test_pattern_debugfs_write, |
2827 | .llseek = default_llseek |
2828 | }; |
2829 | |
2830 | static const struct file_operations sdp_message_fops = { |
2831 | .owner = THIS_MODULE, |
2832 | .write = dp_sdp_message_debugfs_write, |
2833 | .llseek = default_llseek |
2834 | }; |
2835 | |
2836 | static const struct file_operations dp_max_bpc_debugfs_fops = { |
2837 | .owner = THIS_MODULE, |
2838 | .read = dp_max_bpc_read, |
2839 | .write = dp_max_bpc_write, |
2840 | .llseek = default_llseek |
2841 | }; |
2842 | |
2843 | static const struct file_operations dp_dsc_disable_passthrough_debugfs_fops = { |
2844 | .owner = THIS_MODULE, |
2845 | .write = dp_dsc_passthrough_set, |
2846 | .llseek = default_llseek |
2847 | }; |
2848 | |
2849 | static const struct file_operations dp_mst_link_settings_debugfs_fops = { |
2850 | .owner = THIS_MODULE, |
2851 | .write = dp_mst_link_setting, |
2852 | .llseek = default_llseek |
2853 | }; |
2854 | |
2855 | static const struct { |
2856 | char *name; |
2857 | const struct file_operations *fops; |
2858 | } dp_debugfs_entries[] = { |
2859 | {"link_settings" , &dp_link_settings_debugfs_fops}, |
2860 | {"phy_settings" , &dp_phy_settings_debugfs_fop}, |
2861 | {"lttpr_status" , &dp_lttpr_status_fops}, |
2862 | {"test_pattern" , &dp_phy_test_pattern_fops}, |
2863 | {"hdcp_sink_capability" , &hdcp_sink_capability_fops}, |
2864 | {"sdp_message" , &sdp_message_fops}, |
2865 | {"dsc_clock_en" , &dp_dsc_clock_en_debugfs_fops}, |
2866 | {"dsc_slice_width" , &dp_dsc_slice_width_debugfs_fops}, |
2867 | {"dsc_slice_height" , &dp_dsc_slice_height_debugfs_fops}, |
2868 | {"dsc_bits_per_pixel" , &dp_dsc_bits_per_pixel_debugfs_fops}, |
2869 | {"dsc_pic_width" , &dp_dsc_pic_width_debugfs_fops}, |
2870 | {"dsc_pic_height" , &dp_dsc_pic_height_debugfs_fops}, |
2871 | {"dsc_chunk_size" , &dp_dsc_chunk_size_debugfs_fops}, |
2872 | {"dsc_slice_bpg" , &dp_dsc_slice_bpg_offset_debugfs_fops}, |
2873 | {"dp_dsc_fec_support" , &dp_dsc_fec_support_fops}, |
2874 | {"max_bpc" , &dp_max_bpc_debugfs_fops}, |
2875 | {"dsc_disable_passthrough" , &dp_dsc_disable_passthrough_debugfs_fops}, |
2876 | {"is_mst_connector" , &dp_is_mst_connector_fops}, |
2877 | {"mst_progress_status" , &dp_mst_progress_status_fops}, |
2878 | {"is_dpia_link" , &is_dpia_link_fops}, |
2879 | {"mst_link_settings" , &dp_mst_link_settings_debugfs_fops} |
2880 | }; |
2881 | |
2882 | static const struct { |
2883 | char *name; |
2884 | const struct file_operations *fops; |
2885 | } hdmi_debugfs_entries[] = { |
2886 | {"hdcp_sink_capability" , &hdcp_sink_capability_fops} |
2887 | }; |
2888 | |
2889 | /* |
2890 | * Force YUV420 output if available from the given mode |
2891 | */ |
2892 | static int force_yuv420_output_set(void *data, u64 val) |
2893 | { |
2894 | struct amdgpu_dm_connector *connector = data; |
2895 | |
2896 | connector->force_yuv420_output = (bool)val; |
2897 | |
2898 | return 0; |
2899 | } |
2900 | |
2901 | /* |
2902 | * Check if YUV420 is forced when available from the given mode |
2903 | */ |
2904 | static int force_yuv420_output_get(void *data, u64 *val) |
2905 | { |
2906 | struct amdgpu_dm_connector *connector = data; |
2907 | |
2908 | *val = connector->force_yuv420_output; |
2909 | |
2910 | return 0; |
2911 | } |
2912 | |
2913 | DEFINE_DEBUGFS_ATTRIBUTE(force_yuv420_output_fops, force_yuv420_output_get, |
2914 | force_yuv420_output_set, "%llu\n" ); |
2915 | |
2916 | /* |
2917 | * Read PSR state |
2918 | */ |
2919 | static int psr_get(void *data, u64 *val) |
2920 | { |
2921 | struct amdgpu_dm_connector *connector = data; |
2922 | struct dc_link *link = connector->dc_link; |
2923 | enum dc_psr_state state = PSR_STATE0; |
2924 | |
2925 | dc_link_get_psr_state(dc_link: link, state: &state); |
2926 | |
2927 | *val = state; |
2928 | |
2929 | return 0; |
2930 | } |
2931 | |
2932 | /* |
2933 | * Read PSR state residency |
2934 | */ |
2935 | static int psr_read_residency(void *data, u64 *val) |
2936 | { |
2937 | struct amdgpu_dm_connector *connector = data; |
2938 | struct dc_link *link = connector->dc_link; |
2939 | u32 residency; |
2940 | |
2941 | link->dc->link_srv->edp_get_psr_residency(link, &residency); |
2942 | |
2943 | *val = (u64)residency; |
2944 | |
2945 | return 0; |
2946 | } |
2947 | |
2948 | /* read allow_edp_hotplug_detection */ |
2949 | static int allow_edp_hotplug_detection_get(void *data, u64 *val) |
2950 | { |
2951 | struct amdgpu_dm_connector *aconnector = data; |
2952 | struct drm_connector *connector = &aconnector->base; |
2953 | struct drm_device *dev = connector->dev; |
2954 | struct amdgpu_device *adev = drm_to_adev(ddev: dev); |
2955 | |
2956 | *val = adev->dm.dc->config.allow_edp_hotplug_detection; |
2957 | |
2958 | return 0; |
2959 | } |
2960 | |
2961 | /* set allow_edp_hotplug_detection */ |
2962 | static int allow_edp_hotplug_detection_set(void *data, u64 val) |
2963 | { |
2964 | struct amdgpu_dm_connector *aconnector = data; |
2965 | struct drm_connector *connector = &aconnector->base; |
2966 | struct drm_device *dev = connector->dev; |
2967 | struct amdgpu_device *adev = drm_to_adev(ddev: dev); |
2968 | |
2969 | adev->dm.dc->config.allow_edp_hotplug_detection = (uint32_t) val; |
2970 | |
2971 | return 0; |
2972 | } |
2973 | |
2974 | /* |
2975 | * Set dmcub trace event IRQ enable or disable. |
2976 | * Usage to enable dmcub trace event IRQ: echo 1 > /sys/kernel/debug/dri/0/amdgpu_dm_dmcub_trace_event_en |
2977 | * Usage to disable dmcub trace event IRQ: echo 0 > /sys/kernel/debug/dri/0/amdgpu_dm_dmcub_trace_event_en |
2978 | */ |
2979 | static int dmcub_trace_event_state_set(void *data, u64 val) |
2980 | { |
2981 | struct amdgpu_device *adev = data; |
2982 | |
2983 | if (val == 1 || val == 0) { |
2984 | dc_dmub_trace_event_control(dc: adev->dm.dc, enable: val); |
2985 | adev->dm.dmcub_trace_event_en = (bool)val; |
2986 | } else |
2987 | return 0; |
2988 | |
2989 | return 0; |
2990 | } |
2991 | |
2992 | /* |
2993 | * The interface doesn't need get function, so it will return the |
2994 | * value of zero |
2995 | * Usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_dmcub_trace_event_en |
2996 | */ |
2997 | static int dmcub_trace_event_state_get(void *data, u64 *val) |
2998 | { |
2999 | struct amdgpu_device *adev = data; |
3000 | |
3001 | *val = adev->dm.dmcub_trace_event_en; |
3002 | return 0; |
3003 | } |
3004 | |
3005 | DEFINE_DEBUGFS_ATTRIBUTE(dmcub_trace_event_state_fops, dmcub_trace_event_state_get, |
3006 | dmcub_trace_event_state_set, "%llu\n" ); |
3007 | |
3008 | DEFINE_DEBUGFS_ATTRIBUTE(psr_fops, psr_get, NULL, "%llu\n" ); |
3009 | DEFINE_DEBUGFS_ATTRIBUTE(psr_residency_fops, psr_read_residency, NULL, |
3010 | "%llu\n" ); |
3011 | |
3012 | DEFINE_DEBUGFS_ATTRIBUTE(allow_edp_hotplug_detection_fops, |
3013 | allow_edp_hotplug_detection_get, |
3014 | allow_edp_hotplug_detection_set, "%llu\n" ); |
3015 | |
3016 | DEFINE_SHOW_ATTRIBUTE(current_backlight); |
3017 | DEFINE_SHOW_ATTRIBUTE(target_backlight); |
3018 | |
3019 | static const struct { |
3020 | char *name; |
3021 | const struct file_operations *fops; |
3022 | } connector_debugfs_entries[] = { |
3023 | {"force_yuv420_output" , &force_yuv420_output_fops}, |
3024 | {"trigger_hotplug" , &trigger_hotplug_debugfs_fops}, |
3025 | {"internal_display" , &internal_display_fops}, |
3026 | {"odm_combine_segments" , &odm_combine_segments_fops} |
3027 | }; |
3028 | |
3029 | /* |
3030 | * Returns supported customized link rates by this eDP panel. |
3031 | * Example usage: cat /sys/kernel/debug/dri/0/eDP-x/ilr_setting |
3032 | */ |
3033 | static int edp_ilr_show(struct seq_file *m, void *unused) |
3034 | { |
3035 | struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(m->private); |
3036 | struct dc_link *link = aconnector->dc_link; |
3037 | uint8_t supported_link_rates[16]; |
3038 | uint32_t link_rate_in_khz; |
3039 | uint32_t entry = 0; |
3040 | uint8_t dpcd_rev; |
3041 | |
3042 | memset(supported_link_rates, 0, sizeof(supported_link_rates)); |
3043 | dm_helpers_dp_read_dpcd(ctx: link->ctx, link, DP_SUPPORTED_LINK_RATES, |
3044 | data: supported_link_rates, size: sizeof(supported_link_rates)); |
3045 | |
3046 | dpcd_rev = link->dpcd_caps.dpcd_rev.raw; |
3047 | |
3048 | if (dpcd_rev >= DP_DPCD_REV_13 && |
3049 | (supported_link_rates[entry+1] != 0 || supported_link_rates[entry] != 0)) { |
3050 | |
3051 | for (entry = 0; entry < 16; entry += 2) { |
3052 | link_rate_in_khz = (supported_link_rates[entry+1] * 0x100 + |
3053 | supported_link_rates[entry]) * 200; |
3054 | seq_printf(m, fmt: "[%d] %d kHz\n" , entry/2, link_rate_in_khz); |
3055 | } |
3056 | } else { |
3057 | seq_puts(m, s: "ILR is not supported by this eDP panel.\n" ); |
3058 | } |
3059 | |
3060 | return 0; |
3061 | } |
3062 | |
3063 | /* |
3064 | * Set supported customized link rate to eDP panel. |
3065 | * |
3066 | * echo <lane_count> <link_rate option> > ilr_setting |
3067 | * |
3068 | * for example, supported ILR : [0] 1620000 kHz [1] 2160000 kHz [2] 2430000 kHz ... |
3069 | * echo 4 1 > /sys/kernel/debug/dri/0/eDP-x/ilr_setting |
3070 | * to set 4 lanes and 2.16 GHz |
3071 | */ |
3072 | static ssize_t edp_ilr_write(struct file *f, const char __user *buf, |
3073 | size_t size, loff_t *pos) |
3074 | { |
3075 | struct amdgpu_dm_connector *connector = file_inode(f)->i_private; |
3076 | struct dc_link *link = connector->dc_link; |
3077 | struct amdgpu_device *adev = drm_to_adev(ddev: connector->base.dev); |
3078 | struct dc *dc = (struct dc *)link->dc; |
3079 | struct dc_link_settings prefer_link_settings; |
3080 | char *wr_buf = NULL; |
3081 | const uint32_t wr_buf_size = 40; |
3082 | /* 0: lane_count; 1: link_rate */ |
3083 | int max_param_num = 2; |
3084 | uint8_t param_nums = 0; |
3085 | long param[2]; |
3086 | bool valid_input = true; |
3087 | |
3088 | if (size == 0) |
3089 | return -EINVAL; |
3090 | |
3091 | wr_buf = kcalloc(n: wr_buf_size, size: sizeof(char), GFP_KERNEL); |
3092 | if (!wr_buf) |
3093 | return -ENOMEM; |
3094 | |
3095 | if (parse_write_buffer_into_params(wr_buf, wr_buf_size, |
3096 | param: (long *)param, buf, |
3097 | max_param_num, |
3098 | param_nums: ¶m_nums)) { |
3099 | kfree(objp: wr_buf); |
3100 | return -EINVAL; |
3101 | } |
3102 | |
3103 | if (param_nums <= 0) { |
3104 | kfree(objp: wr_buf); |
3105 | return -EINVAL; |
3106 | } |
3107 | |
3108 | switch (param[0]) { |
3109 | case LANE_COUNT_ONE: |
3110 | case LANE_COUNT_TWO: |
3111 | case LANE_COUNT_FOUR: |
3112 | break; |
3113 | default: |
3114 | valid_input = false; |
3115 | break; |
3116 | } |
3117 | |
3118 | if (param[1] >= link->dpcd_caps.edp_supported_link_rates_count) |
3119 | valid_input = false; |
3120 | |
3121 | if (!valid_input) { |
3122 | kfree(objp: wr_buf); |
3123 | DRM_DEBUG_DRIVER("Invalid Input value. No HW will be programmed\n" ); |
3124 | prefer_link_settings.use_link_rate_set = false; |
3125 | mutex_lock(&adev->dm.dc_lock); |
3126 | dc_link_set_preferred_training_settings(dc, NULL, NULL, link, skip_immediate_retrain: false); |
3127 | mutex_unlock(lock: &adev->dm.dc_lock); |
3128 | return size; |
3129 | } |
3130 | |
3131 | /* save user force lane_count, link_rate to preferred settings |
3132 | * spread spectrum will not be changed |
3133 | */ |
3134 | prefer_link_settings.link_spread = link->cur_link_settings.link_spread; |
3135 | prefer_link_settings.lane_count = param[0]; |
3136 | prefer_link_settings.use_link_rate_set = true; |
3137 | prefer_link_settings.link_rate_set = param[1]; |
3138 | prefer_link_settings.link_rate = link->dpcd_caps.edp_supported_link_rates[param[1]]; |
3139 | |
3140 | mutex_lock(&adev->dm.dc_lock); |
3141 | dc_link_set_preferred_training_settings(dc, link_setting: &prefer_link_settings, |
3142 | NULL, link, skip_immediate_retrain: false); |
3143 | mutex_unlock(lock: &adev->dm.dc_lock); |
3144 | |
3145 | kfree(objp: wr_buf); |
3146 | return size; |
3147 | } |
3148 | |
3149 | static int edp_ilr_open(struct inode *inode, struct file *file) |
3150 | { |
3151 | return single_open(file, edp_ilr_show, inode->i_private); |
3152 | } |
3153 | |
3154 | static const struct file_operations edp_ilr_debugfs_fops = { |
3155 | .owner = THIS_MODULE, |
3156 | .open = edp_ilr_open, |
3157 | .read = seq_read, |
3158 | .llseek = seq_lseek, |
3159 | .release = single_release, |
3160 | .write = edp_ilr_write |
3161 | }; |
3162 | |
3163 | void connector_debugfs_init(struct amdgpu_dm_connector *connector) |
3164 | { |
3165 | int i; |
3166 | struct dentry *dir = connector->base.debugfs_entry; |
3167 | |
3168 | if (connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort || |
3169 | connector->base.connector_type == DRM_MODE_CONNECTOR_eDP) { |
3170 | for (i = 0; i < ARRAY_SIZE(dp_debugfs_entries); i++) { |
3171 | debugfs_create_file(name: dp_debugfs_entries[i].name, |
3172 | mode: 0644, parent: dir, data: connector, |
3173 | fops: dp_debugfs_entries[i].fops); |
3174 | } |
3175 | } |
3176 | if (connector->base.connector_type == DRM_MODE_CONNECTOR_eDP) { |
3177 | debugfs_create_file_unsafe(name: "psr_capability" , mode: 0444, parent: dir, data: connector, fops: &psr_capability_fops); |
3178 | debugfs_create_file_unsafe(name: "psr_state" , mode: 0444, parent: dir, data: connector, fops: &psr_fops); |
3179 | debugfs_create_file_unsafe(name: "psr_residency" , mode: 0444, parent: dir, |
3180 | data: connector, fops: &psr_residency_fops); |
3181 | debugfs_create_file(name: "amdgpu_current_backlight_pwm" , mode: 0444, parent: dir, data: connector, |
3182 | fops: ¤t_backlight_fops); |
3183 | debugfs_create_file(name: "amdgpu_target_backlight_pwm" , mode: 0444, parent: dir, data: connector, |
3184 | fops: &target_backlight_fops); |
3185 | debugfs_create_file(name: "ilr_setting" , mode: 0644, parent: dir, data: connector, |
3186 | fops: &edp_ilr_debugfs_fops); |
3187 | debugfs_create_file(name: "allow_edp_hotplug_detection" , mode: 0644, parent: dir, data: connector, |
3188 | fops: &allow_edp_hotplug_detection_fops); |
3189 | } |
3190 | |
3191 | for (i = 0; i < ARRAY_SIZE(connector_debugfs_entries); i++) { |
3192 | debugfs_create_file(name: connector_debugfs_entries[i].name, |
3193 | mode: 0644, parent: dir, data: connector, |
3194 | fops: connector_debugfs_entries[i].fops); |
3195 | } |
3196 | |
3197 | if (connector->base.connector_type == DRM_MODE_CONNECTOR_HDMIA) { |
3198 | for (i = 0; i < ARRAY_SIZE(hdmi_debugfs_entries); i++) { |
3199 | debugfs_create_file(name: hdmi_debugfs_entries[i].name, |
3200 | mode: 0644, parent: dir, data: connector, |
3201 | fops: hdmi_debugfs_entries[i].fops); |
3202 | } |
3203 | } |
3204 | } |
3205 | |
3206 | #ifdef CONFIG_DRM_AMD_SECURE_DISPLAY |
3207 | /* |
3208 | * Set crc window coordinate x start |
3209 | */ |
3210 | static int crc_win_x_start_set(void *data, u64 val) |
3211 | { |
3212 | struct drm_crtc *crtc = data; |
3213 | struct drm_device *drm_dev = crtc->dev; |
3214 | struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); |
3215 | |
3216 | spin_lock_irq(lock: &drm_dev->event_lock); |
3217 | acrtc->dm_irq_params.window_param.x_start = (uint16_t) val; |
3218 | acrtc->dm_irq_params.window_param.update_win = false; |
3219 | spin_unlock_irq(lock: &drm_dev->event_lock); |
3220 | |
3221 | return 0; |
3222 | } |
3223 | |
3224 | /* |
3225 | * Get crc window coordinate x start |
3226 | */ |
3227 | static int crc_win_x_start_get(void *data, u64 *val) |
3228 | { |
3229 | struct drm_crtc *crtc = data; |
3230 | struct drm_device *drm_dev = crtc->dev; |
3231 | struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); |
3232 | |
3233 | spin_lock_irq(lock: &drm_dev->event_lock); |
3234 | *val = acrtc->dm_irq_params.window_param.x_start; |
3235 | spin_unlock_irq(lock: &drm_dev->event_lock); |
3236 | |
3237 | return 0; |
3238 | } |
3239 | |
3240 | DEFINE_DEBUGFS_ATTRIBUTE(crc_win_x_start_fops, crc_win_x_start_get, |
3241 | crc_win_x_start_set, "%llu\n" ); |
3242 | |
3243 | |
3244 | /* |
3245 | * Set crc window coordinate y start |
3246 | */ |
3247 | static int crc_win_y_start_set(void *data, u64 val) |
3248 | { |
3249 | struct drm_crtc *crtc = data; |
3250 | struct drm_device *drm_dev = crtc->dev; |
3251 | struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); |
3252 | |
3253 | spin_lock_irq(lock: &drm_dev->event_lock); |
3254 | acrtc->dm_irq_params.window_param.y_start = (uint16_t) val; |
3255 | acrtc->dm_irq_params.window_param.update_win = false; |
3256 | spin_unlock_irq(lock: &drm_dev->event_lock); |
3257 | |
3258 | return 0; |
3259 | } |
3260 | |
3261 | /* |
3262 | * Get crc window coordinate y start |
3263 | */ |
3264 | static int crc_win_y_start_get(void *data, u64 *val) |
3265 | { |
3266 | struct drm_crtc *crtc = data; |
3267 | struct drm_device *drm_dev = crtc->dev; |
3268 | struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); |
3269 | |
3270 | spin_lock_irq(lock: &drm_dev->event_lock); |
3271 | *val = acrtc->dm_irq_params.window_param.y_start; |
3272 | spin_unlock_irq(lock: &drm_dev->event_lock); |
3273 | |
3274 | return 0; |
3275 | } |
3276 | |
3277 | DEFINE_DEBUGFS_ATTRIBUTE(crc_win_y_start_fops, crc_win_y_start_get, |
3278 | crc_win_y_start_set, "%llu\n" ); |
3279 | |
3280 | /* |
3281 | * Set crc window coordinate x end |
3282 | */ |
3283 | static int crc_win_x_end_set(void *data, u64 val) |
3284 | { |
3285 | struct drm_crtc *crtc = data; |
3286 | struct drm_device *drm_dev = crtc->dev; |
3287 | struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); |
3288 | |
3289 | spin_lock_irq(lock: &drm_dev->event_lock); |
3290 | acrtc->dm_irq_params.window_param.x_end = (uint16_t) val; |
3291 | acrtc->dm_irq_params.window_param.update_win = false; |
3292 | spin_unlock_irq(lock: &drm_dev->event_lock); |
3293 | |
3294 | return 0; |
3295 | } |
3296 | |
3297 | /* |
3298 | * Get crc window coordinate x end |
3299 | */ |
3300 | static int crc_win_x_end_get(void *data, u64 *val) |
3301 | { |
3302 | struct drm_crtc *crtc = data; |
3303 | struct drm_device *drm_dev = crtc->dev; |
3304 | struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); |
3305 | |
3306 | spin_lock_irq(lock: &drm_dev->event_lock); |
3307 | *val = acrtc->dm_irq_params.window_param.x_end; |
3308 | spin_unlock_irq(lock: &drm_dev->event_lock); |
3309 | |
3310 | return 0; |
3311 | } |
3312 | |
3313 | DEFINE_DEBUGFS_ATTRIBUTE(crc_win_x_end_fops, crc_win_x_end_get, |
3314 | crc_win_x_end_set, "%llu\n" ); |
3315 | |
3316 | /* |
3317 | * Set crc window coordinate y end |
3318 | */ |
3319 | static int crc_win_y_end_set(void *data, u64 val) |
3320 | { |
3321 | struct drm_crtc *crtc = data; |
3322 | struct drm_device *drm_dev = crtc->dev; |
3323 | struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); |
3324 | |
3325 | spin_lock_irq(lock: &drm_dev->event_lock); |
3326 | acrtc->dm_irq_params.window_param.y_end = (uint16_t) val; |
3327 | acrtc->dm_irq_params.window_param.update_win = false; |
3328 | spin_unlock_irq(lock: &drm_dev->event_lock); |
3329 | |
3330 | return 0; |
3331 | } |
3332 | |
3333 | /* |
3334 | * Get crc window coordinate y end |
3335 | */ |
3336 | static int crc_win_y_end_get(void *data, u64 *val) |
3337 | { |
3338 | struct drm_crtc *crtc = data; |
3339 | struct drm_device *drm_dev = crtc->dev; |
3340 | struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); |
3341 | |
3342 | spin_lock_irq(lock: &drm_dev->event_lock); |
3343 | *val = acrtc->dm_irq_params.window_param.y_end; |
3344 | spin_unlock_irq(lock: &drm_dev->event_lock); |
3345 | |
3346 | return 0; |
3347 | } |
3348 | |
3349 | DEFINE_DEBUGFS_ATTRIBUTE(crc_win_y_end_fops, crc_win_y_end_get, |
3350 | crc_win_y_end_set, "%llu\n" ); |
3351 | /* |
3352 | * Trigger to commit crc window |
3353 | */ |
3354 | static int crc_win_update_set(void *data, u64 val) |
3355 | { |
3356 | struct drm_crtc *crtc = data; |
3357 | struct amdgpu_crtc *acrtc; |
3358 | struct amdgpu_device *adev = drm_to_adev(ddev: crtc->dev); |
3359 | |
3360 | if (val) { |
3361 | acrtc = to_amdgpu_crtc(crtc); |
3362 | mutex_lock(&adev->dm.dc_lock); |
3363 | /* PSR may write to OTG CRC window control register, |
3364 | * so close it before starting secure_display. |
3365 | */ |
3366 | amdgpu_dm_psr_disable(stream: acrtc->dm_irq_params.stream); |
3367 | |
3368 | spin_lock_irq(lock: &adev_to_drm(adev)->event_lock); |
3369 | |
3370 | acrtc->dm_irq_params.window_param.activated = true; |
3371 | acrtc->dm_irq_params.window_param.update_win = true; |
3372 | acrtc->dm_irq_params.window_param.skip_frame_cnt = 0; |
3373 | |
3374 | spin_unlock_irq(lock: &adev_to_drm(adev)->event_lock); |
3375 | mutex_unlock(lock: &adev->dm.dc_lock); |
3376 | } |
3377 | |
3378 | return 0; |
3379 | } |
3380 | |
3381 | /* |
3382 | * Get crc window update flag |
3383 | */ |
3384 | static int crc_win_update_get(void *data, u64 *val) |
3385 | { |
3386 | *val = 0; |
3387 | return 0; |
3388 | } |
3389 | |
3390 | DEFINE_DEBUGFS_ATTRIBUTE(crc_win_update_fops, crc_win_update_get, |
3391 | crc_win_update_set, "%llu\n" ); |
3392 | #endif |
3393 | void crtc_debugfs_init(struct drm_crtc *crtc) |
3394 | { |
3395 | #ifdef CONFIG_DRM_AMD_SECURE_DISPLAY |
3396 | struct dentry *dir = debugfs_lookup(name: "crc" , parent: crtc->debugfs_entry); |
3397 | |
3398 | if (!dir) |
3399 | return; |
3400 | |
3401 | debugfs_create_file_unsafe(name: "crc_win_x_start" , mode: 0644, parent: dir, data: crtc, |
3402 | fops: &crc_win_x_start_fops); |
3403 | debugfs_create_file_unsafe(name: "crc_win_y_start" , mode: 0644, parent: dir, data: crtc, |
3404 | fops: &crc_win_y_start_fops); |
3405 | debugfs_create_file_unsafe(name: "crc_win_x_end" , mode: 0644, parent: dir, data: crtc, |
3406 | fops: &crc_win_x_end_fops); |
3407 | debugfs_create_file_unsafe(name: "crc_win_y_end" , mode: 0644, parent: dir, data: crtc, |
3408 | fops: &crc_win_y_end_fops); |
3409 | debugfs_create_file_unsafe(name: "crc_win_update" , mode: 0644, parent: dir, data: crtc, |
3410 | fops: &crc_win_update_fops); |
3411 | dput(dir); |
3412 | #endif |
3413 | debugfs_create_file(name: "amdgpu_current_bpc" , mode: 0644, parent: crtc->debugfs_entry, |
3414 | data: crtc, fops: &amdgpu_current_bpc_fops); |
3415 | debugfs_create_file(name: "amdgpu_current_colorspace" , mode: 0644, parent: crtc->debugfs_entry, |
3416 | data: crtc, fops: &amdgpu_current_colorspace_fops); |
3417 | } |
3418 | |
3419 | /* |
3420 | * Writes DTN log state to the user supplied buffer. |
3421 | * Example usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_dtn_log |
3422 | */ |
3423 | static ssize_t dtn_log_read( |
3424 | struct file *f, |
3425 | char __user *buf, |
3426 | size_t size, |
3427 | loff_t *pos) |
3428 | { |
3429 | struct amdgpu_device *adev = file_inode(f)->i_private; |
3430 | struct dc *dc = adev->dm.dc; |
3431 | struct dc_log_buffer_ctx log_ctx = { 0 }; |
3432 | ssize_t result = 0; |
3433 | |
3434 | if (!buf || !size) |
3435 | return -EINVAL; |
3436 | |
3437 | if (!dc->hwss.log_hw_state) |
3438 | return 0; |
3439 | |
3440 | dc->hwss.log_hw_state(dc, &log_ctx); |
3441 | |
3442 | if (*pos < log_ctx.pos) { |
3443 | size_t to_copy = log_ctx.pos - *pos; |
3444 | |
3445 | to_copy = min(to_copy, size); |
3446 | |
3447 | if (!copy_to_user(to: buf, from: log_ctx.buf + *pos, n: to_copy)) { |
3448 | *pos += to_copy; |
3449 | result = to_copy; |
3450 | } |
3451 | } |
3452 | |
3453 | kfree(objp: log_ctx.buf); |
3454 | |
3455 | return result; |
3456 | } |
3457 | |
3458 | /* |
3459 | * Writes DTN log state to dmesg when triggered via a write. |
3460 | * Example usage: echo 1 > /sys/kernel/debug/dri/0/amdgpu_dm_dtn_log |
3461 | */ |
3462 | static ssize_t dtn_log_write( |
3463 | struct file *f, |
3464 | const char __user *buf, |
3465 | size_t size, |
3466 | loff_t *pos) |
3467 | { |
3468 | struct amdgpu_device *adev = file_inode(f)->i_private; |
3469 | struct dc *dc = adev->dm.dc; |
3470 | |
3471 | /* Write triggers log output via dmesg. */ |
3472 | if (size == 0) |
3473 | return 0; |
3474 | |
3475 | if (dc->hwss.log_hw_state) |
3476 | dc->hwss.log_hw_state(dc, NULL); |
3477 | |
3478 | return size; |
3479 | } |
3480 | |
3481 | static int mst_topo_show(struct seq_file *m, void *unused) |
3482 | { |
3483 | struct amdgpu_device *adev = (struct amdgpu_device *)m->private; |
3484 | struct drm_device *dev = adev_to_drm(adev); |
3485 | struct drm_connector *connector; |
3486 | struct drm_connector_list_iter conn_iter; |
3487 | struct amdgpu_dm_connector *aconnector; |
3488 | |
3489 | drm_connector_list_iter_begin(dev, iter: &conn_iter); |
3490 | drm_for_each_connector_iter(connector, &conn_iter) { |
3491 | if (connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) |
3492 | continue; |
3493 | |
3494 | aconnector = to_amdgpu_dm_connector(connector); |
3495 | |
3496 | /* Ensure we're only dumping the topology of a root mst node */ |
3497 | if (!aconnector->mst_mgr.mst_state) |
3498 | continue; |
3499 | |
3500 | seq_printf(m, fmt: "\nMST topology for connector %d\n" , aconnector->connector_id); |
3501 | drm_dp_mst_dump_topology(m, mgr: &aconnector->mst_mgr); |
3502 | } |
3503 | drm_connector_list_iter_end(iter: &conn_iter); |
3504 | |
3505 | return 0; |
3506 | } |
3507 | |
3508 | /* |
3509 | * Sets trigger hpd for MST topologies. |
3510 | * All connected connectors will be rediscovered and re started as needed if val of 1 is sent. |
3511 | * All topologies will be disconnected if val of 0 is set . |
3512 | * Usage to enable topologies: echo 1 > /sys/kernel/debug/dri/0/amdgpu_dm_trigger_hpd_mst |
3513 | * Usage to disable topologies: echo 0 > /sys/kernel/debug/dri/0/amdgpu_dm_trigger_hpd_mst |
3514 | */ |
3515 | static int trigger_hpd_mst_set(void *data, u64 val) |
3516 | { |
3517 | struct amdgpu_device *adev = data; |
3518 | struct drm_device *dev = adev_to_drm(adev); |
3519 | struct drm_connector_list_iter iter; |
3520 | struct amdgpu_dm_connector *aconnector; |
3521 | struct drm_connector *connector; |
3522 | struct dc_link *link = NULL; |
3523 | |
3524 | if (val == 1) { |
3525 | drm_connector_list_iter_begin(dev, iter: &iter); |
3526 | drm_for_each_connector_iter(connector, &iter) { |
3527 | aconnector = to_amdgpu_dm_connector(connector); |
3528 | if (aconnector->dc_link->type == dc_connection_mst_branch && |
3529 | aconnector->mst_mgr.aux) { |
3530 | mutex_lock(&adev->dm.dc_lock); |
3531 | dc_link_detect(link: aconnector->dc_link, reason: DETECT_REASON_HPD); |
3532 | mutex_unlock(lock: &adev->dm.dc_lock); |
3533 | |
3534 | drm_dp_mst_topology_mgr_set_mst(mgr: &aconnector->mst_mgr, mst_state: true); |
3535 | } |
3536 | } |
3537 | } else if (val == 0) { |
3538 | drm_connector_list_iter_begin(dev, iter: &iter); |
3539 | drm_for_each_connector_iter(connector, &iter) { |
3540 | aconnector = to_amdgpu_dm_connector(connector); |
3541 | if (!aconnector->dc_link) |
3542 | continue; |
3543 | |
3544 | if (!aconnector->mst_root) |
3545 | continue; |
3546 | |
3547 | link = aconnector->dc_link; |
3548 | dc_link_dp_receiver_power_ctrl(link, on: false); |
3549 | drm_dp_mst_topology_mgr_set_mst(mgr: &aconnector->mst_root->mst_mgr, mst_state: false); |
3550 | link->mst_stream_alloc_table.stream_count = 0; |
3551 | memset(link->mst_stream_alloc_table.stream_allocations, 0, |
3552 | sizeof(link->mst_stream_alloc_table.stream_allocations)); |
3553 | } |
3554 | } else { |
3555 | return 0; |
3556 | } |
3557 | drm_kms_helper_hotplug_event(dev); |
3558 | |
3559 | return 0; |
3560 | } |
3561 | |
3562 | /* |
3563 | * The interface doesn't need get function, so it will return the |
3564 | * value of zero |
3565 | * Usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_trigger_hpd_mst |
3566 | */ |
3567 | static int trigger_hpd_mst_get(void *data, u64 *val) |
3568 | { |
3569 | *val = 0; |
3570 | return 0; |
3571 | } |
3572 | |
3573 | DEFINE_DEBUGFS_ATTRIBUTE(trigger_hpd_mst_ops, trigger_hpd_mst_get, |
3574 | trigger_hpd_mst_set, "%llu\n" ); |
3575 | |
3576 | |
3577 | /* |
3578 | * Sets the force_timing_sync debug option from the given string. |
3579 | * All connected displays will be force synchronized immediately. |
3580 | * Usage: echo 1 > /sys/kernel/debug/dri/0/amdgpu_dm_force_timing_sync |
3581 | */ |
3582 | static int force_timing_sync_set(void *data, u64 val) |
3583 | { |
3584 | struct amdgpu_device *adev = data; |
3585 | |
3586 | adev->dm.force_timing_sync = (bool)val; |
3587 | |
3588 | amdgpu_dm_trigger_timing_sync(dev: adev_to_drm(adev)); |
3589 | |
3590 | return 0; |
3591 | } |
3592 | |
3593 | /* |
3594 | * Gets the force_timing_sync debug option value into the given buffer. |
3595 | * Usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_force_timing_sync |
3596 | */ |
3597 | static int force_timing_sync_get(void *data, u64 *val) |
3598 | { |
3599 | struct amdgpu_device *adev = data; |
3600 | |
3601 | *val = adev->dm.force_timing_sync; |
3602 | |
3603 | return 0; |
3604 | } |
3605 | |
3606 | DEFINE_DEBUGFS_ATTRIBUTE(force_timing_sync_ops, force_timing_sync_get, |
3607 | force_timing_sync_set, "%llu\n" ); |
3608 | |
3609 | |
3610 | /* |
3611 | * Disables all HPD and HPD RX interrupt handling in the |
3612 | * driver when set to 1. Default is 0. |
3613 | */ |
3614 | static int disable_hpd_set(void *data, u64 val) |
3615 | { |
3616 | struct amdgpu_device *adev = data; |
3617 | |
3618 | adev->dm.disable_hpd_irq = (bool)val; |
3619 | |
3620 | return 0; |
3621 | } |
3622 | |
3623 | |
3624 | /* |
3625 | * Returns 1 if HPD and HPRX interrupt handling is disabled, |
3626 | * 0 otherwise. |
3627 | */ |
3628 | static int disable_hpd_get(void *data, u64 *val) |
3629 | { |
3630 | struct amdgpu_device *adev = data; |
3631 | |
3632 | *val = adev->dm.disable_hpd_irq; |
3633 | |
3634 | return 0; |
3635 | } |
3636 | |
3637 | DEFINE_DEBUGFS_ATTRIBUTE(disable_hpd_ops, disable_hpd_get, |
3638 | disable_hpd_set, "%llu\n" ); |
3639 | |
3640 | /* |
3641 | * Prints hardware capabilities. These are used for IGT testing. |
3642 | */ |
3643 | static int capabilities_show(struct seq_file *m, void *unused) |
3644 | { |
3645 | struct amdgpu_device *adev = (struct amdgpu_device *)m->private; |
3646 | struct dc *dc = adev->dm.dc; |
3647 | bool mall_supported = dc->caps.mall_size_total; |
3648 | bool subvp_supported = dc->caps.subvp_fw_processing_delay_us; |
3649 | unsigned int mall_in_use = false; |
3650 | unsigned int subvp_in_use = dc->cap_funcs.get_subvp_en(dc, dc->current_state); |
3651 | struct hubbub *hubbub = dc->res_pool->hubbub; |
3652 | |
3653 | if (hubbub->funcs->get_mall_en) |
3654 | hubbub->funcs->get_mall_en(hubbub, &mall_in_use); |
3655 | |
3656 | seq_printf(m, fmt: "mall supported: %s, enabled: %s\n" , |
3657 | mall_supported ? "yes" : "no" , mall_in_use ? "yes" : "no" ); |
3658 | seq_printf(m, fmt: "sub-viewport supported: %s, enabled: %s\n" , |
3659 | subvp_supported ? "yes" : "no" , subvp_in_use ? "yes" : "no" ); |
3660 | |
3661 | return 0; |
3662 | } |
3663 | |
3664 | DEFINE_SHOW_ATTRIBUTE(capabilities); |
3665 | |
3666 | /* |
3667 | * Temporary w/a to force sst sequence in M42D DP2 mst receiver |
3668 | * Example usage: echo 1 > /sys/kernel/debug/dri/0/amdgpu_dm_dp_set_mst_en_for_sst |
3669 | */ |
3670 | static int dp_force_sst_set(void *data, u64 val) |
3671 | { |
3672 | struct amdgpu_device *adev = data; |
3673 | |
3674 | adev->dm.dc->debug.set_mst_en_for_sst = val; |
3675 | |
3676 | return 0; |
3677 | } |
3678 | |
3679 | static int dp_force_sst_get(void *data, u64 *val) |
3680 | { |
3681 | struct amdgpu_device *adev = data; |
3682 | |
3683 | *val = adev->dm.dc->debug.set_mst_en_for_sst; |
3684 | |
3685 | return 0; |
3686 | } |
3687 | DEFINE_DEBUGFS_ATTRIBUTE(dp_set_mst_en_for_sst_ops, dp_force_sst_get, |
3688 | dp_force_sst_set, "%llu\n" ); |
3689 | |
3690 | /* |
3691 | * Force DP2 sequence without VESA certified cable. |
3692 | * Example usage: echo 1 > /sys/kernel/debug/dri/0/amdgpu_dm_dp_ignore_cable_id |
3693 | */ |
3694 | static int dp_ignore_cable_id_set(void *data, u64 val) |
3695 | { |
3696 | struct amdgpu_device *adev = data; |
3697 | |
3698 | adev->dm.dc->debug.ignore_cable_id = val; |
3699 | |
3700 | return 0; |
3701 | } |
3702 | |
3703 | static int dp_ignore_cable_id_get(void *data, u64 *val) |
3704 | { |
3705 | struct amdgpu_device *adev = data; |
3706 | |
3707 | *val = adev->dm.dc->debug.ignore_cable_id; |
3708 | |
3709 | return 0; |
3710 | } |
3711 | DEFINE_DEBUGFS_ATTRIBUTE(dp_ignore_cable_id_ops, dp_ignore_cable_id_get, |
3712 | dp_ignore_cable_id_set, "%llu\n" ); |
3713 | |
3714 | /* |
3715 | * Sets the DC visual confirm debug option from the given string. |
3716 | * Example usage: echo 1 > /sys/kernel/debug/dri/0/amdgpu_visual_confirm |
3717 | */ |
3718 | static int visual_confirm_set(void *data, u64 val) |
3719 | { |
3720 | struct amdgpu_device *adev = data; |
3721 | |
3722 | adev->dm.dc->debug.visual_confirm = (enum visual_confirm)val; |
3723 | |
3724 | return 0; |
3725 | } |
3726 | |
3727 | /* |
3728 | * Reads the DC visual confirm debug option value into the given buffer. |
3729 | * Example usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_visual_confirm |
3730 | */ |
3731 | static int visual_confirm_get(void *data, u64 *val) |
3732 | { |
3733 | struct amdgpu_device *adev = data; |
3734 | |
3735 | *val = adev->dm.dc->debug.visual_confirm; |
3736 | |
3737 | return 0; |
3738 | } |
3739 | |
3740 | DEFINE_SHOW_ATTRIBUTE(mst_topo); |
3741 | DEFINE_DEBUGFS_ATTRIBUTE(visual_confirm_fops, visual_confirm_get, |
3742 | visual_confirm_set, "%llu\n" ); |
3743 | |
3744 | |
3745 | /* |
3746 | * Sets the DC skip_detection_link_training debug option from the given string. |
3747 | * Example usage: echo 1 > /sys/kernel/debug/dri/0/amdgpu_skip_detection_link_training |
3748 | */ |
3749 | static int skip_detection_link_training_set(void *data, u64 val) |
3750 | { |
3751 | struct amdgpu_device *adev = data; |
3752 | |
3753 | if (val == 0) |
3754 | adev->dm.dc->debug.skip_detection_link_training = false; |
3755 | else |
3756 | adev->dm.dc->debug.skip_detection_link_training = true; |
3757 | |
3758 | return 0; |
3759 | } |
3760 | |
3761 | /* |
3762 | * Reads the DC skip_detection_link_training debug option value into the given buffer. |
3763 | * Example usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_skip_detection_link_training |
3764 | */ |
3765 | static int skip_detection_link_training_get(void *data, u64 *val) |
3766 | { |
3767 | struct amdgpu_device *adev = data; |
3768 | |
3769 | *val = adev->dm.dc->debug.skip_detection_link_training; |
3770 | |
3771 | return 0; |
3772 | } |
3773 | |
3774 | DEFINE_DEBUGFS_ATTRIBUTE(skip_detection_link_training_fops, |
3775 | skip_detection_link_training_get, |
3776 | skip_detection_link_training_set, "%llu\n" ); |
3777 | |
3778 | /* |
3779 | * Dumps the DCC_EN bit for each pipe. |
3780 | * Example usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_dcc_en |
3781 | */ |
3782 | static ssize_t dcc_en_bits_read( |
3783 | struct file *f, |
3784 | char __user *buf, |
3785 | size_t size, |
3786 | loff_t *pos) |
3787 | { |
3788 | struct amdgpu_device *adev = file_inode(f)->i_private; |
3789 | struct dc *dc = adev->dm.dc; |
3790 | char *rd_buf = NULL; |
3791 | const uint32_t rd_buf_size = 32; |
3792 | uint32_t result = 0; |
3793 | int offset = 0; |
3794 | int num_pipes = dc->res_pool->pipe_count; |
3795 | int *dcc_en_bits; |
3796 | int i, r; |
3797 | |
3798 | dcc_en_bits = kcalloc(n: num_pipes, size: sizeof(int), GFP_KERNEL); |
3799 | if (!dcc_en_bits) |
3800 | return -ENOMEM; |
3801 | |
3802 | if (!dc->hwss.get_dcc_en_bits) { |
3803 | kfree(objp: dcc_en_bits); |
3804 | return 0; |
3805 | } |
3806 | |
3807 | dc->hwss.get_dcc_en_bits(dc, dcc_en_bits); |
3808 | |
3809 | rd_buf = kcalloc(n: rd_buf_size, size: sizeof(char), GFP_KERNEL); |
3810 | if (!rd_buf) { |
3811 | kfree(objp: dcc_en_bits); |
3812 | return -ENOMEM; |
3813 | } |
3814 | |
3815 | for (i = 0; i < num_pipes; i++) |
3816 | offset += snprintf(buf: rd_buf + offset, size: rd_buf_size - offset, |
3817 | fmt: "%d " , dcc_en_bits[i]); |
3818 | rd_buf[strlen(rd_buf)] = '\n'; |
3819 | |
3820 | kfree(objp: dcc_en_bits); |
3821 | |
3822 | while (size) { |
3823 | if (*pos >= rd_buf_size) |
3824 | break; |
3825 | r = put_user(*(rd_buf + result), buf); |
3826 | if (r) { |
3827 | kfree(objp: rd_buf); |
3828 | return r; /* r = -EFAULT */ |
3829 | } |
3830 | buf += 1; |
3831 | size -= 1; |
3832 | *pos += 1; |
3833 | result += 1; |
3834 | } |
3835 | |
3836 | kfree(objp: rd_buf); |
3837 | return result; |
3838 | } |
3839 | |
3840 | void dtn_debugfs_init(struct amdgpu_device *adev) |
3841 | { |
3842 | static const struct file_operations dtn_log_fops = { |
3843 | .owner = THIS_MODULE, |
3844 | .read = dtn_log_read, |
3845 | .write = dtn_log_write, |
3846 | .llseek = default_llseek |
3847 | }; |
3848 | static const struct file_operations dcc_en_bits_fops = { |
3849 | .owner = THIS_MODULE, |
3850 | .read = dcc_en_bits_read, |
3851 | .llseek = default_llseek |
3852 | }; |
3853 | |
3854 | struct drm_minor *minor = adev_to_drm(adev)->primary; |
3855 | struct dentry *root = minor->debugfs_root; |
3856 | |
3857 | debugfs_create_file(name: "amdgpu_mst_topology" , mode: 0444, parent: root, |
3858 | data: adev, fops: &mst_topo_fops); |
3859 | debugfs_create_file(name: "amdgpu_dm_capabilities" , mode: 0444, parent: root, |
3860 | data: adev, fops: &capabilities_fops); |
3861 | debugfs_create_file(name: "amdgpu_dm_dtn_log" , mode: 0644, parent: root, data: adev, |
3862 | fops: &dtn_log_fops); |
3863 | debugfs_create_file(name: "amdgpu_dm_dp_set_mst_en_for_sst" , mode: 0644, parent: root, data: adev, |
3864 | fops: &dp_set_mst_en_for_sst_ops); |
3865 | debugfs_create_file(name: "amdgpu_dm_dp_ignore_cable_id" , mode: 0644, parent: root, data: adev, |
3866 | fops: &dp_ignore_cable_id_ops); |
3867 | |
3868 | debugfs_create_file_unsafe(name: "amdgpu_dm_visual_confirm" , mode: 0644, parent: root, data: adev, |
3869 | fops: &visual_confirm_fops); |
3870 | |
3871 | debugfs_create_file_unsafe(name: "amdgpu_dm_skip_detection_link_training" , mode: 0644, parent: root, data: adev, |
3872 | fops: &skip_detection_link_training_fops); |
3873 | |
3874 | debugfs_create_file_unsafe(name: "amdgpu_dm_dmub_tracebuffer" , mode: 0644, parent: root, |
3875 | data: adev, fops: &dmub_tracebuffer_fops); |
3876 | |
3877 | debugfs_create_file_unsafe(name: "amdgpu_dm_dmub_fw_state" , mode: 0644, parent: root, |
3878 | data: adev, fops: &dmub_fw_state_fops); |
3879 | |
3880 | debugfs_create_file_unsafe(name: "amdgpu_dm_force_timing_sync" , mode: 0644, parent: root, |
3881 | data: adev, fops: &force_timing_sync_ops); |
3882 | |
3883 | debugfs_create_file_unsafe(name: "amdgpu_dm_dmcub_trace_event_en" , mode: 0644, parent: root, |
3884 | data: adev, fops: &dmcub_trace_event_state_fops); |
3885 | |
3886 | debugfs_create_file_unsafe(name: "amdgpu_dm_trigger_hpd_mst" , mode: 0644, parent: root, |
3887 | data: adev, fops: &trigger_hpd_mst_ops); |
3888 | |
3889 | debugfs_create_file_unsafe(name: "amdgpu_dm_dcc_en" , mode: 0644, parent: root, data: adev, |
3890 | fops: &dcc_en_bits_fops); |
3891 | |
3892 | debugfs_create_file_unsafe(name: "amdgpu_dm_disable_hpd" , mode: 0644, parent: root, data: adev, |
3893 | fops: &disable_hpd_ops); |
3894 | |
3895 | } |
3896 | |