drm/amd/display: break down dc_link.c
[linux-2.6-microblaze.git] / drivers / gpu / drm / amd / display / dc / link / protocols / link_dp_irq_handler.c
1 /*
2  * Copyright 2022 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 /* FILE POLICY AND INTENDED USAGE:
27  * This file implements DP HPD short pulse handling sequence according to DP
28  * specifications
29  *
30  */
31
32 #include "link_dp_irq_handler.h"
33 #include "link_dpcd.h"
34 #include "link_dp_training.h"
35 #include "link_dp_capability.h"
36 #include "link/accessories/link_dp_trace.h"
37 #include "dm_helpers.h"
38
39 #define DC_LOGGER_INIT(logger)
40
41 bool dc_link_check_link_loss_status(
42         struct dc_link *link,
43         union hpd_irq_data *hpd_irq_dpcd_data)
44 {
45         uint8_t irq_reg_rx_power_state = 0;
46         enum dc_status dpcd_result = DC_ERROR_UNEXPECTED;
47         union lane_status lane_status;
48         uint32_t lane;
49         bool sink_status_changed;
50         bool return_code;
51
52         sink_status_changed = false;
53         return_code = false;
54
55         if (link->cur_link_settings.lane_count == 0)
56                 return return_code;
57
58         /*1. Check that Link Status changed, before re-training.*/
59
60         /*parse lane status*/
61         for (lane = 0; lane < link->cur_link_settings.lane_count; lane++) {
62                 /* check status of lanes 0,1
63                  * changed DpcdAddress_Lane01Status (0x202)
64                  */
65                 lane_status.raw = dp_get_nibble_at_index(
66                         &hpd_irq_dpcd_data->bytes.lane01_status.raw,
67                         lane);
68
69                 if (!lane_status.bits.CHANNEL_EQ_DONE_0 ||
70                         !lane_status.bits.CR_DONE_0 ||
71                         !lane_status.bits.SYMBOL_LOCKED_0) {
72                         /* if one of the channel equalization, clock
73                          * recovery or symbol lock is dropped
74                          * consider it as (link has been
75                          * dropped) dp sink status has changed
76                          */
77                         sink_status_changed = true;
78                         break;
79                 }
80         }
81
82         /* Check interlane align.*/
83         if (sink_status_changed ||
84                 !hpd_irq_dpcd_data->bytes.lane_status_updated.bits.INTERLANE_ALIGN_DONE) {
85
86                 DC_LOG_HW_HPD_IRQ("%s: Link Status changed.\n", __func__);
87
88                 return_code = true;
89
90                 /*2. Check that we can handle interrupt: Not in FS DOS,
91                  *  Not in "Display Timeout" state, Link is trained.
92                  */
93                 dpcd_result = core_link_read_dpcd(link,
94                         DP_SET_POWER,
95                         &irq_reg_rx_power_state,
96                         sizeof(irq_reg_rx_power_state));
97
98                 if (dpcd_result != DC_OK) {
99                         DC_LOG_HW_HPD_IRQ("%s: DPCD read failed to obtain power state.\n",
100                                 __func__);
101                 } else {
102                         if (irq_reg_rx_power_state != DP_SET_POWER_D0)
103                                 return_code = false;
104                 }
105         }
106
107         return return_code;
108 }
109
110 static bool handle_hpd_irq_psr_sink(struct dc_link *link)
111 {
112         union dpcd_psr_configuration psr_configuration;
113
114         if (!link->psr_settings.psr_feature_enabled)
115                 return false;
116
117         dm_helpers_dp_read_dpcd(
118                 link->ctx,
119                 link,
120                 368,/*DpcdAddress_PSR_Enable_Cfg*/
121                 &psr_configuration.raw,
122                 sizeof(psr_configuration.raw));
123
124         if (psr_configuration.bits.ENABLE) {
125                 unsigned char dpcdbuf[3] = {0};
126                 union psr_error_status psr_error_status;
127                 union psr_sink_psr_status psr_sink_psr_status;
128
129                 dm_helpers_dp_read_dpcd(
130                         link->ctx,
131                         link,
132                         0x2006, /*DpcdAddress_PSR_Error_Status*/
133                         (unsigned char *) dpcdbuf,
134                         sizeof(dpcdbuf));
135
136                 /*DPCD 2006h   ERROR STATUS*/
137                 psr_error_status.raw = dpcdbuf[0];
138                 /*DPCD 2008h   SINK PANEL SELF REFRESH STATUS*/
139                 psr_sink_psr_status.raw = dpcdbuf[2];
140
141                 if (psr_error_status.bits.LINK_CRC_ERROR ||
142                                 psr_error_status.bits.RFB_STORAGE_ERROR ||
143                                 psr_error_status.bits.VSC_SDP_ERROR) {
144                         bool allow_active;
145
146                         /* Acknowledge and clear error bits */
147                         dm_helpers_dp_write_dpcd(
148                                 link->ctx,
149                                 link,
150                                 8198,/*DpcdAddress_PSR_Error_Status*/
151                                 &psr_error_status.raw,
152                                 sizeof(psr_error_status.raw));
153
154                         /* PSR error, disable and re-enable PSR */
155                         if (link->psr_settings.psr_allow_active) {
156                                 allow_active = false;
157                                 dc_link_set_psr_allow_active(link, &allow_active, true, false, NULL);
158                                 allow_active = true;
159                                 dc_link_set_psr_allow_active(link, &allow_active, true, false, NULL);
160                         }
161
162                         return true;
163                 } else if (psr_sink_psr_status.bits.SINK_SELF_REFRESH_STATUS ==
164                                 PSR_SINK_STATE_ACTIVE_DISPLAY_FROM_SINK_RFB){
165                         /* No error is detect, PSR is active.
166                          * We should return with IRQ_HPD handled without
167                          * checking for loss of sync since PSR would have
168                          * powered down main link.
169                          */
170                         return true;
171                 }
172         }
173         return false;
174 }
175
176 void dc_link_dp_handle_link_loss(struct dc_link *link)
177 {
178         int i;
179         struct pipe_ctx *pipe_ctx;
180
181         for (i = 0; i < MAX_PIPES; i++) {
182                 pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i];
183                 if (pipe_ctx && pipe_ctx->stream && pipe_ctx->stream->link == link)
184                         break;
185         }
186
187         if (pipe_ctx == NULL || pipe_ctx->stream == NULL)
188                 return;
189
190         for (i = 0; i < MAX_PIPES; i++) {
191                 pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i];
192                 if (pipe_ctx && pipe_ctx->stream && !pipe_ctx->stream->dpms_off &&
193                                 pipe_ctx->stream->link == link && !pipe_ctx->prev_odm_pipe)
194                         link_set_dpms_off(pipe_ctx);
195         }
196
197         for (i = 0; i < MAX_PIPES; i++) {
198                 pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i];
199                 if (pipe_ctx && pipe_ctx->stream && !pipe_ctx->stream->dpms_off
200                                 && pipe_ctx->stream->link == link && !pipe_ctx->prev_odm_pipe) {
201                         // Always use max settings here for DP 1.4a LL Compliance CTS
202                         if (link->is_automated) {
203                                 pipe_ctx->link_config.dp_link_settings.lane_count =
204                                                 link->verified_link_cap.lane_count;
205                                 pipe_ctx->link_config.dp_link_settings.link_rate =
206                                                 link->verified_link_cap.link_rate;
207                                 pipe_ctx->link_config.dp_link_settings.link_spread =
208                                                 link->verified_link_cap.link_spread;
209                         }
210                         link_set_dpms_on(link->dc->current_state, pipe_ctx);
211                 }
212         }
213 }
214
215 enum dc_status dc_link_dp_read_hpd_rx_irq_data(
216         struct dc_link *link,
217         union hpd_irq_data *irq_data)
218 {
219         static enum dc_status retval;
220
221         /* The HW reads 16 bytes from 200h on HPD,
222          * but if we get an AUX_DEFER, the HW cannot retry
223          * and this causes the CTS tests 4.3.2.1 - 3.2.4 to
224          * fail, so we now explicitly read 6 bytes which is
225          * the req from the above mentioned test cases.
226          *
227          * For DP 1.4 we need to read those from 2002h range.
228          */
229         if (link->dpcd_caps.dpcd_rev.raw < DPCD_REV_14)
230                 retval = core_link_read_dpcd(
231                         link,
232                         DP_SINK_COUNT,
233                         irq_data->raw,
234                         sizeof(union hpd_irq_data));
235         else {
236                 /* Read 14 bytes in a single read and then copy only the required fields.
237                  * This is more efficient than doing it in two separate AUX reads. */
238
239                 uint8_t tmp[DP_SINK_STATUS_ESI - DP_SINK_COUNT_ESI + 1];
240
241                 retval = core_link_read_dpcd(
242                         link,
243                         DP_SINK_COUNT_ESI,
244                         tmp,
245                         sizeof(tmp));
246
247                 if (retval != DC_OK)
248                         return retval;
249
250                 irq_data->bytes.sink_cnt.raw = tmp[DP_SINK_COUNT_ESI - DP_SINK_COUNT_ESI];
251                 irq_data->bytes.device_service_irq.raw = tmp[DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 - DP_SINK_COUNT_ESI];
252                 irq_data->bytes.lane01_status.raw = tmp[DP_LANE0_1_STATUS_ESI - DP_SINK_COUNT_ESI];
253                 irq_data->bytes.lane23_status.raw = tmp[DP_LANE2_3_STATUS_ESI - DP_SINK_COUNT_ESI];
254                 irq_data->bytes.lane_status_updated.raw = tmp[DP_LANE_ALIGN_STATUS_UPDATED_ESI - DP_SINK_COUNT_ESI];
255                 irq_data->bytes.sink_status.raw = tmp[DP_SINK_STATUS_ESI - DP_SINK_COUNT_ESI];
256         }
257
258         return retval;
259 }
260
261 /*************************Short Pulse IRQ***************************/
262 bool dc_link_dp_allow_hpd_rx_irq(const struct dc_link *link)
263 {
264         /*
265          * Don't handle RX IRQ unless one of following is met:
266          * 1) The link is established (cur_link_settings != unknown)
267          * 2) We know we're dealing with a branch device, SST or MST
268          */
269
270         if ((link->cur_link_settings.lane_count != LANE_COUNT_UNKNOWN) ||
271                 is_dp_branch_device(link))
272                 return true;
273
274         return false;
275 }
276
277 bool dc_link_handle_hpd_rx_irq(struct dc_link *link, union hpd_irq_data *out_hpd_irq_dpcd_data, bool *out_link_loss,
278                                                         bool defer_handling, bool *has_left_work)
279 {
280         union hpd_irq_data hpd_irq_dpcd_data = {0};
281         union device_service_irq device_service_clear = {0};
282         enum dc_status result;
283         bool status = false;
284
285         if (out_link_loss)
286                 *out_link_loss = false;
287
288         if (has_left_work)
289                 *has_left_work = false;
290         /* For use cases related to down stream connection status change,
291          * PSR and device auto test, refer to function handle_sst_hpd_irq
292          * in DAL2.1*/
293
294         DC_LOG_HW_HPD_IRQ("%s: Got short pulse HPD on link %d\n",
295                 __func__, link->link_index);
296
297
298          /* All the "handle_hpd_irq_xxx()" methods
299                  * should be called only after
300                  * dal_dpsst_ls_read_hpd_irq_data
301                  * Order of calls is important too
302                  */
303         result = dc_link_dp_read_hpd_rx_irq_data(link, &hpd_irq_dpcd_data);
304         if (out_hpd_irq_dpcd_data)
305                 *out_hpd_irq_dpcd_data = hpd_irq_dpcd_data;
306
307         if (result != DC_OK) {
308                 DC_LOG_HW_HPD_IRQ("%s: DPCD read failed to obtain irq data\n",
309                         __func__);
310                 return false;
311         }
312
313         if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.AUTOMATED_TEST) {
314                 // Workaround for DP 1.4a LL Compliance CTS as USB4 has to share encoders unlike DP and USBC
315                 link->is_automated = true;
316                 device_service_clear.bits.AUTOMATED_TEST = 1;
317                 core_link_write_dpcd(
318                         link,
319                         DP_DEVICE_SERVICE_IRQ_VECTOR,
320                         &device_service_clear.raw,
321                         sizeof(device_service_clear.raw));
322                 device_service_clear.raw = 0;
323                 if (defer_handling && has_left_work)
324                         *has_left_work = true;
325                 else
326                         dc_link_dp_handle_automated_test(link);
327                 return false;
328         }
329
330         if (!dc_link_dp_allow_hpd_rx_irq(link)) {
331                 DC_LOG_HW_HPD_IRQ("%s: skipping HPD handling on %d\n",
332                         __func__, link->link_index);
333                 return false;
334         }
335
336         if (handle_hpd_irq_psr_sink(link))
337                 /* PSR-related error was detected and handled */
338                 return true;
339
340         /* If PSR-related error handled, Main link may be off,
341          * so do not handle as a normal sink status change interrupt.
342          */
343
344         if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.UP_REQ_MSG_RDY) {
345                 if (defer_handling && has_left_work)
346                         *has_left_work = true;
347                 return true;
348         }
349
350         /* check if we have MST msg and return since we poll for it */
351         if (hpd_irq_dpcd_data.bytes.device_service_irq.bits.DOWN_REP_MSG_RDY) {
352                 if (defer_handling && has_left_work)
353                         *has_left_work = true;
354                 return false;
355         }
356
357         /* For now we only handle 'Downstream port status' case.
358          * If we got sink count changed it means
359          * Downstream port status changed,
360          * then DM should call DC to do the detection.
361          * NOTE: Do not handle link loss on eDP since it is internal link*/
362         if ((link->connector_signal != SIGNAL_TYPE_EDP) &&
363                 dc_link_check_link_loss_status(
364                         link,
365                         &hpd_irq_dpcd_data)) {
366                 /* Connectivity log: link loss */
367                 CONN_DATA_LINK_LOSS(link,
368                                         hpd_irq_dpcd_data.raw,
369                                         sizeof(hpd_irq_dpcd_data),
370                                         "Status: ");
371
372                 if (defer_handling && has_left_work)
373                         *has_left_work = true;
374                 else
375                         dc_link_dp_handle_link_loss(link);
376
377                 status = false;
378                 if (out_link_loss)
379                         *out_link_loss = true;
380
381                 dp_trace_link_loss_increment(link);
382         }
383
384         if (link->type == dc_connection_sst_branch &&
385                 hpd_irq_dpcd_data.bytes.sink_cnt.bits.SINK_COUNT
386                         != link->dpcd_sink_count)
387                 status = true;
388
389         /* reasons for HPD RX:
390          * 1. Link Loss - ie Re-train the Link
391          * 2. MST sideband message
392          * 3. Automated Test - ie. Internal Commit
393          * 4. CP (copy protection) - (not interesting for DM???)
394          * 5. DRR
395          * 6. Downstream Port status changed
396          * -ie. Detect - this the only one
397          * which is interesting for DM because
398          * it must call dc_link_detect.
399          */
400         return status;
401 }