1*592ffb21SWarner Losh /* 2*592ffb21SWarner Losh * Copyright (c) 2006-2009 Red Hat Inc. 3*592ffb21SWarner Losh * Copyright (c) 2006-2008 Intel Corporation 4*592ffb21SWarner Losh * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> 5*592ffb21SWarner Losh * 6*592ffb21SWarner Losh * DRM framebuffer helper functions 7*592ffb21SWarner Losh * 8*592ffb21SWarner Losh * Permission to use, copy, modify, distribute, and sell this software and its 9*592ffb21SWarner Losh * documentation for any purpose is hereby granted without fee, provided that 10*592ffb21SWarner Losh * the above copyright notice appear in all copies and that both that copyright 11*592ffb21SWarner Losh * notice and this permission notice appear in supporting documentation, and 12*592ffb21SWarner Losh * that the name of the copyright holders not be used in advertising or 13*592ffb21SWarner Losh * publicity pertaining to distribution of the software without specific, 14*592ffb21SWarner Losh * written prior permission. The copyright holders make no representations 15*592ffb21SWarner Losh * about the suitability of this software for any purpose. It is provided "as 16*592ffb21SWarner Losh * is" without express or implied warranty. 17*592ffb21SWarner Losh * 18*592ffb21SWarner Losh * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 19*592ffb21SWarner Losh * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 20*592ffb21SWarner Losh * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 21*592ffb21SWarner Losh * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 22*592ffb21SWarner Losh * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 23*592ffb21SWarner Losh * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 24*592ffb21SWarner Losh * OF THIS SOFTWARE. 25*592ffb21SWarner Losh * 26*592ffb21SWarner Losh * Authors: 27*592ffb21SWarner Losh * Dave Airlie <airlied@linux.ie> 28*592ffb21SWarner Losh * Jesse Barnes <jesse.barnes@intel.com> 29*592ffb21SWarner Losh */ 30*592ffb21SWarner Losh 31*592ffb21SWarner Losh #include <sys/cdefs.h> 32*592ffb21SWarner Losh __FBSDID("$FreeBSD$"); 33*592ffb21SWarner Losh 34*592ffb21SWarner Losh #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 35*592ffb21SWarner Losh 36*592ffb21SWarner Losh #include <dev/drm2/drmP.h> 37*592ffb21SWarner Losh #include <dev/drm2/drm_crtc.h> 38*592ffb21SWarner Losh #include <dev/drm2/drm_fb_helper.h> 39*592ffb21SWarner Losh #include <dev/drm2/drm_crtc_helper.h> 40*592ffb21SWarner Losh 41*592ffb21SWarner Losh MODULE_AUTHOR("David Airlie, Jesse Barnes"); 42*592ffb21SWarner Losh MODULE_DESCRIPTION("DRM KMS helper"); 43*592ffb21SWarner Losh MODULE_LICENSE("GPL and additional rights"); 44*592ffb21SWarner Losh 45*592ffb21SWarner Losh static DRM_LIST_HEAD(kernel_fb_helper_list); 46*592ffb21SWarner Losh 47*592ffb21SWarner Losh #include <sys/kdb.h> 48*592ffb21SWarner Losh #include <sys/param.h> 49*592ffb21SWarner Losh #include <sys/systm.h> 50*592ffb21SWarner Losh 51*592ffb21SWarner Losh struct vt_kms_softc { 52*592ffb21SWarner Losh struct drm_fb_helper *fb_helper; 53*592ffb21SWarner Losh struct task fb_mode_task; 54*592ffb21SWarner Losh }; 55*592ffb21SWarner Losh 56*592ffb21SWarner Losh /* Call restore out of vt(9) locks. */ 57*592ffb21SWarner Losh static void 58*592ffb21SWarner Losh vt_restore_fbdev_mode(void *arg, int pending) 59*592ffb21SWarner Losh { 60*592ffb21SWarner Losh struct drm_fb_helper *fb_helper; 61*592ffb21SWarner Losh struct vt_kms_softc *sc; 62*592ffb21SWarner Losh 63*592ffb21SWarner Losh sc = (struct vt_kms_softc *)arg; 64*592ffb21SWarner Losh fb_helper = sc->fb_helper; 65*592ffb21SWarner Losh sx_xlock(&fb_helper->dev->mode_config.mutex); 66*592ffb21SWarner Losh drm_fb_helper_restore_fbdev_mode(fb_helper); 67*592ffb21SWarner Losh sx_xunlock(&fb_helper->dev->mode_config.mutex); 68*592ffb21SWarner Losh } 69*592ffb21SWarner Losh 70*592ffb21SWarner Losh static int 71*592ffb21SWarner Losh vt_kms_postswitch(void *arg) 72*592ffb21SWarner Losh { 73*592ffb21SWarner Losh struct vt_kms_softc *sc; 74*592ffb21SWarner Losh 75*592ffb21SWarner Losh sc = (struct vt_kms_softc *)arg; 76*592ffb21SWarner Losh 77*592ffb21SWarner Losh if (!kdb_active && panicstr == NULL) 78*592ffb21SWarner Losh taskqueue_enqueue(taskqueue_thread, &sc->fb_mode_task); 79*592ffb21SWarner Losh else 80*592ffb21SWarner Losh drm_fb_helper_restore_fbdev_mode(sc->fb_helper); 81*592ffb21SWarner Losh 82*592ffb21SWarner Losh return (0); 83*592ffb21SWarner Losh } 84*592ffb21SWarner Losh 85*592ffb21SWarner Losh struct fb_info * 86*592ffb21SWarner Losh framebuffer_alloc() 87*592ffb21SWarner Losh { 88*592ffb21SWarner Losh struct fb_info *info; 89*592ffb21SWarner Losh struct vt_kms_softc *sc; 90*592ffb21SWarner Losh 91*592ffb21SWarner Losh info = malloc(sizeof(*info), DRM_MEM_KMS, M_WAITOK | M_ZERO); 92*592ffb21SWarner Losh 93*592ffb21SWarner Losh sc = malloc(sizeof(*sc), DRM_MEM_KMS, M_WAITOK | M_ZERO); 94*592ffb21SWarner Losh TASK_INIT(&sc->fb_mode_task, 0, vt_restore_fbdev_mode, sc); 95*592ffb21SWarner Losh 96*592ffb21SWarner Losh info->fb_priv = sc; 97*592ffb21SWarner Losh info->enter = &vt_kms_postswitch; 98*592ffb21SWarner Losh 99*592ffb21SWarner Losh return (info); 100*592ffb21SWarner Losh } 101*592ffb21SWarner Losh 102*592ffb21SWarner Losh void 103*592ffb21SWarner Losh framebuffer_release(struct fb_info *info) 104*592ffb21SWarner Losh { 105*592ffb21SWarner Losh 106*592ffb21SWarner Losh free(info->fb_priv, DRM_MEM_KMS); 107*592ffb21SWarner Losh free(info, DRM_MEM_KMS); 108*592ffb21SWarner Losh } 109*592ffb21SWarner Losh 110*592ffb21SWarner Losh static int 111*592ffb21SWarner Losh fb_get_options(const char *connector_name, char **option) 112*592ffb21SWarner Losh { 113*592ffb21SWarner Losh char tunable[64]; 114*592ffb21SWarner Losh 115*592ffb21SWarner Losh /* 116*592ffb21SWarner Losh * A user may use loader tunables to set a specific mode for the 117*592ffb21SWarner Losh * console. Tunables are read in the following order: 118*592ffb21SWarner Losh * 1. kern.vt.fb.modes.$connector_name 119*592ffb21SWarner Losh * 2. kern.vt.fb.default_mode 120*592ffb21SWarner Losh * 121*592ffb21SWarner Losh * Example of a mode specific to the LVDS connector: 122*592ffb21SWarner Losh * kern.vt.fb.modes.LVDS="1024x768" 123*592ffb21SWarner Losh * 124*592ffb21SWarner Losh * Example of a mode applied to all connectors not having a 125*592ffb21SWarner Losh * connector-specific mode: 126*592ffb21SWarner Losh * kern.vt.fb.default_mode="640x480" 127*592ffb21SWarner Losh */ 128*592ffb21SWarner Losh snprintf(tunable, sizeof(tunable), "kern.vt.fb.modes.%s", 129*592ffb21SWarner Losh connector_name); 130*592ffb21SWarner Losh DRM_INFO("Connector %s: get mode from tunables:\n", connector_name); 131*592ffb21SWarner Losh DRM_INFO(" - %s\n", tunable); 132*592ffb21SWarner Losh DRM_INFO(" - kern.vt.fb.default_mode\n"); 133*592ffb21SWarner Losh *option = kern_getenv(tunable); 134*592ffb21SWarner Losh if (*option == NULL) 135*592ffb21SWarner Losh *option = kern_getenv("kern.vt.fb.default_mode"); 136*592ffb21SWarner Losh 137*592ffb21SWarner Losh return (*option != NULL ? 0 : -ENOENT); 138*592ffb21SWarner Losh } 139*592ffb21SWarner Losh 140*592ffb21SWarner Losh /** 141*592ffb21SWarner Losh * DOC: fbdev helpers 142*592ffb21SWarner Losh * 143*592ffb21SWarner Losh * The fb helper functions are useful to provide an fbdev on top of a drm kernel 144*592ffb21SWarner Losh * mode setting driver. They can be used mostly independantely from the crtc 145*592ffb21SWarner Losh * helper functions used by many drivers to implement the kernel mode setting 146*592ffb21SWarner Losh * interfaces. 147*592ffb21SWarner Losh */ 148*592ffb21SWarner Losh 149*592ffb21SWarner Losh /* simple single crtc case helper function */ 150*592ffb21SWarner Losh int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) 151*592ffb21SWarner Losh { 152*592ffb21SWarner Losh struct drm_device *dev = fb_helper->dev; 153*592ffb21SWarner Losh struct drm_connector *connector; 154*592ffb21SWarner Losh int i; 155*592ffb21SWarner Losh 156*592ffb21SWarner Losh list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 157*592ffb21SWarner Losh struct drm_fb_helper_connector *fb_helper_connector; 158*592ffb21SWarner Losh 159*592ffb21SWarner Losh fb_helper_connector = malloc(sizeof(struct drm_fb_helper_connector), 160*592ffb21SWarner Losh DRM_MEM_KMS, M_NOWAIT | M_ZERO); 161*592ffb21SWarner Losh if (!fb_helper_connector) 162*592ffb21SWarner Losh goto fail; 163*592ffb21SWarner Losh 164*592ffb21SWarner Losh fb_helper_connector->connector = connector; 165*592ffb21SWarner Losh fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector; 166*592ffb21SWarner Losh } 167*592ffb21SWarner Losh return 0; 168*592ffb21SWarner Losh fail: 169*592ffb21SWarner Losh for (i = 0; i < fb_helper->connector_count; i++) { 170*592ffb21SWarner Losh free(fb_helper->connector_info[i], DRM_MEM_KMS); 171*592ffb21SWarner Losh fb_helper->connector_info[i] = NULL; 172*592ffb21SWarner Losh } 173*592ffb21SWarner Losh fb_helper->connector_count = 0; 174*592ffb21SWarner Losh return -ENOMEM; 175*592ffb21SWarner Losh } 176*592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors); 177*592ffb21SWarner Losh 178*592ffb21SWarner Losh static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper) 179*592ffb21SWarner Losh { 180*592ffb21SWarner Losh struct drm_fb_helper_connector *fb_helper_conn; 181*592ffb21SWarner Losh int i; 182*592ffb21SWarner Losh 183*592ffb21SWarner Losh for (i = 0; i < fb_helper->connector_count; i++) { 184*592ffb21SWarner Losh struct drm_cmdline_mode *mode; 185*592ffb21SWarner Losh struct drm_connector *connector; 186*592ffb21SWarner Losh char *option = NULL; 187*592ffb21SWarner Losh 188*592ffb21SWarner Losh fb_helper_conn = fb_helper->connector_info[i]; 189*592ffb21SWarner Losh connector = fb_helper_conn->connector; 190*592ffb21SWarner Losh mode = &fb_helper_conn->cmdline_mode; 191*592ffb21SWarner Losh 192*592ffb21SWarner Losh /* do something on return - turn off connector maybe */ 193*592ffb21SWarner Losh if (fb_get_options(drm_get_connector_name(connector), &option)) 194*592ffb21SWarner Losh continue; 195*592ffb21SWarner Losh 196*592ffb21SWarner Losh if (drm_mode_parse_command_line_for_connector(option, 197*592ffb21SWarner Losh connector, 198*592ffb21SWarner Losh mode)) { 199*592ffb21SWarner Losh if (mode->force) { 200*592ffb21SWarner Losh const char *s; 201*592ffb21SWarner Losh switch (mode->force) { 202*592ffb21SWarner Losh case DRM_FORCE_OFF: 203*592ffb21SWarner Losh s = "OFF"; 204*592ffb21SWarner Losh break; 205*592ffb21SWarner Losh case DRM_FORCE_ON_DIGITAL: 206*592ffb21SWarner Losh s = "ON - dig"; 207*592ffb21SWarner Losh break; 208*592ffb21SWarner Losh default: 209*592ffb21SWarner Losh case DRM_FORCE_ON: 210*592ffb21SWarner Losh s = "ON"; 211*592ffb21SWarner Losh break; 212*592ffb21SWarner Losh } 213*592ffb21SWarner Losh 214*592ffb21SWarner Losh DRM_INFO("forcing %s connector %s\n", 215*592ffb21SWarner Losh drm_get_connector_name(connector), s); 216*592ffb21SWarner Losh connector->force = mode->force; 217*592ffb21SWarner Losh } 218*592ffb21SWarner Losh 219*592ffb21SWarner Losh DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n", 220*592ffb21SWarner Losh drm_get_connector_name(connector), 221*592ffb21SWarner Losh mode->xres, mode->yres, 222*592ffb21SWarner Losh mode->refresh_specified ? mode->refresh : 60, 223*592ffb21SWarner Losh mode->rb ? " reduced blanking" : "", 224*592ffb21SWarner Losh mode->margins ? " with margins" : "", 225*592ffb21SWarner Losh mode->interlace ? " interlaced" : ""); 226*592ffb21SWarner Losh } 227*592ffb21SWarner Losh 228*592ffb21SWarner Losh freeenv(option); 229*592ffb21SWarner Losh } 230*592ffb21SWarner Losh return 0; 231*592ffb21SWarner Losh } 232*592ffb21SWarner Losh 233*592ffb21SWarner Losh #if 0 && defined(FREEBSD_NOTYET) 234*592ffb21SWarner Losh static void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_helper *helper) 235*592ffb21SWarner Losh { 236*592ffb21SWarner Losh uint16_t *r_base, *g_base, *b_base; 237*592ffb21SWarner Losh int i; 238*592ffb21SWarner Losh 239*592ffb21SWarner Losh r_base = crtc->gamma_store; 240*592ffb21SWarner Losh g_base = r_base + crtc->gamma_size; 241*592ffb21SWarner Losh b_base = g_base + crtc->gamma_size; 242*592ffb21SWarner Losh 243*592ffb21SWarner Losh for (i = 0; i < crtc->gamma_size; i++) 244*592ffb21SWarner Losh helper->funcs->gamma_get(crtc, &r_base[i], &g_base[i], &b_base[i], i); 245*592ffb21SWarner Losh } 246*592ffb21SWarner Losh 247*592ffb21SWarner Losh static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) 248*592ffb21SWarner Losh { 249*592ffb21SWarner Losh uint16_t *r_base, *g_base, *b_base; 250*592ffb21SWarner Losh 251*592ffb21SWarner Losh if (crtc->funcs->gamma_set == NULL) 252*592ffb21SWarner Losh return; 253*592ffb21SWarner Losh 254*592ffb21SWarner Losh r_base = crtc->gamma_store; 255*592ffb21SWarner Losh g_base = r_base + crtc->gamma_size; 256*592ffb21SWarner Losh b_base = g_base + crtc->gamma_size; 257*592ffb21SWarner Losh 258*592ffb21SWarner Losh crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); 259*592ffb21SWarner Losh } 260*592ffb21SWarner Losh 261*592ffb21SWarner Losh int drm_fb_helper_debug_enter(struct fb_info *info) 262*592ffb21SWarner Losh { 263*592ffb21SWarner Losh struct drm_fb_helper *helper = info->par; 264*592ffb21SWarner Losh struct drm_crtc_helper_funcs *funcs; 265*592ffb21SWarner Losh int i; 266*592ffb21SWarner Losh 267*592ffb21SWarner Losh if (list_empty(&kernel_fb_helper_list)) 268*592ffb21SWarner Losh return false; 269*592ffb21SWarner Losh 270*592ffb21SWarner Losh list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { 271*592ffb21SWarner Losh for (i = 0; i < helper->crtc_count; i++) { 272*592ffb21SWarner Losh struct drm_mode_set *mode_set = 273*592ffb21SWarner Losh &helper->crtc_info[i].mode_set; 274*592ffb21SWarner Losh 275*592ffb21SWarner Losh if (!mode_set->crtc->enabled) 276*592ffb21SWarner Losh continue; 277*592ffb21SWarner Losh 278*592ffb21SWarner Losh funcs = mode_set->crtc->helper_private; 279*592ffb21SWarner Losh drm_fb_helper_save_lut_atomic(mode_set->crtc, helper); 280*592ffb21SWarner Losh funcs->mode_set_base_atomic(mode_set->crtc, 281*592ffb21SWarner Losh mode_set->fb, 282*592ffb21SWarner Losh mode_set->x, 283*592ffb21SWarner Losh mode_set->y, 284*592ffb21SWarner Losh ENTER_ATOMIC_MODE_SET); 285*592ffb21SWarner Losh } 286*592ffb21SWarner Losh } 287*592ffb21SWarner Losh 288*592ffb21SWarner Losh return 0; 289*592ffb21SWarner Losh } 290*592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_debug_enter); 291*592ffb21SWarner Losh 292*592ffb21SWarner Losh /* Find the real fb for a given fb helper CRTC */ 293*592ffb21SWarner Losh static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc) 294*592ffb21SWarner Losh { 295*592ffb21SWarner Losh struct drm_device *dev = crtc->dev; 296*592ffb21SWarner Losh struct drm_crtc *c; 297*592ffb21SWarner Losh 298*592ffb21SWarner Losh list_for_each_entry(c, &dev->mode_config.crtc_list, head) { 299*592ffb21SWarner Losh if (crtc->base.id == c->base.id) 300*592ffb21SWarner Losh return c->fb; 301*592ffb21SWarner Losh } 302*592ffb21SWarner Losh 303*592ffb21SWarner Losh return NULL; 304*592ffb21SWarner Losh } 305*592ffb21SWarner Losh 306*592ffb21SWarner Losh int drm_fb_helper_debug_leave(struct fb_info *info) 307*592ffb21SWarner Losh { 308*592ffb21SWarner Losh struct drm_fb_helper *helper = info->par; 309*592ffb21SWarner Losh struct drm_crtc *crtc; 310*592ffb21SWarner Losh struct drm_crtc_helper_funcs *funcs; 311*592ffb21SWarner Losh struct drm_framebuffer *fb; 312*592ffb21SWarner Losh int i; 313*592ffb21SWarner Losh 314*592ffb21SWarner Losh for (i = 0; i < helper->crtc_count; i++) { 315*592ffb21SWarner Losh struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set; 316*592ffb21SWarner Losh crtc = mode_set->crtc; 317*592ffb21SWarner Losh funcs = crtc->helper_private; 318*592ffb21SWarner Losh fb = drm_mode_config_fb(crtc); 319*592ffb21SWarner Losh 320*592ffb21SWarner Losh if (!crtc->enabled) 321*592ffb21SWarner Losh continue; 322*592ffb21SWarner Losh 323*592ffb21SWarner Losh if (!fb) { 324*592ffb21SWarner Losh DRM_ERROR("no fb to restore??\n"); 325*592ffb21SWarner Losh continue; 326*592ffb21SWarner Losh } 327*592ffb21SWarner Losh 328*592ffb21SWarner Losh drm_fb_helper_restore_lut_atomic(mode_set->crtc); 329*592ffb21SWarner Losh funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x, 330*592ffb21SWarner Losh crtc->y, LEAVE_ATOMIC_MODE_SET); 331*592ffb21SWarner Losh } 332*592ffb21SWarner Losh 333*592ffb21SWarner Losh return 0; 334*592ffb21SWarner Losh } 335*592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_debug_leave); 336*592ffb21SWarner Losh #endif /* FREEBSD_NOTYET */ 337*592ffb21SWarner Losh 338*592ffb21SWarner Losh bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) 339*592ffb21SWarner Losh { 340*592ffb21SWarner Losh bool error = false; 341*592ffb21SWarner Losh int i, ret; 342*592ffb21SWarner Losh 343*592ffb21SWarner Losh for (i = 0; i < fb_helper->crtc_count; i++) { 344*592ffb21SWarner Losh struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; 345*592ffb21SWarner Losh ret = mode_set->crtc->funcs->set_config(mode_set); 346*592ffb21SWarner Losh if (ret) 347*592ffb21SWarner Losh error = true; 348*592ffb21SWarner Losh } 349*592ffb21SWarner Losh return error; 350*592ffb21SWarner Losh } 351*592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode); 352*592ffb21SWarner Losh 353*592ffb21SWarner Losh static bool drm_fb_helper_force_kernel_mode(void) 354*592ffb21SWarner Losh { 355*592ffb21SWarner Losh bool ret, error = false; 356*592ffb21SWarner Losh struct drm_fb_helper *helper; 357*592ffb21SWarner Losh 358*592ffb21SWarner Losh if (list_empty(&kernel_fb_helper_list)) 359*592ffb21SWarner Losh return false; 360*592ffb21SWarner Losh 361*592ffb21SWarner Losh list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { 362*592ffb21SWarner Losh if (helper->dev->switch_power_state == DRM_SWITCH_POWER_OFF) 363*592ffb21SWarner Losh continue; 364*592ffb21SWarner Losh 365*592ffb21SWarner Losh ret = drm_fb_helper_restore_fbdev_mode(helper); 366*592ffb21SWarner Losh if (ret) 367*592ffb21SWarner Losh error = true; 368*592ffb21SWarner Losh } 369*592ffb21SWarner Losh return error; 370*592ffb21SWarner Losh } 371*592ffb21SWarner Losh 372*592ffb21SWarner Losh #if 0 && defined(FREEBSD_NOTYET) 373*592ffb21SWarner Losh int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed, 374*592ffb21SWarner Losh void *panic_str) 375*592ffb21SWarner Losh { 376*592ffb21SWarner Losh /* 377*592ffb21SWarner Losh * It's a waste of time and effort to switch back to text console 378*592ffb21SWarner Losh * if the kernel should reboot before panic messages can be seen. 379*592ffb21SWarner Losh */ 380*592ffb21SWarner Losh if (panic_timeout < 0) 381*592ffb21SWarner Losh return 0; 382*592ffb21SWarner Losh 383*592ffb21SWarner Losh pr_err("panic occurred, switching back to text console\n"); 384*592ffb21SWarner Losh return drm_fb_helper_force_kernel_mode(); 385*592ffb21SWarner Losh } 386*592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_panic); 387*592ffb21SWarner Losh 388*592ffb21SWarner Losh static struct notifier_block paniced = { 389*592ffb21SWarner Losh .notifier_call = drm_fb_helper_panic, 390*592ffb21SWarner Losh }; 391*592ffb21SWarner Losh #endif /* FREEBSD_NOTYET */ 392*592ffb21SWarner Losh 393*592ffb21SWarner Losh /** 394*592ffb21SWarner Losh * drm_fb_helper_restore - restore the framebuffer console (kernel) config 395*592ffb21SWarner Losh * 396*592ffb21SWarner Losh * Restore's the kernel's fbcon mode, used for lastclose & panic paths. 397*592ffb21SWarner Losh */ 398*592ffb21SWarner Losh void drm_fb_helper_restore(void) 399*592ffb21SWarner Losh { 400*592ffb21SWarner Losh bool ret; 401*592ffb21SWarner Losh ret = drm_fb_helper_force_kernel_mode(); 402*592ffb21SWarner Losh if (ret == true) 403*592ffb21SWarner Losh DRM_ERROR("Failed to restore crtc configuration\n"); 404*592ffb21SWarner Losh } 405*592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_restore); 406*592ffb21SWarner Losh 407*592ffb21SWarner Losh #ifdef __linux__ 408*592ffb21SWarner Losh #ifdef CONFIG_MAGIC_SYSRQ 409*592ffb21SWarner Losh static void drm_fb_helper_restore_work_fn(struct work_struct *ignored) 410*592ffb21SWarner Losh { 411*592ffb21SWarner Losh drm_fb_helper_restore(); 412*592ffb21SWarner Losh } 413*592ffb21SWarner Losh static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn); 414*592ffb21SWarner Losh 415*592ffb21SWarner Losh static void drm_fb_helper_sysrq(int dummy1) 416*592ffb21SWarner Losh { 417*592ffb21SWarner Losh schedule_work(&drm_fb_helper_restore_work); 418*592ffb21SWarner Losh } 419*592ffb21SWarner Losh 420*592ffb21SWarner Losh static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { 421*592ffb21SWarner Losh .handler = drm_fb_helper_sysrq, 422*592ffb21SWarner Losh .help_msg = "force-fb(V)", 423*592ffb21SWarner Losh .action_msg = "Restore framebuffer console", 424*592ffb21SWarner Losh }; 425*592ffb21SWarner Losh #else 426*592ffb21SWarner Losh static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { }; 427*592ffb21SWarner Losh #endif 428*592ffb21SWarner Losh #endif 429*592ffb21SWarner Losh 430*592ffb21SWarner Losh #if 0 && defined(FREEBSD_NOTYET) 431*592ffb21SWarner Losh static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) 432*592ffb21SWarner Losh { 433*592ffb21SWarner Losh struct drm_fb_helper *fb_helper = info->par; 434*592ffb21SWarner Losh struct drm_device *dev = fb_helper->dev; 435*592ffb21SWarner Losh struct drm_crtc *crtc; 436*592ffb21SWarner Losh struct drm_connector *connector; 437*592ffb21SWarner Losh int i, j; 438*592ffb21SWarner Losh 439*592ffb21SWarner Losh /* 440*592ffb21SWarner Losh * For each CRTC in this fb, turn the connectors on/off. 441*592ffb21SWarner Losh */ 442*592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 443*592ffb21SWarner Losh for (i = 0; i < fb_helper->crtc_count; i++) { 444*592ffb21SWarner Losh crtc = fb_helper->crtc_info[i].mode_set.crtc; 445*592ffb21SWarner Losh 446*592ffb21SWarner Losh if (!crtc->enabled) 447*592ffb21SWarner Losh continue; 448*592ffb21SWarner Losh 449*592ffb21SWarner Losh /* Walk the connectors & encoders on this fb turning them on/off */ 450*592ffb21SWarner Losh for (j = 0; j < fb_helper->connector_count; j++) { 451*592ffb21SWarner Losh connector = fb_helper->connector_info[j]->connector; 452*592ffb21SWarner Losh connector->funcs->dpms(connector, dpms_mode); 453*592ffb21SWarner Losh drm_object_property_set_value(&connector->base, 454*592ffb21SWarner Losh dev->mode_config.dpms_property, dpms_mode); 455*592ffb21SWarner Losh } 456*592ffb21SWarner Losh } 457*592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 458*592ffb21SWarner Losh } 459*592ffb21SWarner Losh 460*592ffb21SWarner Losh int drm_fb_helper_blank(int blank, struct fb_info *info) 461*592ffb21SWarner Losh { 462*592ffb21SWarner Losh switch (blank) { 463*592ffb21SWarner Losh /* Display: On; HSync: On, VSync: On */ 464*592ffb21SWarner Losh case FB_BLANK_UNBLANK: 465*592ffb21SWarner Losh drm_fb_helper_dpms(info, DRM_MODE_DPMS_ON); 466*592ffb21SWarner Losh break; 467*592ffb21SWarner Losh /* Display: Off; HSync: On, VSync: On */ 468*592ffb21SWarner Losh case FB_BLANK_NORMAL: 469*592ffb21SWarner Losh drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY); 470*592ffb21SWarner Losh break; 471*592ffb21SWarner Losh /* Display: Off; HSync: Off, VSync: On */ 472*592ffb21SWarner Losh case FB_BLANK_HSYNC_SUSPEND: 473*592ffb21SWarner Losh drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY); 474*592ffb21SWarner Losh break; 475*592ffb21SWarner Losh /* Display: Off; HSync: On, VSync: Off */ 476*592ffb21SWarner Losh case FB_BLANK_VSYNC_SUSPEND: 477*592ffb21SWarner Losh drm_fb_helper_dpms(info, DRM_MODE_DPMS_SUSPEND); 478*592ffb21SWarner Losh break; 479*592ffb21SWarner Losh /* Display: Off; HSync: Off, VSync: Off */ 480*592ffb21SWarner Losh case FB_BLANK_POWERDOWN: 481*592ffb21SWarner Losh drm_fb_helper_dpms(info, DRM_MODE_DPMS_OFF); 482*592ffb21SWarner Losh break; 483*592ffb21SWarner Losh } 484*592ffb21SWarner Losh return 0; 485*592ffb21SWarner Losh } 486*592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_blank); 487*592ffb21SWarner Losh #endif /* FREEBSD_NOTYET */ 488*592ffb21SWarner Losh 489*592ffb21SWarner Losh static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) 490*592ffb21SWarner Losh { 491*592ffb21SWarner Losh int i; 492*592ffb21SWarner Losh 493*592ffb21SWarner Losh for (i = 0; i < helper->connector_count; i++) 494*592ffb21SWarner Losh free(helper->connector_info[i], DRM_MEM_KMS); 495*592ffb21SWarner Losh free(helper->connector_info, DRM_MEM_KMS); 496*592ffb21SWarner Losh for (i = 0; i < helper->crtc_count; i++) { 497*592ffb21SWarner Losh free(helper->crtc_info[i].mode_set.connectors, DRM_MEM_KMS); 498*592ffb21SWarner Losh if (helper->crtc_info[i].mode_set.mode) 499*592ffb21SWarner Losh drm_mode_destroy(helper->dev, helper->crtc_info[i].mode_set.mode); 500*592ffb21SWarner Losh } 501*592ffb21SWarner Losh free(helper->crtc_info, DRM_MEM_KMS); 502*592ffb21SWarner Losh } 503*592ffb21SWarner Losh 504*592ffb21SWarner Losh int drm_fb_helper_init(struct drm_device *dev, 505*592ffb21SWarner Losh struct drm_fb_helper *fb_helper, 506*592ffb21SWarner Losh int crtc_count, int max_conn_count) 507*592ffb21SWarner Losh { 508*592ffb21SWarner Losh struct drm_crtc *crtc; 509*592ffb21SWarner Losh int i; 510*592ffb21SWarner Losh 511*592ffb21SWarner Losh fb_helper->dev = dev; 512*592ffb21SWarner Losh 513*592ffb21SWarner Losh INIT_LIST_HEAD(&fb_helper->kernel_fb_list); 514*592ffb21SWarner Losh 515*592ffb21SWarner Losh fb_helper->crtc_info = malloc(crtc_count * sizeof(struct drm_fb_helper_crtc), 516*592ffb21SWarner Losh DRM_MEM_KMS, M_NOWAIT | M_ZERO); 517*592ffb21SWarner Losh if (!fb_helper->crtc_info) 518*592ffb21SWarner Losh return -ENOMEM; 519*592ffb21SWarner Losh 520*592ffb21SWarner Losh fb_helper->crtc_count = crtc_count; 521*592ffb21SWarner Losh fb_helper->connector_info = malloc(dev->mode_config.num_connector * sizeof(struct drm_fb_helper_connector *), 522*592ffb21SWarner Losh DRM_MEM_KMS, M_NOWAIT | M_ZERO); 523*592ffb21SWarner Losh if (!fb_helper->connector_info) { 524*592ffb21SWarner Losh free(fb_helper->crtc_info, DRM_MEM_KMS); 525*592ffb21SWarner Losh return -ENOMEM; 526*592ffb21SWarner Losh } 527*592ffb21SWarner Losh fb_helper->connector_count = 0; 528*592ffb21SWarner Losh 529*592ffb21SWarner Losh for (i = 0; i < crtc_count; i++) { 530*592ffb21SWarner Losh fb_helper->crtc_info[i].mode_set.connectors = 531*592ffb21SWarner Losh malloc(max_conn_count * 532*592ffb21SWarner Losh sizeof(struct drm_connector *), 533*592ffb21SWarner Losh DRM_MEM_KMS, M_NOWAIT | M_ZERO); 534*592ffb21SWarner Losh 535*592ffb21SWarner Losh if (!fb_helper->crtc_info[i].mode_set.connectors) 536*592ffb21SWarner Losh goto out_free; 537*592ffb21SWarner Losh fb_helper->crtc_info[i].mode_set.num_connectors = 0; 538*592ffb21SWarner Losh } 539*592ffb21SWarner Losh 540*592ffb21SWarner Losh i = 0; 541*592ffb21SWarner Losh list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 542*592ffb21SWarner Losh fb_helper->crtc_info[i].mode_set.crtc = crtc; 543*592ffb21SWarner Losh i++; 544*592ffb21SWarner Losh } 545*592ffb21SWarner Losh 546*592ffb21SWarner Losh return 0; 547*592ffb21SWarner Losh out_free: 548*592ffb21SWarner Losh drm_fb_helper_crtc_free(fb_helper); 549*592ffb21SWarner Losh return -ENOMEM; 550*592ffb21SWarner Losh } 551*592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_init); 552*592ffb21SWarner Losh 553*592ffb21SWarner Losh void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) 554*592ffb21SWarner Losh { 555*592ffb21SWarner Losh if (!list_empty(&fb_helper->kernel_fb_list)) { 556*592ffb21SWarner Losh list_del(&fb_helper->kernel_fb_list); 557*592ffb21SWarner Losh #if 0 && defined(FREEBSD_NOTYET) 558*592ffb21SWarner Losh if (list_empty(&kernel_fb_helper_list)) { 559*592ffb21SWarner Losh pr_info("drm: unregistered panic notifier\n"); 560*592ffb21SWarner Losh atomic_notifier_chain_unregister(&panic_notifier_list, 561*592ffb21SWarner Losh &paniced); 562*592ffb21SWarner Losh unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); 563*592ffb21SWarner Losh } 564*592ffb21SWarner Losh #endif /* FREEBSD_NOTYET */ 565*592ffb21SWarner Losh } 566*592ffb21SWarner Losh 567*592ffb21SWarner Losh drm_fb_helper_crtc_free(fb_helper); 568*592ffb21SWarner Losh 569*592ffb21SWarner Losh } 570*592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_fini); 571*592ffb21SWarner Losh 572*592ffb21SWarner Losh #if 0 && defined(FREEBSD_NOTYET) 573*592ffb21SWarner Losh static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, 574*592ffb21SWarner Losh u16 blue, u16 regno, struct fb_info *info) 575*592ffb21SWarner Losh { 576*592ffb21SWarner Losh struct drm_fb_helper *fb_helper = info->par; 577*592ffb21SWarner Losh struct drm_framebuffer *fb = fb_helper->fb; 578*592ffb21SWarner Losh int pindex; 579*592ffb21SWarner Losh 580*592ffb21SWarner Losh if (info->fix.visual == FB_VISUAL_TRUECOLOR) { 581*592ffb21SWarner Losh u32 *palette; 582*592ffb21SWarner Losh u32 value; 583*592ffb21SWarner Losh /* place color in pseudopalette */ 584*592ffb21SWarner Losh if (regno > 16) 585*592ffb21SWarner Losh return -EINVAL; 586*592ffb21SWarner Losh palette = (u32 *)info->pseudo_palette; 587*592ffb21SWarner Losh red >>= (16 - info->var.red.length); 588*592ffb21SWarner Losh green >>= (16 - info->var.green.length); 589*592ffb21SWarner Losh blue >>= (16 - info->var.blue.length); 590*592ffb21SWarner Losh value = (red << info->var.red.offset) | 591*592ffb21SWarner Losh (green << info->var.green.offset) | 592*592ffb21SWarner Losh (blue << info->var.blue.offset); 593*592ffb21SWarner Losh if (info->var.transp.length > 0) { 594*592ffb21SWarner Losh u32 mask = (1 << info->var.transp.length) - 1; 595*592ffb21SWarner Losh mask <<= info->var.transp.offset; 596*592ffb21SWarner Losh value |= mask; 597*592ffb21SWarner Losh } 598*592ffb21SWarner Losh palette[regno] = value; 599*592ffb21SWarner Losh return 0; 600*592ffb21SWarner Losh } 601*592ffb21SWarner Losh 602*592ffb21SWarner Losh pindex = regno; 603*592ffb21SWarner Losh 604*592ffb21SWarner Losh if (fb->bits_per_pixel == 16) { 605*592ffb21SWarner Losh pindex = regno << 3; 606*592ffb21SWarner Losh 607*592ffb21SWarner Losh if (fb->depth == 16 && regno > 63) 608*592ffb21SWarner Losh return -EINVAL; 609*592ffb21SWarner Losh if (fb->depth == 15 && regno > 31) 610*592ffb21SWarner Losh return -EINVAL; 611*592ffb21SWarner Losh 612*592ffb21SWarner Losh if (fb->depth == 16) { 613*592ffb21SWarner Losh u16 r, g, b; 614*592ffb21SWarner Losh int i; 615*592ffb21SWarner Losh if (regno < 32) { 616*592ffb21SWarner Losh for (i = 0; i < 8; i++) 617*592ffb21SWarner Losh fb_helper->funcs->gamma_set(crtc, red, 618*592ffb21SWarner Losh green, blue, pindex + i); 619*592ffb21SWarner Losh } 620*592ffb21SWarner Losh 621*592ffb21SWarner Losh fb_helper->funcs->gamma_get(crtc, &r, 622*592ffb21SWarner Losh &g, &b, 623*592ffb21SWarner Losh pindex >> 1); 624*592ffb21SWarner Losh 625*592ffb21SWarner Losh for (i = 0; i < 4; i++) 626*592ffb21SWarner Losh fb_helper->funcs->gamma_set(crtc, r, 627*592ffb21SWarner Losh green, b, 628*592ffb21SWarner Losh (pindex >> 1) + i); 629*592ffb21SWarner Losh } 630*592ffb21SWarner Losh } 631*592ffb21SWarner Losh 632*592ffb21SWarner Losh if (fb->depth != 16) 633*592ffb21SWarner Losh fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex); 634*592ffb21SWarner Losh return 0; 635*592ffb21SWarner Losh } 636*592ffb21SWarner Losh 637*592ffb21SWarner Losh int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) 638*592ffb21SWarner Losh { 639*592ffb21SWarner Losh struct drm_fb_helper *fb_helper = info->par; 640*592ffb21SWarner Losh struct drm_crtc_helper_funcs *crtc_funcs; 641*592ffb21SWarner Losh u16 *red, *green, *blue, *transp; 642*592ffb21SWarner Losh struct drm_crtc *crtc; 643*592ffb21SWarner Losh int i, j, rc = 0; 644*592ffb21SWarner Losh int start; 645*592ffb21SWarner Losh 646*592ffb21SWarner Losh for (i = 0; i < fb_helper->crtc_count; i++) { 647*592ffb21SWarner Losh crtc = fb_helper->crtc_info[i].mode_set.crtc; 648*592ffb21SWarner Losh crtc_funcs = crtc->helper_private; 649*592ffb21SWarner Losh 650*592ffb21SWarner Losh red = cmap->red; 651*592ffb21SWarner Losh green = cmap->green; 652*592ffb21SWarner Losh blue = cmap->blue; 653*592ffb21SWarner Losh transp = cmap->transp; 654*592ffb21SWarner Losh start = cmap->start; 655*592ffb21SWarner Losh 656*592ffb21SWarner Losh for (j = 0; j < cmap->len; j++) { 657*592ffb21SWarner Losh u16 hred, hgreen, hblue, htransp = 0xffff; 658*592ffb21SWarner Losh 659*592ffb21SWarner Losh hred = *red++; 660*592ffb21SWarner Losh hgreen = *green++; 661*592ffb21SWarner Losh hblue = *blue++; 662*592ffb21SWarner Losh 663*592ffb21SWarner Losh if (transp) 664*592ffb21SWarner Losh htransp = *transp++; 665*592ffb21SWarner Losh 666*592ffb21SWarner Losh rc = setcolreg(crtc, hred, hgreen, hblue, start++, info); 667*592ffb21SWarner Losh if (rc) 668*592ffb21SWarner Losh return rc; 669*592ffb21SWarner Losh } 670*592ffb21SWarner Losh crtc_funcs->load_lut(crtc); 671*592ffb21SWarner Losh } 672*592ffb21SWarner Losh return rc; 673*592ffb21SWarner Losh } 674*592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_setcmap); 675*592ffb21SWarner Losh 676*592ffb21SWarner Losh int drm_fb_helper_check_var(struct fb_var_screeninfo *var, 677*592ffb21SWarner Losh struct fb_info *info) 678*592ffb21SWarner Losh { 679*592ffb21SWarner Losh struct drm_fb_helper *fb_helper = info->par; 680*592ffb21SWarner Losh struct drm_framebuffer *fb = fb_helper->fb; 681*592ffb21SWarner Losh int depth; 682*592ffb21SWarner Losh 683*592ffb21SWarner Losh if (var->pixclock != 0 || in_dbg_master()) 684*592ffb21SWarner Losh return -EINVAL; 685*592ffb21SWarner Losh 686*592ffb21SWarner Losh /* Need to resize the fb object !!! */ 687*592ffb21SWarner Losh if (var->bits_per_pixel > fb->bits_per_pixel || 688*592ffb21SWarner Losh var->xres > fb->width || var->yres > fb->height || 689*592ffb21SWarner Losh var->xres_virtual > fb->width || var->yres_virtual > fb->height) { 690*592ffb21SWarner Losh DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb " 691*592ffb21SWarner Losh "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n", 692*592ffb21SWarner Losh var->xres, var->yres, var->bits_per_pixel, 693*592ffb21SWarner Losh var->xres_virtual, var->yres_virtual, 694*592ffb21SWarner Losh fb->width, fb->height, fb->bits_per_pixel); 695*592ffb21SWarner Losh return -EINVAL; 696*592ffb21SWarner Losh } 697*592ffb21SWarner Losh 698*592ffb21SWarner Losh switch (var->bits_per_pixel) { 699*592ffb21SWarner Losh case 16: 700*592ffb21SWarner Losh depth = (var->green.length == 6) ? 16 : 15; 701*592ffb21SWarner Losh break; 702*592ffb21SWarner Losh case 32: 703*592ffb21SWarner Losh depth = (var->transp.length > 0) ? 32 : 24; 704*592ffb21SWarner Losh break; 705*592ffb21SWarner Losh default: 706*592ffb21SWarner Losh depth = var->bits_per_pixel; 707*592ffb21SWarner Losh break; 708*592ffb21SWarner Losh } 709*592ffb21SWarner Losh 710*592ffb21SWarner Losh switch (depth) { 711*592ffb21SWarner Losh case 8: 712*592ffb21SWarner Losh var->red.offset = 0; 713*592ffb21SWarner Losh var->green.offset = 0; 714*592ffb21SWarner Losh var->blue.offset = 0; 715*592ffb21SWarner Losh var->red.length = 8; 716*592ffb21SWarner Losh var->green.length = 8; 717*592ffb21SWarner Losh var->blue.length = 8; 718*592ffb21SWarner Losh var->transp.length = 0; 719*592ffb21SWarner Losh var->transp.offset = 0; 720*592ffb21SWarner Losh break; 721*592ffb21SWarner Losh case 15: 722*592ffb21SWarner Losh var->red.offset = 10; 723*592ffb21SWarner Losh var->green.offset = 5; 724*592ffb21SWarner Losh var->blue.offset = 0; 725*592ffb21SWarner Losh var->red.length = 5; 726*592ffb21SWarner Losh var->green.length = 5; 727*592ffb21SWarner Losh var->blue.length = 5; 728*592ffb21SWarner Losh var->transp.length = 1; 729*592ffb21SWarner Losh var->transp.offset = 15; 730*592ffb21SWarner Losh break; 731*592ffb21SWarner Losh case 16: 732*592ffb21SWarner Losh var->red.offset = 11; 733*592ffb21SWarner Losh var->green.offset = 5; 734*592ffb21SWarner Losh var->blue.offset = 0; 735*592ffb21SWarner Losh var->red.length = 5; 736*592ffb21SWarner Losh var->green.length = 6; 737*592ffb21SWarner Losh var->blue.length = 5; 738*592ffb21SWarner Losh var->transp.length = 0; 739*592ffb21SWarner Losh var->transp.offset = 0; 740*592ffb21SWarner Losh break; 741*592ffb21SWarner Losh case 24: 742*592ffb21SWarner Losh var->red.offset = 16; 743*592ffb21SWarner Losh var->green.offset = 8; 744*592ffb21SWarner Losh var->blue.offset = 0; 745*592ffb21SWarner Losh var->red.length = 8; 746*592ffb21SWarner Losh var->green.length = 8; 747*592ffb21SWarner Losh var->blue.length = 8; 748*592ffb21SWarner Losh var->transp.length = 0; 749*592ffb21SWarner Losh var->transp.offset = 0; 750*592ffb21SWarner Losh break; 751*592ffb21SWarner Losh case 32: 752*592ffb21SWarner Losh var->red.offset = 16; 753*592ffb21SWarner Losh var->green.offset = 8; 754*592ffb21SWarner Losh var->blue.offset = 0; 755*592ffb21SWarner Losh var->red.length = 8; 756*592ffb21SWarner Losh var->green.length = 8; 757*592ffb21SWarner Losh var->blue.length = 8; 758*592ffb21SWarner Losh var->transp.length = 8; 759*592ffb21SWarner Losh var->transp.offset = 24; 760*592ffb21SWarner Losh break; 761*592ffb21SWarner Losh default: 762*592ffb21SWarner Losh return -EINVAL; 763*592ffb21SWarner Losh } 764*592ffb21SWarner Losh return 0; 765*592ffb21SWarner Losh } 766*592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_check_var); 767*592ffb21SWarner Losh 768*592ffb21SWarner Losh /* this will let fbcon do the mode init */ 769*592ffb21SWarner Losh int drm_fb_helper_set_par(struct fb_info *info) 770*592ffb21SWarner Losh { 771*592ffb21SWarner Losh struct drm_fb_helper *fb_helper = info->par; 772*592ffb21SWarner Losh struct drm_device *dev = fb_helper->dev; 773*592ffb21SWarner Losh struct fb_var_screeninfo *var = &info->var; 774*592ffb21SWarner Losh struct drm_crtc *crtc; 775*592ffb21SWarner Losh int ret; 776*592ffb21SWarner Losh int i; 777*592ffb21SWarner Losh 778*592ffb21SWarner Losh if (var->pixclock != 0) { 779*592ffb21SWarner Losh DRM_ERROR("PIXEL CLOCK SET\n"); 780*592ffb21SWarner Losh return -EINVAL; 781*592ffb21SWarner Losh } 782*592ffb21SWarner Losh 783*592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 784*592ffb21SWarner Losh for (i = 0; i < fb_helper->crtc_count; i++) { 785*592ffb21SWarner Losh crtc = fb_helper->crtc_info[i].mode_set.crtc; 786*592ffb21SWarner Losh ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set); 787*592ffb21SWarner Losh if (ret) { 788*592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 789*592ffb21SWarner Losh return ret; 790*592ffb21SWarner Losh } 791*592ffb21SWarner Losh } 792*592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 793*592ffb21SWarner Losh 794*592ffb21SWarner Losh if (fb_helper->delayed_hotplug) { 795*592ffb21SWarner Losh fb_helper->delayed_hotplug = false; 796*592ffb21SWarner Losh drm_fb_helper_hotplug_event(fb_helper); 797*592ffb21SWarner Losh } 798*592ffb21SWarner Losh return 0; 799*592ffb21SWarner Losh } 800*592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_set_par); 801*592ffb21SWarner Losh 802*592ffb21SWarner Losh int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, 803*592ffb21SWarner Losh struct fb_info *info) 804*592ffb21SWarner Losh { 805*592ffb21SWarner Losh struct drm_fb_helper *fb_helper = info->par; 806*592ffb21SWarner Losh struct drm_device *dev = fb_helper->dev; 807*592ffb21SWarner Losh struct drm_mode_set *modeset; 808*592ffb21SWarner Losh struct drm_crtc *crtc; 809*592ffb21SWarner Losh int ret = 0; 810*592ffb21SWarner Losh int i; 811*592ffb21SWarner Losh 812*592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 813*592ffb21SWarner Losh for (i = 0; i < fb_helper->crtc_count; i++) { 814*592ffb21SWarner Losh crtc = fb_helper->crtc_info[i].mode_set.crtc; 815*592ffb21SWarner Losh 816*592ffb21SWarner Losh modeset = &fb_helper->crtc_info[i].mode_set; 817*592ffb21SWarner Losh 818*592ffb21SWarner Losh modeset->x = var->xoffset; 819*592ffb21SWarner Losh modeset->y = var->yoffset; 820*592ffb21SWarner Losh 821*592ffb21SWarner Losh if (modeset->num_connectors) { 822*592ffb21SWarner Losh ret = crtc->funcs->set_config(modeset); 823*592ffb21SWarner Losh if (!ret) { 824*592ffb21SWarner Losh info->var.xoffset = var->xoffset; 825*592ffb21SWarner Losh info->var.yoffset = var->yoffset; 826*592ffb21SWarner Losh } 827*592ffb21SWarner Losh } 828*592ffb21SWarner Losh } 829*592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 830*592ffb21SWarner Losh return ret; 831*592ffb21SWarner Losh } 832*592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_pan_display); 833*592ffb21SWarner Losh #endif /* FREEBSD_NOTYET */ 834*592ffb21SWarner Losh 835*592ffb21SWarner Losh int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, 836*592ffb21SWarner Losh int preferred_bpp) 837*592ffb21SWarner Losh { 838*592ffb21SWarner Losh int new_fb = 0; 839*592ffb21SWarner Losh int crtc_count = 0; 840*592ffb21SWarner Losh int i; 841*592ffb21SWarner Losh struct fb_info *info; 842*592ffb21SWarner Losh struct drm_fb_helper_surface_size sizes; 843*592ffb21SWarner Losh int gamma_size = 0; 844*592ffb21SWarner Losh #if defined(__FreeBSD__) 845*592ffb21SWarner Losh struct drm_crtc *crtc; 846*592ffb21SWarner Losh struct drm_device *dev; 847*592ffb21SWarner Losh int ret; 848*592ffb21SWarner Losh device_t kdev; 849*592ffb21SWarner Losh #endif 850*592ffb21SWarner Losh 851*592ffb21SWarner Losh memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size)); 852*592ffb21SWarner Losh sizes.surface_depth = 24; 853*592ffb21SWarner Losh sizes.surface_bpp = 32; 854*592ffb21SWarner Losh sizes.fb_width = (unsigned)-1; 855*592ffb21SWarner Losh sizes.fb_height = (unsigned)-1; 856*592ffb21SWarner Losh 857*592ffb21SWarner Losh /* if driver picks 8 or 16 by default use that 858*592ffb21SWarner Losh for both depth/bpp */ 859*592ffb21SWarner Losh if (preferred_bpp != sizes.surface_bpp) 860*592ffb21SWarner Losh sizes.surface_depth = sizes.surface_bpp = preferred_bpp; 861*592ffb21SWarner Losh 862*592ffb21SWarner Losh /* first up get a count of crtcs now in use and new min/maxes width/heights */ 863*592ffb21SWarner Losh for (i = 0; i < fb_helper->connector_count; i++) { 864*592ffb21SWarner Losh struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i]; 865*592ffb21SWarner Losh struct drm_cmdline_mode *cmdline_mode; 866*592ffb21SWarner Losh 867*592ffb21SWarner Losh cmdline_mode = &fb_helper_conn->cmdline_mode; 868*592ffb21SWarner Losh 869*592ffb21SWarner Losh if (cmdline_mode->bpp_specified) { 870*592ffb21SWarner Losh switch (cmdline_mode->bpp) { 871*592ffb21SWarner Losh case 8: 872*592ffb21SWarner Losh sizes.surface_depth = sizes.surface_bpp = 8; 873*592ffb21SWarner Losh break; 874*592ffb21SWarner Losh case 15: 875*592ffb21SWarner Losh sizes.surface_depth = 15; 876*592ffb21SWarner Losh sizes.surface_bpp = 16; 877*592ffb21SWarner Losh break; 878*592ffb21SWarner Losh case 16: 879*592ffb21SWarner Losh sizes.surface_depth = sizes.surface_bpp = 16; 880*592ffb21SWarner Losh break; 881*592ffb21SWarner Losh case 24: 882*592ffb21SWarner Losh sizes.surface_depth = sizes.surface_bpp = 24; 883*592ffb21SWarner Losh break; 884*592ffb21SWarner Losh case 32: 885*592ffb21SWarner Losh sizes.surface_depth = 24; 886*592ffb21SWarner Losh sizes.surface_bpp = 32; 887*592ffb21SWarner Losh break; 888*592ffb21SWarner Losh } 889*592ffb21SWarner Losh break; 890*592ffb21SWarner Losh } 891*592ffb21SWarner Losh } 892*592ffb21SWarner Losh 893*592ffb21SWarner Losh crtc_count = 0; 894*592ffb21SWarner Losh for (i = 0; i < fb_helper->crtc_count; i++) { 895*592ffb21SWarner Losh struct drm_display_mode *desired_mode; 896*592ffb21SWarner Losh desired_mode = fb_helper->crtc_info[i].desired_mode; 897*592ffb21SWarner Losh 898*592ffb21SWarner Losh if (desired_mode) { 899*592ffb21SWarner Losh if (gamma_size == 0) 900*592ffb21SWarner Losh gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size; 901*592ffb21SWarner Losh if (desired_mode->hdisplay < sizes.fb_width) 902*592ffb21SWarner Losh sizes.fb_width = desired_mode->hdisplay; 903*592ffb21SWarner Losh if (desired_mode->vdisplay < sizes.fb_height) 904*592ffb21SWarner Losh sizes.fb_height = desired_mode->vdisplay; 905*592ffb21SWarner Losh if (desired_mode->hdisplay > sizes.surface_width) 906*592ffb21SWarner Losh sizes.surface_width = desired_mode->hdisplay; 907*592ffb21SWarner Losh if (desired_mode->vdisplay > sizes.surface_height) 908*592ffb21SWarner Losh sizes.surface_height = desired_mode->vdisplay; 909*592ffb21SWarner Losh crtc_count++; 910*592ffb21SWarner Losh } 911*592ffb21SWarner Losh } 912*592ffb21SWarner Losh 913*592ffb21SWarner Losh if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) { 914*592ffb21SWarner Losh /* hmm everyone went away - assume VGA cable just fell out 915*592ffb21SWarner Losh and will come back later. */ 916*592ffb21SWarner Losh DRM_INFO("Cannot find any crtc or sizes - going 1024x768\n"); 917*592ffb21SWarner Losh sizes.fb_width = sizes.surface_width = 1024; 918*592ffb21SWarner Losh sizes.fb_height = sizes.surface_height = 768; 919*592ffb21SWarner Losh } 920*592ffb21SWarner Losh 921*592ffb21SWarner Losh /* push down into drivers */ 922*592ffb21SWarner Losh new_fb = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes); 923*592ffb21SWarner Losh if (new_fb < 0) 924*592ffb21SWarner Losh return new_fb; 925*592ffb21SWarner Losh 926*592ffb21SWarner Losh info = fb_helper->fbdev; 927*592ffb21SWarner Losh 928*592ffb21SWarner Losh kdev = fb_helper->dev->dev; 929*592ffb21SWarner Losh info->fb_video_dev = device_get_parent(kdev); 930*592ffb21SWarner Losh 931*592ffb21SWarner Losh /* set the fb pointer */ 932*592ffb21SWarner Losh for (i = 0; i < fb_helper->crtc_count; i++) 933*592ffb21SWarner Losh fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; 934*592ffb21SWarner Losh 935*592ffb21SWarner Losh #if defined(__FreeBSD__) 936*592ffb21SWarner Losh if (new_fb) { 937*592ffb21SWarner Losh int ret; 938*592ffb21SWarner Losh 939*592ffb21SWarner Losh info->fb_fbd_dev = device_add_child(kdev, "fbd", 940*592ffb21SWarner Losh device_get_unit(kdev)); 941*592ffb21SWarner Losh if (info->fb_fbd_dev != NULL) 942*592ffb21SWarner Losh ret = device_probe_and_attach(info->fb_fbd_dev); 943*592ffb21SWarner Losh else 944*592ffb21SWarner Losh ret = ENODEV; 945*592ffb21SWarner Losh #ifdef DEV_VT 946*592ffb21SWarner Losh if (ret != 0) 947*592ffb21SWarner Losh DRM_ERROR("Failed to attach fbd device: %d\n", ret); 948*592ffb21SWarner Losh #endif 949*592ffb21SWarner Losh } else { 950*592ffb21SWarner Losh /* Modified version of drm_fb_helper_set_par() */ 951*592ffb21SWarner Losh dev = fb_helper->dev; 952*592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 953*592ffb21SWarner Losh for (i = 0; i < fb_helper->crtc_count; i++) { 954*592ffb21SWarner Losh crtc = fb_helper->crtc_info[i].mode_set.crtc; 955*592ffb21SWarner Losh ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set); 956*592ffb21SWarner Losh if (ret) { 957*592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 958*592ffb21SWarner Losh return ret; 959*592ffb21SWarner Losh } 960*592ffb21SWarner Losh } 961*592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 962*592ffb21SWarner Losh 963*592ffb21SWarner Losh if (fb_helper->delayed_hotplug) { 964*592ffb21SWarner Losh fb_helper->delayed_hotplug = false; 965*592ffb21SWarner Losh drm_fb_helper_hotplug_event(fb_helper); 966*592ffb21SWarner Losh } 967*592ffb21SWarner Losh } 968*592ffb21SWarner Losh #else 969*592ffb21SWarner Losh if (new_fb) { 970*592ffb21SWarner Losh info->var.pixclock = 0; 971*592ffb21SWarner Losh if (register_framebuffer(info) < 0) 972*592ffb21SWarner Losh return -EINVAL; 973*592ffb21SWarner Losh 974*592ffb21SWarner Losh dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n", 975*592ffb21SWarner Losh info->node, info->fix.id); 976*592ffb21SWarner Losh 977*592ffb21SWarner Losh } else { 978*592ffb21SWarner Losh drm_fb_helper_set_par(info); 979*592ffb21SWarner Losh } 980*592ffb21SWarner Losh #endif 981*592ffb21SWarner Losh 982*592ffb21SWarner Losh #if 0 && defined(FREEBSD_NOTYET) 983*592ffb21SWarner Losh /* Switch back to kernel console on panic */ 984*592ffb21SWarner Losh /* multi card linked list maybe */ 985*592ffb21SWarner Losh if (list_empty(&kernel_fb_helper_list)) { 986*592ffb21SWarner Losh dev_info(fb_helper->dev->dev, "registered panic notifier\n"); 987*592ffb21SWarner Losh atomic_notifier_chain_register(&panic_notifier_list, 988*592ffb21SWarner Losh &paniced); 989*592ffb21SWarner Losh register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); 990*592ffb21SWarner Losh } 991*592ffb21SWarner Losh #endif /* FREEBSD_NOTYET */ 992*592ffb21SWarner Losh if (new_fb) 993*592ffb21SWarner Losh list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list); 994*592ffb21SWarner Losh 995*592ffb21SWarner Losh return 0; 996*592ffb21SWarner Losh } 997*592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_single_fb_probe); 998*592ffb21SWarner Losh 999*592ffb21SWarner Losh void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, 1000*592ffb21SWarner Losh uint32_t depth) 1001*592ffb21SWarner Losh { 1002*592ffb21SWarner Losh info->fb_stride = pitch; 1003*592ffb21SWarner Losh 1004*592ffb21SWarner Losh return; 1005*592ffb21SWarner Losh } 1006*592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_fill_fix); 1007*592ffb21SWarner Losh 1008*592ffb21SWarner Losh void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper, 1009*592ffb21SWarner Losh uint32_t fb_width, uint32_t fb_height) 1010*592ffb21SWarner Losh { 1011*592ffb21SWarner Losh struct drm_framebuffer *fb = fb_helper->fb; 1012*592ffb21SWarner Losh struct vt_kms_softc *sc; 1013*592ffb21SWarner Losh 1014*592ffb21SWarner Losh info->fb_name = device_get_nameunit(fb_helper->dev->dev); 1015*592ffb21SWarner Losh info->fb_width = fb->width; 1016*592ffb21SWarner Losh info->fb_height = fb->height; 1017*592ffb21SWarner Losh info->fb_depth = fb->bits_per_pixel; 1018*592ffb21SWarner Losh 1019*592ffb21SWarner Losh sc = (struct vt_kms_softc *)info->fb_priv; 1020*592ffb21SWarner Losh sc->fb_helper = fb_helper; 1021*592ffb21SWarner Losh } 1022*592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_fill_var); 1023*592ffb21SWarner Losh 1024*592ffb21SWarner Losh static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper, 1025*592ffb21SWarner Losh uint32_t maxX, 1026*592ffb21SWarner Losh uint32_t maxY) 1027*592ffb21SWarner Losh { 1028*592ffb21SWarner Losh struct drm_connector *connector; 1029*592ffb21SWarner Losh int count = 0; 1030*592ffb21SWarner Losh int i; 1031*592ffb21SWarner Losh 1032*592ffb21SWarner Losh for (i = 0; i < fb_helper->connector_count; i++) { 1033*592ffb21SWarner Losh connector = fb_helper->connector_info[i]->connector; 1034*592ffb21SWarner Losh count += connector->funcs->fill_modes(connector, maxX, maxY); 1035*592ffb21SWarner Losh } 1036*592ffb21SWarner Losh 1037*592ffb21SWarner Losh return count; 1038*592ffb21SWarner Losh } 1039*592ffb21SWarner Losh 1040*592ffb21SWarner Losh static struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height) 1041*592ffb21SWarner Losh { 1042*592ffb21SWarner Losh struct drm_display_mode *mode; 1043*592ffb21SWarner Losh 1044*592ffb21SWarner Losh list_for_each_entry(mode, &fb_connector->connector->modes, head) { 1045*592ffb21SWarner Losh if (drm_mode_width(mode) > width || 1046*592ffb21SWarner Losh drm_mode_height(mode) > height) 1047*592ffb21SWarner Losh continue; 1048*592ffb21SWarner Losh if (mode->type & DRM_MODE_TYPE_PREFERRED) 1049*592ffb21SWarner Losh return mode; 1050*592ffb21SWarner Losh } 1051*592ffb21SWarner Losh return NULL; 1052*592ffb21SWarner Losh } 1053*592ffb21SWarner Losh 1054*592ffb21SWarner Losh static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector) 1055*592ffb21SWarner Losh { 1056*592ffb21SWarner Losh struct drm_cmdline_mode *cmdline_mode; 1057*592ffb21SWarner Losh cmdline_mode = &fb_connector->cmdline_mode; 1058*592ffb21SWarner Losh return cmdline_mode->specified; 1059*592ffb21SWarner Losh } 1060*592ffb21SWarner Losh 1061*592ffb21SWarner Losh static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn, 1062*592ffb21SWarner Losh int width, int height) 1063*592ffb21SWarner Losh { 1064*592ffb21SWarner Losh struct drm_cmdline_mode *cmdline_mode; 1065*592ffb21SWarner Losh struct drm_display_mode *mode = NULL; 1066*592ffb21SWarner Losh 1067*592ffb21SWarner Losh cmdline_mode = &fb_helper_conn->cmdline_mode; 1068*592ffb21SWarner Losh if (cmdline_mode->specified == false) 1069*592ffb21SWarner Losh return mode; 1070*592ffb21SWarner Losh 1071*592ffb21SWarner Losh /* attempt to find a matching mode in the list of modes 1072*592ffb21SWarner Losh * we have gotten so far, if not add a CVT mode that conforms 1073*592ffb21SWarner Losh */ 1074*592ffb21SWarner Losh if (cmdline_mode->rb || cmdline_mode->margins) 1075*592ffb21SWarner Losh goto create_mode; 1076*592ffb21SWarner Losh 1077*592ffb21SWarner Losh list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { 1078*592ffb21SWarner Losh /* check width/height */ 1079*592ffb21SWarner Losh if (mode->hdisplay != cmdline_mode->xres || 1080*592ffb21SWarner Losh mode->vdisplay != cmdline_mode->yres) 1081*592ffb21SWarner Losh continue; 1082*592ffb21SWarner Losh 1083*592ffb21SWarner Losh if (cmdline_mode->refresh_specified) { 1084*592ffb21SWarner Losh if (mode->vrefresh != cmdline_mode->refresh) 1085*592ffb21SWarner Losh continue; 1086*592ffb21SWarner Losh } 1087*592ffb21SWarner Losh 1088*592ffb21SWarner Losh if (cmdline_mode->interlace) { 1089*592ffb21SWarner Losh if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) 1090*592ffb21SWarner Losh continue; 1091*592ffb21SWarner Losh } 1092*592ffb21SWarner Losh return mode; 1093*592ffb21SWarner Losh } 1094*592ffb21SWarner Losh 1095*592ffb21SWarner Losh create_mode: 1096*592ffb21SWarner Losh mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev, 1097*592ffb21SWarner Losh cmdline_mode); 1098*592ffb21SWarner Losh list_add(&mode->head, &fb_helper_conn->connector->modes); 1099*592ffb21SWarner Losh return mode; 1100*592ffb21SWarner Losh } 1101*592ffb21SWarner Losh 1102*592ffb21SWarner Losh static bool drm_connector_enabled(struct drm_connector *connector, bool strict) 1103*592ffb21SWarner Losh { 1104*592ffb21SWarner Losh bool enable; 1105*592ffb21SWarner Losh 1106*592ffb21SWarner Losh if (strict) 1107*592ffb21SWarner Losh enable = connector->status == connector_status_connected; 1108*592ffb21SWarner Losh else 1109*592ffb21SWarner Losh enable = connector->status != connector_status_disconnected; 1110*592ffb21SWarner Losh 1111*592ffb21SWarner Losh return enable; 1112*592ffb21SWarner Losh } 1113*592ffb21SWarner Losh 1114*592ffb21SWarner Losh static void drm_enable_connectors(struct drm_fb_helper *fb_helper, 1115*592ffb21SWarner Losh bool *enabled) 1116*592ffb21SWarner Losh { 1117*592ffb21SWarner Losh bool any_enabled = false; 1118*592ffb21SWarner Losh struct drm_connector *connector; 1119*592ffb21SWarner Losh int i = 0; 1120*592ffb21SWarner Losh 1121*592ffb21SWarner Losh for (i = 0; i < fb_helper->connector_count; i++) { 1122*592ffb21SWarner Losh connector = fb_helper->connector_info[i]->connector; 1123*592ffb21SWarner Losh enabled[i] = drm_connector_enabled(connector, true); 1124*592ffb21SWarner Losh DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, 1125*592ffb21SWarner Losh enabled[i] ? "yes" : "no"); 1126*592ffb21SWarner Losh any_enabled |= enabled[i]; 1127*592ffb21SWarner Losh } 1128*592ffb21SWarner Losh 1129*592ffb21SWarner Losh if (any_enabled) 1130*592ffb21SWarner Losh return; 1131*592ffb21SWarner Losh 1132*592ffb21SWarner Losh for (i = 0; i < fb_helper->connector_count; i++) { 1133*592ffb21SWarner Losh connector = fb_helper->connector_info[i]->connector; 1134*592ffb21SWarner Losh enabled[i] = drm_connector_enabled(connector, false); 1135*592ffb21SWarner Losh } 1136*592ffb21SWarner Losh } 1137*592ffb21SWarner Losh 1138*592ffb21SWarner Losh static bool drm_target_cloned(struct drm_fb_helper *fb_helper, 1139*592ffb21SWarner Losh struct drm_display_mode **modes, 1140*592ffb21SWarner Losh bool *enabled, int width, int height) 1141*592ffb21SWarner Losh { 1142*592ffb21SWarner Losh int count, i, j; 1143*592ffb21SWarner Losh bool can_clone = false; 1144*592ffb21SWarner Losh struct drm_fb_helper_connector *fb_helper_conn; 1145*592ffb21SWarner Losh struct drm_display_mode *dmt_mode, *mode; 1146*592ffb21SWarner Losh 1147*592ffb21SWarner Losh /* only contemplate cloning in the single crtc case */ 1148*592ffb21SWarner Losh if (fb_helper->crtc_count > 1) 1149*592ffb21SWarner Losh return false; 1150*592ffb21SWarner Losh 1151*592ffb21SWarner Losh count = 0; 1152*592ffb21SWarner Losh for (i = 0; i < fb_helper->connector_count; i++) { 1153*592ffb21SWarner Losh if (enabled[i]) 1154*592ffb21SWarner Losh count++; 1155*592ffb21SWarner Losh } 1156*592ffb21SWarner Losh 1157*592ffb21SWarner Losh /* only contemplate cloning if more than one connector is enabled */ 1158*592ffb21SWarner Losh if (count <= 1) 1159*592ffb21SWarner Losh return false; 1160*592ffb21SWarner Losh 1161*592ffb21SWarner Losh /* check the command line or if nothing common pick 1024x768 */ 1162*592ffb21SWarner Losh can_clone = true; 1163*592ffb21SWarner Losh for (i = 0; i < fb_helper->connector_count; i++) { 1164*592ffb21SWarner Losh if (!enabled[i]) 1165*592ffb21SWarner Losh continue; 1166*592ffb21SWarner Losh fb_helper_conn = fb_helper->connector_info[i]; 1167*592ffb21SWarner Losh modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height); 1168*592ffb21SWarner Losh if (!modes[i]) { 1169*592ffb21SWarner Losh can_clone = false; 1170*592ffb21SWarner Losh break; 1171*592ffb21SWarner Losh } 1172*592ffb21SWarner Losh for (j = 0; j < i; j++) { 1173*592ffb21SWarner Losh if (!enabled[j]) 1174*592ffb21SWarner Losh continue; 1175*592ffb21SWarner Losh if (!drm_mode_equal(modes[j], modes[i])) 1176*592ffb21SWarner Losh can_clone = false; 1177*592ffb21SWarner Losh } 1178*592ffb21SWarner Losh } 1179*592ffb21SWarner Losh 1180*592ffb21SWarner Losh if (can_clone) { 1181*592ffb21SWarner Losh DRM_DEBUG_KMS("can clone using command line\n"); 1182*592ffb21SWarner Losh return true; 1183*592ffb21SWarner Losh } 1184*592ffb21SWarner Losh 1185*592ffb21SWarner Losh /* try and find a 1024x768 mode on each connector */ 1186*592ffb21SWarner Losh can_clone = true; 1187*592ffb21SWarner Losh dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false); 1188*592ffb21SWarner Losh 1189*592ffb21SWarner Losh for (i = 0; i < fb_helper->connector_count; i++) { 1190*592ffb21SWarner Losh 1191*592ffb21SWarner Losh if (!enabled[i]) 1192*592ffb21SWarner Losh continue; 1193*592ffb21SWarner Losh 1194*592ffb21SWarner Losh fb_helper_conn = fb_helper->connector_info[i]; 1195*592ffb21SWarner Losh list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { 1196*592ffb21SWarner Losh if (drm_mode_equal(mode, dmt_mode)) 1197*592ffb21SWarner Losh modes[i] = mode; 1198*592ffb21SWarner Losh } 1199*592ffb21SWarner Losh if (!modes[i]) 1200*592ffb21SWarner Losh can_clone = false; 1201*592ffb21SWarner Losh } 1202*592ffb21SWarner Losh 1203*592ffb21SWarner Losh if (can_clone) { 1204*592ffb21SWarner Losh DRM_DEBUG_KMS("can clone using 1024x768\n"); 1205*592ffb21SWarner Losh return true; 1206*592ffb21SWarner Losh } 1207*592ffb21SWarner Losh DRM_INFO("kms: can't enable cloning when we probably wanted to.\n"); 1208*592ffb21SWarner Losh return false; 1209*592ffb21SWarner Losh } 1210*592ffb21SWarner Losh 1211*592ffb21SWarner Losh static bool drm_target_preferred(struct drm_fb_helper *fb_helper, 1212*592ffb21SWarner Losh struct drm_display_mode **modes, 1213*592ffb21SWarner Losh bool *enabled, int width, int height) 1214*592ffb21SWarner Losh { 1215*592ffb21SWarner Losh struct drm_fb_helper_connector *fb_helper_conn; 1216*592ffb21SWarner Losh int i; 1217*592ffb21SWarner Losh 1218*592ffb21SWarner Losh for (i = 0; i < fb_helper->connector_count; i++) { 1219*592ffb21SWarner Losh fb_helper_conn = fb_helper->connector_info[i]; 1220*592ffb21SWarner Losh 1221*592ffb21SWarner Losh if (enabled[i] == false) 1222*592ffb21SWarner Losh continue; 1223*592ffb21SWarner Losh 1224*592ffb21SWarner Losh DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", 1225*592ffb21SWarner Losh fb_helper_conn->connector->base.id); 1226*592ffb21SWarner Losh 1227*592ffb21SWarner Losh /* got for command line mode first */ 1228*592ffb21SWarner Losh modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height); 1229*592ffb21SWarner Losh if (!modes[i]) { 1230*592ffb21SWarner Losh DRM_DEBUG_KMS("looking for preferred mode on connector %d\n", 1231*592ffb21SWarner Losh fb_helper_conn->connector->base.id); 1232*592ffb21SWarner Losh modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height); 1233*592ffb21SWarner Losh } 1234*592ffb21SWarner Losh /* No preferred modes, pick one off the list */ 1235*592ffb21SWarner Losh if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) { 1236*592ffb21SWarner Losh list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head) 1237*592ffb21SWarner Losh break; 1238*592ffb21SWarner Losh } 1239*592ffb21SWarner Losh DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : 1240*592ffb21SWarner Losh "none"); 1241*592ffb21SWarner Losh } 1242*592ffb21SWarner Losh return true; 1243*592ffb21SWarner Losh } 1244*592ffb21SWarner Losh 1245*592ffb21SWarner Losh static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, 1246*592ffb21SWarner Losh struct drm_fb_helper_crtc **best_crtcs, 1247*592ffb21SWarner Losh struct drm_display_mode **modes, 1248*592ffb21SWarner Losh int n, int width, int height) 1249*592ffb21SWarner Losh { 1250*592ffb21SWarner Losh int c, o; 1251*592ffb21SWarner Losh struct drm_device *dev = fb_helper->dev; 1252*592ffb21SWarner Losh struct drm_connector *connector; 1253*592ffb21SWarner Losh struct drm_connector_helper_funcs *connector_funcs; 1254*592ffb21SWarner Losh struct drm_encoder *encoder; 1255*592ffb21SWarner Losh struct drm_fb_helper_crtc *best_crtc; 1256*592ffb21SWarner Losh int my_score, best_score, score; 1257*592ffb21SWarner Losh struct drm_fb_helper_crtc **crtcs, *crtc; 1258*592ffb21SWarner Losh struct drm_fb_helper_connector *fb_helper_conn; 1259*592ffb21SWarner Losh 1260*592ffb21SWarner Losh if (n == fb_helper->connector_count) 1261*592ffb21SWarner Losh return 0; 1262*592ffb21SWarner Losh 1263*592ffb21SWarner Losh fb_helper_conn = fb_helper->connector_info[n]; 1264*592ffb21SWarner Losh connector = fb_helper_conn->connector; 1265*592ffb21SWarner Losh 1266*592ffb21SWarner Losh best_crtcs[n] = NULL; 1267*592ffb21SWarner Losh best_crtc = NULL; 1268*592ffb21SWarner Losh best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height); 1269*592ffb21SWarner Losh if (modes[n] == NULL) 1270*592ffb21SWarner Losh return best_score; 1271*592ffb21SWarner Losh 1272*592ffb21SWarner Losh crtcs = malloc(dev->mode_config.num_connector * 1273*592ffb21SWarner Losh sizeof(struct drm_fb_helper_crtc *), DRM_MEM_KMS, M_NOWAIT | M_ZERO); 1274*592ffb21SWarner Losh if (!crtcs) 1275*592ffb21SWarner Losh return best_score; 1276*592ffb21SWarner Losh 1277*592ffb21SWarner Losh my_score = 1; 1278*592ffb21SWarner Losh if (connector->status == connector_status_connected) 1279*592ffb21SWarner Losh my_score++; 1280*592ffb21SWarner Losh if (drm_has_cmdline_mode(fb_helper_conn)) 1281*592ffb21SWarner Losh my_score++; 1282*592ffb21SWarner Losh if (drm_has_preferred_mode(fb_helper_conn, width, height)) 1283*592ffb21SWarner Losh my_score++; 1284*592ffb21SWarner Losh 1285*592ffb21SWarner Losh connector_funcs = connector->helper_private; 1286*592ffb21SWarner Losh encoder = connector_funcs->best_encoder(connector); 1287*592ffb21SWarner Losh if (!encoder) 1288*592ffb21SWarner Losh goto out; 1289*592ffb21SWarner Losh 1290*592ffb21SWarner Losh /* select a crtc for this connector and then attempt to configure 1291*592ffb21SWarner Losh remaining connectors */ 1292*592ffb21SWarner Losh for (c = 0; c < fb_helper->crtc_count; c++) { 1293*592ffb21SWarner Losh crtc = &fb_helper->crtc_info[c]; 1294*592ffb21SWarner Losh 1295*592ffb21SWarner Losh if ((encoder->possible_crtcs & (1 << c)) == 0) 1296*592ffb21SWarner Losh continue; 1297*592ffb21SWarner Losh 1298*592ffb21SWarner Losh for (o = 0; o < n; o++) 1299*592ffb21SWarner Losh if (best_crtcs[o] == crtc) 1300*592ffb21SWarner Losh break; 1301*592ffb21SWarner Losh 1302*592ffb21SWarner Losh if (o < n) { 1303*592ffb21SWarner Losh /* ignore cloning unless only a single crtc */ 1304*592ffb21SWarner Losh if (fb_helper->crtc_count > 1) 1305*592ffb21SWarner Losh continue; 1306*592ffb21SWarner Losh 1307*592ffb21SWarner Losh if (!drm_mode_equal(modes[o], modes[n])) 1308*592ffb21SWarner Losh continue; 1309*592ffb21SWarner Losh } 1310*592ffb21SWarner Losh 1311*592ffb21SWarner Losh crtcs[n] = crtc; 1312*592ffb21SWarner Losh memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *)); 1313*592ffb21SWarner Losh score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1, 1314*592ffb21SWarner Losh width, height); 1315*592ffb21SWarner Losh if (score > best_score) { 1316*592ffb21SWarner Losh best_crtc = crtc; 1317*592ffb21SWarner Losh best_score = score; 1318*592ffb21SWarner Losh memcpy(best_crtcs, crtcs, 1319*592ffb21SWarner Losh dev->mode_config.num_connector * 1320*592ffb21SWarner Losh sizeof(struct drm_fb_helper_crtc *)); 1321*592ffb21SWarner Losh } 1322*592ffb21SWarner Losh } 1323*592ffb21SWarner Losh out: 1324*592ffb21SWarner Losh free(crtcs, DRM_MEM_KMS); 1325*592ffb21SWarner Losh return best_score; 1326*592ffb21SWarner Losh } 1327*592ffb21SWarner Losh 1328*592ffb21SWarner Losh static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) 1329*592ffb21SWarner Losh { 1330*592ffb21SWarner Losh struct drm_device *dev = fb_helper->dev; 1331*592ffb21SWarner Losh struct drm_fb_helper_crtc **crtcs; 1332*592ffb21SWarner Losh struct drm_display_mode **modes; 1333*592ffb21SWarner Losh struct drm_mode_set *modeset; 1334*592ffb21SWarner Losh bool *enabled; 1335*592ffb21SWarner Losh int width, height; 1336*592ffb21SWarner Losh int i, ret; 1337*592ffb21SWarner Losh 1338*592ffb21SWarner Losh DRM_DEBUG_KMS("\n"); 1339*592ffb21SWarner Losh 1340*592ffb21SWarner Losh width = dev->mode_config.max_width; 1341*592ffb21SWarner Losh height = dev->mode_config.max_height; 1342*592ffb21SWarner Losh 1343*592ffb21SWarner Losh crtcs = malloc(dev->mode_config.num_connector * 1344*592ffb21SWarner Losh sizeof(struct drm_fb_helper_crtc *), DRM_MEM_KMS, M_NOWAIT | M_ZERO); 1345*592ffb21SWarner Losh modes = malloc(dev->mode_config.num_connector * 1346*592ffb21SWarner Losh sizeof(struct drm_display_mode *), DRM_MEM_KMS, M_NOWAIT | M_ZERO); 1347*592ffb21SWarner Losh enabled = malloc(dev->mode_config.num_connector * 1348*592ffb21SWarner Losh sizeof(bool), DRM_MEM_KMS, M_NOWAIT | M_ZERO); 1349*592ffb21SWarner Losh if (!crtcs || !modes || !enabled) { 1350*592ffb21SWarner Losh DRM_ERROR("Memory allocation failed\n"); 1351*592ffb21SWarner Losh goto out; 1352*592ffb21SWarner Losh } 1353*592ffb21SWarner Losh 1354*592ffb21SWarner Losh 1355*592ffb21SWarner Losh drm_enable_connectors(fb_helper, enabled); 1356*592ffb21SWarner Losh 1357*592ffb21SWarner Losh ret = drm_target_cloned(fb_helper, modes, enabled, width, height); 1358*592ffb21SWarner Losh if (!ret) { 1359*592ffb21SWarner Losh ret = drm_target_preferred(fb_helper, modes, enabled, width, height); 1360*592ffb21SWarner Losh if (!ret) 1361*592ffb21SWarner Losh DRM_ERROR("Unable to find initial modes\n"); 1362*592ffb21SWarner Losh } 1363*592ffb21SWarner Losh 1364*592ffb21SWarner Losh DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height); 1365*592ffb21SWarner Losh 1366*592ffb21SWarner Losh drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height); 1367*592ffb21SWarner Losh 1368*592ffb21SWarner Losh /* need to set the modesets up here for use later */ 1369*592ffb21SWarner Losh /* fill out the connector<->crtc mappings into the modesets */ 1370*592ffb21SWarner Losh for (i = 0; i < fb_helper->crtc_count; i++) { 1371*592ffb21SWarner Losh modeset = &fb_helper->crtc_info[i].mode_set; 1372*592ffb21SWarner Losh modeset->num_connectors = 0; 1373*592ffb21SWarner Losh } 1374*592ffb21SWarner Losh 1375*592ffb21SWarner Losh for (i = 0; i < fb_helper->connector_count; i++) { 1376*592ffb21SWarner Losh struct drm_display_mode *mode = modes[i]; 1377*592ffb21SWarner Losh struct drm_fb_helper_crtc *fb_crtc = crtcs[i]; 1378*592ffb21SWarner Losh modeset = &fb_crtc->mode_set; 1379*592ffb21SWarner Losh 1380*592ffb21SWarner Losh if (mode && fb_crtc) { 1381*592ffb21SWarner Losh DRM_DEBUG_KMS("desired mode %s set on crtc %d\n", 1382*592ffb21SWarner Losh mode->name, fb_crtc->mode_set.crtc->base.id); 1383*592ffb21SWarner Losh fb_crtc->desired_mode = mode; 1384*592ffb21SWarner Losh if (modeset->mode) 1385*592ffb21SWarner Losh drm_mode_destroy(dev, modeset->mode); 1386*592ffb21SWarner Losh modeset->mode = drm_mode_duplicate(dev, 1387*592ffb21SWarner Losh fb_crtc->desired_mode); 1388*592ffb21SWarner Losh modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector; 1389*592ffb21SWarner Losh } 1390*592ffb21SWarner Losh } 1391*592ffb21SWarner Losh 1392*592ffb21SWarner Losh out: 1393*592ffb21SWarner Losh free(crtcs, DRM_MEM_KMS); 1394*592ffb21SWarner Losh free(modes, DRM_MEM_KMS); 1395*592ffb21SWarner Losh free(enabled, DRM_MEM_KMS); 1396*592ffb21SWarner Losh } 1397*592ffb21SWarner Losh 1398*592ffb21SWarner Losh /** 1399*592ffb21SWarner Losh * drm_helper_initial_config - setup a sane initial connector configuration 1400*592ffb21SWarner Losh * @fb_helper: fb_helper device struct 1401*592ffb21SWarner Losh * @bpp_sel: bpp value to use for the framebuffer configuration 1402*592ffb21SWarner Losh * 1403*592ffb21SWarner Losh * LOCKING: 1404*592ffb21SWarner Losh * Called at init time by the driver to set up the @fb_helper initial 1405*592ffb21SWarner Losh * configuration, must take the mode config lock. 1406*592ffb21SWarner Losh * 1407*592ffb21SWarner Losh * Scans the CRTCs and connectors and tries to put together an initial setup. 1408*592ffb21SWarner Losh * At the moment, this is a cloned configuration across all heads with 1409*592ffb21SWarner Losh * a new framebuffer object as the backing store. 1410*592ffb21SWarner Losh * 1411*592ffb21SWarner Losh * RETURNS: 1412*592ffb21SWarner Losh * Zero if everything went ok, nonzero otherwise. 1413*592ffb21SWarner Losh */ 1414*592ffb21SWarner Losh bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) 1415*592ffb21SWarner Losh { 1416*592ffb21SWarner Losh struct drm_device *dev = fb_helper->dev; 1417*592ffb21SWarner Losh int count = 0; 1418*592ffb21SWarner Losh 1419*592ffb21SWarner Losh /* disable all the possible outputs/crtcs before entering KMS mode */ 1420*592ffb21SWarner Losh drm_helper_disable_unused_functions(fb_helper->dev); 1421*592ffb21SWarner Losh 1422*592ffb21SWarner Losh drm_fb_helper_parse_command_line(fb_helper); 1423*592ffb21SWarner Losh 1424*592ffb21SWarner Losh count = drm_fb_helper_probe_connector_modes(fb_helper, 1425*592ffb21SWarner Losh dev->mode_config.max_width, 1426*592ffb21SWarner Losh dev->mode_config.max_height); 1427*592ffb21SWarner Losh /* 1428*592ffb21SWarner Losh * we shouldn't end up with no modes here. 1429*592ffb21SWarner Losh */ 1430*592ffb21SWarner Losh if (count == 0) 1431*592ffb21SWarner Losh dev_info(fb_helper->dev->dev, "No connectors reported connected with modes\n"); 1432*592ffb21SWarner Losh 1433*592ffb21SWarner Losh drm_setup_crtcs(fb_helper); 1434*592ffb21SWarner Losh 1435*592ffb21SWarner Losh return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); 1436*592ffb21SWarner Losh } 1437*592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_initial_config); 1438*592ffb21SWarner Losh 1439*592ffb21SWarner Losh /** 1440*592ffb21SWarner Losh * drm_fb_helper_hotplug_event - respond to a hotplug notification by 1441*592ffb21SWarner Losh * probing all the outputs attached to the fb 1442*592ffb21SWarner Losh * @fb_helper: the drm_fb_helper 1443*592ffb21SWarner Losh * 1444*592ffb21SWarner Losh * LOCKING: 1445*592ffb21SWarner Losh * Called at runtime, must take mode config lock. 1446*592ffb21SWarner Losh * 1447*592ffb21SWarner Losh * Scan the connectors attached to the fb_helper and try to put together a 1448*592ffb21SWarner Losh * setup after *notification of a change in output configuration. 1449*592ffb21SWarner Losh * 1450*592ffb21SWarner Losh * RETURNS: 1451*592ffb21SWarner Losh * 0 on success and a non-zero error code otherwise. 1452*592ffb21SWarner Losh */ 1453*592ffb21SWarner Losh int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) 1454*592ffb21SWarner Losh { 1455*592ffb21SWarner Losh struct drm_device *dev = fb_helper->dev; 1456*592ffb21SWarner Losh int count = 0; 1457*592ffb21SWarner Losh u32 max_width, max_height, bpp_sel; 1458*592ffb21SWarner Losh int bound = 0, crtcs_bound = 0; 1459*592ffb21SWarner Losh struct drm_crtc *crtc; 1460*592ffb21SWarner Losh 1461*592ffb21SWarner Losh if (!fb_helper->fb) 1462*592ffb21SWarner Losh return 0; 1463*592ffb21SWarner Losh 1464*592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 1465*592ffb21SWarner Losh list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 1466*592ffb21SWarner Losh if (crtc->fb) 1467*592ffb21SWarner Losh crtcs_bound++; 1468*592ffb21SWarner Losh if (crtc->fb == fb_helper->fb) 1469*592ffb21SWarner Losh bound++; 1470*592ffb21SWarner Losh } 1471*592ffb21SWarner Losh 1472*592ffb21SWarner Losh if (bound < crtcs_bound) { 1473*592ffb21SWarner Losh fb_helper->delayed_hotplug = true; 1474*592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 1475*592ffb21SWarner Losh return 0; 1476*592ffb21SWarner Losh } 1477*592ffb21SWarner Losh DRM_DEBUG_KMS("\n"); 1478*592ffb21SWarner Losh 1479*592ffb21SWarner Losh max_width = fb_helper->fb->width; 1480*592ffb21SWarner Losh max_height = fb_helper->fb->height; 1481*592ffb21SWarner Losh bpp_sel = fb_helper->fb->bits_per_pixel; 1482*592ffb21SWarner Losh 1483*592ffb21SWarner Losh count = drm_fb_helper_probe_connector_modes(fb_helper, max_width, 1484*592ffb21SWarner Losh max_height); 1485*592ffb21SWarner Losh drm_setup_crtcs(fb_helper); 1486*592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 1487*592ffb21SWarner Losh 1488*592ffb21SWarner Losh return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); 1489*592ffb21SWarner Losh } 1490*592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_helper_hotplug_event); 1491