1235783Skib/* 2235783Skib * Copyright (c) 2006-2008 Intel Corporation 3235783Skib * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> 4235783Skib * 5235783Skib * DRM core CRTC related functions 6235783Skib * 7235783Skib * Permission to use, copy, modify, distribute, and sell this software and its 8235783Skib * documentation for any purpose is hereby granted without fee, provided that 9235783Skib * the above copyright notice appear in all copies and that both that copyright 10235783Skib * notice and this permission notice appear in supporting documentation, and 11235783Skib * that the name of the copyright holders not be used in advertising or 12235783Skib * publicity pertaining to distribution of the software without specific, 13235783Skib * written prior permission. The copyright holders make no representations 14235783Skib * about the suitability of this software for any purpose. It is provided "as 15235783Skib * is" without express or implied warranty. 16235783Skib * 17235783Skib * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 18235783Skib * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 19235783Skib * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 20235783Skib * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 21235783Skib * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 22235783Skib * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 23235783Skib * OF THIS SOFTWARE. 24235783Skib * 25235783Skib * Authors: 26235783Skib * Keith Packard 27235783Skib * Eric Anholt <eric@anholt.net> 28235783Skib * Dave Airlie <airlied@linux.ie> 29235783Skib * Jesse Barnes <jesse.barnes@intel.com> 30235783Skib */ 31235783Skib 32235783Skib#include <sys/cdefs.h> 33235783Skib__FBSDID("$FreeBSD$"); 34235783Skib 35235783Skib#include <sys/param.h> 36235783Skib#include <sys/systm.h> 37235783Skib#include <dev/drm2/drmP.h> 38235783Skib#include <dev/drm2/drm_crtc.h> 39235783Skib#include <dev/drm2/drm_fourcc.h> 40235783Skib#include <dev/drm2/drm_crtc_helper.h> 41235783Skib#include <dev/drm2/drm_fb_helper.h> 42235783Skib 43235783Skibbool 44235783Skibdrm_fetch_cmdline_mode_from_kenv(struct drm_connector *connector, 45235783Skib struct drm_cmdline_mode *cmdline_mode) 46235783Skib{ 47235783Skib char *tun_var_name, *tun_mode; 48235783Skib static const char tun_prefix[] = "drm_mode."; 49235783Skib bool res; 50235783Skib 51235783Skib res = false; 52235783Skib tun_var_name = malloc(sizeof(tun_prefix) + 53235783Skib strlen(drm_get_connector_name(connector)), M_TEMP, M_WAITOK); 54235783Skib strcpy(tun_var_name, tun_prefix); 55235783Skib strcat(tun_var_name, drm_get_connector_name(connector)); 56235783Skib tun_mode = getenv(tun_var_name); 57235783Skib if (tun_mode != NULL) { 58235783Skib res = drm_mode_parse_command_line_for_connector(tun_mode, 59235783Skib connector, cmdline_mode); 60235783Skib freeenv(tun_mode); 61235783Skib } 62235783Skib free(tun_var_name, M_TEMP); 63235783Skib return (res); 64235783Skib} 65235783Skib 66235783Skibstatic bool drm_kms_helper_poll = true; 67235783Skib 68235783Skibstatic void drm_mode_validate_flag(struct drm_connector *connector, 69235783Skib int flags) 70235783Skib{ 71235783Skib struct drm_display_mode *mode, *t; 72235783Skib 73235783Skib if (flags == (DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_INTERLACE)) 74235783Skib return; 75235783Skib 76235783Skib list_for_each_entry_safe(mode, t, &connector->modes, head) { 77235783Skib if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && 78235783Skib !(flags & DRM_MODE_FLAG_INTERLACE)) 79235783Skib mode->status = MODE_NO_INTERLACE; 80235783Skib if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) && 81235783Skib !(flags & DRM_MODE_FLAG_DBLSCAN)) 82235783Skib mode->status = MODE_NO_DBLESCAN; 83235783Skib } 84235783Skib 85235783Skib return; 86235783Skib} 87235783Skib 88235783Skib/** 89235783Skib * drm_helper_probe_single_connector_modes - get complete set of display modes 90235783Skib * @dev: DRM device 91235783Skib * @maxX: max width for modes 92235783Skib * @maxY: max height for modes 93235783Skib * 94235783Skib * LOCKING: 95235783Skib * Caller must hold mode config lock. 96235783Skib * 97235783Skib * Based on @dev's mode_config layout, scan all the connectors and try to detect 98235783Skib * modes on them. Modes will first be added to the connector's probed_modes 99235783Skib * list, then culled (based on validity and the @maxX, @maxY parameters) and 100235783Skib * put into the normal modes list. 101235783Skib * 102235783Skib * Intended to be used either at bootup time or when major configuration 103235783Skib * changes have occurred. 104235783Skib * 105235783Skib * FIXME: take into account monitor limits 106235783Skib * 107235783Skib * RETURNS: 108235783Skib * Number of modes found on @connector. 109235783Skib */ 110235783Skibint drm_helper_probe_single_connector_modes(struct drm_connector *connector, 111235783Skib uint32_t maxX, uint32_t maxY) 112235783Skib{ 113235783Skib struct drm_device *dev = connector->dev; 114235783Skib struct drm_display_mode *mode, *t; 115235783Skib struct drm_connector_helper_funcs *connector_funcs = 116235783Skib connector->helper_private; 117235783Skib struct drm_cmdline_mode cmdline_mode; 118235783Skib int count = 0; 119235783Skib int mode_flags = 0; 120235783Skib 121235783Skib DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, 122235783Skib drm_get_connector_name(connector)); 123235783Skib /* set all modes to the unverified state */ 124235783Skib list_for_each_entry_safe(mode, t, &connector->modes, head) 125235783Skib mode->status = MODE_UNVERIFIED; 126235783Skib 127235783Skib if (connector->force) { 128235783Skib if (connector->force == DRM_FORCE_ON) 129235783Skib connector->status = connector_status_connected; 130235783Skib else 131235783Skib connector->status = connector_status_disconnected; 132235783Skib if (connector->funcs->force) 133235783Skib connector->funcs->force(connector); 134235783Skib } else { 135235783Skib connector->status = connector->funcs->detect(connector, true); 136235783Skib drm_kms_helper_poll_enable(dev); 137235783Skib } 138235783Skib 139235783Skib if (connector->status == connector_status_disconnected) { 140235783Skib DRM_DEBUG_KMS("[CONNECTOR:%d:%s] disconnected\n", 141235783Skib connector->base.id, drm_get_connector_name(connector)); 142235783Skib drm_mode_connector_update_edid_property(connector, NULL); 143235783Skib goto prune; 144235783Skib } 145235783Skib 146235783Skib count = (*connector_funcs->get_modes)(connector); 147235783Skib if (count == 0 && drm_fetch_cmdline_mode_from_kenv(connector, 148235783Skib &cmdline_mode)) { 149235783Skib mode = drm_mode_create_from_cmdline_mode(dev, 150235783Skib &cmdline_mode); 151235783Skib if (mode != NULL) { 152235783Skib DRM_DEBUG_KMS( 153235783Skib "[CONNECTOR:%d:%s] found manual override ", 154235783Skib connector->base.id, 155235783Skib drm_get_connector_name(connector)); 156235783Skib drm_mode_debug_printmodeline(mode); 157235783Skib drm_mode_probed_add(connector, mode); 158235783Skib count++; 159235783Skib } else { 160235783Skib DRM_ERROR( 161235783Skib "[CONNECTOR:%d:%s] manual override mode: parse error\n", 162235783Skib connector->base.id, 163235783Skib drm_get_connector_name(connector)); 164235783Skib } 165235783Skib } 166235783Skib if (count == 0 && connector->status == connector_status_connected) 167235783Skib count = drm_add_modes_noedid(connector, 1024, 768); 168235783Skib if (count == 0) 169235783Skib goto prune; 170235783Skib 171235783Skib drm_mode_connector_list_update(connector); 172235783Skib 173235783Skib if (maxX && maxY) 174235783Skib drm_mode_validate_size(dev, &connector->modes, maxX, 175235783Skib maxY, 0); 176235783Skib 177235783Skib if (connector->interlace_allowed) 178235783Skib mode_flags |= DRM_MODE_FLAG_INTERLACE; 179235783Skib if (connector->doublescan_allowed) 180235783Skib mode_flags |= DRM_MODE_FLAG_DBLSCAN; 181235783Skib drm_mode_validate_flag(connector, mode_flags); 182235783Skib 183235783Skib list_for_each_entry_safe(mode, t, &connector->modes, head) { 184235783Skib if (mode->status == MODE_OK) 185235783Skib mode->status = connector_funcs->mode_valid(connector, 186235783Skib mode); 187235783Skib } 188235783Skib 189235783Skibprune: 190235783Skib drm_mode_prune_invalid(dev, &connector->modes, true); 191235783Skib 192235783Skib if (list_empty(&connector->modes)) 193235783Skib return 0; 194235783Skib 195235783Skib drm_mode_sort(&connector->modes); 196235783Skib 197235783Skib DRM_DEBUG_KMS("[CONNECTOR:%d:%s] probed modes :\n", connector->base.id, 198235783Skib drm_get_connector_name(connector)); 199235783Skib list_for_each_entry_safe(mode, t, &connector->modes, head) { 200235783Skib mode->vrefresh = drm_mode_vrefresh(mode); 201235783Skib 202235783Skib drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); 203235783Skib drm_mode_debug_printmodeline(mode); 204235783Skib } 205235783Skib 206235783Skib return count; 207235783Skib} 208235783Skib 209235783Skib/** 210235783Skib * drm_helper_encoder_in_use - check if a given encoder is in use 211235783Skib * @encoder: encoder to check 212235783Skib * 213235783Skib * LOCKING: 214235783Skib * Caller must hold mode config lock. 215235783Skib * 216235783Skib * Walk @encoders's DRM device's mode_config and see if it's in use. 217235783Skib * 218235783Skib * RETURNS: 219235783Skib * True if @encoder is part of the mode_config, false otherwise. 220235783Skib */ 221235783Skibbool drm_helper_encoder_in_use(struct drm_encoder *encoder) 222235783Skib{ 223235783Skib struct drm_connector *connector; 224235783Skib struct drm_device *dev = encoder->dev; 225235783Skib list_for_each_entry(connector, &dev->mode_config.connector_list, head) 226235783Skib if (connector->encoder == encoder) 227235783Skib return true; 228235783Skib return false; 229235783Skib} 230235783Skib 231235783Skib/** 232235783Skib * drm_helper_crtc_in_use - check if a given CRTC is in a mode_config 233235783Skib * @crtc: CRTC to check 234235783Skib * 235235783Skib * LOCKING: 236235783Skib * Caller must hold mode config lock. 237235783Skib * 238235783Skib * Walk @crtc's DRM device's mode_config and see if it's in use. 239235783Skib * 240235783Skib * RETURNS: 241235783Skib * True if @crtc is part of the mode_config, false otherwise. 242235783Skib */ 243235783Skibbool drm_helper_crtc_in_use(struct drm_crtc *crtc) 244235783Skib{ 245235783Skib struct drm_encoder *encoder; 246235783Skib struct drm_device *dev = crtc->dev; 247235783Skib /* FIXME: Locking around list access? */ 248235783Skib list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) 249235783Skib if (encoder->crtc == crtc && drm_helper_encoder_in_use(encoder)) 250235783Skib return true; 251235783Skib return false; 252235783Skib} 253235783Skib 254235783Skibstatic void 255235783Skibdrm_encoder_disable(struct drm_encoder *encoder) 256235783Skib{ 257235783Skib struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; 258235783Skib 259235783Skib if (encoder_funcs->disable) 260235783Skib (*encoder_funcs->disable)(encoder); 261235783Skib else 262235783Skib (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); 263235783Skib} 264235783Skib 265235783Skib/** 266235783Skib * drm_helper_disable_unused_functions - disable unused objects 267235783Skib * @dev: DRM device 268235783Skib * 269235783Skib * LOCKING: 270235783Skib * Caller must hold mode config lock. 271235783Skib * 272235783Skib * If an connector or CRTC isn't part of @dev's mode_config, it can be disabled 273235783Skib * by calling its dpms function, which should power it off. 274235783Skib */ 275235783Skibvoid drm_helper_disable_unused_functions(struct drm_device *dev) 276235783Skib{ 277235783Skib struct drm_encoder *encoder; 278235783Skib struct drm_connector *connector; 279235783Skib struct drm_crtc *crtc; 280235783Skib 281235783Skib list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 282235783Skib if (!connector->encoder) 283235783Skib continue; 284235783Skib if (connector->status == connector_status_disconnected) 285235783Skib connector->encoder = NULL; 286235783Skib } 287235783Skib 288235783Skib list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 289235783Skib if (!drm_helper_encoder_in_use(encoder)) { 290235783Skib drm_encoder_disable(encoder); 291235783Skib /* disconnector encoder from any connector */ 292235783Skib encoder->crtc = NULL; 293235783Skib } 294235783Skib } 295235783Skib 296235783Skib list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 297235783Skib struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; 298235783Skib crtc->enabled = drm_helper_crtc_in_use(crtc); 299235783Skib if (!crtc->enabled) { 300235783Skib if (crtc_funcs->disable) 301235783Skib (*crtc_funcs->disable)(crtc); 302235783Skib else 303235783Skib (*crtc_funcs->dpms)(crtc, DRM_MODE_DPMS_OFF); 304235783Skib crtc->fb = NULL; 305235783Skib } 306235783Skib } 307235783Skib} 308235783Skib 309235783Skib/** 310235783Skib * drm_encoder_crtc_ok - can a given crtc drive a given encoder? 311235783Skib * @encoder: encoder to test 312235783Skib * @crtc: crtc to test 313235783Skib * 314235783Skib * Return false if @encoder can't be driven by @crtc, true otherwise. 315235783Skib */ 316235783Skibstatic bool drm_encoder_crtc_ok(struct drm_encoder *encoder, 317235783Skib struct drm_crtc *crtc) 318235783Skib{ 319235783Skib struct drm_device *dev; 320235783Skib struct drm_crtc *tmp; 321235783Skib int crtc_mask = 1; 322235783Skib 323235783Skib if (crtc == NULL) 324235783Skib printf("checking null crtc?\n"); 325235783Skib 326235783Skib dev = crtc->dev; 327235783Skib 328235783Skib list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) { 329235783Skib if (tmp == crtc) 330235783Skib break; 331235783Skib crtc_mask <<= 1; 332235783Skib } 333235783Skib 334235783Skib if (encoder->possible_crtcs & crtc_mask) 335235783Skib return true; 336235783Skib return false; 337235783Skib} 338235783Skib 339235783Skib/* 340235783Skib * Check the CRTC we're going to map each output to vs. its current 341235783Skib * CRTC. If they don't match, we have to disable the output and the CRTC 342235783Skib * since the driver will have to re-route things. 343235783Skib */ 344235783Skibstatic void 345235783Skibdrm_crtc_prepare_encoders(struct drm_device *dev) 346235783Skib{ 347235783Skib struct drm_encoder_helper_funcs *encoder_funcs; 348235783Skib struct drm_encoder *encoder; 349235783Skib 350235783Skib list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 351235783Skib encoder_funcs = encoder->helper_private; 352235783Skib /* Disable unused encoders */ 353235783Skib if (encoder->crtc == NULL) 354235783Skib drm_encoder_disable(encoder); 355235783Skib /* Disable encoders whose CRTC is about to change */ 356235783Skib if (encoder_funcs->get_crtc && 357235783Skib encoder->crtc != (*encoder_funcs->get_crtc)(encoder)) 358235783Skib drm_encoder_disable(encoder); 359235783Skib } 360235783Skib} 361235783Skib 362235783Skib/** 363235783Skib * drm_crtc_set_mode - set a mode 364235783Skib * @crtc: CRTC to program 365235783Skib * @mode: mode to use 366235783Skib * @x: width of mode 367235783Skib * @y: height of mode 368235783Skib * 369235783Skib * LOCKING: 370235783Skib * Caller must hold mode config lock. 371235783Skib * 372235783Skib * Try to set @mode on @crtc. Give @crtc and its associated connectors a chance 373235783Skib * to fixup or reject the mode prior to trying to set it. 374235783Skib * 375235783Skib * RETURNS: 376235783Skib * True if the mode was set successfully, or false otherwise. 377235783Skib */ 378235783Skibbool drm_crtc_helper_set_mode(struct drm_crtc *crtc, 379235783Skib struct drm_display_mode *mode, 380235783Skib int x, int y, 381235783Skib struct drm_framebuffer *old_fb) 382235783Skib{ 383235783Skib struct drm_device *dev = crtc->dev; 384235783Skib struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode; 385235783Skib struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; 386235783Skib struct drm_encoder_helper_funcs *encoder_funcs; 387235783Skib int saved_x, saved_y; 388235783Skib struct drm_encoder *encoder; 389235783Skib bool ret = true; 390235783Skib 391235783Skib crtc->enabled = drm_helper_crtc_in_use(crtc); 392235783Skib if (!crtc->enabled) 393235783Skib return true; 394235783Skib 395235783Skib adjusted_mode = drm_mode_duplicate(dev, mode); 396235783Skib if (!adjusted_mode) 397235783Skib return false; 398235783Skib 399235783Skib saved_hwmode = crtc->hwmode; 400235783Skib saved_mode = crtc->mode; 401235783Skib saved_x = crtc->x; 402235783Skib saved_y = crtc->y; 403235783Skib 404235783Skib /* Update crtc values up front so the driver can rely on them for mode 405235783Skib * setting. 406235783Skib */ 407235783Skib crtc->mode = *mode; 408235783Skib crtc->x = x; 409235783Skib crtc->y = y; 410235783Skib 411235783Skib /* Pass our mode to the connectors and the CRTC to give them a chance to 412235783Skib * adjust it according to limitations or connector properties, and also 413235783Skib * a chance to reject the mode entirely. 414235783Skib */ 415235783Skib list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 416235783Skib 417235783Skib if (encoder->crtc != crtc) 418235783Skib continue; 419235783Skib encoder_funcs = encoder->helper_private; 420235783Skib if (!(ret = encoder_funcs->mode_fixup(encoder, mode, 421235783Skib adjusted_mode))) { 422235783Skib goto done; 423235783Skib } 424235783Skib } 425235783Skib 426235783Skib if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) { 427235783Skib goto done; 428235783Skib } 429235783Skib DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); 430235783Skib 431235783Skib /* Prepare the encoders and CRTCs before setting the mode. */ 432235783Skib list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 433235783Skib 434235783Skib if (encoder->crtc != crtc) 435235783Skib continue; 436235783Skib encoder_funcs = encoder->helper_private; 437235783Skib /* Disable the encoders as the first thing we do. */ 438235783Skib encoder_funcs->prepare(encoder); 439235783Skib } 440235783Skib 441235783Skib drm_crtc_prepare_encoders(dev); 442235783Skib 443235783Skib crtc_funcs->prepare(crtc); 444235783Skib 445235783Skib /* Set up the DPLL and any encoders state that needs to adjust or depend 446235783Skib * on the DPLL. 447235783Skib */ 448235783Skib ret = !crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb); 449235783Skib if (!ret) 450235783Skib goto done; 451235783Skib 452235783Skib list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 453235783Skib 454235783Skib if (encoder->crtc != crtc) 455235783Skib continue; 456235783Skib 457235783Skib DRM_DEBUG_KMS("[ENCODER:%d:%s] set [MODE:%d:%s]\n", 458235783Skib encoder->base.id, drm_get_encoder_name(encoder), 459235783Skib mode->base.id, mode->name); 460235783Skib encoder_funcs = encoder->helper_private; 461235783Skib encoder_funcs->mode_set(encoder, mode, adjusted_mode); 462235783Skib } 463235783Skib 464235783Skib /* Now enable the clocks, plane, pipe, and connectors that we set up. */ 465235783Skib crtc_funcs->commit(crtc); 466235783Skib 467235783Skib list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 468235783Skib 469235783Skib if (encoder->crtc != crtc) 470235783Skib continue; 471235783Skib 472235783Skib encoder_funcs = encoder->helper_private; 473235783Skib encoder_funcs->commit(encoder); 474235783Skib 475235783Skib } 476235783Skib 477235783Skib /* Store real post-adjustment hardware mode. */ 478235783Skib crtc->hwmode = *adjusted_mode; 479235783Skib 480235783Skib /* Calculate and store various constants which 481235783Skib * are later needed by vblank and swap-completion 482235783Skib * timestamping. They are derived from true hwmode. 483235783Skib */ 484235783Skib drm_calc_timestamping_constants(crtc); 485235783Skib 486235783Skib /* FIXME: add subpixel order */ 487235783Skibdone: 488235783Skib drm_mode_destroy(dev, adjusted_mode); 489235783Skib if (!ret) { 490235783Skib crtc->hwmode = saved_hwmode; 491235783Skib crtc->mode = saved_mode; 492235783Skib crtc->x = saved_x; 493235783Skib crtc->y = saved_y; 494235783Skib } 495235783Skib 496235783Skib return ret; 497235783Skib} 498235783Skib 499235783Skibstatic int 500235783Skibdrm_crtc_helper_disable(struct drm_crtc *crtc) 501235783Skib{ 502235783Skib struct drm_device *dev = crtc->dev; 503235783Skib struct drm_connector *connector; 504235783Skib struct drm_encoder *encoder; 505235783Skib 506235783Skib /* Decouple all encoders and their attached connectors from this crtc */ 507235783Skib list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 508235783Skib if (encoder->crtc != crtc) 509235783Skib continue; 510235783Skib 511235783Skib list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 512235783Skib if (connector->encoder != encoder) 513235783Skib continue; 514235783Skib 515235783Skib connector->encoder = NULL; 516235783Skib } 517235783Skib } 518235783Skib 519235783Skib drm_helper_disable_unused_functions(dev); 520235783Skib return 0; 521235783Skib} 522235783Skib 523235783Skib/** 524235783Skib * drm_crtc_helper_set_config - set a new config from userspace 525235783Skib * @crtc: CRTC to setup 526235783Skib * @crtc_info: user provided configuration 527235783Skib * @new_mode: new mode to set 528235783Skib * @connector_set: set of connectors for the new config 529235783Skib * @fb: new framebuffer 530235783Skib * 531235783Skib * LOCKING: 532235783Skib * Caller must hold mode config lock. 533235783Skib * 534235783Skib * Setup a new configuration, provided by the user in @crtc_info, and enable 535235783Skib * it. 536235783Skib * 537235783Skib * RETURNS: 538235783Skib * Zero. (FIXME) 539235783Skib */ 540235783Skibint drm_crtc_helper_set_config(struct drm_mode_set *set) 541235783Skib{ 542235783Skib struct drm_device *dev; 543235783Skib struct drm_crtc *save_crtcs, *new_crtc, *crtc; 544235783Skib struct drm_encoder *save_encoders, *new_encoder, *encoder; 545235783Skib struct drm_framebuffer *old_fb = NULL; 546235783Skib bool mode_changed = false; /* if true do a full mode set */ 547235783Skib bool fb_changed = false; /* if true and !mode_changed just do a flip */ 548235783Skib struct drm_connector *save_connectors, *connector; 549235783Skib int count = 0, ro, fail = 0; 550235783Skib struct drm_crtc_helper_funcs *crtc_funcs; 551235783Skib struct drm_mode_set save_set; 552235783Skib int ret = 0; 553235783Skib int i; 554235783Skib 555235783Skib DRM_DEBUG_KMS("\n"); 556235783Skib 557235783Skib if (!set) 558235783Skib return -EINVAL; 559235783Skib 560235783Skib if (!set->crtc) 561235783Skib return -EINVAL; 562235783Skib 563235783Skib if (!set->crtc->helper_private) 564235783Skib return -EINVAL; 565235783Skib 566235783Skib crtc_funcs = set->crtc->helper_private; 567235783Skib 568235783Skib if (!set->mode) 569235783Skib set->fb = NULL; 570235783Skib 571235783Skib if (set->fb) { 572235783Skib DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n", 573235783Skib set->crtc->base.id, set->fb->base.id, 574235783Skib (int)set->num_connectors, set->x, set->y); 575235783Skib } else { 576235783Skib DRM_DEBUG_KMS("[CRTC:%d] [NOFB]\n", set->crtc->base.id); 577235783Skib return drm_crtc_helper_disable(set->crtc); 578235783Skib } 579235783Skib 580235783Skib dev = set->crtc->dev; 581235783Skib 582235783Skib /* Allocate space for the backup of all (non-pointer) crtc, encoder and 583235783Skib * connector data. */ 584235783Skib save_crtcs = malloc(dev->mode_config.num_crtc * sizeof(struct drm_crtc), 585235783Skib DRM_MEM_KMS, M_WAITOK | M_ZERO); 586235783Skib save_encoders = malloc(dev->mode_config.num_encoder * 587235783Skib sizeof(struct drm_encoder), DRM_MEM_KMS, M_WAITOK | M_ZERO); 588235783Skib save_connectors = malloc(dev->mode_config.num_connector * 589235783Skib sizeof(struct drm_connector), DRM_MEM_KMS, M_WAITOK | M_ZERO); 590235783Skib 591235783Skib /* Copy data. Note that driver private data is not affected. 592235783Skib * Should anything bad happen only the expected state is 593235783Skib * restored, not the drivers personal bookkeeping. 594235783Skib */ 595235783Skib count = 0; 596235783Skib list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 597235783Skib save_crtcs[count++] = *crtc; 598235783Skib } 599235783Skib 600235783Skib count = 0; 601235783Skib list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 602235783Skib save_encoders[count++] = *encoder; 603235783Skib } 604235783Skib 605235783Skib count = 0; 606235783Skib list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 607235783Skib save_connectors[count++] = *connector; 608235783Skib } 609235783Skib 610235783Skib save_set.crtc = set->crtc; 611235783Skib save_set.mode = &set->crtc->mode; 612235783Skib save_set.x = set->crtc->x; 613235783Skib save_set.y = set->crtc->y; 614235783Skib save_set.fb = set->crtc->fb; 615235783Skib 616235783Skib /* We should be able to check here if the fb has the same properties 617235783Skib * and then just flip_or_move it */ 618235783Skib if (set->crtc->fb != set->fb) { 619235783Skib /* If we have no fb then treat it as a full mode set */ 620235783Skib if (set->crtc->fb == NULL) { 621235783Skib DRM_DEBUG_KMS("crtc has no fb, full mode set\n"); 622235783Skib mode_changed = true; 623235783Skib } else if (set->fb == NULL) { 624235783Skib mode_changed = true; 625235783Skib } else 626235783Skib fb_changed = true; 627235783Skib } 628235783Skib 629235783Skib if (set->x != set->crtc->x || set->y != set->crtc->y) 630235783Skib fb_changed = true; 631235783Skib 632235783Skib if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { 633235783Skib DRM_DEBUG_KMS("modes are different, full mode set\n"); 634235783Skib drm_mode_debug_printmodeline(&set->crtc->mode); 635235783Skib drm_mode_debug_printmodeline(set->mode); 636235783Skib mode_changed = true; 637235783Skib } 638235783Skib 639235783Skib /* a) traverse passed in connector list and get encoders for them */ 640235783Skib count = 0; 641235783Skib list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 642235783Skib struct drm_connector_helper_funcs *connector_funcs = 643235783Skib connector->helper_private; 644235783Skib new_encoder = connector->encoder; 645235783Skib for (ro = 0; ro < set->num_connectors; ro++) { 646235783Skib if (set->connectors[ro] == connector) { 647235783Skib new_encoder = connector_funcs->best_encoder(connector); 648235783Skib /* if we can't get an encoder for a connector 649235783Skib we are setting now - then fail */ 650235783Skib if (new_encoder == NULL) 651235783Skib /* don't break so fail path works correct */ 652235783Skib fail = 1; 653235783Skib break; 654235783Skib } 655235783Skib } 656235783Skib 657235783Skib if (new_encoder != connector->encoder) { 658235783Skib DRM_DEBUG_KMS("encoder changed, full mode switch\n"); 659235783Skib mode_changed = true; 660235783Skib /* If the encoder is reused for another connector, then 661235783Skib * the appropriate crtc will be set later. 662235783Skib */ 663235783Skib if (connector->encoder) 664235783Skib connector->encoder->crtc = NULL; 665235783Skib connector->encoder = new_encoder; 666235783Skib } 667235783Skib } 668235783Skib 669235783Skib if (fail) { 670235783Skib ret = -EINVAL; 671235783Skib goto fail; 672235783Skib } 673235783Skib 674235783Skib count = 0; 675235783Skib list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 676235783Skib if (!connector->encoder) 677235783Skib continue; 678235783Skib 679235783Skib if (connector->encoder->crtc == set->crtc) 680235783Skib new_crtc = NULL; 681235783Skib else 682235783Skib new_crtc = connector->encoder->crtc; 683235783Skib 684235783Skib for (ro = 0; ro < set->num_connectors; ro++) { 685235783Skib if (set->connectors[ro] == connector) 686235783Skib new_crtc = set->crtc; 687235783Skib } 688235783Skib 689235783Skib /* Make sure the new CRTC will work with the encoder */ 690235783Skib if (new_crtc && 691235783Skib !drm_encoder_crtc_ok(connector->encoder, new_crtc)) { 692235783Skib ret = -EINVAL; 693235783Skib goto fail; 694235783Skib } 695235783Skib if (new_crtc != connector->encoder->crtc) { 696235783Skib DRM_DEBUG_KMS("crtc changed, full mode switch\n"); 697235783Skib mode_changed = true; 698235783Skib connector->encoder->crtc = new_crtc; 699235783Skib } 700235783Skib if (new_crtc) { 701235783Skib DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n", 702235783Skib connector->base.id, drm_get_connector_name(connector), 703235783Skib new_crtc->base.id); 704235783Skib } else { 705235783Skib DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n", 706235783Skib connector->base.id, drm_get_connector_name(connector)); 707235783Skib } 708235783Skib } 709235783Skib 710235783Skib /* mode_set_base is not a required function */ 711235783Skib if (fb_changed && !crtc_funcs->mode_set_base) 712235783Skib mode_changed = true; 713235783Skib 714235783Skib if (mode_changed) { 715235783Skib set->crtc->enabled = drm_helper_crtc_in_use(set->crtc); 716235783Skib if (set->crtc->enabled) { 717235783Skib DRM_DEBUG_KMS("attempting to set mode from" 718235783Skib " userspace\n"); 719235783Skib drm_mode_debug_printmodeline(set->mode); 720235783Skib old_fb = set->crtc->fb; 721235783Skib set->crtc->fb = set->fb; 722235783Skib if (!drm_crtc_helper_set_mode(set->crtc, set->mode, 723235783Skib set->x, set->y, 724235783Skib old_fb)) { 725235783Skib DRM_ERROR("failed to set mode on [CRTC:%d]\n", 726235783Skib set->crtc->base.id); 727235783Skib set->crtc->fb = old_fb; 728235783Skib ret = -EINVAL; 729235783Skib goto fail; 730235783Skib } 731235783Skib DRM_DEBUG_KMS("Setting connector DPMS state to on\n"); 732235783Skib for (i = 0; i < set->num_connectors; i++) { 733235783Skib DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id, 734235783Skib drm_get_connector_name(set->connectors[i])); 735235783Skib set->connectors[i]->dpms = DRM_MODE_DPMS_ON; 736235783Skib } 737235783Skib } 738235783Skib drm_helper_disable_unused_functions(dev); 739235783Skib } else if (fb_changed) { 740235783Skib set->crtc->x = set->x; 741235783Skib set->crtc->y = set->y; 742235783Skib 743235783Skib old_fb = set->crtc->fb; 744235783Skib if (set->crtc->fb != set->fb) 745235783Skib set->crtc->fb = set->fb; 746235783Skib ret = crtc_funcs->mode_set_base(set->crtc, 747235783Skib set->x, set->y, old_fb); 748235783Skib if (ret != 0) { 749235783Skib set->crtc->fb = old_fb; 750235783Skib goto fail; 751235783Skib } 752235783Skib } 753235783Skib 754235783Skib free(save_connectors, DRM_MEM_KMS); 755235783Skib free(save_encoders, DRM_MEM_KMS); 756235783Skib free(save_crtcs, DRM_MEM_KMS); 757235783Skib return 0; 758235783Skib 759235783Skibfail: 760235783Skib /* Restore all previous data. */ 761235783Skib count = 0; 762235783Skib list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 763235783Skib *crtc = save_crtcs[count++]; 764235783Skib } 765235783Skib 766235783Skib count = 0; 767235783Skib list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 768235783Skib *encoder = save_encoders[count++]; 769235783Skib } 770235783Skib 771235783Skib count = 0; 772235783Skib list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 773235783Skib *connector = save_connectors[count++]; 774235783Skib } 775235783Skib 776235783Skib /* Try to restore the config */ 777235783Skib if (mode_changed && 778235783Skib !drm_crtc_helper_set_mode(save_set.crtc, save_set.mode, save_set.x, 779235783Skib save_set.y, save_set.fb)) 780235783Skib DRM_ERROR("failed to restore config after modeset failure\n"); 781235783Skib 782235783Skib free(save_connectors, DRM_MEM_KMS); 783235783Skib free(save_encoders, DRM_MEM_KMS); 784235783Skib free(save_crtcs, DRM_MEM_KMS); 785235783Skib return ret; 786235783Skib} 787235783Skib 788235783Skibstatic int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder) 789235783Skib{ 790235783Skib int dpms = DRM_MODE_DPMS_OFF; 791235783Skib struct drm_connector *connector; 792235783Skib struct drm_device *dev = encoder->dev; 793235783Skib 794235783Skib list_for_each_entry(connector, &dev->mode_config.connector_list, head) 795235783Skib if (connector->encoder == encoder) 796235783Skib if (connector->dpms < dpms) 797235783Skib dpms = connector->dpms; 798235783Skib return dpms; 799235783Skib} 800235783Skib 801235783Skibstatic int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc) 802235783Skib{ 803235783Skib int dpms = DRM_MODE_DPMS_OFF; 804235783Skib struct drm_connector *connector; 805235783Skib struct drm_device *dev = crtc->dev; 806235783Skib 807235783Skib list_for_each_entry(connector, &dev->mode_config.connector_list, head) 808235783Skib if (connector->encoder && connector->encoder->crtc == crtc) 809235783Skib if (connector->dpms < dpms) 810235783Skib dpms = connector->dpms; 811235783Skib return dpms; 812235783Skib} 813235783Skib 814235783Skib/** 815235783Skib * drm_helper_connector_dpms 816235783Skib * @connector affected connector 817235783Skib * @mode DPMS mode 818235783Skib * 819235783Skib * Calls the low-level connector DPMS function, then 820235783Skib * calls appropriate encoder and crtc DPMS functions as well 821235783Skib */ 822235783Skibvoid drm_helper_connector_dpms(struct drm_connector *connector, int mode) 823235783Skib{ 824235783Skib struct drm_encoder *encoder = connector->encoder; 825235783Skib struct drm_crtc *crtc = encoder ? encoder->crtc : NULL; 826235783Skib int old_dpms; 827235783Skib 828235783Skib if (mode == connector->dpms) 829235783Skib return; 830235783Skib 831235783Skib old_dpms = connector->dpms; 832235783Skib connector->dpms = mode; 833235783Skib 834235783Skib /* from off to on, do crtc then encoder */ 835235783Skib if (mode < old_dpms) { 836235783Skib if (crtc) { 837235783Skib struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; 838235783Skib if (crtc_funcs->dpms) 839235783Skib (*crtc_funcs->dpms) (crtc, 840235783Skib drm_helper_choose_crtc_dpms(crtc)); 841235783Skib } 842235783Skib if (encoder) { 843235783Skib struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; 844235783Skib if (encoder_funcs->dpms) 845235783Skib (*encoder_funcs->dpms) (encoder, 846235783Skib drm_helper_choose_encoder_dpms(encoder)); 847235783Skib } 848235783Skib } 849235783Skib 850235783Skib /* from on to off, do encoder then crtc */ 851235783Skib if (mode > old_dpms) { 852235783Skib if (encoder) { 853235783Skib struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; 854235783Skib if (encoder_funcs->dpms) 855235783Skib (*encoder_funcs->dpms) (encoder, 856235783Skib drm_helper_choose_encoder_dpms(encoder)); 857235783Skib } 858235783Skib if (crtc) { 859235783Skib struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; 860235783Skib if (crtc_funcs->dpms) 861235783Skib (*crtc_funcs->dpms) (crtc, 862235783Skib drm_helper_choose_crtc_dpms(crtc)); 863235783Skib } 864235783Skib } 865235783Skib 866235783Skib return; 867235783Skib} 868235783Skib 869235783Skibint drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, 870235783Skib struct drm_mode_fb_cmd2 *mode_cmd) 871235783Skib{ 872235783Skib int i; 873235783Skib 874235783Skib fb->width = mode_cmd->width; 875235783Skib fb->height = mode_cmd->height; 876235783Skib for (i = 0; i < 4; i++) { 877235783Skib fb->pitches[i] = mode_cmd->pitches[i]; 878235783Skib fb->offsets[i] = mode_cmd->offsets[i]; 879235783Skib } 880235783Skib drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth, 881235783Skib &fb->bits_per_pixel); 882235783Skib fb->pixel_format = mode_cmd->pixel_format; 883235783Skib 884235783Skib return 0; 885235783Skib} 886235783Skib 887235783Skibint drm_helper_resume_force_mode(struct drm_device *dev) 888235783Skib{ 889235783Skib struct drm_crtc *crtc; 890235783Skib struct drm_encoder *encoder; 891235783Skib struct drm_encoder_helper_funcs *encoder_funcs; 892235783Skib struct drm_crtc_helper_funcs *crtc_funcs; 893235783Skib int ret; 894235783Skib 895235783Skib list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 896235783Skib 897235783Skib if (!crtc->enabled) 898235783Skib continue; 899235783Skib 900235783Skib ret = drm_crtc_helper_set_mode(crtc, &crtc->mode, 901235783Skib crtc->x, crtc->y, crtc->fb); 902235783Skib 903235783Skib if (!ret) 904235783Skib DRM_ERROR("failed to set mode on crtc %p\n", crtc); 905235783Skib 906235783Skib /* Turn off outputs that were already powered off */ 907235783Skib if (drm_helper_choose_crtc_dpms(crtc)) { 908235783Skib list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 909235783Skib 910235783Skib if(encoder->crtc != crtc) 911235783Skib continue; 912235783Skib 913235783Skib encoder_funcs = encoder->helper_private; 914235783Skib if (encoder_funcs->dpms) 915235783Skib (*encoder_funcs->dpms) (encoder, 916235783Skib drm_helper_choose_encoder_dpms(encoder)); 917235783Skib } 918235783Skib 919235783Skib crtc_funcs = crtc->helper_private; 920235783Skib if (crtc_funcs->dpms) 921235783Skib (*crtc_funcs->dpms) (crtc, 922235783Skib drm_helper_choose_crtc_dpms(crtc)); 923235783Skib } 924235783Skib } 925235783Skib /* disable the unused connectors while restoring the modesetting */ 926235783Skib drm_helper_disable_unused_functions(dev); 927235783Skib return 0; 928235783Skib} 929235783Skib 930235783Skib#define DRM_OUTPUT_POLL_PERIOD (10 * hz) 931235783Skibstatic void output_poll_execute(void *ctx, int pending) 932235783Skib{ 933235783Skib struct drm_device *dev; 934235783Skib struct drm_connector *connector; 935235783Skib enum drm_connector_status old_status; 936235783Skib bool repoll = false, changed = false; 937235783Skib 938235783Skib if (!drm_kms_helper_poll) 939235783Skib return; 940235783Skib 941235783Skib dev = ctx; 942235783Skib 943235783Skib sx_xlock(&dev->mode_config.mutex); 944235783Skib list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 945235783Skib 946235783Skib /* if this is HPD or polled don't check it - 947235783Skib TV out for instance */ 948235783Skib if (!connector->polled) 949235783Skib continue; 950235783Skib 951235783Skib else if (connector->polled & (DRM_CONNECTOR_POLL_CONNECT | 952235783Skib DRM_CONNECTOR_POLL_DISCONNECT)) 953235783Skib repoll = true; 954235783Skib 955235783Skib old_status = connector->status; 956235783Skib /* if we are connected and don't want to poll for disconnect 957235783Skib skip it */ 958235783Skib if (old_status == connector_status_connected && 959235783Skib !(connector->polled & DRM_CONNECTOR_POLL_DISCONNECT) && 960235783Skib !(connector->polled & DRM_CONNECTOR_POLL_HPD)) 961235783Skib continue; 962235783Skib 963235783Skib connector->status = connector->funcs->detect(connector, false); 964235783Skib DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %d to %d\n", 965235783Skib connector->base.id, 966235783Skib drm_get_connector_name(connector), 967235783Skib old_status, connector->status); 968235783Skib if (old_status != connector->status) 969235783Skib changed = true; 970235783Skib } 971235783Skib 972235783Skib sx_xunlock(&dev->mode_config.mutex); 973235783Skib 974235783Skib if (changed) { 975235783Skib#if 0 976235783Skib /* send a uevent + call fbdev */ 977235783Skib drm_sysfs_hotplug_event(dev); 978235783Skib#endif 979235783Skib if (dev->mode_config.funcs->output_poll_changed) 980235783Skib dev->mode_config.funcs->output_poll_changed(dev); 981235783Skib } 982235783Skib 983235783Skib if (repoll) { 984235783Skib taskqueue_enqueue_timeout(taskqueue_thread, 985235783Skib &dev->mode_config.output_poll_task, 986235783Skib DRM_OUTPUT_POLL_PERIOD); 987235783Skib } 988235783Skib} 989235783Skib 990235783Skibvoid drm_kms_helper_poll_disable(struct drm_device *dev) 991235783Skib{ 992235783Skib if (!dev->mode_config.poll_enabled) 993235783Skib return; 994235783Skib taskqueue_cancel_timeout(taskqueue_thread, 995235783Skib &dev->mode_config.output_poll_task, NULL); 996235783Skib} 997235783Skib 998235783Skibvoid drm_kms_helper_poll_enable(struct drm_device *dev) 999235783Skib{ 1000235783Skib bool poll = false; 1001235783Skib struct drm_connector *connector; 1002235783Skib 1003235783Skib if (!dev->mode_config.poll_enabled || !drm_kms_helper_poll) 1004235783Skib return; 1005235783Skib 1006235783Skib list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 1007235783Skib if (connector->polled) 1008235783Skib poll = true; 1009235783Skib } 1010235783Skib 1011235783Skib if (poll) { 1012235783Skib taskqueue_enqueue_timeout(taskqueue_thread, 1013235783Skib &dev->mode_config.output_poll_task, DRM_OUTPUT_POLL_PERIOD); 1014235783Skib } 1015235783Skib} 1016235783Skib 1017235783Skibvoid drm_kms_helper_poll_init(struct drm_device *dev) 1018235783Skib{ 1019235783Skib 1020235783Skib TIMEOUT_TASK_INIT(taskqueue_thread, &dev->mode_config.output_poll_task, 1021235783Skib 0, output_poll_execute, dev); 1022235783Skib dev->mode_config.poll_enabled = true; 1023235783Skib 1024235783Skib drm_kms_helper_poll_enable(dev); 1025235783Skib} 1026235783Skib 1027235783Skibvoid drm_kms_helper_poll_fini(struct drm_device *dev) 1028235783Skib{ 1029235783Skib drm_kms_helper_poll_disable(dev); 1030235783Skib} 1031235783Skib 1032235783Skibvoid drm_helper_hpd_irq_event(struct drm_device *dev) 1033235783Skib{ 1034235783Skib if (!dev->mode_config.poll_enabled) 1035235783Skib return; 1036235783Skib 1037235783Skib /* kill timer and schedule immediate execution, this doesn't block */ 1038235783Skib taskqueue_cancel_timeout(taskqueue_thread, 1039235783Skib &dev->mode_config.output_poll_task, NULL); 1040235783Skib if (drm_kms_helper_poll) 1041235783Skib taskqueue_enqueue_timeout(taskqueue_thread, 1042235783Skib &dev->mode_config.output_poll_task, 0); 1043235783Skib} 1044