1235783Skib/* 2235783Skib * Copyright (c) 2006-2009 Red Hat Inc. 3235783Skib * Copyright (c) 2006-2008 Intel Corporation 4235783Skib * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> 5235783Skib * 6235783Skib * DRM framebuffer helper functions 7235783Skib * 8235783Skib * Permission to use, copy, modify, distribute, and sell this software and its 9235783Skib * documentation for any purpose is hereby granted without fee, provided that 10235783Skib * the above copyright notice appear in all copies and that both that copyright 11235783Skib * notice and this permission notice appear in supporting documentation, and 12235783Skib * that the name of the copyright holders not be used in advertising or 13235783Skib * publicity pertaining to distribution of the software without specific, 14235783Skib * written prior permission. The copyright holders make no representations 15235783Skib * about the suitability of this software for any purpose. It is provided "as 16235783Skib * is" without express or implied warranty. 17235783Skib * 18235783Skib * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 19235783Skib * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 20235783Skib * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 21235783Skib * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 22235783Skib * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 23235783Skib * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 24235783Skib * OF THIS SOFTWARE. 25235783Skib * 26235783Skib * Authors: 27235783Skib * Dave Airlie <airlied@linux.ie> 28235783Skib * Jesse Barnes <jesse.barnes@intel.com> 29235783Skib */ 30235783Skib 31235783Skib#include <sys/cdefs.h> 32235783Skib__FBSDID("$FreeBSD$"); 33235783Skib 34235783Skib#include <dev/drm2/drmP.h> 35235783Skib#include <dev/drm2/drm_crtc.h> 36235783Skib#include <dev/drm2/drm_fb_helper.h> 37235783Skib#include <dev/drm2/drm_crtc_helper.h> 38235783Skib 39271769Sdumbbell 40271769Sdumbbell#include <sys/kdb.h> 41271769Sdumbbell 42262861Sjhbstruct vt_kms_softc { 43262861Sjhb struct drm_fb_helper *fb_helper; 44262861Sjhb struct task fb_mode_task; 45262861Sjhb}; 46262861Sjhb 47262861Sjhbstatic fb_enter_t vt_kms_postswitch; 48262861Sjhbstatic void vt_restore_fbdev_mode(void *, int); 49262861Sjhb 50262861Sjhb/* Call restore out of vt(9) locks. */ 51262861Sjhbstatic void 52262861Sjhbvt_restore_fbdev_mode(void *arg, int pending) 53262861Sjhb{ 54262861Sjhb struct drm_fb_helper *fb_helper; 55262861Sjhb struct vt_kms_softc *sc; 56262861Sjhb 57262861Sjhb sc = (struct vt_kms_softc *)arg; 58262861Sjhb fb_helper = sc->fb_helper; 59262861Sjhb sx_xlock(&fb_helper->dev->mode_config.mutex); 60262861Sjhb drm_fb_helper_restore_fbdev_mode(fb_helper); 61262861Sjhb sx_xunlock(&fb_helper->dev->mode_config.mutex); 62262861Sjhb} 63262861Sjhb 64262861Sjhbstatic int 65262861Sjhbvt_kms_postswitch(void *arg) 66262861Sjhb{ 67262861Sjhb struct vt_kms_softc *sc; 68262861Sjhb 69262861Sjhb sc = (struct vt_kms_softc *)arg; 70262861Sjhb 71271769Sdumbbell if (!kdb_active && panicstr == NULL) 72271769Sdumbbell taskqueue_enqueue_fast(taskqueue_thread, &sc->fb_mode_task); 73271769Sdumbbell else 74271769Sdumbbell drm_fb_helper_restore_fbdev_mode(sc->fb_helper); 75271769Sdumbbell 76262861Sjhb return (0); 77262861Sjhb} 78262861Sjhb 79235783Skibstatic DRM_LIST_HEAD(kernel_fb_helper_list); 80235783Skib 81235783Skib/* simple single crtc case helper function */ 82235783Skibint drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) 83235783Skib{ 84235783Skib struct drm_device *dev = fb_helper->dev; 85235783Skib struct drm_connector *connector; 86235783Skib 87235783Skib list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 88235783Skib struct drm_fb_helper_connector *fb_helper_connector; 89235783Skib 90235783Skib fb_helper_connector = malloc( 91235783Skib sizeof(struct drm_fb_helper_connector), DRM_MEM_KMS, 92235783Skib M_WAITOK | M_ZERO); 93235783Skib 94235783Skib fb_helper_connector->connector = connector; 95235783Skib fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector; 96235783Skib } 97235783Skib return 0; 98235783Skib} 99235783Skib 100235783Skibconst char *fb_mode_option; 101235783Skib 102235783Skib/** 103235783Skib * drm_fb_helper_connector_parse_command_line - parse command line for connector 104235783Skib * @connector - connector to parse line for 105235783Skib * @mode_option - per connector mode option 106235783Skib * 107235783Skib * This parses the connector specific then generic command lines for 108235783Skib * modes and options to configure the connector. 109235783Skib * 110235783Skib * This uses the same parameters as the fb modedb.c, except for extra 111235783Skib * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd] 112235783Skib * 113235783Skib * enable/enable Digital/disable bit at the end 114235783Skib */ 115235783Skibstatic bool drm_fb_helper_connector_parse_command_line(struct drm_fb_helper_connector *fb_helper_conn, 116235783Skib const char *mode_option) 117235783Skib{ 118235783Skib const char *name; 119235783Skib unsigned int namelen; 120235783Skib int res_specified = 0, bpp_specified = 0, refresh_specified = 0; 121235783Skib unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0; 122235783Skib int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0; 123235783Skib int i; 124235783Skib enum drm_connector_force force = DRM_FORCE_UNSPECIFIED; 125235783Skib struct drm_fb_helper_cmdline_mode *cmdline_mode; 126235783Skib struct drm_connector *connector; 127235783Skib 128235783Skib if (!fb_helper_conn) 129235783Skib return false; 130235783Skib connector = fb_helper_conn->connector; 131235783Skib 132235783Skib cmdline_mode = &fb_helper_conn->cmdline_mode; 133235783Skib if (!mode_option) 134235783Skib mode_option = fb_mode_option; 135235783Skib 136235783Skib if (!mode_option) { 137235783Skib cmdline_mode->specified = false; 138235783Skib return false; 139235783Skib } 140235783Skib 141235783Skib name = mode_option; 142235783Skib namelen = strlen(name); 143235783Skib for (i = namelen-1; i >= 0; i--) { 144235783Skib switch (name[i]) { 145235783Skib case '@': 146235783Skib namelen = i; 147235783Skib if (!refresh_specified && !bpp_specified && 148235783Skib !yres_specified) { 149235783Skib refresh = strtol(&name[i+1], NULL, 10); 150235783Skib refresh_specified = 1; 151235783Skib if (cvt || rb) 152235783Skib cvt = 0; 153235783Skib } else 154235783Skib goto done; 155235783Skib break; 156235783Skib case '-': 157235783Skib namelen = i; 158235783Skib if (!bpp_specified && !yres_specified) { 159235783Skib bpp = strtol(&name[i+1], NULL, 10); 160235783Skib bpp_specified = 1; 161235783Skib if (cvt || rb) 162235783Skib cvt = 0; 163235783Skib } else 164235783Skib goto done; 165235783Skib break; 166235783Skib case 'x': 167235783Skib if (!yres_specified) { 168235783Skib yres = strtol(&name[i+1], NULL, 10); 169235783Skib yres_specified = 1; 170235783Skib } else 171235783Skib goto done; 172235783Skib case '0' ... '9': 173235783Skib break; 174235783Skib case 'M': 175235783Skib if (!yres_specified) 176235783Skib cvt = 1; 177235783Skib break; 178235783Skib case 'R': 179235783Skib if (cvt) 180235783Skib rb = 1; 181235783Skib break; 182235783Skib case 'm': 183235783Skib if (!cvt) 184235783Skib margins = 1; 185235783Skib break; 186235783Skib case 'i': 187235783Skib if (!cvt) 188235783Skib interlace = 1; 189235783Skib break; 190235783Skib case 'e': 191235783Skib force = DRM_FORCE_ON; 192235783Skib break; 193235783Skib case 'D': 194235783Skib if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) && 195235783Skib (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB)) 196235783Skib force = DRM_FORCE_ON; 197235783Skib else 198235783Skib force = DRM_FORCE_ON_DIGITAL; 199235783Skib break; 200235783Skib case 'd': 201235783Skib force = DRM_FORCE_OFF; 202235783Skib break; 203235783Skib default: 204235783Skib goto done; 205235783Skib } 206235783Skib } 207235783Skib if (i < 0 && yres_specified) { 208235783Skib xres = strtol(name, NULL, 10); 209235783Skib res_specified = 1; 210235783Skib } 211235783Skibdone: 212235783Skib 213235783Skib DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n", 214235783Skib drm_get_connector_name(connector), xres, yres, 215235783Skib (refresh) ? refresh : 60, (rb) ? " reduced blanking" : 216235783Skib "", (margins) ? " with margins" : "", (interlace) ? 217235783Skib " interlaced" : ""); 218235783Skib 219235783Skib if (force) { 220235783Skib const char *s; 221235783Skib switch (force) { 222235783Skib case DRM_FORCE_OFF: s = "OFF"; break; 223235783Skib case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break; 224235783Skib default: 225235783Skib case DRM_FORCE_ON: s = "ON"; break; 226235783Skib } 227235783Skib 228235783Skib DRM_INFO("forcing %s connector %s\n", 229235783Skib drm_get_connector_name(connector), s); 230235783Skib connector->force = force; 231235783Skib } 232235783Skib 233235783Skib if (res_specified) { 234235783Skib cmdline_mode->specified = true; 235235783Skib cmdline_mode->xres = xres; 236235783Skib cmdline_mode->yres = yres; 237235783Skib } 238235783Skib 239235783Skib if (refresh_specified) { 240235783Skib cmdline_mode->refresh_specified = true; 241235783Skib cmdline_mode->refresh = refresh; 242235783Skib } 243235783Skib 244235783Skib if (bpp_specified) { 245235783Skib cmdline_mode->bpp_specified = true; 246235783Skib cmdline_mode->bpp = bpp; 247235783Skib } 248235783Skib cmdline_mode->rb = rb ? true : false; 249235783Skib cmdline_mode->cvt = cvt ? true : false; 250235783Skib cmdline_mode->interlace = interlace ? true : false; 251235783Skib 252235783Skib return true; 253235783Skib} 254235783Skib 255235783Skibstatic int 256235783Skibfb_get_options(const char *connector_name, char **option) 257235783Skib{ 258235783Skib 259262861Sjhb /* 260262861Sjhb * TODO: store mode options pointer in ${option} for connector with 261262861Sjhb * name ${connector_name} 262262861Sjhb */ 263235783Skib return (1); 264235783Skib} 265235783Skib 266235783Skibstatic int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper) 267235783Skib{ 268235783Skib struct drm_fb_helper_connector *fb_helper_conn; 269235783Skib int i; 270235783Skib 271235783Skib for (i = 0; i < fb_helper->connector_count; i++) { 272235783Skib char *option = NULL; 273235783Skib 274235783Skib fb_helper_conn = fb_helper->connector_info[i]; 275235783Skib 276235783Skib /* do something on return - turn off connector maybe */ 277235783Skib if (fb_get_options(drm_get_connector_name(fb_helper_conn->connector), &option)) 278235783Skib continue; 279235783Skib 280235783Skib drm_fb_helper_connector_parse_command_line(fb_helper_conn, option); 281235783Skib } 282235783Skib return 0; 283235783Skib} 284235783Skib 285235783Skib#if 0 286235783Skibstatic void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_helper *helper) 287235783Skib{ 288235783Skib uint16_t *r_base, *g_base, *b_base; 289235783Skib int i; 290235783Skib 291235783Skib r_base = crtc->gamma_store; 292235783Skib g_base = r_base + crtc->gamma_size; 293235783Skib b_base = g_base + crtc->gamma_size; 294235783Skib 295235783Skib for (i = 0; i < crtc->gamma_size; i++) 296235783Skib helper->funcs->gamma_get(crtc, &r_base[i], &g_base[i], &b_base[i], i); 297235783Skib} 298235783Skib 299235783Skibstatic void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) 300235783Skib{ 301235783Skib uint16_t *r_base, *g_base, *b_base; 302235783Skib 303235783Skib r_base = crtc->gamma_store; 304235783Skib g_base = r_base + crtc->gamma_size; 305235783Skib b_base = g_base + crtc->gamma_size; 306235783Skib 307235783Skib crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); 308235783Skib} 309235783Skib#endif 310235783Skib 311235783Skib#if 0 312235783Skibint drm_fb_helper_debug_enter(struct fb_info *info) 313235783Skib{ 314235783Skib struct drm_fb_helper *helper = info->par; 315235783Skib struct drm_crtc_helper_funcs *funcs; 316235783Skib int i; 317235783Skib 318235783Skib if (list_empty(&kernel_fb_helper_list)) 319235783Skib return false; 320235783Skib 321235783Skib list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { 322235783Skib for (i = 0; i < helper->crtc_count; i++) { 323235783Skib struct drm_mode_set *mode_set = 324235783Skib &helper->crtc_info[i].mode_set; 325235783Skib 326235783Skib if (!mode_set->crtc->enabled) 327235783Skib continue; 328235783Skib 329235783Skib funcs = mode_set->crtc->helper_private; 330235783Skib drm_fb_helper_save_lut_atomic(mode_set->crtc, helper); 331235783Skib funcs->mode_set_base_atomic(mode_set->crtc, 332235783Skib mode_set->fb, 333235783Skib mode_set->x, 334235783Skib mode_set->y, 335235783Skib ENTER_ATOMIC_MODE_SET); 336235783Skib } 337235783Skib } 338235783Skib 339235783Skib return 0; 340235783Skib} 341235783Skib#endif 342235783Skib 343235783Skib#if 0 344235783Skib/* Find the real fb for a given fb helper CRTC */ 345235783Skibstatic struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc) 346235783Skib{ 347235783Skib struct drm_device *dev = crtc->dev; 348235783Skib struct drm_crtc *c; 349235783Skib 350235783Skib list_for_each_entry(c, &dev->mode_config.crtc_list, head) { 351235783Skib if (crtc->base.id == c->base.id) 352235783Skib return c->fb; 353235783Skib } 354235783Skib 355235783Skib return NULL; 356235783Skib} 357235783Skib#endif 358235783Skib 359235783Skib#if 0 360235783Skibint drm_fb_helper_debug_leave(struct fb_info *info) 361235783Skib{ 362235783Skib struct drm_fb_helper *helper = info->par; 363235783Skib struct drm_crtc *crtc; 364235783Skib struct drm_crtc_helper_funcs *funcs; 365235783Skib struct drm_framebuffer *fb; 366235783Skib int i; 367235783Skib 368235783Skib for (i = 0; i < helper->crtc_count; i++) { 369235783Skib struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set; 370235783Skib crtc = mode_set->crtc; 371235783Skib funcs = crtc->helper_private; 372235783Skib fb = drm_mode_config_fb(crtc); 373235783Skib 374235783Skib if (!crtc->enabled) 375235783Skib continue; 376235783Skib 377235783Skib if (!fb) { 378235783Skib DRM_ERROR("no fb to restore??\n"); 379235783Skib continue; 380235783Skib } 381235783Skib 382235783Skib drm_fb_helper_restore_lut_atomic(mode_set->crtc); 383235783Skib funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x, 384235783Skib crtc->y, LEAVE_ATOMIC_MODE_SET); 385235783Skib } 386235783Skib 387235783Skib return 0; 388235783Skib} 389235783Skib#endif 390235783Skib 391235783Skibbool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) 392235783Skib{ 393235783Skib bool error = false; 394235783Skib int i, ret; 395235783Skib for (i = 0; i < fb_helper->crtc_count; i++) { 396235783Skib struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; 397235783Skib ret = drm_crtc_helper_set_config(mode_set); 398235783Skib if (ret) 399235783Skib error = true; 400235783Skib } 401235783Skib return error; 402235783Skib} 403235783Skib 404235783Skib#if 0 405235783Skibbool drm_fb_helper_force_kernel_mode(void) 406235783Skib{ 407235783Skib bool ret, error = false; 408235783Skib struct drm_fb_helper *helper; 409235783Skib 410235783Skib if (list_empty(&kernel_fb_helper_list)) 411235783Skib return false; 412235783Skib 413235783Skib list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { 414235783Skib if (helper->dev->switch_power_state == DRM_SWITCH_POWER_OFF) 415235783Skib continue; 416235783Skib 417235783Skib ret = drm_fb_helper_restore_fbdev_mode(helper); 418235783Skib if (ret) 419235783Skib error = true; 420235783Skib } 421235783Skib return error; 422235783Skib} 423235783Skib#endif 424235783Skib 425235783Skib#if 0 426235783Skibint drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed, 427235783Skib void *panic_str) 428235783Skib{ 429235783Skib printf("panic occurred, switching back to text console\n"); 430235783Skib return drm_fb_helper_force_kernel_mode(); 431235783Skib return 0; 432235783Skib} 433235783Skib 434235783Skibstatic struct notifier_block paniced = { 435235783Skib .notifier_call = drm_fb_helper_panic, 436235783Skib}; 437235783Skib 438235783Skib/** 439235783Skib * drm_fb_helper_restore - restore the framebuffer console (kernel) config 440235783Skib * 441235783Skib * Restore's the kernel's fbcon mode, used for lastclose & panic paths. 442235783Skib */ 443235783Skibvoid drm_fb_helper_restore(void) 444235783Skib{ 445235783Skib bool ret; 446235783Skib ret = drm_fb_helper_force_kernel_mode(); 447235783Skib if (ret == true) 448235783Skib DRM_ERROR("Failed to restore crtc configuration\n"); 449235783Skib} 450235783Skib 451235783Skib#ifdef CONFIG_MAGIC_SYSRQ 452235783Skibstatic void drm_fb_helper_restore_work_fn(struct work_struct *ignored) 453235783Skib{ 454235783Skib drm_fb_helper_restore(); 455235783Skib} 456235783Skibstatic DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn); 457235783Skib 458235783Skibstatic void drm_fb_helper_sysrq(int dummy1) 459235783Skib{ 460235783Skib schedule_work(&drm_fb_helper_restore_work); 461235783Skib} 462235783Skib 463235783Skibstatic struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { 464235783Skib .handler = drm_fb_helper_sysrq, 465235783Skib .help_msg = "force-fb(V)", 466235783Skib .action_msg = "Restore framebuffer console", 467235783Skib}; 468235783Skib#else 469235783Skibstatic struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { }; 470235783Skib#endif 471235783Skib#endif 472235783Skib 473235783Skib#if 0 474235783Skibstatic void drm_fb_helper_on(struct fb_info *info) 475235783Skib{ 476235783Skib struct drm_fb_helper *fb_helper = info->par; 477235783Skib struct drm_device *dev = fb_helper->dev; 478235783Skib struct drm_crtc *crtc; 479235783Skib struct drm_crtc_helper_funcs *crtc_funcs; 480235783Skib struct drm_connector *connector; 481235783Skib struct drm_encoder *encoder; 482235783Skib int i, j; 483235783Skib 484235783Skib /* 485235783Skib * For each CRTC in this fb, turn the crtc on then, 486235783Skib * find all associated encoders and turn them on. 487235783Skib */ 488235783Skib sx_xlock(&dev->mode_config.mutex); 489235783Skib for (i = 0; i < fb_helper->crtc_count; i++) { 490235783Skib crtc = fb_helper->crtc_info[i].mode_set.crtc; 491235783Skib crtc_funcs = crtc->helper_private; 492235783Skib 493235783Skib if (!crtc->enabled) 494235783Skib continue; 495235783Skib 496235783Skib crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); 497235783Skib 498235783Skib /* Walk the connectors & encoders on this fb turning them on */ 499235783Skib for (j = 0; j < fb_helper->connector_count; j++) { 500235783Skib connector = fb_helper->connector_info[j]->connector; 501235783Skib connector->dpms = DRM_MODE_DPMS_ON; 502235783Skib drm_connector_property_set_value(connector, 503235783Skib dev->mode_config.dpms_property, 504235783Skib DRM_MODE_DPMS_ON); 505235783Skib } 506235783Skib /* Found a CRTC on this fb, now find encoders */ 507235783Skib list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 508235783Skib if (encoder->crtc == crtc) { 509235783Skib struct drm_encoder_helper_funcs *encoder_funcs; 510235783Skib 511235783Skib encoder_funcs = encoder->helper_private; 512235783Skib encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); 513235783Skib } 514235783Skib } 515235783Skib } 516235783Skib sx_xunlock(&dev->mode_config.mutex); 517235783Skib} 518235783Skib#endif 519235783Skib 520235783Skib#if 0 521235783Skibstatic void drm_fb_helper_off(struct fb_info *info, int dpms_mode) 522235783Skib{ 523235783Skib struct drm_fb_helper *fb_helper = info->par; 524235783Skib struct drm_device *dev = fb_helper->dev; 525235783Skib struct drm_crtc *crtc; 526235783Skib struct drm_crtc_helper_funcs *crtc_funcs; 527235783Skib struct drm_connector *connector; 528235783Skib struct drm_encoder *encoder; 529235783Skib int i, j; 530235783Skib 531235783Skib /* 532235783Skib * For each CRTC in this fb, find all associated encoders 533235783Skib * and turn them off, then turn off the CRTC. 534235783Skib */ 535235783Skib sx_xlock(&dev->mode_config.mutex); 536235783Skib for (i = 0; i < fb_helper->crtc_count; i++) { 537235783Skib crtc = fb_helper->crtc_info[i].mode_set.crtc; 538235783Skib crtc_funcs = crtc->helper_private; 539235783Skib 540235783Skib if (!crtc->enabled) 541235783Skib continue; 542235783Skib 543235783Skib /* Walk the connectors on this fb and mark them off */ 544235783Skib for (j = 0; j < fb_helper->connector_count; j++) { 545235783Skib connector = fb_helper->connector_info[j]->connector; 546235783Skib connector->dpms = dpms_mode; 547235783Skib drm_connector_property_set_value(connector, 548235783Skib dev->mode_config.dpms_property, 549235783Skib dpms_mode); 550235783Skib } 551235783Skib /* Found a CRTC on this fb, now find encoders */ 552235783Skib list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 553235783Skib if (encoder->crtc == crtc) { 554235783Skib struct drm_encoder_helper_funcs *encoder_funcs; 555235783Skib 556235783Skib encoder_funcs = encoder->helper_private; 557235783Skib encoder_funcs->dpms(encoder, dpms_mode); 558235783Skib } 559235783Skib } 560235783Skib crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); 561235783Skib } 562235783Skib sx_xunlock(&dev->mode_config.mutex); 563235783Skib} 564235783Skib#endif 565235783Skib 566235783Skib#if 0 567235783Skibint drm_fb_helper_blank(int blank, struct fb_info *info) 568235783Skib{ 569235783Skib switch (blank) { 570235783Skib /* Display: On; HSync: On, VSync: On */ 571235783Skib case FB_BLANK_UNBLANK: 572235783Skib drm_fb_helper_on(info); 573235783Skib break; 574235783Skib /* Display: Off; HSync: On, VSync: On */ 575235783Skib case FB_BLANK_NORMAL: 576235783Skib drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY); 577235783Skib break; 578235783Skib /* Display: Off; HSync: Off, VSync: On */ 579235783Skib case FB_BLANK_HSYNC_SUSPEND: 580235783Skib drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY); 581235783Skib break; 582235783Skib /* Display: Off; HSync: On, VSync: Off */ 583235783Skib case FB_BLANK_VSYNC_SUSPEND: 584235783Skib drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND); 585235783Skib break; 586235783Skib /* Display: Off; HSync: Off, VSync: Off */ 587235783Skib case FB_BLANK_POWERDOWN: 588235783Skib drm_fb_helper_off(info, DRM_MODE_DPMS_OFF); 589235783Skib break; 590235783Skib } 591235783Skib return 0; 592235783Skib} 593235783Skib#endif 594235783Skib 595235783Skibstatic void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) 596235783Skib{ 597235783Skib int i; 598235783Skib 599235783Skib for (i = 0; i < helper->connector_count; i++) 600235783Skib free(helper->connector_info[i], DRM_MEM_KMS); 601235783Skib free(helper->connector_info, DRM_MEM_KMS); 602248061Sdumbbell for (i = 0; i < helper->crtc_count; i++) { 603235783Skib free(helper->crtc_info[i].mode_set.connectors, DRM_MEM_KMS); 604248061Sdumbbell if (helper->crtc_info[i].mode_set.mode) 605248061Sdumbbell drm_mode_destroy(helper->dev, helper->crtc_info[i].mode_set.mode); 606248061Sdumbbell } 607235783Skib free(helper->crtc_info, DRM_MEM_KMS); 608235783Skib} 609235783Skib 610235783Skibint drm_fb_helper_init(struct drm_device *dev, 611235783Skib struct drm_fb_helper *fb_helper, 612235783Skib int crtc_count, int max_conn_count) 613235783Skib{ 614235783Skib struct drm_crtc *crtc; 615235783Skib int i; 616235783Skib 617235783Skib fb_helper->dev = dev; 618235783Skib 619235783Skib INIT_LIST_HEAD(&fb_helper->kernel_fb_list); 620235783Skib 621235783Skib fb_helper->crtc_info = malloc(crtc_count * 622235783Skib sizeof(struct drm_fb_helper_crtc), DRM_MEM_KMS, M_WAITOK | M_ZERO); 623235783Skib 624235783Skib fb_helper->crtc_count = crtc_count; 625235783Skib fb_helper->connector_info = malloc(dev->mode_config.num_connector * 626235783Skib sizeof(struct drm_fb_helper_connector *), DRM_MEM_KMS, 627235783Skib M_WAITOK | M_ZERO); 628235783Skib fb_helper->connector_count = 0; 629235783Skib 630235783Skib for (i = 0; i < crtc_count; i++) { 631235783Skib fb_helper->crtc_info[i].mode_set.connectors = 632235783Skib malloc(max_conn_count * sizeof(struct drm_connector *), 633235783Skib DRM_MEM_KMS, M_WAITOK | M_ZERO); 634235783Skib 635235783Skib fb_helper->crtc_info[i].mode_set.num_connectors = 0; 636235783Skib } 637235783Skib 638235783Skib i = 0; 639235783Skib list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 640235783Skib fb_helper->crtc_info[i].crtc_id = crtc->base.id; 641235783Skib fb_helper->crtc_info[i].mode_set.crtc = crtc; 642235783Skib i++; 643235783Skib } 644235783Skib fb_helper->conn_limit = max_conn_count; 645235783Skib return 0; 646235783Skib} 647235783Skib 648235783Skibvoid drm_fb_helper_fini(struct drm_fb_helper *fb_helper) 649235783Skib{ 650235783Skib if (!list_empty(&fb_helper->kernel_fb_list)) { 651235783Skib list_del(&fb_helper->kernel_fb_list); 652235783Skib if (list_empty(&kernel_fb_helper_list)) { 653235783Skib#if 0 654235783Skib printk(KERN_INFO "drm: unregistered panic notifier\n"); 655235783Skib atomic_notifier_chain_unregister(&panic_notifier_list, 656235783Skib &paniced); 657235783Skib unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); 658235783Skib#endif 659235783Skib } 660235783Skib } 661235783Skib 662235783Skib drm_fb_helper_crtc_free(fb_helper); 663235783Skib 664235783Skib} 665235783Skib 666235783Skib#if 0 667235783Skibstatic int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, 668235783Skib u16 blue, u16 regno, struct fb_info *info) 669235783Skib{ 670235783Skib struct drm_fb_helper *fb_helper = info->par; 671235783Skib struct drm_framebuffer *fb = fb_helper->fb; 672235783Skib int pindex; 673235783Skib 674235783Skib if (info->fix.visual == FB_VISUAL_trueCOLOR) { 675235783Skib u32 *palette; 676235783Skib u32 value; 677235783Skib /* place color in psuedopalette */ 678235783Skib if (regno > 16) 679235783Skib return -EINVAL; 680235783Skib palette = (u32 *)info->pseudo_palette; 681235783Skib red >>= (16 - info->var.red.length); 682235783Skib green >>= (16 - info->var.green.length); 683235783Skib blue >>= (16 - info->var.blue.length); 684235783Skib value = (red << info->var.red.offset) | 685235783Skib (green << info->var.green.offset) | 686235783Skib (blue << info->var.blue.offset); 687235783Skib if (info->var.transp.length > 0) { 688235783Skib u32 mask = (1 << info->var.transp.length) - 1; 689235783Skib mask <<= info->var.transp.offset; 690235783Skib value |= mask; 691235783Skib } 692235783Skib palette[regno] = value; 693235783Skib return 0; 694235783Skib } 695235783Skib 696235783Skib pindex = regno; 697235783Skib 698235783Skib if (fb->bits_per_pixel == 16) { 699235783Skib pindex = regno << 3; 700235783Skib 701235783Skib if (fb->depth == 16 && regno > 63) 702235783Skib return -EINVAL; 703235783Skib if (fb->depth == 15 && regno > 31) 704235783Skib return -EINVAL; 705235783Skib 706235783Skib if (fb->depth == 16) { 707235783Skib u16 r, g, b; 708235783Skib int i; 709235783Skib if (regno < 32) { 710235783Skib for (i = 0; i < 8; i++) 711235783Skib fb_helper->funcs->gamma_set(crtc, red, 712235783Skib green, blue, pindex + i); 713235783Skib } 714235783Skib 715235783Skib fb_helper->funcs->gamma_get(crtc, &r, 716235783Skib &g, &b, 717235783Skib pindex >> 1); 718235783Skib 719235783Skib for (i = 0; i < 4; i++) 720235783Skib fb_helper->funcs->gamma_set(crtc, r, 721235783Skib green, b, 722235783Skib (pindex >> 1) + i); 723235783Skib } 724235783Skib } 725235783Skib 726235783Skib if (fb->depth != 16) 727235783Skib fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex); 728235783Skib return 0; 729235783Skib} 730235783Skib#endif 731235783Skib 732235783Skib#if 0 733235783Skibint drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) 734235783Skib{ 735235783Skib struct drm_fb_helper *fb_helper = info->par; 736235783Skib struct drm_crtc_helper_funcs *crtc_funcs; 737235783Skib u16 *red, *green, *blue, *transp; 738235783Skib struct drm_crtc *crtc; 739235783Skib int i, j, rc = 0; 740235783Skib int start; 741235783Skib 742235783Skib for (i = 0; i < fb_helper->crtc_count; i++) { 743235783Skib crtc = fb_helper->crtc_info[i].mode_set.crtc; 744235783Skib crtc_funcs = crtc->helper_private; 745235783Skib 746235783Skib red = cmap->red; 747235783Skib green = cmap->green; 748235783Skib blue = cmap->blue; 749235783Skib transp = cmap->transp; 750235783Skib start = cmap->start; 751235783Skib 752235783Skib for (j = 0; j < cmap->len; j++) { 753235783Skib u16 hred, hgreen, hblue, htransp = 0xffff; 754235783Skib 755235783Skib hred = *red++; 756235783Skib hgreen = *green++; 757235783Skib hblue = *blue++; 758235783Skib 759235783Skib if (transp) 760235783Skib htransp = *transp++; 761235783Skib 762235783Skib rc = setcolreg(crtc, hred, hgreen, hblue, start++, info); 763235783Skib if (rc) 764235783Skib return rc; 765235783Skib } 766235783Skib crtc_funcs->load_lut(crtc); 767235783Skib } 768235783Skib return rc; 769235783Skib} 770235783Skib#endif 771235783Skib 772235783Skib#if 0 773235783Skibint drm_fb_helper_check_var(struct fb_var_screeninfo *var, 774235783Skib struct fb_info *info) 775235783Skib{ 776235783Skib struct drm_fb_helper *fb_helper = info->par; 777235783Skib struct drm_framebuffer *fb = fb_helper->fb; 778235783Skib int depth; 779235783Skib 780235783Skib if (var->pixclock != 0 || in_dbg_master()) 781235783Skib return -EINVAL; 782235783Skib 783235783Skib /* Need to resize the fb object !!! */ 784235783Skib if (var->bits_per_pixel > fb->bits_per_pixel || 785235783Skib var->xres > fb->width || var->yres > fb->height || 786235783Skib var->xres_virtual > fb->width || var->yres_virtual > fb->height) { 787235783Skib DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb " 788235783Skib "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n", 789235783Skib var->xres, var->yres, var->bits_per_pixel, 790235783Skib var->xres_virtual, var->yres_virtual, 791235783Skib fb->width, fb->height, fb->bits_per_pixel); 792235783Skib return -EINVAL; 793235783Skib } 794235783Skib 795235783Skib switch (var->bits_per_pixel) { 796235783Skib case 16: 797235783Skib depth = (var->green.length == 6) ? 16 : 15; 798235783Skib break; 799235783Skib case 32: 800235783Skib depth = (var->transp.length > 0) ? 32 : 24; 801235783Skib break; 802235783Skib default: 803235783Skib depth = var->bits_per_pixel; 804235783Skib break; 805235783Skib } 806235783Skib 807235783Skib switch (depth) { 808235783Skib case 8: 809235783Skib var->red.offset = 0; 810235783Skib var->green.offset = 0; 811235783Skib var->blue.offset = 0; 812235783Skib var->red.length = 8; 813235783Skib var->green.length = 8; 814235783Skib var->blue.length = 8; 815235783Skib var->transp.length = 0; 816235783Skib var->transp.offset = 0; 817235783Skib break; 818235783Skib case 15: 819235783Skib var->red.offset = 10; 820235783Skib var->green.offset = 5; 821235783Skib var->blue.offset = 0; 822235783Skib var->red.length = 5; 823235783Skib var->green.length = 5; 824235783Skib var->blue.length = 5; 825235783Skib var->transp.length = 1; 826235783Skib var->transp.offset = 15; 827235783Skib break; 828235783Skib case 16: 829235783Skib var->red.offset = 11; 830235783Skib var->green.offset = 5; 831235783Skib var->blue.offset = 0; 832235783Skib var->red.length = 5; 833235783Skib var->green.length = 6; 834235783Skib var->blue.length = 5; 835235783Skib var->transp.length = 0; 836235783Skib var->transp.offset = 0; 837235783Skib break; 838235783Skib case 24: 839235783Skib var->red.offset = 16; 840235783Skib var->green.offset = 8; 841235783Skib var->blue.offset = 0; 842235783Skib var->red.length = 8; 843235783Skib var->green.length = 8; 844235783Skib var->blue.length = 8; 845235783Skib var->transp.length = 0; 846235783Skib var->transp.offset = 0; 847235783Skib break; 848235783Skib case 32: 849235783Skib var->red.offset = 16; 850235783Skib var->green.offset = 8; 851235783Skib var->blue.offset = 0; 852235783Skib var->red.length = 8; 853235783Skib var->green.length = 8; 854235783Skib var->blue.length = 8; 855235783Skib var->transp.length = 8; 856235783Skib var->transp.offset = 24; 857235783Skib break; 858235783Skib default: 859235783Skib return -EINVAL; 860235783Skib } 861235783Skib return 0; 862235783Skib} 863235783Skib#endif 864235783Skib 865235783Skib#if 0 866235783Skib/* this will let fbcon do the mode init */ 867235783Skibint drm_fb_helper_set_par(struct fb_info *info) 868235783Skib{ 869235783Skib struct drm_fb_helper *fb_helper = info->par; 870235783Skib struct drm_device *dev = fb_helper->dev; 871235783Skib struct fb_var_screeninfo *var = &info->var; 872235783Skib struct drm_crtc *crtc; 873235783Skib int ret; 874235783Skib int i; 875235783Skib 876235783Skib if (var->pixclock != 0) { 877235783Skib DRM_ERROR("PIXEL CLOCK SET\n"); 878235783Skib return -EINVAL; 879235783Skib } 880235783Skib 881235783Skib mutex_lock(&dev->mode_config.mutex); 882235783Skib for (i = 0; i < fb_helper->crtc_count; i++) { 883235783Skib crtc = fb_helper->crtc_info[i].mode_set.crtc; 884235783Skib ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set); 885235783Skib if (ret) { 886235783Skib mutex_unlock(&dev->mode_config.mutex); 887235783Skib return ret; 888235783Skib } 889235783Skib } 890235783Skib mutex_unlock(&dev->mode_config.mutex); 891235783Skib 892235783Skib if (fb_helper->delayed_hotplug) { 893235783Skib fb_helper->delayed_hotplug = false; 894235783Skib drm_fb_helper_hotplug_event(fb_helper); 895235783Skib } 896235783Skib return 0; 897235783Skib} 898235783Skib#endif 899235783Skib 900235783Skib#if 0 901235783Skibint drm_fb_helper_pan_display(struct fb_var_screeninfo *var, 902235783Skib struct fb_info *info) 903235783Skib{ 904235783Skib struct drm_fb_helper *fb_helper = info->par; 905235783Skib struct drm_device *dev = fb_helper->dev; 906235783Skib struct drm_mode_set *modeset; 907235783Skib struct drm_crtc *crtc; 908235783Skib int ret = 0; 909235783Skib int i; 910235783Skib 911235783Skib mutex_lock(&dev->mode_config.mutex); 912235783Skib for (i = 0; i < fb_helper->crtc_count; i++) { 913235783Skib crtc = fb_helper->crtc_info[i].mode_set.crtc; 914235783Skib 915235783Skib modeset = &fb_helper->crtc_info[i].mode_set; 916235783Skib 917235783Skib modeset->x = var->xoffset; 918235783Skib modeset->y = var->yoffset; 919235783Skib 920235783Skib if (modeset->num_connectors) { 921235783Skib ret = crtc->funcs->set_config(modeset); 922235783Skib if (!ret) { 923235783Skib info->var.xoffset = var->xoffset; 924235783Skib info->var.yoffset = var->yoffset; 925235783Skib } 926235783Skib } 927235783Skib } 928235783Skib mutex_unlock(&dev->mode_config.mutex); 929235783Skib return ret; 930235783Skib} 931235783Skib#endif 932235783Skib 933235783Skibint drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, 934235783Skib int preferred_bpp) 935235783Skib{ 936235783Skib int new_fb = 0; 937235783Skib int crtc_count = 0; 938235783Skib int i; 939235783Skib struct fb_info *info; 940235783Skib struct drm_fb_helper_surface_size sizes; 941235783Skib int gamma_size = 0; 942262861Sjhb struct vt_kms_softc *sc; 943262861Sjhb device_t kdev; 944235783Skib 945235783Skib memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size)); 946235783Skib sizes.surface_depth = 24; 947235783Skib sizes.surface_bpp = 32; 948235783Skib sizes.fb_width = (unsigned)-1; 949235783Skib sizes.fb_height = (unsigned)-1; 950235783Skib 951235783Skib /* if driver picks 8 or 16 by default use that 952235783Skib for both depth/bpp */ 953235783Skib if (preferred_bpp != sizes.surface_bpp) { 954235783Skib sizes.surface_depth = sizes.surface_bpp = preferred_bpp; 955235783Skib } 956235783Skib /* first up get a count of crtcs now in use and new min/maxes width/heights */ 957235783Skib for (i = 0; i < fb_helper->connector_count; i++) { 958235783Skib struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i]; 959235783Skib struct drm_fb_helper_cmdline_mode *cmdline_mode; 960235783Skib 961235783Skib cmdline_mode = &fb_helper_conn->cmdline_mode; 962235783Skib 963235783Skib if (cmdline_mode->bpp_specified) { 964235783Skib switch (cmdline_mode->bpp) { 965235783Skib case 8: 966235783Skib sizes.surface_depth = sizes.surface_bpp = 8; 967235783Skib break; 968235783Skib case 15: 969235783Skib sizes.surface_depth = 15; 970235783Skib sizes.surface_bpp = 16; 971235783Skib break; 972235783Skib case 16: 973235783Skib sizes.surface_depth = sizes.surface_bpp = 16; 974235783Skib break; 975235783Skib case 24: 976235783Skib sizes.surface_depth = sizes.surface_bpp = 24; 977235783Skib break; 978235783Skib case 32: 979235783Skib sizes.surface_depth = 24; 980235783Skib sizes.surface_bpp = 32; 981235783Skib break; 982235783Skib } 983235783Skib break; 984235783Skib } 985235783Skib } 986235783Skib 987235783Skib crtc_count = 0; 988235783Skib for (i = 0; i < fb_helper->crtc_count; i++) { 989235783Skib struct drm_display_mode *desired_mode; 990235783Skib desired_mode = fb_helper->crtc_info[i].desired_mode; 991235783Skib 992235783Skib if (desired_mode) { 993235783Skib if (gamma_size == 0) 994235783Skib gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size; 995235783Skib if (desired_mode->hdisplay < sizes.fb_width) 996235783Skib sizes.fb_width = desired_mode->hdisplay; 997235783Skib if (desired_mode->vdisplay < sizes.fb_height) 998235783Skib sizes.fb_height = desired_mode->vdisplay; 999235783Skib if (desired_mode->hdisplay > sizes.surface_width) 1000235783Skib sizes.surface_width = desired_mode->hdisplay; 1001235783Skib if (desired_mode->vdisplay > sizes.surface_height) 1002235783Skib sizes.surface_height = desired_mode->vdisplay; 1003235783Skib crtc_count++; 1004235783Skib } 1005235783Skib } 1006235783Skib 1007235783Skib if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) { 1008235783Skib /* hmm everyone went away - assume VGA cable just fell out 1009235783Skib and will come back later. */ 1010235783Skib DRM_INFO("Cannot find any crtc or sizes - going 1024x768\n"); 1011235783Skib sizes.fb_width = sizes.surface_width = 1024; 1012235783Skib sizes.fb_height = sizes.surface_height = 768; 1013235783Skib } 1014235783Skib 1015235783Skib /* push down into drivers */ 1016235783Skib new_fb = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes); 1017235783Skib if (new_fb < 0) 1018235783Skib return new_fb; 1019235783Skib 1020262861Sjhb sc = malloc(sizeof(struct vt_kms_softc), DRM_MEM_KMS, 1021262861Sjhb M_WAITOK | M_ZERO); 1022262861Sjhb sc->fb_helper = fb_helper; 1023262861Sjhb TASK_INIT(&sc->fb_mode_task, 0, vt_restore_fbdev_mode, sc); 1024262861Sjhb 1025235783Skib info = fb_helper->fbdev; 1026262861Sjhb 1027262861Sjhb info->fb_name = device_get_nameunit(fb_helper->dev->device); 1028262861Sjhb info->fb_depth = fb_helper->fb->bits_per_pixel; 1029262861Sjhb info->fb_height = fb_helper->fb->height; 1030262861Sjhb info->fb_width = fb_helper->fb->width; 1031262861Sjhb info->fb_stride = fb_helper->fb->pitches[0]; 1032262861Sjhb info->fb_priv = sc; 1033262861Sjhb info->enter = &vt_kms_postswitch; 1034235783Skib 1035235783Skib /* set the fb pointer */ 1036235783Skib for (i = 0; i < fb_helper->crtc_count; i++) { 1037235783Skib fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; 1038235783Skib } 1039235783Skib 1040235783Skib if (new_fb) { 1041262861Sjhb device_t fbd; 1042262861Sjhb int ret; 1043262861Sjhb 1044262861Sjhb kdev = fb_helper->dev->device; 1045262861Sjhb fbd = device_add_child(kdev, "fbd", device_get_unit(kdev)); 1046262861Sjhb if (fbd != NULL) 1047262861Sjhb ret = device_probe_and_attach(fbd); 1048262861Sjhb else 1049262861Sjhb ret = ENODEV; 1050262861Sjhb#ifdef DEV_VT 1051262861Sjhb if (ret != 0) 1052262861Sjhb DRM_ERROR("Failed to attach fbd device: %d\n", ret); 1053262861Sjhb#endif 1054262861Sjhb } 1055235783Skib return 0; 1056235783Skib} 1057235783Skib 1058235783Skib#if 0 1059235783Skibvoid drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, 1060235783Skib uint32_t depth) 1061235783Skib{ 1062235783Skib info->fix.type = FB_TYPE_PACKED_PIXELS; 1063235783Skib info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR : 1064235783Skib FB_VISUAL_trueCOLOR; 1065235783Skib info->fix.mmio_start = 0; 1066235783Skib info->fix.mmio_len = 0; 1067235783Skib info->fix.type_aux = 0; 1068235783Skib info->fix.xpanstep = 1; /* doing it in hw */ 1069235783Skib info->fix.ypanstep = 1; /* doing it in hw */ 1070235783Skib info->fix.ywrapstep = 0; 1071235783Skib info->fix.accel = FB_ACCEL_NONE; 1072235783Skib info->fix.type_aux = 0; 1073235783Skib 1074235783Skib info->fix.line_length = pitch; 1075235783Skib return; 1076235783Skib} 1077235783Skib 1078235783Skibvoid drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper, 1079235783Skib uint32_t fb_width, uint32_t fb_height) 1080235783Skib{ 1081235783Skib struct drm_framebuffer *fb = fb_helper->fb; 1082235783Skib info->pseudo_palette = fb_helper->pseudo_palette; 1083235783Skib info->var.xres_virtual = fb->width; 1084235783Skib info->var.yres_virtual = fb->height; 1085235783Skib info->var.bits_per_pixel = fb->bits_per_pixel; 1086235783Skib info->var.accel_flags = FB_ACCELF_TEXT; 1087235783Skib info->var.xoffset = 0; 1088235783Skib info->var.yoffset = 0; 1089235783Skib info->var.activate = FB_ACTIVATE_NOW; 1090235783Skib info->var.height = -1; 1091235783Skib info->var.width = -1; 1092235783Skib 1093235783Skib switch (fb->depth) { 1094235783Skib case 8: 1095235783Skib info->var.red.offset = 0; 1096235783Skib info->var.green.offset = 0; 1097235783Skib info->var.blue.offset = 0; 1098235783Skib info->var.red.length = 8; /* 8bit DAC */ 1099235783Skib info->var.green.length = 8; 1100235783Skib info->var.blue.length = 8; 1101235783Skib info->var.transp.offset = 0; 1102235783Skib info->var.transp.length = 0; 1103235783Skib break; 1104235783Skib case 15: 1105235783Skib info->var.red.offset = 10; 1106235783Skib info->var.green.offset = 5; 1107235783Skib info->var.blue.offset = 0; 1108235783Skib info->var.red.length = 5; 1109235783Skib info->var.green.length = 5; 1110235783Skib info->var.blue.length = 5; 1111235783Skib info->var.transp.offset = 15; 1112235783Skib info->var.transp.length = 1; 1113235783Skib break; 1114235783Skib case 16: 1115235783Skib info->var.red.offset = 11; 1116235783Skib info->var.green.offset = 5; 1117235783Skib info->var.blue.offset = 0; 1118235783Skib info->var.red.length = 5; 1119235783Skib info->var.green.length = 6; 1120235783Skib info->var.blue.length = 5; 1121235783Skib info->var.transp.offset = 0; 1122235783Skib break; 1123235783Skib case 24: 1124235783Skib info->var.red.offset = 16; 1125235783Skib info->var.green.offset = 8; 1126235783Skib info->var.blue.offset = 0; 1127235783Skib info->var.red.length = 8; 1128235783Skib info->var.green.length = 8; 1129235783Skib info->var.blue.length = 8; 1130235783Skib info->var.transp.offset = 0; 1131235783Skib info->var.transp.length = 0; 1132235783Skib break; 1133235783Skib case 32: 1134235783Skib info->var.red.offset = 16; 1135235783Skib info->var.green.offset = 8; 1136235783Skib info->var.blue.offset = 0; 1137235783Skib info->var.red.length = 8; 1138235783Skib info->var.green.length = 8; 1139235783Skib info->var.blue.length = 8; 1140235783Skib info->var.transp.offset = 24; 1141235783Skib info->var.transp.length = 8; 1142235783Skib break; 1143235783Skib default: 1144235783Skib break; 1145235783Skib } 1146235783Skib 1147235783Skib info->var.xres = fb_width; 1148235783Skib info->var.yres = fb_height; 1149235783Skib} 1150235783Skib#endif 1151235783Skib 1152235783Skibstatic int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper, 1153235783Skib uint32_t maxX, 1154235783Skib uint32_t maxY) 1155235783Skib{ 1156235783Skib struct drm_connector *connector; 1157235783Skib int count = 0; 1158235783Skib int i; 1159235783Skib 1160235783Skib for (i = 0; i < fb_helper->connector_count; i++) { 1161235783Skib connector = fb_helper->connector_info[i]->connector; 1162235783Skib count += connector->funcs->fill_modes(connector, maxX, maxY); 1163235783Skib } 1164235783Skib 1165235783Skib return count; 1166235783Skib} 1167235783Skib 1168235783Skibstatic struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height) 1169235783Skib{ 1170235783Skib struct drm_display_mode *mode; 1171235783Skib 1172235783Skib list_for_each_entry(mode, &fb_connector->connector->modes, head) { 1173235783Skib if (drm_mode_width(mode) > width || 1174235783Skib drm_mode_height(mode) > height) 1175235783Skib continue; 1176235783Skib if (mode->type & DRM_MODE_TYPE_PREFERRED) 1177235783Skib return mode; 1178235783Skib } 1179235783Skib return NULL; 1180235783Skib} 1181235783Skib 1182235783Skibstatic bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector) 1183235783Skib{ 1184235783Skib struct drm_fb_helper_cmdline_mode *cmdline_mode; 1185235783Skib cmdline_mode = &fb_connector->cmdline_mode; 1186235783Skib return cmdline_mode->specified; 1187235783Skib} 1188235783Skib 1189235783Skibstatic struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn, 1190235783Skib int width, int height) 1191235783Skib{ 1192235783Skib struct drm_cmdline_mode *cmdline_mode; 1193235783Skib struct drm_display_mode *mode = NULL; 1194235783Skib 1195235783Skib cmdline_mode = &fb_helper_conn->cmdline_mode1; 1196235783Skib if (cmdline_mode->specified == false && 1197235783Skib !drm_fetch_cmdline_mode_from_kenv(fb_helper_conn->connector, 1198235783Skib cmdline_mode)) 1199235783Skib return (NULL); 1200235783Skib 1201235783Skib /* attempt to find a matching mode in the list of modes 1202235783Skib * we have gotten so far, if not add a CVT mode that conforms 1203235783Skib */ 1204235783Skib if (cmdline_mode->rb || cmdline_mode->margins) 1205235783Skib goto create_mode; 1206235783Skib 1207235783Skib list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { 1208235783Skib /* check width/height */ 1209235783Skib if (mode->hdisplay != cmdline_mode->xres || 1210235783Skib mode->vdisplay != cmdline_mode->yres) 1211235783Skib continue; 1212235783Skib 1213235783Skib if (cmdline_mode->refresh_specified) { 1214235783Skib if (mode->vrefresh != cmdline_mode->refresh) 1215235783Skib continue; 1216235783Skib } 1217235783Skib 1218235783Skib if (cmdline_mode->interlace) { 1219235783Skib if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) 1220235783Skib continue; 1221235783Skib } 1222235783Skib return mode; 1223235783Skib } 1224235783Skib 1225235783Skibcreate_mode: 1226235783Skib if (cmdline_mode->cvt) 1227235783Skib mode = drm_cvt_mode(fb_helper_conn->connector->dev, 1228235783Skib cmdline_mode->xres, cmdline_mode->yres, 1229235783Skib cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60, 1230235783Skib cmdline_mode->rb, cmdline_mode->interlace, 1231235783Skib cmdline_mode->margins); 1232235783Skib else 1233235783Skib mode = drm_gtf_mode(fb_helper_conn->connector->dev, 1234235783Skib cmdline_mode->xres, cmdline_mode->yres, 1235235783Skib cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60, 1236235783Skib cmdline_mode->interlace, 1237235783Skib cmdline_mode->margins); 1238235783Skib drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); 1239235783Skib list_add(&mode->head, &fb_helper_conn->connector->modes); 1240235783Skib return mode; 1241235783Skib} 1242235783Skib 1243235783Skibstatic bool drm_connector_enabled(struct drm_connector *connector, bool strict) 1244235783Skib{ 1245235783Skib bool enable; 1246235783Skib 1247235783Skib if (strict) { 1248235783Skib enable = connector->status == connector_status_connected; 1249235783Skib } else { 1250235783Skib enable = connector->status != connector_status_disconnected; 1251235783Skib } 1252235783Skib return enable; 1253235783Skib} 1254235783Skib 1255235783Skibstatic void drm_enable_connectors(struct drm_fb_helper *fb_helper, 1256235783Skib bool *enabled) 1257235783Skib{ 1258235783Skib bool any_enabled = false; 1259235783Skib struct drm_connector *connector; 1260235783Skib int i = 0; 1261235783Skib 1262235783Skib for (i = 0; i < fb_helper->connector_count; i++) { 1263235783Skib connector = fb_helper->connector_info[i]->connector; 1264235783Skib enabled[i] = drm_connector_enabled(connector, true); 1265235783Skib DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, 1266235783Skib enabled[i] ? "yes" : "no"); 1267235783Skib any_enabled |= enabled[i]; 1268235783Skib } 1269235783Skib 1270235783Skib if (any_enabled) 1271235783Skib return; 1272235783Skib 1273235783Skib for (i = 0; i < fb_helper->connector_count; i++) { 1274235783Skib connector = fb_helper->connector_info[i]->connector; 1275235783Skib enabled[i] = drm_connector_enabled(connector, false); 1276235783Skib } 1277235783Skib} 1278235783Skib 1279235783Skibstatic bool drm_target_cloned(struct drm_fb_helper *fb_helper, 1280235783Skib struct drm_display_mode **modes, 1281235783Skib bool *enabled, int width, int height) 1282235783Skib{ 1283235783Skib int count, i, j; 1284235783Skib bool can_clone = false; 1285235783Skib struct drm_fb_helper_connector *fb_helper_conn; 1286235783Skib struct drm_display_mode *dmt_mode, *mode; 1287235783Skib 1288235783Skib /* only contemplate cloning in the single crtc case */ 1289235783Skib if (fb_helper->crtc_count > 1) 1290235783Skib return false; 1291235783Skib 1292235783Skib count = 0; 1293235783Skib for (i = 0; i < fb_helper->connector_count; i++) { 1294235783Skib if (enabled[i]) 1295235783Skib count++; 1296235783Skib } 1297235783Skib 1298235783Skib /* only contemplate cloning if more than one connector is enabled */ 1299235783Skib if (count <= 1) 1300235783Skib return false; 1301235783Skib 1302235783Skib /* check the command line or if nothing common pick 1024x768 */ 1303235783Skib can_clone = true; 1304235783Skib for (i = 0; i < fb_helper->connector_count; i++) { 1305235783Skib if (!enabled[i]) 1306235783Skib continue; 1307235783Skib fb_helper_conn = fb_helper->connector_info[i]; 1308235783Skib modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height); 1309235783Skib if (!modes[i]) { 1310235783Skib can_clone = false; 1311235783Skib break; 1312235783Skib } 1313235783Skib for (j = 0; j < i; j++) { 1314235783Skib if (!enabled[j]) 1315235783Skib continue; 1316235783Skib if (!drm_mode_equal(modes[j], modes[i])) 1317235783Skib can_clone = false; 1318235783Skib } 1319235783Skib } 1320235783Skib 1321235783Skib if (can_clone) { 1322235783Skib DRM_DEBUG_KMS("can clone using command line\n"); 1323235783Skib return true; 1324235783Skib } 1325235783Skib 1326235783Skib /* try and find a 1024x768 mode on each connector */ 1327235783Skib can_clone = true; 1328235783Skib dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60); 1329235783Skib 1330235783Skib for (i = 0; i < fb_helper->connector_count; i++) { 1331235783Skib 1332235783Skib if (!enabled[i]) 1333235783Skib continue; 1334235783Skib 1335235783Skib fb_helper_conn = fb_helper->connector_info[i]; 1336235783Skib list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { 1337235783Skib if (drm_mode_equal(mode, dmt_mode)) 1338235783Skib modes[i] = mode; 1339235783Skib } 1340235783Skib if (!modes[i]) 1341235783Skib can_clone = false; 1342235783Skib } 1343235783Skib 1344235783Skib if (can_clone) { 1345235783Skib DRM_DEBUG_KMS("can clone using 1024x768\n"); 1346235783Skib return true; 1347235783Skib } 1348235783Skib DRM_INFO("kms: can't enable cloning when we probably wanted to.\n"); 1349235783Skib return false; 1350235783Skib} 1351235783Skib 1352235783Skibstatic bool drm_target_preferred(struct drm_fb_helper *fb_helper, 1353235783Skib struct drm_display_mode **modes, 1354235783Skib bool *enabled, int width, int height) 1355235783Skib{ 1356235783Skib struct drm_fb_helper_connector *fb_helper_conn; 1357235783Skib int i; 1358235783Skib 1359235783Skib for (i = 0; i < fb_helper->connector_count; i++) { 1360235783Skib fb_helper_conn = fb_helper->connector_info[i]; 1361235783Skib 1362235783Skib if (enabled[i] == false) 1363235783Skib continue; 1364235783Skib 1365235783Skib DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", 1366235783Skib fb_helper_conn->connector->base.id); 1367235783Skib 1368235783Skib /* got for command line mode first */ 1369235783Skib modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height); 1370235783Skib if (!modes[i]) { 1371235783Skib DRM_DEBUG_KMS("looking for preferred mode on connector %d\n", 1372235783Skib fb_helper_conn->connector->base.id); 1373235783Skib modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height); 1374235783Skib } 1375235783Skib /* No preferred modes, pick one off the list */ 1376235783Skib if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) { 1377235783Skib list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head) 1378235783Skib break; 1379235783Skib } 1380235783Skib DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : 1381235783Skib "none"); 1382235783Skib } 1383235783Skib return true; 1384235783Skib} 1385235783Skib 1386235783Skibstatic int drm_pick_crtcs(struct drm_fb_helper *fb_helper, 1387235783Skib struct drm_fb_helper_crtc **best_crtcs, 1388235783Skib struct drm_display_mode **modes, 1389235783Skib int n, int width, int height) 1390235783Skib{ 1391235783Skib int c, o; 1392235783Skib struct drm_device *dev = fb_helper->dev; 1393235783Skib struct drm_connector *connector; 1394235783Skib struct drm_connector_helper_funcs *connector_funcs; 1395235783Skib struct drm_encoder *encoder; 1396235783Skib struct drm_fb_helper_crtc *best_crtc; 1397235783Skib int my_score, best_score, score; 1398235783Skib struct drm_fb_helper_crtc **crtcs, *crtc; 1399235783Skib struct drm_fb_helper_connector *fb_helper_conn; 1400235783Skib 1401235783Skib if (n == fb_helper->connector_count) 1402235783Skib return 0; 1403235783Skib 1404235783Skib fb_helper_conn = fb_helper->connector_info[n]; 1405235783Skib connector = fb_helper_conn->connector; 1406235783Skib 1407235783Skib best_crtcs[n] = NULL; 1408235783Skib best_crtc = NULL; 1409235783Skib best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height); 1410235783Skib if (modes[n] == NULL) 1411235783Skib return best_score; 1412235783Skib 1413235783Skib crtcs = malloc(dev->mode_config.num_connector * 1414235783Skib sizeof(struct drm_fb_helper_crtc *), DRM_MEM_KMS, 1415235783Skib M_WAITOK | M_ZERO); 1416235783Skib 1417235783Skib my_score = 1; 1418235783Skib if (connector->status == connector_status_connected) 1419235783Skib my_score++; 1420235783Skib if (drm_has_cmdline_mode(fb_helper_conn)) 1421235783Skib my_score++; 1422235783Skib if (drm_has_preferred_mode(fb_helper_conn, width, height)) 1423235783Skib my_score++; 1424235783Skib 1425235783Skib connector_funcs = connector->helper_private; 1426235783Skib encoder = connector_funcs->best_encoder(connector); 1427235783Skib if (!encoder) 1428235783Skib goto out; 1429235783Skib 1430235783Skib /* select a crtc for this connector and then attempt to configure 1431235783Skib remaining connectors */ 1432235783Skib for (c = 0; c < fb_helper->crtc_count; c++) { 1433235783Skib crtc = &fb_helper->crtc_info[c]; 1434235783Skib 1435235783Skib if ((encoder->possible_crtcs & (1 << c)) == 0) { 1436235783Skib continue; 1437235783Skib } 1438235783Skib 1439235783Skib for (o = 0; o < n; o++) 1440235783Skib if (best_crtcs[o] == crtc) 1441235783Skib break; 1442235783Skib 1443235783Skib if (o < n) { 1444235783Skib /* ignore cloning unless only a single crtc */ 1445235783Skib if (fb_helper->crtc_count > 1) 1446235783Skib continue; 1447235783Skib 1448235783Skib if (!drm_mode_equal(modes[o], modes[n])) 1449235783Skib continue; 1450235783Skib } 1451235783Skib 1452235783Skib crtcs[n] = crtc; 1453235783Skib memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *)); 1454235783Skib score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1, 1455235783Skib width, height); 1456235783Skib if (score > best_score) { 1457235783Skib best_crtc = crtc; 1458235783Skib best_score = score; 1459235783Skib memcpy(best_crtcs, crtcs, 1460235783Skib dev->mode_config.num_connector * 1461235783Skib sizeof(struct drm_fb_helper_crtc *)); 1462235783Skib } 1463235783Skib } 1464235783Skibout: 1465235783Skib free(crtcs, DRM_MEM_KMS); 1466235783Skib return best_score; 1467235783Skib} 1468235783Skib 1469235783Skibstatic void drm_setup_crtcs(struct drm_fb_helper *fb_helper) 1470235783Skib{ 1471235783Skib struct drm_device *dev = fb_helper->dev; 1472235783Skib struct drm_fb_helper_crtc **crtcs; 1473235783Skib struct drm_display_mode **modes; 1474235783Skib struct drm_encoder *encoder; 1475235783Skib struct drm_mode_set *modeset; 1476235783Skib bool *enabled; 1477235783Skib int width, height; 1478235783Skib int i, ret; 1479235783Skib 1480235783Skib DRM_DEBUG_KMS("\n"); 1481235783Skib 1482235783Skib width = dev->mode_config.max_width; 1483235783Skib height = dev->mode_config.max_height; 1484235783Skib 1485235783Skib /* clean out all the encoder/crtc combos */ 1486235783Skib list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 1487235783Skib encoder->crtc = NULL; 1488235783Skib } 1489235783Skib 1490235783Skib crtcs = malloc(dev->mode_config.num_connector * 1491235783Skib sizeof(struct drm_fb_helper_crtc *), DRM_MEM_KMS, 1492235783Skib M_WAITOK | M_ZERO); 1493235783Skib modes = malloc(dev->mode_config.num_connector * 1494235783Skib sizeof(struct drm_display_mode *), DRM_MEM_KMS, 1495235783Skib M_WAITOK | M_ZERO); 1496235783Skib enabled = malloc(dev->mode_config.num_connector * 1497235783Skib sizeof(bool), DRM_MEM_KMS, M_WAITOK | M_ZERO); 1498235783Skib 1499235783Skib drm_enable_connectors(fb_helper, enabled); 1500235783Skib 1501235783Skib ret = drm_target_cloned(fb_helper, modes, enabled, width, height); 1502235783Skib if (!ret) { 1503235783Skib ret = drm_target_preferred(fb_helper, modes, enabled, width, height); 1504235783Skib if (!ret) 1505235783Skib DRM_ERROR("Unable to find initial modes\n"); 1506235783Skib } 1507235783Skib 1508235783Skib DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height); 1509235783Skib 1510235783Skib drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height); 1511235783Skib 1512235783Skib /* need to set the modesets up here for use later */ 1513235783Skib /* fill out the connector<->crtc mappings into the modesets */ 1514235783Skib for (i = 0; i < fb_helper->crtc_count; i++) { 1515235783Skib modeset = &fb_helper->crtc_info[i].mode_set; 1516235783Skib modeset->num_connectors = 0; 1517235783Skib } 1518235783Skib 1519235783Skib for (i = 0; i < fb_helper->connector_count; i++) { 1520235783Skib struct drm_display_mode *mode = modes[i]; 1521235783Skib struct drm_fb_helper_crtc *fb_crtc = crtcs[i]; 1522235783Skib modeset = &fb_crtc->mode_set; 1523235783Skib 1524235783Skib if (mode && fb_crtc) { 1525235783Skib DRM_DEBUG_KMS("desired mode %s set on crtc %d\n", 1526235783Skib mode->name, fb_crtc->mode_set.crtc->base.id); 1527235783Skib fb_crtc->desired_mode = mode; 1528235783Skib if (modeset->mode) 1529235783Skib drm_mode_destroy(dev, modeset->mode); 1530235783Skib modeset->mode = drm_mode_duplicate(dev, 1531235783Skib fb_crtc->desired_mode); 1532235783Skib modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector; 1533235783Skib } 1534235783Skib } 1535235783Skib 1536235783Skib free(crtcs, DRM_MEM_KMS); 1537235783Skib free(modes, DRM_MEM_KMS); 1538235783Skib free(enabled, DRM_MEM_KMS); 1539235783Skib} 1540235783Skib 1541235783Skib/** 1542235783Skib * drm_helper_initial_config - setup a sane initial connector configuration 1543235783Skib * @dev: DRM device 1544235783Skib * 1545235783Skib * LOCKING: 1546235783Skib * Called at init time, must take mode config lock. 1547235783Skib * 1548235783Skib * Scan the CRTCs and connectors and try to put together an initial setup. 1549235783Skib * At the moment, this is a cloned configuration across all heads with 1550235783Skib * a new framebuffer object as the backing store. 1551235783Skib * 1552235783Skib * RETURNS: 1553235783Skib * Zero if everything went ok, nonzero otherwise. 1554235783Skib */ 1555235783Skibbool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) 1556235783Skib{ 1557235783Skib struct drm_device *dev = fb_helper->dev; 1558235783Skib int count = 0; 1559235783Skib 1560235783Skib /* disable all the possible outputs/crtcs before entering KMS mode */ 1561235783Skib drm_helper_disable_unused_functions(fb_helper->dev); 1562235783Skib 1563235783Skib drm_fb_helper_parse_command_line(fb_helper); 1564235783Skib 1565235783Skib count = drm_fb_helper_probe_connector_modes(fb_helper, 1566235783Skib dev->mode_config.max_width, 1567235783Skib dev->mode_config.max_height); 1568235783Skib /* 1569235783Skib * we shouldn't end up with no modes here. 1570235783Skib */ 1571235783Skib if (count == 0) { 1572235783Skib printf("No connectors reported connected with modes\n"); 1573235783Skib } 1574235783Skib drm_setup_crtcs(fb_helper); 1575235783Skib 1576235783Skib return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); 1577235783Skib} 1578235783Skib 1579235783Skibint drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) 1580235783Skib{ 1581235783Skib struct drm_device *dev = fb_helper->dev; 1582235783Skib int count = 0; 1583235783Skib u32 max_width, max_height, bpp_sel; 1584235783Skib bool bound = false, crtcs_bound = false; 1585235783Skib struct drm_crtc *crtc; 1586235783Skib 1587235783Skib if (!fb_helper->fb) 1588235783Skib return 0; 1589235783Skib 1590235783Skib sx_xlock(&dev->mode_config.mutex); 1591235783Skib list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 1592235783Skib if (crtc->fb) 1593235783Skib crtcs_bound = true; 1594235783Skib if (crtc->fb == fb_helper->fb) 1595235783Skib bound = true; 1596235783Skib } 1597235783Skib 1598235783Skib if (!bound && crtcs_bound) { 1599235783Skib fb_helper->delayed_hotplug = true; 1600235783Skib sx_xunlock(&dev->mode_config.mutex); 1601235783Skib return 0; 1602235783Skib } 1603235783Skib DRM_DEBUG_KMS("\n"); 1604235783Skib 1605235783Skib max_width = fb_helper->fb->width; 1606235783Skib max_height = fb_helper->fb->height; 1607235783Skib bpp_sel = fb_helper->fb->bits_per_pixel; 1608235783Skib 1609235783Skib count = drm_fb_helper_probe_connector_modes(fb_helper, max_width, 1610235783Skib max_height); 1611235783Skib drm_setup_crtcs(fb_helper); 1612235783Skib sx_xunlock(&dev->mode_config.mutex); 1613235783Skib 1614235783Skib return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); 1615235783Skib} 1616235783Skib 1617