Branch data Line data Source code
1 : : // SPDX-License-Identifier: MIT
2 : : /*
3 : : * Copyright 2018 Noralf Trønnes
4 : : * Copyright (c) 2006-2009 Red Hat Inc.
5 : : * Copyright (c) 2006-2008 Intel Corporation
6 : : * Jesse Barnes <jesse.barnes@intel.com>
7 : : * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
8 : : */
9 : :
10 : : #include <linux/module.h>
11 : : #include <linux/mutex.h>
12 : : #include <linux/slab.h>
13 : :
14 : : #include <drm/drm_atomic.h>
15 : : #include <drm/drm_client.h>
16 : : #include <drm/drm_connector.h>
17 : : #include <drm/drm_crtc.h>
18 : : #include <drm/drm_device.h>
19 : : #include <drm/drm_drv.h>
20 : : #include <drm/drm_encoder.h>
21 : : #include <drm/drm_print.h>
22 : :
23 : : #include "drm_crtc_internal.h"
24 : : #include "drm_internal.h"
25 : :
26 : : #define DRM_CLIENT_MAX_CLONED_CONNECTORS 8
27 : :
28 : : struct drm_client_offset {
29 : : int x, y;
30 : : };
31 : :
32 : 0 : int drm_client_modeset_create(struct drm_client_dev *client)
33 : : {
34 : 0 : struct drm_device *dev = client->dev;
35 : 0 : unsigned int num_crtc = dev->mode_config.num_crtc;
36 : 0 : unsigned int max_connector_count = 1;
37 : 0 : struct drm_mode_set *modeset;
38 : 0 : struct drm_crtc *crtc;
39 : 0 : unsigned int i = 0;
40 : :
41 : : /* Add terminating zero entry to enable index less iteration */
42 : 0 : client->modesets = kcalloc(num_crtc + 1, sizeof(*client->modesets), GFP_KERNEL);
43 [ # # ]: 0 : if (!client->modesets)
44 : : return -ENOMEM;
45 : :
46 : 0 : mutex_init(&client->modeset_mutex);
47 : :
48 [ # # ]: 0 : drm_for_each_crtc(crtc, dev)
49 : 0 : client->modesets[i++].crtc = crtc;
50 : :
51 : : /* Cloning is only supported in the single crtc case. */
52 [ # # ]: 0 : if (num_crtc == 1)
53 : 0 : max_connector_count = DRM_CLIENT_MAX_CLONED_CONNECTORS;
54 : :
55 [ # # ]: 0 : for (modeset = client->modesets; modeset->crtc; modeset++) {
56 : 0 : modeset->connectors = kcalloc(max_connector_count,
57 : : sizeof(*modeset->connectors), GFP_KERNEL);
58 [ # # ]: 0 : if (!modeset->connectors)
59 : 0 : goto err_free;
60 : : }
61 : :
62 : : return 0;
63 : :
64 : : err_free:
65 : 0 : drm_client_modeset_free(client);
66 : :
67 : 0 : return -ENOMEM;
68 : : }
69 : :
70 : : static void drm_client_modeset_release(struct drm_client_dev *client)
71 : : {
72 : : struct drm_mode_set *modeset;
73 : : unsigned int i;
74 : :
75 : : drm_client_for_each_modeset(modeset, client) {
76 : : drm_mode_destroy(client->dev, modeset->mode);
77 : : modeset->mode = NULL;
78 : : modeset->fb = NULL;
79 : :
80 : : for (i = 0; i < modeset->num_connectors; i++) {
81 : : drm_connector_put(modeset->connectors[i]);
82 : : modeset->connectors[i] = NULL;
83 : : }
84 : : modeset->num_connectors = 0;
85 : : }
86 : : }
87 : :
88 : 0 : void drm_client_modeset_free(struct drm_client_dev *client)
89 : : {
90 : 0 : struct drm_mode_set *modeset;
91 : :
92 : 0 : mutex_lock(&client->modeset_mutex);
93 : :
94 : 0 : drm_client_modeset_release(client);
95 : :
96 [ # # ]: 0 : drm_client_for_each_modeset(modeset, client)
97 : 0 : kfree(modeset->connectors);
98 : :
99 : 0 : mutex_unlock(&client->modeset_mutex);
100 : :
101 : 0 : mutex_destroy(&client->modeset_mutex);
102 : 0 : kfree(client->modesets);
103 : 0 : }
104 : :
105 : : static struct drm_mode_set *
106 : 0 : drm_client_find_modeset(struct drm_client_dev *client, struct drm_crtc *crtc)
107 : : {
108 : 0 : struct drm_mode_set *modeset;
109 : :
110 [ # # ]: 0 : drm_client_for_each_modeset(modeset, client)
111 [ # # ]: 0 : if (modeset->crtc == crtc)
112 : : return modeset;
113 : :
114 : : return NULL;
115 : : }
116 : :
117 : : static struct drm_display_mode *
118 : 0 : drm_connector_get_tiled_mode(struct drm_connector *connector)
119 : : {
120 : 0 : struct drm_display_mode *mode;
121 : :
122 [ # # # # ]: 0 : list_for_each_entry(mode, &connector->modes, head) {
123 [ # # # # ]: 0 : if (mode->hdisplay == connector->tile_h_size &&
124 [ # # # # ]: 0 : mode->vdisplay == connector->tile_v_size)
125 : : return mode;
126 : : }
127 : : return NULL;
128 : : }
129 : :
130 : : static struct drm_display_mode *
131 : 0 : drm_connector_fallback_non_tiled_mode(struct drm_connector *connector)
132 : : {
133 : 0 : struct drm_display_mode *mode;
134 : :
135 [ # # ]: 0 : list_for_each_entry(mode, &connector->modes, head) {
136 [ # # ]: 0 : if (mode->hdisplay == connector->tile_h_size &&
137 [ # # ]: 0 : mode->vdisplay == connector->tile_v_size)
138 : 0 : continue;
139 : : return mode;
140 : : }
141 : : return NULL;
142 : : }
143 : :
144 : : static struct drm_display_mode *
145 : 0 : drm_connector_has_preferred_mode(struct drm_connector *connector, int width, int height)
146 : : {
147 : 0 : struct drm_display_mode *mode;
148 : :
149 [ # # # # ]: 0 : list_for_each_entry(mode, &connector->modes, head) {
150 [ # # # # ]: 0 : if (mode->hdisplay > width ||
151 [ # # # # ]: 0 : mode->vdisplay > height)
152 : 0 : continue;
153 [ # # # # ]: 0 : if (mode->type & DRM_MODE_TYPE_PREFERRED)
154 : : return mode;
155 : : }
156 : : return NULL;
157 : : }
158 : :
159 : : static struct drm_display_mode *
160 : 0 : drm_connector_pick_cmdline_mode(struct drm_connector *connector)
161 : : {
162 : 0 : struct drm_cmdline_mode *cmdline_mode;
163 : 0 : struct drm_display_mode *mode;
164 : 0 : bool prefer_non_interlace;
165 : :
166 : 0 : cmdline_mode = &connector->cmdline_mode;
167 [ # # ]: 0 : if (cmdline_mode->specified == false)
168 : : return NULL;
169 : :
170 : : /* attempt to find a matching mode in the list of modes
171 : : * we have gotten so far, if not add a CVT mode that conforms
172 : : */
173 [ # # ]: 0 : if (cmdline_mode->rb || cmdline_mode->margins)
174 : 0 : goto create_mode;
175 : :
176 : 0 : prefer_non_interlace = !cmdline_mode->interlace;
177 : 0 : again:
178 [ # # ]: 0 : list_for_each_entry(mode, &connector->modes, head) {
179 : : /* Check (optional) mode name first */
180 [ # # ]: 0 : if (!strcmp(mode->name, cmdline_mode->name))
181 : 0 : return mode;
182 : :
183 : : /* check width/height */
184 [ # # ]: 0 : if (mode->hdisplay != cmdline_mode->xres ||
185 [ # # ]: 0 : mode->vdisplay != cmdline_mode->yres)
186 : 0 : continue;
187 : :
188 [ # # ]: 0 : if (cmdline_mode->refresh_specified) {
189 [ # # ]: 0 : if (mode->vrefresh != cmdline_mode->refresh)
190 : 0 : continue;
191 : : }
192 : :
193 [ # # ]: 0 : if (cmdline_mode->interlace) {
194 [ # # ]: 0 : if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
195 : 0 : continue;
196 [ # # ]: 0 : } else if (prefer_non_interlace) {
197 [ # # ]: 0 : if (mode->flags & DRM_MODE_FLAG_INTERLACE)
198 : 0 : continue;
199 : : }
200 : : return mode;
201 : : }
202 : :
203 [ # # ]: 0 : if (prefer_non_interlace) {
204 : 0 : prefer_non_interlace = false;
205 : 0 : goto again;
206 : : }
207 : :
208 : 0 : create_mode:
209 : 0 : mode = drm_mode_create_from_cmdline_mode(connector->dev, cmdline_mode);
210 [ # # ]: 0 : if (mode)
211 : 0 : list_add(&mode->head, &connector->modes);
212 : :
213 : : return mode;
214 : : }
215 : :
216 : 0 : static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
217 : : {
218 : 0 : bool enable;
219 : :
220 : 0 : if (connector->display_info.non_desktop)
221 : : return false;
222 : :
223 : 0 : if (strict)
224 : 0 : enable = connector->status == connector_status_connected;
225 : : else
226 : 0 : enable = connector->status != connector_status_disconnected;
227 : :
228 : : return enable;
229 : : }
230 : :
231 : 0 : static void drm_client_connectors_enabled(struct drm_connector **connectors,
232 : : unsigned int connector_count,
233 : : bool *enabled)
234 : : {
235 : 0 : bool any_enabled = false;
236 : 0 : struct drm_connector *connector;
237 : 0 : int i = 0;
238 : :
239 [ # # ]: 0 : for (i = 0; i < connector_count; i++) {
240 : 0 : connector = connectors[i];
241 [ # # ]: 0 : enabled[i] = drm_connector_enabled(connector, true);
242 [ # # # # ]: 0 : DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
243 : : connector->display_info.non_desktop ? "non desktop" : enabled[i] ? "yes" : "no");
244 : :
245 : 0 : any_enabled |= enabled[i];
246 : : }
247 : :
248 [ # # ]: 0 : if (any_enabled)
249 : : return;
250 : :
251 [ # # ]: 0 : for (i = 0; i < connector_count; i++)
252 [ # # ]: 0 : enabled[i] = drm_connector_enabled(connectors[i], false);
253 : : }
254 : :
255 : : static bool drm_client_target_cloned(struct drm_device *dev,
256 : : struct drm_connector **connectors,
257 : : unsigned int connector_count,
258 : : struct drm_display_mode **modes,
259 : : struct drm_client_offset *offsets,
260 : : bool *enabled, int width, int height)
261 : : {
262 : : int count, i, j;
263 : : bool can_clone = false;
264 : : struct drm_display_mode *dmt_mode, *mode;
265 : :
266 : : /* only contemplate cloning in the single crtc case */
267 : : if (dev->mode_config.num_crtc > 1)
268 : : return false;
269 : :
270 : : count = 0;
271 : : for (i = 0; i < connector_count; i++) {
272 : : if (enabled[i])
273 : : count++;
274 : : }
275 : :
276 : : /* only contemplate cloning if more than one connector is enabled */
277 : : if (count <= 1)
278 : : return false;
279 : :
280 : : /* check the command line or if nothing common pick 1024x768 */
281 : : can_clone = true;
282 : : for (i = 0; i < connector_count; i++) {
283 : : if (!enabled[i])
284 : : continue;
285 : : modes[i] = drm_connector_pick_cmdline_mode(connectors[i]);
286 : : if (!modes[i]) {
287 : : can_clone = false;
288 : : break;
289 : : }
290 : : for (j = 0; j < i; j++) {
291 : : if (!enabled[j])
292 : : continue;
293 : : if (!drm_mode_match(modes[j], modes[i],
294 : : DRM_MODE_MATCH_TIMINGS |
295 : : DRM_MODE_MATCH_CLOCK |
296 : : DRM_MODE_MATCH_FLAGS |
297 : : DRM_MODE_MATCH_3D_FLAGS))
298 : : can_clone = false;
299 : : }
300 : : }
301 : :
302 : : if (can_clone) {
303 : : DRM_DEBUG_KMS("can clone using command line\n");
304 : : return true;
305 : : }
306 : :
307 : : /* try and find a 1024x768 mode on each connector */
308 : : can_clone = true;
309 : : dmt_mode = drm_mode_find_dmt(dev, 1024, 768, 60, false);
310 : :
311 : : for (i = 0; i < connector_count; i++) {
312 : : if (!enabled[i])
313 : : continue;
314 : :
315 : : list_for_each_entry(mode, &connectors[i]->modes, head) {
316 : : if (drm_mode_match(mode, dmt_mode,
317 : : DRM_MODE_MATCH_TIMINGS |
318 : : DRM_MODE_MATCH_CLOCK |
319 : : DRM_MODE_MATCH_FLAGS |
320 : : DRM_MODE_MATCH_3D_FLAGS))
321 : : modes[i] = mode;
322 : : }
323 : : if (!modes[i])
324 : : can_clone = false;
325 : : }
326 : :
327 : : if (can_clone) {
328 : : DRM_DEBUG_KMS("can clone using 1024x768\n");
329 : : return true;
330 : : }
331 : : DRM_INFO("kms: can't enable cloning when we probably wanted to.\n");
332 : : return false;
333 : : }
334 : :
335 : 0 : static int drm_client_get_tile_offsets(struct drm_connector **connectors,
336 : : unsigned int connector_count,
337 : : struct drm_display_mode **modes,
338 : : struct drm_client_offset *offsets,
339 : : int idx,
340 : : int h_idx, int v_idx)
341 : : {
342 : 0 : struct drm_connector *connector;
343 : 0 : int i;
344 : 0 : int hoffset = 0, voffset = 0;
345 : :
346 [ # # ]: 0 : for (i = 0; i < connector_count; i++) {
347 : 0 : connector = connectors[i];
348 [ # # ]: 0 : if (!connector->has_tile)
349 : 0 : continue;
350 : :
351 [ # # # # ]: 0 : if (!modes[i] && (h_idx || v_idx)) {
352 : 0 : DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i,
353 : : connector->base.id);
354 : 0 : continue;
355 : : }
356 [ # # ]: 0 : if (connector->tile_h_loc < h_idx)
357 : 0 : hoffset += modes[i]->hdisplay;
358 : :
359 [ # # ]: 0 : if (connector->tile_v_loc < v_idx)
360 : 0 : voffset += modes[i]->vdisplay;
361 : : }
362 : 0 : offsets[idx].x = hoffset;
363 : 0 : offsets[idx].y = voffset;
364 : 0 : DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx);
365 : 0 : return 0;
366 : : }
367 : :
368 : 0 : static bool drm_client_target_preferred(struct drm_connector **connectors,
369 : : unsigned int connector_count,
370 : : struct drm_display_mode **modes,
371 : : struct drm_client_offset *offsets,
372 : : bool *enabled, int width, int height)
373 : : {
374 : 0 : const u64 mask = BIT_ULL(connector_count) - 1;
375 : 0 : struct drm_connector *connector;
376 : 0 : u64 conn_configured = 0;
377 : 0 : int tile_pass = 0;
378 : 0 : int num_tiled_conns = 0;
379 : 0 : int i;
380 : :
381 [ # # ]: 0 : for (i = 0; i < connector_count; i++) {
382 [ # # ]: 0 : if (connectors[i]->has_tile &&
383 [ # # ]: 0 : connectors[i]->status == connector_status_connected)
384 : 0 : num_tiled_conns++;
385 : : }
386 : :
387 : 0 : retry:
388 [ # # ]: 0 : for (i = 0; i < connector_count; i++) {
389 : 0 : connector = connectors[i];
390 : :
391 [ # # ]: 0 : if (conn_configured & BIT_ULL(i))
392 : 0 : continue;
393 : :
394 [ # # ]: 0 : if (enabled[i] == false) {
395 : 0 : conn_configured |= BIT_ULL(i);
396 : 0 : continue;
397 : : }
398 : :
399 : : /* first pass over all the untiled connectors */
400 [ # # # # ]: 0 : if (tile_pass == 0 && connector->has_tile)
401 : 0 : continue;
402 : :
403 [ # # ]: 0 : if (tile_pass == 1) {
404 [ # # ]: 0 : if (connector->tile_h_loc != 0 ||
405 : : connector->tile_v_loc != 0)
406 : 0 : continue;
407 : :
408 : : } else {
409 [ # # ]: 0 : if (connector->tile_h_loc != tile_pass - 1 &&
410 [ # # ]: 0 : connector->tile_v_loc != tile_pass - 1)
411 : : /* if this tile_pass doesn't cover any of the tiles - keep going */
412 : 0 : continue;
413 : :
414 : : /*
415 : : * find the tile offsets for this pass - need to find
416 : : * all tiles left and above
417 : : */
418 : 0 : drm_client_get_tile_offsets(connectors, connector_count, modes, offsets, i,
419 : 0 : connector->tile_h_loc, connector->tile_v_loc);
420 : : }
421 : 0 : DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
422 : : connector->base.id);
423 : :
424 : : /* got for command line mode first */
425 : 0 : modes[i] = drm_connector_pick_cmdline_mode(connector);
426 [ # # ]: 0 : if (!modes[i]) {
427 [ # # ]: 0 : DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n",
428 : : connector->base.id, connector->tile_group ? connector->tile_group->id : 0);
429 : 0 : modes[i] = drm_connector_has_preferred_mode(connector, width, height);
430 : : }
431 : : /* No preferred modes, pick one off the list */
432 [ # # # # ]: 0 : if (!modes[i] && !list_empty(&connector->modes)) {
433 : 0 : list_for_each_entry(modes[i], &connector->modes, head)
434 : : break;
435 : : }
436 : : /*
437 : : * In case of tiled mode if all tiles not present fallback to
438 : : * first available non tiled mode.
439 : : * After all tiles are present, try to find the tiled mode
440 : : * for all and if tiled mode not present due to fbcon size
441 : : * limitations, use first non tiled mode only for
442 : : * tile 0,0 and set to no mode for all other tiles.
443 : : */
444 [ # # ]: 0 : if (connector->has_tile) {
445 : 0 : if (num_tiled_conns <
446 [ # # ]: 0 : connector->num_h_tile * connector->num_v_tile ||
447 [ # # ]: 0 : (connector->tile_h_loc == 0 &&
448 [ # # ]: 0 : connector->tile_v_loc == 0 &&
449 : : !drm_connector_get_tiled_mode(connector))) {
450 : 0 : DRM_DEBUG_KMS("Falling back to non tiled mode on Connector %d\n",
451 : : connector->base.id);
452 : 0 : modes[i] = drm_connector_fallback_non_tiled_mode(connector);
453 : : } else {
454 : 0 : modes[i] = drm_connector_get_tiled_mode(connector);
455 : : }
456 : : }
457 : :
458 [ # # ]: 0 : DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
459 : : "none");
460 : 0 : conn_configured |= BIT_ULL(i);
461 : : }
462 : :
463 [ # # ]: 0 : if ((conn_configured & mask) != mask) {
464 : 0 : tile_pass++;
465 : 0 : goto retry;
466 : : }
467 : 0 : return true;
468 : : }
469 : :
470 : 0 : static bool connector_has_possible_crtc(struct drm_connector *connector,
471 : : struct drm_crtc *crtc)
472 : : {
473 : 0 : struct drm_encoder *encoder;
474 : :
475 [ # # # # ]: 0 : drm_connector_for_each_possible_encoder(connector, encoder) {
476 [ # # ]: 0 : if (encoder->possible_crtcs & drm_crtc_mask(crtc))
477 : : return true;
478 : : }
479 : :
480 : : return false;
481 : : }
482 : :
483 : 0 : static int drm_client_pick_crtcs(struct drm_client_dev *client,
484 : : struct drm_connector **connectors,
485 : : unsigned int connector_count,
486 : : struct drm_crtc **best_crtcs,
487 : : struct drm_display_mode **modes,
488 : : int n, int width, int height)
489 : : {
490 : 0 : struct drm_device *dev = client->dev;
491 : 0 : struct drm_connector *connector;
492 : 0 : int my_score, best_score, score;
493 : 0 : struct drm_crtc **crtcs, *crtc;
494 : 0 : struct drm_mode_set *modeset;
495 : 0 : int o;
496 : :
497 [ # # ]: 0 : if (n == connector_count)
498 : : return 0;
499 : :
500 : 0 : connector = connectors[n];
501 : :
502 : 0 : best_crtcs[n] = NULL;
503 : 0 : best_score = drm_client_pick_crtcs(client, connectors, connector_count,
504 : : best_crtcs, modes, n + 1, width, height);
505 [ # # ]: 0 : if (modes[n] == NULL)
506 : : return best_score;
507 : :
508 : 0 : crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL);
509 [ # # ]: 0 : if (!crtcs)
510 : : return best_score;
511 : :
512 : 0 : my_score = 1;
513 [ # # ]: 0 : if (connector->status == connector_status_connected)
514 : 0 : my_score++;
515 [ # # ]: 0 : if (connector->cmdline_mode.specified)
516 : 0 : my_score++;
517 [ # # ]: 0 : if (drm_connector_has_preferred_mode(connector, width, height))
518 : 0 : my_score++;
519 : :
520 : : /*
521 : : * select a crtc for this connector and then attempt to configure
522 : : * remaining connectors
523 : : */
524 [ # # ]: 0 : drm_client_for_each_modeset(modeset, client) {
525 : 0 : crtc = modeset->crtc;
526 : :
527 [ # # ]: 0 : if (!connector_has_possible_crtc(connector, crtc))
528 : 0 : continue;
529 : :
530 [ # # ]: 0 : for (o = 0; o < n; o++)
531 [ # # ]: 0 : if (best_crtcs[o] == crtc)
532 : : break;
533 : :
534 [ # # ]: 0 : if (o < n) {
535 : : /* ignore cloning unless only a single crtc */
536 [ # # ]: 0 : if (dev->mode_config.num_crtc > 1)
537 : 0 : continue;
538 : :
539 [ # # ]: 0 : if (!drm_mode_equal(modes[o], modes[n]))
540 : 0 : continue;
541 : : }
542 : :
543 : 0 : crtcs[n] = crtc;
544 : 0 : memcpy(crtcs, best_crtcs, n * sizeof(*crtcs));
545 : 0 : score = my_score + drm_client_pick_crtcs(client, connectors, connector_count,
546 : : crtcs, modes, n + 1, width, height);
547 [ # # ]: 0 : if (score > best_score) {
548 : 0 : best_score = score;
549 : 0 : memcpy(best_crtcs, crtcs, connector_count * sizeof(*crtcs));
550 : : }
551 : : }
552 : :
553 : 0 : kfree(crtcs);
554 : 0 : return best_score;
555 : : }
556 : :
557 : : /* Try to read the BIOS display configuration and use it for the initial config */
558 : : static bool drm_client_firmware_config(struct drm_client_dev *client,
559 : : struct drm_connector **connectors,
560 : : unsigned int connector_count,
561 : : struct drm_crtc **crtcs,
562 : : struct drm_display_mode **modes,
563 : : struct drm_client_offset *offsets,
564 : : bool *enabled, int width, int height)
565 : : {
566 : : unsigned int count = min_t(unsigned int, connector_count, BITS_PER_LONG);
567 : : unsigned long conn_configured, conn_seq, mask;
568 : : struct drm_device *dev = client->dev;
569 : : int i, j;
570 : : bool *save_enabled;
571 : : bool fallback = true, ret = true;
572 : : int num_connectors_enabled = 0;
573 : : int num_connectors_detected = 0;
574 : : int num_tiled_conns = 0;
575 : : struct drm_modeset_acquire_ctx ctx;
576 : :
577 : : if (!drm_drv_uses_atomic_modeset(dev))
578 : : return false;
579 : :
580 : : save_enabled = kcalloc(count, sizeof(bool), GFP_KERNEL);
581 : : if (!save_enabled)
582 : : return false;
583 : :
584 : : drm_modeset_acquire_init(&ctx, 0);
585 : :
586 : : while (drm_modeset_lock_all_ctx(dev, &ctx) != 0)
587 : : drm_modeset_backoff(&ctx);
588 : :
589 : : memcpy(save_enabled, enabled, count);
590 : : mask = GENMASK(count - 1, 0);
591 : : conn_configured = 0;
592 : : for (i = 0; i < count; i++) {
593 : : if (connectors[i]->has_tile &&
594 : : connectors[i]->status == connector_status_connected)
595 : : num_tiled_conns++;
596 : : }
597 : : retry:
598 : : conn_seq = conn_configured;
599 : : for (i = 0; i < count; i++) {
600 : : struct drm_connector *connector;
601 : : struct drm_encoder *encoder;
602 : : struct drm_crtc *new_crtc;
603 : :
604 : : connector = connectors[i];
605 : :
606 : : if (conn_configured & BIT(i))
607 : : continue;
608 : :
609 : : if (conn_seq == 0 && !connector->has_tile)
610 : : continue;
611 : :
612 : : if (connector->status == connector_status_connected)
613 : : num_connectors_detected++;
614 : :
615 : : if (!enabled[i]) {
616 : : DRM_DEBUG_KMS("connector %s not enabled, skipping\n",
617 : : connector->name);
618 : : conn_configured |= BIT(i);
619 : : continue;
620 : : }
621 : :
622 : : if (connector->force == DRM_FORCE_OFF) {
623 : : DRM_DEBUG_KMS("connector %s is disabled by user, skipping\n",
624 : : connector->name);
625 : : enabled[i] = false;
626 : : continue;
627 : : }
628 : :
629 : : encoder = connector->state->best_encoder;
630 : : if (!encoder || WARN_ON(!connector->state->crtc)) {
631 : : if (connector->force > DRM_FORCE_OFF)
632 : : goto bail;
633 : :
634 : : DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n",
635 : : connector->name);
636 : : enabled[i] = false;
637 : : conn_configured |= BIT(i);
638 : : continue;
639 : : }
640 : :
641 : : num_connectors_enabled++;
642 : :
643 : : new_crtc = connector->state->crtc;
644 : :
645 : : /*
646 : : * Make sure we're not trying to drive multiple connectors
647 : : * with a single CRTC, since our cloning support may not
648 : : * match the BIOS.
649 : : */
650 : : for (j = 0; j < count; j++) {
651 : : if (crtcs[j] == new_crtc) {
652 : : DRM_DEBUG_KMS("fallback: cloned configuration\n");
653 : : goto bail;
654 : : }
655 : : }
656 : :
657 : : DRM_DEBUG_KMS("looking for cmdline mode on connector %s\n",
658 : : connector->name);
659 : :
660 : : /* go for command line mode first */
661 : : modes[i] = drm_connector_pick_cmdline_mode(connector);
662 : :
663 : : /* try for preferred next */
664 : : if (!modes[i]) {
665 : : DRM_DEBUG_KMS("looking for preferred mode on connector %s %d\n",
666 : : connector->name, connector->has_tile);
667 : : modes[i] = drm_connector_has_preferred_mode(connector, width, height);
668 : : }
669 : :
670 : : /* No preferred mode marked by the EDID? Are there any modes? */
671 : : if (!modes[i] && !list_empty(&connector->modes)) {
672 : : DRM_DEBUG_KMS("using first mode listed on connector %s\n",
673 : : connector->name);
674 : : modes[i] = list_first_entry(&connector->modes,
675 : : struct drm_display_mode,
676 : : head);
677 : : }
678 : :
679 : : /* last resort: use current mode */
680 : : if (!modes[i]) {
681 : : /*
682 : : * IMPORTANT: We want to use the adjusted mode (i.e.
683 : : * after the panel fitter upscaling) as the initial
684 : : * config, not the input mode, which is what crtc->mode
685 : : * usually contains. But since our current
686 : : * code puts a mode derived from the post-pfit timings
687 : : * into crtc->mode this works out correctly.
688 : : *
689 : : * This is crtc->mode and not crtc->state->mode for the
690 : : * fastboot check to work correctly.
691 : : */
692 : : DRM_DEBUG_KMS("looking for current mode on connector %s\n",
693 : : connector->name);
694 : : modes[i] = &connector->state->crtc->mode;
695 : : }
696 : : /*
697 : : * In case of tiled modes, if all tiles are not present
698 : : * then fallback to a non tiled mode.
699 : : */
700 : : if (connector->has_tile &&
701 : : num_tiled_conns < connector->num_h_tile * connector->num_v_tile) {
702 : : DRM_DEBUG_KMS("Falling back to non tiled mode on Connector %d\n",
703 : : connector->base.id);
704 : : modes[i] = drm_connector_fallback_non_tiled_mode(connector);
705 : : }
706 : : crtcs[i] = new_crtc;
707 : :
708 : : DRM_DEBUG_KMS("connector %s on [CRTC:%d:%s]: %dx%d%s\n",
709 : : connector->name,
710 : : connector->state->crtc->base.id,
711 : : connector->state->crtc->name,
712 : : modes[i]->hdisplay, modes[i]->vdisplay,
713 : : modes[i]->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "");
714 : :
715 : : fallback = false;
716 : : conn_configured |= BIT(i);
717 : : }
718 : :
719 : : if ((conn_configured & mask) != mask && conn_configured != conn_seq)
720 : : goto retry;
721 : :
722 : : /*
723 : : * If the BIOS didn't enable everything it could, fall back to have the
724 : : * same user experiencing of lighting up as much as possible like the
725 : : * fbdev helper library.
726 : : */
727 : : if (num_connectors_enabled != num_connectors_detected &&
728 : : num_connectors_enabled < dev->mode_config.num_crtc) {
729 : : DRM_DEBUG_KMS("fallback: Not all outputs enabled\n");
730 : : DRM_DEBUG_KMS("Enabled: %i, detected: %i\n", num_connectors_enabled,
731 : : num_connectors_detected);
732 : : fallback = true;
733 : : }
734 : :
735 : : if (fallback) {
736 : : bail:
737 : : DRM_DEBUG_KMS("Not using firmware configuration\n");
738 : : memcpy(enabled, save_enabled, count);
739 : : ret = false;
740 : : }
741 : :
742 : : drm_modeset_drop_locks(&ctx);
743 : : drm_modeset_acquire_fini(&ctx);
744 : :
745 : : kfree(save_enabled);
746 : : return ret;
747 : : }
748 : :
749 : : /**
750 : : * drm_client_modeset_probe() - Probe for displays
751 : : * @client: DRM client
752 : : * @width: Maximum display mode width (optional)
753 : : * @height: Maximum display mode height (optional)
754 : : *
755 : : * This function sets up display pipelines for enabled connectors and stores the
756 : : * config in the client's modeset array.
757 : : *
758 : : * Returns:
759 : : * Zero on success or negative error code on failure.
760 : : */
761 : 0 : int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width, unsigned int height)
762 : : {
763 : 0 : struct drm_connector *connector, **connectors = NULL;
764 : 0 : struct drm_connector_list_iter conn_iter;
765 : 0 : struct drm_device *dev = client->dev;
766 : 0 : unsigned int total_modes_count = 0;
767 : 0 : struct drm_client_offset *offsets;
768 : 0 : unsigned int connector_count = 0;
769 : 0 : struct drm_display_mode **modes;
770 : 0 : struct drm_crtc **crtcs;
771 : 0 : int i, ret = 0;
772 : 0 : bool *enabled;
773 : :
774 : 0 : DRM_DEBUG_KMS("\n");
775 : :
776 [ # # ]: 0 : if (!width)
777 : 0 : width = dev->mode_config.max_width;
778 [ # # ]: 0 : if (!height)
779 : 0 : height = dev->mode_config.max_height;
780 : :
781 : 0 : drm_connector_list_iter_begin(dev, &conn_iter);
782 [ # # # # ]: 0 : drm_client_for_each_connector_iter(connector, &conn_iter) {
783 : 0 : struct drm_connector **tmp;
784 : :
785 : 0 : tmp = krealloc(connectors, (connector_count + 1) * sizeof(*connectors), GFP_KERNEL);
786 [ # # ]: 0 : if (!tmp) {
787 : 0 : ret = -ENOMEM;
788 : 0 : goto free_connectors;
789 : : }
790 : :
791 : 0 : connectors = tmp;
792 : 0 : drm_connector_get(connector);
793 : 0 : connectors[connector_count++] = connector;
794 : : }
795 : 0 : drm_connector_list_iter_end(&conn_iter);
796 : :
797 [ # # ]: 0 : if (!connector_count)
798 : : return 0;
799 : :
800 : 0 : crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL);
801 : 0 : modes = kcalloc(connector_count, sizeof(*modes), GFP_KERNEL);
802 : 0 : offsets = kcalloc(connector_count, sizeof(*offsets), GFP_KERNEL);
803 : 0 : enabled = kcalloc(connector_count, sizeof(bool), GFP_KERNEL);
804 [ # # # # ]: 0 : if (!crtcs || !modes || !enabled || !offsets) {
805 : 0 : DRM_ERROR("Memory allocation failed\n");
806 : 0 : ret = -ENOMEM;
807 : 0 : goto out;
808 : : }
809 : :
810 : 0 : mutex_lock(&client->modeset_mutex);
811 : :
812 : 0 : mutex_lock(&dev->mode_config.mutex);
813 [ # # ]: 0 : for (i = 0; i < connector_count; i++)
814 : 0 : total_modes_count += connectors[i]->funcs->fill_modes(connectors[i], width, height);
815 [ # # ]: 0 : if (!total_modes_count)
816 : 0 : DRM_DEBUG_KMS("No connectors reported connected with modes\n");
817 : 0 : drm_client_connectors_enabled(connectors, connector_count, enabled);
818 : :
819 [ # # ]: 0 : if (!drm_client_firmware_config(client, connectors, connector_count, crtcs,
820 : : modes, offsets, enabled, width, height)) {
821 : 0 : memset(modes, 0, connector_count * sizeof(*modes));
822 : 0 : memset(crtcs, 0, connector_count * sizeof(*crtcs));
823 : 0 : memset(offsets, 0, connector_count * sizeof(*offsets));
824 : :
825 [ # # ]: 0 : if (!drm_client_target_cloned(dev, connectors, connector_count, modes,
826 [ # # ]: 0 : offsets, enabled, width, height) &&
827 : 0 : !drm_client_target_preferred(connectors, connector_count, modes,
828 : : offsets, enabled, width, height))
829 : 0 : DRM_ERROR("Unable to find initial modes\n");
830 : :
831 : 0 : DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n",
832 : : width, height);
833 : :
834 : 0 : drm_client_pick_crtcs(client, connectors, connector_count,
835 : : crtcs, modes, 0, width, height);
836 : : }
837 : 0 : mutex_unlock(&dev->mode_config.mutex);
838 : :
839 : 0 : drm_client_modeset_release(client);
840 : :
841 [ # # ]: 0 : for (i = 0; i < connector_count; i++) {
842 : 0 : struct drm_display_mode *mode = modes[i];
843 : 0 : struct drm_crtc *crtc = crtcs[i];
844 : 0 : struct drm_client_offset *offset = &offsets[i];
845 : :
846 [ # # ]: 0 : if (mode && crtc) {
847 : 0 : struct drm_mode_set *modeset = drm_client_find_modeset(client, crtc);
848 : 0 : struct drm_connector *connector = connectors[i];
849 : :
850 : 0 : DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n",
851 : : mode->name, crtc->base.id, offset->x, offset->y);
852 : :
853 [ # # # # : 0 : if (WARN_ON_ONCE(modeset->num_connectors == DRM_CLIENT_MAX_CLONED_CONNECTORS ||
# # # # #
# ]
854 : : (dev->mode_config.num_crtc > 1 && modeset->num_connectors == 1))) {
855 : : ret = -EINVAL;
856 : : break;
857 : : }
858 : :
859 : 0 : modeset->mode = drm_mode_duplicate(dev, mode);
860 : 0 : drm_connector_get(connector);
861 : 0 : modeset->connectors[modeset->num_connectors++] = connector;
862 : 0 : modeset->x = offset->x;
863 : 0 : modeset->y = offset->y;
864 : : }
865 : : }
866 : :
867 : 0 : mutex_unlock(&client->modeset_mutex);
868 : 0 : out:
869 : 0 : kfree(crtcs);
870 : 0 : kfree(modes);
871 : 0 : kfree(offsets);
872 : 0 : kfree(enabled);
873 : 0 : free_connectors:
874 [ # # ]: 0 : for (i = 0; i < connector_count; i++)
875 : 0 : drm_connector_put(connectors[i]);
876 : 0 : kfree(connectors);
877 : :
878 : 0 : return ret;
879 : : }
880 : : EXPORT_SYMBOL(drm_client_modeset_probe);
881 : :
882 : : /**
883 : : * drm_client_rotation() - Check the initial rotation value
884 : : * @modeset: DRM modeset
885 : : * @rotation: Returned rotation value
886 : : *
887 : : * This function checks if the primary plane in @modeset can hw rotate
888 : : * to match the rotation needed on its connector.
889 : : *
890 : : * Note: Currently only 0 and 180 degrees are supported.
891 : : *
892 : : * Return:
893 : : * True if the plane can do the rotation, false otherwise.
894 : : */
895 : 0 : bool drm_client_rotation(struct drm_mode_set *modeset, unsigned int *rotation)
896 : : {
897 : 0 : struct drm_connector *connector = modeset->connectors[0];
898 : 0 : struct drm_plane *plane = modeset->crtc->primary;
899 : 0 : struct drm_cmdline_mode *cmdline;
900 : 0 : u64 valid_mask = 0;
901 : 0 : unsigned int i;
902 : :
903 [ # # ]: 0 : if (!modeset->num_connectors)
904 : : return false;
905 : :
906 [ # # # # ]: 0 : switch (connector->display_info.panel_orientation) {
907 : 0 : case DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP:
908 : 0 : *rotation = DRM_MODE_ROTATE_180;
909 : 0 : break;
910 : 0 : case DRM_MODE_PANEL_ORIENTATION_LEFT_UP:
911 : 0 : *rotation = DRM_MODE_ROTATE_90;
912 : 0 : break;
913 : 0 : case DRM_MODE_PANEL_ORIENTATION_RIGHT_UP:
914 : 0 : *rotation = DRM_MODE_ROTATE_270;
915 : 0 : break;
916 : 0 : default:
917 : 0 : *rotation = DRM_MODE_ROTATE_0;
918 : : }
919 : :
920 : : /**
921 : : * The panel already defined the default rotation
922 : : * through its orientation. Whatever has been provided
923 : : * on the command line needs to be added to that.
924 : : *
925 : : * Unfortunately, the rotations are at different bit
926 : : * indices, so the math to add them up are not as
927 : : * trivial as they could.
928 : : *
929 : : * Reflections on the other hand are pretty trivial to deal with, a
930 : : * simple XOR between the two handle the addition nicely.
931 : : */
932 : 0 : cmdline = &connector->cmdline_mode;
933 [ # # # # ]: 0 : if (cmdline->specified && cmdline->rotation_reflection) {
934 : 0 : unsigned int cmdline_rest, panel_rest;
935 : 0 : unsigned int cmdline_rot, panel_rot;
936 : 0 : unsigned int sum_rot, sum_rest;
937 : :
938 [ # # # # : 0 : panel_rot = ilog2(*rotation & DRM_MODE_ROTATE_MASK);
# # # # ]
939 [ # # # # : 0 : cmdline_rot = ilog2(cmdline->rotation_reflection & DRM_MODE_ROTATE_MASK);
# # # # ]
940 : 0 : sum_rot = (panel_rot + cmdline_rot) % 4;
941 : :
942 : 0 : panel_rest = *rotation & ~DRM_MODE_ROTATE_MASK;
943 : 0 : cmdline_rest = cmdline->rotation_reflection & ~DRM_MODE_ROTATE_MASK;
944 : 0 : sum_rest = panel_rest ^ cmdline_rest;
945 : :
946 : 0 : *rotation = (1 << sum_rot) | sum_rest;
947 : : }
948 : :
949 : : /*
950 : : * TODO: support 90 / 270 degree hardware rotation,
951 : : * depending on the hardware this may require the framebuffer
952 : : * to be in a specific tiling format.
953 : : */
954 [ # # ]: 0 : if (((*rotation & DRM_MODE_ROTATE_MASK) != DRM_MODE_ROTATE_0 &&
955 : 0 : (*rotation & DRM_MODE_ROTATE_MASK) != DRM_MODE_ROTATE_180) ||
956 [ # # ]: 0 : !plane->rotation_property)
957 : : return false;
958 : :
959 [ # # ]: 0 : for (i = 0; i < plane->rotation_property->num_values; i++)
960 : 0 : valid_mask |= (1ULL << plane->rotation_property->values[i]);
961 : :
962 [ # # ]: 0 : if (!(*rotation & valid_mask))
963 : 0 : return false;
964 : :
965 : : return true;
966 : : }
967 : : EXPORT_SYMBOL(drm_client_rotation);
968 : :
969 : 0 : static int drm_client_modeset_commit_atomic(struct drm_client_dev *client, bool active)
970 : : {
971 : 0 : struct drm_device *dev = client->dev;
972 : 0 : struct drm_plane *plane;
973 : 0 : struct drm_atomic_state *state;
974 : 0 : struct drm_modeset_acquire_ctx ctx;
975 : 0 : struct drm_mode_set *mode_set;
976 : 0 : int ret;
977 : :
978 : 0 : drm_modeset_acquire_init(&ctx, 0);
979 : :
980 : 0 : state = drm_atomic_state_alloc(dev);
981 [ # # ]: 0 : if (!state) {
982 : 0 : ret = -ENOMEM;
983 : 0 : goto out_ctx;
984 : : }
985 : :
986 : 0 : state->acquire_ctx = &ctx;
987 : 0 : retry:
988 [ # # ]: 0 : drm_for_each_plane(plane, dev) {
989 : 0 : struct drm_plane_state *plane_state;
990 : :
991 : 0 : plane_state = drm_atomic_get_plane_state(state, plane);
992 [ # # ]: 0 : if (IS_ERR(plane_state)) {
993 : 0 : ret = PTR_ERR(plane_state);
994 : 0 : goto out_state;
995 : : }
996 : :
997 : 0 : plane_state->rotation = DRM_MODE_ROTATE_0;
998 : :
999 : : /* disable non-primary: */
1000 [ # # ]: 0 : if (plane->type == DRM_PLANE_TYPE_PRIMARY)
1001 : 0 : continue;
1002 : :
1003 : 0 : ret = __drm_atomic_helper_disable_plane(plane, plane_state);
1004 [ # # ]: 0 : if (ret != 0)
1005 : 0 : goto out_state;
1006 : : }
1007 : :
1008 [ # # ]: 0 : drm_client_for_each_modeset(mode_set, client) {
1009 : 0 : struct drm_plane *primary = mode_set->crtc->primary;
1010 : 0 : unsigned int rotation;
1011 : :
1012 [ # # ]: 0 : if (drm_client_rotation(mode_set, &rotation)) {
1013 : 0 : struct drm_plane_state *plane_state;
1014 : :
1015 : : /* Cannot fail as we've already gotten the plane state above */
1016 : 0 : plane_state = drm_atomic_get_new_plane_state(state, primary);
1017 : 0 : plane_state->rotation = rotation;
1018 : : }
1019 : :
1020 : 0 : ret = __drm_atomic_helper_set_config(mode_set, state);
1021 [ # # ]: 0 : if (ret != 0)
1022 : 0 : goto out_state;
1023 : :
1024 : : /*
1025 : : * __drm_atomic_helper_set_config() sets active when a
1026 : : * mode is set, unconditionally clear it if we force DPMS off
1027 : : */
1028 [ # # ]: 0 : if (!active) {
1029 : 0 : struct drm_crtc *crtc = mode_set->crtc;
1030 : 0 : struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
1031 : :
1032 : 0 : crtc_state->active = false;
1033 : : }
1034 : : }
1035 : :
1036 : 0 : ret = drm_atomic_commit(state);
1037 : :
1038 : 0 : out_state:
1039 [ # # ]: 0 : if (ret == -EDEADLK)
1040 : 0 : goto backoff;
1041 : :
1042 : 0 : drm_atomic_state_put(state);
1043 : 0 : out_ctx:
1044 : 0 : drm_modeset_drop_locks(&ctx);
1045 : 0 : drm_modeset_acquire_fini(&ctx);
1046 : :
1047 : 0 : return ret;
1048 : :
1049 : : backoff:
1050 : 0 : drm_atomic_state_clear(state);
1051 : 0 : drm_modeset_backoff(&ctx);
1052 : :
1053 : 0 : goto retry;
1054 : : }
1055 : :
1056 : : static int drm_client_modeset_commit_legacy(struct drm_client_dev *client)
1057 : : {
1058 : : struct drm_device *dev = client->dev;
1059 : : struct drm_mode_set *mode_set;
1060 : : struct drm_plane *plane;
1061 : : int ret = 0;
1062 : :
1063 : : drm_modeset_lock_all(dev);
1064 : : drm_for_each_plane(plane, dev) {
1065 : : if (plane->type != DRM_PLANE_TYPE_PRIMARY)
1066 : : drm_plane_force_disable(plane);
1067 : :
1068 : : if (plane->rotation_property)
1069 : : drm_mode_plane_set_obj_prop(plane,
1070 : : plane->rotation_property,
1071 : : DRM_MODE_ROTATE_0);
1072 : : }
1073 : :
1074 : : drm_client_for_each_modeset(mode_set, client) {
1075 : : struct drm_crtc *crtc = mode_set->crtc;
1076 : :
1077 : : if (crtc->funcs->cursor_set2) {
1078 : : ret = crtc->funcs->cursor_set2(crtc, NULL, 0, 0, 0, 0, 0);
1079 : : if (ret)
1080 : : goto out;
1081 : : } else if (crtc->funcs->cursor_set) {
1082 : : ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0);
1083 : : if (ret)
1084 : : goto out;
1085 : : }
1086 : :
1087 : : ret = drm_mode_set_config_internal(mode_set);
1088 : : if (ret)
1089 : : goto out;
1090 : : }
1091 : : out:
1092 : : drm_modeset_unlock_all(dev);
1093 : :
1094 : : return ret;
1095 : : }
1096 : :
1097 : : /**
1098 : : * drm_client_modeset_commit_force() - Force commit CRTC configuration
1099 : : * @client: DRM client
1100 : : *
1101 : : * Commit modeset configuration to crtcs without checking if there is a DRM master.
1102 : : *
1103 : : * Returns:
1104 : : * Zero on success or negative error code on failure.
1105 : : */
1106 : 0 : int drm_client_modeset_commit_force(struct drm_client_dev *client)
1107 : : {
1108 : 0 : struct drm_device *dev = client->dev;
1109 : 0 : int ret;
1110 : :
1111 : 0 : mutex_lock(&client->modeset_mutex);
1112 [ # # # # ]: 0 : if (drm_drv_uses_atomic_modeset(dev))
1113 : 0 : ret = drm_client_modeset_commit_atomic(client, true);
1114 : : else
1115 : 0 : ret = drm_client_modeset_commit_legacy(client);
1116 : 0 : mutex_unlock(&client->modeset_mutex);
1117 : :
1118 : 0 : return ret;
1119 : : }
1120 : : EXPORT_SYMBOL(drm_client_modeset_commit_force);
1121 : :
1122 : : /**
1123 : : * drm_client_modeset_commit() - Commit CRTC configuration
1124 : : * @client: DRM client
1125 : : *
1126 : : * Commit modeset configuration to crtcs.
1127 : : *
1128 : : * Returns:
1129 : : * Zero on success or negative error code on failure.
1130 : : */
1131 : 0 : int drm_client_modeset_commit(struct drm_client_dev *client)
1132 : : {
1133 : 0 : struct drm_device *dev = client->dev;
1134 : 0 : int ret;
1135 : :
1136 [ # # ]: 0 : if (!drm_master_internal_acquire(dev))
1137 : : return -EBUSY;
1138 : :
1139 : 0 : ret = drm_client_modeset_commit_force(client);
1140 : :
1141 : 0 : drm_master_internal_release(dev);
1142 : :
1143 : 0 : return ret;
1144 : : }
1145 : : EXPORT_SYMBOL(drm_client_modeset_commit);
1146 : :
1147 : : static void drm_client_modeset_dpms_legacy(struct drm_client_dev *client, int dpms_mode)
1148 : : {
1149 : : struct drm_device *dev = client->dev;
1150 : : struct drm_connector *connector;
1151 : : struct drm_mode_set *modeset;
1152 : : int j;
1153 : :
1154 : : drm_modeset_lock_all(dev);
1155 : : drm_client_for_each_modeset(modeset, client) {
1156 : : if (!modeset->crtc->enabled)
1157 : : continue;
1158 : :
1159 : : for (j = 0; j < modeset->num_connectors; j++) {
1160 : : connector = modeset->connectors[j];
1161 : : connector->funcs->dpms(connector, dpms_mode);
1162 : : drm_object_property_set_value(&connector->base,
1163 : : dev->mode_config.dpms_property, dpms_mode);
1164 : : }
1165 : : }
1166 : : drm_modeset_unlock_all(dev);
1167 : : }
1168 : :
1169 : : /**
1170 : : * drm_client_modeset_dpms() - Set DPMS mode
1171 : : * @client: DRM client
1172 : : * @mode: DPMS mode
1173 : : *
1174 : : * Note: For atomic drivers @mode is reduced to on/off.
1175 : : *
1176 : : * Returns:
1177 : : * Zero on success or negative error code on failure.
1178 : : */
1179 : 0 : int drm_client_modeset_dpms(struct drm_client_dev *client, int mode)
1180 : : {
1181 : 0 : struct drm_device *dev = client->dev;
1182 : 0 : int ret = 0;
1183 : :
1184 [ # # ]: 0 : if (!drm_master_internal_acquire(dev))
1185 : : return -EBUSY;
1186 : :
1187 : 0 : mutex_lock(&client->modeset_mutex);
1188 [ # # # # ]: 0 : if (drm_drv_uses_atomic_modeset(dev))
1189 : 0 : ret = drm_client_modeset_commit_atomic(client, mode == DRM_MODE_DPMS_ON);
1190 : : else
1191 : 0 : drm_client_modeset_dpms_legacy(client, mode);
1192 : 0 : mutex_unlock(&client->modeset_mutex);
1193 : :
1194 : 0 : drm_master_internal_release(dev);
1195 : :
1196 : 0 : return ret;
1197 : : }
1198 : : EXPORT_SYMBOL(drm_client_modeset_dpms);
|