#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);