staging: udlfb: add module options for console and fb_defio

Add module options for console and fb_defio

Convert fb_defio on/off switch to module option and add console option.

>From the command line, pass options to modprobe
modprobe udlfb defio=1 console=1

Or for permanent option, create file like /etc/modprobe.d/options with text
options udlfb defio=1 console=1

Accepted options:

fb_defio	Make use of the fb_defio (CONFIG_FB_DEFERRED_IO) kernel
		module to track changed areas of the framebuffer by page faults.
        	Standard fbdev applications that use mmap but that do not
		report damage, may be able to work with this enabled.
		Disabled by default because of overhead and other issues.

console		Allow fbcon to attach to udlfb provided framebuffers. This
		is disabled by default because fbcon will aggressively consume
		the first framebuffer it finds, which isn't usually what the
		user wants in the case of USB displays.

Signed-off-by: Bernie Thompson <bernie@plugable.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
diff --git a/drivers/staging/udlfb/udlfb.c b/drivers/staging/udlfb/udlfb.c
index b13869b..80a1e0e 100644
--- a/drivers/staging/udlfb/udlfb.c
+++ b/drivers/staging/udlfb/udlfb.c
@@ -59,19 +59,9 @@
 };
 MODULE_DEVICE_TABLE(usb, id_table);
 
-#ifndef CONFIG_FB_DEFERRED_IO
-#warning Please set CONFIG_FB_DEFFERRED_IO option to support generic fbdev apps
-#endif
-
-#ifndef CONFIG_FB_SYS_IMAGEBLIT
-#ifndef CONFIG_FB_SYS_IMAGEBLIT_MODULE
-#warning Please set CONFIG_FB_SYS_IMAGEBLIT option to support fb console
-#endif
-#endif
-
-#ifndef CONFIG_FB_MODE_HELPERS
-#warning CONFIG_FB_MODE_HELPERS required. Expect build break
-#endif
+/* module options */
+static int console;   /* Optionally allow fbcon to consume first framebuffer */
+static int fb_defio;  /* Optionally enable experimental fb_defio mmap support */
 
 /* dlfb keeps a list of urbs for efficient bulk transfers */
 static void dlfb_urb_completion(struct urb *urb);
@@ -695,6 +685,68 @@
 
 }
 
+#ifdef CONFIG_FB_DEFERRED_IO
+/*
+ * NOTE: fb_defio.c is holding info->fbdefio.mutex
+ *   Touching ANY framebuffer memory that triggers a page fault
+ *   in fb_defio will cause a deadlock, when it also tries to
+ *   grab the same mutex.
+ */
+static void dlfb_dpy_deferred_io(struct fb_info *info,
+				struct list_head *pagelist)
+{
+	struct page *cur;
+	struct fb_deferred_io *fbdefio = info->fbdefio;
+	struct dlfb_data *dev = info->par;
+	struct urb *urb;
+	char *cmd;
+	cycles_t start_cycles, end_cycles;
+	int bytes_sent = 0;
+	int bytes_identical = 0;
+	int bytes_rendered = 0;
+
+	if (!fb_defio)
+		return;
+
+	if (!atomic_read(&dev->usb_active))
+		return;
+
+	start_cycles = get_cycles();
+
+	urb = dlfb_get_urb(dev);
+	if (!urb)
+		return;
+
+	cmd = urb->transfer_buffer;
+
+	/* walk the written page list and render each to device */
+	list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+
+		dlfb_render_hline(dev, &urb, (char *) info->fix.smem_start,
+				  &cmd, cur->index << PAGE_SHIFT,
+				  PAGE_SIZE, &bytes_identical, &bytes_sent);
+		bytes_rendered += PAGE_SIZE;
+	}
+
+	if (cmd > (char *) urb->transfer_buffer) {
+		/* Send partial buffer remaining before exiting */
+		int len = cmd - (char *) urb->transfer_buffer;
+		dlfb_submit_urb(dev, urb, len);
+		bytes_sent += len;
+	} else
+		dlfb_urb_completion(urb);
+
+	atomic_add(bytes_sent, &dev->bytes_sent);
+	atomic_add(bytes_identical, &dev->bytes_identical);
+	atomic_add(bytes_rendered, &dev->bytes_rendered);
+	end_cycles = get_cycles();
+	atomic_add(((unsigned int) ((end_cycles - start_cycles)
+		    >> 10)), /* Kcycles */
+		   &dev->cpu_kcycles_used);
+}
+
+#endif
+
 static int dlfb_get_edid(struct dlfb_data *dev, char *edid, int len)
 {
 	int i;
@@ -758,8 +810,6 @@
 		if (area->y > info->var.yres)
 			area->y = info->var.yres;
 
-		atomic_set(&dev->use_defio, 0);
-
 		dlfb_handle_damage(dev, area->x, area->y, area->w, area->h,
 			   info->screen_base);
 	}
