ia64/linux-2.6.18-xen.hg

changeset 493:92f7b3144f41

xenfb: Dynamic modes support

Corresponding backend IOEMU patch is required for functionality but
this patch is not dependent on it, preserving backwards compatibility.

Signed-off-by: Pat Campbell <plc@novell.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Mar 26 08:54:24 2008 +0000 (2008-03-26)
parents ba11d3cf69f0
children 1327b9dcc63a
files drivers/xen/fbfront/xenfb.c drivers/xen/fbfront/xenkbd.c include/xen/interface/io/fbif.h
line diff
     1.1 --- a/drivers/xen/fbfront/xenfb.c	Wed Mar 26 08:52:36 2008 +0000
     1.2 +++ b/drivers/xen/fbfront/xenfb.c	Wed Mar 26 08:54:24 2008 +0000
     1.3 @@ -62,15 +62,21 @@ struct xenfb_info
     1.4  	struct xenfb_page	*page;
     1.5  	unsigned long 		*mfns;
     1.6  	int			update_wanted; /* XENFB_TYPE_UPDATE wanted */
     1.7 +	int			feature_resize; /* Backend has resize feature */
     1.8 +	struct xenfb_resize	resize;
     1.9 +	int			resize_dpy;
    1.10 +	spinlock_t		resize_lock;
    1.11  
    1.12  	struct xenbus_device	*xbdev;
    1.13  };
    1.14  
    1.15  /*
    1.16 - * How the locks work together
    1.17 + * There are three locks:
    1.18 + *    spinlock resize_lock protecting resize_dpy and resize
    1.19 + *    spinlock dirty_lock protecting the dirty rectangle
    1.20 + *    mutex mm_lock protecting mappings.
    1.21   *
    1.22 - * There are two locks: spinlock dirty_lock protecting the dirty
    1.23 - * rectangle, and mutex mm_lock protecting mappings.
    1.24 + * How the dirty and mapping locks work together
    1.25   *
    1.26   * The problem is that dirty rectangle and mappings aren't
    1.27   * independent: the dirty rectangle must cover all faulted pages in
    1.28 @@ -129,20 +135,41 @@ struct xenfb_info
    1.29   *
    1.30   * Oh well, we wont be updating the writes to this page anytime soon.
    1.31   */
    1.32 +#define MB_ (1024*1024)
    1.33 +#define XENFB_DEFAULT_FB_LEN (XENFB_WIDTH * XENFB_HEIGHT * XENFB_DEPTH / 8)
    1.34 +
    1.35 +enum {KPARAM_MEM, KPARAM_WIDTH, KPARAM_HEIGHT, KPARAM_CNT};
    1.36 +static int video[KPARAM_CNT] = {2, XENFB_WIDTH, XENFB_HEIGHT};
    1.37 +module_param_array(video, int, NULL, 0);
    1.38 +MODULE_PARM_DESC(video,
    1.39 +		"Size of video memory in MB and width,height in pixels, default = (2,800,600)");
    1.40  
    1.41  static int xenfb_fps = 20;
    1.42 -static unsigned long xenfb_mem_len = XENFB_WIDTH * XENFB_HEIGHT * XENFB_DEPTH / 8;
    1.43  
    1.44  static int xenfb_remove(struct xenbus_device *);
    1.45 -static void xenfb_init_shared_page(struct xenfb_info *);
    1.46 +static void xenfb_init_shared_page(struct xenfb_info *, struct fb_info *);
    1.47  static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *);
    1.48  static void xenfb_disconnect_backend(struct xenfb_info *);
    1.49  
    1.50 +static void xenfb_send_event(struct xenfb_info *info,
    1.51 +		union xenfb_out_event *event)
    1.52 +{
    1.53 +	__u32 prod;
    1.54 +
    1.55 +	prod = info->page->out_prod;
    1.56 +	/* caller ensures !xenfb_queue_full() */
    1.57 +	mb();			/* ensure ring space available */
    1.58 +	XENFB_OUT_RING_REF(info->page, prod) = *event;
    1.59 +	wmb();			/* ensure ring contents visible */
    1.60 +	info->page->out_prod = prod + 1;
    1.61 +
    1.62 +	notify_remote_via_irq(info->irq);
    1.63 +}
    1.64 +
    1.65  static void xenfb_do_update(struct xenfb_info *info,
    1.66  			    int x, int y, int w, int h)
    1.67  {
    1.68  	union xenfb_out_event event;
    1.69 -	__u32 prod;
    1.70  
    1.71  	event.type = XENFB_TYPE_UPDATE;
    1.72  	event.update.x = x;
    1.73 @@ -150,14 +177,18 @@ static void xenfb_do_update(struct xenfb
    1.74  	event.update.width = w;
    1.75  	event.update.height = h;
    1.76  
    1.77 -	prod = info->page->out_prod;
    1.78  	/* caller ensures !xenfb_queue_full() */
    1.79 -	mb();			/* ensure ring space available */
    1.80 -	XENFB_OUT_RING_REF(info->page, prod) = event;
    1.81 -	wmb();			/* ensure ring contents visible */
    1.82 -	info->page->out_prod = prod + 1;
    1.83 +	xenfb_send_event(info, &event);
    1.84 +}
    1.85  
    1.86 -	notify_remote_via_irq(info->irq);
    1.87 +static void xenfb_do_resize(struct xenfb_info *info)
    1.88 +{
    1.89 +	union xenfb_out_event event;
    1.90 +
    1.91 +	event.resize = info->resize;
    1.92 +
    1.93 +	/* caller ensures !xenfb_queue_full() */
    1.94 +	xenfb_send_event(info, &event);
    1.95  }
    1.96  
    1.97  static int xenfb_queue_full(struct xenfb_info *info)
    1.98 @@ -209,11 +240,26 @@ static void xenfb_update_screen(struct x
    1.99  	xenfb_do_update(info, x1, y1, x2 - x1, y2 - y1);
   1.100  }
   1.101  
   1.102 +static void xenfb_handle_resize_dpy(struct xenfb_info *info)
   1.103 +{
   1.104 +	unsigned long flags;
   1.105 +
   1.106 +	spin_lock_irqsave(&info->resize_lock, flags);
   1.107 +	if (info->resize_dpy) {
   1.108 +		if (!xenfb_queue_full(info)) {
   1.109 +			info->resize_dpy = 0;
   1.110 +			xenfb_do_resize(info);
   1.111 +		}
   1.112 +	}
   1.113 +	spin_unlock_irqrestore(&info->resize_lock, flags);
   1.114 +}
   1.115 +
   1.116  static int xenfb_thread(void *data)
   1.117  {
   1.118  	struct xenfb_info *info = data;
   1.119  
   1.120  	while (!kthread_should_stop()) {
   1.121 +		xenfb_handle_resize_dpy(info);
   1.122  		if (info->dirty) {
   1.123  			info->dirty = 0;
   1.124  			xenfb_update_screen(info);
   1.125 @@ -413,6 +459,56 @@ static int xenfb_mmap(struct fb_info *fb
   1.126  	return 0;
   1.127  }
   1.128  
   1.129 +static int
   1.130 +xenfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
   1.131 +{
   1.132 +	struct xenfb_info *xenfb_info;
   1.133 +	int required_mem_len;
   1.134 +
   1.135 +	xenfb_info = info->par;
   1.136 +
   1.137 +	if (!xenfb_info->feature_resize) {
   1.138 +		if (var->xres == video[KPARAM_WIDTH] &&
   1.139 +			var->yres == video[KPARAM_HEIGHT] &&
   1.140 +			var->bits_per_pixel == xenfb_info->page->depth) {
   1.141 +			return 0;
   1.142 +		}
   1.143 +		return -EINVAL;
   1.144 +	}
   1.145 +
   1.146 +	/* Can't resize past initial width and height */
   1.147 +	if (var->xres > video[KPARAM_WIDTH] || var->yres > video[KPARAM_HEIGHT])
   1.148 +		return -EINVAL;
   1.149 +
   1.150 +	required_mem_len = var->xres * var->yres * (xenfb_info->page->depth / 8);
   1.151 +	if (var->bits_per_pixel == xenfb_info->page->depth &&
   1.152 +		var->xres <= info->fix.line_length / (XENFB_DEPTH / 8) &&
   1.153 +		required_mem_len <= info->fix.smem_len) {
   1.154 +		var->xres_virtual = var->xres;
   1.155 +		var->yres_virtual = var->yres;
   1.156 +		return 0;
   1.157 +	}
   1.158 +	return -EINVAL;
   1.159 +}
   1.160 +
   1.161 +static int xenfb_set_par(struct fb_info *info)
   1.162 +{
   1.163 +	struct xenfb_info *xenfb_info;
   1.164 +	unsigned long flags;
   1.165 +
   1.166 +	xenfb_info = info->par;
   1.167 +
   1.168 +	spin_lock_irqsave(&xenfb_info->resize_lock, flags);
   1.169 +	xenfb_info->resize.type = XENFB_TYPE_RESIZE;
   1.170 +	xenfb_info->resize.width = info->var.xres;
   1.171 +	xenfb_info->resize.height = info->var.yres;
   1.172 +	xenfb_info->resize.stride = info->fix.line_length;
   1.173 +	xenfb_info->resize.depth = info->var.bits_per_pixel;
   1.174 +	xenfb_info->resize_dpy = 1;
   1.175 +	spin_unlock_irqrestore(&xenfb_info->resize_lock, flags);
   1.176 +	return 0;
   1.177 +}
   1.178 +
   1.179  static struct fb_ops xenfb_fb_ops = {
   1.180  	.owner		= THIS_MODULE,
   1.181  	.fb_setcolreg	= xenfb_setcolreg,
   1.182 @@ -420,6 +516,8 @@ static struct fb_ops xenfb_fb_ops = {
   1.183  	.fb_copyarea	= xenfb_copyarea,
   1.184  	.fb_imageblit	= xenfb_imageblit,
   1.185  	.fb_mmap	= xenfb_mmap,
   1.186 +	.fb_check_var	= xenfb_check_var,
   1.187 +	.fb_set_par     = xenfb_set_par,
   1.188  };
   1.189  
   1.190  static irqreturn_t xenfb_event_handler(int rq, void *dev_id,
   1.191 @@ -450,6 +548,8 @@ static int __devinit xenfb_probe(struct 
   1.192  {
   1.193  	struct xenfb_info *info;
   1.194  	struct fb_info *fb_info;
   1.195 +	int fb_size;
   1.196 +	int val;
   1.197  	int ret;
   1.198  
   1.199  	info = kzalloc(sizeof(*info), GFP_KERNEL);
   1.200 @@ -457,11 +557,27 @@ static int __devinit xenfb_probe(struct 
   1.201  		xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
   1.202  		return -ENOMEM;
   1.203  	}
   1.204 +
   1.205 +	/* Limit kernel param videoram amount to what is in xenstore */
   1.206 +	if (xenbus_scanf(XBT_NIL, dev->otherend, "videoram", "%d", &val) == 1) {
   1.207 +		if (val < video[KPARAM_MEM])
   1.208 +			video[KPARAM_MEM] = val;
   1.209 +	}
   1.210 +
   1.211 +	/* If requested res does not fit in available memory, use default */
   1.212 +	fb_size = video[KPARAM_MEM] * MB_;
   1.213 +	if (video[KPARAM_WIDTH] * video[KPARAM_HEIGHT] * XENFB_DEPTH/8 > fb_size) {
   1.214 +		video[KPARAM_WIDTH] = XENFB_WIDTH;
   1.215 +		video[KPARAM_HEIGHT] = XENFB_HEIGHT;
   1.216 +		fb_size = XENFB_DEFAULT_FB_LEN;
   1.217 +	}
   1.218 +
   1.219  	dev->dev.driver_data = info;
   1.220  	info->xbdev = dev;
   1.221  	info->irq = -1;
   1.222  	info->x1 = info->y1 = INT_MAX;
   1.223  	spin_lock_init(&info->dirty_lock);
   1.224 +	spin_lock_init(&info->resize_lock);
   1.225  	mutex_init(&info->mm_lock);
   1.226  	init_waitqueue_head(&info->wq);
   1.227  	init_timer(&info->refresh);
   1.228 @@ -469,12 +585,12 @@ static int __devinit xenfb_probe(struct 
   1.229  	info->refresh.data = (unsigned long)info;
   1.230  	INIT_LIST_HEAD(&info->mappings);
   1.231  
   1.232 -	info->fb = vmalloc(xenfb_mem_len);
   1.233 +	info->fb = vmalloc(fb_size);
   1.234  	if (info->fb == NULL)
   1.235  		goto error_nomem;
   1.236 -	memset(info->fb, 0, xenfb_mem_len);
   1.237 +	memset(info->fb, 0, fb_size);
   1.238  
   1.239 -	info->nr_pages = (xenfb_mem_len + PAGE_SIZE - 1) >> PAGE_SHIFT;
   1.240 +	info->nr_pages = (fb_size + PAGE_SIZE - 1) >> PAGE_SHIFT;
   1.241  
   1.242  	info->pages = kmalloc(sizeof(struct page *) * info->nr_pages,
   1.243  			      GFP_KERNEL);
   1.244 @@ -490,8 +606,6 @@ static int __devinit xenfb_probe(struct 
   1.245  	if (!info->page)
   1.246  		goto error_nomem;
   1.247  
   1.248 -	xenfb_init_shared_page(info);
   1.249 -
   1.250  	fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL);
   1.251  				/* see fishy hackery below */
   1.252  	if (fb_info == NULL)
   1.253 @@ -504,9 +618,9 @@ static int __devinit xenfb_probe(struct 
   1.254  	fb_info->screen_base = info->fb;
   1.255  
   1.256  	fb_info->fbops = &xenfb_fb_ops;
   1.257 -	fb_info->var.xres_virtual = fb_info->var.xres = info->page->width;
   1.258 -	fb_info->var.yres_virtual = fb_info->var.yres = info->page->height;
   1.259 -	fb_info->var.bits_per_pixel = info->page->depth;
   1.260 +	fb_info->var.xres_virtual = fb_info->var.xres = video[KPARAM_WIDTH];
   1.261 +	fb_info->var.yres_virtual = fb_info->var.yres = video[KPARAM_HEIGHT];
   1.262 +	fb_info->var.bits_per_pixel = XENFB_DEPTH;
   1.263  
   1.264  	fb_info->var.red = (struct fb_bitfield){16, 8, 0};
   1.265  	fb_info->var.green = (struct fb_bitfield){8, 8, 0};
   1.266 @@ -518,9 +632,9 @@ static int __devinit xenfb_probe(struct 
   1.267  	fb_info->var.vmode = FB_VMODE_NONINTERLACED;
   1.268  
   1.269  	fb_info->fix.visual = FB_VISUAL_TRUECOLOR;
   1.270 -	fb_info->fix.line_length = info->page->line_length;
   1.271 +	fb_info->fix.line_length = fb_info->var.xres * (XENFB_DEPTH / 8);
   1.272  	fb_info->fix.smem_start = 0;
   1.273 -	fb_info->fix.smem_len = xenfb_mem_len;
   1.274 +	fb_info->fix.smem_len = fb_size;
   1.275  	strcpy(fb_info->fix.id, "xen");
   1.276  	fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
   1.277  	fb_info->fix.accel = FB_ACCEL_NONE;
   1.278 @@ -534,6 +648,8 @@ static int __devinit xenfb_probe(struct 
   1.279  		goto error;
   1.280  	}
   1.281  
   1.282 +	xenfb_init_shared_page(info, fb_info);
   1.283 +
   1.284  	ret = register_framebuffer(fb_info);
   1.285  	if (ret) {
   1.286  		fb_dealloc_cmap(&info->fb_info->cmap);
   1.287 @@ -571,7 +687,7 @@ static int xenfb_resume(struct xenbus_de
   1.288  	struct xenfb_info *info = dev->dev.driver_data;
   1.289  
   1.290  	xenfb_disconnect_backend(info);
   1.291 -	xenfb_init_shared_page(info);
   1.292 +	xenfb_init_shared_page(info, info->fb_info);
   1.293  	return xenfb_connect_backend(dev, info);
   1.294  }
   1.295  
   1.296 @@ -597,9 +713,11 @@ static int xenfb_remove(struct xenbus_de
   1.297  	return 0;
   1.298  }
   1.299  
   1.300 -static void xenfb_init_shared_page(struct xenfb_info *info)
   1.301 +static void xenfb_init_shared_page(struct xenfb_info *info,
   1.302 +                                   struct fb_info * fb_info)
   1.303  {
   1.304  	int i;
   1.305 +	int epd = PAGE_SIZE / sizeof(info->mfns[0]);
   1.306  
   1.307  	for (i = 0; i < info->nr_pages; i++)
   1.308  		info->pages[i] = vmalloc_to_page(info->fb + i * PAGE_SIZE);
   1.309 @@ -607,13 +725,14 @@ static void xenfb_init_shared_page(struc
   1.310  	for (i = 0; i < info->nr_pages; i++)
   1.311  		info->mfns[i] = vmalloc_to_mfn(info->fb + i * PAGE_SIZE);
   1.312  
   1.313 -	info->page->pd[0] = vmalloc_to_mfn(info->mfns);
   1.314 -	info->page->pd[1] = 0;
   1.315 -	info->page->width = XENFB_WIDTH;
   1.316 -	info->page->height = XENFB_HEIGHT;
   1.317 -	info->page->depth = XENFB_DEPTH;
   1.318 -	info->page->line_length = (info->page->depth / 8) * info->page->width;
   1.319 -	info->page->mem_length = xenfb_mem_len;
   1.320 +	for (i = 0; i * epd < info->nr_pages; i++)
   1.321 +		info->page->pd[i] = vmalloc_to_mfn(&info->mfns[i * epd]);
   1.322 +
   1.323 +	info->page->width = fb_info->var.xres;
   1.324 +	info->page->height = fb_info->var.yres;
   1.325 +	info->page->depth = fb_info->var.bits_per_pixel;
   1.326 +	info->page->line_length = fb_info->fix.line_length;
   1.327 +	info->page->mem_length = fb_info->fix.smem_len;
   1.328  	info->page->in_cons = info->page->in_prod = 0;
   1.329  	info->page->out_cons = info->page->out_prod = 0;
   1.330  }
   1.331 @@ -712,6 +831,11 @@ static void xenfb_backend_changed(struct
   1.332  			val = 0;
   1.333  		if (val)
   1.334  			info->update_wanted = 1;
   1.335 +
   1.336 +		if (xenbus_scanf(XBT_NIL, dev->otherend,
   1.337 +					"feature-resize", "%d", &val) < 0)
   1.338 +			val = 0;
   1.339 +		info->feature_resize = val;
   1.340  		break;
   1.341  
   1.342  	case XenbusStateClosing:
     2.1 --- a/drivers/xen/fbfront/xenkbd.c	Wed Mar 26 08:52:36 2008 +0000
     2.2 +++ b/drivers/xen/fbfront/xenkbd.c	Wed Mar 26 08:54:24 2008 +0000
     2.3 @@ -297,6 +297,16 @@ static void xenkbd_backend_changed(struc
     2.4  		 */
     2.5  		if (dev->state != XenbusStateConnected)
     2.6  			goto InitWait; /* no InitWait seen yet, fudge it */
     2.7 +
     2.8 +		/* Set input abs params to match backend screen res */
     2.9 +		if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
    2.10 +				   "width", "%d", &val) > 0 )
    2.11 +			input_set_abs_params(info->ptr, ABS_X, 0, val, 0, 0);
    2.12 +
    2.13 +		if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
    2.14 +				   "height", "%d", &val) > 0 )
    2.15 +			input_set_abs_params(info->ptr, ABS_Y, 0, val, 0, 0);
    2.16 +
    2.17  		break;
    2.18  
    2.19  	case XenbusStateClosing:
     3.1 --- a/include/xen/interface/io/fbif.h	Wed Mar 26 08:52:36 2008 +0000
     3.2 +++ b/include/xen/interface/io/fbif.h	Wed Mar 26 08:54:24 2008 +0000
     3.3 @@ -50,12 +50,28 @@ struct xenfb_update
     3.4      int32_t height; /* rect height */
     3.5  };
     3.6  
     3.7 +/*
     3.8 + * Framebuffer resize notification event
     3.9 + * Capable backend sets feature-resize in xenstore.
    3.10 + */
    3.11 +#define XENFB_TYPE_RESIZE 3
    3.12 +
    3.13 +struct xenfb_resize
    3.14 +{
    3.15 +    uint8_t type;    /* XENFB_TYPE_RESIZE */
    3.16 +    int32_t width;   /* width in pixels */
    3.17 +    int32_t height;  /* height in pixels */
    3.18 +    int32_t stride;  /* stride in bytes */
    3.19 +    int32_t depth;   /* depth in bits */
    3.20 +};
    3.21 +
    3.22  #define XENFB_OUT_EVENT_SIZE 40
    3.23  
    3.24  union xenfb_out_event
    3.25  {
    3.26      uint8_t type;
    3.27      struct xenfb_update update;
    3.28 +    struct xenfb_resize resize;
    3.29      char pad[XENFB_OUT_EVENT_SIZE];
    3.30  };
    3.31  
    3.32 @@ -109,15 +125,17 @@ struct xenfb_page
    3.33       * Each directory page holds PAGE_SIZE / sizeof(*pd)
    3.34       * framebuffer pages, and can thus map up to PAGE_SIZE *
    3.35       * PAGE_SIZE / sizeof(*pd) bytes.  With PAGE_SIZE == 4096 and
    3.36 -     * sizeof(unsigned long) == 4, that's 4 Megs.  Two directory
    3.37 -     * pages should be enough for a while.
    3.38 +     * sizeof(unsigned long) == 4/8, that's 4 Megs 32 bit and 2 Megs
    3.39 +     * 64 bit.  256 directories give enough room for a 512 Meg
    3.40 +     * framebuffer with a max resolution of 12,800x10,240.  Should
    3.41 +     * be enough for a while with room leftover for expansion.
    3.42       */
    3.43 -    unsigned long pd[2];
    3.44 +    unsigned long pd[256];
    3.45  };
    3.46  
    3.47  /*
    3.48 - * Wart: xenkbd needs to know resolution.  Put it here until a better
    3.49 - * solution is found, but don't leak it to the backend.
    3.50 + * Wart: xenkbd needs to know default resolution.  Put it here until a
    3.51 + * better solution is found, but don't leak it to the backend.
    3.52   */
    3.53  #ifdef __KERNEL__
    3.54  #define XENFB_WIDTH 800