diff options
| author | Dmitry Torokhov <[email protected]> | 2023-08-30 16:06:38 -0700 | 
|---|---|---|
| committer | Dmitry Torokhov <[email protected]> | 2023-08-30 16:06:38 -0700 | 
| commit | 1ac731c529cd4d6adbce134754b51ff7d822b145 (patch) | |
| tree | 143ab3f35ca5f3b69f583c84e6964b17139c2ec1 /drivers/gpu/drm/drm_fb_helper.c | |
| parent | 07b4c950f27bef0362dc6ad7ee713aab61d58149 (diff) | |
| parent | 54116d442e001e1b6bd482122043b1870998a1f3 (diff) | |
Merge branch 'next' into for-linus
Prepare input updates for 6.6 merge window.
Diffstat (limited to 'drivers/gpu/drm/drm_fb_helper.c')
| -rw-r--r-- | drivers/gpu/drm/drm_fb_helper.c | 134 | 
1 files changed, 91 insertions, 43 deletions
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index a39998047f8a..fd27f1978635 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -60,16 +60,17 @@ MODULE_PARM_DESC(drm_fbdev_overalloc,   * In order to keep user-space compatibility, we want in certain use-cases   * to keep leaking the fbdev physical address to the user-space program   * handling the fbdev buffer. - * This is a bad habit essentially kept into closed source opengl driver - * that should really be moved into open-source upstream projects instead - * of using legacy physical addresses in user space to communicate with - * other out-of-tree kernel modules. + * + * This is a bad habit, essentially kept to support closed-source OpenGL + * drivers that should really be moved into open-source upstream projects + * instead of using legacy physical addresses in user space to communicate + * with other out-of-tree kernel modules.   *   * This module_param *should* be removed as soon as possible and be   * considered as a broken and legacy behaviour from a modern fbdev device.   */ -#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM)  static bool drm_leak_fbdev_smem; +#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM)  module_param_unsafe(drm_leak_fbdev_smem, bool, 0600);  MODULE_PARM_DESC(drm_leak_fbdev_smem,  		 "Allow unsafe leaking fbdev physical smem address [default=false]"); @@ -539,6 +540,29 @@ err_release:  EXPORT_SYMBOL(drm_fb_helper_alloc_info);  /** + * drm_fb_helper_release_info - release fb_info and its members + * @fb_helper: driver-allocated fbdev helper + * + * A helper to release fb_info and the member cmap.  Drivers do not + * need to release the allocated fb_info structure themselves, this is + * automatically done when calling drm_fb_helper_fini(). + */ +void drm_fb_helper_release_info(struct drm_fb_helper *fb_helper) +{ +	struct fb_info *info = fb_helper->info; + +	if (!info) +		return; + +	fb_helper->info = NULL; + +	if (info->cmap.len) +		fb_dealloc_cmap(&info->cmap); +	framebuffer_release(info); +} +EXPORT_SYMBOL(drm_fb_helper_release_info); + +/**   * drm_fb_helper_unregister_info - unregister fb_info framebuffer device   * @fb_helper: driver-allocated fbdev helper, can be NULL   * @@ -561,8 +585,6 @@ EXPORT_SYMBOL(drm_fb_helper_unregister_info);   */  void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)  { -	struct fb_info *info; -  	if (!fb_helper)  		return; @@ -574,13 +596,7 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)  	cancel_work_sync(&fb_helper->resume_work);  	cancel_work_sync(&fb_helper->damage_work); -	info = fb_helper->info; -	if (info) { -		if (info->cmap.len) -			fb_dealloc_cmap(&info->cmap); -		framebuffer_release(info); -	} -	fb_helper->info = NULL; +	drm_fb_helper_release_info(fb_helper);  	mutex_lock(&kernel_fb_helper_lock);  	if (!list_empty(&fb_helper->kernel_fb_list)) { @@ -625,19 +641,27 @@ static void drm_fb_helper_damage(struct drm_fb_helper *helper, u32 x, u32 y,  static void drm_fb_helper_memory_range_to_clip(struct fb_info *info, off_t off, size_t len,  					       struct drm_rect *clip)  { +	u32 line_length = info->fix.line_length; +	u32 fb_height = info->var.yres;  	off_t end = off + len;  	u32 x1 = 0; -	u32 y1 = off / info->fix.line_length; +	u32 y1 = off / line_length;  	u32 x2 = info->var.xres; -	u32 y2 = DIV_ROUND_UP(end, info->fix.line_length); +	u32 y2 = DIV_ROUND_UP(end, line_length); + +	/* Don't allow any of them beyond the bottom bound of display area */ +	if (y1 > fb_height) +		y1 = fb_height; +	if (y2 > fb_height) +		y2 = fb_height;  	if ((y2 - y1) == 1) {  		/*  		 * We've only written to a single scanline. Try to reduce  		 * the number of horizontal pixels that need an update.  		 */ -		off_t bit_off = (off % info->fix.line_length) * 8; -		off_t bit_end = (end % info->fix.line_length) * 8; +		off_t bit_off = (off % line_length) * 8; +		off_t bit_end = (end % line_length) * 8;  		x1 = bit_off / info->var.bits_per_pixel;  		x2 = DIV_ROUND_UP(bit_end, info->var.bits_per_pixel); @@ -657,7 +681,7 @@ static void drm_fb_helper_memory_range_to_clip(struct fb_info *info, off_t off,  void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagereflist)  {  	struct drm_fb_helper *helper = info->par; -	unsigned long start, end, min_off, max_off; +	unsigned long start, end, min_off, max_off, total_size;  	struct fb_deferred_io_pageref *pageref;  	struct drm_rect damage_area; @@ -675,7 +699,11 @@ void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagerefli  	 * of the screen and account for non-existing scanlines. Hence,  	 * keep the covered memory area within the screen buffer.  	 */ -	max_off = min(max_off, info->screen_size); +	if (info->screen_size) +		total_size = info->screen_size; +	else +		total_size = info->fix.smem_len; +	max_off = min(max_off, total_size);  	if (min_off < max_off) {  		drm_fb_helper_memory_range_to_clip(info, min_off, max_off - min_off, &damage_area); @@ -1517,6 +1545,29 @@ static void drm_fb_helper_fill_pixel_fmt(struct fb_var_screeninfo *var,  	}  } +static void __fill_var(struct fb_var_screeninfo *var, struct fb_info *info, +		       struct drm_framebuffer *fb) +{ +	int i; + +	var->xres_virtual = fb->width; +	var->yres_virtual = fb->height; +	var->accel_flags = 0; +	var->bits_per_pixel = drm_format_info_bpp(fb->format, 0); + +	var->height = info->var.height; +	var->width = info->var.width; + +	var->left_margin = var->right_margin = 0; +	var->upper_margin = var->lower_margin = 0; +	var->hsync_len = var->vsync_len = 0; +	var->sync = var->vmode = 0; +	var->rotate = 0; +	var->colorspace = 0; +	for (i = 0; i < 4; i++) +		var->reserved[i] = 0; +} +  /**   * drm_fb_helper_check_var - implementation for &fb_ops.fb_check_var   * @var: screeninfo to check @@ -1569,6 +1620,23 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var,  		return -EINVAL;  	} +	__fill_var(var, info, fb); + +	/* +	 * fb_pan_display() validates this, but fb_set_par() doesn't and just +	 * falls over. Note that __fill_var above adjusts y/res_virtual. +	 */ +	if (var->yoffset > var->yres_virtual - var->yres || +	    var->xoffset > var->xres_virtual - var->xres) +		return -EINVAL; + +	/* We neither support grayscale nor FOURCC (also stored in here). */ +	if (var->grayscale > 0) +		return -EINVAL; + +	if (var->nonstd) +		return -EINVAL; +  	/*  	 * Workaround for SDL 1.2, which is known to be setting all pixel format  	 * fields values to zero in some cases. We treat this situation as a @@ -1584,11 +1652,6 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var,  	}  	/* -	 * Likewise, bits_per_pixel should be rounded up to a supported value. -	 */ -	var->bits_per_pixel = bpp; - -	/*  	 * drm fbdev emulation doesn't support changing the pixel format at all,  	 * so reject all pixel format changing requests.  	 */ @@ -1618,11 +1681,6 @@ int drm_fb_helper_set_par(struct fb_info *info)  	if (oops_in_progress)  		return -EBUSY; -	if (var->pixclock != 0) { -		drm_err(fb_helper->dev, "PIXEL CLOCK SET\n"); -		return -EINVAL; -	} -  	/*  	 * Normally we want to make sure that a kms master takes precedence over  	 * fbdev, to avoid fbdev flickering and occasionally stealing the @@ -1964,10 +2022,6 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper)  		return ret;  	} -#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM) -	fb_helper->hint_leak_smem_start = drm_leak_fbdev_smem; -#endif -  	/* push down into drivers */  	ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);  	if (ret < 0) @@ -2020,12 +2074,9 @@ static void drm_fb_helper_fill_var(struct fb_info *info,  	}  	info->pseudo_palette = fb_helper->pseudo_palette; -	info->var.xres_virtual = fb->width; -	info->var.yres_virtual = fb->height; -	info->var.bits_per_pixel = drm_format_info_bpp(format, 0); -	info->var.accel_flags = FB_ACCELF_TEXT;  	info->var.xoffset = 0;  	info->var.yoffset = 0; +	__fill_var(&info->var, info, fb);  	info->var.activate = FB_ACTIVATE_NOW;  	drm_fb_helper_fill_pixel_fmt(&info->var, format); @@ -2166,11 +2217,8 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper)  	info = fb_helper->info;  	info->var.pixclock = 0; -	/* Shamelessly allow physical address leaking to userspace */ -#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM) -	if (!fb_helper->hint_leak_smem_start) -#endif -		/* don't leak any physical addresses to userspace */ + +	if (!drm_leak_fbdev_smem)  		info->flags |= FBINFO_HIDE_SMEM_START;  	/* Need to drop locks to avoid recursive deadlock in  |