@@ -803,9 +853,13 @@
 {
 	struct dlfb_data *dev = info->par;
 
-/*	if (user == 0)
- *		We could special case kernel mode clients (fbcon) here
- */
+	/*
+	 * fbcon aggressively connects to first framebuffer it finds,
+	 * preventing other clients (X) from working properly. Usually
+	 * not what the user wants. Fail by default with option to enable.
+	 */
+	if ((user == 0) & (!console))
+		return -EBUSY;
 
 	/* If the USB device is gone, we don't accept new opens */
 	if (dev->virtualized)
@@ -816,7 +870,7 @@
 	kref_get(&dev->kref);
 
 #ifdef CONFIG_FB_DEFERRED_IO
-	if ((atomic_read(&dev->use_defio)) && (info->fbdefio == NULL)) {
+	if (fb_defio && (info->fbdefio == NULL)) {
 		/* enable defio */
 		info->fbdefio = &dlfb_defio;
 		fb_deferred_io_init(info);
@@ -1345,30 +1399,6 @@
 	return count;
 }
 
-static ssize_t use_defio_show(struct device *fbdev,
-				   struct device_attribute *a, char *buf) {
-	struct fb_info *fb_info = dev_get_drvdata(fbdev);
-	struct dlfb_data *dev = fb_info->par;
-	return snprintf(buf, PAGE_SIZE, "%d\n",
-			atomic_read(&dev->use_defio));
-}
-
-static ssize_t use_defio_store(struct device *fbdev,
-			   struct device_attribute *attr,
-			   const char *buf, size_t count)
-{
-	struct fb_info *fb_info = dev_get_drvdata(fbdev);
-	struct dlfb_data *dev = fb_info->par;
-
-	if (count > 0) {
-		if (buf[0] == '0')
-			atomic_set(&dev->use_defio, 0);
-		if (buf[0] == '1')
-			atomic_set(&dev->use_defio, 1);
-	}
-	return count;
-}
-
 static struct bin_attribute edid_attr = {
 	.attr.name = "edid",
 	.attr.mode = 0666,
@@ -1383,60 +1413,9 @@
 	__ATTR_RO(metrics_bytes_sent),
 	__ATTR_RO(metrics_cpu_kcycles_used),
 	__ATTR(metrics_reset, S_IWUGO, NULL, metrics_reset_store),
-	__ATTR_RW(use_defio),
 };
 
 #ifdef CONFIG_FB_DEFERRED_IO
-static void dlfb_dpy_deferred_io(struct fb_info *info,
-				struct list_head *pagelist)
-{
-	struct page *cur;
-	struct fb_deferred_io *fbdefio = info->fbdefio;
-	struct dlfb_data *dev = info->par;
-	struct urb *urb;
-	char *cmd;
-	cycles_t start_cycles, end_cycles;
-	int bytes_sent = 0;
-	int bytes_identical = 0;
-	int bytes_rendered = 0;
-
-	if (!atomic_read(&dev->use_defio))
-		return;
-
-	if (!atomic_read(&dev->usb_active))
-		return;
-
-	start_cycles = get_cycles();
-
-	urb = dlfb_get_urb(dev);
-	if (!urb)
-		return;
-	cmd = urb->transfer_buffer;
-
-	/* walk the written page list and render each to device */
-	list_for_each_entry(cur, &fbdefio->pagelist, lru) {
-		dlfb_render_hline(dev, &urb, (char *) info->fix.smem_start,
-				  &cmd, cur->index << PAGE_SHIFT,
-				  PAGE_SIZE, &bytes_identical, &bytes_sent);
-		bytes_rendered += PAGE_SIZE;
-	}
-
-	if (cmd > (char *) urb->transfer_buffer) {
-		/* Send partial buffer remaining before exiting */
-		int len = cmd - (char *) urb->transfer_buffer;
-		dlfb_submit_urb(dev, urb, len);
-		bytes_sent += len;
-	} else
-		dlfb_urb_completion(urb);
-
-	atomic_add(bytes_sent, &dev->bytes_sent);
-	atomic_add(bytes_identical, &dev->bytes_identical);
-	atomic_add(bytes_rendered, &dev->bytes_rendered);
-	end_cycles = get_cycles();
-	atomic_add(((unsigned int) ((end_cycles - start_cycles)
-		    >> 10)), /* Kcycles */
-		   &dev->cpu_kcycles_used);
-}
 
 static struct fb_deferred_io dlfb_defio = {
 	.delay          = 5,
@@ -1563,6 +1542,8 @@
 	dl_info("vid_%04x&pid_%04x&rev_%04x driver's dlfb_data struct at %p\n",
 		usbdev->descriptor.idVendor, usbdev->descriptor.idProduct,
 		usbdev->descriptor.bcdDevice, dev);
+	dl_info("console enable=%d\n", console);
+	dl_info("fb_defio enable=%d\n", fb_defio);
 
 	dev->sku_pixel_limit = 2048 * 1152; /* default to maximum */
 
@@ -1611,9 +1592,6 @@
 
 	/* ready to begin using device */
 
-#ifdef CONFIG_FB_DEFERRED_IO
-	atomic_set(&dev->use_defio, 1);
-#endif
 	atomic_set(&dev->usb_active, 1);
 	dlfb_select_std_channel(dev);
 
@@ -1893,6 +1871,12 @@
 	return ret;
 }
 
+module_param(console, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
+MODULE_PARM_DESC(console, "Allow fbcon to consume first framebuffer found");
+
+module_param(fb_defio, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
+MODULE_PARM_DESC(fb_defio, "Enable fb_defio mmap support. *Experimental*");
+
 MODULE_AUTHOR("Roberto De Ioris <roberto@unbit.it>, "
 	      "Jaya Kumar <jayakumar.lkml@gmail.com>, "
 	      "Bernie Thompson <bernie@plugable.com>");