Subversion Repositories Kolibri OS

Rev

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