1592ffb21SWarner Losh /* 2592ffb21SWarner Losh * Copyright (c) 2006-2009 Red Hat Inc. 3592ffb21SWarner Losh * Copyright (c) 2006-2008 Intel Corporation 4592ffb21SWarner Losh * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> 5592ffb21SWarner Losh * 6592ffb21SWarner Losh * DRM framebuffer helper functions 7592ffb21SWarner Losh * 8592ffb21SWarner Losh * Permission to use, copy, modify, distribute, and sell this software and its 9592ffb21SWarner Losh * documentation for any purpose is hereby granted without fee, provided that 10592ffb21SWarner Losh * the above copyright notice appear in all copies and that both that copyright 11592ffb21SWarner Losh * notice and this permission notice appear in supporting documentation, and 12592ffb21SWarner Losh * that the name of the copyright holders not be used in advertising or 13592ffb21SWarner Losh * publicity pertaining to distribution of the software without specific, 14592ffb21SWarner Losh * written prior permission. The copyright holders make no representations 15592ffb21SWarner Losh * about the suitability of this software for any purpose. It is provided "as 16592ffb21SWarner Losh * is" without express or implied warranty. 17592ffb21SWarner Losh * 18592ffb21SWarner Losh * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 19592ffb21SWarner Losh * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 20592ffb21SWarner Losh * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 21592ffb21SWarner Losh * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 22592ffb21SWarner Losh * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 23592ffb21SWarner Losh * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 24592ffb21SWarner Losh * OF THIS SOFTWARE. 25592ffb21SWarner Losh * 26592ffb21SWarner Losh * Authors: 27592ffb21SWarner Losh * Dave Airlie <airlied@linux.ie> 28592ffb21SWarner Losh * Jesse Barnes <jesse.barnes@intel.com> 29592ffb21SWarner Losh */ 30592ffb21SWarner Losh 31592ffb21SWarner Losh #include <sys/cdefs.h> 32592ffb21SWarner Losh #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 33592ffb21SWarner Losh 34592ffb21SWarner Losh #include <dev/drm2/drmP.h> 35592ffb21SWarner Losh #include <dev/drm2/drm_crtc.h> 36592ffb21SWarner Losh #include <dev/drm2/drm_fb_helper.h> 37592ffb21SWarner Losh #include <dev/drm2/drm_crtc_helper.h> 38592ffb21SWarner Losh 39592ffb21SWarner Losh MODULE_AUTHOR("David Airlie, Jesse Barnes"); 40592ffb21SWarner Losh MODULE_DESCRIPTION("DRM KMS helper"); 41592ffb21SWarner Losh MODULE_LICENSE("GPL and additional rights"); 42592ffb21SWarner Losh 43592ffb21SWarner Losh static DRM_LIST_HEAD(kernel_fb_helper_list); 44592ffb21SWarner Losh 45592ffb21SWarner Losh #include <sys/kdb.h> 46592ffb21SWarner Losh #include <sys/param.h> 47592ffb21SWarner Losh #include <sys/systm.h> 48592ffb21SWarner Losh 49592ffb21SWarner Losh struct vt_kms_softc { 50592ffb21SWarner Losh struct drm_fb_helper *fb_helper; 51592ffb21SWarner Losh struct task fb_mode_task; 52592ffb21SWarner Losh }; 53592ffb21SWarner Losh 54592ffb21SWarner Losh /* Call restore out of vt(9) locks. */ 55592ffb21SWarner Losh static void 56592ffb21SWarner Losh vt_restore_fbdev_mode(void *arg, int pending) 57592ffb21SWarner Losh { 58592ffb21SWarner Losh struct drm_fb_helper *fb_helper; 59592ffb21SWarner Losh struct vt_kms_softc *sc; 60592ffb21SWarner Losh 61592ffb21SWarner Losh sc = (struct vt_kms_softc *)arg; 62592ffb21SWarner Losh fb_helper = sc->fb_helper; 63592ffb21SWarner Losh sx_xlock(&fb_helper->dev->mode_config.mutex); 64592ffb21SWarner Losh drm_fb_helper_restore_fbdev_mode(fb_helper); 65592ffb21SWarner Losh sx_xunlock(&fb_helper->dev->mode_config.mutex); 66592ffb21SWarner Losh } 67592ffb21SWarner Losh 68592ffb21SWarner Losh static int 69592ffb21SWarner Losh vt_kms_postswitch(void *arg) 70592ffb21SWarner Losh { 71592ffb21SWarner Losh struct vt_kms_softc *sc; 72592ffb21SWarner Losh 73592ffb21SWarner Losh sc = (struct vt_kms_softc *)arg; 74592ffb21SWarner Losh 75879e0604SMateusz Guzik if (!kdb_active && !KERNEL_PANICKED()) 76592ffb21SWarner Losh taskqueue_enqueue(taskqueue_thread, &sc->fb_mode_task); 77592ffb21SWarner Losh else 78592ffb21SWarner Losh drm_fb_helper_restore_fbdev_mode(sc->fb_helper); 79592ffb21SWarner Losh 80592ffb21SWarner Losh return (0); 81592ffb21SWarner Losh } 82592ffb21SWarner Losh 83592ffb21SWarner Losh struct fb_info * 84*7a7bbe10SDimitry Andric framebuffer_alloc(void) 85592ffb21SWarner Losh { 86592ffb21SWarner Losh struct fb_info *info; 87592ffb21SWarner Losh struct vt_kms_softc *sc; 88592ffb21SWarner Losh 89592ffb21SWarner Losh info = malloc(sizeof(*info), DRM_MEM_KMS, M_WAITOK | M_ZERO); 90592ffb21SWarner Losh 91592ffb21SWarner Losh sc = malloc(sizeof(*sc), DRM_MEM_KMS, M_WAITOK | M_ZERO); 92592ffb21SWarner Losh TASK_INIT(&sc->fb_mode_task, 0, vt_restore_fbdev_mode, sc); 93592ffb21SWarner Losh 94592ffb21SWarner Losh info->fb_priv = sc; 95592ffb21SWarner Losh info->enter = &vt_kms_postswitch; 96592ffb21SWarner Losh 97592ffb21SWarner Losh return (info); 98592ffb21SWarner Losh } 99592ffb21SWarner Losh 100592ffb21SWarner Losh void 101592ffb21SWarner Losh framebuffer_release(struct fb_info *info) 102592ffb21SWarner Losh { 103592ffb21SWarner Losh 104592ffb21SWarner Losh free(info->fb_priv, DRM_MEM_KMS); 105592ffb21SWarner Losh free(info, DRM_MEM_KMS); 106592ffb21SWarner Losh } 107592ffb21SWarner Losh 108592ffb21SWarner Losh static int 109592ffb21SWarner Losh fb_get_options(const char *connector_name, char **option) 110592ffb21SWarner Losh { 111592ffb21SWarner Losh char tunable[64]; 112592ffb21SWarner Losh 113592ffb21SWarner Losh /* 114592ffb21SWarner Losh * A user may use loader tunables to set a specific mode for the 115592ffb21SWarner Losh * console. Tunables are read in the following order: 116592ffb21SWarner Losh * 1. kern.vt.fb.modes.$connector_name 117592ffb21SWarner Losh * 2. kern.vt.fb.default_mode 118592ffb21SWarner Losh * 119592ffb21SWarner Losh * Example of a mode specific to the LVDS connector: 120592ffb21SWarner Losh * kern.vt.fb.modes.LVDS="1024x768" 121592ffb21SWarner Losh * 122592ffb21SWarner Losh * Example of a mode applied to all connectors not having a 123592ffb21SWarner Losh * connector-specific mode: 124592ffb21SWarner Losh * kern.vt.fb.default_mode="640x480" 125592ffb21SWarner Losh */ 126592ffb21SWarner Losh snprintf(tunable, sizeof(tunable), "kern.vt.fb.modes.%s", 127592ffb21SWarner Losh connector_name); 128592ffb21SWarner Losh DRM_INFO("Connector %s: get mode from tunables:\n", connector_name); 129592ffb21SWarner Losh DRM_INFO(" - %s\n", tunable); 130592ffb21SWarner Losh DRM_INFO(" - kern.vt.fb.default_mode\n"); 131592ffb21SWarner Losh *option = kern_getenv(tunable); 132592ffb21SWarner Losh if (*option == NULL) 133592ffb21SWarner Losh *option = kern_getenv("kern.vt.fb.default_mode"); 134592ffb21SWarner Losh 135592ffb21SWarner Losh return (*option != NULL ? 0 : -ENOENT); 136592ffb21SWarner Losh } 137592ffb21SWarner Losh 138592ffb21SWarner Losh /** 139592ffb21SWarner Losh * DOC: fbdev helpers 140592ffb21SWarner Losh * 141592ffb21SWarner Losh * The fb helper functions are useful to provide an fbdev on top of a drm kernel 142592ffb21SWarner Losh * mode setting driver. They can be used mostly independantely from the crtc 143592ffb21SWarner Losh * helper functions used by many drivers to implement the kernel mode setting 144592ffb21SWarner Losh * interfaces. 145592ffb21SWarner Losh */ 146592ffb21SWarner Losh 147592ffb21SWarner Losh /* simple single crtc case helper function */ 148592ffb21SWarner Losh int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) 149592ffb21SWarner Losh { 150592ffb21SWarner Losh struct drm_device *dev = fb_helper->dev; 151592ffb21SWarner Losh struct drm_connector *connector; 152592ffb21SWarner Losh int i; 153592ffb21SWarner Losh 154592ffb21SWarner Losh list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 155592ffb21SWarner Losh struct drm_fb_helper_connector *fb_helper_connector; 156592ffb21SWarner Losh 157592ffb21SWarner Losh fb_helper_connector = malloc(sizeof(struct drm_fb_helper_connector), 158592ffb21SWarner Losh DRM_MEM_KMS, M_NOWAIT | M_ZERO); 159592ffb21SWarner Losh if (!fb_helper_connector) 160592ffb21SWarner Losh goto fail; 161592ffb21SWarner Losh 162592ffb21SWarner Losh fb_helper_connector->connector = connector; 163592ffb21SWarner Losh fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector; 164592ffb21SWarner Losh } 165592ffb21SWarner Losh return 0; 166592ffb21SWarner Losh fail: 167592ffb21SWarner Losh for (i = 0; i < fb_helper->connector_count; i++) { 168592ffb21SWarner Losh free(fb_helper->connector_info[i], DRM_MEM_KMS); 169592ffb21SWarner Losh fb_helper->connector_info[i] = NULL; 170592ffb21SWarner Losh } 171592ffb21SWarner Losh fb_helper->connector_count = 0; 172592ffb21SWarner Losh return -ENOMEM; 173592ffb21SWarner Losh } 174592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors); 175592ffb21SWarner Losh 176592ffb21SWarner Losh static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper) 177592ffb21SWarner Losh { 178592ffb21SWarner Losh struct drm_fb_helper_connector *fb_helper_conn; 179592ffb21SWarner Losh int i; 180592ffb21SWarner Losh 181592ffb21SWarner Losh for (i = 0; i < fb_helper->connector_count; i++) { 182592ffb21SWarner Losh struct drm_cmdline_mode *mode; 183592ffb21SWarner Losh struct drm_connector *connector; 184592ffb21SWarner Losh char *option = NULL; 185592ffb21SWarner Losh 186592ffb21SWarner Losh fb_helper_conn = fb_helper->connector_info[i]; 187592ffb21SWarner Losh connector = fb_helper_conn->connector; 188592ffb21SWarner Losh mode = &fb_helper_conn->cmdline_mode; 189592ffb21SWarner Losh 190592ffb21SWarner Losh /* do something on return - turn off connector maybe */ 191592ffb21SWarner Losh if (fb_get_options(drm_get_connector_name(connector), &option)) 192592ffb21SWarner Losh continue; 193592ffb21SWarner Losh 194592ffb21SWarner Losh if (drm_mode_parse_command_line_for_connector(option, 195592ffb21SWarner Losh connector, 196592ffb21SWarner Losh mode)) { 197592ffb21SWarner Losh if (mode->force) { 198592ffb21SWarner Losh const char *s; 199592ffb21SWarner Losh switch (mode->force) { 200592ffb21SWarner Losh case DRM_FORCE_OFF: 201592ffb21SWarner Losh s = "OFF"; 202592ffb21SWarner Losh break; 203592ffb21SWarner Losh case DRM_FORCE_ON_DIGITAL: 204592ffb21SWarner Losh s = "ON - dig"; 205592ffb21SWarner Losh break; 206592ffb21SWarner Losh default: 207592ffb21SWarner Losh case DRM_FORCE_ON: 208592ffb21SWarner Losh s = "ON"; 209592ffb21SWarner Losh break; 210592ffb21SWarner Losh } 211592ffb21SWarner Losh 212592ffb21SWarner Losh DRM_INFO("forcing %s connector %s\n", 213592ffb21SWarner Losh drm_get_connector_name(connector), s); 214592ffb21SWarner Losh connector->force = mode->force; 215592ffb21SWarner Losh } 216592ffb21SWarner Losh 217592ffb21SWarner Losh DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n", 218592ffb21SWarner Losh drm_get_connector_name(connector), 219592ffb21SWarner Losh mode->xres, mode->yres, 220592ffb21SWarner Losh mode->refresh_specified ? mode->refresh : 60, 221592ffb21SWarner Losh mode->rb ? " reduced blanking" : "", 222592ffb21SWarner Losh mode->margins ? " with margins" : "", 223592ffb21SWarner Losh mode->interlace ? " interlaced" : ""); 224592ffb21SWarner Losh } 225592ffb21SWarner Losh 226592ffb21SWarner Losh freeenv(option); 227592ffb21SWarner Losh } 228592ffb21SWarner Losh return 0; 229592ffb21SWarner Losh } 230592ffb21SWarner Losh 231592ffb21SWarner Losh #if 0 && defined(FREEBSD_NOTYET) 232592ffb21SWarner Losh static void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_helper *helper) 233592ffb21SWarner Losh { 234592ffb21SWarner Losh uint16_t *r_base, *g_base, *b_base; 235592ffb21SWarner Losh int i; 236592ffb21SWarner Losh 237592ffb21SWarner Losh r_base = crtc->gamma_store; 238592ffb21SWarner Losh g_base = r_base + crtc->gamma_size; 239592ffb21SWarner Losh b_base = g_base + crtc->gamma_size; 240592ffb21SWarner Losh 241592ffb21SWarner Losh for (i = 0; i < crtc->gamma_size; i++) 242592ffb21SWarner Losh helper->funcs->gamma_get(crtc, &r_base[i], &g_base[i], &b_base[i], i); 243592ffb21SWarner Losh } 244592ffb21SWarner Losh 245592ffb21SWarner Losh static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) 246592ffb21SWarner Losh { 247592ffb21SWarner Losh uint16_t *r_base, *g_base, *b_base; 248592ffb21SWarner Losh 249592ffb21SWarner Losh if (crtc->funcs->gamma_set == NULL) 250592ffb21SWarner Losh return; 251592ffb21SWarner Losh 252592ffb21SWarner Losh r_base = crtc->gamma_store; 253592ffb21SWarner Losh g_base = r_base + crtc->gamma_size; 254592ffb21SWarner Losh b_base = g_base + crtc->gamma_size; 255592ffb21SWarner Losh 256592ffb21SWarner Losh crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); 257592ffb21SWarner Losh } 258592ffb21SWarner Losh 259592ffb21SWarner Losh int drm_fb_helper_debug_enter(struct fb_info *info) 260592ffb21SWarner Losh { 261592ffb21SWarner Losh struct drm_fb_helper *helper = info->par; 262592ffb21SWarner Losh struct drm_crtc_helper_funcs *funcs; 263592ffb21SWarner Losh int i; 264592ffb21SWarner Losh 265592ffb21SWarner Losh if (list_empty(&kernel_fb_helper_list)) 266592ffb21SWarner Losh return false; 267592ffb21SWarner Losh 268592ffb21SWarner Losh list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { 269592ffb21SWarner Losh for (i = 0; i < helper->crtc_count; i++) { 270592ffb21SWarner Losh struct drm_mode_set *mode_set = 271592ffb21SWarner Losh &helper->crtc_info[i].mode_set; 272592ffb21SWarner Losh 273592ffb21SWarner Losh if (!mode_set->crtc->enabled) 274592ffb21SWarner Losh continue; 275592ffb21SWarner Losh 276592ffb21SWarner Losh funcs = mode_set->crtc->helper_private; 277592ffb21SWarner Losh drm_fb_helper_save_lut_atomic(mode_set->crtc, helper); 278592ffb21SWarner Losh funcs->mode_set_base_atomic(mode_set->crtc, 279592ffb21SWarner Losh mode_set->fb, 280592ffb21SWarner Losh mode_set->x, 281592ffb21SWarner Losh mode_set->y, 282592ffb21SWarner Losh ENTER_ATOMIC_MODE_SET); 283592ffb21SWarner Losh } 284592ffb21SWarner Losh } 285592ffb21SWarner Losh 286592ffb21SWarner Losh return 0; 287592ffb21SWarner Losh } 288592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_debug_enter); 289592ffb21SWarner Losh 290592ffb21SWarner Losh /* Find the real fb for a given fb helper CRTC */ 291592ffb21SWarner Losh static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc) 292592ffb21SWarner Losh { 293592ffb21SWarner Losh struct drm_device *dev = crtc->dev; 294592ffb21SWarner Losh struct drm_crtc *c; 295592ffb21SWarner Losh 296592ffb21SWarner Losh list_for_each_entry(c, &dev->mode_config.crtc_list, head) { 297592ffb21SWarner Losh if (crtc->base.id == c->base.id) 298592ffb21SWarner Losh return c->fb; 299592ffb21SWarner Losh } 300592ffb21SWarner Losh 301592ffb21SWarner Losh return NULL; 302592ffb21SWarner Losh } 303592ffb21SWarner Losh 304592ffb21SWarner Losh int drm_fb_helper_debug_leave(struct fb_info *info) 305592ffb21SWarner Losh { 306592ffb21SWarner Losh struct drm_fb_helper *helper = info->par; 307592ffb21SWarner Losh struct drm_crtc *crtc; 308592ffb21SWarner Losh struct drm_crtc_helper_funcs *funcs; 309592ffb21SWarner Losh struct drm_framebuffer *fb; 310592ffb21SWarner Losh int i; 311592ffb21SWarner Losh 312592ffb21SWarner Losh for (i = 0; i < helper->crtc_count; i++) { 313592ffb21SWarner Losh struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set; 314592ffb21SWarner Losh crtc = mode_set->crtc; 315592ffb21SWarner Losh funcs = crtc->helper_private; 316592ffb21SWarner Losh fb = drm_mode_config_fb(crtc); 317592ffb21SWarner Losh 318592ffb21SWarner Losh if (!crtc->enabled) 319592ffb21SWarner Losh continue; 320592ffb21SWarner Losh 321592ffb21SWarner Losh if (!fb) { 322592ffb21SWarner Losh DRM_ERROR("no fb to restore??\n"); 323592ffb21SWarner Losh continue; 324592ffb21SWarner Losh } 325592ffb21SWarner Losh 326592ffb21SWarner Losh drm_fb_helper_restore_lut_atomic(mode_set->crtc); 327592ffb21SWarner Losh funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x, 328592ffb21SWarner Losh crtc->y, LEAVE_ATOMIC_MODE_SET); 329592ffb21SWarner Losh } 330592ffb21SWarner Losh 331592ffb21SWarner Losh return 0; 332592ffb21SWarner Losh } 333592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_debug_leave); 334592ffb21SWarner Losh #endif /* FREEBSD_NOTYET */ 335592ffb21SWarner Losh 336592ffb21SWarner Losh bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) 337592ffb21SWarner Losh { 338592ffb21SWarner Losh bool error = false; 339592ffb21SWarner Losh int i, ret; 340592ffb21SWarner Losh 341592ffb21SWarner Losh for (i = 0; i < fb_helper->crtc_count; i++) { 342592ffb21SWarner Losh struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; 343592ffb21SWarner Losh ret = mode_set->crtc->funcs->set_config(mode_set); 344592ffb21SWarner Losh if (ret) 345592ffb21SWarner Losh error = true; 346592ffb21SWarner Losh } 347592ffb21SWarner Losh return error; 348592ffb21SWarner Losh } 349592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode); 350592ffb21SWarner Losh 351592ffb21SWarner Losh static bool drm_fb_helper_force_kernel_mode(void) 352592ffb21SWarner Losh { 353592ffb21SWarner Losh bool ret, error = false; 354592ffb21SWarner Losh struct drm_fb_helper *helper; 355592ffb21SWarner Losh 356592ffb21SWarner Losh if (list_empty(&kernel_fb_helper_list)) 357592ffb21SWarner Losh return false; 358592ffb21SWarner Losh 359592ffb21SWarner Losh list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { 360592ffb21SWarner Losh if (helper->dev->switch_power_state == DRM_SWITCH_POWER_OFF) 361592ffb21SWarner Losh continue; 362592ffb21SWarner Losh 363592ffb21SWarner Losh ret = drm_fb_helper_restore_fbdev_mode(helper); 364592ffb21SWarner Losh if (ret) 365592ffb21SWarner Losh error = true; 366592ffb21SWarner Losh } 367592ffb21SWarner Losh return error; 368592ffb21SWarner Losh } 369592ffb21SWarner Losh 370592ffb21SWarner Losh #if 0 && defined(FREEBSD_NOTYET) 371592ffb21SWarner Losh int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed, 372592ffb21SWarner Losh void *panic_str) 373592ffb21SWarner Losh { 374592ffb21SWarner Losh /* 375592ffb21SWarner Losh * It's a waste of time and effort to switch back to text console 376592ffb21SWarner Losh * if the kernel should reboot before panic messages can be seen. 377592ffb21SWarner Losh */ 378592ffb21SWarner Losh if (panic_timeout < 0) 379592ffb21SWarner Losh return 0; 380592ffb21SWarner Losh 381592ffb21SWarner Losh pr_err("panic occurred, switching back to text console\n"); 382592ffb21SWarner Losh return drm_fb_helper_force_kernel_mode(); 383592ffb21SWarner Losh } 384592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_panic); 385592ffb21SWarner Losh 386592ffb21SWarner Losh static struct notifier_block paniced = { 387592ffb21SWarner Losh .notifier_call = drm_fb_helper_panic, 388592ffb21SWarner Losh }; 389592ffb21SWarner Losh #endif /* FREEBSD_NOTYET */ 390592ffb21SWarner Losh 391592ffb21SWarner Losh /** 392592ffb21SWarner Losh * drm_fb_helper_restore - restore the framebuffer console (kernel) config 393592ffb21SWarner Losh * 394592ffb21SWarner Losh * Restore's the kernel's fbcon mode, used for lastclose & panic paths. 395592ffb21SWarner Losh */ 396592ffb21SWarner Losh void drm_fb_helper_restore(void) 397592ffb21SWarner Losh { 398592ffb21SWarner Losh bool ret; 399592ffb21SWarner Losh ret = drm_fb_helper_force_kernel_mode(); 400592ffb21SWarner Losh if (ret == true) 401592ffb21SWarner Losh DRM_ERROR("Failed to restore crtc configuration\n"); 402592ffb21SWarner Losh } 403592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_restore); 404592ffb21SWarner Losh 405592ffb21SWarner Losh #ifdef __linux__ 406592ffb21SWarner Losh #ifdef CONFIG_MAGIC_SYSRQ 407592ffb21SWarner Losh static void drm_fb_helper_restore_work_fn(struct work_struct *ignored) 408592ffb21SWarner Losh { 409592ffb21SWarner Losh drm_fb_helper_restore(); 410592ffb21SWarner Losh } 411592ffb21SWarner Losh static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn); 412592ffb21SWarner Losh 413592ffb21SWarner Losh static void drm_fb_helper_sysrq(int dummy1) 414592ffb21SWarner Losh { 415592ffb21SWarner Losh schedule_work(&drm_fb_helper_restore_work); 416592ffb21SWarner Losh } 417592ffb21SWarner Losh 418592ffb21SWarner Losh static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { 419592ffb21SWarner Losh .handler = drm_fb_helper_sysrq, 420592ffb21SWarner Losh .help_msg = "force-fb(V)", 421592ffb21SWarner Losh .action_msg = "Restore framebuffer console", 422592ffb21SWarner Losh }; 423592ffb21SWarner Losh #else 424592ffb21SWarner Losh static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { }; 425592ffb21SWarner Losh #endif 426592ffb21SWarner Losh #endif 427592ffb21SWarner Losh 428592ffb21SWarner Losh #if 0 && defined(FREEBSD_NOTYET) 429592ffb21SWarner Losh static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) 430592ffb21SWarner Losh { 431592ffb21SWarner Losh struct drm_fb_helper *fb_helper = info->par; 432592ffb21SWarner Losh struct drm_device *dev = fb_helper->dev; 433592ffb21SWarner Losh struct drm_crtc *crtc; 434592ffb21SWarner Losh struct drm_connector *connector; 435592ffb21SWarner Losh int i, j; 436592ffb21SWarner Losh 437592ffb21SWarner Losh /* 438592ffb21SWarner Losh * For each CRTC in this fb, turn the connectors on/off. 439592ffb21SWarner Losh */ 440592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 441592ffb21SWarner Losh for (i = 0; i < fb_helper->crtc_count; i++) { 442592ffb21SWarner Losh crtc = fb_helper->crtc_info[i].mode_set.crtc; 443592ffb21SWarner Losh 444592ffb21SWarner Losh if (!crtc->enabled) 445592ffb21SWarner Losh continue; 446592ffb21SWarner Losh 447592ffb21SWarner Losh /* Walk the connectors & encoders on this fb turning them on/off */ 448592ffb21SWarner Losh for (j = 0; j < fb_helper->connector_count; j++) { 449592ffb21SWarner Losh connector = fb_helper->connector_info[j]->connector; 450592ffb21SWarner Losh connector->funcs->dpms(connector, dpms_mode); 451592ffb21SWarner Losh drm_object_property_set_value(&connector->base, 452592ffb21SWarner Losh dev->mode_config.dpms_property, dpms_mode); 453592ffb21SWarner Losh } 454592ffb21SWarner Losh } 455592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 456592ffb21SWarner Losh } 457592ffb21SWarner Losh 458592ffb21SWarner Losh int drm_fb_helper_blank(int blank, struct fb_info *info) 459592ffb21SWarner Losh { 460592ffb21SWarner Losh switch (blank) { 461592ffb21SWarner Losh /* Display: On; HSync: On, VSync: On */ 462592ffb21SWarner Losh case FB_BLANK_UNBLANK: 463592ffb21SWarner Losh drm_fb_helper_dpms(info, DRM_MODE_DPMS_ON); 464592ffb21SWarner Losh break; 465592ffb21SWarner Losh /* Display: Off; HSync: On, VSync: On */ 466592ffb21SWarner Losh case FB_BLANK_NORMAL: 467592ffb21SWarner Losh drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY); 468592ffb21SWarner Losh break; 469592ffb21SWarner Losh /* Display: Off; HSync: Off, VSync: On */ 470592ffb21SWarner Losh case FB_BLANK_HSYNC_SUSPEND: 471592ffb21SWarner Losh drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY); 472592ffb21SWarner Losh break; 473592ffb21SWarner Losh /* Display: Off; HSync: On, VSync: Off */ 474592ffb21SWarner Losh case FB_BLANK_VSYNC_SUSPEND: 475592ffb21SWarner Losh drm_fb_helper_dpms(info, DRM_MODE_DPMS_SUSPEND); 476592ffb21SWarner Losh break; 477592ffb21SWarner Losh /* Display: Off; HSync: Off, VSync: Off */ 478592ffb21SWarner Losh case FB_BLANK_POWERDOWN: 479592ffb21SWarner Losh drm_fb_helper_dpms(info, DRM_MODE_DPMS_OFF); 480592ffb21SWarner Losh break; 481592ffb21SWarner Losh } 482592ffb21SWarner Losh return 0; 483592ffb21SWarner Losh } 484592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_blank); 485592ffb21SWarner Losh #endif /* FREEBSD_NOTYET */ 486592ffb21SWarner Losh 487592ffb21SWarner Losh static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) 488592ffb21SWarner Losh { 489592ffb21SWarner Losh int i; 490592ffb21SWarner Losh 491592ffb21SWarner Losh for (i = 0; i < helper->connector_count; i++) 492592ffb21SWarner Losh free(helper->connector_info[i], DRM_MEM_KMS); 493592ffb21SWarner Losh free(helper->connector_info, DRM_MEM_KMS); 494592ffb21SWarner Losh for (i = 0; i < helper->crtc_count; i++) { 495592ffb21SWarner Losh free(helper->crtc_info[i].mode_set.connectors, DRM_MEM_KMS); 496592ffb21SWarner Losh if (helper->crtc_info[i].mode_set.mode) 497592ffb21SWarner Losh drm_mode_destroy(helper->dev, helper->crtc_info[i].mode_set.mode); 498592ffb21SWarner Losh } 499592ffb21SWarner Losh free(helper->crtc_info, DRM_MEM_KMS); 500592ffb21SWarner Losh } 501592ffb21SWarner Losh 502592ffb21SWarner Losh int drm_fb_helper_init(struct drm_device *dev, 503592ffb21SWarner Losh struct drm_fb_helper *fb_helper, 504592ffb21SWarner Losh int crtc_count, int max_conn_count) 505592ffb21SWarner Losh { 506592ffb21SWarner Losh struct drm_crtc *crtc; 507592ffb21SWarner Losh int i; 508592ffb21SWarner Losh 509592ffb21SWarner Losh fb_helper->dev = dev; 510592ffb21SWarner Losh 511592ffb21SWarner Losh INIT_LIST_HEAD(&fb_helper->kernel_fb_list); 512592ffb21SWarner Losh 513592ffb21SWarner Losh fb_helper->crtc_info = malloc(crtc_count * sizeof(struct drm_fb_helper_crtc), 514592ffb21SWarner Losh DRM_MEM_KMS, M_NOWAIT | M_ZERO); 515592ffb21SWarner Losh if (!fb_helper->crtc_info) 516592ffb21SWarner Losh return -ENOMEM; 517592ffb21SWarner Losh 518592ffb21SWarner Losh fb_helper->crtc_count = crtc_count; 519592ffb21SWarner Losh fb_helper->connector_info = malloc(dev->mode_config.num_connector * sizeof(struct drm_fb_helper_connector *), 520592ffb21SWarner Losh DRM_MEM_KMS, M_NOWAIT | M_ZERO); 521592ffb21SWarner Losh if (!fb_helper->connector_info) { 522592ffb21SWarner Losh free(fb_helper->crtc_info, DRM_MEM_KMS); 523592ffb21SWarner Losh return -ENOMEM; 524592ffb21SWarner Losh } 525592ffb21SWarner Losh fb_helper->connector_count = 0; 526592ffb21SWarner Losh 527592ffb21SWarner Losh for (i = 0; i < crtc_count; i++) { 528592ffb21SWarner Losh fb_helper->crtc_info[i].mode_set.connectors = 529592ffb21SWarner Losh malloc(max_conn_count * 530592ffb21SWarner Losh sizeof(struct drm_connector *), 531592ffb21SWarner Losh DRM_MEM_KMS, M_NOWAIT | M_ZERO); 532592ffb21SWarner Losh 533592ffb21SWarner Losh if (!fb_helper->crtc_info[i].mode_set.connectors) 534592ffb21SWarner Losh goto out_free; 535592ffb21SWarner Losh fb_helper->crtc_info[i].mode_set.num_connectors = 0; 536592ffb21SWarner Losh } 537592ffb21SWarner Losh 538592ffb21SWarner Losh i = 0; 539592ffb21SWarner Losh list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 540592ffb21SWarner Losh fb_helper->crtc_info[i].mode_set.crtc = crtc; 541592ffb21SWarner Losh i++; 542592ffb21SWarner Losh } 543592ffb21SWarner Losh 544592ffb21SWarner Losh return 0; 545592ffb21SWarner Losh out_free: 546592ffb21SWarner Losh drm_fb_helper_crtc_free(fb_helper); 547592ffb21SWarner Losh return -ENOMEM; 548592ffb21SWarner Losh } 549592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_init); 550592ffb21SWarner Losh 551592ffb21SWarner Losh void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) 552592ffb21SWarner Losh { 553592ffb21SWarner Losh if (!list_empty(&fb_helper->kernel_fb_list)) { 554592ffb21SWarner Losh list_del(&fb_helper->kernel_fb_list); 555592ffb21SWarner Losh #if 0 && defined(FREEBSD_NOTYET) 556592ffb21SWarner Losh if (list_empty(&kernel_fb_helper_list)) { 557592ffb21SWarner Losh pr_info("drm: unregistered panic notifier\n"); 558592ffb21SWarner Losh atomic_notifier_chain_unregister(&panic_notifier_list, 559592ffb21SWarner Losh &paniced); 560592ffb21SWarner Losh unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); 561592ffb21SWarner Losh } 562592ffb21SWarner Losh #endif /* FREEBSD_NOTYET */ 563592ffb21SWarner Losh } 564592ffb21SWarner Losh 565592ffb21SWarner Losh drm_fb_helper_crtc_free(fb_helper); 566592ffb21SWarner Losh 567592ffb21SWarner Losh } 568592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_fini); 569592ffb21SWarner Losh 570592ffb21SWarner Losh #if 0 && defined(FREEBSD_NOTYET) 571592ffb21SWarner Losh static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, 572592ffb21SWarner Losh u16 blue, u16 regno, struct fb_info *info) 573592ffb21SWarner Losh { 574592ffb21SWarner Losh struct drm_fb_helper *fb_helper = info->par; 575592ffb21SWarner Losh struct drm_framebuffer *fb = fb_helper->fb; 576592ffb21SWarner Losh int pindex; 577592ffb21SWarner Losh 578592ffb21SWarner Losh if (info->fix.visual == FB_VISUAL_TRUECOLOR) { 579592ffb21SWarner Losh u32 *palette; 580592ffb21SWarner Losh u32 value; 581592ffb21SWarner Losh /* place color in pseudopalette */ 582592ffb21SWarner Losh if (regno > 16) 583592ffb21SWarner Losh return -EINVAL; 584592ffb21SWarner Losh palette = (u32 *)info->pseudo_palette; 585592ffb21SWarner Losh red >>= (16 - info->var.red.length); 586592ffb21SWarner Losh green >>= (16 - info->var.green.length); 587592ffb21SWarner Losh blue >>= (16 - info->var.blue.length); 588592ffb21SWarner Losh value = (red << info->var.red.offset) | 589592ffb21SWarner Losh (green << info->var.green.offset) | 590592ffb21SWarner Losh (blue << info->var.blue.offset); 591592ffb21SWarner Losh if (info->var.transp.length > 0) { 592592ffb21SWarner Losh u32 mask = (1 << info->var.transp.length) - 1; 593592ffb21SWarner Losh mask <<= info->var.transp.offset; 594592ffb21SWarner Losh value |= mask; 595592ffb21SWarner Losh } 596592ffb21SWarner Losh palette[regno] = value; 597592ffb21SWarner Losh return 0; 598592ffb21SWarner Losh } 599592ffb21SWarner Losh 600592ffb21SWarner Losh pindex = regno; 601592ffb21SWarner Losh 602592ffb21SWarner Losh if (fb->bits_per_pixel == 16) { 603592ffb21SWarner Losh pindex = regno << 3; 604592ffb21SWarner Losh 605592ffb21SWarner Losh if (fb->depth == 16 && regno > 63) 606592ffb21SWarner Losh return -EINVAL; 607592ffb21SWarner Losh if (fb->depth == 15 && regno > 31) 608592ffb21SWarner Losh return -EINVAL; 609592ffb21SWarner Losh 610592ffb21SWarner Losh if (fb->depth == 16) { 611592ffb21SWarner Losh u16 r, g, b; 612592ffb21SWarner Losh int i; 613592ffb21SWarner Losh if (regno < 32) { 614592ffb21SWarner Losh for (i = 0; i < 8; i++) 615592ffb21SWarner Losh fb_helper->funcs->gamma_set(crtc, red, 616592ffb21SWarner Losh green, blue, pindex + i); 617592ffb21SWarner Losh } 618592ffb21SWarner Losh 619592ffb21SWarner Losh fb_helper->funcs->gamma_get(crtc, &r, 620592ffb21SWarner Losh &g, &b, 621592ffb21SWarner Losh pindex >> 1); 622592ffb21SWarner Losh 623592ffb21SWarner Losh for (i = 0; i < 4; i++) 624592ffb21SWarner Losh fb_helper->funcs->gamma_set(crtc, r, 625592ffb21SWarner Losh green, b, 626592ffb21SWarner Losh (pindex >> 1) + i); 627592ffb21SWarner Losh } 628592ffb21SWarner Losh } 629592ffb21SWarner Losh 630592ffb21SWarner Losh if (fb->depth != 16) 631592ffb21SWarner Losh fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex); 632592ffb21SWarner Losh return 0; 633592ffb21SWarner Losh } 634592ffb21SWarner Losh 635592ffb21SWarner Losh int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) 636592ffb21SWarner Losh { 637592ffb21SWarner Losh struct drm_fb_helper *fb_helper = info->par; 638592ffb21SWarner Losh struct drm_crtc_helper_funcs *crtc_funcs; 639592ffb21SWarner Losh u16 *red, *green, *blue, *transp; 640592ffb21SWarner Losh struct drm_crtc *crtc; 641592ffb21SWarner Losh int i, j, rc = 0; 642592ffb21SWarner Losh int start; 643592ffb21SWarner Losh 644592ffb21SWarner Losh for (i = 0; i < fb_helper->crtc_count; i++) { 645592ffb21SWarner Losh crtc = fb_helper->crtc_info[i].mode_set.crtc; 646592ffb21SWarner Losh crtc_funcs = crtc->helper_private; 647592ffb21SWarner Losh 648592ffb21SWarner Losh red = cmap->red; 649592ffb21SWarner Losh green = cmap->green; 650592ffb21SWarner Losh blue = cmap->blue; 651592ffb21SWarner Losh transp = cmap->transp; 652592ffb21SWarner Losh start = cmap->start; 653592ffb21SWarner Losh 654592ffb21SWarner Losh for (j = 0; j < cmap->len; j++) { 655592ffb21SWarner Losh u16 hred, hgreen, hblue, htransp = 0xffff; 656592ffb21SWarner Losh 657592ffb21SWarner Losh hred = *red++; 658592ffb21SWarner Losh hgreen = *green++; 659592ffb21SWarner Losh hblue = *blue++; 660592ffb21SWarner Losh 661592ffb21SWarner Losh if (transp) 662592ffb21SWarner Losh htransp = *transp++; 663592ffb21SWarner Losh 664592ffb21SWarner Losh rc = setcolreg(crtc, hred, hgreen, hblue, start++, info); 665592ffb21SWarner Losh if (rc) 666592ffb21SWarner Losh return rc; 667592ffb21SWarner Losh } 668592ffb21SWarner Losh crtc_funcs->load_lut(crtc); 669592ffb21SWarner Losh } 670592ffb21SWarner Losh return rc; 671592ffb21SWarner Losh } 672592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_setcmap); 673592ffb21SWarner Losh 674592ffb21SWarner Losh int drm_fb_helper_check_var(struct fb_var_screeninfo *var, 675592ffb21SWarner Losh struct fb_info *info) 676592ffb21SWarner Losh { 677592ffb21SWarner Losh struct drm_fb_helper *fb_helper = info->par; 678592ffb21SWarner Losh struct drm_framebuffer *fb = fb_helper->fb; 679592ffb21SWarner Losh int depth; 680592ffb21SWarner Losh 681592ffb21SWarner Losh if (var->pixclock != 0 || in_dbg_master()) 682592ffb21SWarner Losh return -EINVAL; 683592ffb21SWarner Losh 684592ffb21SWarner Losh /* Need to resize the fb object !!! */ 685592ffb21SWarner Losh if (var->bits_per_pixel > fb->bits_per_pixel || 686592ffb21SWarner Losh var->xres > fb->width || var->yres > fb->height || 687592ffb21SWarner Losh var->xres_virtual > fb->width || var->yres_virtual > fb->height) { 688592ffb21SWarner Losh DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb " 689592ffb21SWarner Losh "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n", 690592ffb21SWarner Losh var->xres, var->yres, var->bits_per_pixel, 691592ffb21SWarner Losh var->xres_virtual, var->yres_virtual, 692592ffb21SWarner Losh fb->width, fb->height, fb->bits_per_pixel); 693592ffb21SWarner Losh return -EINVAL; 694592ffb21SWarner Losh } 695592ffb21SWarner Losh 696592ffb21SWarner Losh switch (var->bits_per_pixel) { 697592ffb21SWarner Losh case 16: 698592ffb21SWarner Losh depth = (var->green.length == 6) ? 16 : 15; 699592ffb21SWarner Losh break; 700592ffb21SWarner Losh case 32: 701592ffb21SWarner Losh depth = (var->transp.length > 0) ? 32 : 24; 702592ffb21SWarner Losh break; 703592ffb21SWarner Losh default: 704592ffb21SWarner Losh depth = var->bits_per_pixel; 705592ffb21SWarner Losh break; 706592ffb21SWarner Losh } 707592ffb21SWarner Losh 708592ffb21SWarner Losh switch (depth) { 709592ffb21SWarner Losh case 8: 710592ffb21SWarner Losh var->red.offset = 0; 711592ffb21SWarner Losh var->green.offset = 0; 712592ffb21SWarner Losh var->blue.offset = 0; 713592ffb21SWarner Losh var->red.length = 8; 714592ffb21SWarner Losh var->green.length = 8; 715592ffb21SWarner Losh var->blue.length = 8; 716592ffb21SWarner Losh var->transp.length = 0; 717592ffb21SWarner Losh var->transp.offset = 0; 718592ffb21SWarner Losh break; 719592ffb21SWarner Losh case 15: 720592ffb21SWarner Losh var->red.offset = 10; 721592ffb21SWarner Losh var->green.offset = 5; 722592ffb21SWarner Losh var->blue.offset = 0; 723592ffb21SWarner Losh var->red.length = 5; 724592ffb21SWarner Losh var->green.length = 5; 725592ffb21SWarner Losh var->blue.length = 5; 726592ffb21SWarner Losh var->transp.length = 1; 727592ffb21SWarner Losh var->transp.offset = 15; 728592ffb21SWarner Losh break; 729592ffb21SWarner Losh case 16: 730592ffb21SWarner Losh var->red.offset = 11; 731592ffb21SWarner Losh var->green.offset = 5; 732592ffb21SWarner Losh var->blue.offset = 0; 733592ffb21SWarner Losh var->red.length = 5; 734592ffb21SWarner Losh var->green.length = 6; 735592ffb21SWarner Losh var->blue.length = 5; 736592ffb21SWarner Losh var->transp.length = 0; 737592ffb21SWarner Losh var->transp.offset = 0; 738592ffb21SWarner Losh break; 739592ffb21SWarner Losh case 24: 740592ffb21SWarner Losh var->red.offset = 16; 741592ffb21SWarner Losh var->green.offset = 8; 742592ffb21SWarner Losh var->blue.offset = 0; 743592ffb21SWarner Losh var->red.length = 8; 744592ffb21SWarner Losh var->green.length = 8; 745592ffb21SWarner Losh var->blue.length = 8; 746592ffb21SWarner Losh var->transp.length = 0; 747592ffb21SWarner Losh var->transp.offset = 0; 748592ffb21SWarner Losh break; 749592ffb21SWarner Losh case 32: 750592ffb21SWarner Losh var->red.offset = 16; 751592ffb21SWarner Losh var->green.offset = 8; 752592ffb21SWarner Losh var->blue.offset = 0; 753592ffb21SWarner Losh var->red.length = 8; 754592ffb21SWarner Losh var->green.length = 8; 755592ffb21SWarner Losh var->blue.length = 8; 756592ffb21SWarner Losh var->transp.length = 8; 757592ffb21SWarner Losh var->transp.offset = 24; 758592ffb21SWarner Losh break; 759592ffb21SWarner Losh default: 760592ffb21SWarner Losh return -EINVAL; 761592ffb21SWarner Losh } 762592ffb21SWarner Losh return 0; 763592ffb21SWarner Losh } 764592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_check_var); 765592ffb21SWarner Losh 766592ffb21SWarner Losh /* this will let fbcon do the mode init */ 767592ffb21SWarner Losh int drm_fb_helper_set_par(struct fb_info *info) 768592ffb21SWarner Losh { 769592ffb21SWarner Losh struct drm_fb_helper *fb_helper = info->par; 770592ffb21SWarner Losh struct drm_device *dev = fb_helper->dev; 771592ffb21SWarner Losh struct fb_var_screeninfo *var = &info->var; 772592ffb21SWarner Losh struct drm_crtc *crtc; 773592ffb21SWarner Losh int ret; 774592ffb21SWarner Losh int i; 775592ffb21SWarner Losh 776592ffb21SWarner Losh if (var->pixclock != 0) { 777592ffb21SWarner Losh DRM_ERROR("PIXEL CLOCK SET\n"); 778592ffb21SWarner Losh return -EINVAL; 779592ffb21SWarner Losh } 780592ffb21SWarner Losh 781592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 782592ffb21SWarner Losh for (i = 0; i < fb_helper->crtc_count; i++) { 783592ffb21SWarner Losh crtc = fb_helper->crtc_info[i].mode_set.crtc; 784592ffb21SWarner Losh ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set); 785592ffb21SWarner Losh if (ret) { 786592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 787592ffb21SWarner Losh return ret; 788592ffb21SWarner Losh } 789592ffb21SWarner Losh } 790592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 791592ffb21SWarner Losh 792592ffb21SWarner Losh if (fb_helper->delayed_hotplug) { 793592ffb21SWarner Losh fb_helper->delayed_hotplug = false; 794592ffb21SWarner Losh drm_fb_helper_hotplug_event(fb_helper); 795592ffb21SWarner Losh } 796592ffb21SWarner Losh return 0; 797592ffb21SWarner Losh } 798592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_set_par); 799592ffb21SWarner Losh 800592ffb21SWarner Losh int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, 801592ffb21SWarner Losh struct fb_info *info) 802592ffb21SWarner Losh { 803592ffb21SWarner Losh struct drm_fb_helper *fb_helper = info->par; 804592ffb21SWarner Losh struct drm_device *dev = fb_helper->dev; 805592ffb21SWarner Losh struct drm_mode_set *modeset; 806592ffb21SWarner Losh struct drm_crtc *crtc; 807592ffb21SWarner Losh int ret = 0; 808592ffb21SWarner Losh int i; 809592ffb21SWarner Losh 810592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 811592ffb21SWarner Losh for (i = 0; i < fb_helper->crtc_count; i++) { 812592ffb21SWarner Losh crtc = fb_helper->crtc_info[i].mode_set.crtc; 813592ffb21SWarner Losh 814592ffb21SWarner Losh modeset = &fb_helper->crtc_info[i].mode_set; 815592ffb21SWarner Losh 816592ffb21SWarner Losh modeset->x = var->xoffset; 817592ffb21SWarner Losh modeset->y = var->yoffset; 818592ffb21SWarner Losh 819592ffb21SWarner Losh if (modeset->num_connectors) { 820592ffb21SWarner Losh ret = crtc->funcs->set_config(modeset); 821592ffb21SWarner Losh if (!ret) { 822592ffb21SWarner Losh info->var.xoffset = var->xoffset; 823592ffb21SWarner Losh info->var.yoffset = var->yoffset; 824592ffb21SWarner Losh } 825592ffb21SWarner Losh } 826592ffb21SWarner Losh } 827592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 828592ffb21SWarner Losh return ret; 829592ffb21SWarner Losh } 830592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_pan_display); 831592ffb21SWarner Losh #endif /* FREEBSD_NOTYET */ 832592ffb21SWarner Losh 833592ffb21SWarner Losh int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, 834592ffb21SWarner Losh int preferred_bpp) 835592ffb21SWarner Losh { 836592ffb21SWarner Losh int new_fb = 0; 837592ffb21SWarner Losh int crtc_count = 0; 838592ffb21SWarner Losh int i; 839592ffb21SWarner Losh struct fb_info *info; 840592ffb21SWarner Losh struct drm_fb_helper_surface_size sizes; 841592ffb21SWarner Losh int gamma_size = 0; 842592ffb21SWarner Losh #if defined(__FreeBSD__) 843592ffb21SWarner Losh struct drm_crtc *crtc; 844592ffb21SWarner Losh struct drm_device *dev; 845592ffb21SWarner Losh int ret; 846592ffb21SWarner Losh device_t kdev; 847592ffb21SWarner Losh #endif 848592ffb21SWarner Losh 849592ffb21SWarner Losh memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size)); 850592ffb21SWarner Losh sizes.surface_depth = 24; 851592ffb21SWarner Losh sizes.surface_bpp = 32; 852592ffb21SWarner Losh sizes.fb_width = (unsigned)-1; 853592ffb21SWarner Losh sizes.fb_height = (unsigned)-1; 854592ffb21SWarner Losh 855592ffb21SWarner Losh /* if driver picks 8 or 16 by default use that 856592ffb21SWarner Losh for both depth/bpp */ 857592ffb21SWarner Losh if (preferred_bpp != sizes.surface_bpp) 858592ffb21SWarner Losh sizes.surface_depth = sizes.surface_bpp = preferred_bpp; 859592ffb21SWarner Losh 860592ffb21SWarner Losh /* first up get a count of crtcs now in use and new min/maxes width/heights */ 861592ffb21SWarner Losh for (i = 0; i < fb_helper->connector_count; i++) { 862592ffb21SWarner Losh struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i]; 863592ffb21SWarner Losh struct drm_cmdline_mode *cmdline_mode; 864592ffb21SWarner Losh 865592ffb21SWarner Losh cmdline_mode = &fb_helper_conn->cmdline_mode; 866592ffb21SWarner Losh 867592ffb21SWarner Losh if (cmdline_mode->bpp_specified) { 868592ffb21SWarner Losh switch (cmdline_mode->bpp) { 869592ffb21SWarner Losh case 8: 870592ffb21SWarner Losh sizes.surface_depth = sizes.surface_bpp = 8; 871592ffb21SWarner Losh break; 872592ffb21SWarner Losh case 15: 873592ffb21SWarner Losh sizes.surface_depth = 15; 874592ffb21SWarner Losh sizes.surface_bpp = 16; 875592ffb21SWarner Losh break; 876592ffb21SWarner Losh case 16: 877592ffb21SWarner Losh sizes.surface_depth = sizes.surface_bpp = 16; 878592ffb21SWarner Losh break; 879592ffb21SWarner Losh case 24: 880592ffb21SWarner Losh sizes.surface_depth = sizes.surface_bpp = 24; 881592ffb21SWarner Losh break; 882592ffb21SWarner Losh case 32: 883592ffb21SWarner Losh sizes.surface_depth = 24; 884592ffb21SWarner Losh sizes.surface_bpp = 32; 885592ffb21SWarner Losh break; 886592ffb21SWarner Losh } 887592ffb21SWarner Losh break; 888592ffb21SWarner Losh } 889592ffb21SWarner Losh } 890592ffb21SWarner Losh 891592ffb21SWarner Losh crtc_count = 0; 892592ffb21SWarner Losh for (i = 0; i < fb_helper->crtc_count; i++) { 893592ffb21SWarner Losh struct drm_display_mode *desired_mode; 894592ffb21SWarner Losh desired_mode = fb_helper->crtc_info[i].desired_mode; 895592ffb21SWarner Losh 896592ffb21SWarner Losh if (desired_mode) { 897592ffb21SWarner Losh if (gamma_size == 0) 898592ffb21SWarner Losh gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size; 899592ffb21SWarner Losh if (desired_mode->hdisplay < sizes.fb_width) 900592ffb21SWarner Losh sizes.fb_width = desired_mode->hdisplay; 901592ffb21SWarner Losh if (desired_mode->vdisplay < sizes.fb_height) 902592ffb21SWarner Losh sizes.fb_height = desired_mode->vdisplay; 903592ffb21SWarner Losh if (desired_mode->hdisplay > sizes.surface_width) 904592ffb21SWarner Losh sizes.surface_width = desired_mode->hdisplay; 905592ffb21SWarner Losh if (desired_mode->vdisplay > sizes.surface_height) 906592ffb21SWarner Losh sizes.surface_height = desired_mode->vdisplay; 907592ffb21SWarner Losh crtc_count++; 908592ffb21SWarner Losh } 909592ffb21SWarner Losh } 910592ffb21SWarner Losh 911592ffb21SWarner Losh if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) { 912592ffb21SWarner Losh /* hmm everyone went away - assume VGA cable just fell out 913592ffb21SWarner Losh and will come back later. */ 914592ffb21SWarner Losh DRM_INFO("Cannot find any crtc or sizes - going 1024x768\n"); 915592ffb21SWarner Losh sizes.fb_width = sizes.surface_width = 1024; 916592ffb21SWarner Losh sizes.fb_height = sizes.surface_height = 768; 917592ffb21SWarner Losh } 918592ffb21SWarner Losh 919592ffb21SWarner Losh /* push down into drivers */ 920592ffb21SWarner Losh new_fb = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes); 921592ffb21SWarner Losh if (new_fb < 0) 922592ffb21SWarner Losh return new_fb; 923592ffb21SWarner Losh 924592ffb21SWarner Losh info = fb_helper->fbdev; 925592ffb21SWarner Losh 926592ffb21SWarner Losh kdev = fb_helper->dev->dev; 927592ffb21SWarner Losh info->fb_video_dev = device_get_parent(kdev); 928592ffb21SWarner Losh 929592ffb21SWarner Losh /* set the fb pointer */ 930592ffb21SWarner Losh for (i = 0; i < fb_helper->crtc_count; i++) 931592ffb21SWarner Losh fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; 932592ffb21SWarner Losh 933592ffb21SWarner Losh #if defined(__FreeBSD__) 934592ffb21SWarner Losh if (new_fb) { 935592ffb21SWarner Losh int ret; 936592ffb21SWarner Losh 937592ffb21SWarner Losh info->fb_fbd_dev = device_add_child(kdev, "fbd", 938592ffb21SWarner Losh device_get_unit(kdev)); 939592ffb21SWarner Losh if (info->fb_fbd_dev != NULL) 940592ffb21SWarner Losh ret = device_probe_and_attach(info->fb_fbd_dev); 941592ffb21SWarner Losh else 942592ffb21SWarner Losh ret = ENODEV; 943592ffb21SWarner Losh #ifdef DEV_VT 944592ffb21SWarner Losh if (ret != 0) 945592ffb21SWarner Losh DRM_ERROR("Failed to attach fbd device: %d\n", ret); 946592ffb21SWarner Losh #endif 947592ffb21SWarner Losh } else { 948592ffb21SWarner Losh /* Modified version of drm_fb_helper_set_par() */ 949592ffb21SWarner Losh dev = fb_helper->dev; 950592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 951592ffb21SWarner Losh for (i = 0; i < fb_helper->crtc_count; i++) { 952592ffb21SWarner Losh crtc = fb_helper->crtc_info[i].mode_set.crtc; 953592ffb21SWarner Losh ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set); 954592ffb21SWarner Losh if (ret) { 955592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 956592ffb21SWarner Losh return ret; 957592ffb21SWarner Losh } 958592ffb21SWarner Losh } 959592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 960592ffb21SWarner Losh 961592ffb21SWarner Losh if (fb_helper->delayed_hotplug) { 962592ffb21SWarner Losh fb_helper->delayed_hotplug = false; 963592ffb21SWarner Losh drm_fb_helper_hotplug_event(fb_helper); 964592ffb21SWarner Losh } 965592ffb21SWarner Losh } 966592ffb21SWarner Losh #else 967592ffb21SWarner Losh if (new_fb) { 968592ffb21SWarner Losh info->var.pixclock = 0; 969592ffb21SWarner Losh if (register_framebuffer(info) < 0) 970592ffb21SWarner Losh return -EINVAL; 971592ffb21SWarner Losh 972592ffb21SWarner Losh dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n", 973592ffb21SWarner Losh info->node, info->fix.id); 974592ffb21SWarner Losh 975592ffb21SWarner Losh } else { 976592ffb21SWarner Losh drm_fb_helper_set_par(info); 977592ffb21SWarner Losh } 978592ffb21SWarner Losh #endif 979592ffb21SWarner Losh 980592ffb21SWarner Losh #if 0 && defined(FREEBSD_NOTYET) 981592ffb21SWarner Losh /* Switch back to kernel console on panic */ 982592ffb21SWarner Losh /* multi card linked list maybe */ 983592ffb21SWarner Losh if (list_empty(&kernel_fb_helper_list)) { 984592ffb21SWarner Losh dev_info(fb_helper->dev->dev, "registered panic notifier\n"); 985592ffb21SWarner Losh atomic_notifier_chain_register(&panic_notifier_list, 986592ffb21SWarner Losh &paniced); 987592ffb21SWarner Losh register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); 988592ffb21SWarner Losh } 989592ffb21SWarner Losh #endif /* FREEBSD_NOTYET */ 990592ffb21SWarner Losh if (new_fb) 991592ffb21SWarner Losh list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list); 992592ffb21SWarner Losh 993592ffb21SWarner Losh return 0; 994592ffb21SWarner Losh } 995592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_single_fb_probe); 996592ffb21SWarner Losh 997592ffb21SWarner Losh void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, 998592ffb21SWarner Losh uint32_t depth) 999592ffb21SWarner Losh { 1000592ffb21SWarner Losh info->fb_stride = pitch; 1001592ffb21SWarner Losh 1002592ffb21SWarner Losh return; 1003592ffb21SWarner Losh } 1004592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_fill_fix); 1005592ffb21SWarner Losh 1006592ffb21SWarner Losh void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper, 1007592ffb21SWarner Losh uint32_t fb_width, uint32_t fb_height) 1008592ffb21SWarner Losh { 1009592ffb21SWarner Losh struct drm_framebuffer *fb = fb_helper->fb; 1010592ffb21SWarner Losh struct vt_kms_softc *sc; 1011592ffb21SWarner Losh 1012592ffb21SWarner Losh info->fb_name = device_get_nameunit(fb_helper->dev->dev); 1013592ffb21SWarner Losh info->fb_width = fb->width; 1014592ffb21SWarner Losh info->fb_height = fb->height; 1015592ffb21SWarner Losh info->fb_depth = fb->bits_per_pixel; 1016592ffb21SWarner Losh 1017592ffb21SWarner Losh sc = (struct vt_kms_softc *)info->fb_priv; 1018592ffb21SWarner Losh sc->fb_helper = fb_helper; 1019592ffb21SWarner Losh } 1020592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_fill_var); 1021592ffb21SWarner Losh 1022592ffb21SWarner Losh static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper, 1023592ffb21SWarner Losh uint32_t maxX, 1024592ffb21SWarner Losh uint32_t maxY) 1025592ffb21SWarner Losh { 1026592ffb21SWarner Losh struct drm_connector *connector; 1027592ffb21SWarner Losh int count = 0; 1028592ffb21SWarner Losh int i; 1029592ffb21SWarner Losh 1030592ffb21SWarner Losh for (i = 0; i < fb_helper->connector_count; i++) { 1031592ffb21SWarner Losh connector = fb_helper->connector_info[i]->connector; 1032592ffb21SWarner Losh count += connector->funcs->fill_modes(connector, maxX, maxY); 1033592ffb21SWarner Losh } 1034592ffb21SWarner Losh 1035592ffb21SWarner Losh return count; 1036592ffb21SWarner Losh } 1037592ffb21SWarner Losh 1038592ffb21SWarner Losh static struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height) 1039592ffb21SWarner Losh { 1040592ffb21SWarner Losh struct drm_display_mode *mode; 1041592ffb21SWarner Losh 1042592ffb21SWarner Losh list_for_each_entry(mode, &fb_connector->connector->modes, head) { 1043592ffb21SWarner Losh if (drm_mode_width(mode) > width || 1044592ffb21SWarner Losh drm_mode_height(mode) > height) 1045592ffb21SWarner Losh continue; 1046592ffb21SWarner Losh if (mode->type & DRM_MODE_TYPE_PREFERRED) 1047592ffb21SWarner Losh return mode; 1048592ffb21SWarner Losh } 1049592ffb21SWarner Losh return NULL; 1050592ffb21SWarner Losh } 1051592ffb21SWarner Losh 1052592ffb21SWarner Losh static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector) 1053592ffb21SWarner Losh { 1054592ffb21SWarner Losh struct drm_cmdline_mode *cmdline_mode; 1055592ffb21SWarner Losh cmdline_mode = &fb_connector->cmdline_mode; 1056592ffb21SWarner Losh return cmdline_mode->specified; 1057592ffb21SWarner Losh } 1058592ffb21SWarner Losh 1059592ffb21SWarner Losh static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn, 1060592ffb21SWarner Losh int width, int height) 1061592ffb21SWarner Losh { 1062592ffb21SWarner Losh struct drm_cmdline_mode *cmdline_mode; 1063592ffb21SWarner Losh struct drm_display_mode *mode = NULL; 1064592ffb21SWarner Losh 1065592ffb21SWarner Losh cmdline_mode = &fb_helper_conn->cmdline_mode; 1066592ffb21SWarner Losh if (cmdline_mode->specified == false) 1067592ffb21SWarner Losh return mode; 1068592ffb21SWarner Losh 1069592ffb21SWarner Losh /* attempt to find a matching mode in the list of modes 1070592ffb21SWarner Losh * we have gotten so far, if not add a CVT mode that conforms 1071592ffb21SWarner Losh */ 1072592ffb21SWarner Losh if (cmdline_mode->rb || cmdline_mode->margins) 1073592ffb21SWarner Losh goto create_mode; 1074592ffb21SWarner Losh 1075592ffb21SWarner Losh list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { 1076592ffb21SWarner Losh /* check width/height */ 1077592ffb21SWarner Losh if (mode->hdisplay != cmdline_mode->xres || 1078592ffb21SWarner Losh mode->vdisplay != cmdline_mode->yres) 1079592ffb21SWarner Losh continue; 1080592ffb21SWarner Losh 1081592ffb21SWarner Losh if (cmdline_mode->refresh_specified) { 1082592ffb21SWarner Losh if (mode->vrefresh != cmdline_mode->refresh) 1083592ffb21SWarner Losh continue; 1084592ffb21SWarner Losh } 1085592ffb21SWarner Losh 1086592ffb21SWarner Losh if (cmdline_mode->interlace) { 1087592ffb21SWarner Losh if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) 1088592ffb21SWarner Losh continue; 1089592ffb21SWarner Losh } 1090592ffb21SWarner Losh return mode; 1091592ffb21SWarner Losh } 1092592ffb21SWarner Losh 1093592ffb21SWarner Losh create_mode: 1094592ffb21SWarner Losh mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev, 1095592ffb21SWarner Losh cmdline_mode); 1096592ffb21SWarner Losh list_add(&mode->head, &fb_helper_conn->connector->modes); 1097592ffb21SWarner Losh return mode; 1098592ffb21SWarner Losh } 1099592ffb21SWarner Losh 1100592ffb21SWarner Losh static bool drm_connector_enabled(struct drm_connector *connector, bool strict) 1101592ffb21SWarner Losh { 1102592ffb21SWarner Losh bool enable; 1103592ffb21SWarner Losh 1104592ffb21SWarner Losh if (strict) 1105592ffb21SWarner Losh enable = connector->status == connector_status_connected; 1106592ffb21SWarner Losh else 1107592ffb21SWarner Losh enable = connector->status != connector_status_disconnected; 1108592ffb21SWarner Losh 1109592ffb21SWarner Losh return enable; 1110592ffb21SWarner Losh } 1111592ffb21SWarner Losh 1112592ffb21SWarner Losh static void drm_enable_connectors(struct drm_fb_helper *fb_helper, 1113592ffb21SWarner Losh bool *enabled) 1114592ffb21SWarner Losh { 1115592ffb21SWarner Losh bool any_enabled = false; 1116592ffb21SWarner Losh struct drm_connector *connector; 1117592ffb21SWarner Losh int i = 0; 1118592ffb21SWarner Losh 1119592ffb21SWarner Losh for (i = 0; i < fb_helper->connector_count; i++) { 1120592ffb21SWarner Losh connector = fb_helper->connector_info[i]->connector; 1121592ffb21SWarner Losh enabled[i] = drm_connector_enabled(connector, true); 1122592ffb21SWarner Losh DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, 1123592ffb21SWarner Losh enabled[i] ? "yes" : "no"); 1124592ffb21SWarner Losh any_enabled |= enabled[i]; 1125592ffb21SWarner Losh } 1126592ffb21SWarner Losh 1127592ffb21SWarner Losh if (any_enabled) 1128592ffb21SWarner Losh return; 1129592ffb21SWarner Losh 1130592ffb21SWarner Losh for (i = 0; i < fb_helper->connector_count; i++) { 1131592ffb21SWarner Losh connector = fb_helper->connector_info[i]->connector; 1132592ffb21SWarner Losh enabled[i] = drm_connector_enabled(connector, false); 1133592ffb21SWarner Losh } 1134592ffb21SWarner Losh } 1135592ffb21SWarner Losh 1136592ffb21SWarner Losh static bool drm_target_cloned(struct drm_fb_helper *fb_helper, 1137592ffb21SWarner Losh struct drm_display_mode **modes, 1138592ffb21SWarner Losh bool *enabled, int width, int height) 1139592ffb21SWarner Losh { 1140592ffb21SWarner Losh int count, i, j; 1141592ffb21SWarner Losh bool can_clone = false; 1142592ffb21SWarner Losh struct drm_fb_helper_connector *fb_helper_conn; 1143592ffb21SWarner Losh struct drm_display_mode *dmt_mode, *mode; 1144592ffb21SWarner Losh 1145592ffb21SWarner Losh /* only contemplate cloning in the single crtc case */ 1146592ffb21SWarner Losh if (fb_helper->crtc_count > 1) 1147592ffb21SWarner Losh return false; 1148592ffb21SWarner Losh 1149592ffb21SWarner Losh count = 0; 1150592ffb21SWarner Losh for (i = 0; i < fb_helper->connector_count; i++) { 1151592ffb21SWarner Losh if (enabled[i]) 1152592ffb21SWarner Losh count++; 1153592ffb21SWarner Losh } 1154592ffb21SWarner Losh 1155592ffb21SWarner Losh /* only contemplate cloning if more than one connector is enabled */ 1156592ffb21SWarner Losh if (count <= 1) 1157592ffb21SWarner Losh return false; 1158592ffb21SWarner Losh 1159592ffb21SWarner Losh /* check the command line or if nothing common pick 1024x768 */ 1160592ffb21SWarner Losh can_clone = true; 1161592ffb21SWarner Losh for (i = 0; i < fb_helper->connector_count; i++) { 1162592ffb21SWarner Losh if (!enabled[i]) 1163592ffb21SWarner Losh continue; 1164592ffb21SWarner Losh fb_helper_conn = fb_helper->connector_info[i]; 1165592ffb21SWarner Losh modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height); 1166592ffb21SWarner Losh if (!modes[i]) { 1167592ffb21SWarner Losh can_clone = false; 1168592ffb21SWarner Losh break; 1169592ffb21SWarner Losh } 1170592ffb21SWarner Losh for (j = 0; j < i; j++) { 1171592ffb21SWarner Losh if (!enabled[j]) 1172592ffb21SWarner Losh continue; 1173592ffb21SWarner Losh if (!drm_mode_equal(modes[j], modes[i])) 1174592ffb21SWarner Losh can_clone = false; 1175592ffb21SWarner Losh } 1176592ffb21SWarner Losh } 1177592ffb21SWarner Losh 1178592ffb21SWarner Losh if (can_clone) { 1179592ffb21SWarner Losh DRM_DEBUG_KMS("can clone using command line\n"); 1180592ffb21SWarner Losh return true; 1181592ffb21SWarner Losh } 1182592ffb21SWarner Losh 1183592ffb21SWarner Losh /* try and find a 1024x768 mode on each connector */ 1184592ffb21SWarner Losh can_clone = true; 1185592ffb21SWarner Losh dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false); 1186592ffb21SWarner Losh 1187592ffb21SWarner Losh for (i = 0; i < fb_helper->connector_count; i++) { 1188592ffb21SWarner Losh 1189592ffb21SWarner Losh if (!enabled[i]) 1190592ffb21SWarner Losh continue; 1191592ffb21SWarner Losh 1192592ffb21SWarner Losh fb_helper_conn = fb_helper->connector_info[i]; 1193592ffb21SWarner Losh list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { 1194592ffb21SWarner Losh if (drm_mode_equal(mode, dmt_mode)) 1195592ffb21SWarner Losh modes[i] = mode; 1196592ffb21SWarner Losh } 1197592ffb21SWarner Losh if (!modes[i]) 1198592ffb21SWarner Losh can_clone = false; 1199592ffb21SWarner Losh } 1200592ffb21SWarner Losh 1201592ffb21SWarner Losh if (can_clone) { 1202592ffb21SWarner Losh DRM_DEBUG_KMS("can clone using 1024x768\n"); 1203592ffb21SWarner Losh return true; 1204592ffb21SWarner Losh } 1205592ffb21SWarner Losh DRM_INFO("kms: can't enable cloning when we probably wanted to.\n"); 1206592ffb21SWarner Losh return false; 1207592ffb21SWarner Losh } 1208592ffb21SWarner Losh 1209592ffb21SWarner Losh static bool drm_target_preferred(struct drm_fb_helper *fb_helper, 1210592ffb21SWarner Losh struct drm_display_mode **modes, 1211592ffb21SWarner Losh bool *enabled, int width, int height) 1212592ffb21SWarner Losh { 1213592ffb21SWarner Losh struct drm_fb_helper_connector *fb_helper_conn; 1214592ffb21SWarner Losh int i; 1215592ffb21SWarner Losh 1216592ffb21SWarner Losh for (i = 0; i < fb_helper->connector_count; i++) { 1217592ffb21SWarner Losh fb_helper_conn = fb_helper->connector_info[i]; 1218592ffb21SWarner Losh 1219592ffb21SWarner Losh if (enabled[i] == false) 1220592ffb21SWarner Losh continue; 1221592ffb21SWarner Losh 1222592ffb21SWarner Losh DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", 1223592ffb21SWarner Losh fb_helper_conn->connector->base.id); 1224592ffb21SWarner Losh 1225592ffb21SWarner Losh /* got for command line mode first */ 1226592ffb21SWarner Losh modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height); 1227592ffb21SWarner Losh if (!modes[i]) { 1228592ffb21SWarner Losh DRM_DEBUG_KMS("looking for preferred mode on connector %d\n", 1229592ffb21SWarner Losh fb_helper_conn->connector->base.id); 1230592ffb21SWarner Losh modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height); 1231592ffb21SWarner Losh } 1232592ffb21SWarner Losh /* No preferred modes, pick one off the list */ 1233592ffb21SWarner Losh if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) { 1234592ffb21SWarner Losh list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head) 1235592ffb21SWarner Losh break; 1236592ffb21SWarner Losh } 1237592ffb21SWarner Losh DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : 1238592ffb21SWarner Losh "none"); 1239592ffb21SWarner Losh } 1240592ffb21SWarner Losh return true; 1241592ffb21SWarner Losh } 1242592ffb21SWarner Losh 1243592ffb21SWarner Losh static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, 1244592ffb21SWarner Losh struct drm_fb_helper_crtc **best_crtcs, 1245592ffb21SWarner Losh struct drm_display_mode **modes, 1246592ffb21SWarner Losh int n, int width, int height) 1247592ffb21SWarner Losh { 1248592ffb21SWarner Losh int c, o; 1249592ffb21SWarner Losh struct drm_device *dev = fb_helper->dev; 1250592ffb21SWarner Losh struct drm_connector *connector; 1251592ffb21SWarner Losh struct drm_connector_helper_funcs *connector_funcs; 1252592ffb21SWarner Losh struct drm_encoder *encoder; 1253592ffb21SWarner Losh int my_score, best_score, score; 1254592ffb21SWarner Losh struct drm_fb_helper_crtc **crtcs, *crtc; 1255592ffb21SWarner Losh struct drm_fb_helper_connector *fb_helper_conn; 1256592ffb21SWarner Losh 1257592ffb21SWarner Losh if (n == fb_helper->connector_count) 1258592ffb21SWarner Losh return 0; 1259592ffb21SWarner Losh 1260592ffb21SWarner Losh fb_helper_conn = fb_helper->connector_info[n]; 1261592ffb21SWarner Losh connector = fb_helper_conn->connector; 1262592ffb21SWarner Losh 1263592ffb21SWarner Losh best_crtcs[n] = NULL; 1264592ffb21SWarner Losh best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height); 1265592ffb21SWarner Losh if (modes[n] == NULL) 1266592ffb21SWarner Losh return best_score; 1267592ffb21SWarner Losh 1268592ffb21SWarner Losh crtcs = malloc(dev->mode_config.num_connector * 1269592ffb21SWarner Losh sizeof(struct drm_fb_helper_crtc *), DRM_MEM_KMS, M_NOWAIT | M_ZERO); 1270592ffb21SWarner Losh if (!crtcs) 1271592ffb21SWarner Losh return best_score; 1272592ffb21SWarner Losh 1273592ffb21SWarner Losh my_score = 1; 1274592ffb21SWarner Losh if (connector->status == connector_status_connected) 1275592ffb21SWarner Losh my_score++; 1276592ffb21SWarner Losh if (drm_has_cmdline_mode(fb_helper_conn)) 1277592ffb21SWarner Losh my_score++; 1278592ffb21SWarner Losh if (drm_has_preferred_mode(fb_helper_conn, width, height)) 1279592ffb21SWarner Losh my_score++; 1280592ffb21SWarner Losh 1281592ffb21SWarner Losh connector_funcs = connector->helper_private; 1282592ffb21SWarner Losh encoder = connector_funcs->best_encoder(connector); 1283592ffb21SWarner Losh if (!encoder) 1284592ffb21SWarner Losh goto out; 1285592ffb21SWarner Losh 1286592ffb21SWarner Losh /* select a crtc for this connector and then attempt to configure 1287592ffb21SWarner Losh remaining connectors */ 1288592ffb21SWarner Losh for (c = 0; c < fb_helper->crtc_count; c++) { 1289592ffb21SWarner Losh crtc = &fb_helper->crtc_info[c]; 1290592ffb21SWarner Losh 1291592ffb21SWarner Losh if ((encoder->possible_crtcs & (1 << c)) == 0) 1292592ffb21SWarner Losh continue; 1293592ffb21SWarner Losh 1294592ffb21SWarner Losh for (o = 0; o < n; o++) 1295592ffb21SWarner Losh if (best_crtcs[o] == crtc) 1296592ffb21SWarner Losh break; 1297592ffb21SWarner Losh 1298592ffb21SWarner Losh if (o < n) { 1299592ffb21SWarner Losh /* ignore cloning unless only a single crtc */ 1300592ffb21SWarner Losh if (fb_helper->crtc_count > 1) 1301592ffb21SWarner Losh continue; 1302592ffb21SWarner Losh 1303592ffb21SWarner Losh if (!drm_mode_equal(modes[o], modes[n])) 1304592ffb21SWarner Losh continue; 1305592ffb21SWarner Losh } 1306592ffb21SWarner Losh 1307592ffb21SWarner Losh crtcs[n] = crtc; 1308592ffb21SWarner Losh memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *)); 1309592ffb21SWarner Losh score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1, 1310592ffb21SWarner Losh width, height); 1311592ffb21SWarner Losh if (score > best_score) { 1312592ffb21SWarner Losh best_score = score; 1313592ffb21SWarner Losh memcpy(best_crtcs, crtcs, 1314592ffb21SWarner Losh dev->mode_config.num_connector * 1315592ffb21SWarner Losh sizeof(struct drm_fb_helper_crtc *)); 1316592ffb21SWarner Losh } 1317592ffb21SWarner Losh } 1318592ffb21SWarner Losh out: 1319592ffb21SWarner Losh free(crtcs, DRM_MEM_KMS); 1320592ffb21SWarner Losh return best_score; 1321592ffb21SWarner Losh } 1322592ffb21SWarner Losh 1323592ffb21SWarner Losh static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) 1324592ffb21SWarner Losh { 1325592ffb21SWarner Losh struct drm_device *dev = fb_helper->dev; 1326592ffb21SWarner Losh struct drm_fb_helper_crtc **crtcs; 1327592ffb21SWarner Losh struct drm_display_mode **modes; 1328592ffb21SWarner Losh struct drm_mode_set *modeset; 1329592ffb21SWarner Losh bool *enabled; 1330592ffb21SWarner Losh int width, height; 1331592ffb21SWarner Losh int i, ret; 1332592ffb21SWarner Losh 1333592ffb21SWarner Losh DRM_DEBUG_KMS("\n"); 1334592ffb21SWarner Losh 1335592ffb21SWarner Losh width = dev->mode_config.max_width; 1336592ffb21SWarner Losh height = dev->mode_config.max_height; 1337592ffb21SWarner Losh 1338592ffb21SWarner Losh crtcs = malloc(dev->mode_config.num_connector * 1339592ffb21SWarner Losh sizeof(struct drm_fb_helper_crtc *), DRM_MEM_KMS, M_NOWAIT | M_ZERO); 1340592ffb21SWarner Losh modes = malloc(dev->mode_config.num_connector * 1341592ffb21SWarner Losh sizeof(struct drm_display_mode *), DRM_MEM_KMS, M_NOWAIT | M_ZERO); 1342592ffb21SWarner Losh enabled = malloc(dev->mode_config.num_connector * 1343592ffb21SWarner Losh sizeof(bool), DRM_MEM_KMS, M_NOWAIT | M_ZERO); 1344592ffb21SWarner Losh if (!crtcs || !modes || !enabled) { 1345592ffb21SWarner Losh DRM_ERROR("Memory allocation failed\n"); 1346592ffb21SWarner Losh goto out; 1347592ffb21SWarner Losh } 1348592ffb21SWarner Losh 1349592ffb21SWarner Losh 1350592ffb21SWarner Losh drm_enable_connectors(fb_helper, enabled); 1351592ffb21SWarner Losh 1352592ffb21SWarner Losh ret = drm_target_cloned(fb_helper, modes, enabled, width, height); 1353592ffb21SWarner Losh if (!ret) { 1354592ffb21SWarner Losh ret = drm_target_preferred(fb_helper, modes, enabled, width, height); 1355592ffb21SWarner Losh if (!ret) 1356592ffb21SWarner Losh DRM_ERROR("Unable to find initial modes\n"); 1357592ffb21SWarner Losh } 1358592ffb21SWarner Losh 1359592ffb21SWarner Losh DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height); 1360592ffb21SWarner Losh 1361592ffb21SWarner Losh drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height); 1362592ffb21SWarner Losh 1363592ffb21SWarner Losh /* need to set the modesets up here for use later */ 1364592ffb21SWarner Losh /* fill out the connector<->crtc mappings into the modesets */ 1365592ffb21SWarner Losh for (i = 0; i < fb_helper->crtc_count; i++) { 1366592ffb21SWarner Losh modeset = &fb_helper->crtc_info[i].mode_set; 1367592ffb21SWarner Losh modeset->num_connectors = 0; 1368592ffb21SWarner Losh } 1369592ffb21SWarner Losh 1370592ffb21SWarner Losh for (i = 0; i < fb_helper->connector_count; i++) { 1371592ffb21SWarner Losh struct drm_display_mode *mode = modes[i]; 1372592ffb21SWarner Losh struct drm_fb_helper_crtc *fb_crtc = crtcs[i]; 1373592ffb21SWarner Losh modeset = &fb_crtc->mode_set; 1374592ffb21SWarner Losh 1375592ffb21SWarner Losh if (mode && fb_crtc) { 1376592ffb21SWarner Losh DRM_DEBUG_KMS("desired mode %s set on crtc %d\n", 1377592ffb21SWarner Losh mode->name, fb_crtc->mode_set.crtc->base.id); 1378592ffb21SWarner Losh fb_crtc->desired_mode = mode; 1379592ffb21SWarner Losh if (modeset->mode) 1380592ffb21SWarner Losh drm_mode_destroy(dev, modeset->mode); 1381592ffb21SWarner Losh modeset->mode = drm_mode_duplicate(dev, 1382592ffb21SWarner Losh fb_crtc->desired_mode); 1383592ffb21SWarner Losh modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector; 1384592ffb21SWarner Losh } 1385592ffb21SWarner Losh } 1386592ffb21SWarner Losh 1387592ffb21SWarner Losh out: 1388592ffb21SWarner Losh free(crtcs, DRM_MEM_KMS); 1389592ffb21SWarner Losh free(modes, DRM_MEM_KMS); 1390592ffb21SWarner Losh free(enabled, DRM_MEM_KMS); 1391592ffb21SWarner Losh } 1392592ffb21SWarner Losh 1393592ffb21SWarner Losh /** 1394592ffb21SWarner Losh * drm_helper_initial_config - setup a sane initial connector configuration 1395592ffb21SWarner Losh * @fb_helper: fb_helper device struct 1396592ffb21SWarner Losh * @bpp_sel: bpp value to use for the framebuffer configuration 1397592ffb21SWarner Losh * 1398592ffb21SWarner Losh * LOCKING: 1399592ffb21SWarner Losh * Called at init time by the driver to set up the @fb_helper initial 1400592ffb21SWarner Losh * configuration, must take the mode config lock. 1401592ffb21SWarner Losh * 1402592ffb21SWarner Losh * Scans the CRTCs and connectors and tries to put together an initial setup. 1403592ffb21SWarner Losh * At the moment, this is a cloned configuration across all heads with 1404592ffb21SWarner Losh * a new framebuffer object as the backing store. 1405592ffb21SWarner Losh * 1406592ffb21SWarner Losh * RETURNS: 1407592ffb21SWarner Losh * Zero if everything went ok, nonzero otherwise. 1408592ffb21SWarner Losh */ 1409592ffb21SWarner Losh bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) 1410592ffb21SWarner Losh { 1411592ffb21SWarner Losh struct drm_device *dev = fb_helper->dev; 1412592ffb21SWarner Losh int count = 0; 1413592ffb21SWarner Losh 1414592ffb21SWarner Losh /* disable all the possible outputs/crtcs before entering KMS mode */ 1415592ffb21SWarner Losh drm_helper_disable_unused_functions(fb_helper->dev); 1416592ffb21SWarner Losh 1417592ffb21SWarner Losh drm_fb_helper_parse_command_line(fb_helper); 1418592ffb21SWarner Losh 1419592ffb21SWarner Losh count = drm_fb_helper_probe_connector_modes(fb_helper, 1420592ffb21SWarner Losh dev->mode_config.max_width, 1421592ffb21SWarner Losh dev->mode_config.max_height); 1422592ffb21SWarner Losh /* 1423592ffb21SWarner Losh * we shouldn't end up with no modes here. 1424592ffb21SWarner Losh */ 1425592ffb21SWarner Losh if (count == 0) 1426592ffb21SWarner Losh dev_info(fb_helper->dev->dev, "No connectors reported connected with modes\n"); 1427592ffb21SWarner Losh 1428592ffb21SWarner Losh drm_setup_crtcs(fb_helper); 1429592ffb21SWarner Losh 1430592ffb21SWarner Losh return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); 1431592ffb21SWarner Losh } 1432592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_initial_config); 1433592ffb21SWarner Losh 1434592ffb21SWarner Losh /** 1435592ffb21SWarner Losh * drm_fb_helper_hotplug_event - respond to a hotplug notification by 1436592ffb21SWarner Losh * probing all the outputs attached to the fb 1437592ffb21SWarner Losh * @fb_helper: the drm_fb_helper 1438592ffb21SWarner Losh * 1439592ffb21SWarner Losh * LOCKING: 1440592ffb21SWarner Losh * Called at runtime, must take mode config lock. 1441592ffb21SWarner Losh * 1442592ffb21SWarner Losh * Scan the connectors attached to the fb_helper and try to put together a 1443592ffb21SWarner Losh * setup after *notification of a change in output configuration. 1444592ffb21SWarner Losh * 1445592ffb21SWarner Losh * RETURNS: 1446592ffb21SWarner Losh * 0 on success and a non-zero error code otherwise. 1447592ffb21SWarner Losh */ 1448592ffb21SWarner Losh int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) 1449592ffb21SWarner Losh { 1450592ffb21SWarner Losh struct drm_device *dev = fb_helper->dev; 1451592ffb21SWarner Losh u32 max_width, max_height, bpp_sel; 1452592ffb21SWarner Losh int bound = 0, crtcs_bound = 0; 1453592ffb21SWarner Losh struct drm_crtc *crtc; 1454592ffb21SWarner Losh 1455592ffb21SWarner Losh if (!fb_helper->fb) 1456592ffb21SWarner Losh return 0; 1457592ffb21SWarner Losh 1458592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 1459592ffb21SWarner Losh list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 1460592ffb21SWarner Losh if (crtc->fb) 1461592ffb21SWarner Losh crtcs_bound++; 1462592ffb21SWarner Losh if (crtc->fb == fb_helper->fb) 1463592ffb21SWarner Losh bound++; 1464592ffb21SWarner Losh } 1465592ffb21SWarner Losh 1466592ffb21SWarner Losh if (bound < crtcs_bound) { 1467592ffb21SWarner Losh fb_helper->delayed_hotplug = true; 1468592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 1469592ffb21SWarner Losh return 0; 1470592ffb21SWarner Losh } 1471592ffb21SWarner Losh DRM_DEBUG_KMS("\n"); 1472592ffb21SWarner Losh 1473592ffb21SWarner Losh max_width = fb_helper->fb->width; 1474592ffb21SWarner Losh max_height = fb_helper->fb->height; 1475592ffb21SWarner Losh bpp_sel = fb_helper->fb->bits_per_pixel; 1476592ffb21SWarner Losh 147742278fc2SJohn Baldwin drm_fb_helper_probe_connector_modes(fb_helper, max_width, 1478592ffb21SWarner Losh max_height); 1479592ffb21SWarner Losh drm_setup_crtcs(fb_helper); 1480592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 1481592ffb21SWarner Losh 1482592ffb21SWarner Losh return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); 1483592ffb21SWarner Losh } 1484592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_hotplug_event); 1485