#include <linux/uaccess.h>
 #include <linux/mm.h>
 #include <linux/fb.h>
-#include <linux/mutex.h>
 #include <linux/vmalloc.h>
 
 #include "udlfb.h"
 }
 
 /*
- * Once you send this command, the DisplayLink framebuffer gets driven to the
- * display.
+ * On/Off for driving the DisplayLink framebuffer to the display
  */
-static char *dlfb_enable_hvsync(char *buf)
+static char *dlfb_enable_hvsync(char *buf, bool enable)
 {
-       return dlfb_set_register(buf, 0x1F, 0x00);
+       if (enable)
+               return dlfb_set_register(buf, 0x1F, 0x00);
+       else
+               return dlfb_set_register(buf, 0x1F, 0x01);
 }
 
 static char *dlfb_set_color_depth(char *buf, u8 selection)
        char *wrptr;
        int retval = 0;
        int writesize;
+       struct urb *urb;
 
-       buf = dev->buf;
+       if (!atomic_read(&dev->usb_active))
+               return -EPERM;
+
+       urb = dlfb_get_urb(dev);
+       if (!urb)
+               return -ENOMEM;
+       buf = (char *) urb->transfer_buffer;
 
        /*
        * This first section has to do with setting the base address on the
        wrptr = dlfb_set_base8bpp(wrptr, dev->info->fix.smem_len);
 
        wrptr = dlfb_set_vid_cmds(wrptr, var);
-       wrptr = dlfb_enable_hvsync(wrptr);
+       wrptr = dlfb_enable_hvsync(wrptr, true);
        wrptr = dlfb_vidreg_unlock(wrptr);
 
        writesize = wrptr - buf;
 
-       mutex_lock(&dev->bulk_mutex);
-       if (!dev->interface) {          /* disconnect() was called */
-               mutex_unlock(&dev->bulk_mutex);
-               retval = -ENODEV;
-               goto error;
-       }
-
-       retval = dlfb_bulk_msg(dev, writesize);
-       mutex_unlock(&dev->bulk_mutex);
-       if (retval) {
-               dev_err(&dev->udev->dev, "Problem %d with submit write bulk.\n",
-                                       retval);
-               goto error;
-       }
-
-       return 0;
+       retval = dlfb_submit_urb(dev, urb, writesize);
 
-error:
        return retval;
 }
 
-
-/*
- * Query EDID from the handware, then hand it off to fbdev's edid parse
- * routine which should give us back a filled in screeninfo structure.
- */
-static int dlfb_get_var_from_edid(struct dlfb_data *dev,
-                                       struct fb_var_screeninfo *var)
-{
-       int ret;
-
-       dlfb_edid(dev);
-       ret = fb_parse_edid(dev->edid, var);
-
-       return ret;
-}
-
 static int dlfb_ops_mmap(struct fb_info *info, struct vm_area_struct *vma)
 {
        unsigned long start = vma->vm_start;
 
 }
 
