BigW Consortium Gitlab

Commit 2a1cea91 by Shikai Chen

Performance improvements and bug fixs:

1. greatly improve the screen fps via RLE compression algorithm (for FW>=1.04) 2. fixed a dirty rect logic bug 3. fixed a urb transmission bug
parent d79255bb
/* /*
* RoboPeak Project * RoboPeak Project
* Copyright 2009 - 2013 * Copyright 2009 - 2013
* *
* RP USB Display * RP USB Display
* Protocol Def * Protocol Def
* *
* Initial Version by Shikai Chen * Initial Version by Shikai Chen
*/ */
#pragma once #pragma once
#define RPUSBDISP_DISP_CHANNEL_MAX_SIZE 64 //64bytes #define RPUSBDISP_DISP_CHANNEL_MAX_SIZE 64 //64bytes
#define RPUSBDISP_STATUS_CHANNEL_MAX_SIZE 32 //32bytes #define RPUSBDISP_STATUS_CHANNEL_MAX_SIZE 32 //32bytes
// -- Display Packets // -- Display Packets
#define RPUSBDISP_DISPCMD_NOPE 0 #define RPUSBDISP_DISPCMD_NOPE 0
#define RPUSBDISP_DISPCMD_FILL 1 #define RPUSBDISP_DISPCMD_FILL 1
#define RPUSBDISP_DISPCMD_BITBLT 2 #define RPUSBDISP_DISPCMD_BITBLT 2
#define RPUSBDISP_DISPCMD_RECT 3 #define RPUSBDISP_DISPCMD_RECT 3
#define RPUSBDISP_DISPCMD_COPY_AREA 4 #define RPUSBDISP_DISPCMD_COPY_AREA 4
#define RPUSBDISP_DISPCMD_BITBLT_RLE 5
#define RPUSBDISP_OPERATION_COPY 0
#define RPUSBDISP_OPERATION_XOR 1 #define RPUSBDISP_OPERATION_COPY 0
#define RPUSBDISP_OPERATION_OR 2 #define RPUSBDISP_OPERATION_XOR 1
#define RPUSBDISP_OPERATION_AND 3 #define RPUSBDISP_OPERATION_OR 2
#define RPUSBDISP_OPERATION_AND 3
#if defined(_WIN32) || defined(__ICCARM__)
#pragma pack(1) #if defined(_WIN32) || defined(__ICCARM__)
#endif #pragma pack(1)
#endif
#define RPUSBDISP_CMD_MASK (0x3F)
#define RPUSBDISP_CMD_FLAG_CLEARDITY (0x1<<6) #define RPUSBDISP_CMD_MASK (0x3F)
#define RPUSBDISP_CMD_FLAG_START (0x1<<7) #define RPUSBDISP_CMD_FLAG_CLEARDITY (0x1<<6)
typedef struct _rpusbdisp_disp_packet_header_t { #define RPUSBDISP_CMD_FLAG_START (0x1<<7)
#if 0 typedef struct _rpusbdisp_disp_packet_header_t {
_u8 cmd:6; #if 0
_u8 cleardirty:1; _u8 cmd:6;
_u8 start:1; _u8 cleardirty:1;
#else _u8 start:1;
_u8 cmd_flag; #else
#endif _u8 cmd_flag;
} __attribute__((packed)) rpusbdisp_disp_packet_header_t; #endif
} __attribute__((packed)) rpusbdisp_disp_packet_header_t;
typedef struct _rpusbdisp_disp_fill_packet_t {
rpusbdisp_disp_packet_header_t header; typedef struct _rpusbdisp_disp_fill_packet_t {
_u16 color_565; rpusbdisp_disp_packet_header_t header;
} __attribute__((packed)) rpusbdisp_disp_fill_packet_t; _u16 color_565;
} __attribute__((packed)) rpusbdisp_disp_fill_packet_t;
typedef struct _rpusbdisp_disp_bitblt_packet_t {
rpusbdisp_disp_packet_header_t header;
typedef struct _rpusbdisp_disp_bitblt_packet_t { _u16 x;
rpusbdisp_disp_packet_header_t header; _u16 y;
_u16 x; _u16 width;
_u16 y; _u16 height;
_u16 width; _u8 operation;
_u16 height; } __attribute__((packed)) rpusbdisp_disp_bitblt_packet_t;
_u8 operation;
} __attribute__((packed)) rpusbdisp_disp_bitblt_packet_t;
typedef struct _rpusbdisp_disp_fillrect_packet_t {
rpusbdisp_disp_packet_header_t header;
typedef struct _rpusbdisp_disp_fillrect_packet_t { _u16 left;
rpusbdisp_disp_packet_header_t header; _u16 top;
_u16 left; _u16 right;
_u16 top; _u16 bottom;
_u16 right; _u16 color_565;
_u16 bottom; _u8 operation;
_u16 color_565; } __attribute__((packed)) rpusbdisp_disp_fillrect_packet_t;
_u8 operation;
} __attribute__((packed)) rpusbdisp_disp_fillrect_packet_t;
typedef struct _rpusbdisp_disp_copyarea_packet_t {
rpusbdisp_disp_packet_header_t header;
typedef struct _rpusbdisp_disp_copyarea_packet_t { _u16 sx;
rpusbdisp_disp_packet_header_t header; _u16 sy;
_u16 sx; _u16 dx;
_u16 sy; _u16 dy;
_u16 dx; _u16 width;
_u16 dy; _u16 height;
_u16 width; } __attribute__((packed)) rpusbdisp_disp_copyarea_packet_t;
_u16 height;
} __attribute__((packed)) rpusbdisp_disp_copyarea_packet_t; #if defined(_WIN32) || defined(__ICCARM__)
#pragma pack()
#if defined(_WIN32) || defined(__ICCARM__) #endif
#pragma pack()
#endif
// RLE Packet Define
#define RPUSBDISP_RLE_BLOCKFLAG_COMMON_BIT 0x80
#define RPUSBDISP_RLE_BLOCKFLAG_SIZE_BIT 0x7f
// -- Status Packets
// -- Status Packets
#define RPUSBDISP_STATUS_TYPE_NORMAL 0
#define RPUSBDISP_STATUS_TYPE_NORMAL 0
#define RPUSBDISP_DISPLAY_STATUS_DIRTY_FLAG 0x80 //a full screen transfer is required
#define RPUSBDISP_DISPLAY_STATUS_DIRTY_FLAG 0x80 //a full screen transfer is required
#define RPUSBDISP_TOUCH_STATUS_NO_TOUCH 0
#define RPUSBDISP_TOUCH_STATUS_PRESSED 1 #define RPUSBDISP_TOUCH_STATUS_NO_TOUCH 0
#define RPUSBDISP_TOUCH_STATUS_PRESSED 1
#if defined(_WIN32) || defined(__ICCARM__)
#pragma pack(1) #if defined(_WIN32) || defined(__ICCARM__)
#endif #pragma pack(1)
#endif
typedef struct _rpusbdisp_status_packet_header_t {
_u8 packet_type; typedef struct _rpusbdisp_status_packet_header_t {
} __attribute__((packed)) rpusbdisp_status_packet_header_t; _u8 packet_type;
} __attribute__((packed)) rpusbdisp_status_packet_header_t;
typedef struct _rpusbdisp_status_normal_packet_t {
rpusbdisp_status_packet_header_t header; typedef struct _rpusbdisp_status_normal_packet_t {
_u8 display_status; rpusbdisp_status_packet_header_t header;
_u8 touch_status; _u8 display_status;
int touch_x; _u8 touch_status;
int touch_y; _s32 touch_x;
} __attribute__((packed)) rpusbdisp_status_normal_packet_t; _s32 touch_y;
} __attribute__((packed)) rpusbdisp_status_normal_packet_t;
#if defined(_WIN32) || defined(__ICCARM__)
#pragma pack() #if defined(_WIN32) || defined(__ICCARM__)
#endif #pragma pack()
#endif
...@@ -123,6 +123,7 @@ static void _display_update( struct fb_info *p, int x, int y, int width, int he ...@@ -123,6 +123,7 @@ static void _display_update( struct fb_info *p, int x, int y, int width, int he
clear_dirty = 1; clear_dirty = 1;
} else { } else {
if (pa->dirty_rect.top > y) pa->dirty_rect.top = y; if (pa->dirty_rect.top > y) pa->dirty_rect.top = y;
if (pa->dirty_rect.bottom < height+y-1) pa->dirty_rect.bottom = height+y-1; if (pa->dirty_rect.bottom < height+y-1) pa->dirty_rect.bottom = height+y-1;
if (pa->dirty_rect.left > x) pa->dirty_rect.left = x; if (pa->dirty_rect.left > x) pa->dirty_rect.left = x;
...@@ -130,9 +131,9 @@ static void _display_update( struct fb_info *p, int x, int y, int width, int he ...@@ -130,9 +131,9 @@ static void _display_update( struct fb_info *p, int x, int y, int width, int he
} }
if (pa->dirty_rect.top > pa->dirty_rect.bottom || pa->dirty_rect.left > pa->dirty_rect.right) if (pa->dirty_rect.top > pa->dirty_rect.bottom || pa->dirty_rect.left > pa->dirty_rect.right) {
goto final; goto final;
}
atomic_set(&pa->dirty_rect.dirty_flag, 1); atomic_set(&pa->dirty_rect.dirty_flag, 1);
// 2. try to send it // 2. try to send it
...@@ -167,14 +168,13 @@ static void _display_update( struct fb_info *p, int x, int y, int width, int he ...@@ -167,14 +168,13 @@ static void _display_update( struct fb_info *p, int x, int y, int width, int he
} }
break; break;
default: default:
if (rpusbdisp_usb_try_send_image(pa->binded_usbdev, (const pixel_type_t *)p->fix.smem_start, if (rpusbdisp_usb_try_send_image(pa->binded_usbdev, (const pixel_type_t *)p->fix.smem_start,
pa->dirty_rect.left, pa->dirty_rect.top, pa->dirty_rect.right, pa->dirty_rect.bottom, p->fix.line_length/(RP_DISP_DEFAULT_PIXEL_BITS/8), pa->dirty_rect.left, pa->dirty_rect.top, pa->dirty_rect.right, pa->dirty_rect.bottom, p->fix.line_length/(RP_DISP_DEFAULT_PIXEL_BITS/8),
clear_dirty)) { clear_dirty)) {
// data sent, rect the dirty rect // data sent, rect the dirty rect
_clear_dirty_rect(&pa->dirty_rect); _clear_dirty_rect(&pa->dirty_rect);
} }
} }
} }
...@@ -193,12 +193,14 @@ static void _display_fillrect ( struct fb_info * p, const struct fb_fillrect * ...@@ -193,12 +193,14 @@ static void _display_fillrect ( struct fb_info * p, const struct fb_fillrect *
static void _display_imageblit ( struct fb_info * p, const struct fb_image * image) static void _display_imageblit ( struct fb_info * p, const struct fb_image * image)
{ {
sys_imageblit (p, image); sys_imageblit (p, image);
_display_update(p, image->dx, image->dy, image->width, image->height, DISPLAY_UPDATE_HINT_BITBLT, image); _display_update(p, image->dx, image->dy, image->width, image->height, DISPLAY_UPDATE_HINT_BITBLT, image);
} }
static void _display_copyarea ( struct fb_info * p, const struct fb_copyarea * area) static void _display_copyarea ( struct fb_info * p, const struct fb_copyarea * area)
{ {
sys_copyarea (p, area); sys_copyarea (p, area);
_display_update(p, area->dx, area->dy, area->width, area->height, DISPLAY_UPDATE_HINT_COPYAREA, area); _display_update(p, area->dx, area->dy, area->width, area->height, DISPLAY_UPDATE_HINT_COPYAREA, area);
} }
...@@ -208,6 +210,7 @@ static ssize_t _display_write ( struct fb_info * p, const char * buf __user, ...@@ -208,6 +210,7 @@ static ssize_t _display_write ( struct fb_info * p, const char * buf __user,
{ {
int retval; int retval;
retval = fb_sys_write (p, buf, count, ppos); retval = fb_sys_write (p, buf, count, ppos);
_display_update(p, 0, 0, p->var.width, p->var.height, DISPLAY_UPDATE_HINT_NONE, NULL); _display_update(p, 0, 0, p->var.width, p->var.height, DISPLAY_UPDATE_HINT_NONE, NULL);
return retval; return retval;
} }
...@@ -279,6 +282,7 @@ static void _display_defio_handler(struct fb_info *info, ...@@ -279,6 +282,7 @@ static void _display_defio_handler(struct fb_info *info,
} }
if (bottom >= RP_DISP_DEFAULT_HEIGHT) bottom = RP_DISP_DEFAULT_HEIGHT - 1; if (bottom >= RP_DISP_DEFAULT_HEIGHT) bottom = RP_DISP_DEFAULT_HEIGHT - 1;
_display_update(info, 0, top, info->var.width, bottom - top + 1, DISPLAY_UPDATE_HINT_NONE, NULL); _display_update(info, 0, top, info->var.width, bottom - top + 1, DISPLAY_UPDATE_HINT_NONE, NULL);
} }
...@@ -477,8 +481,8 @@ void fbhandler_on_all_transfer_done(struct rpusbdisp_dev * dev) ...@@ -477,8 +481,8 @@ void fbhandler_on_all_transfer_done(struct rpusbdisp_dev * dev)
fb_pri = _get_fb_private(fb); fb_pri = _get_fb_private(fb);
if (atomic_read(&fb_pri->dirty_rect.dirty_flag) || atomic_read(&fb_pri->unsync_flag)) { if (atomic_read(&fb_pri->dirty_rect.dirty_flag) || atomic_read(&fb_pri->unsync_flag)==1) {
_display_update(fb, RP_DISP_DEFAULT_WIDTH, RP_DISP_DEFAULT_HEIGHT, 0, 0, DISPLAY_UPDATE_HINT_NONE, NULL); _display_update(fb, 0, 0, RP_DISP_DEFAULT_WIDTH, RP_DISP_DEFAULT_HEIGHT, DISPLAY_UPDATE_HINT_NONE, NULL);
} }
} }
......
...@@ -25,4 +25,7 @@ ...@@ -25,4 +25,7 @@
#define RP_DISP_DEFAULT_PIXEL_BITS 16 #define RP_DISP_DEFAULT_PIXEL_BITS 16
typedef _u16 pixel_type_t; typedef _u16 pixel_type_t;
#define RP_DISP_FEATURE_RLE_FWVERSION 0x0104
#endif #endif
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
#define DRIVER_LICENSE_INFO "GPL" #define DRIVER_LICENSE_INFO "GPL"
#define DRIVER_VERSION "RoboPeak USB Display Driver Version 0.01" #define DRIVER_VERSION "RoboPeak USB Display Driver Version 1.00"
#define RPUSBDISP_MINOR 300 #define RPUSBDISP_MINOR 300
......
...@@ -121,6 +121,9 @@ static struct usb_class_driver lcd_class = { ...@@ -121,6 +121,9 @@ static struct usb_class_driver lcd_class = {
#endif #endif
static int _return_disp_tickets(struct rpusbdisp_dev * dev, struct rpusbdisp_disp_ticket * ticket);
struct device * rpusbdisp_usb_get_devicehandle(struct rpusbdisp_dev *dev) struct device * rpusbdisp_usb_get_devicehandle(struct rpusbdisp_dev *dev)
{ {
return &dev->udev->dev; return &dev->udev->dev;
...@@ -208,7 +211,7 @@ static void _on_display_transfer_finished(struct urb *urb) ...@@ -208,7 +211,7 @@ static void _on_display_transfer_finished(struct urb *urb)
{ {
struct rpusbdisp_disp_ticket * ticket = (struct rpusbdisp_disp_ticket *)urb->context; struct rpusbdisp_disp_ticket * ticket = (struct rpusbdisp_disp_ticket *)urb->context;
struct rpusbdisp_dev * dev = ticket->binded_dev; struct rpusbdisp_dev * dev = ticket->binded_dev;
unsigned long irq_flags;
int all_finished = 0; int all_finished = 0;
/* sync/async unlink faults aren't errors */ /* sync/async unlink faults aren't errors */
if (urb->status) { if (urb->status) {
...@@ -221,26 +224,17 @@ static void _on_display_transfer_finished(struct urb *urb) ...@@ -221,26 +224,17 @@ static void _on_display_transfer_finished(struct urb *urb)
} }
urb->transfer_buffer_length = dev->disp_tickets_pool.packet_size_factor * dev->disp_out_ep_max_size; // reset buffer size
// insert to the available queue
spin_lock_irqsave(&dev->disp_tickets_pool.oplock, irq_flags);
list_add_tail(&ticket->ticket_list_node, &dev->disp_tickets_pool.list);
if ( ++dev->disp_tickets_pool.availiable_count == dev->disp_tickets_pool.disp_urb_count) { urb->transfer_buffer_length = dev->disp_tickets_pool.packet_size_factor * dev->disp_out_ep_max_size; // reset buffer size
all_finished = 1;
}
spin_unlock_irqrestore(&dev->disp_tickets_pool.oplock, irq_flags);
// insert to the available queue
all_finished = _return_disp_tickets(dev, ticket);
wake_up(&dev->disp_tickets_pool.wait_queue);
if (all_finished && !urb->status) { if (all_finished && !urb->status) {
schedule_delayed_work(&dev->disp_tickets_pool.completion_work, 0); schedule_delayed_work(&dev->disp_tickets_pool.completion_work, 0);
} }
} }
static void _on_status_query_finished(struct urb *urb) static void _on_status_query_finished(struct urb *urb)
...@@ -357,6 +351,26 @@ static int _sell_disp_tickets(struct rpusbdisp_dev * dev, struct rpusbdisp_disp_ ...@@ -357,6 +351,26 @@ static int _sell_disp_tickets(struct rpusbdisp_dev * dev, struct rpusbdisp_disp_
} }
static int _return_disp_tickets(struct rpusbdisp_dev * dev, struct rpusbdisp_disp_ticket * ticket)
{
int all_finished = 0;
unsigned long irq_flags;
// insert to the available queue
spin_lock_irqsave(&dev->disp_tickets_pool.oplock, irq_flags);
list_add_tail(&ticket->ticket_list_node, &dev->disp_tickets_pool.list);
if ( ++dev->disp_tickets_pool.availiable_count == dev->disp_tickets_pool.disp_urb_count) {
all_finished = 1;
}
spin_unlock_irqrestore(&dev->disp_tickets_pool.oplock, irq_flags);
wake_up(&dev->disp_tickets_pool.wait_queue);
return all_finished;
}
int rpusbdisp_usb_try_copy_area(struct rpusbdisp_dev * dev, int sx, int sy, int dx, int dy, int width, int height) int rpusbdisp_usb_try_copy_area(struct rpusbdisp_dev * dev, int sx, int sy, int dx, int dy, int width, int height)
{ {
...@@ -385,7 +399,8 @@ int rpusbdisp_usb_try_copy_area(struct rpusbdisp_dev * dev, int sx, int sy, int ...@@ -385,7 +399,8 @@ int rpusbdisp_usb_try_copy_area(struct rpusbdisp_dev * dev, int sx, int sy, int
cmd_copyarea->width= cpu_to_le16(width); cmd_copyarea->width= cpu_to_le16(width);
cmd_copyarea->height = cpu_to_le16(height); cmd_copyarea->height = cpu_to_le16(height);
ticket->transfer_urb->transfer_buffer_length = sizeof(rpusbdisp_disp_copyarea_packet_t); //add one more byte to bypass usbdisp 1.03 fw bug
ticket->transfer_urb->transfer_buffer_length = sizeof(rpusbdisp_disp_copyarea_packet_t) + 1;
if (usb_submit_urb(ticket->transfer_urb, GFP_KERNEL)) { if (usb_submit_urb(ticket->transfer_urb, GFP_KERNEL)) {
// submit failure, // submit failure,
...@@ -436,30 +451,49 @@ int rpusbdisp_usb_try_draw_rect(struct rpusbdisp_dev * dev, int x, int y, int ri ...@@ -436,30 +451,49 @@ int rpusbdisp_usb_try_draw_rect(struct rpusbdisp_dev * dev, int x, int y, int ri
return 1; return 1;
} }
int rpusbdisp_usb_try_send_image(struct rpusbdisp_dev * dev, const pixel_type_t * framebuffer, int x, int y, int right, int bottom, int line_width, int clear_dirty)
{
// context used by the bitblt display packet encoder
struct bitblt_encoding_context_t {
struct rpusbdisp_disp_ticket_bundle bundle; struct rpusbdisp_disp_ticket_bundle bundle;
struct rpusbdisp_disp_ticket * ticket; struct rpusbdisp_disp_ticket * ticket;
struct list_head * current_node; struct list_head * current_node;
size_t encoded_pos, packet_pos; size_t encoded_pos;
int last_copied_x, last_copied_y; size_t packet_pos;
_u8 * urbbuffer ; _u8 * urbbuffer ;
_u16 partial_byte = 0; //the 0x8000 bit indicates the existance of the partial byte int rlemode;
rpusbdisp_disp_bitblt_packet_t * bitblt_header ; } ;
// estimate how many tickets are needed
const size_t image_size = (right-x + 1)* (bottom-y+1) * (RP_DISP_DEFAULT_PIXEL_BITS/8);
const size_t payload_without_header_size = sizeof(rpusbdisp_disp_bitblt_packet_t) - sizeof(rpusbdisp_disp_packet_header_t) static int _bitblt_encoder_init(struct bitblt_encoding_context_t * ctx, struct rpusbdisp_dev * dev, size_t image_size, int rlemode)
+ image_size; {
const size_t effective_payload_per_packet_size = dev->disp_out_ep_max_size - sizeof(rpusbdisp_disp_packet_header_t);
const size_t packet_count = (payload_without_header_size + effective_payload_per_packet_size-1) /effective_payload_per_packet_size; size_t effective_payload_per_packet_size;
size_t packet_count;
size_t required_tickets_count;
size_t payload_without_header_size = sizeof(rpusbdisp_disp_bitblt_packet_t) - sizeof(rpusbdisp_disp_packet_header_t)
+ image_size;
const size_t required_tickets_count = (packet_count + dev->disp_tickets_pool.packet_size_factor-1) / dev->disp_tickets_pool.packet_size_factor; if (rlemode) {
// the worst case for RLE is 1 extra byte with each 128 pixels (since the 7bit size block can represent 128 uints)
payload_without_header_size += (((image_size>>1) + 0x7f)>>7);
}
// calc how many tickets are needed ...
effective_payload_per_packet_size = dev->disp_out_ep_max_size - sizeof(rpusbdisp_disp_packet_header_t);
packet_count = (payload_without_header_size + effective_payload_per_packet_size-1) /effective_payload_per_packet_size;
required_tickets_count = (packet_count + dev->disp_tickets_pool.packet_size_factor-1) / dev->disp_tickets_pool.packet_size_factor;
if ( required_tickets_count>RPUSBDISP_MAX_TRANSFER_TICKETS_COUNT) if ( required_tickets_count>RPUSBDISP_MAX_TRANSFER_TICKETS_COUNT)
...@@ -470,27 +504,31 @@ int rpusbdisp_usb_try_send_image(struct rpusbdisp_dev * dev, const pixel_type_t ...@@ -470,27 +504,31 @@ int rpusbdisp_usb_try_send_image(struct rpusbdisp_dev * dev, const pixel_type_t
} }
if (!_sell_disp_tickets(dev, &bundle, required_tickets_count)) {
if (!_sell_disp_tickets(dev, &ctx->bundle, required_tickets_count)) {
// tickets is inadequate, try next time // tickets is inadequate, try next time
return 0; return 0;
} }
// init the context...
ctx->packet_pos = 0;
ctx->current_node = ctx->bundle.ticket_list.next;
ctx->ticket = list_entry(ctx->current_node, struct rpusbdisp_disp_ticket, ticket_list_node);
ctx->urbbuffer = (_u8 *)ctx->ticket->transfer_urb->transfer_buffer;
ctx->encoded_pos = 0;
ctx->rlemode = rlemode;
return 1;
}
// begin urbs filling...
// locate to the begining...
framebuffer += (y*line_width + x);
packet_pos = 0;
current_node = bundle.ticket_list.next; static void _bitblt_encode_command_header(struct bitblt_encoding_context_t * ctx, struct rpusbdisp_dev * dev, int x, int y, int right, int bottom, int clear_dirty)
ticket = list_entry(current_node, struct rpusbdisp_disp_ticket, ticket_list_node); {
rpusbdisp_disp_bitblt_packet_t * bitblt_header ;
urbbuffer = (_u8 *)ticket->transfer_urb->transfer_buffer; // encoding the command header...
// encoding the command header bitblt_header = (rpusbdisp_disp_bitblt_packet_t *)ctx->urbbuffer;
bitblt_header = (rpusbdisp_disp_bitblt_packet_t *)urbbuffer; bitblt_header->header.cmd_flag = ctx->rlemode?RPUSBDISP_DISPCMD_BITBLT_RLE:RPUSBDISP_DISPCMD_BITBLT;
bitblt_header->header.cmd_flag = RPUSBDISP_DISPCMD_BITBLT;
bitblt_header->header.cmd_flag |= RPUSBDISP_CMD_FLAG_START; bitblt_header->header.cmd_flag |= RPUSBDISP_CMD_FLAG_START;
if (clear_dirty) { if (clear_dirty) {
bitblt_header->header.cmd_flag |= RPUSBDISP_CMD_FLAG_CLEARDITY; bitblt_header->header.cmd_flag |= RPUSBDISP_CMD_FLAG_CLEARDITY;
...@@ -502,85 +540,298 @@ int rpusbdisp_usb_try_send_image(struct rpusbdisp_dev * dev, const pixel_type_t ...@@ -502,85 +540,298 @@ int rpusbdisp_usb_try_send_image(struct rpusbdisp_dev * dev, const pixel_type_t
bitblt_header->height = cpu_to_le16(bottom+1-y); bitblt_header->height = cpu_to_le16(bottom+1-y);
bitblt_header->operation = RPUSBDISP_OPERATION_COPY; bitblt_header->operation = RPUSBDISP_OPERATION_COPY;
encoded_pos = sizeof(rpusbdisp_disp_bitblt_packet_t); ctx->encoded_pos = sizeof(rpusbdisp_disp_bitblt_packet_t);
}
static void _bitblt_encoder_cleanup(struct bitblt_encoding_context_t * ctx, struct rpusbdisp_dev * dev)
{
struct rpusbdisp_disp_ticket * ticket;
// return unused tickets
while (ctx->current_node != &ctx->bundle.ticket_list) {
ticket = list_entry(ctx->current_node, struct rpusbdisp_disp_ticket, ticket_list_node);
ctx->current_node = ctx->current_node->next;
_return_disp_tickets(dev, ticket);
}
}
// The following code should have very poor performance... static int _bitblt_encoder_flush(struct bitblt_encoding_context_t * ctx, struct rpusbdisp_dev * dev)
// but the usb screen performs even worse... {
for (last_copied_y = y; last_copied_y <= bottom; ++last_copied_y) {
// submit the final ticket
size_t transfer_size = ctx->packet_pos * dev->disp_out_ep_max_size + ctx->encoded_pos;
if (transfer_size) {
ctx->ticket->transfer_urb->transfer_buffer_length = transfer_size;
for (last_copied_x = x; last_copied_x <= right; ++last_copied_x) { ctx->current_node = ctx->current_node->next;
pixel_type_t current_pixel = *framebuffer; if (usb_submit_urb(ctx->ticket->transfer_urb, GFP_KERNEL)) {
if (encoded_pos > dev->disp_out_ep_max_size - sizeof(pixel_type_t) ) { // submit failure,
// no room for transmit the whole pixel
#if (RP_DISP_DEFAULT_PIXEL_BITS/8) != 2 _on_display_transfer_finished(ctx->ticket->transfer_urb);
#error "only 16bit pixel type is supported" return 0; //abort
#endif }
// transmit the low byte }
urbbuffer[encoded_pos++] = current_pixel & 0xFF;
partial_byte = 0x8000 | (current_pixel>>8);
} else {
*(pixel_type_t *)(urbbuffer + encoded_pos) = cpu_to_le16(current_pixel);
encoded_pos+=sizeof(pixel_type_t);
}
++framebuffer; _bitblt_encoder_cleanup(ctx, dev);
return 1;
}
static int _bitblt_encode_n_transfer_data(struct bitblt_encoding_context_t * ctx, struct rpusbdisp_dev * dev, const void * data, size_t count)
{
const _u8 * payload_in_bytes = (const _u8 *)data;
while (count) {
// fill the buffer as much as possible...
size_t buffer_avail_length = dev->disp_out_ep_max_size - ctx->encoded_pos;
size_t size_to_copy = count>buffer_avail_length?buffer_avail_length:count;
memcpy(ctx->urbbuffer + ctx->encoded_pos, payload_in_bytes, size_to_copy);
payload_in_bytes += size_to_copy;
ctx->encoded_pos += size_to_copy;
count -= size_to_copy;
if (buffer_avail_length == size_to_copy) {
// current transfer block is full
ctx->urbbuffer += dev->disp_out_ep_max_size;
if (++ctx->packet_pos >= dev->disp_tickets_pool.packet_size_factor) {
// current urb is full, send the ticket
ctx->current_node = ctx->current_node->next;
if (usb_submit_urb(ctx->ticket->transfer_urb, GFP_KERNEL)) {
// submit failure,
_on_display_transfer_finished(ctx->ticket->transfer_urb);
return 0; //abort
}
if (encoded_pos >= dev->disp_out_ep_max_size) {
urbbuffer+=dev->disp_out_ep_max_size; // a new ticket
// a new transmission packet ctx->packet_pos = 0;
if (++packet_pos >= dev->disp_tickets_pool.packet_size_factor) {
// the current ticket is full, submit it BUG_ON(ctx->current_node == &ctx->bundle.ticket_list);
current_node = current_node->next;
if (usb_submit_urb(ticket->transfer_urb, GFP_KERNEL)) {
// submit failure,
_on_display_transfer_finished(ticket->transfer_urb);
return 0; //abort
}
ctx->ticket = list_entry(ctx->current_node, struct rpusbdisp_disp_ticket, ticket_list_node);
// a new ticket ctx->urbbuffer = (_u8 *)ctx->ticket->transfer_urb->transfer_buffer;
packet_pos = 0;
}
BUG_ON(current_node == &bundle.ticket_list);
ticket = list_entry(current_node, struct rpusbdisp_disp_ticket, ticket_list_node); // encoding the header
urbbuffer = (_u8 *)ticket->transfer_urb->transfer_buffer; *ctx->urbbuffer = 0;
((rpusbdisp_disp_packet_header_t *)ctx->urbbuffer)->cmd_flag = ctx->rlemode?RPUSBDISP_DISPCMD_BITBLT_RLE:RPUSBDISP_DISPCMD_BITBLT;
ctx->encoded_pos = sizeof(rpusbdisp_disp_packet_header_t);
}
}
return 1;
}
struct rle_encoder_context {
struct bitblt_encoding_context_t * encoder_ctx;
int is_common_section;
size_t section_size;
pixel_type_t section_data[128];
};
static void _rle_compress_init(struct rle_encoder_context * rle_ctx, struct bitblt_encoding_context_t * encoder_ctx, struct rpusbdisp_dev * dev)
{
rle_ctx->encoder_ctx = encoder_ctx;
rle_ctx->is_common_section = 0;
rle_ctx->section_size = 0;
}
static int _rle_flush_section(struct rle_encoder_context * rle_ctx, struct rpusbdisp_dev * dev)
{
// flush the current section ...
_u8 section_header;
size_t section_data_len;
BUG_ON(rle_ctx->section_size > RPUSBDISP_RLE_BLOCKFLAG_SIZE_BIT + 1);
if (rle_ctx->section_size == 0) {
return 1; //do not flush an empty section...
}
section_header = ((rle_ctx->section_size - 1) & RPUSBDISP_RLE_BLOCKFLAG_SIZE_BIT);
if (rle_ctx->is_common_section) {
section_header |= RPUSBDISP_RLE_BLOCKFLAG_COMMON_BIT;
}
if (!_bitblt_encode_n_transfer_data(rle_ctx->encoder_ctx, dev, &section_header, sizeof(_u8))) {
// abort the operation...
return 0;
}
section_data_len = rle_ctx->is_common_section? 1:rle_ctx->section_size;
if (!_bitblt_encode_n_transfer_data(rle_ctx->encoder_ctx, dev, rle_ctx->section_data, sizeof(pixel_type_t) * section_data_len)) {
// abort the operation...
return 0;
}
// reinit the section
rle_ctx->is_common_section = 0;
rle_ctx->section_size = 0;
return 1;
}
static int _rle_compress_n_encode(struct rle_encoder_context * rle_ctx, struct rpusbdisp_dev * dev, pixel_type_t pixel)
{
#if RPUSBDISP_RLE_BLOCKFLAG_SIZE_BIT + 1 > 128
#error "RPUSBDISP_RLE_BLOCKFLAG_SIZE_BIT + 1 > 128"
#endif
if (rle_ctx->is_common_section) {
BUG_ON(rle_ctx->section_size == 0);
if (pixel != rle_ctx->section_data[0]) {
if (!_rle_flush_section(rle_ctx, dev)) {
return 0;
}
rle_ctx->section_size = 1;
rle_ctx->section_data[0] = pixel;
} else {
if (rle_ctx->section_size == RPUSBDISP_RLE_BLOCKFLAG_SIZE_BIT + 1) {
// section is full, flush it...
if (!_rle_flush_section(rle_ctx, dev)) {
return 0;
} }
rle_ctx->section_size = 1;
rle_ctx->section_data[0] = pixel;
} else {
// simply increase the section size...
++ rle_ctx->section_size;
}
}
} else {
// check whether the current pixel is equal to the last pixel value...
if (rle_ctx->section_size && (pixel == rle_ctx->section_data[rle_ctx->section_size-1])) {
// remove the last pixel from the current section, flush the section and form a common section
--rle_ctx->section_size;
if (rle_ctx->section_size) {
// only flush a section with non-zero data
if (!_rle_flush_section(rle_ctx, dev)) {
return 0;
}
}
// form a common section
rle_ctx->section_size = 2;
rle_ctx->section_data[0] = pixel;
rle_ctx->is_common_section = 1;
} else {
if (rle_ctx->section_size == RPUSBDISP_RLE_BLOCKFLAG_SIZE_BIT + 1) {
// section is full, flush it...
// encoding the header if (!_rle_flush_section(rle_ctx, dev)) {
*urbbuffer = 0; return 0;
((rpusbdisp_disp_packet_header_t *)urbbuffer)->cmd_flag = RPUSBDISP_DISPCMD_BITBLT;
encoded_pos = sizeof(rpusbdisp_disp_packet_header_t);
// flush the partial byte
if (partial_byte & 0x8000) {
urbbuffer[encoded_pos++] = partial_byte & 0xFF;
partial_byte = 0;
} }
}
}
// include the new data
rle_ctx->section_data[rle_ctx->section_size++] = pixel;
} }
framebuffer += line_width - right - 1 + x;
}
return 1;
}
int rpusbdisp_usb_try_send_image(struct rpusbdisp_dev * dev, const pixel_type_t * framebuffer, int x, int y, int right, int bottom, int line_width, int clear_dirty)
{
struct bitblt_encoding_context_t encoder_ctx;
struct rle_encoder_context rle_ctx;
int last_copied_x, last_copied_y;
int rlemode;
// estimate how many tickets are needed
const size_t image_size = (right-x + 1)* (bottom-y+1) * (RP_DISP_DEFAULT_PIXEL_BITS/8);
// do not transmit zero size image
if (!image_size) return 1;
if (dev->udev->descriptor.bcdDevice >= RP_DISP_FEATURE_RLE_FWVERSION) {
rlemode = 1;
} else {
rlemode = 0;
} }
if (!_bitblt_encoder_init(&encoder_ctx, dev, image_size, rlemode)) return 0;
// submit the final ticket if (rlemode) {
if (encoded_pos>sizeof(rpusbdisp_disp_packet_header_t) || packet_pos) { _rle_compress_init(&rle_ctx, &encoder_ctx, dev);
ticket->transfer_urb->transfer_buffer_length = packet_pos * dev->disp_out_ep_max_size + encoded_pos; }
if (usb_submit_urb(ticket->transfer_urb, GFP_KERNEL)) { _bitblt_encode_command_header(&encoder_ctx, dev, x, y, right, bottom, clear_dirty);
// submit failure,
// locate to the begining...
framebuffer += (y*line_width + x);
for (last_copied_y = y; last_copied_y <= bottom; ++last_copied_y) {
for (last_copied_x = x; last_copied_x <= right; ++last_copied_x) {
pixel_type_t current_pixel_le = cpu_to_le16(*framebuffer);
#if (RP_DISP_DEFAULT_PIXEL_BITS/8) != 2
#error "only 16bit pixel type is supported"
#endif
if (rlemode) {
if (!_rle_compress_n_encode(&rle_ctx, dev, current_pixel_le)) {
_bitblt_encoder_cleanup(&encoder_ctx, dev);
return 0;
}
} else {
if (!_bitblt_encode_n_transfer_data(&encoder_ctx, dev, &current_pixel_le, sizeof(pixel_type_t))) {
// abort the operation...
_bitblt_encoder_cleanup(&encoder_ctx, dev);
return 0;
}
}
++framebuffer;
_on_display_transfer_finished(ticket->transfer_urb);
return 0; //abort
} }
framebuffer += line_width - right - 1 + x;
}
if (rlemode) {
if (!_rle_flush_section(&rle_ctx, dev)) {
_bitblt_encoder_cleanup(&encoder_ctx, dev);
return 0;
}
} }
return 1; return _bitblt_encoder_flush(&encoder_ctx, dev);
} }
static void _on_release_disp_tickets_pool(struct rpusbdisp_dev * dev) static void _on_release_disp_tickets_pool(struct rpusbdisp_dev * dev)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment