Subversion Repositories Kolibri OS

Rev

Rev 6296 | Rev 6937 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  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. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  31.  
  32. #include <linux/kernel.h>
  33. #include <linux/sysrq.h>
  34. #include <linux/slab.h>
  35. #include <linux/fb.h>
  36. #include <linux/module.h>
  37. #include <drm/drmP.h>
  38. #include <drm/drm_crtc.h>
  39. #include <drm/drm_fb_helper.h>
  40. #include <drm/drm_crtc_helper.h>
  41. #include <drm/drm_atomic.h>
  42. #include <drm/drm_atomic_helper.h>
  43.  
  44. static bool drm_fbdev_emulation = true;
  45. module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600);
  46. MODULE_PARM_DESC(fbdev_emulation,
  47.                  "Enable legacy fbdev emulation [default=true]");
  48.  
  49. static LIST_HEAD(kernel_fb_helper_list);
  50.  
  51. /**
  52.  * DOC: fbdev helpers
  53.  *
  54.  * The fb helper functions are useful to provide an fbdev on top of a drm kernel
  55.  * mode setting driver. They can be used mostly independently from the crtc
  56.  * helper functions used by many drivers to implement the kernel mode setting
  57.  * interfaces.
  58.  *
  59.  * Initialization is done as a four-step process with drm_fb_helper_prepare(),
  60.  * drm_fb_helper_init(), drm_fb_helper_single_add_all_connectors() and
  61.  * drm_fb_helper_initial_config(). Drivers with fancier requirements than the
  62.  * default behaviour can override the third step with their own code.
  63.  * Teardown is done with drm_fb_helper_fini().
  64.  *
  65.  * At runtime drivers should restore the fbdev console by calling
  66.  * drm_fb_helper_restore_fbdev_mode_unlocked() from their ->lastclose callback.
  67.  * They should also notify the fb helper code from updates to the output
  68.  * configuration by calling drm_fb_helper_hotplug_event(). For easier
  69.  * integration with the output polling code in drm_crtc_helper.c the modeset
  70.  * code provides a ->output_poll_changed callback.
  71.  *
  72.  * All other functions exported by the fb helper library can be used to
  73.  * implement the fbdev driver interface by the driver.
  74.  *
  75.  * It is possible, though perhaps somewhat tricky, to implement race-free
  76.  * hotplug detection using the fbdev helpers. The drm_fb_helper_prepare()
  77.  * helper must be called first to initialize the minimum required to make
  78.  * hotplug detection work. Drivers also need to make sure to properly set up
  79.  * the dev->mode_config.funcs member. After calling drm_kms_helper_poll_init()
  80.  * it is safe to enable interrupts and start processing hotplug events. At the
  81.  * same time, drivers should initialize all modeset objects such as CRTCs,
  82.  * encoders and connectors. To finish up the fbdev helper initialization, the
  83.  * drm_fb_helper_init() function is called. To probe for all attached displays
  84.  * and set up an initial configuration using the detected hardware, drivers
  85.  * should call drm_fb_helper_single_add_all_connectors() followed by
  86.  * drm_fb_helper_initial_config().
  87.  */
  88.  
  89. /**
  90.  * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev
  91.  *                                             emulation helper
  92.  * @fb_helper: fbdev initialized with drm_fb_helper_init
  93.  *
  94.  * This functions adds all the available connectors for use with the given
  95.  * fb_helper. This is a separate step to allow drivers to freely assign
  96.  * connectors to the fbdev, e.g. if some are reserved for special purposes or
  97.  * not adequate to be used for the fbcon.
  98.  *
  99.  * This function is protected against concurrent connector hotadds/removals
  100.  * using drm_fb_helper_add_one_connector() and
  101.  * drm_fb_helper_remove_one_connector().
  102.  */
  103. int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
  104. {
  105.         struct drm_device *dev = fb_helper->dev;
  106.         struct drm_connector *connector;
  107.         int i;
  108.  
  109.         if (!drm_fbdev_emulation)
  110.                 return 0;
  111.  
  112.         mutex_lock(&dev->mode_config.mutex);
  113.         drm_for_each_connector(connector, dev) {
  114.                 struct drm_fb_helper_connector *fb_helper_connector;
  115.  
  116.                 fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
  117.                 if (!fb_helper_connector)
  118.                         goto fail;
  119.  
  120.                 fb_helper_connector->connector = connector;
  121.                 fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector;
  122.         }
  123.         mutex_unlock(&dev->mode_config.mutex);
  124.         return 0;
  125. fail:
  126.         for (i = 0; i < fb_helper->connector_count; i++) {
  127.                 kfree(fb_helper->connector_info[i]);
  128.                 fb_helper->connector_info[i] = NULL;
  129.         }
  130.         fb_helper->connector_count = 0;
  131.         mutex_unlock(&dev->mode_config.mutex);
  132.  
  133.         return -ENOMEM;
  134. }
  135. EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
  136.  
  137. int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector)
  138. {
  139.         struct drm_fb_helper_connector **temp;
  140.         struct drm_fb_helper_connector *fb_helper_connector;
  141.  
  142.         if (!drm_fbdev_emulation)
  143.                 return 0;
  144.  
  145.         WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
  146.         if (fb_helper->connector_count + 1 > fb_helper->connector_info_alloc_count) {
  147.                 temp = krealloc(fb_helper->connector_info, sizeof(struct drm_fb_helper_connector *) * (fb_helper->connector_count + 1), GFP_KERNEL);
  148.                 if (!temp)
  149.                         return -ENOMEM;
  150.  
  151.                 fb_helper->connector_info_alloc_count = fb_helper->connector_count + 1;
  152.                 fb_helper->connector_info = temp;
  153.         }
  154.  
  155.  
  156.         fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
  157.         if (!fb_helper_connector)
  158.                 return -ENOMEM;
  159.  
  160.         fb_helper_connector->connector = connector;
  161.         fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector;
  162.         return 0;
  163. }
  164. EXPORT_SYMBOL(drm_fb_helper_add_one_connector);
  165.  
  166. static void remove_from_modeset(struct drm_mode_set *set,
  167.                 struct drm_connector *connector)
  168. {
  169.         int i, j;
  170.  
  171.         for (i = 0; i < set->num_connectors; i++) {
  172.                 if (set->connectors[i] == connector)
  173.                         break;
  174.         }
  175.  
  176.         if (i == set->num_connectors)
  177.                 return;
  178.  
  179.         for (j = i + 1; j < set->num_connectors; j++) {
  180.                 set->connectors[j - 1] = set->connectors[j];
  181.         }
  182.         set->num_connectors--;
  183.  
  184.         /*
  185.          * TODO maybe need to makes sure we set it back to !=NULL somewhere?
  186.          */
  187.         if (set->num_connectors == 0) {
  188.                 set->fb = NULL;
  189.                 drm_mode_destroy(connector->dev, set->mode);
  190.                 set->mode = NULL;
  191.         }
  192. }
  193.  
  194. int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
  195.                                        struct drm_connector *connector)
  196. {
  197.         struct drm_fb_helper_connector *fb_helper_connector;
  198.         int i, j;
  199.  
  200.         if (!drm_fbdev_emulation)
  201.                 return 0;
  202.  
  203.         WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
  204.  
  205.         for (i = 0; i < fb_helper->connector_count; i++) {
  206.                 if (fb_helper->connector_info[i]->connector == connector)
  207.                         break;
  208.         }
  209.  
  210.         if (i == fb_helper->connector_count)
  211.                 return -EINVAL;
  212.         fb_helper_connector = fb_helper->connector_info[i];
  213.  
  214.         for (j = i + 1; j < fb_helper->connector_count; j++) {
  215.                 fb_helper->connector_info[j - 1] = fb_helper->connector_info[j];
  216.         }
  217.         fb_helper->connector_count--;
  218.         kfree(fb_helper_connector);
  219.  
  220.         /* also cleanup dangling references to the connector: */
  221.         for (i = 0; i < fb_helper->crtc_count; i++)
  222.                 remove_from_modeset(&fb_helper->crtc_info[i].mode_set, connector);
  223.  
  224.         return 0;
  225. }
  226. EXPORT_SYMBOL(drm_fb_helper_remove_one_connector);
  227.  
  228. static void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_helper *helper)
  229. {
  230.         uint16_t *r_base, *g_base, *b_base;
  231.         int i;
  232.  
  233.         if (helper->funcs->gamma_get == NULL)
  234.                 return;
  235.  
  236.         r_base = crtc->gamma_store;
  237.         g_base = r_base + crtc->gamma_size;
  238.         b_base = g_base + crtc->gamma_size;
  239.  
  240.         for (i = 0; i < crtc->gamma_size; i++)
  241.                 helper->funcs->gamma_get(crtc, &r_base[i], &g_base[i], &b_base[i], i);
  242. }
  243.  
  244. static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc)
  245. {
  246.         uint16_t *r_base, *g_base, *b_base;
  247.  
  248.         if (crtc->funcs->gamma_set == NULL)
  249.                 return;
  250.  
  251.         r_base = crtc->gamma_store;
  252.         g_base = r_base + crtc->gamma_size;
  253.         b_base = g_base + crtc->gamma_size;
  254.  
  255.         crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);
  256. }
  257.  
  258. /* Find the real fb for a given fb helper CRTC */
  259. static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc)
  260. {
  261.         struct drm_device *dev = crtc->dev;
  262.         struct drm_crtc *c;
  263.  
  264.         drm_for_each_crtc(c, dev) {
  265.                 if (crtc->base.id == c->base.id)
  266.                         return c->primary->fb;
  267.         }
  268.  
  269.         return NULL;
  270. }
  271. static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper)
  272. {
  273.         struct drm_device *dev = fb_helper->dev;
  274.         struct drm_plane *plane;
  275.         struct drm_atomic_state *state;
  276.         int i, ret;
  277.         unsigned plane_mask;
  278.  
  279.         state = drm_atomic_state_alloc(dev);
  280.         if (!state)
  281.                 return -ENOMEM;
  282.  
  283.         state->acquire_ctx = dev->mode_config.acquire_ctx;
  284. retry:
  285.         plane_mask = 0;
  286.         drm_for_each_plane(plane, dev) {
  287.                 struct drm_plane_state *plane_state;
  288.  
  289.                 plane_state = drm_atomic_get_plane_state(state, plane);
  290.                 if (IS_ERR(plane_state)) {
  291.                         ret = PTR_ERR(plane_state);
  292.                         goto fail;
  293.                 }
  294.  
  295.                 plane_state->rotation = BIT(DRM_ROTATE_0);
  296.  
  297.                 plane->old_fb = plane->fb;
  298.                 plane_mask |= 1 << drm_plane_index(plane);
  299.  
  300.                 /* disable non-primary: */
  301.                 if (plane->type == DRM_PLANE_TYPE_PRIMARY)
  302.                         continue;
  303.  
  304.                 ret = __drm_atomic_helper_disable_plane(plane, plane_state);
  305.                 if (ret != 0)
  306.                         goto fail;
  307.         }
  308.  
  309.         for(i = 0; i < fb_helper->crtc_count; i++) {
  310.                 struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
  311.  
  312.                 ret = __drm_atomic_helper_set_config(mode_set, state);
  313.                 if (ret != 0)
  314.                         goto fail;
  315.         }
  316.  
  317.         ret = drm_atomic_commit(state);
  318.  
  319. fail:
  320.         drm_atomic_clean_old_fb(dev, plane_mask, ret);
  321.  
  322.         if (ret == -EDEADLK)
  323.                 goto backoff;
  324.  
  325.         if (ret != 0)
  326.                 drm_atomic_state_free(state);
  327.  
  328.         return ret;
  329.  
  330. backoff:
  331.         drm_atomic_state_clear(state);
  332.         drm_atomic_legacy_backoff(state);
  333.  
  334.         goto retry;
  335. }
  336.  
  337. static int restore_fbdev_mode(struct drm_fb_helper *fb_helper)
  338. {
  339.         struct drm_device *dev = fb_helper->dev;
  340.         struct drm_plane *plane;
  341.         int i;
  342.  
  343.         drm_warn_on_modeset_not_all_locked(dev);
  344.  
  345.         if (fb_helper->atomic)
  346.                 return restore_fbdev_mode_atomic(fb_helper);
  347.  
  348.         drm_for_each_plane(plane, dev) {
  349.                 if (plane->type != DRM_PLANE_TYPE_PRIMARY)
  350.                         drm_plane_force_disable(plane);
  351.  
  352.                 if (dev->mode_config.rotation_property) {
  353.                         drm_mode_plane_set_obj_prop(plane,
  354.                                                     dev->mode_config.rotation_property,
  355.                                                     BIT(DRM_ROTATE_0));
  356.                 }
  357.         }
  358.  
  359.         for (i = 0; i < fb_helper->crtc_count; i++) {
  360.                 struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
  361.                 struct drm_crtc *crtc = mode_set->crtc;
  362.                 int ret;
  363.  
  364.                 if (crtc->funcs->cursor_set2) {
  365.                         ret = crtc->funcs->cursor_set2(crtc, NULL, 0, 0, 0, 0, 0);
  366.                         if (ret)
  367.                                 return ret;
  368.                 } else if (crtc->funcs->cursor_set) {
  369.                         ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0);
  370.                         if (ret)
  371.                                 return ret;
  372.                 }
  373.  
  374.                 ret = drm_mode_set_config_internal(mode_set);
  375.                 if (ret)
  376.                         return ret;
  377.         }
  378.  
  379.         return 0;
  380. }
  381.  
  382. /**
  383.  * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration
  384.  * @fb_helper: fbcon to restore
  385.  *
  386.  * This should be called from driver's drm ->lastclose callback
  387.  * when implementing an fbcon on top of kms using this helper. This ensures that
  388.  * the user isn't greeted with a black screen when e.g. X dies.
  389.  *
  390.  * RETURNS:
  391.  * Zero if everything went ok, negative error code otherwise.
  392.  */
  393. int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
  394. {
  395.         struct drm_device *dev = fb_helper->dev;
  396.         bool do_delayed;
  397.         int ret;
  398.  
  399.         if (!drm_fbdev_emulation)
  400.                 return -ENODEV;
  401.  
  402.         drm_modeset_lock_all(dev);
  403.         ret = restore_fbdev_mode(fb_helper);
  404.  
  405.         do_delayed = fb_helper->delayed_hotplug;
  406.         if (do_delayed)
  407.                 fb_helper->delayed_hotplug = false;
  408.         drm_modeset_unlock_all(dev);
  409.  
  410.         if (do_delayed)
  411.                 drm_fb_helper_hotplug_event(fb_helper);
  412.         return ret;
  413. }
  414. EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked);
  415.  
  416. static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper)
  417. {
  418.         struct drm_device *dev = fb_helper->dev;
  419.         struct drm_crtc *crtc;
  420.         int bound = 0, crtcs_bound = 0;
  421.  
  422.         /* Sometimes user space wants everything disabled, so don't steal the
  423.          * display if there's a master. */
  424. //   if (dev->primary->master)
  425. //       return false;
  426.  
  427.         drm_for_each_crtc(crtc, dev) {
  428.                 if (crtc->primary->fb)
  429.                         crtcs_bound++;
  430.                 if (crtc->primary->fb == fb_helper->fb)
  431.                         bound++;
  432.         }
  433.  
  434.         if (bound < crtcs_bound)
  435.                 return false;
  436.  
  437.         return true;
  438. }
  439.  
  440. #ifdef CONFIG_MAGIC_SYSRQ
  441. static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
  442. {
  443.         bool ret;
  444.         ret = drm_fb_helper_force_kernel_mode();
  445.         if (ret == true)
  446.                 DRM_ERROR("Failed to restore crtc configuration\n");
  447. }
  448. static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
  449.  
  450. static void drm_fb_helper_sysrq(int dummy1)
  451. {
  452.         schedule_work(&drm_fb_helper_restore_work);
  453. }
  454.  
  455. static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
  456.         .handler = drm_fb_helper_sysrq,
  457.         .help_msg = "force-fb(V)",
  458.         .action_msg = "Restore framebuffer console",
  459. };
  460. #else
  461.  
  462. #endif
  463.  
  464. static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
  465. {
  466.         struct drm_fb_helper *fb_helper = info->par;
  467.         struct drm_device *dev = fb_helper->dev;
  468.         struct drm_crtc *crtc;
  469.         struct drm_connector *connector;
  470.         int i, j;
  471.  
  472.         /*
  473.          * For each CRTC in this fb, turn the connectors on/off.
  474.          */
  475.         drm_modeset_lock_all(dev);
  476.         if (!drm_fb_helper_is_bound(fb_helper)) {
  477.                 drm_modeset_unlock_all(dev);
  478.                 return;
  479.         }
  480.  
  481.         for (i = 0; i < fb_helper->crtc_count; i++) {
  482.                 crtc = fb_helper->crtc_info[i].mode_set.crtc;
  483.  
  484.                 if (!crtc->enabled)
  485.                         continue;
  486.  
  487.                 /* Walk the connectors & encoders on this fb turning them on/off */
  488.                 for (j = 0; j < fb_helper->connector_count; j++) {
  489.                         connector = fb_helper->connector_info[j]->connector;
  490.                         connector->funcs->dpms(connector, dpms_mode);
  491.                         drm_object_property_set_value(&connector->base,
  492.                                 dev->mode_config.dpms_property, dpms_mode);
  493.                 }
  494.         }
  495.         drm_modeset_unlock_all(dev);
  496. }
  497.  
  498. /**
  499.  * drm_fb_helper_blank - implementation for ->fb_blank
  500.  * @blank: desired blanking state
  501.  * @info: fbdev registered by the helper
  502.  */
  503. int drm_fb_helper_blank(int blank, struct fb_info *info)
  504. {
  505.         if (oops_in_progress)
  506.                 return -EBUSY;
  507.  
  508.         switch (blank) {
  509.         /* Display: On; HSync: On, VSync: On */
  510.         case FB_BLANK_UNBLANK:
  511.                 drm_fb_helper_dpms(info, DRM_MODE_DPMS_ON);
  512.                 break;
  513.         /* Display: Off; HSync: On, VSync: On */
  514.         case FB_BLANK_NORMAL:
  515.                 drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
  516.                 break;
  517.         /* Display: Off; HSync: Off, VSync: On */
  518.         case FB_BLANK_HSYNC_SUSPEND:
  519.                 drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
  520.                 break;
  521.         /* Display: Off; HSync: On, VSync: Off */
  522.         case FB_BLANK_VSYNC_SUSPEND:
  523.                 drm_fb_helper_dpms(info, DRM_MODE_DPMS_SUSPEND);
  524.                 break;
  525.         /* Display: Off; HSync: Off, VSync: Off */
  526.         case FB_BLANK_POWERDOWN:
  527.                 drm_fb_helper_dpms(info, DRM_MODE_DPMS_OFF);
  528.                 break;
  529.         }
  530.         return 0;
  531. }
  532. EXPORT_SYMBOL(drm_fb_helper_blank);
  533.  
  534. static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
  535. {
  536.         int i;
  537.  
  538.         for (i = 0; i < helper->connector_count; i++)
  539.                 kfree(helper->connector_info[i]);
  540.         kfree(helper->connector_info);
  541.         for (i = 0; i < helper->crtc_count; i++) {
  542.                 kfree(helper->crtc_info[i].mode_set.connectors);
  543.                 if (helper->crtc_info[i].mode_set.mode)
  544.                         drm_mode_destroy(helper->dev, helper->crtc_info[i].mode_set.mode);
  545.         }
  546.         kfree(helper->crtc_info);
  547. }
  548.  
  549. /**
  550.  * drm_fb_helper_prepare - setup a drm_fb_helper structure
  551.  * @dev: DRM device
  552.  * @helper: driver-allocated fbdev helper structure to set up
  553.  * @funcs: pointer to structure of functions associate with this helper
  554.  *
  555.  * Sets up the bare minimum to make the framebuffer helper usable. This is
  556.  * useful to implement race-free initialization of the polling helpers.
  557.  */
  558. void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
  559.                            const struct drm_fb_helper_funcs *funcs)
  560. {
  561.         INIT_LIST_HEAD(&helper->kernel_fb_list);
  562.         helper->funcs = funcs;
  563.         helper->dev = dev;
  564. }
  565. EXPORT_SYMBOL(drm_fb_helper_prepare);
  566.  
  567. /**
  568.  * drm_fb_helper_init - initialize a drm_fb_helper structure
  569.  * @dev: drm device
  570.  * @fb_helper: driver-allocated fbdev helper structure to initialize
  571.  * @crtc_count: maximum number of crtcs to support in this fbdev emulation
  572.  * @max_conn_count: max connector count
  573.  *
  574.  * This allocates the structures for the fbdev helper with the given limits.
  575.  * Note that this won't yet touch the hardware (through the driver interfaces)
  576.  * nor register the fbdev. This is only done in drm_fb_helper_initial_config()
  577.  * to allow driver writes more control over the exact init sequence.
  578.  *
  579.  * Drivers must call drm_fb_helper_prepare() before calling this function.
  580.  *
  581.  * RETURNS:
  582.  * Zero if everything went ok, nonzero otherwise.
  583.  */
  584. int drm_fb_helper_init(struct drm_device *dev,
  585.                        struct drm_fb_helper *fb_helper,
  586.                        int crtc_count, int max_conn_count)
  587. {
  588.         struct drm_crtc *crtc;
  589.         int i;
  590.  
  591.         if (!drm_fbdev_emulation)
  592.                 return 0;
  593.  
  594.         if (!max_conn_count)
  595.                 return -EINVAL;
  596.  
  597.         fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
  598.         if (!fb_helper->crtc_info)
  599.                 return -ENOMEM;
  600.  
  601.         fb_helper->crtc_count = crtc_count;
  602.         fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL);
  603.         if (!fb_helper->connector_info) {
  604.                 kfree(fb_helper->crtc_info);
  605.                 return -ENOMEM;
  606.         }
  607.         fb_helper->connector_info_alloc_count = dev->mode_config.num_connector;
  608.         fb_helper->connector_count = 0;
  609.  
  610.         for (i = 0; i < crtc_count; i++) {
  611.                 fb_helper->crtc_info[i].mode_set.connectors =
  612.                         kcalloc(max_conn_count,
  613.                                 sizeof(struct drm_connector *),
  614.                                 GFP_KERNEL);
  615.  
  616.                 if (!fb_helper->crtc_info[i].mode_set.connectors)
  617.                         goto out_free;
  618.                 fb_helper->crtc_info[i].mode_set.num_connectors = 0;
  619.         }
  620.  
  621.         i = 0;
  622.         drm_for_each_crtc(crtc, dev) {
  623.                 fb_helper->crtc_info[i].mode_set.crtc = crtc;
  624.                 i++;
  625.         }
  626.  
  627.         fb_helper->atomic = !!drm_core_check_feature(dev, DRIVER_ATOMIC);
  628.  
  629.         return 0;
  630. out_free:
  631.         drm_fb_helper_crtc_free(fb_helper);
  632.         return -ENOMEM;
  633. }
  634. EXPORT_SYMBOL(drm_fb_helper_init);
  635.  
  636. /**
  637.  * drm_fb_helper_alloc_fbi - allocate fb_info and some of its members
  638.  * @fb_helper: driver-allocated fbdev helper
  639.  *
  640.  * A helper to alloc fb_info and the members cmap and apertures. Called
  641.  * by the driver within the fb_probe fb_helper callback function.
  642.  *
  643.  * RETURNS:
  644.  * fb_info pointer if things went okay, pointer containing error code
  645.  * otherwise
  646.  */
  647. struct fb_info *drm_fb_helper_alloc_fbi(struct drm_fb_helper *fb_helper)
  648. {
  649.         struct device *dev = fb_helper->dev->dev;
  650.         struct fb_info *info;
  651.         int ret;
  652.  
  653.         info = framebuffer_alloc(0, dev);
  654.         if (!info)
  655.                 return ERR_PTR(-ENOMEM);
  656.  
  657.  
  658.         fb_helper->fbdev = info;
  659.  
  660.         return info;
  661.  
  662. }
  663. EXPORT_SYMBOL(drm_fb_helper_alloc_fbi);
  664. static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
  665.                      u16 blue, u16 regno, struct fb_info *info)
  666. {
  667.         struct drm_fb_helper *fb_helper = info->par;
  668.         struct drm_framebuffer *fb = fb_helper->fb;
  669.         int pindex;
  670.  
  671.         if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
  672.                 u32 *palette;
  673.                 u32 value;
  674.                 /* place color in psuedopalette */
  675.                 if (regno > 16)
  676.                         return -EINVAL;
  677.                 palette = (u32 *)info->pseudo_palette;
  678.                 red >>= (16 - info->var.red.length);
  679.                 green >>= (16 - info->var.green.length);
  680.                 blue >>= (16 - info->var.blue.length);
  681.                 value = (red << info->var.red.offset) |
  682.                         (green << info->var.green.offset) |
  683.                         (blue << info->var.blue.offset);
  684.                 if (info->var.transp.length > 0) {
  685.                         u32 mask = (1 << info->var.transp.length) - 1;
  686.                         mask <<= info->var.transp.offset;
  687.                         value |= mask;
  688.                 }
  689.                 palette[regno] = value;
  690.                 return 0;
  691.         }
  692.  
  693.         /*
  694.          * The driver really shouldn't advertise pseudo/directcolor
  695.          * visuals if it can't deal with the palette.
  696.          */
  697.         if (WARN_ON(!fb_helper->funcs->gamma_set ||
  698.                     !fb_helper->funcs->gamma_get))
  699.                 return -EINVAL;
  700.  
  701.         pindex = regno;
  702.  
  703.         if (fb->bits_per_pixel == 16) {
  704.                 pindex = regno << 3;
  705.  
  706.                 if (fb->depth == 16 && regno > 63)
  707.                         return -EINVAL;
  708.                 if (fb->depth == 15 && regno > 31)
  709.                         return -EINVAL;
  710.  
  711.                 if (fb->depth == 16) {
  712.                         u16 r, g, b;
  713.                         int i;
  714.                         if (regno < 32) {
  715.                                 for (i = 0; i < 8; i++)
  716.                                         fb_helper->funcs->gamma_set(crtc, red,
  717.                                                 green, blue, pindex + i);
  718.                         }
  719.  
  720.                         fb_helper->funcs->gamma_get(crtc, &r,
  721.                                                     &g, &b,
  722.                                                     pindex >> 1);
  723.  
  724.                         for (i = 0; i < 4; i++)
  725.                                 fb_helper->funcs->gamma_set(crtc, r,
  726.                                                             green, b,
  727.                                                             (pindex >> 1) + i);
  728.                 }
  729.         }
  730.  
  731.         if (fb->depth != 16)
  732.                 fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
  733.         return 0;
  734. }
  735.  
  736. /**
  737.  * drm_fb_helper_setcmap - implementation for ->fb_setcmap
  738.  * @cmap: cmap to set
  739.  * @info: fbdev registered by the helper
  740.  */
  741. int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
  742. {
  743.         struct drm_fb_helper *fb_helper = info->par;
  744.         struct drm_device *dev = fb_helper->dev;
  745.         const struct drm_crtc_helper_funcs *crtc_funcs;
  746.         u16 *red, *green, *blue, *transp;
  747.         struct drm_crtc *crtc;
  748.         int i, j, rc = 0;
  749.         int start;
  750.  
  751.         if (oops_in_progress)
  752.                 return -EBUSY;
  753.  
  754.         drm_modeset_lock_all(dev);
  755.         if (!drm_fb_helper_is_bound(fb_helper)) {
  756.                 drm_modeset_unlock_all(dev);
  757.                 return -EBUSY;
  758.         }
  759.  
  760.         for (i = 0; i < fb_helper->crtc_count; i++) {
  761.                 crtc = fb_helper->crtc_info[i].mode_set.crtc;
  762.                 crtc_funcs = crtc->helper_private;
  763.  
  764.                 red = cmap->red;
  765.                 green = cmap->green;
  766.                 blue = cmap->blue;
  767.                 transp = cmap->transp;
  768.                 start = cmap->start;
  769.  
  770.                 for (j = 0; j < cmap->len; j++) {
  771.                         u16 hred, hgreen, hblue, htransp = 0xffff;
  772.  
  773.                         hred = *red++;
  774.                         hgreen = *green++;
  775.                         hblue = *blue++;
  776.  
  777.                         if (transp)
  778.                                 htransp = *transp++;
  779.  
  780.                         rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
  781.                         if (rc)
  782.                                 goto out;
  783.                 }
  784.                 if (crtc_funcs->load_lut)
  785.                         crtc_funcs->load_lut(crtc);
  786.         }
  787.  out:
  788.         drm_modeset_unlock_all(dev);
  789.         return rc;
  790. }
  791. EXPORT_SYMBOL(drm_fb_helper_setcmap);
  792.  
  793. /**
  794.  * drm_fb_helper_check_var - implementation for ->fb_check_var
  795.  * @var: screeninfo to check
  796.  * @info: fbdev registered by the helper
  797.  */
  798. int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
  799.                             struct fb_info *info)
  800. {
  801.         struct drm_fb_helper *fb_helper = info->par;
  802.         struct drm_framebuffer *fb = fb_helper->fb;
  803.         int depth;
  804.  
  805.         if (var->pixclock != 0 || in_dbg_master())
  806.                 return -EINVAL;
  807.  
  808.         /* Need to resize the fb object !!! */
  809.         if (var->bits_per_pixel > fb->bits_per_pixel ||
  810.             var->xres > fb->width || var->yres > fb->height ||
  811.             var->xres_virtual > fb->width || var->yres_virtual > fb->height) {
  812.                 DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb "
  813.                           "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n",
  814.                           var->xres, var->yres, var->bits_per_pixel,
  815.                           var->xres_virtual, var->yres_virtual,
  816.                           fb->width, fb->height, fb->bits_per_pixel);
  817.                 return -EINVAL;
  818.         }
  819.  
  820.         switch (var->bits_per_pixel) {
  821.         case 16:
  822.                 depth = (var->green.length == 6) ? 16 : 15;
  823.                 break;
  824.         case 32:
  825.                 depth = (var->transp.length > 0) ? 32 : 24;
  826.                 break;
  827.         default:
  828.                 depth = var->bits_per_pixel;
  829.                 break;
  830.         }
  831.  
  832.         switch (depth) {
  833.         case 8:
  834.                 var->red.offset = 0;
  835.                 var->green.offset = 0;
  836.                 var->blue.offset = 0;
  837.                 var->red.length = 8;
  838.                 var->green.length = 8;
  839.                 var->blue.length = 8;
  840.                 var->transp.length = 0;
  841.                 var->transp.offset = 0;
  842.                 break;
  843.         case 15:
  844.                 var->red.offset = 10;
  845.                 var->green.offset = 5;
  846.                 var->blue.offset = 0;
  847.                 var->red.length = 5;
  848.                 var->green.length = 5;
  849.                 var->blue.length = 5;
  850.                 var->transp.length = 1;
  851.                 var->transp.offset = 15;
  852.                 break;
  853.         case 16:
  854.                 var->red.offset = 11;
  855.                 var->green.offset = 5;
  856.                 var->blue.offset = 0;
  857.                 var->red.length = 5;
  858.                 var->green.length = 6;
  859.                 var->blue.length = 5;
  860.                 var->transp.length = 0;
  861.                 var->transp.offset = 0;
  862.                 break;
  863.         case 24:
  864.                 var->red.offset = 16;
  865.                 var->green.offset = 8;
  866.                 var->blue.offset = 0;
  867.                 var->red.length = 8;
  868.                 var->green.length = 8;
  869.                 var->blue.length = 8;
  870.                 var->transp.length = 0;
  871.                 var->transp.offset = 0;
  872.                 break;
  873.         case 32:
  874.                 var->red.offset = 16;
  875.                 var->green.offset = 8;
  876.                 var->blue.offset = 0;
  877.                 var->red.length = 8;
  878.                 var->green.length = 8;
  879.                 var->blue.length = 8;
  880.                 var->transp.length = 8;
  881.                 var->transp.offset = 24;
  882.                 break;
  883.         default:
  884.                 return -EINVAL;
  885.         }
  886.         return 0;
  887. }
  888. EXPORT_SYMBOL(drm_fb_helper_check_var);
  889.  
  890. /**
  891.  * drm_fb_helper_set_par - implementation for ->fb_set_par
  892.  * @info: fbdev registered by the helper
  893.  *
  894.  * This will let fbcon do the mode init and is called at initialization time by
  895.  * the fbdev core when registering the driver, and later on through the hotplug
  896.  * callback.
  897.  */
  898. int drm_fb_helper_set_par(struct fb_info *info)
  899. {
  900.         struct drm_fb_helper *fb_helper = info->par;
  901.         struct fb_var_screeninfo *var = &info->var;
  902.  
  903.         if (oops_in_progress)
  904.                 return -EBUSY;
  905.  
  906.         if (var->pixclock != 0) {
  907.                 DRM_ERROR("PIXEL CLOCK SET\n");
  908.                 return -EINVAL;
  909.         }
  910.  
  911.         drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
  912.  
  913.         return 0;
  914. }
  915. EXPORT_SYMBOL(drm_fb_helper_set_par);
  916.  
  917. static int pan_display_atomic(struct fb_var_screeninfo *var,
  918.                               struct fb_info *info)
  919. {
  920.         struct drm_fb_helper *fb_helper = info->par;
  921.         struct drm_device *dev = fb_helper->dev;
  922.         struct drm_atomic_state *state;
  923.         struct drm_plane *plane;
  924.         int i, ret;
  925.         unsigned plane_mask;
  926.  
  927.         state = drm_atomic_state_alloc(dev);
  928.         if (!state)
  929.                 return -ENOMEM;
  930.  
  931.         state->acquire_ctx = dev->mode_config.acquire_ctx;
  932. retry:
  933.         plane_mask = 0;
  934.         for(i = 0; i < fb_helper->crtc_count; i++) {
  935.                 struct drm_mode_set *mode_set;
  936.  
  937.                 mode_set = &fb_helper->crtc_info[i].mode_set;
  938.  
  939.                 mode_set->x = var->xoffset;
  940.                 mode_set->y = var->yoffset;
  941.  
  942.                 ret = __drm_atomic_helper_set_config(mode_set, state);
  943.                 if (ret != 0)
  944.                         goto fail;
  945.  
  946.                 plane = mode_set->crtc->primary;
  947.                 plane_mask |= drm_plane_index(plane);
  948.                 plane->old_fb = plane->fb;
  949.         }
  950.  
  951.         ret = drm_atomic_commit(state);
  952.         if (ret != 0)
  953.                 goto fail;
  954.  
  955.         info->var.xoffset = var->xoffset;
  956.         info->var.yoffset = var->yoffset;
  957.  
  958.  
  959. fail:
  960.         drm_atomic_clean_old_fb(dev, plane_mask, ret);
  961.  
  962.         if (ret == -EDEADLK)
  963.                 goto backoff;
  964.  
  965.         if (ret != 0)
  966.                 drm_atomic_state_free(state);
  967.  
  968.         return ret;
  969.  
  970. backoff:
  971.         drm_atomic_state_clear(state);
  972.         drm_atomic_legacy_backoff(state);
  973.  
  974.         goto retry;
  975. }
  976.  
  977. /**
  978.  * drm_fb_helper_pan_display - implementation for ->fb_pan_display
  979.  * @var: updated screen information
  980.  * @info: fbdev registered by the helper
  981.  */
  982. int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
  983.                               struct fb_info *info)
  984. {
  985.         struct drm_fb_helper *fb_helper = info->par;
  986.         struct drm_device *dev = fb_helper->dev;
  987.         struct drm_mode_set *modeset;
  988.         int ret = 0;
  989.         int i;
  990.  
  991.         if (oops_in_progress)
  992.                 return -EBUSY;
  993.  
  994.         drm_modeset_lock_all(dev);
  995.         if (!drm_fb_helper_is_bound(fb_helper)) {
  996.                 drm_modeset_unlock_all(dev);
  997.                 return -EBUSY;
  998.         }
  999.  
  1000.         if (fb_helper->atomic) {
  1001.                 ret = pan_display_atomic(var, info);
  1002.                 goto unlock;
  1003.         }
  1004.  
  1005.         for (i = 0; i < fb_helper->crtc_count; i++) {
  1006.                 modeset = &fb_helper->crtc_info[i].mode_set;
  1007.  
  1008.                 modeset->x = var->xoffset;
  1009.                 modeset->y = var->yoffset;
  1010.  
  1011.                 if (modeset->num_connectors) {
  1012.                         ret = drm_mode_set_config_internal(modeset);
  1013.                         if (!ret) {
  1014.                                 info->var.xoffset = var->xoffset;
  1015.                                 info->var.yoffset = var->yoffset;
  1016.                         }
  1017.                 }
  1018.         }
  1019. unlock:
  1020.         drm_modeset_unlock_all(dev);
  1021.         return ret;
  1022. }
  1023. EXPORT_SYMBOL(drm_fb_helper_pan_display);
  1024.  
  1025. /*
  1026.  * Allocates the backing storage and sets up the fbdev info structure through
  1027.  * the ->fb_probe callback and then registers the fbdev and sets up the panic
  1028.  * notifier.
  1029.  */
  1030. static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
  1031.                                          int preferred_bpp)
  1032. {
  1033.         int ret = 0;
  1034.         int crtc_count = 0;
  1035.         int i;
  1036.         struct fb_info *info;
  1037.         struct drm_fb_helper_surface_size sizes;
  1038.         int gamma_size = 0;
  1039.  
  1040.         memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size));
  1041.         sizes.surface_depth = 24;
  1042.         sizes.surface_bpp = 32;
  1043.         sizes.fb_width = (unsigned)-1;
  1044.         sizes.fb_height = (unsigned)-1;
  1045.  
  1046.         /* if driver picks 8 or 16 by default use that
  1047.            for both depth/bpp */
  1048.         if (preferred_bpp != sizes.surface_bpp)
  1049.                 sizes.surface_depth = sizes.surface_bpp = preferred_bpp;
  1050.  
  1051.         /* first up get a count of crtcs now in use and new min/maxes width/heights */
  1052.         for (i = 0; i < fb_helper->connector_count; i++) {
  1053.                 struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
  1054.                 struct drm_cmdline_mode *cmdline_mode;
  1055.  
  1056.                 cmdline_mode = &fb_helper_conn->connector->cmdline_mode;
  1057.  
  1058.                 if (cmdline_mode->bpp_specified) {
  1059.                         switch (cmdline_mode->bpp) {
  1060.                         case 8:
  1061.                                 sizes.surface_depth = sizes.surface_bpp = 8;
  1062.                                 break;
  1063.                         case 15:
  1064.                                 sizes.surface_depth = 15;
  1065.                                 sizes.surface_bpp = 16;
  1066.                                 break;
  1067.                         case 16:
  1068.                                 sizes.surface_depth = sizes.surface_bpp = 16;
  1069.                                 break;
  1070.                         case 24:
  1071.                                 sizes.surface_depth = sizes.surface_bpp = 24;
  1072.                                 break;
  1073.                         case 32:
  1074.                                 sizes.surface_depth = 24;
  1075.                                 sizes.surface_bpp = 32;
  1076.                                 break;
  1077.                         }
  1078.                         break;
  1079.                 }
  1080.         }
  1081.  
  1082.         crtc_count = 0;
  1083.         for (i = 0; i < fb_helper->crtc_count; i++) {
  1084.                 struct drm_display_mode *desired_mode;
  1085.                 struct drm_mode_set *mode_set;
  1086.                 int x, y, j;
  1087.                 /* in case of tile group, are we the last tile vert or horiz?
  1088.                  * If no tile group you are always the last one both vertically
  1089.                  * and horizontally
  1090.                  */
  1091.                 bool lastv = true, lasth = true;
  1092.  
  1093.                 desired_mode = fb_helper->crtc_info[i].desired_mode;
  1094.                 mode_set = &fb_helper->crtc_info[i].mode_set;
  1095.  
  1096.                 if (!desired_mode)
  1097.                         continue;
  1098.  
  1099.                 crtc_count++;
  1100.  
  1101.                 x = fb_helper->crtc_info[i].x;
  1102.                 y = fb_helper->crtc_info[i].y;
  1103.  
  1104.                 if (gamma_size == 0)
  1105.                         gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size;
  1106.  
  1107.                 sizes.surface_width  = max_t(u32, desired_mode->hdisplay + x, sizes.surface_width);
  1108.                 sizes.surface_height = max_t(u32, desired_mode->vdisplay + y, sizes.surface_height);
  1109.  
  1110.                 for (j = 0; j < mode_set->num_connectors; j++) {
  1111.                         struct drm_connector *connector = mode_set->connectors[j];
  1112.                         if (connector->has_tile) {
  1113.                                 lasth = (connector->tile_h_loc == (connector->num_h_tile - 1));
  1114.                                 lastv = (connector->tile_v_loc == (connector->num_v_tile - 1));
  1115.                                 /* cloning to multiple tiles is just crazy-talk, so: */
  1116.                                 break;
  1117.                         }
  1118.                 }
  1119.  
  1120.                 if (lasth)
  1121.                         sizes.fb_width  = min_t(u32, desired_mode->hdisplay + x, sizes.fb_width);
  1122.                 if (lastv)
  1123.                         sizes.fb_height = min_t(u32, desired_mode->vdisplay + y, sizes.fb_height);
  1124.         }
  1125.  
  1126.         if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) {
  1127.                 /* hmm everyone went away - assume VGA cable just fell out
  1128.                    and will come back later. */
  1129.                 DRM_INFO("Cannot find any crtc or sizes - going 1024x768\n");
  1130.                 sizes.fb_width = sizes.surface_width = 1024;
  1131.                 sizes.fb_height = sizes.surface_height = 768;
  1132.         }
  1133.  
  1134.         /* push down into drivers */
  1135.         ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);
  1136.         if (ret < 0)
  1137.                 return ret;
  1138.  
  1139.         info = fb_helper->fbdev;
  1140.  
  1141.         /*
  1142.          * Set the fb pointer - usually drm_setup_crtcs does this for hotplug
  1143.          * events, but at init time drm_setup_crtcs needs to be called before
  1144.          * the fb is allocated (since we need to figure out the desired size of
  1145.          * the fb before we can allocate it ...). Hence we need to fix things up
  1146.          * here again.
  1147.          */
  1148.         for (i = 0; i < fb_helper->crtc_count; i++)
  1149.                 if (fb_helper->crtc_info[i].mode_set.num_connectors)
  1150.                         fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
  1151.  
  1152.  
  1153.         info->var.pixclock = 0;
  1154.  
  1155.         dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n",
  1156.                         info->node, info->fix.id);
  1157.  
  1158.  
  1159.         list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
  1160.  
  1161.         return 0;
  1162. }
  1163.  
  1164. /**
  1165.  * drm_fb_helper_fill_fix - initializes fixed fbdev information
  1166.  * @info: fbdev registered by the helper
  1167.  * @pitch: desired pitch
  1168.  * @depth: desired depth
  1169.  *
  1170.  * Helper to fill in the fixed fbdev information useful for a non-accelerated
  1171.  * fbdev emulations. Drivers which support acceleration methods which impose
  1172.  * additional constraints need to set up their own limits.
  1173.  *
  1174.  * Drivers should call this (or their equivalent setup code) from their
  1175.  * ->fb_probe callback.
  1176.  */
  1177. void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
  1178.                             uint32_t depth)
  1179. {
  1180.         info->fix.type = FB_TYPE_PACKED_PIXELS;
  1181.         info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
  1182.                 FB_VISUAL_TRUECOLOR;
  1183.         info->fix.mmio_start = 0;
  1184.         info->fix.mmio_len = 0;
  1185.         info->fix.type_aux = 0;
  1186.         info->fix.xpanstep = 1; /* doing it in hw */
  1187.         info->fix.ypanstep = 1; /* doing it in hw */
  1188.         info->fix.ywrapstep = 0;
  1189.         info->fix.accel = FB_ACCEL_NONE;
  1190.  
  1191.         info->fix.line_length = pitch;
  1192.         return;
  1193. }
  1194. EXPORT_SYMBOL(drm_fb_helper_fill_fix);
  1195.  
  1196. /**
  1197.  * drm_fb_helper_fill_var - initalizes variable fbdev information
  1198.  * @info: fbdev instance to set up
  1199.  * @fb_helper: fb helper instance to use as template
  1200.  * @fb_width: desired fb width
  1201.  * @fb_height: desired fb height
  1202.  *
  1203.  * Sets up the variable fbdev metainformation from the given fb helper instance
  1204.  * and the drm framebuffer allocated in fb_helper->fb.
  1205.  *
  1206.  * Drivers should call this (or their equivalent setup code) from their
  1207.  * ->fb_probe callback after having allocated the fbdev backing
  1208.  * storage framebuffer.
  1209.  */
  1210. void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper,
  1211.                             uint32_t fb_width, uint32_t fb_height)
  1212. {
  1213.         struct drm_framebuffer *fb = fb_helper->fb;
  1214.         info->pseudo_palette = fb_helper->pseudo_palette;
  1215.         info->var.xres_virtual = fb->width;
  1216.         info->var.yres_virtual = fb->height;
  1217.         info->var.bits_per_pixel = fb->bits_per_pixel;
  1218.         info->var.accel_flags = FB_ACCELF_TEXT;
  1219.         info->var.xoffset = 0;
  1220.         info->var.yoffset = 0;
  1221.         info->var.activate = FB_ACTIVATE_NOW;
  1222.         info->var.height = -1;
  1223.         info->var.width = -1;
  1224.  
  1225.         switch (fb->depth) {
  1226.         case 8:
  1227.                 info->var.red.offset = 0;
  1228.                 info->var.green.offset = 0;
  1229.                 info->var.blue.offset = 0;
  1230.                 info->var.red.length = 8; /* 8bit DAC */
  1231.                 info->var.green.length = 8;
  1232.                 info->var.blue.length = 8;
  1233.                 info->var.transp.offset = 0;
  1234.                 info->var.transp.length = 0;
  1235.                 break;
  1236.         case 15:
  1237.                 info->var.red.offset = 10;
  1238.                 info->var.green.offset = 5;
  1239.                 info->var.blue.offset = 0;
  1240.                 info->var.red.length = 5;
  1241.                 info->var.green.length = 5;
  1242.                 info->var.blue.length = 5;
  1243.                 info->var.transp.offset = 15;
  1244.                 info->var.transp.length = 1;
  1245.                 break;
  1246.         case 16:
  1247.                 info->var.red.offset = 11;
  1248.                 info->var.green.offset = 5;
  1249.                 info->var.blue.offset = 0;
  1250.                 info->var.red.length = 5;
  1251.                 info->var.green.length = 6;
  1252.                 info->var.blue.length = 5;
  1253.                 info->var.transp.offset = 0;
  1254.                 break;
  1255.         case 24:
  1256.                 info->var.red.offset = 16;
  1257.                 info->var.green.offset = 8;
  1258.                 info->var.blue.offset = 0;
  1259.                 info->var.red.length = 8;
  1260.                 info->var.green.length = 8;
  1261.                 info->var.blue.length = 8;
  1262.                 info->var.transp.offset = 0;
  1263.                 info->var.transp.length = 0;
  1264.                 break;
  1265.         case 32:
  1266.                 info->var.red.offset = 16;
  1267.                 info->var.green.offset = 8;
  1268.                 info->var.blue.offset = 0;
  1269.                 info->var.red.length = 8;
  1270.                 info->var.green.length = 8;
  1271.                 info->var.blue.length = 8;
  1272.                 info->var.transp.offset = 24;
  1273.                 info->var.transp.length = 8;
  1274.                 break;
  1275.         default:
  1276.                 break;
  1277.         }
  1278.  
  1279.         info->var.xres = fb_width;
  1280.         info->var.yres = fb_height;
  1281. }
  1282. EXPORT_SYMBOL(drm_fb_helper_fill_var);
  1283.  
  1284. static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper,
  1285.                                                uint32_t maxX,
  1286.                                                uint32_t maxY)
  1287. {
  1288.         struct drm_connector *connector;
  1289.         int count = 0;
  1290.         int i;
  1291.  
  1292.         for (i = 0; i < fb_helper->connector_count; i++) {
  1293.                 connector = fb_helper->connector_info[i]->connector;
  1294.                 count += connector->funcs->fill_modes(connector, maxX, maxY);
  1295.         }
  1296.  
  1297.         return count;
  1298. }
  1299.  
  1300. struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height)
  1301. {
  1302.         struct drm_display_mode *mode;
  1303.  
  1304.         list_for_each_entry(mode, &fb_connector->connector->modes, head) {
  1305.                 if (mode->hdisplay > width ||
  1306.                     mode->vdisplay > height)
  1307.                         continue;
  1308.                 if (mode->type & DRM_MODE_TYPE_PREFERRED)
  1309.                         return mode;
  1310.         }
  1311.         return NULL;
  1312. }
  1313. EXPORT_SYMBOL(drm_has_preferred_mode);
  1314.  
  1315. static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
  1316. {
  1317.         return fb_connector->connector->cmdline_mode.specified;
  1318. }
  1319.  
  1320. struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn,
  1321.                                                       int width, int height)
  1322. {
  1323.         struct drm_cmdline_mode *cmdline_mode;
  1324.         struct drm_display_mode *mode;
  1325.         bool prefer_non_interlace;
  1326.  
  1327.         cmdline_mode = &fb_helper_conn->connector->cmdline_mode;
  1328.         if (cmdline_mode->specified == false)
  1329.                 return NULL;
  1330.  
  1331.         /* attempt to find a matching mode in the list of modes
  1332.          *  we have gotten so far, if not add a CVT mode that conforms
  1333.          */
  1334.         if (cmdline_mode->rb || cmdline_mode->margins)
  1335.                 goto create_mode;
  1336.  
  1337.         prefer_non_interlace = !cmdline_mode->interlace;
  1338. again:
  1339.         list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
  1340.                 /* check width/height */
  1341.                 if (mode->hdisplay != cmdline_mode->xres ||
  1342.                     mode->vdisplay != cmdline_mode->yres)
  1343.                         continue;
  1344.  
  1345.                 if (cmdline_mode->refresh_specified) {
  1346.                         if (mode->vrefresh != cmdline_mode->refresh)
  1347.                                 continue;
  1348.                 }
  1349.  
  1350.                 if (cmdline_mode->interlace) {
  1351.                         if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
  1352.                                 continue;
  1353.                 } else if (prefer_non_interlace) {
  1354.                         if (mode->flags & DRM_MODE_FLAG_INTERLACE)
  1355.                                 continue;
  1356.                 }
  1357.                 return mode;
  1358.         }
  1359.  
  1360.         if (prefer_non_interlace) {
  1361.                 prefer_non_interlace = false;
  1362.                 goto again;
  1363.         }
  1364.  
  1365. create_mode:
  1366.         mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev,
  1367.                                                  cmdline_mode);
  1368.         list_add(&mode->head, &fb_helper_conn->connector->modes);
  1369.         return mode;
  1370. }
  1371. EXPORT_SYMBOL(drm_pick_cmdline_mode);
  1372.  
  1373. static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
  1374. {
  1375.         bool enable;
  1376.  
  1377.         if (strict)
  1378.                 enable = connector->status == connector_status_connected;
  1379.         else
  1380.                 enable = connector->status != connector_status_disconnected;
  1381.  
  1382.         return enable;
  1383. }
  1384.  
  1385. static void drm_enable_connectors(struct drm_fb_helper *fb_helper,
  1386.                                   bool *enabled)
  1387. {
  1388.         bool any_enabled = false;
  1389.         struct drm_connector *connector;
  1390.         int i = 0;
  1391.  
  1392.         for (i = 0; i < fb_helper->connector_count; i++) {
  1393.                 connector = fb_helper->connector_info[i]->connector;
  1394.                 enabled[i] = drm_connector_enabled(connector, true);
  1395.                 DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
  1396.                           enabled[i] ? "yes" : "no");
  1397.                 any_enabled |= enabled[i];
  1398.         }
  1399.  
  1400.         if (any_enabled)
  1401.                 return;
  1402.  
  1403.         for (i = 0; i < fb_helper->connector_count; i++) {
  1404.                 connector = fb_helper->connector_info[i]->connector;
  1405.                 enabled[i] = drm_connector_enabled(connector, false);
  1406.         }
  1407. }
  1408.  
  1409. static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
  1410.                               struct drm_display_mode **modes,
  1411.                               struct drm_fb_offset *offsets,
  1412.                               bool *enabled, int width, int height)
  1413. {
  1414.         int count, i, j;
  1415.         bool can_clone = false;
  1416.         struct drm_fb_helper_connector *fb_helper_conn;
  1417.         struct drm_display_mode *dmt_mode, *mode;
  1418.  
  1419.         /* only contemplate cloning in the single crtc case */
  1420.         if (fb_helper->crtc_count > 1)
  1421.                 return false;
  1422.  
  1423.         count = 0;
  1424.         for (i = 0; i < fb_helper->connector_count; i++) {
  1425.                 if (enabled[i])
  1426.                         count++;
  1427.         }
  1428.  
  1429.         /* only contemplate cloning if more than one connector is enabled */
  1430.         if (count <= 1)
  1431.                 return false;
  1432.  
  1433.         /* check the command line or if nothing common pick 1024x768 */
  1434.         can_clone = true;
  1435.         for (i = 0; i < fb_helper->connector_count; i++) {
  1436.                 if (!enabled[i])
  1437.                         continue;
  1438.                 fb_helper_conn = fb_helper->connector_info[i];
  1439.                 modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
  1440.                 if (!modes[i]) {
  1441.                         can_clone = false;
  1442.                         break;
  1443.                 }
  1444.                 for (j = 0; j < i; j++) {
  1445.                         if (!enabled[j])
  1446.                                 continue;
  1447.                         if (!drm_mode_equal(modes[j], modes[i]))
  1448.                                 can_clone = false;
  1449.                 }
  1450.         }
  1451.  
  1452.         if (can_clone) {
  1453.                 DRM_DEBUG_KMS("can clone using command line\n");
  1454.                 return true;
  1455.         }
  1456.  
  1457.         /* try and find a 1024x768 mode on each connector */
  1458.         can_clone = true;
  1459.         dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false);
  1460.  
  1461.         for (i = 0; i < fb_helper->connector_count; i++) {
  1462.  
  1463.                 if (!enabled[i])
  1464.                         continue;
  1465.  
  1466.                 fb_helper_conn = fb_helper->connector_info[i];
  1467.                 list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
  1468.                         if (drm_mode_equal(mode, dmt_mode))
  1469.                                 modes[i] = mode;
  1470.                 }
  1471.                 if (!modes[i])
  1472.                         can_clone = false;
  1473.         }
  1474.  
  1475.         if (can_clone) {
  1476.                 DRM_DEBUG_KMS("can clone using 1024x768\n");
  1477.                 return true;
  1478.         }
  1479.         DRM_INFO("kms: can't enable cloning when we probably wanted to.\n");
  1480.         return false;
  1481. }
  1482.  
  1483. static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper,
  1484.                                 struct drm_display_mode **modes,
  1485.                                 struct drm_fb_offset *offsets,
  1486.                                 int idx,
  1487.                                 int h_idx, int v_idx)
  1488. {
  1489.         struct drm_fb_helper_connector *fb_helper_conn;
  1490.         int i;
  1491.         int hoffset = 0, voffset = 0;
  1492.  
  1493.         for (i = 0; i < fb_helper->connector_count; i++) {
  1494.                 fb_helper_conn = fb_helper->connector_info[i];
  1495.                 if (!fb_helper_conn->connector->has_tile)
  1496.                         continue;
  1497.  
  1498.                 if (!modes[i] && (h_idx || v_idx)) {
  1499.                         DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i,
  1500.                                       fb_helper_conn->connector->base.id);
  1501.                         continue;
  1502.                 }
  1503.                 if (fb_helper_conn->connector->tile_h_loc < h_idx)
  1504.                         hoffset += modes[i]->hdisplay;
  1505.  
  1506.                 if (fb_helper_conn->connector->tile_v_loc < v_idx)
  1507.                         voffset += modes[i]->vdisplay;
  1508.         }
  1509.         offsets[idx].x = hoffset;
  1510.         offsets[idx].y = voffset;
  1511.         DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx);
  1512.         return 0;
  1513. }
  1514.  
  1515. static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
  1516.                                  struct drm_display_mode **modes,
  1517.                                  struct drm_fb_offset *offsets,
  1518.                                  bool *enabled, int width, int height)
  1519. {
  1520.         struct drm_fb_helper_connector *fb_helper_conn;
  1521.         int i;
  1522.         uint64_t conn_configured = 0, mask;
  1523.         int tile_pass = 0;
  1524.         mask = (1 << fb_helper->connector_count) - 1;
  1525. retry:
  1526.         for (i = 0; i < fb_helper->connector_count; i++) {
  1527.                 fb_helper_conn = fb_helper->connector_info[i];
  1528.  
  1529.                 if (conn_configured & (1 << i))
  1530.                         continue;
  1531.  
  1532.                 if (enabled[i] == false) {
  1533.                         conn_configured |= (1 << i);
  1534.                         continue;
  1535.                 }
  1536.  
  1537.                 /* first pass over all the untiled connectors */
  1538.                 if (tile_pass == 0 && fb_helper_conn->connector->has_tile)
  1539.                         continue;
  1540.  
  1541.                 if (tile_pass == 1) {
  1542.                         if (fb_helper_conn->connector->tile_h_loc != 0 ||
  1543.                             fb_helper_conn->connector->tile_v_loc != 0)
  1544.                                 continue;
  1545.  
  1546.                 } else {
  1547.                         if (fb_helper_conn->connector->tile_h_loc != tile_pass -1 &&
  1548.                             fb_helper_conn->connector->tile_v_loc != tile_pass - 1)
  1549.                         /* if this tile_pass doesn't cover any of the tiles - keep going */
  1550.                                 continue;
  1551.  
  1552.                         /* find the tile offsets for this pass - need
  1553.                            to find all tiles left and above */
  1554.                         drm_get_tile_offsets(fb_helper, modes, offsets,
  1555.                                              i, fb_helper_conn->connector->tile_h_loc, fb_helper_conn->connector->tile_v_loc);
  1556.                 }
  1557.                 DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
  1558.                               fb_helper_conn->connector->base.id);
  1559.  
  1560.                 /* got for command line mode first */
  1561.                 modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
  1562.                 if (!modes[i]) {
  1563.                         DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n",
  1564.                                       fb_helper_conn->connector->base.id, fb_helper_conn->connector->tile_group ? fb_helper_conn->connector->tile_group->id : 0);
  1565.                         modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height);
  1566.                 }
  1567.                 /* No preferred modes, pick one off the list */
  1568.                 if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) {
  1569.                         list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head)
  1570.                                 break;
  1571.                 }
  1572.                 DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
  1573.                           "none");
  1574.                 conn_configured |= (1 << i);
  1575.         }
  1576.  
  1577.         if ((conn_configured & mask) != mask) {
  1578.                 tile_pass++;
  1579.                 goto retry;
  1580.         }
  1581.         return true;
  1582. }
  1583.  
  1584. static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
  1585.                           struct drm_fb_helper_crtc **best_crtcs,
  1586.                           struct drm_display_mode **modes,
  1587.                           int n, int width, int height)
  1588. {
  1589.         int c, o;
  1590.         struct drm_connector *connector;
  1591.         const struct drm_connector_helper_funcs *connector_funcs;
  1592.         struct drm_encoder *encoder;
  1593.         int my_score, best_score, score;
  1594.         struct drm_fb_helper_crtc **crtcs, *crtc;
  1595.         struct drm_fb_helper_connector *fb_helper_conn;
  1596.  
  1597.         if (n == fb_helper->connector_count)
  1598.                 return 0;
  1599.  
  1600.         fb_helper_conn = fb_helper->connector_info[n];
  1601.         connector = fb_helper_conn->connector;
  1602.  
  1603.         best_crtcs[n] = NULL;
  1604.         best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height);
  1605.         if (modes[n] == NULL)
  1606.                 return best_score;
  1607.  
  1608.         crtcs = kzalloc(fb_helper->connector_count *
  1609.                         sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
  1610.         if (!crtcs)
  1611.                 return best_score;
  1612.  
  1613.         my_score = 1;
  1614.         if (connector->status == connector_status_connected)
  1615.                 my_score++;
  1616.         if (drm_has_cmdline_mode(fb_helper_conn))
  1617.                 my_score++;
  1618.         if (drm_has_preferred_mode(fb_helper_conn, width, height))
  1619.                 my_score++;
  1620.  
  1621.         connector_funcs = connector->helper_private;
  1622.         encoder = connector_funcs->best_encoder(connector);
  1623.         if (!encoder)
  1624.                 goto out;
  1625.  
  1626.         /* select a crtc for this connector and then attempt to configure
  1627.            remaining connectors */
  1628.         for (c = 0; c < fb_helper->crtc_count; c++) {
  1629.                 crtc = &fb_helper->crtc_info[c];
  1630.  
  1631.                 if ((encoder->possible_crtcs & (1 << c)) == 0)
  1632.                         continue;
  1633.  
  1634.                 for (o = 0; o < n; o++)
  1635.                         if (best_crtcs[o] == crtc)
  1636.                                 break;
  1637.  
  1638.                 if (o < n) {
  1639.                         /* ignore cloning unless only a single crtc */
  1640.                         if (fb_helper->crtc_count > 1)
  1641.                                 continue;
  1642.  
  1643.                         if (!drm_mode_equal(modes[o], modes[n]))
  1644.                                 continue;
  1645.                 }
  1646.  
  1647.                 crtcs[n] = crtc;
  1648.                 memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *));
  1649.                 score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1,
  1650.                                                   width, height);
  1651.                 if (score > best_score) {
  1652.                         best_score = score;
  1653.                         memcpy(best_crtcs, crtcs,
  1654.                                fb_helper->connector_count *
  1655.                                sizeof(struct drm_fb_helper_crtc *));
  1656.                 }
  1657.         }
  1658. out:
  1659.         kfree(crtcs);
  1660.         return best_score;
  1661. }
  1662.  
  1663. static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
  1664. {
  1665.         struct drm_device *dev = fb_helper->dev;
  1666.         struct drm_fb_helper_crtc **crtcs;
  1667.         struct drm_display_mode **modes;
  1668.         struct drm_fb_offset *offsets;
  1669.         struct drm_mode_set *modeset;
  1670.         bool *enabled;
  1671.         int width, height;
  1672.         int i;
  1673.  
  1674.         DRM_DEBUG_KMS("\n");
  1675.  
  1676.         width = dev->mode_config.max_width;
  1677.         height = dev->mode_config.max_height;
  1678.  
  1679.         crtcs = kcalloc(dev->mode_config.num_connector,
  1680.                         sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
  1681.         modes = kcalloc(dev->mode_config.num_connector,
  1682.                         sizeof(struct drm_display_mode *), GFP_KERNEL);
  1683.         offsets = kcalloc(dev->mode_config.num_connector,
  1684.                           sizeof(struct drm_fb_offset), GFP_KERNEL);
  1685.         enabled = kcalloc(dev->mode_config.num_connector,
  1686.                           sizeof(bool), GFP_KERNEL);
  1687.         if (!crtcs || !modes || !enabled || !offsets) {
  1688.                 DRM_ERROR("Memory allocation failed\n");
  1689.                 goto out;
  1690.         }
  1691.  
  1692.  
  1693.         drm_enable_connectors(fb_helper, enabled);
  1694.  
  1695.         if (!(fb_helper->funcs->initial_config &&
  1696.               fb_helper->funcs->initial_config(fb_helper, crtcs, modes,
  1697.                                                offsets,
  1698.                                                enabled, width, height))) {
  1699.                 memset(modes, 0, dev->mode_config.num_connector*sizeof(modes[0]));
  1700.                 memset(crtcs, 0, dev->mode_config.num_connector*sizeof(crtcs[0]));
  1701.                 memset(offsets, 0, dev->mode_config.num_connector*sizeof(offsets[0]));
  1702.  
  1703.                 if (!drm_target_cloned(fb_helper, modes, offsets,
  1704.                                        enabled, width, height) &&
  1705.                     !drm_target_preferred(fb_helper, modes, offsets,
  1706.                                           enabled, width, height))
  1707.                         DRM_ERROR("Unable to find initial modes\n");
  1708.  
  1709.                 DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n",
  1710.                               width, height);
  1711.  
  1712.                 drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height);
  1713.         }
  1714.  
  1715.         /* need to set the modesets up here for use later */
  1716.         /* fill out the connector<->crtc mappings into the modesets */
  1717.         for (i = 0; i < fb_helper->crtc_count; i++) {
  1718.                 modeset = &fb_helper->crtc_info[i].mode_set;
  1719.                 modeset->num_connectors = 0;
  1720.                 modeset->fb = NULL;
  1721.         }
  1722.  
  1723.         for (i = 0; i < fb_helper->connector_count; i++) {
  1724.                 struct drm_display_mode *mode = modes[i];
  1725.                 struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
  1726.                 struct drm_fb_offset *offset = &offsets[i];
  1727.                 modeset = &fb_crtc->mode_set;
  1728.  
  1729.                 if (mode && fb_crtc) {
  1730.                         DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n",
  1731.                                       mode->name, fb_crtc->mode_set.crtc->base.id, offset->x, offset->y);
  1732.                         fb_crtc->desired_mode = mode;
  1733.                         fb_crtc->x = offset->x;
  1734.                         fb_crtc->y = offset->y;
  1735.                         if (modeset->mode)
  1736.                                 drm_mode_destroy(dev, modeset->mode);
  1737.                         modeset->mode = drm_mode_duplicate(dev,
  1738.                                                            fb_crtc->desired_mode);
  1739.                         modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector;
  1740.                         modeset->fb = fb_helper->fb;
  1741.                         modeset->x = offset->x;
  1742.                         modeset->y = offset->y;
  1743.                 }
  1744.         }
  1745.  
  1746.         /* Clear out any old modes if there are no more connected outputs. */
  1747.         for (i = 0; i < fb_helper->crtc_count; i++) {
  1748.                 modeset = &fb_helper->crtc_info[i].mode_set;
  1749.                 if (modeset->num_connectors == 0) {
  1750.                         BUG_ON(modeset->fb);
  1751.                         if (modeset->mode)
  1752.                                 drm_mode_destroy(dev, modeset->mode);
  1753.                         modeset->mode = NULL;
  1754.                 }
  1755.         }
  1756. out:
  1757.         kfree(crtcs);
  1758.         kfree(modes);
  1759.         kfree(offsets);
  1760.         kfree(enabled);
  1761. }
  1762.  
  1763. /**
  1764.  * drm_fb_helper_initial_config - setup a sane initial connector configuration
  1765.  * @fb_helper: fb_helper device struct
  1766.  * @bpp_sel: bpp value to use for the framebuffer configuration
  1767.  *
  1768.  * Scans the CRTCs and connectors and tries to put together an initial setup.
  1769.  * At the moment, this is a cloned configuration across all heads with
  1770.  * a new framebuffer object as the backing store.
  1771.  *
  1772.  * Note that this also registers the fbdev and so allows userspace to call into
  1773.  * the driver through the fbdev interfaces.
  1774.  *
  1775.  * This function will call down into the ->fb_probe callback to let
  1776.  * the driver allocate and initialize the fbdev info structure and the drm
  1777.  * framebuffer used to back the fbdev. drm_fb_helper_fill_var() and
  1778.  * drm_fb_helper_fill_fix() are provided as helpers to setup simple default
  1779.  * values for the fbdev info structure.
  1780.  *
  1781.  * RETURNS:
  1782.  * Zero if everything went ok, nonzero otherwise.
  1783.  */
  1784. int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
  1785. {
  1786.         struct drm_device *dev = fb_helper->dev;
  1787.         int count = 0;
  1788.  
  1789.         if (!drm_fbdev_emulation)
  1790.                 return 0;
  1791.  
  1792.         mutex_lock(&dev->mode_config.mutex);
  1793.         count = drm_fb_helper_probe_connector_modes(fb_helper,
  1794.                                                     dev->mode_config.max_width,
  1795.                                                     dev->mode_config.max_height);
  1796.         mutex_unlock(&dev->mode_config.mutex);
  1797.         /*
  1798.          * we shouldn't end up with no modes here.
  1799.          */
  1800.         if (count == 0)
  1801.                 dev_info(fb_helper->dev->dev, "No connectors reported connected with modes\n");
  1802.  
  1803.         drm_setup_crtcs(fb_helper);
  1804.  
  1805.         return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
  1806. }
  1807. EXPORT_SYMBOL(drm_fb_helper_initial_config);
  1808.  
  1809. /**
  1810.  * drm_fb_helper_hotplug_event - respond to a hotplug notification by
  1811.  *                               probing all the outputs attached to the fb
  1812.  * @fb_helper: the drm_fb_helper
  1813.  *
  1814.  * Scan the connectors attached to the fb_helper and try to put together a
  1815.  * setup after *notification of a change in output configuration.
  1816.  *
  1817.  * Called at runtime, takes the mode config locks to be able to check/change the
  1818.  * modeset configuration. Must be run from process context (which usually means
  1819.  * either the output polling work or a work item launched from the driver's
  1820.  * hotplug interrupt).
  1821.  *
  1822.  * Note that drivers may call this even before calling
  1823.  * drm_fb_helper_initial_config but only aftert drm_fb_helper_init. This allows
  1824.  * for a race-free fbcon setup and will make sure that the fbdev emulation will
  1825.  * not miss any hotplug events.
  1826.  *
  1827.  * RETURNS:
  1828.  * 0 on success and a non-zero error code otherwise.
  1829.  */
  1830. int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
  1831. {
  1832.         struct drm_device *dev = fb_helper->dev;
  1833.         u32 max_width, max_height;
  1834.  
  1835.         if (!drm_fbdev_emulation)
  1836.                 return 0;
  1837.  
  1838.         mutex_lock(&fb_helper->dev->mode_config.mutex);
  1839.         if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
  1840.                 fb_helper->delayed_hotplug = true;
  1841.                 mutex_unlock(&fb_helper->dev->mode_config.mutex);
  1842.                 return 0;
  1843.         }
  1844.         DRM_DEBUG_KMS("\n");
  1845.  
  1846.     max_width = 8192; //fb_helper->fb->width;
  1847.     max_height = 8192; //fb_helper->fb->height;
  1848.  
  1849.         drm_fb_helper_probe_connector_modes(fb_helper, max_width, max_height);
  1850.         mutex_unlock(&fb_helper->dev->mode_config.mutex);
  1851.  
  1852. //   drm_modeset_lock_all(dev);
  1853. //   drm_setup_crtcs(fb_helper);
  1854. //   drm_modeset_unlock_all(dev);
  1855. //   drm_fb_helper_set_par(fb_helper->fbdev);
  1856.         return 0;
  1857. }
  1858. EXPORT_SYMBOL(drm_fb_helper_hotplug_event);
  1859.  
  1860. /* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT)
  1861.  * but the module doesn't depend on any fb console symbols.  At least
  1862.  * attempt to load fbcon to avoid leaving the system without a usable console.
  1863.  */
  1864. #if defined(CONFIG_FRAMEBUFFER_CONSOLE_MODULE) && !defined(CONFIG_EXPERT)
  1865. static int __init drm_fb_helper_modinit(void)
  1866. {
  1867.         const char *name = "fbcon";
  1868.         struct module *fbcon;
  1869.  
  1870.         mutex_lock(&module_mutex);
  1871.         fbcon = find_module(name);
  1872.         mutex_unlock(&module_mutex);
  1873.  
  1874.         if (!fbcon)
  1875.                 request_module_nowait(name);
  1876.         return 0;
  1877. }
  1878.  
  1879. module_init(drm_fb_helper_modinit);
  1880. #endif
  1881.