The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/drm2/drm_fb_helper.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 923c38e849a1bc23a74fa2abfcb818a7


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.