X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=drivers%2Fgpu%2Fdrm%2Fdrm_fb_helper.c;h=a0ea3241c65117ad056a67c8d3be17af317d80c5;hb=42f4ac66c5352d3b84aa5119b3419750ec57e008;hp=324a688b3f3013e9020ce2b9f0f76083ad91f890;hpb=53b4d5911d6adef3f543e1ad23643068c5c343ac;p=mirror_ubuntu-bionic-kernel.git diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 324a688b3f30..a0ea3241c651 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -48,6 +48,12 @@ module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600); MODULE_PARM_DESC(fbdev_emulation, "Enable legacy fbdev emulation [default=true]"); +static int drm_fbdev_overalloc = CONFIG_DRM_FBDEV_OVERALLOC; +module_param(drm_fbdev_overalloc, int, 0444); +MODULE_PARM_DESC(drm_fbdev_overalloc, + "Overallocation of the fbdev buffer (%) [default=" + __MODULE_STRING(CONFIG_DRM_FBDEV_OVERALLOC) "]"); + static LIST_HEAD(kernel_fb_helper_list); static DEFINE_MUTEX(kernel_fb_helper_lock); @@ -63,7 +69,8 @@ static DEFINE_MUTEX(kernel_fb_helper_lock); * drm_fb_helper_init(), drm_fb_helper_single_add_all_connectors() and * drm_fb_helper_initial_config(). Drivers with fancier requirements than the * default behaviour can override the third step with their own code. - * Teardown is done with drm_fb_helper_fini(). + * Teardown is done with drm_fb_helper_fini() after the fbdev device is + * unregisters using drm_fb_helper_unregister_fbi(). * * At runtime drivers should restore the fbdev console by calling * drm_fb_helper_restore_fbdev_mode_unlocked() from their &drm_driver.lastclose @@ -102,6 +109,42 @@ static DEFINE_MUTEX(kernel_fb_helper_lock); for (({ lockdep_assert_held(&(fbh)->dev->mode_config.mutex); }), \ i__ = 0; i__ < (fbh)->connector_count; i__++) +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; + + WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex)); + + count = fb_helper->connector_count + 1; + + if (count > fb_helper->connector_info_alloc_count) { + size_t size = count * sizeof(fb_conn); + + temp = krealloc(fb_helper->connector_info, size, GFP_KERNEL); + if (!temp) + return -ENOMEM; + + 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; +} +EXPORT_SYMBOL(drm_fb_helper_add_one_connector); + /** * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev * emulation helper @@ -127,7 +170,7 @@ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) return 0; mutex_lock(&dev->mode_config.mutex); - drm_connector_list_iter_get(dev, &conn_iter); + drm_connector_list_iter_begin(dev, &conn_iter); drm_for_each_connector_iter(connector, &conn_iter) { ret = drm_fb_helper_add_one_connector(fb_helper, connector); @@ -141,50 +184,20 @@ fail: struct drm_fb_helper_connector *fb_helper_connector = fb_helper->connector_info[i]; - drm_connector_unreference(fb_helper_connector->connector); + 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_put(&conn_iter); + drm_connector_list_iter_end(&conn_iter); mutex_unlock(&dev->mode_config.mutex); return ret; } EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors); -int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector) -{ - struct drm_fb_helper_connector **temp; - struct drm_fb_helper_connector *fb_helper_connector; - - if (!drm_fbdev_emulation) - return 0; - - WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex)); - if (fb_helper->connector_count + 1 > fb_helper->connector_info_alloc_count) { - temp = krealloc(fb_helper->connector_info, sizeof(struct drm_fb_helper_connector *) * (fb_helper->connector_count + 1), GFP_KERNEL); - if (!temp) - return -ENOMEM; - - fb_helper->connector_info_alloc_count = fb_helper->connector_count + 1; - fb_helper->connector_info = temp; - } - - - fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL); - if (!fb_helper_connector) - return -ENOMEM; - - drm_connector_reference(connector); - fb_helper_connector->connector = connector; - fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector; - return 0; -} -EXPORT_SYMBOL(drm_fb_helper_add_one_connector); - int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector) { @@ -204,11 +217,11 @@ int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, if (i == fb_helper->connector_count) return -EINVAL; fb_helper_connector = fb_helper->connector_info[i]; - drm_connector_unreference(fb_helper_connector->connector); + drm_connector_put(fb_helper_connector->connector); - for (j = i + 1; j < fb_helper->connector_count; j++) { + 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); @@ -243,7 +256,8 @@ static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) g_base = r_base + crtc->gamma_size; b_base = g_base + crtc->gamma_size; - crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, crtc->gamma_size); + crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, + crtc->gamma_size, NULL); } /** @@ -268,6 +282,9 @@ int drm_fb_helper_debug_enter(struct fb_info *info) if (funcs->mode_set_base_atomic == NULL) continue; + if (drm_drv_uses_atomic_modeset(mode_set->crtc->dev)) + continue; + drm_fb_helper_save_lut_atomic(mode_set->crtc, helper); funcs->mode_set_base_atomic(mode_set->crtc, mode_set->fb, @@ -309,6 +326,7 @@ int drm_fb_helper_debug_leave(struct fb_info *info) for (i = 0; i < helper->crtc_count; i++) { struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set; + crtc = mode_set->crtc; funcs = crtc->helper_private; fb = drm_mode_config_fb(crtc); @@ -324,6 +342,9 @@ int drm_fb_helper_debug_leave(struct fb_info *info) if (funcs->mode_set_base_atomic == NULL) continue; + if (drm_drv_uses_atomic_modeset(crtc->dev)) + continue; + drm_fb_helper_restore_lut_atomic(mode_set->crtc); funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x, crtc->y, LEAVE_ATOMIC_MODE_SET); @@ -339,7 +360,7 @@ static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper) struct drm_plane *plane; struct drm_atomic_state *state; int i, ret; - unsigned plane_mask; + unsigned int plane_mask; state = drm_atomic_state_alloc(dev); if (!state) @@ -371,7 +392,7 @@ retry: goto fail; } - for(i = 0; i < fb_helper->crtc_count; i++) { + for (i = 0; i < fb_helper->crtc_count; i++) { struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; ret = __drm_atomic_helper_set_config(mode_set, state); @@ -397,17 +418,12 @@ backoff: goto retry; } -static int restore_fbdev_mode(struct drm_fb_helper *fb_helper) +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; - drm_warn_on_modeset_not_all_locked(dev); - - if (drm_drv_uses_atomic_modeset(dev)) - return restore_fbdev_mode_atomic(fb_helper); - drm_for_each_plane(plane, dev) { if (plane->type != DRM_PLANE_TYPE_PRIMARY) drm_plane_force_disable(plane); @@ -441,6 +457,18 @@ static int restore_fbdev_mode(struct drm_fb_helper *fb_helper) return 0; } +static int restore_fbdev_mode(struct drm_fb_helper *fb_helper) +{ + struct drm_device *dev = fb_helper->dev; + + drm_warn_on_modeset_not_all_locked(dev); + + if (drm_drv_uses_atomic_modeset(dev)) + return restore_fbdev_mode_atomic(fb_helper); + else + return restore_fbdev_mode_legacy(fb_helper); +} + /** * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration * @fb_helper: fbcon to restore @@ -481,8 +509,10 @@ static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) 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. */ + /* + * Sometimes user space wants everything disabled, so don't steal the + * display if there's a master. + */ if (READ_ONCE(dev->master)) return false; @@ -530,6 +560,7 @@ static bool drm_fb_helper_force_kernel_mode(void) static void drm_fb_helper_restore_work_fn(struct work_struct *ignored) { bool ret; + ret = drm_fb_helper_force_kernel_mode(); if (ret == true) DRM_ERROR("Failed to restore crtc configuration\n"); @@ -626,7 +657,7 @@ static void drm_fb_helper_modeset_release(struct drm_fb_helper *helper, int i; for (i = 0; i < modeset->num_connectors; i++) { - drm_connector_unreference(modeset->connectors[i]); + drm_connector_put(modeset->connectors[i]); modeset->connectors[i] = NULL; } modeset->num_connectors = 0; @@ -643,7 +674,7 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) int i; for (i = 0; i < helper->connector_count; i++) { - drm_connector_unreference(helper->connector_info[i]->connector); + drm_connector_put(helper->connector_info[i]->connector); kfree(helper->connector_info[i]); } kfree(helper->connector_info); @@ -709,7 +740,7 @@ void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper, EXPORT_SYMBOL(drm_fb_helper_prepare); /** - * drm_fb_helper_init - initialize a drm_fb_helper structure + * 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 @@ -780,7 +811,9 @@ EXPORT_SYMBOL(drm_fb_helper_init); * @fb_helper: driver-allocated fbdev helper * * A helper to alloc fb_info and the members cmap and apertures. Called - * by the driver within the fb_probe fb_helper callback function. + * by the driver within the fb_probe fb_helper callback function. Drivers do not + * need to release the allocated fb_info structure themselves, this is + * automatically done when calling drm_fb_helper_fini(). * * RETURNS: * fb_info pointer if things went okay, pointer containing error code @@ -823,7 +856,8 @@ EXPORT_SYMBOL(drm_fb_helper_alloc_fbi); * @fb_helper: driver-allocated fbdev helper * * A wrapper around unregister_framebuffer, to release the fb_info - * framebuffer device + * framebuffer device. This must be called before releasing all resources for + * @fb_helper by calling drm_fb_helper_fini(). */ void drm_fb_helper_unregister_fbi(struct drm_fb_helper *fb_helper) { @@ -833,32 +867,26 @@ void drm_fb_helper_unregister_fbi(struct drm_fb_helper *fb_helper) EXPORT_SYMBOL(drm_fb_helper_unregister_fbi); /** - * drm_fb_helper_release_fbi - dealloc fb_info and its members + * drm_fb_helper_fini - finialize a &struct drm_fb_helper * @fb_helper: driver-allocated fbdev helper * - * A helper to free memory taken by fb_info and the members cmap and - * apertures + * This cleans up all remaining resources associated with @fb_helper. Must be + * called after drm_fb_helper_unlink_fbi() was called. */ -void drm_fb_helper_release_fbi(struct drm_fb_helper *fb_helper) +void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) { - if (fb_helper) { - struct fb_info *info = fb_helper->fbdev; + struct fb_info *info; - if (info) { - if (info->cmap.len) - fb_dealloc_cmap(&info->cmap); - framebuffer_release(info); - } + if (!drm_fbdev_emulation || !fb_helper) + return; - fb_helper->fbdev = NULL; + info = fb_helper->fbdev; + if (info) { + if (info->cmap.len) + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); } -} -EXPORT_SYMBOL(drm_fb_helper_release_fbi); - -void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) -{ - if (!drm_fbdev_emulation) - return; + fb_helper->fbdev = NULL; cancel_work_sync(&fb_helper->resume_work); cancel_work_sync(&fb_helper->dirty_work); @@ -866,9 +894,8 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) mutex_lock(&kernel_fb_helper_lock); if (!list_empty(&fb_helper->kernel_fb_list)) { list_del(&fb_helper->kernel_fb_list); - if (list_empty(&kernel_fb_helper_list)) { + if (list_empty(&kernel_fb_helper_list)) unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); - } } mutex_unlock(&kernel_fb_helper_lock); @@ -1161,6 +1188,7 @@ static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, (blue << info->var.blue.offset); if (info->var.transp.length > 0) { u32 mask = (1 << info->var.transp.length) - 1; + mask <<= info->var.transp.offset; value |= mask; } @@ -1240,6 +1268,74 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) } EXPORT_SYMBOL(drm_fb_helper_setcmap); +/** + * drm_fb_helper_ioctl - legacy ioctl implementation + * @info: fbdev registered by the helper + * @cmd: ioctl command + * @arg: ioctl argument + * + * A helper to implement the standard fbdev ioctl. Only + * FBIO_WAITFORVSYNC is implemented for now. + */ +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_device *dev = fb_helper->dev; + struct drm_mode_set *mode_set; + struct drm_crtc *crtc; + int ret = 0; + + mutex_lock(&dev->mode_config.mutex); + if (!drm_fb_helper_is_bound(fb_helper)) { + ret = -EBUSY; + goto unlock; + } + + switch (cmd) { + case FBIO_WAITFORVSYNC: + /* + * Only consider the first CRTC. + * + * This ioctl is supposed to take the CRTC number as + * an argument, but in fbdev times, what that number + * was supposed to be was quite unclear, different + * drivers were passing that argument differently + * (some by reference, some by value), and most of the + * userspace applications were just hardcoding 0 as an + * argument. + * + * The first CRTC should be the integrated panel on + * most drivers, so this is the best choice we can + * 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; + + /* + * Only wait for a vblank event if the CRTC is + * enabled, otherwise just don't do anythintg, + * not even report an error. + */ + ret = drm_crtc_vblank_get(crtc); + if (!ret) { + drm_crtc_wait_one_vblank(crtc); + drm_crtc_vblank_put(crtc); + } + + ret = 0; + goto unlock; + default: + ret = -ENOTTY; + } + +unlock: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} +EXPORT_SYMBOL(drm_fb_helper_ioctl); + /** * drm_fb_helper_check_var - implementation for &fb_ops.fb_check_var * @var: screeninfo to check @@ -1375,7 +1471,7 @@ static int pan_display_atomic(struct fb_var_screeninfo *var, struct drm_atomic_state *state; struct drm_plane *plane; int i, ret; - unsigned plane_mask; + unsigned int plane_mask; state = drm_atomic_state_alloc(dev); if (!state) @@ -1384,7 +1480,7 @@ static int pan_display_atomic(struct fb_var_screeninfo *var, state->acquire_ctx = dev->mode_config.acquire_ctx; retry: plane_mask = 0; - for(i = 0; i < fb_helper->crtc_count; 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; @@ -1424,34 +1520,14 @@ backoff: goto retry; } -/** - * drm_fb_helper_pan_display - implementation for &fb_ops.fb_pan_display - * @var: updated screen information - * @info: fbdev registered by the helper - */ -int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, +static int pan_display_legacy(struct fb_var_screeninfo *var, struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par; - struct drm_device *dev = fb_helper->dev; struct drm_mode_set *modeset; int ret = 0; int i; - if (oops_in_progress) - return -EBUSY; - - drm_modeset_lock_all(dev); - if (!drm_fb_helper_is_bound(fb_helper)) { - drm_modeset_unlock_all(dev); - return -EBUSY; - } - - if (drm_drv_uses_atomic_modeset(dev)) { - ret = pan_display_atomic(var, info); - goto unlock; - } - for (i = 0; i < fb_helper->crtc_count; i++) { modeset = &fb_helper->crtc_info[i].mode_set; @@ -1466,8 +1542,37 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, } } } -unlock: + + return ret; +} + +/** + * drm_fb_helper_pan_display - implementation for &fb_ops.fb_pan_display + * @var: updated screen information + * @info: fbdev registered by the helper + */ +int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, + 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) + return -EBUSY; + + drm_modeset_lock_all(dev); + if (!drm_fb_helper_is_bound(fb_helper)) { + drm_modeset_unlock_all(dev); + return -EBUSY; + } + + if (drm_drv_uses_atomic_modeset(dev)) + ret = pan_display_atomic(var, info); + else + ret = pan_display_legacy(var, info); drm_modeset_unlock_all(dev); + return ret; } EXPORT_SYMBOL(drm_fb_helper_pan_display); @@ -1489,11 +1594,10 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size)); sizes.surface_depth = 24; sizes.surface_bpp = 32; - sizes.fb_width = (unsigned)-1; - sizes.fb_height = (unsigned)-1; + 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 */ if (preferred_bpp != sizes.surface_bpp) sizes.surface_depth = sizes.surface_bpp = preferred_bpp; @@ -1558,6 +1662,7 @@ 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) { lasth = (connector->tile_h_loc == (connector->num_h_tile - 1)); lastv = (connector->tile_v_loc == (connector->num_v_tile - 1)); @@ -1573,13 +1678,19 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, } if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) { - /* hmm everyone went away - assume VGA cable just fell out - and will come back later. */ + /* + * 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"); sizes.fb_width = sizes.surface_width = 1024; sizes.fb_height = sizes.surface_height = 768; } + /* Handle our overallocation */ + sizes.surface_height *= drm_fbdev_overalloc; + sizes.surface_height /= 100; + /* push down into drivers */ ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes); if (ret < 0) @@ -1627,7 +1738,6 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, info->fix.accel = FB_ACCEL_NONE; info->fix.line_length = pitch; - return; } EXPORT_SYMBOL(drm_fb_helper_fill_fix); @@ -1649,6 +1759,7 @@ void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helpe uint32_t fb_width, uint32_t fb_height) { struct drm_framebuffer *fb = fb_helper->fb; + info->pseudo_palette = fb_helper->pseudo_palette; info->var.xres_virtual = fb->width; info->var.yres_virtual = fb->height; @@ -1981,13 +2092,15 @@ retry: continue; } else { - if (fb_helper_conn->connector->tile_h_loc != tile_pass -1 && + 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 */ + /* + * 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); } @@ -2071,8 +2184,10 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, if (!encoder) goto out; - /* select a crtc for this connector and then attempt to configure - remaining connectors */ + /* + * 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]; @@ -2184,7 +2299,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, fb_crtc->y = offset->y; modeset->mode = drm_mode_duplicate(dev, fb_crtc->desired_mode); - drm_connector_reference(connector); + drm_connector_get(connector); modeset->connectors[modeset->num_connectors++] = connector; modeset->fb = fb_helper->fb; modeset->x = offset->x;