bus: mhi: core: Add helper API to return number of free TREs
[linux-2.6-microblaze.git] / drivers / gpu / drm / amd / display / amdgpu_dm / amdgpu_dm_crc.c
1 /*
2  * Copyright 2015 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 <drm/drm_crtc.h>
27 #include <drm/drm_vblank.h>
28
29 #include "amdgpu.h"
30 #include "amdgpu_dm.h"
31 #include "dc.h"
32
33 static const char *const pipe_crc_sources[] = {
34         "none",
35         "crtc",
36         "crtc dither",
37         "dprx",
38         "dprx dither",
39         "auto",
40 };
41
42 static enum amdgpu_dm_pipe_crc_source dm_parse_crc_source(const char *source)
43 {
44         if (!source || !strcmp(source, "none"))
45                 return AMDGPU_DM_PIPE_CRC_SOURCE_NONE;
46         if (!strcmp(source, "auto") || !strcmp(source, "crtc"))
47                 return AMDGPU_DM_PIPE_CRC_SOURCE_CRTC;
48         if (!strcmp(source, "dprx"))
49                 return AMDGPU_DM_PIPE_CRC_SOURCE_DPRX;
50         if (!strcmp(source, "crtc dither"))
51                 return AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER;
52         if (!strcmp(source, "dprx dither"))
53                 return AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER;
54
55         return AMDGPU_DM_PIPE_CRC_SOURCE_INVALID;
56 }
57
58 static bool dm_is_crc_source_crtc(enum amdgpu_dm_pipe_crc_source src)
59 {
60         return (src == AMDGPU_DM_PIPE_CRC_SOURCE_CRTC) ||
61                (src == AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER);
62 }
63
64 static bool dm_is_crc_source_dprx(enum amdgpu_dm_pipe_crc_source src)
65 {
66         return (src == AMDGPU_DM_PIPE_CRC_SOURCE_DPRX) ||
67                (src == AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER);
68 }
69
70 static bool dm_need_crc_dither(enum amdgpu_dm_pipe_crc_source src)
71 {
72         return (src == AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER) ||
73                (src == AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER) ||
74                (src == AMDGPU_DM_PIPE_CRC_SOURCE_NONE);
75 }
76
77 const char *const *amdgpu_dm_crtc_get_crc_sources(struct drm_crtc *crtc,
78                                                   size_t *count)
79 {
80         *count = ARRAY_SIZE(pipe_crc_sources);
81         return pipe_crc_sources;
82 }
83
84 static void amdgpu_dm_set_crc_window_default(struct dm_crtc_state *dm_crtc_state)
85 {
86         dm_crtc_state->crc_window.x_start = 0;
87         dm_crtc_state->crc_window.y_start = 0;
88         dm_crtc_state->crc_window.x_end = 0;
89         dm_crtc_state->crc_window.y_end = 0;
90 }
91
92 bool amdgpu_dm_crc_window_is_default(struct dm_crtc_state *dm_crtc_state)
93 {
94         bool ret = true;
95
96         if ((dm_crtc_state->crc_window.x_start != 0) ||
97                 (dm_crtc_state->crc_window.y_start != 0) ||
98                 (dm_crtc_state->crc_window.x_end != 0) ||
99                 (dm_crtc_state->crc_window.y_end != 0))
100                 ret = false;
101
102         return ret;
103 }
104
105 bool amdgpu_dm_crc_window_changed(struct dm_crtc_state *dm_new_crtc_state,
106                                         struct dm_crtc_state *dm_old_crtc_state)
107 {
108         bool ret = false;
109
110         if ((dm_new_crtc_state->crc_window.x_start != dm_old_crtc_state->crc_window.x_start) ||
111                 (dm_new_crtc_state->crc_window.y_start != dm_old_crtc_state->crc_window.y_start) ||
112                 (dm_new_crtc_state->crc_window.x_end != dm_old_crtc_state->crc_window.x_end) ||
113                 (dm_new_crtc_state->crc_window.y_end != dm_old_crtc_state->crc_window.y_end))
114                 ret = true;
115
116         return ret;
117 }
118
119 int
120 amdgpu_dm_crtc_verify_crc_source(struct drm_crtc *crtc, const char *src_name,
121                                  size_t *values_cnt)
122 {
123         enum amdgpu_dm_pipe_crc_source source = dm_parse_crc_source(src_name);
124
125         if (source < 0) {
126                 DRM_DEBUG_DRIVER("Unknown CRC source %s for CRTC%d\n",
127                                  src_name, crtc->index);
128                 return -EINVAL;
129         }
130
131         *values_cnt = 3;
132         return 0;
133 }
134
135 int amdgpu_dm_crtc_configure_crc_source(struct drm_crtc *crtc,
136                                         struct dm_crtc_state *dm_crtc_state,
137                                         enum amdgpu_dm_pipe_crc_source source)
138 {
139         struct amdgpu_device *adev = drm_to_adev(crtc->dev);
140         struct dc_stream_state *stream_state = dm_crtc_state->stream;
141         bool enable = amdgpu_dm_is_valid_crc_source(source);
142         int ret = 0;
143         struct crc_params *crc_window = NULL, tmp_window;
144
145         /* Configuration will be deferred to stream enable. */
146         if (!stream_state)
147                 return 0;
148
149         mutex_lock(&adev->dm.dc_lock);
150
151         /* Enable CRTC CRC generation if necessary. */
152         if (dm_is_crc_source_crtc(source) || source == AMDGPU_DM_PIPE_CRC_SOURCE_NONE) {
153                 if (!enable)
154                         amdgpu_dm_set_crc_window_default(dm_crtc_state);
155
156                 if (!amdgpu_dm_crc_window_is_default(dm_crtc_state)) {
157                         crc_window = &tmp_window;
158
159                         tmp_window.windowa_x_start = dm_crtc_state->crc_window.x_start;
160                         tmp_window.windowa_y_start = dm_crtc_state->crc_window.y_start;
161                         tmp_window.windowa_x_end = dm_crtc_state->crc_window.x_end;
162                         tmp_window.windowa_y_end = dm_crtc_state->crc_window.y_end;
163                         tmp_window.windowb_x_start = dm_crtc_state->crc_window.x_start;
164                         tmp_window.windowb_y_start = dm_crtc_state->crc_window.y_start;
165                         tmp_window.windowb_x_end = dm_crtc_state->crc_window.x_end;
166                         tmp_window.windowb_y_end = dm_crtc_state->crc_window.y_end;
167                 }
168
169                 if (!dc_stream_configure_crc(stream_state->ctx->dc,
170                                              stream_state, crc_window, enable, enable)) {
171                         ret = -EINVAL;
172                         goto unlock;
173                 }
174         }
175
176         /* Configure dithering */
177         if (!dm_need_crc_dither(source)) {
178                 dc_stream_set_dither_option(stream_state, DITHER_OPTION_TRUN8);
179                 dc_stream_set_dyn_expansion(stream_state->ctx->dc, stream_state,
180                                             DYN_EXPANSION_DISABLE);
181         } else {
182                 dc_stream_set_dither_option(stream_state,
183                                             DITHER_OPTION_DEFAULT);
184                 dc_stream_set_dyn_expansion(stream_state->ctx->dc, stream_state,
185                                             DYN_EXPANSION_AUTO);
186         }
187
188 unlock:
189         mutex_unlock(&adev->dm.dc_lock);
190
191         return ret;
192 }
193
194 int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name)
195 {
196         enum amdgpu_dm_pipe_crc_source source = dm_parse_crc_source(src_name);
197         struct drm_crtc_commit *commit;
198         struct dm_crtc_state *crtc_state;
199         struct drm_dp_aux *aux = NULL;
200         bool enable = false;
201         bool enabled = false;
202         int ret = 0;
203
204         if (source < 0) {
205                 DRM_DEBUG_DRIVER("Unknown CRC source %s for CRTC%d\n",
206                                  src_name, crtc->index);
207                 return -EINVAL;
208         }
209
210         ret = drm_modeset_lock(&crtc->mutex, NULL);
211         if (ret)
212                 return ret;
213
214         spin_lock(&crtc->commit_lock);
215         commit = list_first_entry_or_null(&crtc->commit_list,
216                                           struct drm_crtc_commit, commit_entry);
217         if (commit)
218                 drm_crtc_commit_get(commit);
219         spin_unlock(&crtc->commit_lock);
220
221         if (commit) {
222                 /*
223                  * Need to wait for all outstanding programming to complete
224                  * in commit tail since it can modify CRC related fields and
225                  * hardware state. Since we're holding the CRTC lock we're
226                  * guaranteed that no other commit work can be queued off
227                  * before we modify the state below.
228                  */
229                 ret = wait_for_completion_interruptible_timeout(
230                         &commit->hw_done, 10 * HZ);
231                 if (ret)
232                         goto cleanup;
233         }
234
235         enable = amdgpu_dm_is_valid_crc_source(source);
236         crtc_state = to_dm_crtc_state(crtc->state);
237
238         /*
239          * USER REQ SRC | CURRENT SRC | BEHAVIOR
240          * -----------------------------
241          * None         | None        | Do nothing
242          * None         | CRTC        | Disable CRTC CRC, set default to dither
243          * None         | DPRX        | Disable DPRX CRC, need 'aux', set default to dither
244          * None         | CRTC DITHER | Disable CRTC CRC
245          * None         | DPRX DITHER | Disable DPRX CRC, need 'aux'
246          * CRTC         | XXXX        | Enable CRTC CRC, no dither
247          * DPRX         | XXXX        | Enable DPRX CRC, need 'aux', no dither
248          * CRTC DITHER  | XXXX        | Enable CRTC CRC, set dither
249          * DPRX DITHER  | XXXX        | Enable DPRX CRC, need 'aux', set dither
250          */
251         if (dm_is_crc_source_dprx(source) ||
252             (source == AMDGPU_DM_PIPE_CRC_SOURCE_NONE &&
253              dm_is_crc_source_dprx(crtc_state->crc_src))) {
254                 struct amdgpu_dm_connector *aconn = NULL;
255                 struct drm_connector *connector;
256                 struct drm_connector_list_iter conn_iter;
257
258                 drm_connector_list_iter_begin(crtc->dev, &conn_iter);
259                 drm_for_each_connector_iter(connector, &conn_iter) {
260                         if (!connector->state || connector->state->crtc != crtc)
261                                 continue;
262
263                         aconn = to_amdgpu_dm_connector(connector);
264                         break;
265                 }
266                 drm_connector_list_iter_end(&conn_iter);
267
268                 if (!aconn) {
269                         DRM_DEBUG_DRIVER("No amd connector matching CRTC-%d\n", crtc->index);
270                         ret = -EINVAL;
271                         goto cleanup;
272                 }
273
274                 aux = &aconn->dm_dp_aux.aux;
275
276                 if (!aux) {
277                         DRM_DEBUG_DRIVER("No dp aux for amd connector\n");
278                         ret = -EINVAL;
279                         goto cleanup;
280                 }
281         }
282
283         if (amdgpu_dm_crtc_configure_crc_source(crtc, crtc_state, source)) {
284                 ret = -EINVAL;
285                 goto cleanup;
286         }
287
288         /*
289          * Reading the CRC requires the vblank interrupt handler to be
290          * enabled. Keep a reference until CRC capture stops.
291          */
292         enabled = amdgpu_dm_is_valid_crc_source(crtc_state->crc_src);
293         if (!enabled && enable) {
294                 ret = drm_crtc_vblank_get(crtc);
295                 if (ret)
296                         goto cleanup;
297
298                 if (dm_is_crc_source_dprx(source)) {
299                         if (drm_dp_start_crc(aux, crtc)) {
300                                 DRM_DEBUG_DRIVER("dp start crc failed\n");
301                                 ret = -EINVAL;
302                                 goto cleanup;
303                         }
304                 }
305         } else if (enabled && !enable) {
306                 drm_crtc_vblank_put(crtc);
307                 if (dm_is_crc_source_dprx(source)) {
308                         if (drm_dp_stop_crc(aux)) {
309                                 DRM_DEBUG_DRIVER("dp stop crc failed\n");
310                                 ret = -EINVAL;
311                                 goto cleanup;
312                         }
313                 }
314         }
315
316         crtc_state->crc_src = source;
317
318         /* Reset crc_skipped on dm state */
319         crtc_state->crc_skip_count = 0;
320
321 cleanup:
322         if (commit)
323                 drm_crtc_commit_put(commit);
324
325         drm_modeset_unlock(&crtc->mutex);
326
327         return ret;
328 }
329
330 /**
331  * amdgpu_dm_crtc_handle_crc_irq: Report to DRM the CRC on given CRTC.
332  * @crtc: DRM CRTC object.
333  *
334  * This function should be called at the end of a vblank, when the fb has been
335  * fully processed through the pipe.
336  */
337 void amdgpu_dm_crtc_handle_crc_irq(struct drm_crtc *crtc)
338 {
339         struct dm_crtc_state *crtc_state;
340         struct dc_stream_state *stream_state;
341         uint32_t crcs[3];
342
343         if (crtc == NULL)
344                 return;
345
346         crtc_state = to_dm_crtc_state(crtc->state);
347         stream_state = crtc_state->stream;
348
349         /* Early return if CRC capture is not enabled. */
350         if (!amdgpu_dm_is_valid_crc_source(crtc_state->crc_src))
351                 return;
352
353         /*
354          * Since flipping and crc enablement happen asynchronously, we - more
355          * often than not - will be returning an 'uncooked' crc on first frame.
356          * Probably because hw isn't ready yet. For added security, skip the
357          * first two CRC values.
358          */
359         if (crtc_state->crc_skip_count < 2) {
360                 crtc_state->crc_skip_count += 1;
361                 return;
362         }
363
364         if (dm_is_crc_source_crtc(crtc_state->crc_src)) {
365                 if (!dc_stream_get_crc(stream_state->ctx->dc, stream_state,
366                                        &crcs[0], &crcs[1], &crcs[2]))
367                         return;
368
369                 drm_crtc_add_crc_entry(crtc, true,
370                                        drm_crtc_accurate_vblank_count(crtc), crcs);
371         }
372 }