diff options
author | 2020-06-08 04:47:57 +0000 | |
---|---|---|
committer | 2020-06-08 04:47:57 +0000 | |
commit | c349dbc7938c71a30e13c1be4acc1976165f4630 (patch) | |
tree | 8798187dfd7a927a15123e8dad31b782b074baa8 /sys/dev/pci/drm/drm_fb_helper.c | |
parent | The errcheck() function treats an errno of ERANGE or EDOM as something (diff) | |
download | wireguard-openbsd-c349dbc7938c71a30e13c1be4acc1976165f4630.tar.xz wireguard-openbsd-c349dbc7938c71a30e13c1be4acc1976165f4630.zip |
update drm to linux 5.7
adds kernel support for
amdgpu: vega20, raven2, renoir, navi10, navi14
inteldrm: icelake, tigerlake
Thanks to the OpenBSD Foundation for sponsoring this work, kettenis@ for
helping, patrick@ for helping adapt rockchip drm and many developers for
testing.
Diffstat (limited to 'sys/dev/pci/drm/drm_fb_helper.c')
-rw-r--r-- | sys/dev/pci/drm/drm_fb_helper.c | 1736 |
1 files changed, 339 insertions, 1397 deletions
diff --git a/sys/dev/pci/drm/drm_fb_helper.c b/sys/dev/pci/drm/drm_fb_helper.c index 90cba88a69a..7f161ffe594 100644 --- a/sys/dev/pci/drm/drm_fb_helper.c +++ b/sys/dev/pci/drm/drm_fb_helper.c @@ -32,18 +32,22 @@ #include <linux/console.h> #include <linux/dma-buf.h> #include <linux/kernel.h> -#include <linux/sysrq.h> -#include <linux/slab.h> #include <linux/module.h> -#include <drm/drmP.h> +#include <linux/slab.h> +#include <linux/sysrq.h> +#include <linux/vmalloc.h> + +#include <drm/drm_atomic.h> #include <drm/drm_crtc.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_crtc_helper.h> -#include <drm/drm_atomic.h> -#include <drm/drm_atomic_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_print.h> +#include <drm/drm_vblank.h> -#include "drm_crtc_internal.h" #include "drm_crtc_helper_internal.h" +#include "drm_internal.h" static bool drm_fbdev_emulation = true; module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600); @@ -75,7 +79,7 @@ static int drm_fbdev_overalloc = 100; #if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM) static bool drm_leak_fbdev_smem = false; module_param_unsafe(drm_leak_fbdev_smem, bool, 0600); -MODULE_PARM_DESC(fbdev_emulation, +MODULE_PARM_DESC(drm_leak_fbdev_smem, "Allow unsafe leaking fbdev physical smem address [default=false]"); #endif @@ -92,15 +96,8 @@ static DEFINE_MUTEX(kernel_fb_helper_lock); * * Drivers that support a dumb buffer with a virtual address and mmap support, * should try out the generic fbdev emulation using drm_fbdev_generic_setup(). - * - * Setup fbdev emulation by calling drm_fb_helper_fbdev_setup() and tear it - * down by calling drm_fb_helper_fbdev_teardown(). - * - * Drivers that need to handle connector hotplugging (e.g. dp mst) can't use - * the setup helper and will need to do the whole four-step setup process with - * drm_fb_helper_prepare(), drm_fb_helper_init(), - * drm_fb_helper_single_add_all_connectors(), enable hotplugging and - * drm_fb_helper_initial_config() to avoid a possible race window. + * It will automatically set up deferred I/O if the driver requires a shadow + * buffer. * * At runtime drivers should restore the fbdev console by using * drm_fb_helper_lastclose() as their &drm_driver.lastclose callback. @@ -124,8 +121,7 @@ static DEFINE_MUTEX(kernel_fb_helper_lock); * encoders and connectors. To finish up the fbdev helper initialization, the * drm_fb_helper_init() function is called. To probe for all attached displays * and set up an initial configuration using the detected hardware, drivers - * should call drm_fb_helper_single_add_all_connectors() followed by - * drm_fb_helper_initial_config(). + * should call drm_fb_helper_initial_config(). * * If &drm_framebuffer_funcs.dirty is set, the * drm_fb_helper_{cfb,sys}_{write,fillrect,copyarea,imageblit} functions will @@ -134,170 +130,11 @@ static DEFINE_MUTEX(kernel_fb_helper_lock); * always run in process context since the fb_*() function could be running in * atomic context. If drm_fb_helper_deferred_io() is used as the deferred_io * callback it will also schedule dirty_work with the damage collected from the - * mmap page writes. Drivers can use drm_fb_helper_defio_init() to setup - * deferred I/O (coupled with drm_fb_helper_fbdev_teardown()). - */ - -#define drm_fb_helper_for_each_connector(fbh, i__) \ - for (({ lockdep_assert_held(&(fbh)->lock); }), \ - i__ = 0; i__ < (fbh)->connector_count; i__++) - -static int __drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - struct drm_fb_helper_connector *fb_conn; - struct drm_fb_helper_connector **temp; - unsigned int count; - - if (!drm_fbdev_emulation) - return 0; - - lockdep_assert_held(&fb_helper->lock); - - count = fb_helper->connector_count + 1; - - if (count > fb_helper->connector_info_alloc_count) { - size_t size = count * sizeof(fb_conn); - - temp = kmalloc(size, GFP_KERNEL); - if (!temp) - return -ENOMEM; - memcpy(temp, fb_helper->connector_info, sizeof(fb_conn) * fb_helper->connector_info_alloc_count); - kfree(fb_helper->connector_info); - - fb_helper->connector_info_alloc_count = count; - fb_helper->connector_info = temp; - } - - fb_conn = kzalloc(sizeof(*fb_conn), GFP_KERNEL); - if (!fb_conn) - return -ENOMEM; - - drm_connector_get(connector); - fb_conn->connector = connector; - fb_helper->connector_info[fb_helper->connector_count++] = fb_conn; - - return 0; -} - -int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - int err; - - if (!fb_helper) - return 0; - - mutex_lock(&fb_helper->lock); - err = __drm_fb_helper_add_one_connector(fb_helper, connector); - mutex_unlock(&fb_helper->lock); - - return err; -} -EXPORT_SYMBOL(drm_fb_helper_add_one_connector); - -/** - * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev - * emulation helper - * @fb_helper: fbdev initialized with drm_fb_helper_init, can be NULL + * mmap page writes. * - * This functions adds all the available connectors for use with the given - * fb_helper. This is a separate step to allow drivers to freely assign - * connectors to the fbdev, e.g. if some are reserved for special purposes or - * not adequate to be used for the fbcon. - * - * This function is protected against concurrent connector hotadds/removals - * using drm_fb_helper_add_one_connector() and - * drm_fb_helper_remove_one_connector(). + * Deferred I/O is not compatible with SHMEM. Such drivers should request an + * fbdev shadow buffer and call drm_fbdev_generic_setup() instead. */ -int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) -{ - struct drm_device *dev; - struct drm_connector *connector; - struct drm_connector_list_iter conn_iter; - int i, ret = 0; - - if (!drm_fbdev_emulation || !fb_helper) - return 0; - - dev = fb_helper->dev; - - mutex_lock(&fb_helper->lock); - drm_connector_list_iter_begin(dev, &conn_iter); - drm_for_each_connector_iter(connector, &conn_iter) { - if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) - continue; - - ret = __drm_fb_helper_add_one_connector(fb_helper, connector); - if (ret) - goto fail; - } - goto out; - -fail: - drm_fb_helper_for_each_connector(fb_helper, i) { - struct drm_fb_helper_connector *fb_helper_connector = - fb_helper->connector_info[i]; - - drm_connector_put(fb_helper_connector->connector); - - kfree(fb_helper_connector); - fb_helper->connector_info[i] = NULL; - } - fb_helper->connector_count = 0; -out: - drm_connector_list_iter_end(&conn_iter); - mutex_unlock(&fb_helper->lock); - - return ret; -} -EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors); - -static int __drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - struct drm_fb_helper_connector *fb_helper_connector; - int i, j; - - if (!drm_fbdev_emulation) - return 0; - - lockdep_assert_held(&fb_helper->lock); - - drm_fb_helper_for_each_connector(fb_helper, i) { - if (fb_helper->connector_info[i]->connector == connector) - break; - } - - if (i == fb_helper->connector_count) - return -EINVAL; - fb_helper_connector = fb_helper->connector_info[i]; - drm_connector_put(fb_helper_connector->connector); - - for (j = i + 1; j < fb_helper->connector_count; j++) - fb_helper->connector_info[j - 1] = fb_helper->connector_info[j]; - - fb_helper->connector_count--; - kfree(fb_helper_connector); - - return 0; -} - -int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - int err; - - if (!fb_helper) - return 0; - - mutex_lock(&fb_helper->lock); - err = __drm_fb_helper_remove_one_connector(fb_helper, connector); - mutex_unlock(&fb_helper->lock); - - return err; -} -EXPORT_SYMBOL(drm_fb_helper_remove_one_connector); static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) { @@ -322,13 +159,11 @@ int drm_fb_helper_debug_enter(struct fb_info *info) { struct drm_fb_helper *helper = info->par; const struct drm_crtc_helper_funcs *funcs; - int i; + struct drm_mode_set *mode_set; list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { - for (i = 0; i < helper->crtc_count; i++) { - struct drm_mode_set *mode_set = - &helper->crtc_info[i].mode_set; - + mutex_lock(&helper->client.modeset_mutex); + drm_client_for_each_modeset(mode_set, &helper->client) { if (!mode_set->crtc->enabled) continue; @@ -345,6 +180,7 @@ int drm_fb_helper_debug_enter(struct fb_info *info) mode_set->y, ENTER_ATOMIC_MODE_SET); } + mutex_unlock(&helper->client.modeset_mutex); } return 0; @@ -358,14 +194,17 @@ EXPORT_SYMBOL(drm_fb_helper_debug_enter); int drm_fb_helper_debug_leave(struct fb_info *info) { struct drm_fb_helper *helper = info->par; + struct drm_client_dev *client = &helper->client; +#ifdef notyet + struct drm_device *dev = helper->dev; +#endif struct drm_crtc *crtc; const struct drm_crtc_helper_funcs *funcs; + struct drm_mode_set *mode_set; struct drm_framebuffer *fb; - int i; - - for (i = 0; i < helper->crtc_count; i++) { - struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set; + mutex_lock(&client->modeset_mutex); + drm_client_for_each_modeset(mode_set, client) { crtc = mode_set->crtc; if (drm_drv_uses_atomic_modeset(crtc->dev)) continue; @@ -377,7 +216,7 @@ int drm_fb_helper_debug_leave(struct fb_info *info) continue; if (!fb) { - DRM_ERROR("no fb to restore??\n"); + drm_err(dev, "no fb to restore?\n"); continue; } @@ -388,143 +227,12 @@ int drm_fb_helper_debug_leave(struct fb_info *info) funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x, crtc->y, LEAVE_ATOMIC_MODE_SET); } + mutex_unlock(&client->modeset_mutex); return 0; } EXPORT_SYMBOL(drm_fb_helper_debug_leave); -static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper, bool active) -{ - struct drm_device *dev = fb_helper->dev; - struct drm_plane_state *plane_state; - struct drm_plane *plane; - struct drm_atomic_state *state; - int i, ret; - struct drm_modeset_acquire_ctx ctx; - - drm_modeset_acquire_init(&ctx, 0); - - state = drm_atomic_state_alloc(dev); - if (!state) { - ret = -ENOMEM; - goto out_ctx; - } - - state->acquire_ctx = &ctx; -retry: - drm_for_each_plane(plane, dev) { - plane_state = drm_atomic_get_plane_state(state, plane); - if (IS_ERR(plane_state)) { - ret = PTR_ERR(plane_state); - goto out_state; - } - - plane_state->rotation = DRM_MODE_ROTATE_0; - - /* disable non-primary: */ - if (plane->type == DRM_PLANE_TYPE_PRIMARY) - continue; - - ret = __drm_atomic_helper_disable_plane(plane, plane_state); - if (ret != 0) - goto out_state; - } - - for (i = 0; i < fb_helper->crtc_count; i++) { - struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; - struct drm_plane *primary = mode_set->crtc->primary; - - /* Cannot fail as we've already gotten the plane state above */ - plane_state = drm_atomic_get_new_plane_state(state, primary); - plane_state->rotation = fb_helper->crtc_info[i].rotation; - - ret = __drm_atomic_helper_set_config(mode_set, state); - if (ret != 0) - goto out_state; - - /* - * __drm_atomic_helper_set_config() sets active when a - * mode is set, unconditionally clear it if we force DPMS off - */ - if (!active) { - struct drm_crtc *crtc = mode_set->crtc; - struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); - - crtc_state->active = false; - } - } - - ret = drm_atomic_commit(state); - -out_state: - if (ret == -EDEADLK) - goto backoff; - - drm_atomic_state_put(state); -out_ctx: - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); - - return ret; - -backoff: - drm_atomic_state_clear(state); - drm_modeset_backoff(&ctx); - - goto retry; -} - -static int restore_fbdev_mode_legacy(struct drm_fb_helper *fb_helper) -{ - struct drm_device *dev = fb_helper->dev; - struct drm_plane *plane; - int i, ret = 0; - - drm_modeset_lock_all(fb_helper->dev); - drm_for_each_plane(plane, dev) { - if (plane->type != DRM_PLANE_TYPE_PRIMARY) - drm_plane_force_disable(plane); - - if (plane->rotation_property) - drm_mode_plane_set_obj_prop(plane, - plane->rotation_property, - DRM_MODE_ROTATE_0); - } - - for (i = 0; i < fb_helper->crtc_count; i++) { - struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; - struct drm_crtc *crtc = mode_set->crtc; - - if (crtc->funcs->cursor_set2) { - ret = crtc->funcs->cursor_set2(crtc, NULL, 0, 0, 0, 0, 0); - if (ret) - goto out; - } else if (crtc->funcs->cursor_set) { - ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0); - if (ret) - goto out; - } - - ret = drm_mode_set_config_internal(mode_set); - if (ret) - goto out; - } -out: - drm_modeset_unlock_all(fb_helper->dev); - - return ret; -} - -static int restore_fbdev_mode(struct drm_fb_helper *fb_helper) -{ - struct drm_device *dev = fb_helper->dev; - - if (drm_drv_uses_atomic_modeset(dev)) - return restore_fbdev_mode_atomic(fb_helper, true); - else - return restore_fbdev_mode_legacy(fb_helper); -} - /** * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration * @fb_helper: driver-allocated fbdev helper, can be NULL @@ -548,7 +256,11 @@ int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper) return 0; mutex_lock(&fb_helper->lock); - ret = restore_fbdev_mode(fb_helper); +#ifdef __linux__ + ret = drm_client_modeset_commit(&fb_helper->client); +#else + ret = drm_client_modeset_commit_locked(&fb_helper->client); +#endif do_delayed = fb_helper->delayed_hotplug; if (do_delayed) @@ -562,39 +274,6 @@ int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper) } EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked); -static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) -{ - struct drm_device *dev = fb_helper->dev; - struct drm_crtc *crtc; - int bound = 0, crtcs_bound = 0; - - /* - * Sometimes user space wants everything disabled, so don't steal the - * display if there's a master. - */ -#ifdef notyet - if (READ_ONCE(dev->master)) - return false; -#else - if (!SPLAY_EMPTY(&dev->files)) - return false; -#endif - - drm_for_each_crtc(crtc, dev) { - drm_modeset_lock(&crtc->mutex, NULL); - if (crtc->primary->fb) - crtcs_bound++; - if (crtc->primary->fb == fb_helper->fb) - bound++; - drm_modeset_unlock(&crtc->mutex); - } - - if (bound < crtcs_bound) - return false; - - return true; -} - #ifdef CONFIG_MAGIC_SYSRQ /* * restore fbcon display for all kms driver's using this helper, used for sysrq @@ -615,7 +294,7 @@ static bool drm_fb_helper_force_kernel_mode(void) continue; mutex_lock(&helper->lock); - ret = restore_fbdev_mode(helper); + ret = drm_client_modeset_commit_locked(&helper->client); if (ret) error = true; mutex_unlock(&helper->lock); @@ -643,52 +322,16 @@ static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { .help_msg = "force-fb(V)", .action_msg = "Restore framebuffer console", }; -#elif defined(__linux__) +#else static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { }; #endif -static void dpms_legacy(struct drm_fb_helper *fb_helper, int dpms_mode) -{ - struct drm_device *dev = fb_helper->dev; - struct drm_crtc *crtc; - struct drm_connector *connector; - int i, j; - - drm_modeset_lock_all(dev); - for (i = 0; i < fb_helper->crtc_count; i++) { - crtc = fb_helper->crtc_info[i].mode_set.crtc; - - if (!crtc->enabled) - continue; - - /* Walk the connectors & encoders on this fb turning them on/off */ - drm_fb_helper_for_each_connector(fb_helper, j) { - connector = fb_helper->connector_info[j]->connector; - connector->funcs->dpms(connector, dpms_mode); - drm_object_property_set_value(&connector->base, - dev->mode_config.dpms_property, dpms_mode); - } - } - drm_modeset_unlock_all(dev); -} - static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) { struct drm_fb_helper *fb_helper = info->par; - /* - * For each CRTC in this fb, turn the connectors on/off. - */ mutex_lock(&fb_helper->lock); - if (!drm_fb_helper_is_bound(fb_helper)) { - mutex_unlock(&fb_helper->lock); - return; - } - - if (drm_drv_uses_atomic_modeset(fb_helper->dev)) - restore_fbdev_mode_atomic(fb_helper, dpms_mode == DRM_MODE_DPMS_ON); - else - dpms_legacy(fb_helper, dpms_mode); + drm_client_modeset_dpms(&fb_helper->client, dpms_mode); mutex_unlock(&fb_helper->lock); } @@ -728,62 +371,21 @@ int drm_fb_helper_blank(int blank, struct fb_info *info) } EXPORT_SYMBOL(drm_fb_helper_blank); -static void drm_fb_helper_modeset_release(struct drm_fb_helper *helper, - struct drm_mode_set *modeset) -{ - int i; - - for (i = 0; i < modeset->num_connectors; i++) { - drm_connector_put(modeset->connectors[i]); - modeset->connectors[i] = NULL; - } - modeset->num_connectors = 0; - - drm_mode_destroy(helper->dev, modeset->mode); - modeset->mode = NULL; - - /* FIXME should hold a ref? */ - modeset->fb = NULL; -} - -static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) -{ - int i; - - for (i = 0; i < helper->connector_count; i++) { - drm_connector_put(helper->connector_info[i]->connector); - kfree(helper->connector_info[i]); - } - kfree(helper->connector_info); - - for (i = 0; i < helper->crtc_count; i++) { - struct drm_mode_set *modeset = &helper->crtc_info[i].mode_set; - - drm_fb_helper_modeset_release(helper, modeset); - kfree(modeset->connectors); - } - kfree(helper->crtc_info); -} - static void drm_fb_helper_resume_worker(struct work_struct *work) { -#ifdef __linux__ struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper, resume_work); console_lock(); fb_set_suspend(helper->fbdev, 0); console_unlock(); -#endif } static void drm_fb_helper_dirty_blit_real(struct drm_fb_helper *fb_helper, struct drm_clip_rect *clip) { - STUB(); -#if 0 struct drm_framebuffer *fb = fb_helper->fb; - unsigned int cpp = drm_format_plane_cpp(fb->format->format, 0); + unsigned int cpp = fb->format->cpp[0]; size_t offset = clip->y1 * fb->pitches[0] + clip->x1 * cpp; void *src = fb_helper->fbdev->screen_buffer + offset; void *dst = fb_helper->buffer->vaddr + offset; @@ -795,7 +397,6 @@ static void drm_fb_helper_dirty_blit_real(struct drm_fb_helper *fb_helper, src += fb->pitches[0]; dst += fb->pitches[0]; } -#endif } static void drm_fb_helper_dirty_work(struct work_struct *work) @@ -805,6 +406,7 @@ static void drm_fb_helper_dirty_work(struct work_struct *work) struct drm_clip_rect *clip = &helper->dirty_clip; struct drm_clip_rect clip_copy; unsigned long flags; + void *vaddr; spin_lock_irqsave(&helper->dirty_lock, flags); clip_copy = *clip; @@ -814,10 +416,20 @@ static void drm_fb_helper_dirty_work(struct work_struct *work) /* call dirty callback only when it has been really touched */ if (clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2) { + /* Generic fbdev uses a shadow buffer */ - if (helper->buffer) + if (helper->buffer) { + vaddr = drm_client_buffer_vmap(helper->buffer); + if (IS_ERR(vaddr)) + return; drm_fb_helper_dirty_blit_real(helper, &clip_copy); - helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, &clip_copy, 1); + } + if (helper->fb->funcs->dirty) + helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, + &clip_copy, 1); + + if (helper->buffer) + drm_client_buffer_vunmap(helper->buffer); } } @@ -848,7 +460,6 @@ EXPORT_SYMBOL(drm_fb_helper_prepare); * drm_fb_helper_init - initialize a &struct drm_fb_helper * @dev: drm device * @fb_helper: driver-allocated fbdev helper structure to initialize - * @max_conn_count: max connector count * * This allocates the structures for the fbdev helper with the given limits. * Note that this won't yet touch the hardware (through the driver interfaces) @@ -861,58 +472,28 @@ EXPORT_SYMBOL(drm_fb_helper_prepare); * Zero if everything went ok, nonzero otherwise. */ int drm_fb_helper_init(struct drm_device *dev, - struct drm_fb_helper *fb_helper, - int max_conn_count) + struct drm_fb_helper *fb_helper) { - struct drm_crtc *crtc; - struct drm_mode_config *config = &dev->mode_config; - int i; + int ret; if (!drm_fbdev_emulation) { dev->fb_helper = fb_helper; return 0; } - if (!max_conn_count) - return -EINVAL; - - fb_helper->crtc_info = kcalloc(config->num_crtc, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL); - if (!fb_helper->crtc_info) - return -ENOMEM; - - fb_helper->crtc_count = config->num_crtc; - fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL); - if (!fb_helper->connector_info) { - kfree(fb_helper->crtc_info); - return -ENOMEM; - } - fb_helper->connector_info_alloc_count = dev->mode_config.num_connector; - fb_helper->connector_count = 0; - - for (i = 0; i < fb_helper->crtc_count; i++) { - fb_helper->crtc_info[i].mode_set.connectors = - kcalloc(max_conn_count, - sizeof(struct drm_connector *), - GFP_KERNEL); - - if (!fb_helper->crtc_info[i].mode_set.connectors) - goto out_free; - fb_helper->crtc_info[i].mode_set.num_connectors = 0; - fb_helper->crtc_info[i].rotation = DRM_MODE_ROTATE_0; - } - - i = 0; - drm_for_each_crtc(crtc, dev) { - fb_helper->crtc_info[i].mode_set.crtc = crtc; - i++; + /* + * If this is not the generic fbdev client, initialize a drm_client + * without callbacks so we can use the modesets. + */ + if (!fb_helper->client.funcs) { + ret = drm_client_init(dev, &fb_helper->client, "drm_fb_helper", NULL); + if (ret) + return ret; } dev->fb_helper = fb_helper; return 0; -out_free: - drm_fb_helper_crtc_free(fb_helper); - return -ENOMEM; } EXPORT_SYMBOL(drm_fb_helper_init); @@ -931,9 +512,7 @@ EXPORT_SYMBOL(drm_fb_helper_init); */ struct fb_info *drm_fb_helper_alloc_fbi(struct drm_fb_helper *fb_helper) { -#ifdef __linux__ struct device *dev = fb_helper->dev->dev; -#endif struct fb_info *info; #ifdef __linux__ int ret; @@ -956,6 +535,7 @@ struct fb_info *drm_fb_helper_alloc_fbi(struct drm_fb_helper *fb_helper) #endif fb_helper->fbdev = info; + info->skip_vt_switch = true; return info; @@ -979,10 +559,8 @@ EXPORT_SYMBOL(drm_fb_helper_alloc_fbi); */ void drm_fb_helper_unregister_fbi(struct drm_fb_helper *fb_helper) { -#ifdef __linux__ if (fb_helper && fb_helper->fbdev) unregister_framebuffer(fb_helper->fbdev); -#endif } EXPORT_SYMBOL(drm_fb_helper_unregister_fbi); @@ -990,14 +568,11 @@ EXPORT_SYMBOL(drm_fb_helper_unregister_fbi); * drm_fb_helper_fini - finialize a &struct drm_fb_helper * @fb_helper: driver-allocated fbdev helper, can be NULL * - * This cleans up all remaining resources associated with @fb_helper. Must be - * called after drm_fb_helper_unlink_fbi() was called. + * This cleans up all remaining resources associated with @fb_helper. */ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) { -#ifdef __linux__ struct fb_info *info; -#endif if (!fb_helper) return; @@ -1010,45 +585,42 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) cancel_work_sync(&fb_helper->resume_work); cancel_work_sync(&fb_helper->dirty_work); -#ifdef __linux__ info = fb_helper->fbdev; if (info) { +#ifdef __linux__ if (info->cmap.len) fb_dealloc_cmap(&info->cmap); +#endif framebuffer_release(info); } fb_helper->fbdev = NULL; -#endif mutex_lock(&kernel_fb_helper_lock); if (!list_empty(&fb_helper->kernel_fb_list)) { list_del(&fb_helper->kernel_fb_list); -#ifdef __linux__ if (list_empty(&kernel_fb_helper_list)) unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); -#endif } mutex_unlock(&kernel_fb_helper_lock); mutex_destroy(&fb_helper->lock); - drm_fb_helper_crtc_free(fb_helper); + if (!fb_helper->client.funcs) + drm_client_release(&fb_helper->client); } EXPORT_SYMBOL(drm_fb_helper_fini); #ifdef __linux__ -/** - * drm_fb_helper_unlink_fbi - wrapper around unlink_framebuffer - * @fb_helper: driver-allocated fbdev helper, can be NULL - * - * A wrapper around unlink_framebuffer implemented by fbdev core - */ -void drm_fb_helper_unlink_fbi(struct drm_fb_helper *fb_helper) + +static bool drm_fbdev_use_shadow_fb(struct drm_fb_helper *fb_helper) { - if (fb_helper && fb_helper->fbdev) - unlink_framebuffer(fb_helper->fbdev); + struct drm_device *dev = fb_helper->dev; + struct drm_framebuffer *fb = fb_helper->fb; + + return dev->mode_config.prefer_shadow_fbdev || + dev->mode_config.prefer_shadow || + fb->funcs->dirty; } -EXPORT_SYMBOL(drm_fb_helper_unlink_fbi); static void drm_fb_helper_dirty(struct fb_info *info, u32 x, u32 y, u32 width, u32 height) @@ -1057,7 +629,7 @@ static void drm_fb_helper_dirty(struct fb_info *info, u32 x, u32 y, struct drm_clip_rect *clip = &helper->dirty_clip; unsigned long flags; - if (!helper->fb->funcs->dirty) + if (!drm_fbdev_use_shadow_fb(helper)) return; spin_lock_irqsave(&helper->dirty_lock, flags); @@ -1082,7 +654,7 @@ void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagelist) { unsigned long start, end, min, max; - struct page *page; + struct vm_page *page; u32 y1, y2; min = ULONG_MAX; @@ -1104,49 +676,6 @@ void drm_fb_helper_deferred_io(struct fb_info *info, EXPORT_SYMBOL(drm_fb_helper_deferred_io); /** - * drm_fb_helper_defio_init - fbdev deferred I/O initialization - * @fb_helper: driver-allocated fbdev helper - * - * This function allocates &fb_deferred_io, sets callback to - * drm_fb_helper_deferred_io(), delay to 50ms and calls fb_deferred_io_init(). - * It should be called from the &drm_fb_helper_funcs->fb_probe callback. - * drm_fb_helper_fbdev_teardown() cleans up deferred I/O. - * - * NOTE: A copy of &fb_ops is made and assigned to &info->fbops. This is done - * because fb_deferred_io_cleanup() clears &fbops->fb_mmap and would thereby - * affect other instances of that &fb_ops. - * - * Returns: - * 0 on success or a negative error code on failure. - */ -int drm_fb_helper_defio_init(struct drm_fb_helper *fb_helper) -{ - struct fb_info *info = fb_helper->fbdev; - struct fb_deferred_io *fbdefio; - struct fb_ops *fbops; - - fbdefio = kzalloc(sizeof(*fbdefio), GFP_KERNEL); - fbops = kzalloc(sizeof(*fbops), GFP_KERNEL); - if (!fbdefio || !fbops) { - kfree(fbdefio); - kfree(fbops); - return -ENOMEM; - } - - info->fbdefio = fbdefio; - fbdefio->delay = msecs_to_jiffies(50); - fbdefio->deferred_io = drm_fb_helper_deferred_io; - - *fbops = *info->fbops; - info->fbops = fbops; - - fb_deferred_io_init(info); - - return 0; -} -EXPORT_SYMBOL(drm_fb_helper_defio_init); - -/** * drm_fb_helper_sys_read - wrapper around fb_sys_read * @info: fb_info struct pointer * @buf: userspace buffer to read from framebuffer memory @@ -1281,7 +810,7 @@ void drm_fb_helper_cfb_imageblit(struct fb_info *info, } EXPORT_SYMBOL(drm_fb_helper_cfb_imageblit); -#endif +#endif /* __linux__ */ /** * drm_fb_helper_set_suspend - wrapper around fb_set_suspend @@ -1318,6 +847,7 @@ EXPORT_SYMBOL(drm_fb_helper_set_suspend); void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper, bool suspend) { + STUB(); #ifdef notyet if (!fb_helper || !fb_helper->fbdev) return; @@ -1349,7 +879,6 @@ EXPORT_SYMBOL(drm_fb_helper_set_suspend_unlocked); #ifdef __linux__ - static int setcmap_pseudo_palette(struct fb_cmap *cmap, struct fb_info *info) { u32 *palette = (u32 *)info->pseudo_palette; @@ -1385,13 +914,14 @@ static int setcmap_pseudo_palette(struct fb_cmap *cmap, struct fb_info *info) static int setcmap_legacy(struct fb_cmap *cmap, struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par; + struct drm_mode_set *modeset; struct drm_crtc *crtc; u16 *r, *g, *b; - int i, ret = 0; + int ret = 0; drm_modeset_lock_all(fb_helper->dev); - for (i = 0; i < fb_helper->crtc_count; i++) { - crtc = fb_helper->crtc_info[i].mode_set.crtc; + drm_client_for_each_modeset(modeset, &fb_helper->client) { + crtc = modeset->crtc; if (!crtc->funcs->gamma_set || !crtc->gamma_size) return -EINVAL; @@ -1467,10 +997,11 @@ static int setcmap_atomic(struct fb_cmap *cmap, struct fb_info *info) struct drm_modeset_acquire_ctx ctx; struct drm_crtc_state *crtc_state; struct drm_atomic_state *state; + struct drm_mode_set *modeset; struct drm_crtc *crtc; u16 *r, *g, *b; - int i, ret = 0; bool replaced; + int ret = 0; drm_modeset_acquire_init(&ctx, 0); @@ -1482,8 +1013,8 @@ static int setcmap_atomic(struct fb_cmap *cmap, struct fb_info *info) state->acquire_ctx = &ctx; retry: - for (i = 0; i < fb_helper->crtc_count; i++) { - crtc = fb_helper->crtc_info[i].mode_set.crtc; + drm_client_for_each_modeset(modeset, &fb_helper->client) { + crtc = modeset->crtc; if (!gamma_lut) gamma_lut = setcmap_new_gamma_lut(crtc, cmap); @@ -1511,8 +1042,8 @@ retry: if (ret) goto out_state; - for (i = 0; i < fb_helper->crtc_count; i++) { - crtc = fb_helper->crtc_info[i].mode_set.crtc; + drm_client_for_each_modeset(modeset, &fb_helper->client) { + crtc = modeset->crtc; r = crtc->gamma_store; g = r + crtc->gamma_size; @@ -1549,6 +1080,7 @@ backoff: int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par; + struct drm_device *dev = fb_helper->dev; int ret; if (oops_in_progress) @@ -1556,19 +1088,22 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) mutex_lock(&fb_helper->lock); - if (!drm_fb_helper_is_bound(fb_helper)) { + if (!drm_master_internal_acquire(dev)) { ret = -EBUSY; - goto out; + goto unlock; } + mutex_lock(&fb_helper->client.modeset_mutex); if (info->fix.visual == FB_VISUAL_TRUECOLOR) ret = setcmap_pseudo_palette(cmap, info); else if (drm_drv_uses_atomic_modeset(fb_helper->dev)) ret = setcmap_atomic(cmap, info); else ret = setcmap_legacy(cmap, info); + mutex_unlock(&fb_helper->client.modeset_mutex); -out: + drm_master_internal_release(dev); +unlock: mutex_unlock(&fb_helper->lock); return ret; @@ -1588,12 +1123,12 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { struct drm_fb_helper *fb_helper = info->par; - struct drm_mode_set *mode_set; + struct drm_device *dev = fb_helper->dev; struct drm_crtc *crtc; int ret = 0; mutex_lock(&fb_helper->lock); - if (!drm_fb_helper_is_bound(fb_helper)) { + if (!drm_master_internal_acquire(dev)) { ret = -EBUSY; goto unlock; } @@ -1616,8 +1151,7 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd, * make. If we're not smart enough here, one should * just consider switch the userspace to KMS. */ - mode_set = &fb_helper->crtc_info[0].mode_set; - crtc = mode_set->crtc; + crtc = fb_helper->client.modesets[0].crtc; /* * Only wait for a vblank event if the CRTC is @@ -1631,11 +1165,12 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd, } ret = 0; - goto unlock; + break; default: ret = -ENOTTY; } + drm_master_internal_release(dev); unlock: mutex_unlock(&fb_helper->lock); return ret; @@ -1729,15 +1264,20 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, { struct drm_fb_helper *fb_helper = info->par; struct drm_framebuffer *fb = fb_helper->fb; + struct drm_device *dev = fb_helper->dev; if (in_dbg_master()) return -EINVAL; if (var->pixclock != 0) { - DRM_DEBUG("fbdev emulation doesn't support changing the pixel clock, value of pixclock is ignored\n"); + drm_dbg_kms(dev, "fbdev emulation doesn't support changing the pixel clock, value of pixclock is ignored\n"); var->pixclock = 0; } + if ((drm_format_info_block_width(fb->format, 0) > 1) || + (drm_format_info_block_height(fb->format, 0) > 1)) + return -EINVAL; + /* * Changes struct fb_var_screeninfo are currently not pushed back * to KMS, hence fail if different settings are requested. @@ -1745,7 +1285,7 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, if (var->bits_per_pixel > fb->format->cpp[0] * 8 || var->xres > fb->width || var->yres > fb->height || var->xres_virtual > fb->width || var->yres_virtual > fb->height) { - DRM_DEBUG("fb requested width/height/bpp can't fit in current fb " + drm_dbg_kms(dev, "fb requested width/height/bpp can't fit in current fb " "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n", var->xres, var->yres, var->bits_per_pixel, var->xres_virtual, var->yres_virtual, @@ -1777,14 +1317,15 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, * so reject all pixel format changing requests. */ if (!drm_fb_pixel_format_equal(var, &info->var)) { - DRM_DEBUG("fbdev emulation doesn't support changing the pixel format\n"); + drm_dbg_kms(dev, "fbdev emulation doesn't support changing the pixel format\n"); return -EINVAL; } return 0; } EXPORT_SYMBOL(drm_fb_helper_check_var); -#endif + +#endif /* __linux__ */ /** * drm_fb_helper_set_par - implementation for &fb_ops.fb_set_par @@ -1803,7 +1344,7 @@ int drm_fb_helper_set_par(struct fb_info *info) return -EBUSY; if (var->pixclock != 0) { - DRM_ERROR("PIXEL CLOCK SET\n"); + drm_err(fb_helper->dev, "PIXEL CLOCK SET\n"); return -EINVAL; } @@ -1813,30 +1354,32 @@ int drm_fb_helper_set_par(struct fb_info *info) } EXPORT_SYMBOL(drm_fb_helper_set_par); -#ifdef __linux__ +#ifdef notyet static void pan_set(struct drm_fb_helper *fb_helper, int x, int y) { - int i; - - for (i = 0; i < fb_helper->crtc_count; i++) { - struct drm_mode_set *mode_set; - - mode_set = &fb_helper->crtc_info[i].mode_set; + struct drm_mode_set *mode_set; + mutex_lock(&fb_helper->client.modeset_mutex); + drm_client_for_each_modeset(mode_set, &fb_helper->client) { mode_set->x = x; mode_set->y = y; } + mutex_unlock(&fb_helper->client.modeset_mutex); } +#endif static int pan_display_atomic(struct fb_var_screeninfo *var, struct fb_info *info) { + STUB(); + return -ENOSYS; +#ifdef notyet struct drm_fb_helper *fb_helper = info->par; int ret; pan_set(fb_helper, var->xoffset, var->yoffset); - ret = restore_fbdev_mode_atomic(fb_helper, true); + ret = drm_client_modeset_commit_locked(&fb_helper->client); if (!ret) { info->var.xoffset = var->xoffset; info->var.yoffset = var->yoffset; @@ -1844,20 +1387,23 @@ static int pan_display_atomic(struct fb_var_screeninfo *var, pan_set(fb_helper, info->var.xoffset, info->var.yoffset); return ret; +#endif } static int pan_display_legacy(struct fb_var_screeninfo *var, struct fb_info *info) { + STUB(); + return -ENOSYS; +#ifdef notyet struct drm_fb_helper *fb_helper = info->par; + struct drm_client_dev *client = &fb_helper->client; struct drm_mode_set *modeset; int ret = 0; - int i; + mutex_lock(&client->modeset_mutex); drm_modeset_lock_all(fb_helper->dev); - for (i = 0; i < fb_helper->crtc_count; i++) { - modeset = &fb_helper->crtc_info[i].mode_set; - + drm_client_for_each_modeset(modeset, client) { modeset->x = var->xoffset; modeset->y = var->yoffset; @@ -1870,8 +1416,10 @@ static int pan_display_legacy(struct fb_var_screeninfo *var, } } drm_modeset_unlock_all(fb_helper->dev); + mutex_unlock(&client->modeset_mutex); return ret; +#endif } /** @@ -1890,23 +1438,24 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, return -EBUSY; mutex_lock(&fb_helper->lock); - if (!drm_fb_helper_is_bound(fb_helper)) { - mutex_unlock(&fb_helper->lock); - return -EBUSY; + if (!drm_master_internal_acquire(dev)) { + ret = -EBUSY; + goto unlock; } if (drm_drv_uses_atomic_modeset(dev)) ret = pan_display_atomic(var, info); else ret = pan_display_legacy(var, info); + + drm_master_internal_release(dev); +unlock: mutex_unlock(&fb_helper->lock); return ret; } EXPORT_SYMBOL(drm_fb_helper_pan_display); -#endif - /* * Allocates the backing storage and sets up the fbdev info structure through * the ->fb_probe callback. @@ -1914,11 +1463,15 @@ EXPORT_SYMBOL(drm_fb_helper_pan_display); static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, int preferred_bpp) { + struct drm_client_dev *client = &fb_helper->client; + struct drm_device *dev = fb_helper->dev; int ret = 0; int crtc_count = 0; - int i; + struct drm_connector_list_iter conn_iter; struct drm_fb_helper_surface_size sizes; - int gamma_size = 0; + struct drm_connector *connector; + struct drm_mode_set *mode_set; + int best_depth = 0; memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size)); sizes.surface_depth = 24; @@ -1926,16 +1479,18 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, sizes.fb_width = (u32)-1; sizes.fb_height = (u32)-1; - /* if driver picks 8 or 16 by default use that for both depth/bpp */ + /* + * If driver picks 8 or 16 by default use that for both depth/bpp + * to begin with + */ if (preferred_bpp != sizes.surface_bpp) sizes.surface_depth = sizes.surface_bpp = preferred_bpp; - /* first up get a count of crtcs now in use and new min/maxes width/heights */ - drm_fb_helper_for_each_connector(fb_helper, i) { - struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i]; + drm_connector_list_iter_begin(fb_helper->dev, &conn_iter); + drm_client_for_each_connector_iter(connector, &conn_iter) { struct drm_cmdline_mode *cmdline_mode; - cmdline_mode = &fb_helper_conn->connector->cmdline_mode; + cmdline_mode = &connector->cmdline_mode; if (cmdline_mode->bpp_specified) { switch (cmdline_mode->bpp) { @@ -1960,11 +1515,61 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, break; } } + drm_connector_list_iter_end(&conn_iter); + + /* + * If we run into a situation where, for example, the primary plane + * supports RGBA5551 (16 bpp, depth 15) but not RGB565 (16 bpp, depth + * 16) we need to scale down the depth of the sizes we request. + */ + mutex_lock(&client->modeset_mutex); + drm_client_for_each_modeset(mode_set, client) { + struct drm_crtc *crtc = mode_set->crtc; + struct drm_plane *plane = crtc->primary; + int j; + + drm_dbg_kms(dev, "test CRTC %u primary plane\n", drm_crtc_index(crtc)); + + for (j = 0; j < plane->format_count; j++) { + const struct drm_format_info *fmt; + + fmt = drm_format_info(plane->format_types[j]); + + /* + * Do not consider YUV or other complicated formats + * for framebuffers. This means only legacy formats + * are supported (fmt->depth is a legacy field) but + * the framebuffer emulation can only deal with such + * formats, specifically RGB/BGA formats. + */ + if (fmt->depth == 0) + continue; + + /* We found a perfect fit, great */ + if (fmt->depth == sizes.surface_depth) { + best_depth = fmt->depth; + break; + } + + /* Skip depths above what we're looking for */ + if (fmt->depth > sizes.surface_depth) + continue; + + /* Best depth found so far */ + if (fmt->depth > best_depth) + best_depth = fmt->depth; + } + } + if (sizes.surface_depth != best_depth && best_depth) { + drm_info(dev, "requested bpp %d, scaled depth down to %d", + sizes.surface_bpp, best_depth); + sizes.surface_depth = best_depth; + } + /* first up get a count of crtcs now in use and new min/maxes width/heights */ crtc_count = 0; - for (i = 0; i < fb_helper->crtc_count; i++) { + drm_client_for_each_modeset(mode_set, client) { struct drm_display_mode *desired_mode; - struct drm_mode_set *mode_set; int x, y, j; /* in case of tile group, are we the last tile vert or horiz? * If no tile group you are always the last one both vertically @@ -1972,19 +1577,15 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, */ bool lastv = true, lasth = true; - desired_mode = fb_helper->crtc_info[i].desired_mode; - mode_set = &fb_helper->crtc_info[i].mode_set; + desired_mode = mode_set->mode; if (!desired_mode) continue; crtc_count++; - x = fb_helper->crtc_info[i].x; - y = fb_helper->crtc_info[i].y; - - if (gamma_size == 0) - gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size; + x = mode_set->x; + y = mode_set->y; sizes.surface_width = max_t(u32, desired_mode->hdisplay + x, sizes.surface_width); sizes.surface_height = max_t(u32, desired_mode->vdisplay + y, sizes.surface_height); @@ -1992,7 +1593,9 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, for (j = 0; j < mode_set->num_connectors; j++) { struct drm_connector *connector = mode_set->connectors[j]; - if (connector->has_tile) { + if (connector->has_tile && + desired_mode->hdisplay == connector->tile_h_size && + desired_mode->vdisplay == connector->tile_v_size) { lasth = (connector->tile_h_loc == (connector->num_h_tile - 1)); lastv = (connector->tile_v_loc == (connector->num_v_tile - 1)); /* cloning to multiple tiles is just crazy-talk, so: */ @@ -2005,22 +1608,18 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, if (lastv) sizes.fb_height = min_t(u32, desired_mode->vdisplay + y, sizes.fb_height); } + mutex_unlock(&client->modeset_mutex); if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) { #ifdef __linux__ - DRM_INFO("Cannot find any crtc or sizes\n"); + drm_info(dev, "Cannot find any crtc or sizes\n"); /* First time: disable all crtc's.. */ - /* XXX calling this hangs boot with no connected outputs */ - if (!fb_helper->deferred_setup /* && SPLAY_EMPTY(fb_helper->dev->files) */) - restore_fbdev_mode(fb_helper); + if (!fb_helper->deferred_setup) + drm_client_modeset_commit(client); return -EAGAIN; #else - /* - * hmm everyone went away - assume VGA cable just fell out - * and will come back later. - */ - DRM_INFO("Cannot find any crtc or sizes - going 1024x768\n"); + drm_info(dev, "Cannot find any crtc or sizes - going 1024x768\n"); sizes.fb_width = sizes.surface_width = 1024; sizes.fb_height = sizes.surface_height = 768; #endif @@ -2043,21 +1642,8 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, #ifdef __linux__ -/** - * drm_fb_helper_fill_fix - initializes fixed fbdev information - * @info: fbdev registered by the helper - * @pitch: desired pitch - * @depth: desired depth - * - * Helper to fill in the fixed fbdev information useful for a non-accelerated - * fbdev emulations. Drivers which support acceleration methods which impose - * additional constraints need to set up their own limits. - * - * Drivers should call this (or their equivalent setup code) from their - * &drm_fb_helper_funcs.fb_probe callback. - */ -void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, - uint32_t depth) +static void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, + uint32_t depth) { info->fix.type = FB_TYPE_PACKED_PIXELS; info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR : @@ -2072,27 +1658,15 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, info->fix.line_length = pitch; } -EXPORT_SYMBOL(drm_fb_helper_fill_fix); -/** - * drm_fb_helper_fill_var - initalizes variable fbdev information - * @info: fbdev instance to set up - * @fb_helper: fb helper instance to use as template - * @fb_width: desired fb width - * @fb_height: desired fb height - * - * Sets up the variable fbdev metainformation from the given fb helper instance - * and the drm framebuffer allocated in &drm_fb_helper.fb. - * - * Drivers should call this (or their equivalent setup code) from their - * &drm_fb_helper_funcs.fb_probe callback after having allocated the fbdev - * backing storage framebuffer. - */ -void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper, - uint32_t fb_width, uint32_t fb_height) +static void drm_fb_helper_fill_var(struct fb_info *info, + struct drm_fb_helper *fb_helper, + uint32_t fb_width, uint32_t fb_height) { struct drm_framebuffer *fb = fb_helper->fb; + WARN_ON((drm_format_info_block_width(fb->format, 0) > 1) || + (drm_format_info_block_height(fb->format, 0) > 1)); info->pseudo_palette = fb_helper->pseudo_palette; info->var.xres_virtual = fb->width; info->var.yres_virtual = fb->height; @@ -2107,557 +1681,42 @@ void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helpe info->var.xres = fb_width; info->var.yres = fb_height; } -EXPORT_SYMBOL(drm_fb_helper_fill_var); -#endif /* __linux__ */ - -static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper, - uint32_t maxX, - uint32_t maxY) -{ - struct drm_connector *connector; - int i, count = 0; - - drm_fb_helper_for_each_connector(fb_helper, i) { - connector = fb_helper->connector_info[i]->connector; - count += connector->funcs->fill_modes(connector, maxX, maxY); - } - - return count; -} - -struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height) -{ - struct drm_display_mode *mode; - - list_for_each_entry(mode, &fb_connector->connector->modes, head) { - if (mode->hdisplay > width || - mode->vdisplay > height) - continue; - if (mode->type & DRM_MODE_TYPE_PREFERRED) - return mode; - } - return NULL; -} -EXPORT_SYMBOL(drm_has_preferred_mode); - -static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector) -{ - return fb_connector->connector->cmdline_mode.specified; -} - -struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn) -{ - struct drm_cmdline_mode *cmdline_mode; - struct drm_display_mode *mode; - bool prefer_non_interlace; - - cmdline_mode = &fb_helper_conn->connector->cmdline_mode; - if (cmdline_mode->specified == false) - return NULL; - - /* attempt to find a matching mode in the list of modes - * we have gotten so far, if not add a CVT mode that conforms - */ - if (cmdline_mode->rb || cmdline_mode->margins) - goto create_mode; - - prefer_non_interlace = !cmdline_mode->interlace; -again: - list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { - /* check width/height */ - if (mode->hdisplay != cmdline_mode->xres || - mode->vdisplay != cmdline_mode->yres) - continue; - - if (cmdline_mode->refresh_specified) { - if (mode->vrefresh != cmdline_mode->refresh) - continue; - } - - if (cmdline_mode->interlace) { - if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) - continue; - } else if (prefer_non_interlace) { - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - continue; - } - return mode; - } - - if (prefer_non_interlace) { - prefer_non_interlace = false; - goto again; - } - -create_mode: - mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev, - cmdline_mode); - list_add(&mode->head, &fb_helper_conn->connector->modes); - return mode; -} -EXPORT_SYMBOL(drm_pick_cmdline_mode); - -static bool drm_connector_enabled(struct drm_connector *connector, bool strict) -{ - bool enable; - - if (connector->display_info.non_desktop) - return false; - - if (strict) - enable = connector->status == connector_status_connected; - else - enable = connector->status != connector_status_disconnected; - - return enable; -} - -static void drm_enable_connectors(struct drm_fb_helper *fb_helper, - bool *enabled) -{ - bool any_enabled = false; - struct drm_connector *connector; - int i = 0; - - drm_fb_helper_for_each_connector(fb_helper, i) { - connector = fb_helper->connector_info[i]->connector; - enabled[i] = drm_connector_enabled(connector, true); - DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, - connector->display_info.non_desktop ? "non desktop" : enabled[i] ? "yes" : "no"); - - any_enabled |= enabled[i]; - } - - if (any_enabled) - return; - - drm_fb_helper_for_each_connector(fb_helper, i) { - connector = fb_helper->connector_info[i]->connector; - enabled[i] = drm_connector_enabled(connector, false); - } -} - -static bool drm_target_cloned(struct drm_fb_helper *fb_helper, - struct drm_display_mode **modes, - struct drm_fb_offset *offsets, - bool *enabled, int width, int height) -{ - int count, i, j; - bool can_clone = false; - struct drm_fb_helper_connector *fb_helper_conn; - struct drm_display_mode *dmt_mode, *mode; - - /* only contemplate cloning in the single crtc case */ - if (fb_helper->crtc_count > 1) - return false; - - count = 0; - drm_fb_helper_for_each_connector(fb_helper, i) { - if (enabled[i]) - count++; - } - - /* only contemplate cloning if more than one connector is enabled */ - if (count <= 1) - return false; - - /* check the command line or if nothing common pick 1024x768 */ - can_clone = true; - drm_fb_helper_for_each_connector(fb_helper, i) { - if (!enabled[i]) - continue; - fb_helper_conn = fb_helper->connector_info[i]; - modes[i] = drm_pick_cmdline_mode(fb_helper_conn); - if (!modes[i]) { - can_clone = false; - break; - } - for (j = 0; j < i; j++) { - if (!enabled[j]) - continue; - if (!drm_mode_match(modes[j], modes[i], - DRM_MODE_MATCH_TIMINGS | - DRM_MODE_MATCH_CLOCK | - DRM_MODE_MATCH_FLAGS | - DRM_MODE_MATCH_3D_FLAGS)) - can_clone = false; - } - } - - if (can_clone) { - DRM_DEBUG_KMS("can clone using command line\n"); - return true; - } - - /* try and find a 1024x768 mode on each connector */ - can_clone = true; - dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false); - - drm_fb_helper_for_each_connector(fb_helper, i) { - if (!enabled[i]) - continue; - - fb_helper_conn = fb_helper->connector_info[i]; - list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { - if (drm_mode_match(mode, dmt_mode, - DRM_MODE_MATCH_TIMINGS | - DRM_MODE_MATCH_CLOCK | - DRM_MODE_MATCH_FLAGS | - DRM_MODE_MATCH_3D_FLAGS)) - modes[i] = mode; - } - if (!modes[i]) - can_clone = false; - } - - if (can_clone) { - DRM_DEBUG_KMS("can clone using 1024x768\n"); - return true; - } - DRM_INFO("kms: can't enable cloning when we probably wanted to.\n"); - return false; -} - -static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper, - struct drm_display_mode **modes, - struct drm_fb_offset *offsets, - int idx, - int h_idx, int v_idx) -{ - struct drm_fb_helper_connector *fb_helper_conn; - int i; - int hoffset = 0, voffset = 0; - - drm_fb_helper_for_each_connector(fb_helper, i) { - fb_helper_conn = fb_helper->connector_info[i]; - if (!fb_helper_conn->connector->has_tile) - continue; - - if (!modes[i] && (h_idx || v_idx)) { - DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i, - fb_helper_conn->connector->base.id); - continue; - } - if (fb_helper_conn->connector->tile_h_loc < h_idx) - hoffset += modes[i]->hdisplay; - - if (fb_helper_conn->connector->tile_v_loc < v_idx) - voffset += modes[i]->vdisplay; - } - offsets[idx].x = hoffset; - offsets[idx].y = voffset; - DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx); - return 0; -} - -static bool drm_target_preferred(struct drm_fb_helper *fb_helper, - struct drm_display_mode **modes, - struct drm_fb_offset *offsets, - bool *enabled, int width, int height) -{ - struct drm_fb_helper_connector *fb_helper_conn; - const u64 mask = BIT_ULL(fb_helper->connector_count) - 1; - u64 conn_configured = 0; - int tile_pass = 0; - int i; - -retry: - drm_fb_helper_for_each_connector(fb_helper, i) { - fb_helper_conn = fb_helper->connector_info[i]; - - if (conn_configured & BIT_ULL(i)) - continue; - - if (enabled[i] == false) { - conn_configured |= BIT_ULL(i); - continue; - } - - /* first pass over all the untiled connectors */ - if (tile_pass == 0 && fb_helper_conn->connector->has_tile) - continue; - - if (tile_pass == 1) { - if (fb_helper_conn->connector->tile_h_loc != 0 || - fb_helper_conn->connector->tile_v_loc != 0) - continue; - - } else { - if (fb_helper_conn->connector->tile_h_loc != tile_pass - 1 && - fb_helper_conn->connector->tile_v_loc != tile_pass - 1) - /* if this tile_pass doesn't cover any of the tiles - keep going */ - continue; - - /* - * find the tile offsets for this pass - need to find - * all tiles left and above - */ - drm_get_tile_offsets(fb_helper, modes, offsets, - i, fb_helper_conn->connector->tile_h_loc, fb_helper_conn->connector->tile_v_loc); - } - DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", - fb_helper_conn->connector->base.id); - - /* got for command line mode first */ - modes[i] = drm_pick_cmdline_mode(fb_helper_conn); - if (!modes[i]) { - DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n", - fb_helper_conn->connector->base.id, fb_helper_conn->connector->tile_group ? fb_helper_conn->connector->tile_group->id : 0); - modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height); - } - /* No preferred modes, pick one off the list */ - if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) { - list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head) - break; - } - DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : - "none"); - conn_configured |= BIT_ULL(i); - } - - if ((conn_configured & mask) != mask) { - tile_pass++; - goto retry; - } - return true; -} - -static bool connector_has_possible_crtc(struct drm_connector *connector, - struct drm_crtc *crtc) -{ - struct drm_encoder *encoder; - int i; - - drm_connector_for_each_possible_encoder(connector, encoder, i) { - if (encoder->possible_crtcs & drm_crtc_mask(crtc)) - return true; - } - - return false; -} - -static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, - struct drm_fb_helper_crtc **best_crtcs, - struct drm_display_mode **modes, - int n, int width, int height) -{ - int c, o; - struct drm_connector *connector; - int my_score, best_score, score; - struct drm_fb_helper_crtc **crtcs, *crtc; - struct drm_fb_helper_connector *fb_helper_conn; - - if (n == fb_helper->connector_count) - return 0; - - fb_helper_conn = fb_helper->connector_info[n]; - connector = fb_helper_conn->connector; - - best_crtcs[n] = NULL; - best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height); - if (modes[n] == NULL) - return best_score; - - crtcs = kcalloc(fb_helper->connector_count, - sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL); - if (!crtcs) - return best_score; - - my_score = 1; - if (connector->status == connector_status_connected) - my_score++; - if (drm_has_cmdline_mode(fb_helper_conn)) - my_score++; - if (drm_has_preferred_mode(fb_helper_conn, width, height)) - my_score++; - - /* - * select a crtc for this connector and then attempt to configure - * remaining connectors - */ - for (c = 0; c < fb_helper->crtc_count; c++) { - crtc = &fb_helper->crtc_info[c]; - - if (!connector_has_possible_crtc(connector, - crtc->mode_set.crtc)) - continue; - - for (o = 0; o < n; o++) - if (best_crtcs[o] == crtc) - break; - - if (o < n) { - /* ignore cloning unless only a single crtc */ - if (fb_helper->crtc_count > 1) - continue; - - if (!drm_mode_equal(modes[o], modes[n])) - continue; - } - - crtcs[n] = crtc; - memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *)); - score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1, - width, height); - if (score > best_score) { - best_score = score; - memcpy(best_crtcs, crtcs, - fb_helper->connector_count * - sizeof(struct drm_fb_helper_crtc *)); - } - } - kfree(crtcs); - return best_score; -} +#endif /* __linux__ */ -/* - * This function checks if rotation is necessary because of panel orientation - * and if it is, if it is supported. - * If rotation is necessary and supported, its gets set in fb_crtc.rotation. - * If rotation is necessary but not supported, a DRM_MODE_ROTATE_* flag gets - * or-ed into fb_helper->sw_rotations. In drm_setup_crtcs_fb() we check if only - * one bit is set and then we set fb_info.fbcon_rotate_hint to make fbcon do - * the unsupported rotation. +/** + * drm_fb_helper_fill_info - initializes fbdev information + * @info: fbdev instance to set up + * @fb_helper: fb helper instance to use as template + * @sizes: describes fbdev size and scanout surface size + * + * Sets up the variable and fixed fbdev metainformation from the given fb helper + * instance and the drm framebuffer allocated in &drm_fb_helper.fb. + * + * Drivers should call this (or their equivalent setup code) from their + * &drm_fb_helper_funcs.fb_probe callback after having allocated the fbdev + * backing storage framebuffer. */ -static void drm_setup_crtc_rotation(struct drm_fb_helper *fb_helper, - struct drm_fb_helper_crtc *fb_crtc, - struct drm_connector *connector) +void drm_fb_helper_fill_info(struct fb_info *info, + struct drm_fb_helper *fb_helper, + struct drm_fb_helper_surface_size *sizes) { - struct drm_plane *plane = fb_crtc->mode_set.crtc->primary; - uint64_t valid_mask = 0; - int i, rotation; - - fb_crtc->rotation = DRM_MODE_ROTATE_0; - - switch (connector->display_info.panel_orientation) { - case DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP: - rotation = DRM_MODE_ROTATE_180; - break; - case DRM_MODE_PANEL_ORIENTATION_LEFT_UP: - rotation = DRM_MODE_ROTATE_90; - break; - case DRM_MODE_PANEL_ORIENTATION_RIGHT_UP: - rotation = DRM_MODE_ROTATE_270; - break; - default: - rotation = DRM_MODE_ROTATE_0; - } - - /* - * TODO: support 90 / 270 degree hardware rotation, - * depending on the hardware this may require the framebuffer - * to be in a specific tiling format. - */ - if (rotation != DRM_MODE_ROTATE_180 || !plane->rotation_property) { - fb_helper->sw_rotations |= rotation; - return; - } - - for (i = 0; i < plane->rotation_property->num_values; i++) - valid_mask |= (1ULL << plane->rotation_property->values[i]); - - if (!(rotation & valid_mask)) { - fb_helper->sw_rotations |= rotation; - return; - } - - fb_crtc->rotation = rotation; - /* Rotating in hardware, fbcon should not rotate */ - fb_helper->sw_rotations |= DRM_MODE_ROTATE_0; -} +#ifdef __linux__ + struct drm_framebuffer *fb = fb_helper->fb; -static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, - u32 width, u32 height) -{ - struct drm_device *dev = fb_helper->dev; - struct drm_fb_helper_crtc **crtcs; - struct drm_display_mode **modes; - struct drm_fb_offset *offsets; - bool *enabled; - int i; + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); + drm_fb_helper_fill_var(info, fb_helper, + sizes->fb_width, sizes->fb_height); +#endif - DRM_DEBUG_KMS("\n"); - /* prevent concurrent modification of connector_count by hotplug */ - lockdep_assert_held(&fb_helper->lock); - - crtcs = kcalloc(fb_helper->connector_count, - sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL); - modes = kcalloc(fb_helper->connector_count, - sizeof(struct drm_display_mode *), GFP_KERNEL); - offsets = kcalloc(fb_helper->connector_count, - sizeof(struct drm_fb_offset), GFP_KERNEL); - enabled = kcalloc(fb_helper->connector_count, - sizeof(bool), GFP_KERNEL); - if (!crtcs || !modes || !enabled || !offsets) { - DRM_ERROR("Memory allocation failed\n"); - goto out; - } + info->par = fb_helper; +#ifdef __linux__ + snprintf(info->fix.id, sizeof(info->fix.id), "%sdrmfb", + fb_helper->dev->driver->name); +#endif - mutex_lock(&fb_helper->dev->mode_config.mutex); - if (drm_fb_helper_probe_connector_modes(fb_helper, width, height) == 0) - DRM_DEBUG_KMS("No connectors reported connected with modes\n"); - drm_enable_connectors(fb_helper, enabled); - - if (!(fb_helper->funcs->initial_config && - fb_helper->funcs->initial_config(fb_helper, crtcs, modes, - offsets, - enabled, width, height))) { - memset(modes, 0, fb_helper->connector_count*sizeof(modes[0])); - memset(crtcs, 0, fb_helper->connector_count*sizeof(crtcs[0])); - memset(offsets, 0, fb_helper->connector_count*sizeof(offsets[0])); - - if (!drm_target_cloned(fb_helper, modes, offsets, - enabled, width, height) && - !drm_target_preferred(fb_helper, modes, offsets, - enabled, width, height)) - DRM_ERROR("Unable to find initial modes\n"); - - DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", - width, height); - - drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height); - } - mutex_unlock(&fb_helper->dev->mode_config.mutex); - - /* need to set the modesets up here for use later */ - /* fill out the connector<->crtc mappings into the modesets */ - for (i = 0; i < fb_helper->crtc_count; i++) - drm_fb_helper_modeset_release(fb_helper, - &fb_helper->crtc_info[i].mode_set); - - fb_helper->sw_rotations = 0; - drm_fb_helper_for_each_connector(fb_helper, i) { - struct drm_display_mode *mode = modes[i]; - struct drm_fb_helper_crtc *fb_crtc = crtcs[i]; - struct drm_fb_offset *offset = &offsets[i]; - - if (mode && fb_crtc) { - struct drm_mode_set *modeset = &fb_crtc->mode_set; - struct drm_connector *connector = - fb_helper->connector_info[i]->connector; - - DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n", - mode->name, fb_crtc->mode_set.crtc->base.id, offset->x, offset->y); - - fb_crtc->desired_mode = mode; - fb_crtc->x = offset->x; - fb_crtc->y = offset->y; - modeset->mode = drm_mode_duplicate(dev, - fb_crtc->desired_mode); - drm_connector_get(connector); - drm_setup_crtc_rotation(fb_helper, fb_crtc, connector); - modeset->connectors[modeset->num_connectors++] = connector; - modeset->x = offset->x; - modeset->y = offset->y; - } - } -out: - kfree(crtcs); - kfree(modes); - kfree(offsets); - kfree(enabled); } +EXPORT_SYMBOL(drm_fb_helper_fill_info); /* * This is a continuation of drm_setup_crtcs() that sets up anything related @@ -2668,17 +1727,30 @@ out: */ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper) { + struct drm_client_dev *client = &fb_helper->client; + struct drm_connector_list_iter conn_iter; struct fb_info *info = fb_helper->fbdev; - int i; + unsigned int rotation, sw_rotations = 0; + struct drm_connector *connector; + struct drm_mode_set *modeset; - for (i = 0; i < fb_helper->crtc_count; i++) - if (fb_helper->crtc_info[i].mode_set.num_connectors) - fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; + mutex_lock(&client->modeset_mutex); + drm_client_for_each_modeset(modeset, client) { + if (!modeset->num_connectors) + continue; + + modeset->fb = fb_helper->fb; - mutex_lock(&fb_helper->dev->mode_config.mutex); - drm_fb_helper_for_each_connector(fb_helper, i) { - struct drm_connector *connector = - fb_helper->connector_info[i]->connector; + if (drm_client_rotation(modeset, &rotation)) + /* Rotating in hardware, fbcon should not rotate */ + sw_rotations |= DRM_MODE_ROTATE_0; + else + sw_rotations |= rotation; + } + mutex_unlock(&client->modeset_mutex); + + drm_connector_list_iter_begin(fb_helper->dev, &conn_iter); + drm_client_for_each_connector_iter(connector, &conn_iter) { /* use first connected connector for the physical dimensions */ if (connector->status == connector_status_connected) { @@ -2687,9 +1759,9 @@ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper) break; } } - mutex_unlock(&fb_helper->dev->mode_config.mutex); + drm_connector_list_iter_end(&conn_iter); - switch (fb_helper->sw_rotations) { + switch (sw_rotations) { case DRM_MODE_ROTATE_0: info->fbcon_rotate_hint = FB_ROTATE_UR; break; @@ -2725,7 +1797,7 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper, width = dev->mode_config.max_width; height = dev->mode_config.max_height; - drm_setup_crtcs(fb_helper, width, height); + drm_client_modeset_probe(&fb_helper->client, width, height); ret = drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); if (ret < 0) { if (ret == -EAGAIN) { @@ -2743,17 +1815,23 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper, info = fb_helper->fbdev; info->var.pixclock = 0; + /* Shamelessly allow physical address leaking to userspace */ +#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM) + if (!drm_leak_fbdev_smem) +#endif + /* don't leak any physical addresses to userspace */ + info->flags |= FBINFO_HIDE_SMEM_START; /* Need to drop locks to avoid recursive deadlock in * register_framebuffer. This is ok because the only thing left to do is * register the fbdev emulation instance in kernel_fb_helper_list. */ mutex_unlock(&fb_helper->lock); -#ifdef __linux__ ret = register_framebuffer(info); if (ret < 0) return ret; +#ifdef __linux__ dev_info(dev->dev, "fb%d: %s frame buffer device\n", info->node, info->fix.id); #endif @@ -2782,9 +1860,8 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper, * * This function will call down into the &drm_fb_helper_funcs.fb_probe callback * to let the driver allocate and initialize the fbdev info structure and the - * drm framebuffer used to back the fbdev. drm_fb_helper_fill_var() and - * drm_fb_helper_fill_fix() are provided as helpers to setup simple default - * values for the fbdev info structure. + * drm framebuffer used to back the fbdev. drm_fb_helper_fill_info() is provided + * as a helper to setup simple default values for the fbdev info structure. * * HANG DEBUGGING: * @@ -2859,15 +1936,17 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) return err; } - if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) { + if (!fb_helper->fb || !drm_master_internal_acquire(fb_helper->dev)) { fb_helper->delayed_hotplug = true; mutex_unlock(&fb_helper->lock); return err; } - DRM_DEBUG_KMS("\n"); + drm_master_internal_release(fb_helper->dev); + + drm_dbg_kms(fb_helper->dev, "\n"); - drm_setup_crtcs(fb_helper, fb_helper->fb->width, fb_helper->fb->height); + drm_client_modeset_probe(&fb_helper->client, fb_helper->fb->width, fb_helper->fb->height); drm_setup_crtcs_fb(fb_helper); mutex_unlock(&fb_helper->lock); @@ -2877,122 +1956,6 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) } EXPORT_SYMBOL(drm_fb_helper_hotplug_event); -#ifdef __linux__ -/** - * drm_fb_helper_fbdev_setup() - Setup fbdev emulation - * @dev: DRM device - * @fb_helper: fbdev helper structure to set up - * @funcs: fbdev helper functions - * @preferred_bpp: Preferred bits per pixel for the device. - * @dev->mode_config.preferred_depth is used if this is zero. - * @max_conn_count: Maximum number of connectors. - * @dev->mode_config.num_connector is used if this is zero. - * - * This function sets up fbdev emulation and registers fbdev for access by - * userspace. If all connectors are disconnected, setup is deferred to the next - * time drm_fb_helper_hotplug_event() is called. - * The caller must to provide a &drm_fb_helper_funcs->fb_probe callback - * function. - * - * See also: drm_fb_helper_initial_config() - * - * Returns: - * Zero on success or negative error code on failure. - */ -int drm_fb_helper_fbdev_setup(struct drm_device *dev, - struct drm_fb_helper *fb_helper, - const struct drm_fb_helper_funcs *funcs, - unsigned int preferred_bpp, - unsigned int max_conn_count) -{ - int ret; - - if (!preferred_bpp) - preferred_bpp = dev->mode_config.preferred_depth; - if (!preferred_bpp) - preferred_bpp = 32; - - if (!max_conn_count) - max_conn_count = dev->mode_config.num_connector; - if (!max_conn_count) { - DRM_DEV_ERROR(dev->dev, "No connectors\n"); - return -EINVAL; - } - - drm_fb_helper_prepare(dev, fb_helper, funcs); - - ret = drm_fb_helper_init(dev, fb_helper, max_conn_count); - if (ret < 0) { - DRM_DEV_ERROR(dev->dev, "Failed to initialize fbdev helper\n"); - return ret; - } - - ret = drm_fb_helper_single_add_all_connectors(fb_helper); - if (ret < 0) { - DRM_DEV_ERROR(dev->dev, "Failed to add connectors\n"); - goto err_drm_fb_helper_fini; - } - - if (!drm_drv_uses_atomic_modeset(dev)) - drm_helper_disable_unused_functions(dev); - - ret = drm_fb_helper_initial_config(fb_helper, preferred_bpp); - if (ret < 0) { - DRM_DEV_ERROR(dev->dev, "Failed to set fbdev configuration\n"); - goto err_drm_fb_helper_fini; - } - - return 0; - -err_drm_fb_helper_fini: - drm_fb_helper_fbdev_teardown(dev); - - return ret; -} -EXPORT_SYMBOL(drm_fb_helper_fbdev_setup); - -/** - * drm_fb_helper_fbdev_teardown - Tear down fbdev emulation - * @dev: DRM device - * - * This function unregisters fbdev if not already done and cleans up the - * associated resources including the &drm_framebuffer. - * The driver is responsible for freeing the &drm_fb_helper structure which is - * stored in &drm_device->fb_helper. Do note that this pointer has been cleared - * when this function returns. - * - * In order to support device removal/unplug while file handles are still open, - * drm_fb_helper_unregister_fbi() should be called on device removal and - * drm_fb_helper_fbdev_teardown() in the &drm_driver->release callback when - * file handles are closed. - */ -void drm_fb_helper_fbdev_teardown(struct drm_device *dev) -{ - struct drm_fb_helper *fb_helper = dev->fb_helper; - struct fb_ops *fbops = NULL; - - if (!fb_helper) - return; - - /* Unregister if it hasn't been done already */ - if (fb_helper->fbdev && fb_helper->fbdev->dev) - drm_fb_helper_unregister_fbi(fb_helper); - - if (fb_helper->fbdev && fb_helper->fbdev->fbdefio) { - fb_deferred_io_cleanup(fb_helper->fbdev); - kfree(fb_helper->fbdev->fbdefio); - fbops = fb_helper->fbdev->fbops; - } - - drm_fb_helper_fini(fb_helper); - kfree(fbops); - - if (fb_helper->fb) - drm_framebuffer_remove(fb_helper->fb); -} -EXPORT_SYMBOL(drm_fb_helper_fbdev_teardown); -#endif - /** * drm_fb_helper_lastclose - DRM driver lastclose helper for fbdev emulation * @dev: DRM device @@ -3047,7 +2010,6 @@ static int drm_fbdev_fb_release(struct fb_info *info, int user) static void drm_fbdev_cleanup(struct drm_fb_helper *fb_helper) { struct fb_info *fbi = fb_helper->fbdev; - struct fb_ops *fbops = NULL; void *shadow = NULL; if (!fb_helper->dev) @@ -3056,15 +2018,11 @@ static void drm_fbdev_cleanup(struct drm_fb_helper *fb_helper) if (fbi && fbi->fbdefio) { fb_deferred_io_cleanup(fbi); shadow = fbi->screen_buffer; - fbops = fbi->fbops; } drm_fb_helper_fini(fb_helper); - if (shadow) { - vfree(shadow); - kfree(fbops); - } + vfree(shadow); drm_client_framebuffer_delete(fb_helper->buffer); } @@ -3072,16 +2030,8 @@ static void drm_fbdev_cleanup(struct drm_fb_helper *fb_helper) static void drm_fbdev_release(struct drm_fb_helper *fb_helper) { drm_fbdev_cleanup(fb_helper); - - /* - * FIXME: - * Remove conditional when all CMA drivers have been moved over to using - * drm_fbdev_generic_setup(). - */ - if (fb_helper->client.funcs) { - drm_client_release(&fb_helper->client); - kfree(fb_helper); - } + drm_client_release(&fb_helper->client); + kfree(fb_helper); } /* @@ -3103,7 +2053,7 @@ static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) return -ENODEV; } -static struct fb_ops drm_fbdev_fb_ops = { +static const struct fb_ops drm_fbdev_fb_ops = { .owner = THIS_MODULE, DRM_FB_HELPER_DEFAULT_OPS, .fb_open = drm_fbdev_fb_open, @@ -3122,31 +2072,26 @@ static struct fb_deferred_io drm_fbdev_defio = { .deferred_io = drm_fb_helper_deferred_io, }; -/** - * drm_fb_helper_generic_probe - Generic fbdev emulation probe helper - * @fb_helper: fbdev helper structure - * @sizes: describes fbdev size and scanout surface size - * - * This function uses the client API to crate a framebuffer backed by a dumb buffer. +/* + * This function uses the client API to create a framebuffer backed by a dumb buffer. * * The _sys_ versions are used for &fb_ops.fb_read, fb_write, fb_fillrect, * fb_copyarea, fb_imageblit. - * - * Returns: - * Zero on success or negative error code on failure. */ -int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, - struct drm_fb_helper_surface_size *sizes) +static int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, + struct drm_fb_helper_surface_size *sizes) { struct drm_client_dev *client = &fb_helper->client; + struct drm_device *dev = fb_helper->dev; struct drm_client_buffer *buffer; struct drm_framebuffer *fb; struct fb_info *fbi; u32 format; + void *vaddr; - DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n", - sizes->surface_width, sizes->surface_height, - sizes->surface_bpp); + drm_dbg_kms(dev, "surface width(%d), height(%d) and bpp(%d)\n", + sizes->surface_width, sizes->surface_height, + sizes->surface_bpp); format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); buffer = drm_client_framebuffer_create(client, sizes->surface_width, @@ -3162,49 +2107,37 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, if (IS_ERR(fbi)) return PTR_ERR(fbi); - fbi->par = fb_helper; fbi->fbops = &drm_fbdev_fb_ops; fbi->screen_size = fb->height * fb->pitches[0]; fbi->fix.smem_len = fbi->screen_size; - fbi->screen_buffer = buffer->vaddr; - /* Shamelessly leak the physical address to user-space */ -#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM) - if (drm_leak_fbdev_smem && fbi->fix.smem_start == 0) - fbi->fix.smem_start = - page_to_phys(virt_to_page(fbi->screen_buffer)); -#endif - strcpy(fbi->fix.id, "DRM emulated"); - drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth); - drm_fb_helper_fill_var(fbi, fb_helper, sizes->fb_width, sizes->fb_height); + drm_fb_helper_fill_info(fbi, fb_helper, sizes); - if (fb->funcs->dirty) { - struct fb_ops *fbops; - void *shadow; - - /* - * fb_deferred_io_cleanup() clears &fbops->fb_mmap so a per - * instance version is necessary. - */ - fbops = kzalloc(sizeof(*fbops), GFP_KERNEL); - shadow = vzalloc(fbi->screen_size); - if (!fbops || !shadow) { - kfree(fbops); - vfree(shadow); + if (drm_fbdev_use_shadow_fb(fb_helper)) { + fbi->screen_buffer = vzalloc(fbi->screen_size); + if (!fbi->screen_buffer) return -ENOMEM; - } - *fbops = *fbi->fbops; - fbi->fbops = fbops; - fbi->screen_buffer = shadow; fbi->fbdefio = &drm_fbdev_defio; fb_deferred_io_init(fbi); + } else { + /* buffer is mapped for HW framebuffer */ + vaddr = drm_client_buffer_vmap(fb_helper->buffer); + if (IS_ERR(vaddr)) + return PTR_ERR(vaddr); + + fbi->screen_buffer = vaddr; + /* Shamelessly leak the physical address to user-space */ +#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM) + if (drm_leak_fbdev_smem && fbi->fix.smem_start == 0) + fbi->fix.smem_start = + page_to_phys(virt_to_page(fbi->screen_buffer)); +#endif } return 0; } -EXPORT_SYMBOL(drm_fb_helper_generic_probe); static const struct drm_fb_helper_funcs drm_fb_helper_generic_funcs = { .fb_probe = drm_fb_helper_generic_probe, @@ -3241,19 +2174,17 @@ static int drm_fbdev_client_hotplug(struct drm_client_dev *client) if (dev->fb_helper) return drm_fb_helper_hotplug_event(dev->fb_helper); - if (!dev->mode_config.num_connector) + if (!dev->mode_config.num_connector) { + drm_dbg_kms(dev, "No connectors found, will not create framebuffer!\n"); return 0; + } drm_fb_helper_prepare(dev, fb_helper, &drm_fb_helper_generic_funcs); - ret = drm_fb_helper_init(dev, fb_helper, dev->mode_config.num_connector); + ret = drm_fb_helper_init(dev, fb_helper); if (ret) goto err; - ret = drm_fb_helper_single_add_all_connectors(fb_helper); - if (ret) - goto err_cleanup; - if (!drm_drv_uses_atomic_modeset(dev)) drm_helper_disable_unused_functions(dev); @@ -3269,7 +2200,7 @@ err: fb_helper->dev = NULL; fb_helper->fbdev = NULL; - DRM_DEV_ERROR(dev->dev, "fbdev: Failed to setup generic emulation (ret=%d)\n", ret); + drm_err(dev, "fbdev: Failed to setup generic emulation (ret=%d)\n", ret); return ret; } @@ -3282,7 +2213,7 @@ static const struct drm_client_funcs drm_fbdev_client_funcs = { }; /** - * drm_fb_helper_generic_fbdev_setup() - Setup generic fbdev emulation + * drm_fbdev_generic_setup() - Setup generic fbdev emulation * @dev: DRM device * @preferred_bpp: Preferred bits per pixel for the device. * @dev->mode_config.preferred_depth is used if this is zero. @@ -3296,11 +2227,16 @@ static const struct drm_client_funcs drm_fbdev_client_funcs = { * * Drivers that set the dirty callback on their framebuffer will get a shadow * fbdev buffer that is blitted onto the real buffer. This is done in order to - * make deferred I/O work with all kinds of buffers. + * make deferred I/O work with all kinds of buffers. A shadow buffer can be + * requested explicitly by setting struct drm_mode_config.prefer_shadow or + * struct drm_mode_config.prefer_shadow_fbdev to true beforehand. This is + * required to use generic fbdev emulation with SHMEM helpers. * * This function is safe to call even when there are no connectors present. * Setup will be retried on the next hotplug event. * + * The fbdev is destroyed by drm_dev_unregister(). + * * Returns: * Zero on success or negative error code on failure. */ @@ -3309,6 +2245,8 @@ int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp) struct drm_fb_helper *fb_helper; int ret; + WARN(dev->fb_helper, "fb_helper is already set!\n"); + if (!drm_fbdev_emulation) return 0; @@ -3319,6 +2257,7 @@ int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp) ret = drm_client_init(dev, &fb_helper->client, "fbdev", &drm_fbdev_client_funcs); if (ret) { kfree(fb_helper); + drm_err(dev, "Failed to register client: %d\n", ret); return ret; } @@ -3328,14 +2267,17 @@ int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp) preferred_bpp = 32; fb_helper->preferred_bpp = preferred_bpp; - drm_fbdev_client_hotplug(&fb_helper->client); + ret = drm_fbdev_client_hotplug(&fb_helper->client); + if (ret) + drm_dbg_kms(dev, "client hotplug ret=%d\n", ret); - drm_client_add(&fb_helper->client); + drm_client_register(&fb_helper->client); return 0; } EXPORT_SYMBOL(drm_fbdev_generic_setup); -#endif + +#endif /* __linux__ */ /* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT) * but the module doesn't depend on any fb console symbols. At least |