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