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