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