18c2ecf20Sopenharmony_ci/**************************************************************************
28c2ecf20Sopenharmony_ci *
38c2ecf20Sopenharmony_ci * Copyright © 2007 David Airlie
48c2ecf20Sopenharmony_ci * Copyright © 2009-2015 VMware, Inc., Palo Alto, CA., USA
58c2ecf20Sopenharmony_ci * All Rights Reserved.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
88c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the
98c2ecf20Sopenharmony_ci * "Software"), to deal in the Software without restriction, including
108c2ecf20Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish,
118c2ecf20Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to
128c2ecf20Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to
138c2ecf20Sopenharmony_ci * the following conditions:
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the
168c2ecf20Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions
178c2ecf20Sopenharmony_ci * of the Software.
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
208c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
218c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
228c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
238c2ecf20Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
248c2ecf20Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
258c2ecf20Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE.
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci **************************************************************************/
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include <linux/pci.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h>
328c2ecf20Sopenharmony_ci#include <drm/ttm/ttm_placement.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include "vmwgfx_drv.h"
358c2ecf20Sopenharmony_ci#include "vmwgfx_kms.h"
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define VMW_DIRTY_DELAY (HZ / 30)
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistruct vmw_fb_par {
408c2ecf20Sopenharmony_ci	struct vmw_private *vmw_priv;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	void *vmalloc;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	struct mutex bo_mutex;
458c2ecf20Sopenharmony_ci	struct vmw_buffer_object *vmw_bo;
468c2ecf20Sopenharmony_ci	unsigned bo_size;
478c2ecf20Sopenharmony_ci	struct drm_framebuffer *set_fb;
488c2ecf20Sopenharmony_ci	struct drm_display_mode *set_mode;
498c2ecf20Sopenharmony_ci	u32 fb_x;
508c2ecf20Sopenharmony_ci	u32 fb_y;
518c2ecf20Sopenharmony_ci	bool bo_iowrite;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	u32 pseudo_palette[17];
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	unsigned max_width;
568c2ecf20Sopenharmony_ci	unsigned max_height;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	struct {
598c2ecf20Sopenharmony_ci		spinlock_t lock;
608c2ecf20Sopenharmony_ci		bool active;
618c2ecf20Sopenharmony_ci		unsigned x1;
628c2ecf20Sopenharmony_ci		unsigned y1;
638c2ecf20Sopenharmony_ci		unsigned x2;
648c2ecf20Sopenharmony_ci		unsigned y2;
658c2ecf20Sopenharmony_ci	} dirty;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	struct drm_crtc *crtc;
688c2ecf20Sopenharmony_ci	struct drm_connector *con;
698c2ecf20Sopenharmony_ci	struct delayed_work local_work;
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic int vmw_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
738c2ecf20Sopenharmony_ci			    unsigned blue, unsigned transp,
748c2ecf20Sopenharmony_ci			    struct fb_info *info)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	struct vmw_fb_par *par = info->par;
778c2ecf20Sopenharmony_ci	u32 *pal = par->pseudo_palette;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (regno > 15) {
808c2ecf20Sopenharmony_ci		DRM_ERROR("Bad regno %u.\n", regno);
818c2ecf20Sopenharmony_ci		return 1;
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	switch (par->set_fb->format->depth) {
858c2ecf20Sopenharmony_ci	case 24:
868c2ecf20Sopenharmony_ci	case 32:
878c2ecf20Sopenharmony_ci		pal[regno] = ((red & 0xff00) << 8) |
888c2ecf20Sopenharmony_ci			      (green & 0xff00) |
898c2ecf20Sopenharmony_ci			     ((blue  & 0xff00) >> 8);
908c2ecf20Sopenharmony_ci		break;
918c2ecf20Sopenharmony_ci	default:
928c2ecf20Sopenharmony_ci		DRM_ERROR("Bad depth %u, bpp %u.\n",
938c2ecf20Sopenharmony_ci			  par->set_fb->format->depth,
948c2ecf20Sopenharmony_ci			  par->set_fb->format->cpp[0] * 8);
958c2ecf20Sopenharmony_ci		return 1;
968c2ecf20Sopenharmony_ci	}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	return 0;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic int vmw_fb_check_var(struct fb_var_screeninfo *var,
1028c2ecf20Sopenharmony_ci			    struct fb_info *info)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	int depth = var->bits_per_pixel;
1058c2ecf20Sopenharmony_ci	struct vmw_fb_par *par = info->par;
1068c2ecf20Sopenharmony_ci	struct vmw_private *vmw_priv = par->vmw_priv;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	switch (var->bits_per_pixel) {
1098c2ecf20Sopenharmony_ci	case 32:
1108c2ecf20Sopenharmony_ci		depth = (var->transp.length > 0) ? 32 : 24;
1118c2ecf20Sopenharmony_ci		break;
1128c2ecf20Sopenharmony_ci	default:
1138c2ecf20Sopenharmony_ci		DRM_ERROR("Bad bpp %u.\n", var->bits_per_pixel);
1148c2ecf20Sopenharmony_ci		return -EINVAL;
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	switch (depth) {
1188c2ecf20Sopenharmony_ci	case 24:
1198c2ecf20Sopenharmony_ci		var->red.offset = 16;
1208c2ecf20Sopenharmony_ci		var->green.offset = 8;
1218c2ecf20Sopenharmony_ci		var->blue.offset = 0;
1228c2ecf20Sopenharmony_ci		var->red.length = 8;
1238c2ecf20Sopenharmony_ci		var->green.length = 8;
1248c2ecf20Sopenharmony_ci		var->blue.length = 8;
1258c2ecf20Sopenharmony_ci		var->transp.length = 0;
1268c2ecf20Sopenharmony_ci		var->transp.offset = 0;
1278c2ecf20Sopenharmony_ci		break;
1288c2ecf20Sopenharmony_ci	case 32:
1298c2ecf20Sopenharmony_ci		var->red.offset = 16;
1308c2ecf20Sopenharmony_ci		var->green.offset = 8;
1318c2ecf20Sopenharmony_ci		var->blue.offset = 0;
1328c2ecf20Sopenharmony_ci		var->red.length = 8;
1338c2ecf20Sopenharmony_ci		var->green.length = 8;
1348c2ecf20Sopenharmony_ci		var->blue.length = 8;
1358c2ecf20Sopenharmony_ci		var->transp.length = 8;
1368c2ecf20Sopenharmony_ci		var->transp.offset = 24;
1378c2ecf20Sopenharmony_ci		break;
1388c2ecf20Sopenharmony_ci	default:
1398c2ecf20Sopenharmony_ci		DRM_ERROR("Bad depth %u.\n", depth);
1408c2ecf20Sopenharmony_ci		return -EINVAL;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if ((var->xoffset + var->xres) > par->max_width ||
1448c2ecf20Sopenharmony_ci	    (var->yoffset + var->yres) > par->max_height) {
1458c2ecf20Sopenharmony_ci		DRM_ERROR("Requested geom can not fit in framebuffer\n");
1468c2ecf20Sopenharmony_ci		return -EINVAL;
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	if (!vmw_kms_validate_mode_vram(vmw_priv,
1508c2ecf20Sopenharmony_ci					var->xres * var->bits_per_pixel/8,
1518c2ecf20Sopenharmony_ci					var->yoffset + var->yres)) {
1528c2ecf20Sopenharmony_ci		DRM_ERROR("Requested geom can not fit in framebuffer\n");
1538c2ecf20Sopenharmony_ci		return -EINVAL;
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	return 0;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic int vmw_fb_blank(int blank, struct fb_info *info)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	return 0;
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci/**
1658c2ecf20Sopenharmony_ci * vmw_fb_dirty_flush - flush dirty regions to the kms framebuffer
1668c2ecf20Sopenharmony_ci *
1678c2ecf20Sopenharmony_ci * @work: The struct work_struct associated with this task.
1688c2ecf20Sopenharmony_ci *
1698c2ecf20Sopenharmony_ci * This function flushes the dirty regions of the vmalloc framebuffer to the
1708c2ecf20Sopenharmony_ci * kms framebuffer, and if the kms framebuffer is visible, also updated the
1718c2ecf20Sopenharmony_ci * corresponding displays. Note that this function runs even if the kms
1728c2ecf20Sopenharmony_ci * framebuffer is not bound to a crtc and thus not visible, but it's turned
1738c2ecf20Sopenharmony_ci * off during hibernation using the par->dirty.active bool.
1748c2ecf20Sopenharmony_ci */
1758c2ecf20Sopenharmony_cistatic void vmw_fb_dirty_flush(struct work_struct *work)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	struct vmw_fb_par *par = container_of(work, struct vmw_fb_par,
1788c2ecf20Sopenharmony_ci					      local_work.work);
1798c2ecf20Sopenharmony_ci	struct vmw_private *vmw_priv = par->vmw_priv;
1808c2ecf20Sopenharmony_ci	struct fb_info *info = vmw_priv->fb_info;
1818c2ecf20Sopenharmony_ci	unsigned long irq_flags;
1828c2ecf20Sopenharmony_ci	s32 dst_x1, dst_x2, dst_y1, dst_y2, w = 0, h = 0;
1838c2ecf20Sopenharmony_ci	u32 cpp, max_x, max_y;
1848c2ecf20Sopenharmony_ci	struct drm_clip_rect clip;
1858c2ecf20Sopenharmony_ci	struct drm_framebuffer *cur_fb;
1868c2ecf20Sopenharmony_ci	u8 *src_ptr, *dst_ptr;
1878c2ecf20Sopenharmony_ci	struct vmw_buffer_object *vbo = par->vmw_bo;
1888c2ecf20Sopenharmony_ci	void *virtual;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	if (!READ_ONCE(par->dirty.active))
1918c2ecf20Sopenharmony_ci		return;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	mutex_lock(&par->bo_mutex);
1948c2ecf20Sopenharmony_ci	cur_fb = par->set_fb;
1958c2ecf20Sopenharmony_ci	if (!cur_fb)
1968c2ecf20Sopenharmony_ci		goto out_unlock;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	(void) ttm_read_lock(&vmw_priv->reservation_sem, false);
1998c2ecf20Sopenharmony_ci	(void) ttm_bo_reserve(&vbo->base, false, false, NULL);
2008c2ecf20Sopenharmony_ci	virtual = vmw_bo_map_and_cache(vbo);
2018c2ecf20Sopenharmony_ci	if (!virtual)
2028c2ecf20Sopenharmony_ci		goto out_unreserve;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	spin_lock_irqsave(&par->dirty.lock, irq_flags);
2058c2ecf20Sopenharmony_ci	if (!par->dirty.active) {
2068c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&par->dirty.lock, irq_flags);
2078c2ecf20Sopenharmony_ci		goto out_unreserve;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	/*
2118c2ecf20Sopenharmony_ci	 * Handle panning when copying from vmalloc to framebuffer.
2128c2ecf20Sopenharmony_ci	 * Clip dirty area to framebuffer.
2138c2ecf20Sopenharmony_ci	 */
2148c2ecf20Sopenharmony_ci	cpp = cur_fb->format->cpp[0];
2158c2ecf20Sopenharmony_ci	max_x = par->fb_x + cur_fb->width;
2168c2ecf20Sopenharmony_ci	max_y = par->fb_y + cur_fb->height;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	dst_x1 = par->dirty.x1 - par->fb_x;
2198c2ecf20Sopenharmony_ci	dst_y1 = par->dirty.y1 - par->fb_y;
2208c2ecf20Sopenharmony_ci	dst_x1 = max_t(s32, dst_x1, 0);
2218c2ecf20Sopenharmony_ci	dst_y1 = max_t(s32, dst_y1, 0);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	dst_x2 = par->dirty.x2 - par->fb_x;
2248c2ecf20Sopenharmony_ci	dst_y2 = par->dirty.y2 - par->fb_y;
2258c2ecf20Sopenharmony_ci	dst_x2 = min_t(s32, dst_x2, max_x);
2268c2ecf20Sopenharmony_ci	dst_y2 = min_t(s32, dst_y2, max_y);
2278c2ecf20Sopenharmony_ci	w = dst_x2 - dst_x1;
2288c2ecf20Sopenharmony_ci	h = dst_y2 - dst_y1;
2298c2ecf20Sopenharmony_ci	w = max_t(s32, 0, w);
2308c2ecf20Sopenharmony_ci	h = max_t(s32, 0, h);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	par->dirty.x1 = par->dirty.x2 = 0;
2338c2ecf20Sopenharmony_ci	par->dirty.y1 = par->dirty.y2 = 0;
2348c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&par->dirty.lock, irq_flags);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	if (w && h) {
2378c2ecf20Sopenharmony_ci		dst_ptr = (u8 *)virtual  +
2388c2ecf20Sopenharmony_ci			(dst_y1 * par->set_fb->pitches[0] + dst_x1 * cpp);
2398c2ecf20Sopenharmony_ci		src_ptr = (u8 *)par->vmalloc +
2408c2ecf20Sopenharmony_ci			((dst_y1 + par->fb_y) * info->fix.line_length +
2418c2ecf20Sopenharmony_ci			 (dst_x1 + par->fb_x) * cpp);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci		while (h-- > 0) {
2448c2ecf20Sopenharmony_ci			memcpy(dst_ptr, src_ptr, w*cpp);
2458c2ecf20Sopenharmony_ci			dst_ptr += par->set_fb->pitches[0];
2468c2ecf20Sopenharmony_ci			src_ptr += info->fix.line_length;
2478c2ecf20Sopenharmony_ci		}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci		clip.x1 = dst_x1;
2508c2ecf20Sopenharmony_ci		clip.x2 = dst_x2;
2518c2ecf20Sopenharmony_ci		clip.y1 = dst_y1;
2528c2ecf20Sopenharmony_ci		clip.y2 = dst_y2;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ciout_unreserve:
2568c2ecf20Sopenharmony_ci	ttm_bo_unreserve(&vbo->base);
2578c2ecf20Sopenharmony_ci	ttm_read_unlock(&vmw_priv->reservation_sem);
2588c2ecf20Sopenharmony_ci	if (w && h) {
2598c2ecf20Sopenharmony_ci		WARN_ON_ONCE(par->set_fb->funcs->dirty(cur_fb, NULL, 0, 0,
2608c2ecf20Sopenharmony_ci						       &clip, 1));
2618c2ecf20Sopenharmony_ci		vmw_fifo_flush(vmw_priv, false);
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ciout_unlock:
2648c2ecf20Sopenharmony_ci	mutex_unlock(&par->bo_mutex);
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic void vmw_fb_dirty_mark(struct vmw_fb_par *par,
2688c2ecf20Sopenharmony_ci			      unsigned x1, unsigned y1,
2698c2ecf20Sopenharmony_ci			      unsigned width, unsigned height)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	unsigned long flags;
2728c2ecf20Sopenharmony_ci	unsigned x2 = x1 + width;
2738c2ecf20Sopenharmony_ci	unsigned y2 = y1 + height;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	spin_lock_irqsave(&par->dirty.lock, flags);
2768c2ecf20Sopenharmony_ci	if (par->dirty.x1 == par->dirty.x2) {
2778c2ecf20Sopenharmony_ci		par->dirty.x1 = x1;
2788c2ecf20Sopenharmony_ci		par->dirty.y1 = y1;
2798c2ecf20Sopenharmony_ci		par->dirty.x2 = x2;
2808c2ecf20Sopenharmony_ci		par->dirty.y2 = y2;
2818c2ecf20Sopenharmony_ci		/* if we are active start the dirty work
2828c2ecf20Sopenharmony_ci		 * we share the work with the defio system */
2838c2ecf20Sopenharmony_ci		if (par->dirty.active)
2848c2ecf20Sopenharmony_ci			schedule_delayed_work(&par->local_work,
2858c2ecf20Sopenharmony_ci					      VMW_DIRTY_DELAY);
2868c2ecf20Sopenharmony_ci	} else {
2878c2ecf20Sopenharmony_ci		if (x1 < par->dirty.x1)
2888c2ecf20Sopenharmony_ci			par->dirty.x1 = x1;
2898c2ecf20Sopenharmony_ci		if (y1 < par->dirty.y1)
2908c2ecf20Sopenharmony_ci			par->dirty.y1 = y1;
2918c2ecf20Sopenharmony_ci		if (x2 > par->dirty.x2)
2928c2ecf20Sopenharmony_ci			par->dirty.x2 = x2;
2938c2ecf20Sopenharmony_ci		if (y2 > par->dirty.y2)
2948c2ecf20Sopenharmony_ci			par->dirty.y2 = y2;
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&par->dirty.lock, flags);
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic int vmw_fb_pan_display(struct fb_var_screeninfo *var,
3008c2ecf20Sopenharmony_ci			      struct fb_info *info)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	struct vmw_fb_par *par = info->par;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if ((var->xoffset + var->xres) > var->xres_virtual ||
3058c2ecf20Sopenharmony_ci	    (var->yoffset + var->yres) > var->yres_virtual) {
3068c2ecf20Sopenharmony_ci		DRM_ERROR("Requested panning can not fit in framebuffer\n");
3078c2ecf20Sopenharmony_ci		return -EINVAL;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	mutex_lock(&par->bo_mutex);
3118c2ecf20Sopenharmony_ci	par->fb_x = var->xoffset;
3128c2ecf20Sopenharmony_ci	par->fb_y = var->yoffset;
3138c2ecf20Sopenharmony_ci	if (par->set_fb)
3148c2ecf20Sopenharmony_ci		vmw_fb_dirty_mark(par, par->fb_x, par->fb_y, par->set_fb->width,
3158c2ecf20Sopenharmony_ci				  par->set_fb->height);
3168c2ecf20Sopenharmony_ci	mutex_unlock(&par->bo_mutex);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	return 0;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic void vmw_deferred_io(struct fb_info *info,
3228c2ecf20Sopenharmony_ci			    struct list_head *pagelist)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	struct vmw_fb_par *par = info->par;
3258c2ecf20Sopenharmony_ci	unsigned long start, end, min, max;
3268c2ecf20Sopenharmony_ci	unsigned long flags;
3278c2ecf20Sopenharmony_ci	struct page *page;
3288c2ecf20Sopenharmony_ci	int y1, y2;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	min = ULONG_MAX;
3318c2ecf20Sopenharmony_ci	max = 0;
3328c2ecf20Sopenharmony_ci	list_for_each_entry(page, pagelist, lru) {
3338c2ecf20Sopenharmony_ci		start = page->index << PAGE_SHIFT;
3348c2ecf20Sopenharmony_ci		end = start + PAGE_SIZE - 1;
3358c2ecf20Sopenharmony_ci		min = min(min, start);
3368c2ecf20Sopenharmony_ci		max = max(max, end);
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	if (min < max) {
3408c2ecf20Sopenharmony_ci		y1 = min / info->fix.line_length;
3418c2ecf20Sopenharmony_ci		y2 = (max / info->fix.line_length) + 1;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci		spin_lock_irqsave(&par->dirty.lock, flags);
3448c2ecf20Sopenharmony_ci		par->dirty.x1 = 0;
3458c2ecf20Sopenharmony_ci		par->dirty.y1 = y1;
3468c2ecf20Sopenharmony_ci		par->dirty.x2 = info->var.xres;
3478c2ecf20Sopenharmony_ci		par->dirty.y2 = y2;
3488c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&par->dirty.lock, flags);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci		/*
3518c2ecf20Sopenharmony_ci		 * Since we've already waited on this work once, try to
3528c2ecf20Sopenharmony_ci		 * execute asap.
3538c2ecf20Sopenharmony_ci		 */
3548c2ecf20Sopenharmony_ci		cancel_delayed_work(&par->local_work);
3558c2ecf20Sopenharmony_ci		schedule_delayed_work(&par->local_work, 0);
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci};
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic struct fb_deferred_io vmw_defio = {
3608c2ecf20Sopenharmony_ci	.delay		= VMW_DIRTY_DELAY,
3618c2ecf20Sopenharmony_ci	.deferred_io	= vmw_deferred_io,
3628c2ecf20Sopenharmony_ci};
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci/*
3658c2ecf20Sopenharmony_ci * Draw code
3668c2ecf20Sopenharmony_ci */
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic void vmw_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	cfb_fillrect(info, rect);
3718c2ecf20Sopenharmony_ci	vmw_fb_dirty_mark(info->par, rect->dx, rect->dy,
3728c2ecf20Sopenharmony_ci			  rect->width, rect->height);
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic void vmw_fb_copyarea(struct fb_info *info, const struct fb_copyarea *region)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	cfb_copyarea(info, region);
3788c2ecf20Sopenharmony_ci	vmw_fb_dirty_mark(info->par, region->dx, region->dy,
3798c2ecf20Sopenharmony_ci			  region->width, region->height);
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_cistatic void vmw_fb_imageblit(struct fb_info *info, const struct fb_image *image)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	cfb_imageblit(info, image);
3858c2ecf20Sopenharmony_ci	vmw_fb_dirty_mark(info->par, image->dx, image->dy,
3868c2ecf20Sopenharmony_ci			  image->width, image->height);
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci/*
3908c2ecf20Sopenharmony_ci * Bring up code
3918c2ecf20Sopenharmony_ci */
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic int vmw_fb_create_bo(struct vmw_private *vmw_priv,
3948c2ecf20Sopenharmony_ci			    size_t size, struct vmw_buffer_object **out)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	struct vmw_buffer_object *vmw_bo;
3978c2ecf20Sopenharmony_ci	int ret;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	(void) ttm_write_lock(&vmw_priv->reservation_sem, false);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	vmw_bo = kmalloc(sizeof(*vmw_bo), GFP_KERNEL);
4028c2ecf20Sopenharmony_ci	if (!vmw_bo) {
4038c2ecf20Sopenharmony_ci		ret = -ENOMEM;
4048c2ecf20Sopenharmony_ci		goto err_unlock;
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	ret = vmw_bo_init(vmw_priv, vmw_bo, size,
4088c2ecf20Sopenharmony_ci			      &vmw_sys_placement,
4098c2ecf20Sopenharmony_ci			      false,
4108c2ecf20Sopenharmony_ci			      &vmw_bo_bo_free);
4118c2ecf20Sopenharmony_ci	if (unlikely(ret != 0))
4128c2ecf20Sopenharmony_ci		goto err_unlock; /* init frees the buffer on failure */
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	*out = vmw_bo;
4158c2ecf20Sopenharmony_ci	ttm_write_unlock(&vmw_priv->reservation_sem);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	return 0;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cierr_unlock:
4208c2ecf20Sopenharmony_ci	ttm_write_unlock(&vmw_priv->reservation_sem);
4218c2ecf20Sopenharmony_ci	return ret;
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_cistatic int vmw_fb_compute_depth(struct fb_var_screeninfo *var,
4258c2ecf20Sopenharmony_ci				int *depth)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	switch (var->bits_per_pixel) {
4288c2ecf20Sopenharmony_ci	case 32:
4298c2ecf20Sopenharmony_ci		*depth = (var->transp.length > 0) ? 32 : 24;
4308c2ecf20Sopenharmony_ci		break;
4318c2ecf20Sopenharmony_ci	default:
4328c2ecf20Sopenharmony_ci		DRM_ERROR("Bad bpp %u.\n", var->bits_per_pixel);
4338c2ecf20Sopenharmony_ci		return -EINVAL;
4348c2ecf20Sopenharmony_ci	}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	return 0;
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cistatic int vmwgfx_set_config_internal(struct drm_mode_set *set)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	struct drm_crtc *crtc = set->crtc;
4428c2ecf20Sopenharmony_ci	struct drm_modeset_acquire_ctx ctx;
4438c2ecf20Sopenharmony_ci	int ret;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	drm_modeset_acquire_init(&ctx, 0);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_cirestart:
4488c2ecf20Sopenharmony_ci	ret = crtc->funcs->set_config(set, &ctx);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	if (ret == -EDEADLK) {
4518c2ecf20Sopenharmony_ci		drm_modeset_backoff(&ctx);
4528c2ecf20Sopenharmony_ci		goto restart;
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	drm_modeset_drop_locks(&ctx);
4568c2ecf20Sopenharmony_ci	drm_modeset_acquire_fini(&ctx);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	return ret;
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic int vmw_fb_kms_detach(struct vmw_fb_par *par,
4628c2ecf20Sopenharmony_ci			     bool detach_bo,
4638c2ecf20Sopenharmony_ci			     bool unref_bo)
4648c2ecf20Sopenharmony_ci{
4658c2ecf20Sopenharmony_ci	struct drm_framebuffer *cur_fb = par->set_fb;
4668c2ecf20Sopenharmony_ci	int ret;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	/* Detach the KMS framebuffer from crtcs */
4698c2ecf20Sopenharmony_ci	if (par->set_mode) {
4708c2ecf20Sopenharmony_ci		struct drm_mode_set set;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci		set.crtc = par->crtc;
4738c2ecf20Sopenharmony_ci		set.x = 0;
4748c2ecf20Sopenharmony_ci		set.y = 0;
4758c2ecf20Sopenharmony_ci		set.mode = NULL;
4768c2ecf20Sopenharmony_ci		set.fb = NULL;
4778c2ecf20Sopenharmony_ci		set.num_connectors = 0;
4788c2ecf20Sopenharmony_ci		set.connectors = &par->con;
4798c2ecf20Sopenharmony_ci		ret = vmwgfx_set_config_internal(&set);
4808c2ecf20Sopenharmony_ci		if (ret) {
4818c2ecf20Sopenharmony_ci			DRM_ERROR("Could not unset a mode.\n");
4828c2ecf20Sopenharmony_ci			return ret;
4838c2ecf20Sopenharmony_ci		}
4848c2ecf20Sopenharmony_ci		drm_mode_destroy(par->vmw_priv->dev, par->set_mode);
4858c2ecf20Sopenharmony_ci		par->set_mode = NULL;
4868c2ecf20Sopenharmony_ci	}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	if (cur_fb) {
4898c2ecf20Sopenharmony_ci		drm_framebuffer_put(cur_fb);
4908c2ecf20Sopenharmony_ci		par->set_fb = NULL;
4918c2ecf20Sopenharmony_ci	}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	if (par->vmw_bo && detach_bo && unref_bo)
4948c2ecf20Sopenharmony_ci		vmw_bo_unreference(&par->vmw_bo);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	return 0;
4978c2ecf20Sopenharmony_ci}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_cistatic int vmw_fb_kms_framebuffer(struct fb_info *info)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	struct drm_mode_fb_cmd2 mode_cmd = {0};
5028c2ecf20Sopenharmony_ci	struct vmw_fb_par *par = info->par;
5038c2ecf20Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
5048c2ecf20Sopenharmony_ci	struct drm_framebuffer *cur_fb;
5058c2ecf20Sopenharmony_ci	struct vmw_framebuffer *vfb;
5068c2ecf20Sopenharmony_ci	int ret = 0, depth;
5078c2ecf20Sopenharmony_ci	size_t new_bo_size;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	ret = vmw_fb_compute_depth(var, &depth);
5108c2ecf20Sopenharmony_ci	if (ret)
5118c2ecf20Sopenharmony_ci		return ret;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	mode_cmd.width = var->xres;
5148c2ecf20Sopenharmony_ci	mode_cmd.height = var->yres;
5158c2ecf20Sopenharmony_ci	mode_cmd.pitches[0] = ((var->bits_per_pixel + 7) / 8) * mode_cmd.width;
5168c2ecf20Sopenharmony_ci	mode_cmd.pixel_format =
5178c2ecf20Sopenharmony_ci		drm_mode_legacy_fb_format(var->bits_per_pixel, depth);
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	cur_fb = par->set_fb;
5208c2ecf20Sopenharmony_ci	if (cur_fb && cur_fb->width == mode_cmd.width &&
5218c2ecf20Sopenharmony_ci	    cur_fb->height == mode_cmd.height &&
5228c2ecf20Sopenharmony_ci	    cur_fb->format->format == mode_cmd.pixel_format &&
5238c2ecf20Sopenharmony_ci	    cur_fb->pitches[0] == mode_cmd.pitches[0])
5248c2ecf20Sopenharmony_ci		return 0;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	/* Need new buffer object ? */
5278c2ecf20Sopenharmony_ci	new_bo_size = (size_t) mode_cmd.pitches[0] * (size_t) mode_cmd.height;
5288c2ecf20Sopenharmony_ci	ret = vmw_fb_kms_detach(par,
5298c2ecf20Sopenharmony_ci				par->bo_size < new_bo_size ||
5308c2ecf20Sopenharmony_ci				par->bo_size > 2*new_bo_size,
5318c2ecf20Sopenharmony_ci				true);
5328c2ecf20Sopenharmony_ci	if (ret)
5338c2ecf20Sopenharmony_ci		return ret;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	if (!par->vmw_bo) {
5368c2ecf20Sopenharmony_ci		ret = vmw_fb_create_bo(par->vmw_priv, new_bo_size,
5378c2ecf20Sopenharmony_ci				       &par->vmw_bo);
5388c2ecf20Sopenharmony_ci		if (ret) {
5398c2ecf20Sopenharmony_ci			DRM_ERROR("Failed creating a buffer object for "
5408c2ecf20Sopenharmony_ci				  "fbdev.\n");
5418c2ecf20Sopenharmony_ci			return ret;
5428c2ecf20Sopenharmony_ci		}
5438c2ecf20Sopenharmony_ci		par->bo_size = new_bo_size;
5448c2ecf20Sopenharmony_ci	}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	vfb = vmw_kms_new_framebuffer(par->vmw_priv, par->vmw_bo, NULL,
5478c2ecf20Sopenharmony_ci				      true, &mode_cmd);
5488c2ecf20Sopenharmony_ci	if (IS_ERR(vfb))
5498c2ecf20Sopenharmony_ci		return PTR_ERR(vfb);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	par->set_fb = &vfb->base;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	return 0;
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_cistatic int vmw_fb_set_par(struct fb_info *info)
5578c2ecf20Sopenharmony_ci{
5588c2ecf20Sopenharmony_ci	struct vmw_fb_par *par = info->par;
5598c2ecf20Sopenharmony_ci	struct vmw_private *vmw_priv = par->vmw_priv;
5608c2ecf20Sopenharmony_ci	struct drm_mode_set set;
5618c2ecf20Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
5628c2ecf20Sopenharmony_ci	struct drm_display_mode new_mode = { DRM_MODE("fb_mode",
5638c2ecf20Sopenharmony_ci		DRM_MODE_TYPE_DRIVER,
5648c2ecf20Sopenharmony_ci		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
5658c2ecf20Sopenharmony_ci		DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC)
5668c2ecf20Sopenharmony_ci	};
5678c2ecf20Sopenharmony_ci	struct drm_display_mode *mode;
5688c2ecf20Sopenharmony_ci	int ret;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	mode = drm_mode_duplicate(vmw_priv->dev, &new_mode);
5718c2ecf20Sopenharmony_ci	if (!mode) {
5728c2ecf20Sopenharmony_ci		DRM_ERROR("Could not create new fb mode.\n");
5738c2ecf20Sopenharmony_ci		return -ENOMEM;
5748c2ecf20Sopenharmony_ci	}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	mode->hdisplay = var->xres;
5778c2ecf20Sopenharmony_ci	mode->vdisplay = var->yres;
5788c2ecf20Sopenharmony_ci	vmw_guess_mode_timing(mode);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	if (!vmw_kms_validate_mode_vram(vmw_priv,
5818c2ecf20Sopenharmony_ci					mode->hdisplay *
5828c2ecf20Sopenharmony_ci					DIV_ROUND_UP(var->bits_per_pixel, 8),
5838c2ecf20Sopenharmony_ci					mode->vdisplay)) {
5848c2ecf20Sopenharmony_ci		drm_mode_destroy(vmw_priv->dev, mode);
5858c2ecf20Sopenharmony_ci		return -EINVAL;
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	mutex_lock(&par->bo_mutex);
5898c2ecf20Sopenharmony_ci	ret = vmw_fb_kms_framebuffer(info);
5908c2ecf20Sopenharmony_ci	if (ret)
5918c2ecf20Sopenharmony_ci		goto out_unlock;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	par->fb_x = var->xoffset;
5948c2ecf20Sopenharmony_ci	par->fb_y = var->yoffset;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	set.crtc = par->crtc;
5978c2ecf20Sopenharmony_ci	set.x = 0;
5988c2ecf20Sopenharmony_ci	set.y = 0;
5998c2ecf20Sopenharmony_ci	set.mode = mode;
6008c2ecf20Sopenharmony_ci	set.fb = par->set_fb;
6018c2ecf20Sopenharmony_ci	set.num_connectors = 1;
6028c2ecf20Sopenharmony_ci	set.connectors = &par->con;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	ret = vmwgfx_set_config_internal(&set);
6058c2ecf20Sopenharmony_ci	if (ret)
6068c2ecf20Sopenharmony_ci		goto out_unlock;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	vmw_fb_dirty_mark(par, par->fb_x, par->fb_y,
6098c2ecf20Sopenharmony_ci			  par->set_fb->width, par->set_fb->height);
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	/* If there already was stuff dirty we wont
6128c2ecf20Sopenharmony_ci	 * schedule a new work, so lets do it now */
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	schedule_delayed_work(&par->local_work, 0);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ciout_unlock:
6178c2ecf20Sopenharmony_ci	if (par->set_mode)
6188c2ecf20Sopenharmony_ci		drm_mode_destroy(vmw_priv->dev, par->set_mode);
6198c2ecf20Sopenharmony_ci	par->set_mode = mode;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	mutex_unlock(&par->bo_mutex);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	return ret;
6248c2ecf20Sopenharmony_ci}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_cistatic const struct fb_ops vmw_fb_ops = {
6288c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
6298c2ecf20Sopenharmony_ci	.fb_check_var = vmw_fb_check_var,
6308c2ecf20Sopenharmony_ci	.fb_set_par = vmw_fb_set_par,
6318c2ecf20Sopenharmony_ci	.fb_setcolreg = vmw_fb_setcolreg,
6328c2ecf20Sopenharmony_ci	.fb_fillrect = vmw_fb_fillrect,
6338c2ecf20Sopenharmony_ci	.fb_copyarea = vmw_fb_copyarea,
6348c2ecf20Sopenharmony_ci	.fb_imageblit = vmw_fb_imageblit,
6358c2ecf20Sopenharmony_ci	.fb_pan_display = vmw_fb_pan_display,
6368c2ecf20Sopenharmony_ci	.fb_blank = vmw_fb_blank,
6378c2ecf20Sopenharmony_ci};
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ciint vmw_fb_init(struct vmw_private *vmw_priv)
6408c2ecf20Sopenharmony_ci{
6418c2ecf20Sopenharmony_ci	struct device *device = &vmw_priv->dev->pdev->dev;
6428c2ecf20Sopenharmony_ci	struct vmw_fb_par *par;
6438c2ecf20Sopenharmony_ci	struct fb_info *info;
6448c2ecf20Sopenharmony_ci	unsigned fb_width, fb_height;
6458c2ecf20Sopenharmony_ci	unsigned int fb_bpp, fb_pitch, fb_size;
6468c2ecf20Sopenharmony_ci	struct drm_display_mode *init_mode;
6478c2ecf20Sopenharmony_ci	int ret;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	fb_bpp = 32;
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	/* XXX As shouldn't these be as well. */
6528c2ecf20Sopenharmony_ci	fb_width = min(vmw_priv->fb_max_width, (unsigned)2048);
6538c2ecf20Sopenharmony_ci	fb_height = min(vmw_priv->fb_max_height, (unsigned)2048);
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	fb_pitch = fb_width * fb_bpp / 8;
6568c2ecf20Sopenharmony_ci	fb_size = fb_pitch * fb_height;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	info = framebuffer_alloc(sizeof(*par), device);
6598c2ecf20Sopenharmony_ci	if (!info)
6608c2ecf20Sopenharmony_ci		return -ENOMEM;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	/*
6638c2ecf20Sopenharmony_ci	 * Par
6648c2ecf20Sopenharmony_ci	 */
6658c2ecf20Sopenharmony_ci	vmw_priv->fb_info = info;
6668c2ecf20Sopenharmony_ci	par = info->par;
6678c2ecf20Sopenharmony_ci	memset(par, 0, sizeof(*par));
6688c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&par->local_work, &vmw_fb_dirty_flush);
6698c2ecf20Sopenharmony_ci	par->vmw_priv = vmw_priv;
6708c2ecf20Sopenharmony_ci	par->vmalloc = NULL;
6718c2ecf20Sopenharmony_ci	par->max_width = fb_width;
6728c2ecf20Sopenharmony_ci	par->max_height = fb_height;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	ret = vmw_kms_fbdev_init_data(vmw_priv, 0, par->max_width,
6758c2ecf20Sopenharmony_ci				      par->max_height, &par->con,
6768c2ecf20Sopenharmony_ci				      &par->crtc, &init_mode);
6778c2ecf20Sopenharmony_ci	if (ret)
6788c2ecf20Sopenharmony_ci		goto err_kms;
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	info->var.xres = init_mode->hdisplay;
6818c2ecf20Sopenharmony_ci	info->var.yres = init_mode->vdisplay;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	/*
6848c2ecf20Sopenharmony_ci	 * Create buffers and alloc memory
6858c2ecf20Sopenharmony_ci	 */
6868c2ecf20Sopenharmony_ci	par->vmalloc = vzalloc(fb_size);
6878c2ecf20Sopenharmony_ci	if (unlikely(par->vmalloc == NULL)) {
6888c2ecf20Sopenharmony_ci		ret = -ENOMEM;
6898c2ecf20Sopenharmony_ci		goto err_free;
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	/*
6938c2ecf20Sopenharmony_ci	 * Fixed and var
6948c2ecf20Sopenharmony_ci	 */
6958c2ecf20Sopenharmony_ci	strcpy(info->fix.id, "svgadrmfb");
6968c2ecf20Sopenharmony_ci	info->fix.type = FB_TYPE_PACKED_PIXELS;
6978c2ecf20Sopenharmony_ci	info->fix.visual = FB_VISUAL_TRUECOLOR;
6988c2ecf20Sopenharmony_ci	info->fix.type_aux = 0;
6998c2ecf20Sopenharmony_ci	info->fix.xpanstep = 1; /* doing it in hw */
7008c2ecf20Sopenharmony_ci	info->fix.ypanstep = 1; /* doing it in hw */
7018c2ecf20Sopenharmony_ci	info->fix.ywrapstep = 0;
7028c2ecf20Sopenharmony_ci	info->fix.accel = FB_ACCEL_NONE;
7038c2ecf20Sopenharmony_ci	info->fix.line_length = fb_pitch;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	info->fix.smem_start = 0;
7068c2ecf20Sopenharmony_ci	info->fix.smem_len = fb_size;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	info->pseudo_palette = par->pseudo_palette;
7098c2ecf20Sopenharmony_ci	info->screen_base = (char __iomem *)par->vmalloc;
7108c2ecf20Sopenharmony_ci	info->screen_size = fb_size;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	info->fbops = &vmw_fb_ops;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	/* 24 depth per default */
7158c2ecf20Sopenharmony_ci	info->var.red.offset = 16;
7168c2ecf20Sopenharmony_ci	info->var.green.offset = 8;
7178c2ecf20Sopenharmony_ci	info->var.blue.offset = 0;
7188c2ecf20Sopenharmony_ci	info->var.red.length = 8;
7198c2ecf20Sopenharmony_ci	info->var.green.length = 8;
7208c2ecf20Sopenharmony_ci	info->var.blue.length = 8;
7218c2ecf20Sopenharmony_ci	info->var.transp.offset = 0;
7228c2ecf20Sopenharmony_ci	info->var.transp.length = 0;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	info->var.xres_virtual = fb_width;
7258c2ecf20Sopenharmony_ci	info->var.yres_virtual = fb_height;
7268c2ecf20Sopenharmony_ci	info->var.bits_per_pixel = fb_bpp;
7278c2ecf20Sopenharmony_ci	info->var.xoffset = 0;
7288c2ecf20Sopenharmony_ci	info->var.yoffset = 0;
7298c2ecf20Sopenharmony_ci	info->var.activate = FB_ACTIVATE_NOW;
7308c2ecf20Sopenharmony_ci	info->var.height = -1;
7318c2ecf20Sopenharmony_ci	info->var.width = -1;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	/* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
7348c2ecf20Sopenharmony_ci	info->apertures = alloc_apertures(1);
7358c2ecf20Sopenharmony_ci	if (!info->apertures) {
7368c2ecf20Sopenharmony_ci		ret = -ENOMEM;
7378c2ecf20Sopenharmony_ci		goto err_aper;
7388c2ecf20Sopenharmony_ci	}
7398c2ecf20Sopenharmony_ci	info->apertures->ranges[0].base = vmw_priv->vram_start;
7408c2ecf20Sopenharmony_ci	info->apertures->ranges[0].size = vmw_priv->vram_size;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	/*
7438c2ecf20Sopenharmony_ci	 * Dirty & Deferred IO
7448c2ecf20Sopenharmony_ci	 */
7458c2ecf20Sopenharmony_ci	par->dirty.x1 = par->dirty.x2 = 0;
7468c2ecf20Sopenharmony_ci	par->dirty.y1 = par->dirty.y2 = 0;
7478c2ecf20Sopenharmony_ci	par->dirty.active = true;
7488c2ecf20Sopenharmony_ci	spin_lock_init(&par->dirty.lock);
7498c2ecf20Sopenharmony_ci	mutex_init(&par->bo_mutex);
7508c2ecf20Sopenharmony_ci	info->fbdefio = &vmw_defio;
7518c2ecf20Sopenharmony_ci	fb_deferred_io_init(info);
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	ret = register_framebuffer(info);
7548c2ecf20Sopenharmony_ci	if (unlikely(ret != 0))
7558c2ecf20Sopenharmony_ci		goto err_defio;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	vmw_fb_set_par(info);
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	return 0;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_cierr_defio:
7628c2ecf20Sopenharmony_ci	fb_deferred_io_cleanup(info);
7638c2ecf20Sopenharmony_cierr_aper:
7648c2ecf20Sopenharmony_cierr_free:
7658c2ecf20Sopenharmony_ci	vfree(par->vmalloc);
7668c2ecf20Sopenharmony_cierr_kms:
7678c2ecf20Sopenharmony_ci	framebuffer_release(info);
7688c2ecf20Sopenharmony_ci	vmw_priv->fb_info = NULL;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	return ret;
7718c2ecf20Sopenharmony_ci}
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ciint vmw_fb_close(struct vmw_private *vmw_priv)
7748c2ecf20Sopenharmony_ci{
7758c2ecf20Sopenharmony_ci	struct fb_info *info;
7768c2ecf20Sopenharmony_ci	struct vmw_fb_par *par;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	if (!vmw_priv->fb_info)
7798c2ecf20Sopenharmony_ci		return 0;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	info = vmw_priv->fb_info;
7828c2ecf20Sopenharmony_ci	par = info->par;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	/* ??? order */
7858c2ecf20Sopenharmony_ci	fb_deferred_io_cleanup(info);
7868c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&par->local_work);
7878c2ecf20Sopenharmony_ci	unregister_framebuffer(info);
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	mutex_lock(&par->bo_mutex);
7908c2ecf20Sopenharmony_ci	(void) vmw_fb_kms_detach(par, true, true);
7918c2ecf20Sopenharmony_ci	mutex_unlock(&par->bo_mutex);
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	vfree(par->vmalloc);
7948c2ecf20Sopenharmony_ci	framebuffer_release(info);
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	return 0;
7978c2ecf20Sopenharmony_ci}
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ciint vmw_fb_off(struct vmw_private *vmw_priv)
8008c2ecf20Sopenharmony_ci{
8018c2ecf20Sopenharmony_ci	struct fb_info *info;
8028c2ecf20Sopenharmony_ci	struct vmw_fb_par *par;
8038c2ecf20Sopenharmony_ci	unsigned long flags;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	if (!vmw_priv->fb_info)
8068c2ecf20Sopenharmony_ci		return -EINVAL;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	info = vmw_priv->fb_info;
8098c2ecf20Sopenharmony_ci	par = info->par;
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	spin_lock_irqsave(&par->dirty.lock, flags);
8128c2ecf20Sopenharmony_ci	par->dirty.active = false;
8138c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&par->dirty.lock, flags);
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	flush_delayed_work(&info->deferred_work);
8168c2ecf20Sopenharmony_ci	flush_delayed_work(&par->local_work);
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	return 0;
8198c2ecf20Sopenharmony_ci}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ciint vmw_fb_on(struct vmw_private *vmw_priv)
8228c2ecf20Sopenharmony_ci{
8238c2ecf20Sopenharmony_ci	struct fb_info *info;
8248c2ecf20Sopenharmony_ci	struct vmw_fb_par *par;
8258c2ecf20Sopenharmony_ci	unsigned long flags;
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	if (!vmw_priv->fb_info)
8288c2ecf20Sopenharmony_ci		return -EINVAL;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	info = vmw_priv->fb_info;
8318c2ecf20Sopenharmony_ci	par = info->par;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	spin_lock_irqsave(&par->dirty.lock, flags);
8348c2ecf20Sopenharmony_ci	par->dirty.active = true;
8358c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&par->dirty.lock, flags);
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	/*
8388c2ecf20Sopenharmony_ci	 * Need to reschedule a dirty update, because otherwise that's
8398c2ecf20Sopenharmony_ci	 * only done in dirty_mark() if the previous coalesced
8408c2ecf20Sopenharmony_ci	 * dirty region was empty.
8418c2ecf20Sopenharmony_ci	 */
8428c2ecf20Sopenharmony_ci	schedule_delayed_work(&par->local_work, 0);
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	return 0;
8458c2ecf20Sopenharmony_ci}
846