// SPDX-License-Identifier: GPL-2.0-only /* * udlfb.c -- Framebuffer driver for DisplayLink USB controller * * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it> * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com> * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com> * * Layout is based on skeletonfb by James Simmons and Geert Uytterhoeven, * usb-skeleton by GregKH. * * Device-specific portions based on information from Displaylink, with work * from Florian Echtler, Henrik Bjerregaard Pedersen, and others.
*/
/* * There are many DisplayLink-based graphics products, all with unique PIDs. * So we match on DisplayLink's VID + Vendor-Defined Interface Class (0xff) * We also require a match on SubClass (0x00) and Protocol (0x00), * which is compatible with all known USB 2.0 era graphics chips and firmware, * but allows DisplayLink to increment those for any future incompatible chips
*/ staticconststruct usb_device_id id_table[] = {
{.idVendor = 0x17e9,
.bInterfaceClass = 0xff,
.bInterfaceSubClass = 0x00,
.bInterfaceProtocol = 0x00,
.match_flags = USB_DEVICE_ID_MATCH_VENDOR |
USB_DEVICE_ID_MATCH_INT_CLASS |
USB_DEVICE_ID_MATCH_INT_SUBCLASS |
USB_DEVICE_ID_MATCH_INT_PROTOCOL,
},
{},
};
MODULE_DEVICE_TABLE(usb, id_table);
/* module options */ staticbool console = true; /* Allow fbcon to open framebuffer */ staticbool fb_defio = true; /* Detect mmap writes using page faults */ staticbool shadow = true; /* Optionally disable shadow framebuffer */ staticint pixel_limit; /* Optionally force a pixel resolution limit */
/* dlfb keeps a list of urbs for efficient bulk transfers */ staticvoid dlfb_urb_completion(struct urb *urb); staticstruct urb *dlfb_get_urb(struct dlfb_data *dlfb); staticint dlfb_submit_urb(struct dlfb_data *dlfb, struct urb * urb, size_t len); staticint dlfb_alloc_urb_list(struct dlfb_data *dlfb, int count, size_t size); staticvoid dlfb_free_urb_list(struct dlfb_data *dlfb);
/* * All DisplayLink bulk operations start with 0xAF, followed by specific code * All operations are written to buffers which then later get sent to device
*/ staticchar *dlfb_set_register(char *buf, u8 reg, u8 val)
{
*buf++ = 0xAF;
*buf++ = 0x20;
*buf++ = reg;
*buf++ = val; return buf;
}
staticchar *dlfb_set_base16bpp(char *wrptr, u32 base)
{ /* the base pointer is 16 bits wide, 0x20 is hi byte. */
wrptr = dlfb_set_register(wrptr, 0x20, base >> 16);
wrptr = dlfb_set_register(wrptr, 0x21, base >> 8); return dlfb_set_register(wrptr, 0x22, base);
}
/* * DisplayLink HW has separate 16bpp and 8bpp framebuffers. * In 24bpp modes, the low 323 RGB bits go in the 8bpp framebuffer
*/ staticchar *dlfb_set_base8bpp(char *wrptr, u32 base)
{
wrptr = dlfb_set_register(wrptr, 0x26, base >> 16);
wrptr = dlfb_set_register(wrptr, 0x27, base >> 8); return dlfb_set_register(wrptr, 0x28, base);
}
/* * This is kind of weird because the controller takes some * register values in a different byte order than other registers.
*/ staticchar *dlfb_set_register_16be(char *wrptr, u8 reg, u16 value)
{
wrptr = dlfb_set_register(wrptr, reg, value); return dlfb_set_register(wrptr, reg+1, value >> 8);
}
/* * LFSR is linear feedback shift register. The reason we have this is * because the display controller needs to minimize the clock depth of * various counters used in the display path. So this code reverses the * provided value into the lfsr16 value by counting backwards to get * the value that needs to be set in the hardware comparator to get the * same actual count. This makes sense once you read above a couple of * times and think about it from a hardware perspective.
*/ static u16 dlfb_lfsr16(u16 actual_count)
{
u32 lv = 0xFFFF; /* This is the lfsr value that the hw starts with */
/* * This does LFSR conversion on the value that is to be written. * See LFSR explanation above for more detail.
*/ staticchar *dlfb_set_register_lfsr16(char *wrptr, u8 reg, u16 value)
{ return dlfb_set_register_16(wrptr, reg, dlfb_lfsr16(value));
}
/* * This takes a standard fbdev screeninfo struct and all of its monitor mode * details and converts them into the DisplayLink equivalent register commands.
*/ staticchar *dlfb_set_vid_cmds(char *wrptr, struct fb_var_screeninfo *var)
{
u16 xds, yds;
u16 xde, yde;
u16 yec;
/* x display start */
xds = var->left_margin + var->hsync_len;
wrptr = dlfb_set_register_lfsr16(wrptr, 0x01, xds); /* x display end */
xde = xds + var->xres;
wrptr = dlfb_set_register_lfsr16(wrptr, 0x03, xde);
/* y display start */
yds = var->upper_margin + var->vsync_len;
wrptr = dlfb_set_register_lfsr16(wrptr, 0x05, yds); /* y display end */
yde = yds + var->yres;
wrptr = dlfb_set_register_lfsr16(wrptr, 0x07, yde);
/* x end count is active + blanking - 1 */
wrptr = dlfb_set_register_lfsr16(wrptr, 0x09,
xde + var->right_margin - 1);
/* vsync end is width of vsync pulse */
wrptr = dlfb_set_register_lfsr16(wrptr, 0x15, var->vsync_len);
/* vpixels is active pixels */
wrptr = dlfb_set_register_16(wrptr, 0x17, var->yres);
/* convert picoseconds to 5kHz multiple for pclk5k = x * 1E12/5k */
wrptr = dlfb_set_register_16be(wrptr, 0x1B,
200*1000*1000/var->pixclock);
return wrptr;
}
/* * This takes a standard fbdev screeninfo struct that was fetched or prepared * and then generates the appropriate command sequence that then drives the * display controller.
*/ staticint dlfb_set_video_mode(struct dlfb_data *dlfb, struct fb_var_screeninfo *var)
{ char *buf; char *wrptr; int retval; int writesize; struct urb *urb;
if (!atomic_read(&dlfb->usb_active)) return -EPERM;
urb = dlfb_get_urb(dlfb); if (!urb) return -ENOMEM;
buf = (char *) urb->transfer_buffer;
/* * This first section has to do with setting the base address on the * controller * associated with the display. There are 2 base * pointers, currently, we only * use the 16 bpp segment.
*/
wrptr = dlfb_vidreg_lock(buf);
wrptr = dlfb_set_color_depth(wrptr, 0x00); /* set base for 16bpp segment to 0 */
wrptr = dlfb_set_base16bpp(wrptr, 0); /* set base for 8bpp segment to end of fb */
wrptr = dlfb_set_base8bpp(wrptr, dlfb->info->fix.smem_len);
/* * 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
*/ staticint dlfb_trim_hline(const u8 *bback, const u8 **bfront, int *width_bytes)
{ int j, k; constunsignedlong *back = (constunsignedlong *) bback; constunsignedlong *front = (constunsignedlong *) *bfront; constint width = *width_bytes / sizeof(unsignedlong); int identical; int start = width; int end = width;
for (j = 0; j < width; j++) { if (back[j] != front[j]) {
start = j; break;
}
}
for (k = width - 1; k > j; k--) { if (back[k] != front[k]) {
end = k+1; break;
}
}
/* * 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.
*/ staticvoid 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, unsignedlong back_buffer_offset, int *ident_ptr)
{ const uint16_t *pixel = *pixel_start_ptr;
uint32_t dev_addr = *device_address_ptr;
uint8_t *cmd = *command_buffer_ptr;
if (back_buffer_offset) { /* note: the framebuffer may change under us, so we must test for underflow */ while (cmd_pixel_end - 1 > pixel &&
*(cmd_pixel_end - 1) == *(u16 *)((u8 *)(cmd_pixel_end - 1) + back_buffer_offset))
cmd_pixel_end--;
}
if (unlikely((pixel < cmd_pixel_end) &&
(*pixel == pixel_value))) { /* go back and fill in raw pixel count */
*raw_pixels_count_byte = ((repeating_pixel -
raw_pixel_start) + 1) & 0xFF;
do { if (back_buffer_offset)
*(u16 *)((u8 *)pixel + back_buffer_offset) = pixel_value;
pixel++;
} while ((pixel < cmd_pixel_end) &&
(*pixel == pixel_value));
/* 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 (pixel > raw_pixel_start) { /* finalize last RAW span */
*raw_pixels_count_byte = (pixel-raw_pixel_start) & 0xFF;
} else { /* undo unused byte */
cmd--;
}
/* * 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.
*/ staticint dlfb_render_hline(struct dlfb_data *dlfb, struct urb **urb_ptr, constchar *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 = dlfb->base16 + byte_offset; struct urb *urb = *urb_ptr;
u8 *cmd = *urb_buf_ptr;
u8 *cmd_end = (u8 *) urb->transfer_buffer + urb->transfer_buffer_length; unsignedlong back_buffer_offset = 0;
if (cmd >= cmd_end) { int len = cmd - (u8 *) urb->transfer_buffer; if (dlfb_submit_urb(dlfb, urb, len)) return 1; /* lost pixels is set */
*sent_ptr += len;
urb = dlfb_get_urb(dlfb); if (!urb) return 1; /* lost_pixels is set */
*urb_ptr = urb;
cmd = urb->transfer_buffer;
cmd_end = &cmd[urb->transfer_buffer_length];
}
}
*urb_buf_ptr = cmd;
return 0;
}
staticint dlfb_handle_damage(struct dlfb_data *dlfb, int x, int y, int width, int height)
{ 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;
staticvoid dlfb_damage_work(struct work_struct *w)
{ struct dlfb_data *dlfb = container_of(w, struct dlfb_data, damage_work); int x, x2, y, y2;
spin_lock_irq(&dlfb->damage_lock);
x = dlfb->damage_x;
x2 = dlfb->damage_x2;
y = dlfb->damage_y;
y2 = dlfb->damage_y2;
dlfb_init_damage(dlfb);
spin_unlock_irq(&dlfb->damage_lock);
if (x < x2 && y < y2)
dlfb_handle_damage(dlfb, x, y, x2 - x, y2 - y);
}
staticvoid dlfb_offload_damage(struct dlfb_data *dlfb, int x, int y, int width, int height)
{ unsignedlong flags; int x2 = x + width; int y2 = y + height;
/* * 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.
*/ staticvoid dlfb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist)
{ struct fb_deferred_io_pageref *pageref; struct dlfb_data *dlfb = 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;
mutex_lock(&dlfb->render_mutex);
if (!fb_defio) goto unlock_ret;
if (!atomic_read(&dlfb->usb_active)) goto unlock_ret;
start_cycles = get_cycles();
urb = dlfb_get_urb(dlfb); if (!urb) goto unlock_ret;
cmd = urb->transfer_buffer;
/* walk the written page list and render each to device */
list_for_each_entry(pageref, pagereflist, list) { if (dlfb_render_hline(dlfb, &urb, (char *) info->fix.smem_start,
&cmd, pageref->offset, PAGE_SIZE,
&bytes_identical, &bytes_sent)) goto error;
bytes_rendered += PAGE_SIZE;
}
if (cmd > (char *) urb->transfer_buffer) { int len; if (cmd < (char *) urb->transfer_buffer + urb->transfer_buffer_length)
*cmd++ = 0xAF; /* Send partial buffer remaining before exiting */
len = cmd - (char *) urb->transfer_buffer;
dlfb_submit_urb(dlfb, urb, len);
bytes_sent += len;
} else
dlfb_urb_completion(urb);
/* TODO: Update X server to get this from sysfs instead */ if (cmd == DLFB_IOCTL_RETURN_EDID) { void __user *edid = (void __user *)arg; if (copy_to_user(edid, dlfb->edid, dlfb->edid_size)) return -EFAULT; return 0;
}
/* TODO: Help propose a standard fb.h ioctl to report mmap damage */ if (cmd == DLFB_IOCTL_REPORT_DAMAGE) { struct dloarea area;
if (copy_from_user(&area, (void __user *)arg, sizeof(struct dloarea))) return -EFAULT;
/* * If we have a damage-aware client, turn fb_defio "off" * To avoid perf imact of unnecessary page fault handling. * Done by resetting the delay for this fb_info to a very * long period. Pages will become writable and stay that way. * Reset to normal value when all clients have closed this fb.
*/ if (info->fbdefio)
info->fbdefio->delay = DL_DEFIO_WRITE_DISABLE;
if (area.x < 0)
area.x = 0;
if (area.x > info->var.xres)
area.x = info->var.xres;
if (area.y < 0)
area.y = 0;
if (area.y > info->var.yres)
area.y = info->var.yres;
/* * It's common for several clients to have framebuffer open simultaneously. * e.g. both fbcon and X. Makes things interesting. * Assumes caller is holding info->lock (for open and release at least)
*/ staticint dlfb_ops_open(struct fb_info *info, int user)
{ struct dlfb_data *dlfb = info->par;
/* * 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 (dlfb->virtualized) return -ENODEV;
dlfb->fb_count++;
if (fb_defio && (info->fbdefio == NULL)) { /* enable defio at last moment if not disabled by client */
if (info->cmap.len != 0)
fb_dealloc_cmap(&info->cmap); if (info->monspecs.modedb)
fb_destroy_modedb(info->monspecs.modedb);
vfree(info->screen_buffer);
/* * Check whether a video mode is supported by the DisplayLink chip * We start from monitor's modes, so don't need to filter that here
*/ staticint dlfb_is_valid_mode(struct fb_videomode *mode, struct dlfb_data *dlfb)
{ if (mode->xres * mode->yres > dlfb->sku_pixel_limit) return 0;
/* To fonzi the jukebox (e.g. make blanking changes take effect) */ staticchar *dlfb_dummy_render(char *buf)
{
*buf++ = 0xAF;
*buf++ = 0x6A; /* copy */
*buf++ = 0x00; /* from address*/
*buf++ = 0x00;
*buf++ = 0x00;
*buf++ = 0x01; /* one pixel */
*buf++ = 0x00; /* to address */
*buf++ = 0x00;
*buf++ = 0x00; return buf;
}
/* * In order to come back from full DPMS off, we need to set the mode again
*/ staticint dlfb_ops_blank(int blank_mode, struct fb_info *info)
{ struct dlfb_data *dlfb = info->par; char *bufptr; struct urb *urb;
/* * Second framebuffer copy to mirror the framebuffer state * on the physical USB device. We can function without this. * But with imperfect damage info we may send pixels over USB * that were, in fact, unchanged - wasting limited USB bandwidth
*/ if (shadow)
new_back = vzalloc(new_len); if (!new_back)
dev_info(info->dev, "No shadow/backing buffer allocated\n"); else {
dlfb_deferred_vfree(dlfb, dlfb->backing_buffer);
dlfb->backing_buffer = new_back;
}
} return 0;
}
/* * 1) Get EDID from hw, or use sw default * 2) Parse into various fb_info structs * 3) Allocate virtual framebuffer memory to back highest res mode * * Parses EDID into three places used by various parts of fbdev: * fb_var_screeninfo contains the timing of the monitor's preferred mode * fb_info.monspecs is full parsed EDID info, including monspecs.modedb * fb_info.modelist is a linked list of all monitor & VESA modes which work * * If EDID is not readable/valid, then modelist is all VESA modes, * monspecs is NULL, and fb_var_screeninfo is set to safe VESA mode * Returns 0 if successful
*/ staticint dlfb_setup_modes(struct dlfb_data *dlfb, struct fb_info *info, char *default_edid, size_t default_edid_size)
{ char *edid; int i, result = 0, tries = 3; struct device *dev = info->device; struct fb_videomode *mode; conststruct fb_videomode *default_vmode = NULL;
if (info->dev) { /* only use mutex if info has been registered */
mutex_lock(&info->lock); /* parent device is used otherwise */
dev = info->dev;
}
edid = kmalloc(EDID_LENGTH, GFP_KERNEL); if (!edid) {
result = -ENOMEM; goto error;
}
/* * Try to (re)read EDID from hardware first * EDID data may return, but not parse as valid * Try again a few times, in case of e.g. analog cable noise
*/ while (tries--) {
i = dlfb_get_edid(dlfb, edid, EDID_LENGTH);
if (i >= EDID_LENGTH)
fb_edid_to_monspecs(edid, &info->monspecs);
/* If that fails, use a previously returned EDID if available */ if (info->monspecs.modedb_len == 0) {
dev_err(dev, "Unable to get valid EDID from device/display\n");
if (dlfb->edid) {
fb_edid_to_monspecs(dlfb->edid, &info->monspecs); if (info->monspecs.modedb_len > 0)
dev_err(dev, "Using previously queried EDID\n");
}
}
/* If that fails, use the default EDID we were handed */ if (info->monspecs.modedb_len == 0) { if (default_edid_size >= EDID_LENGTH) {
fb_edid_to_monspecs(default_edid, &info->monspecs); if (info->monspecs.modedb_len > 0) {
memcpy(edid, default_edid, default_edid_size);
dlfb->edid = edid;
dlfb->edid_size = default_edid_size;
dev_err(dev, "Using default/backup EDID\n");
}
}
}
/* If we've got modes, let's pick a best default mode */ if (info->monspecs.modedb_len > 0) {
for (i = 0; i < info->monspecs.modedb_len; i++) {
mode = &info->monspecs.modedb[i]; if (dlfb_is_valid_mode(mode, dlfb)) {
fb_add_videomode(mode, &info->modelist);
} else {
dev_dbg(dev, "Specified mode %dx%d too big\n",
mode->xres, mode->yres); if (i == 0) /* if we've removed top/best mode */
info->monspecs.misc
&= ~FB_MISC_1ST_DETAIL;
}
}
/* If everything else has failed, fall back to safe default mode */ if (default_vmode == NULL) {
struct fb_videomode fb_vmode = {0};
/* * Add the standard VESA modes to our modelist * Since we don't have EDID, there may be modes that * overspec monitor and/or are incorrect aspect ratio, etc. * But at least the user has a chance to choose
*/ for (i = 0; i < VESA_MODEDB_SIZE; i++) {
mode = (struct fb_videomode *)&vesa_modes[i]; if (dlfb_is_valid_mode(mode, dlfb))
fb_add_videomode(mode, &info->modelist); else
dev_dbg(dev, "VESA mode %dx%d too big\n",
mode->xres, mode->yres);
}
/* * default to resolution safe for projectors * (since they are most common case without EDID)
*/
fb_vmode.xres = 800;
fb_vmode.yres = 600;
fb_vmode.refresh = 60;
default_vmode = fb_find_nearest_mode(&fb_vmode,
&info->modelist);
}
/* If we have good mode and no active clients*/ if ((default_vmode != NULL) && (dlfb->fb_count == 0)) {
/* if not found, look in configuration descriptor */ if (total_len < 0) { if (0 == usb_get_extra_descriptor(intf->cur_altsetting,
0x5f, &desc))
total_len = (int) desc[0];
}
retval = dlfb_setup_modes(dlfb, info, NULL, 0); if (retval != 0) {
dev_err(info->device, "unable to find common mode for display and adapter\n"); goto error;
}
/* urb->transfer_buffer_length set to actual before submit */
usb_fill_bulk_urb(urb, dlfb->udev,
usb_sndbulkpipe(dlfb->udev, OUT_EP_NUM),
buf, size, dlfb_urb_completion, unode);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/* Wait for an in-flight buffer to complete and get re-queued */
ret = down_timeout(&dlfb->urbs.limit_sem, GET_URB_TIMEOUT); if (ret) {
atomic_set(&dlfb->lost_pixels, 1);
dev_warn(&dlfb->udev->dev, "wait for urb interrupted: %d available: %d\n",
ret, dlfb->urbs.available); return NULL;
}
spin_lock_irq(&dlfb->urbs.lock);
BUG_ON(list_empty(&dlfb->urbs.list)); /* reserved one with limit_sem */
entry = dlfb->urbs.list.next;
list_del_init(entry);
dlfb->urbs.available--;
urb->transfer_buffer_length = len; /* set to actual payload len */
ret = usb_submit_urb(urb, GFP_KERNEL); if (ret) {
dlfb_urb_completion(urb); /* because no one else will */
atomic_set(&dlfb->lost_pixels, 1);
dev_err(&dlfb->udev->dev, "submit urb error: %d\n", ret);
} return ret;
}
module_param(console, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
MODULE_PARM_DESC(console, "Allow fbcon to open framebuffer");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.