-/* ioctl structure */
-struct dloarea {
-       int x, y;
-       int w, h;
-       int x2, y2;
-};
-
-static struct usb_driver dlfb_driver;
-
-/* thanks to Henrik Bjerregaard Pedersen for this function */
-static char *rle_compress16(uint16_t * src, char *dst, int rem)
+/*
+ * Trims identical data from front and back of line
+ * Sets new front buffer address and width
+ * And returns byte count of identical pixels
+ * Assumes CPU natural alignment (unsigned long)
+ * for back and front buffer ptrs and width
+ */
+static int dlfb_trim_hline(const u8 *bback, const u8 **bfront, int *width_bytes)
 {
+       int j, k;
+       const unsigned long *back = (const unsigned long *) bback;
+       const unsigned long *front = (const unsigned long *) *bfront;
+       const int width = *width_bytes / sizeof(unsigned long);
+       int identical = width;
+       int start = width;
+       int end = width;
+
+       prefetch((void *) front);
+       prefetch((void *) back);
+
+       for (j = 0; j < width; j++) {
+               if (back[j] != front[j]) {
+                       start = j;
+                       break;
+               }
+       }
 
-       int rl;
-       uint16_t pix0;
-       char *end_if_raw = dst + 6 + 2 * rem;
-
-       dst += 6; /* header will be filled in if RLE is worth it */
-
-       while (rem && dst < end_if_raw) {
-               char *start = (char *)src;
-
-               pix0 = *src++;
-               rl = 1;
-               rem--;
-               while (rem && *src == pix0)
-                       rem--, rl++, src++;
-               *dst++ = rl;
-               *dst++ = start[1];
-               *dst++ = start[0];
+       for (k = width - 1; k > j; k--) {
+               if (back[k] != front[k]) {
+                       end = k+1;
+                       break;
+               }
        }
 
-       return dst;
+       identical = start + (width - end);
+       *bfront = (u8 *) &front[start];
+       *width_bytes = (end - start) * sizeof(unsigned long);
+
+       return identical * sizeof(unsigned long);
 }
 
 /*
-Thanks to Henrik Bjerregaard Pedersen for rle implementation
-and code refactoring. Next step is huffman compression.
+Render a command stream for an encoded horizontal line segment of pixels.
+
+A command buffer holds several commands.
+It always begins with a fresh command header
+(the protocol doesn't require this, but we enforce it to allow
+multiple buffers to be potentially encoded and sent in parallel).
+A single command encodes one contiguous horizontal line of pixels
+
+The function relies on the client to do all allocation, so that
+rendering can be done directly to output buffers (e.g. USB URBs).
+The function fills the supplied command buffer, providing information
+on where it left off, so the client may call in again with additional
+buffers if the line will take several buffers to complete.
+
+A single command can transmit a maximum of 256 pixels,
+regardless of the compression ratio (protocol design limit).
+To the hardware, 0 for a size byte means 256
+
+Rather than 256 pixel commands which are either rl or raw encoded,
+the rlx command simply assumes alternating raw and rl spans within one cmd.
+This has a slightly larger header overhead, but produces more even results.
+It also processes all data (read and write) in a single pass.
+Performance benchmarks of common cases show it having just slightly better
+compression than 256 pixel raw -or- rle commands, with similar CPU consumpion.
+But for very rl friendly data, will compress not quite as well.
 */
-
-static int
-image_blit(struct dlfb_data *dev_info, int x, int y, int width, int height,
-          char *data)
+static void dlfb_compress_hline(
+       const uint16_t **pixel_start_ptr,
+       const uint16_t *const pixel_end,
+       uint32_t *device_address_ptr,
+       uint8_t **command_buffer_ptr,
+       const uint8_t *const cmd_buffer_end)
 {
-
-       int i, j, base;
-       int rem = width;
-       int ret;
-
-       int firstdiff, thistime;
-
-       char *bufptr;
-
-       if (x + width > dev_info->info->var.xres)
-               return -EINVAL;
-
-       if (y + height > dev_info->info->var.yres)
-               return -EINVAL;
-
-       mutex_lock(&dev_info->bulk_mutex);
-
-       base =
-           dev_info->base16 + ((dev_info->info->var.xres * 2 * y) + (x * 2));
-
-       data += (dev_info->info->var.xres * 2 * y) + (x * 2);
-
-       /* printk("IMAGE_BLIT\n"); */
-
-       bufptr = dev_info->buf;
-
-       for (i = y; i < y + height; i++) {
-
-               if (dev_info->bufend - bufptr < BUF_HIGH_WATER_MARK) {
-                       ret = dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
-                       bufptr = dev_info->buf;
-               }
-
-               rem = width;
-
-               /* printk("WRITING LINE %d\n", i); */
-
-               while (rem) {
-
-                       if (dev_info->bufend - bufptr < BUF_HIGH_WATER_MARK) {
-                               ret =
-                                   dlfb_bulk_msg(dev_info,
-                                                 bufptr - dev_info->buf);
-                               bufptr = dev_info->buf;
-                       }
-                       /* number of pixels to consider this time */
-                       thistime = rem;
-                       if (thistime > 255)
-                               thistime = 255;
-
-                       if (dev_info->backing_buffer) {
-                               /* find first pixel that has changed */
-                               firstdiff = -1;
-                               for (j = 0; j < thistime * 2; j++) {
-                                       if (dev_info->backing_buffer
-                                           [base - dev_info->base16 + j]
-                                           != data[j]) {
-                                               firstdiff = j / 2;
-                                               break;
-                                       }
+       const uint16_t *pixel = *pixel_start_ptr;
+       uint32_t dev_addr  = *device_address_ptr;
+       uint8_t *cmd = *command_buffer_ptr;
+       const int bpp = 2;
+
+       while ((pixel_end > pixel) &&
+              (cmd_buffer_end - MIN_RLX_CMD_BYTES > cmd)) {
+               uint8_t *raw_pixels_count_byte = 0;
+               uint8_t *cmd_pixels_count_byte = 0;
+               const uint16_t *raw_pixel_start = 0;
+               const uint16_t *cmd_pixel_start, *cmd_pixel_end = 0;
+               const uint32_t be_dev_addr = cpu_to_be32(dev_addr);
+
+               prefetchw((void *) cmd); /* pull in one cache line at least */
+
+               *cmd++ = 0xAF;
+               *cmd++ = 0x6B;
+               *cmd++ = (uint8_t) ((be_dev_addr >> 8) & 0xFF);
+               *cmd++ = (uint8_t) ((be_dev_addr >> 16) & 0xFF);
+               *cmd++ = (uint8_t) ((be_dev_addr >> 24) & 0xFF);
+
+               cmd_pixels_count_byte = cmd++; /*  we'll know this later */
+               cmd_pixel_start = pixel;
+
+               raw_pixels_count_byte = cmd++; /*  we'll know this later */
+               raw_pixel_start = pixel;
+
+               cmd_pixel_end = pixel + min(MAX_CMD_PIXELS + 1,
+                       min((int)(pixel_end - pixel),
+                           (int)(cmd_buffer_end - cmd) / bpp));
+
+               prefetch_range((void *) pixel, (cmd_pixel_end - pixel) * bpp);
+
+               while (pixel < cmd_pixel_end) {
+                       const uint16_t * const repeating_pixel = pixel;
+
+                       *(uint16_t *)cmd = cpu_to_be16p(pixel);
+                       cmd += 2;
+                       pixel++;
+
+                       if (unlikely((pixel < cmd_pixel_end) &&
+                                    (*pixel == *repeating_pixel))) {
+                               /* go back and fill in raw pixel count */
+                               *raw_pixels_count_byte = ((repeating_pixel -
+                                               raw_pixel_start) + 1) & 0xFF;
+
+                               while ((pixel < cmd_pixel_end)
+                                      && (*pixel == *repeating_pixel)) {
+                                       pixel++;
                                }
 
-                       } else {
-                               firstdiff = 0;
+                               /* immediately after raw data is repeat byte */
+                               *cmd++ = ((pixel - repeating_pixel) - 1) & 0xFF;
 
+                               /* Then start another raw pixel span */
+                               raw_pixel_start = pixel;
+                               raw_pixels_count_byte = cmd++;
                        }
-
-                       if (firstdiff >= 0) {
-                               char *end_of_rle;
-
-                               end_of_rle =
-                                   rle_compress16((uint16_t *) (data +
-                                                                firstdiff * 2),
-                                                  bufptr,
-                                                  thistime - firstdiff);
-
-                               if (end_of_rle <
-                                   bufptr + 6 + 2 * (thistime - firstdiff)) {
-                                       bufptr[0] = 0xAF;
-                                       bufptr[1] = 0x69;
-
-                                       bufptr[2] =
-                                           (char)((base +
-                                                   firstdiff * 2) >> 16);
-                                       bufptr[3] =
-                                           (char)((base + firstdiff * 2) >> 8);
-                                       bufptr[4] =
-                                           (char)(base + firstdiff * 2);
-                                       bufptr[5] = thistime - firstdiff;
-
-                                       bufptr = end_of_rle;
-
-                               } else {
-                                       /* fallback to raw (or other?) */
-                                       *bufptr++ = 0xAF;
-                                       *bufptr++ = 0x68;
-
-                                       *bufptr++ =
-                                           (char)((base +
-                                                   firstdiff * 2) >> 16);
-                                       *bufptr++ =
-                                           (char)((base + firstdiff * 2) >> 8);
-                                       *bufptr++ =
-                                           (char)(base + firstdiff * 2);
-                                       *bufptr++ = thistime - firstdiff;
-                                       for (j = firstdiff * 2;
-                                            j < thistime * 2; j += 2) {
-                                               *bufptr++ = data[j + 1];
-                                               *bufptr++ = data[j];
-                                       }
-                               }
-                       }
-
-                       base += thistime * 2;
-                       data += thistime * 2;
-                       rem -= thistime;
                }
 
-               if (dev_info->backing_buffer)
-                       memcpy(dev_info->backing_buffer +
-                              (base - dev_info->base16) -
-                              (width * 2), data - (width * 2), width * 2);
-
-               base += (dev_info->info->var.xres * 2) - (width * 2);
-               data += (dev_info->info->var.xres * 2) - (width * 2);
+               if (pixel > raw_pixel_start) {
+                       /* finalize last RAW span */
+                       *raw_pixels_count_byte = (pixel-raw_pixel_start) & 0xFF;
+               }
 
+               *cmd_pixels_count_byte = (pixel - cmd_pixel_start) & 0xFF;
+               dev_addr += (pixel - cmd_pixel_start) * bpp;
        }
 
-       if (bufptr > dev_info->buf) {
-               ret = dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
+       if (cmd_buffer_end <= MIN_RLX_CMD_BYTES + cmd) {
+               /* Fill leftover bytes with no-ops */
+               if (cmd_buffer_end > cmd)
+                       memset(cmd, 0xAF, cmd_buffer_end - cmd);
+               cmd = (uint8_t *) cmd_buffer_end;
        }
 
-       mutex_unlock(&dev_info->bulk_mutex);
-
-       return base;
+       *command_buffer_ptr = cmd;
+       *pixel_start_ptr = pixel;
+       *device_address_ptr = dev_addr;
 
+       return;
 }
 
-static int
-draw_rect(struct dlfb_data *dev_info, int x, int y, int width, int height,
-         unsigned char red, unsigned char green, unsigned char blue)
+/*
+ * There are 3 copies of every pixel: The front buffer that the fbdev
+ * client renders to, the actual framebuffer across the USB bus in hardware
+ * (that we can only write to, slowly, and can never read), and (optionally)
+ * our shadow copy that tracks what's been sent to that hardware buffer.
+ */
+static void dlfb_render_hline(struct dlfb_data *dev, struct urb **urb_ptr,
+                             const char *front, char **urb_buf_ptr,
+                             u32 byte_offset, u32 byte_width,
+                             int *ident_ptr, int *sent_ptr)
 {
+       const u8 *line_start, *line_end, *next_pixel;
+       u32 dev_addr = dev->base16 + byte_offset;
+       struct urb *urb = *urb_ptr;
+       u8 *cmd = *urb_buf_ptr;
+       u8 *cmd_end = (u8 *) urb->transfer_buffer + urb->transfer_buffer_length;
+
+       line_start = (u8 *) (front + byte_offset);
+       next_pixel = line_start;
+       line_end = next_pixel + byte_width;
+
+       if (dev->backing_buffer) {
+               int offset;
+               const u8 *back_start = (u8 *) (dev->backing_buffer
+                                               + byte_offset);
+
+               *ident_ptr += dlfb_trim_hline(back_start, &next_pixel,
+                       &byte_width);
+
+               offset = next_pixel - line_start;
+               line_end = next_pixel + byte_width;
+               dev_addr += offset;
+               back_start += offset;
+               line_start += offset;
+
+               memcpy((char *)back_start, (char *) line_start,
+                      byte_width);
+       }
 
-       int i, j, base;
-       int ret;
-       unsigned short col =
-           (((((red) & 0xF8) | ((green) >> 5)) & 0xFF) << 8) +
-           (((((green) & 0x1C) << 3) | ((blue) >> 3)) & 0xFF);
-       int rem = width;
-
-       char *bufptr;
-
-       if (x + width > dev_info->info->var.xres)
-               return -EINVAL;
-
-       if (y + height > dev_info->info->var.yres)
-               return -EINVAL;
-
-       mutex_lock(&dev_info->bulk_mutex);
-
-       base = dev_info->base16 + (dev_info->info->var.xres * 2 * y) + (x * 2);
-
-       bufptr = dev_info->buf;
-
-       for (i = y; i < y + height; i++) {
-
-               if (dev_info->backing_buffer) {
-                       for (j = 0; j < width * 2; j += 2) {
-                               dev_info->backing_buffer
-                                       [base - dev_info->base16 + j] =
-                                       (char)(col >> 8);
-                               dev_info->backing_buffer
-                                       [base - dev_info->base16 + j + 1] =
-                                       (char)(col);
-                       }
-               }
-
-               if (dev_info->bufend - bufptr < BUF_HIGH_WATER_MARK) {
-                       ret = dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
-                       bufptr = dev_info->buf;
-               }
-
-               rem = width;
-
-               while (rem) {
-
-                       if (dev_info->bufend - bufptr < BUF_HIGH_WATER_MARK) {
-                               ret =
-                                   dlfb_bulk_msg(dev_info,
-                                                 bufptr - dev_info->buf);
-                               bufptr = dev_info->buf;
-                       }
-
-                       *bufptr++ = 0xAF;
-                       *bufptr++ = 0x69;
-
-                       *bufptr++ = (char)(base >> 16);
-                       *bufptr++ = (char)(base >> 8);
-                       *bufptr++ = (char)(base);
-
-                       if (rem > 255) {
-                               *bufptr++ = 255;
-                               *bufptr++ = 255;
-                               rem -= 255;
-                               base += 255 * 2;
-                       } else {
-                               *bufptr++ = rem;
-                               *bufptr++ = rem;
-                               base += rem * 2;
-                               rem = 0;
-                       }
-
-                       *bufptr++ = (char)(col >> 8);
-                       *bufptr++ = (char)(col);
-
+       while (next_pixel < line_end) {
+
+               dlfb_compress_hline((const uint16_t **) &next_pixel,
+                            (const uint16_t *) line_end, &dev_addr,
+                       (u8 **) &cmd, (u8 *) cmd_end);
+
+               if (cmd >= cmd_end) {
+                       int len = cmd - (u8 *) urb->transfer_buffer;
+                       if (dlfb_submit_urb(dev, urb, len))
+                               return; /* lost pixels is set */
+                       *sent_ptr += len;
+                       urb = dlfb_get_urb(dev);
+                       if (!urb)
+                               return; /* lost_pixels is set */
+                       *urb_ptr = urb;
+                       cmd = urb->transfer_buffer;
+                       cmd_end = &cmd[urb->transfer_buffer_length];
                }
-
-               base += (dev_info->info->var.xres * 2) - (width * 2);
-
        }
 
-       if (bufptr > dev_info->buf)
-               ret = dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
-
-       mutex_unlock(&dev_info->bulk_mutex);
-
-       return 1;
+       *urb_buf_ptr = cmd;
 }
 
-static void swapfb(struct dlfb_data *dev_info)
+int dlfb_handle_damage(struct dlfb_data *dev, int x, int y,
+              int width, int height, char *data)
 {
-
-       int tmpbase;
-       char *bufptr;
-
-       mutex_lock(&dev_info->bulk_mutex);
-
-       tmpbase = dev_info->base16;
-
-       dev_info->base16 = dev_info->base16d;
-       dev_info->base16d = tmpbase;
-
-       bufptr = dev_info->buf;
-
-       bufptr = dlfb_set_register(bufptr, 0xFF, 0x00);
-
-       /* set addresses */
-       bufptr =
-           dlfb_set_register(bufptr, 0x20, (char)(dev_info->base16 >> 16));
-       bufptr = dlfb_set_register(bufptr, 0x21, (char)(dev_info->base16 >> 8));
-       bufptr = dlfb_set_register(bufptr, 0x22, (char)(dev_info->base16));
-
-       bufptr = dlfb_set_register(bufptr, 0xFF, 0x00);
-
-       dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
-
-       mutex_unlock(&dev_info->bulk_mutex);
-}
-
-static int copyfb(struct dlfb_data *dev_info)
-{
-       int base;
-       int source;
-       int rem;
        int i, ret;
+       char *cmd;
+       cycles_t start_cycles, end_cycles;
+       int bytes_sent = 0;
+       int bytes_identical = 0;
+       struct urb *urb;
+       int aligned_x;
 
-       char *bufptr;
-
-       base = dev_info->base16d;
-
-       mutex_lock(&dev_info->bulk_mutex);
-
-       source = dev_info->base16;
-
-       bufptr = dev_info->buf;
-
-       for (i = 0; i < dev_info->info->var.yres; i++) {
-
-               if (dev_info->bufend - bufptr < BUF_HIGH_WATER_MARK) {
-                       ret = dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
-                       bufptr = dev_info->buf;
-               }
-
-               rem = dev_info->info->var.xres;
-
-               while (rem) {
-
-                       if (dev_info->bufend - bufptr < BUF_HIGH_WATER_MARK) {
-                               ret =
-                                   dlfb_bulk_msg(dev_info,
-                                                 bufptr - dev_info->buf);
-                               bufptr = dev_info->buf;
-
-                       }
+       start_cycles = get_cycles();
 
-                       *bufptr++ = 0xAF;
-                       *bufptr++ = 0x6A;
+       aligned_x = DL_ALIGN_DOWN(x, sizeof(unsigned long));
+       width = DL_ALIGN_UP(width + (x-aligned_x), sizeof(unsigned long));
+       x = aligned_x;
 
-                       *bufptr++ = (char)(base >> 16);
-                       *bufptr++ = (char)(base >> 8);
-                       *bufptr++ = (char)(base);
+       if ((width <= 0) ||
+           (x + width > dev->info->var.xres) ||
+           (y + height > dev->info->var.yres))
+               return -EINVAL;
 
-                       if (rem > 255) {
-                               *bufptr++ = 255;
-                               *bufptr++ = (char)(source >> 16);
-                               *bufptr++ = (char)(source >> 8);
-                               *bufptr++ = (char)(source);
+       if (!atomic_read(&dev->usb_active))
+               return 0;
 
-                               rem -= 255;
-                               base += 255 * 2;
-                               source += 255 * 2;
+       urb = dlfb_get_urb(dev);
+       if (!urb)
+               return 0;
+       cmd = urb->transfer_buffer;
 
-                       } else {
-                               *bufptr++ = rem;
-                               *bufptr++ = (char)(source >> 16);
-                               *bufptr++ = (char)(source >> 8);
-                               *bufptr++ = (char)(source);
+       for (i = y; i < y + height ; i++) {
+               const int line_offset = dev->info->fix.line_length * i;
+               const int byte_offset = line_offset + (x * BPP);
 
-                               base += rem * 2;
-                               source += rem * 2;
-                               rem = 0;
-                       }
-               }
+               dlfb_render_hline(dev, &urb, (char *) dev->info->fix.smem_start,
+                                 &cmd, byte_offset, width * BPP,
+                                 &bytes_identical, &bytes_sent);
        }
 
-       if (bufptr > dev_info->buf)
-               ret = dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
-
-       mutex_unlock(&dev_info->bulk_mutex);
-
-       return 1;
+       if (cmd > (char *) urb->transfer_buffer) {
+               /* Send partial buffer remaining before exiting */
+               int len = cmd - (char *) urb->transfer_buffer;
+               ret = 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(width*height*2, &dev->bytes_rendered);
+       end_cycles = get_cycles();
+       atomic_add(((unsigned int) ((end_cycles - start_cycles)
+                   >> 10)), /* Kcycles */
+                  &dev->cpu_kcycles_used);
 
+       return 0;
 }
 
-static int
-copyarea(struct dlfb_data *dev_info, int dx, int dy, int sx, int sy,
-        int width, int height)
+/* hardware has native COPY command (see libdlo), but not worth it for fbcon */
+static void dlfb_ops_copyarea(struct fb_info *info,
+                               const struct fb_copyarea *area)
 {
-       int base;
-       int source;
-       int rem;
-       int i, ret;
-
-       char *bufptr;
-
-       if (dx + width > dev_info->info->var.xres)
-               return -EINVAL;
 
-       if (dy + height > dev_info->info->var.yres)
-               return -EINVAL;
-
-       mutex_lock(&dev_info->bulk_mutex);
-
-       base =
-           dev_info->base16 + (dev_info->info->var.xres * 2 * dy) + (dx * 2);
-       source = (dev_info->info->var.xres * 2 * sy) + (sx * 2);
-
-       bufptr = dev_info->buf;
-
-       for (i = sy; i < sy + height; i++) {
-
-               memcpy(dev_info->backing_buffer + base - dev_info->base16,
-                      dev_info->backing_buffer + source, width * 2);
-
-               if (dev_info->bufend - bufptr < BUF_HIGH_WATER_MARK) {
-                       ret = dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
-                       bufptr = dev_info->buf;
-               }
-
-               rem = width;
-
-               while (rem) {
-
-                       if (dev_info->bufend - bufptr < BUF_HIGH_WATER_MARK) {
-                               ret =
-                                   dlfb_bulk_msg(dev_info,
-                                                 bufptr - dev_info->buf);
-                               bufptr = dev_info->buf;
-                       }
+       struct dlfb_data *dev = info->par;
 
-                       *bufptr++ = 0xAF;
-                       *bufptr++ = 0x6A;
+#if defined CONFIG_FB_SYS_COPYAREA || defined CONFIG_FB_SYS_COPYAREA_MODULE
 
-                       *bufptr++ = (char)(base >> 16);
-                       *bufptr++ = (char)(base >> 8);
-                       *bufptr++ = (char)(base);
+       sys_copyarea(info, area);
 
-                       if (rem > 255) {
-                               *bufptr++ = 255;
-                               *bufptr++ = (char)(source >> 16);
-                               *bufptr++ = (char)(source >> 8);
-                               *bufptr++ = (char)(source);
+       dlfb_handle_damage(dev, area->dx, area->dy,
+                       area->width, area->height, info->screen_base);
+#endif
+       atomic_inc(&dev->copy_count);
 
-                               rem -= 255;
-                               base += 255 * 2;
-                               source += 255 * 2;
+}
 
-                       } else {
-                               *bufptr++ = rem;
-                               *bufptr++ = (char)(source >> 16);
-                               *bufptr++ = (char)(source >> 8);
-                               *bufptr++ = (char)(source);
+static void dlfb_ops_imageblit(struct fb_info *info,
+                               const struct fb_image *image)
+{
+       struct dlfb_data *dev = info->par;
 
-                               base += rem * 2;
-                               source += rem * 2;
-                               rem = 0;
-                       }
-               }
+#if defined CONFIG_FB_SYS_IMAGEBLIT || defined CONFIG_FB_SYS_IMAGEBLIT_MODULE
 
-               base += (dev_info->info->var.xres * 2) - (width * 2);
-               source += (dev_info->info->var.xres * 2) - (width * 2);
-       }
+       sys_imageblit(info, image);
 
-       if (bufptr > dev_info->buf)
-               ret = dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
+       dlfb_handle_damage(dev, image->dx, image->dy,
+                       image->width, image->height, info->screen_base);
 
-       mutex_unlock(&dev_info->bulk_mutex);
+#endif
 
-       return 1;
+       atomic_inc(&dev->blit_count);
 }
 
-static void dlfb_ops_copyarea(struct fb_info *info,
-                       const struct fb_copyarea *area)
+static void dlfb_ops_fillrect(struct fb_info *info,
+                         const struct fb_fillrect *rect)
 {
        struct dlfb_data *dev = info->par;
 
-       copyarea(dev, area->dx, area->dy, area->sx, area->sy, area->width,
-                area->height);
-}
-
-static void dlfb_ops_imageblit(struct fb_info *info,
-                       const struct fb_image *image)
-{
-       int ret;
-       struct dlfb_data *dev = info->par;
-       cfb_imageblit(info, image);
-       ret =
-           image_blit(dev, image->dx, image->dy, image->width, image->height,
-                      info->screen_base);
-}
+#if defined CONFIG_FB_SYS_FILLRECT || defined CONFIG_FB_SYS_FILLRECT_MODULE
 
-static void dlfb_ops_fillrect(struct fb_info *info,
-                         const struct fb_fillrect *region)
-{
+       sys_fillrect(info, rect);
 
-       unsigned char red, green, blue;
-       struct dlfb_data *dev = info->par;
+       dlfb_handle_damage(dev, rect->dx, rect->dy, rect->width,
+                             rect->height, info->screen_base);
+#endif
 
-       memcpy(&red, ®ion->color, 1);
-       memcpy(&green, ®ion->color + 1, 1);
-       memcpy(&blue, ®ion->color + 2, 1);
-       draw_rect(dev, region->dx, region->dy, region->width, region->height,
-                 red, green, blue);
-       /* printk("FILL RECT %d %d !!!\n", region->dx, region->dy); */
+       atomic_inc(&dev->fill_count);
 
 }
 
 }
 
 static int dlfb_ops_ioctl(struct fb_info *info, unsigned int cmd,
-                       unsigned long arg)
+                               unsigned long arg)
 {
-       struct dlfb_data *dev_info = info->par;
+
+       struct dlfb_data *dev = info->par;
        struct dloarea *area = NULL;
 
-       if (cmd == 0xAD) {
+       if (!atomic_read(&dev->usb_active))
+               return 0;
+
+       /* TODO: Update X server to get this from sysfs instead */
+       if (cmd == DLFB_IOCTL_RETURN_EDID) {
                char *edid = (char *)arg;
-               dlfb_edid(dev_info);
-               if (copy_to_user(edid, dev_info->edid, 128)) {
+               dlfb_get_edid(dev);
+               if (copy_to_user(edid, dev->edid, sizeof(dev->edid)))
                        return -EFAULT;
-               }
                return 0;
        }
 
-       if (cmd == 0xAA || cmd == 0xAB || cmd == 0xAC) {
+       /* TODO: Help propose a standard fb.h ioctl to report mmap damage */
+       if (cmd == DLFB_IOCTL_REPORT_DAMAGE) {
 
                area = (struct dloarea *)arg;
 
 
                if (area->y > info->var.yres)
                        area->y = info->var.yres;
-       }
 
-       if (cmd == 0xAA) {
-               image_blit(dev_info, area->x, area->y, area->w, area->h,
+               atomic_set(&dev->use_defio, 0);
+
+               dlfb_handle_damage(dev, area->x, area->y, area->w, area->h,
                           info->screen_base);
+               atomic_inc(&dev->damage_count);
        }
-       if (cmd == 0xAC) {
-               copyfb(dev_info);
-               image_blit(dev_info, area->x, area->y, area->w, area->h,
-                          info->screen_base);
-               swapfb(dev_info);
-       } else if (cmd == 0xAB) {
-
-               if (area->x2 < 0)
-                       area->x2 = 0;
 
-               if (area->y2 < 0)
-                       area->y2 = 0;
-
-               copyarea(dev_info,
-                        area->x2, area->y2, area->x, area->y, area->w,
-                        area->h);
-       }
        return 0;
 }
 
 static int dlfb_ops_release(struct fb_info *info, int user)
 {
        struct dlfb_data *dev_info = info->par;
-       image_blit(dev_info, 0, 0, info->var.xres, info->var.yres,
-                  info->screen_base);
        return 0;
 }
 
 
 static int dlfb_ops_blank(int blank_mode, struct fb_info *info)
 {
-       struct dlfb_data *dev_info = info->par;
-       char *bufptr = dev_info->buf;
+       struct dlfb_data *dev = info->par;
+       char *bufptr;
+       struct urb *urb;
 
-       bufptr = dlfb_set_register(bufptr, 0xFF, 0x00);
+       urb = dlfb_get_urb(dev);
+       if (!urb)
+               return 0;
+       bufptr = (char *) urb->transfer_buffer;
+
+       /* overloading usb_active.  UNBLANK can conflict with teardown */
+
+       bufptr = dlfb_vidreg_lock(bufptr);
        if (blank_mode != FB_BLANK_UNBLANK) {
-               bufptr = dlfb_set_register(bufptr, 0x1F, 0x01);
+               atomic_set(&dev->usb_active, 0);
+               bufptr = dlfb_enable_hvsync(bufptr, false);
        } else {
-               bufptr = dlfb_set_register(bufptr, 0x1F, 0x00);
+               atomic_set(&dev->usb_active, 1);
+               bufptr = dlfb_enable_hvsync(bufptr, true);
        }
-       bufptr = dlfb_set_register(bufptr, 0xFF, 0xFF);
+       bufptr = dlfb_vidreg_unlock(bufptr);
 
-       dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
+       dlfb_submit_urb(dev, urb, bufptr - (char *) urb->transfer_buffer);
 
        return 0;
 }
        kref_init(&dev->kref); /* matching kref_put in usb .disconnect fn */
        kref_get(&dev->kref); /* matching kref_put in .fb_destroy function*/
 
-       mutex_init(&dev->bulk_mutex);
-
        dev->udev = usbdev;
        dev->gdev = &usbdev->dev; /* our generic struct device * */
        usb_set_intfdata(interface, dev);
 
        mutex_init(&dev->fb_open_lock);
 
-       /*
-        * TODO: replace single 64K buffer with buffer list
-        * and async dispatch
-        */
-       dev->buf = kmalloc(BUF_SIZE, GFP_KERNEL);
-       if (dev->buf == NULL) {
-               dl_err("unable to allocate memory for dlfb commands\n");
-               goto error;
-       }
-       dev->bufend = dev->buf + BUF_SIZE;
-
-       dev->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
-       usb_fill_bulk_urb(dev->tx_urb, dev->udev,
-                         usb_sndbulkpipe(dev->udev, 1), dev->buf, 0,
-                         dlfb_bulk_callback, dev);
-
        /* We don't register a new USB class. Our client interface is fbdev */
 
        /* allocates framebuffer driver structure, not framebuffer memory */
        dlfb_ops_set_par(info);
 
        /* paint greenscreen */
-/*
        pix_framebuffer = (u16 *) videomemory;
        for (i = 0; i < videomemorysize / 2; i++)
                pix_framebuffer[i] = 0x37e6;
 
        dlfb_handle_damage(dev, 0, 0, info->var.xres, info->var.yres,
                                videomemory);
-*/
+
        retval = register_framebuffer(info);
        if (retval < 0) {
                dl_err("register_framebuffer failed %d\n", retval);