162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * udlfb.c -- Framebuffer driver for DisplayLink USB controller 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it> 662306a36Sopenharmony_ci * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com> 762306a36Sopenharmony_ci * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Layout is based on skeletonfb by James Simmons and Geert Uytterhoeven, 1062306a36Sopenharmony_ci * usb-skeleton by GregKH. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Device-specific portions based on information from Displaylink, with work 1362306a36Sopenharmony_ci * from Florian Echtler, Henrik Bjerregaard Pedersen, and others. 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/init.h> 1962306a36Sopenharmony_ci#include <linux/usb.h> 2062306a36Sopenharmony_ci#include <linux/uaccess.h> 2162306a36Sopenharmony_ci#include <linux/mm.h> 2262306a36Sopenharmony_ci#include <linux/fb.h> 2362306a36Sopenharmony_ci#include <linux/vmalloc.h> 2462306a36Sopenharmony_ci#include <linux/slab.h> 2562306a36Sopenharmony_ci#include <linux/delay.h> 2662306a36Sopenharmony_ci#include <asm/unaligned.h> 2762306a36Sopenharmony_ci#include <video/udlfb.h> 2862306a36Sopenharmony_ci#include "edid.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define OUT_EP_NUM 1 /* The endpoint number we will use */ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic const struct fb_fix_screeninfo dlfb_fix = { 3362306a36Sopenharmony_ci .id = "udlfb", 3462306a36Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 3562306a36Sopenharmony_ci .visual = FB_VISUAL_TRUECOLOR, 3662306a36Sopenharmony_ci .xpanstep = 0, 3762306a36Sopenharmony_ci .ypanstep = 0, 3862306a36Sopenharmony_ci .ywrapstep = 0, 3962306a36Sopenharmony_ci .accel = FB_ACCEL_NONE, 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic const u32 udlfb_info_flags = FBINFO_READS_FAST | 4362306a36Sopenharmony_ci FBINFO_VIRTFB | 4462306a36Sopenharmony_ci FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_FILLRECT | 4562306a36Sopenharmony_ci FBINFO_HWACCEL_COPYAREA | FBINFO_MISC_ALWAYS_SETPAR; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* 4862306a36Sopenharmony_ci * There are many DisplayLink-based graphics products, all with unique PIDs. 4962306a36Sopenharmony_ci * So we match on DisplayLink's VID + Vendor-Defined Interface Class (0xff) 5062306a36Sopenharmony_ci * We also require a match on SubClass (0x00) and Protocol (0x00), 5162306a36Sopenharmony_ci * which is compatible with all known USB 2.0 era graphics chips and firmware, 5262306a36Sopenharmony_ci * but allows DisplayLink to increment those for any future incompatible chips 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_cistatic const struct usb_device_id id_table[] = { 5562306a36Sopenharmony_ci {.idVendor = 0x17e9, 5662306a36Sopenharmony_ci .bInterfaceClass = 0xff, 5762306a36Sopenharmony_ci .bInterfaceSubClass = 0x00, 5862306a36Sopenharmony_ci .bInterfaceProtocol = 0x00, 5962306a36Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_VENDOR | 6062306a36Sopenharmony_ci USB_DEVICE_ID_MATCH_INT_CLASS | 6162306a36Sopenharmony_ci USB_DEVICE_ID_MATCH_INT_SUBCLASS | 6262306a36Sopenharmony_ci USB_DEVICE_ID_MATCH_INT_PROTOCOL, 6362306a36Sopenharmony_ci }, 6462306a36Sopenharmony_ci {}, 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* module options */ 6962306a36Sopenharmony_cistatic bool console = true; /* Allow fbcon to open framebuffer */ 7062306a36Sopenharmony_cistatic bool fb_defio = true; /* Detect mmap writes using page faults */ 7162306a36Sopenharmony_cistatic bool shadow = true; /* Optionally disable shadow framebuffer */ 7262306a36Sopenharmony_cistatic int pixel_limit; /* Optionally force a pixel resolution limit */ 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistruct dlfb_deferred_free { 7562306a36Sopenharmony_ci struct list_head list; 7662306a36Sopenharmony_ci void *mem; 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic int dlfb_realloc_framebuffer(struct dlfb_data *dlfb, struct fb_info *info, u32 new_len); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* dlfb keeps a list of urbs for efficient bulk transfers */ 8262306a36Sopenharmony_cistatic void dlfb_urb_completion(struct urb *urb); 8362306a36Sopenharmony_cistatic struct urb *dlfb_get_urb(struct dlfb_data *dlfb); 8462306a36Sopenharmony_cistatic int dlfb_submit_urb(struct dlfb_data *dlfb, struct urb * urb, size_t len); 8562306a36Sopenharmony_cistatic int dlfb_alloc_urb_list(struct dlfb_data *dlfb, int count, size_t size); 8662306a36Sopenharmony_cistatic void dlfb_free_urb_list(struct dlfb_data *dlfb); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* 8962306a36Sopenharmony_ci * All DisplayLink bulk operations start with 0xAF, followed by specific code 9062306a36Sopenharmony_ci * All operations are written to buffers which then later get sent to device 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_cistatic char *dlfb_set_register(char *buf, u8 reg, u8 val) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci *buf++ = 0xAF; 9562306a36Sopenharmony_ci *buf++ = 0x20; 9662306a36Sopenharmony_ci *buf++ = reg; 9762306a36Sopenharmony_ci *buf++ = val; 9862306a36Sopenharmony_ci return buf; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic char *dlfb_vidreg_lock(char *buf) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci return dlfb_set_register(buf, 0xFF, 0x00); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic char *dlfb_vidreg_unlock(char *buf) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci return dlfb_set_register(buf, 0xFF, 0xFF); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* 11262306a36Sopenharmony_ci * Map FB_BLANK_* to DisplayLink register 11362306a36Sopenharmony_ci * DLReg FB_BLANK_* 11462306a36Sopenharmony_ci * ----- ----------------------------- 11562306a36Sopenharmony_ci * 0x00 FB_BLANK_UNBLANK (0) 11662306a36Sopenharmony_ci * 0x01 FB_BLANK (1) 11762306a36Sopenharmony_ci * 0x03 FB_BLANK_VSYNC_SUSPEND (2) 11862306a36Sopenharmony_ci * 0x05 FB_BLANK_HSYNC_SUSPEND (3) 11962306a36Sopenharmony_ci * 0x07 FB_BLANK_POWERDOWN (4) Note: requires modeset to come back 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_cistatic char *dlfb_blanking(char *buf, int fb_blank) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci u8 reg; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci switch (fb_blank) { 12662306a36Sopenharmony_ci case FB_BLANK_POWERDOWN: 12762306a36Sopenharmony_ci reg = 0x07; 12862306a36Sopenharmony_ci break; 12962306a36Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 13062306a36Sopenharmony_ci reg = 0x05; 13162306a36Sopenharmony_ci break; 13262306a36Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 13362306a36Sopenharmony_ci reg = 0x03; 13462306a36Sopenharmony_ci break; 13562306a36Sopenharmony_ci case FB_BLANK_NORMAL: 13662306a36Sopenharmony_ci reg = 0x01; 13762306a36Sopenharmony_ci break; 13862306a36Sopenharmony_ci default: 13962306a36Sopenharmony_ci reg = 0x00; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci buf = dlfb_set_register(buf, 0x1F, reg); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return buf; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic char *dlfb_set_color_depth(char *buf, u8 selection) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci return dlfb_set_register(buf, 0x00, selection); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic char *dlfb_set_base16bpp(char *wrptr, u32 base) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci /* the base pointer is 16 bits wide, 0x20 is hi byte. */ 15562306a36Sopenharmony_ci wrptr = dlfb_set_register(wrptr, 0x20, base >> 16); 15662306a36Sopenharmony_ci wrptr = dlfb_set_register(wrptr, 0x21, base >> 8); 15762306a36Sopenharmony_ci return dlfb_set_register(wrptr, 0x22, base); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/* 16162306a36Sopenharmony_ci * DisplayLink HW has separate 16bpp and 8bpp framebuffers. 16262306a36Sopenharmony_ci * In 24bpp modes, the low 323 RGB bits go in the 8bpp framebuffer 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_cistatic char *dlfb_set_base8bpp(char *wrptr, u32 base) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci wrptr = dlfb_set_register(wrptr, 0x26, base >> 16); 16762306a36Sopenharmony_ci wrptr = dlfb_set_register(wrptr, 0x27, base >> 8); 16862306a36Sopenharmony_ci return dlfb_set_register(wrptr, 0x28, base); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic char *dlfb_set_register_16(char *wrptr, u8 reg, u16 value) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci wrptr = dlfb_set_register(wrptr, reg, value >> 8); 17462306a36Sopenharmony_ci return dlfb_set_register(wrptr, reg+1, value); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/* 17862306a36Sopenharmony_ci * This is kind of weird because the controller takes some 17962306a36Sopenharmony_ci * register values in a different byte order than other registers. 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_cistatic char *dlfb_set_register_16be(char *wrptr, u8 reg, u16 value) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci wrptr = dlfb_set_register(wrptr, reg, value); 18462306a36Sopenharmony_ci return dlfb_set_register(wrptr, reg+1, value >> 8); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/* 18862306a36Sopenharmony_ci * LFSR is linear feedback shift register. The reason we have this is 18962306a36Sopenharmony_ci * because the display controller needs to minimize the clock depth of 19062306a36Sopenharmony_ci * various counters used in the display path. So this code reverses the 19162306a36Sopenharmony_ci * provided value into the lfsr16 value by counting backwards to get 19262306a36Sopenharmony_ci * the value that needs to be set in the hardware comparator to get the 19362306a36Sopenharmony_ci * same actual count. This makes sense once you read above a couple of 19462306a36Sopenharmony_ci * times and think about it from a hardware perspective. 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_cistatic u16 dlfb_lfsr16(u16 actual_count) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci u32 lv = 0xFFFF; /* This is the lfsr value that the hw starts with */ 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci while (actual_count--) { 20162306a36Sopenharmony_ci lv = ((lv << 1) | 20262306a36Sopenharmony_ci (((lv >> 15) ^ (lv >> 4) ^ (lv >> 2) ^ (lv >> 1)) & 1)) 20362306a36Sopenharmony_ci & 0xFFFF; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci return (u16) lv; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci/* 21062306a36Sopenharmony_ci * This does LFSR conversion on the value that is to be written. 21162306a36Sopenharmony_ci * See LFSR explanation above for more detail. 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_cistatic char *dlfb_set_register_lfsr16(char *wrptr, u8 reg, u16 value) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci return dlfb_set_register_16(wrptr, reg, dlfb_lfsr16(value)); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci/* 21962306a36Sopenharmony_ci * This takes a standard fbdev screeninfo struct and all of its monitor mode 22062306a36Sopenharmony_ci * details and converts them into the DisplayLink equivalent register commands. 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_cistatic char *dlfb_set_vid_cmds(char *wrptr, struct fb_var_screeninfo *var) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci u16 xds, yds; 22562306a36Sopenharmony_ci u16 xde, yde; 22662306a36Sopenharmony_ci u16 yec; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* x display start */ 22962306a36Sopenharmony_ci xds = var->left_margin + var->hsync_len; 23062306a36Sopenharmony_ci wrptr = dlfb_set_register_lfsr16(wrptr, 0x01, xds); 23162306a36Sopenharmony_ci /* x display end */ 23262306a36Sopenharmony_ci xde = xds + var->xres; 23362306a36Sopenharmony_ci wrptr = dlfb_set_register_lfsr16(wrptr, 0x03, xde); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* y display start */ 23662306a36Sopenharmony_ci yds = var->upper_margin + var->vsync_len; 23762306a36Sopenharmony_ci wrptr = dlfb_set_register_lfsr16(wrptr, 0x05, yds); 23862306a36Sopenharmony_ci /* y display end */ 23962306a36Sopenharmony_ci yde = yds + var->yres; 24062306a36Sopenharmony_ci wrptr = dlfb_set_register_lfsr16(wrptr, 0x07, yde); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* x end count is active + blanking - 1 */ 24362306a36Sopenharmony_ci wrptr = dlfb_set_register_lfsr16(wrptr, 0x09, 24462306a36Sopenharmony_ci xde + var->right_margin - 1); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* libdlo hardcodes hsync start to 1 */ 24762306a36Sopenharmony_ci wrptr = dlfb_set_register_lfsr16(wrptr, 0x0B, 1); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* hsync end is width of sync pulse + 1 */ 25062306a36Sopenharmony_ci wrptr = dlfb_set_register_lfsr16(wrptr, 0x0D, var->hsync_len + 1); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* hpixels is active pixels */ 25362306a36Sopenharmony_ci wrptr = dlfb_set_register_16(wrptr, 0x0F, var->xres); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* yendcount is vertical active + vertical blanking */ 25662306a36Sopenharmony_ci yec = var->yres + var->upper_margin + var->lower_margin + 25762306a36Sopenharmony_ci var->vsync_len; 25862306a36Sopenharmony_ci wrptr = dlfb_set_register_lfsr16(wrptr, 0x11, yec); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* libdlo hardcodes vsync start to 0 */ 26162306a36Sopenharmony_ci wrptr = dlfb_set_register_lfsr16(wrptr, 0x13, 0); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* vsync end is width of vsync pulse */ 26462306a36Sopenharmony_ci wrptr = dlfb_set_register_lfsr16(wrptr, 0x15, var->vsync_len); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* vpixels is active pixels */ 26762306a36Sopenharmony_ci wrptr = dlfb_set_register_16(wrptr, 0x17, var->yres); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* convert picoseconds to 5kHz multiple for pclk5k = x * 1E12/5k */ 27062306a36Sopenharmony_ci wrptr = dlfb_set_register_16be(wrptr, 0x1B, 27162306a36Sopenharmony_ci 200*1000*1000/var->pixclock); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return wrptr; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci/* 27762306a36Sopenharmony_ci * This takes a standard fbdev screeninfo struct that was fetched or prepared 27862306a36Sopenharmony_ci * and then generates the appropriate command sequence that then drives the 27962306a36Sopenharmony_ci * display controller. 28062306a36Sopenharmony_ci */ 28162306a36Sopenharmony_cistatic int dlfb_set_video_mode(struct dlfb_data *dlfb, 28262306a36Sopenharmony_ci struct fb_var_screeninfo *var) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci char *buf; 28562306a36Sopenharmony_ci char *wrptr; 28662306a36Sopenharmony_ci int retval; 28762306a36Sopenharmony_ci int writesize; 28862306a36Sopenharmony_ci struct urb *urb; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (!atomic_read(&dlfb->usb_active)) 29162306a36Sopenharmony_ci return -EPERM; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci urb = dlfb_get_urb(dlfb); 29462306a36Sopenharmony_ci if (!urb) 29562306a36Sopenharmony_ci return -ENOMEM; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci buf = (char *) urb->transfer_buffer; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* 30062306a36Sopenharmony_ci * This first section has to do with setting the base address on the 30162306a36Sopenharmony_ci * controller * associated with the display. There are 2 base 30262306a36Sopenharmony_ci * pointers, currently, we only * use the 16 bpp segment. 30362306a36Sopenharmony_ci */ 30462306a36Sopenharmony_ci wrptr = dlfb_vidreg_lock(buf); 30562306a36Sopenharmony_ci wrptr = dlfb_set_color_depth(wrptr, 0x00); 30662306a36Sopenharmony_ci /* set base for 16bpp segment to 0 */ 30762306a36Sopenharmony_ci wrptr = dlfb_set_base16bpp(wrptr, 0); 30862306a36Sopenharmony_ci /* set base for 8bpp segment to end of fb */ 30962306a36Sopenharmony_ci wrptr = dlfb_set_base8bpp(wrptr, dlfb->info->fix.smem_len); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci wrptr = dlfb_set_vid_cmds(wrptr, var); 31262306a36Sopenharmony_ci wrptr = dlfb_blanking(wrptr, FB_BLANK_UNBLANK); 31362306a36Sopenharmony_ci wrptr = dlfb_vidreg_unlock(wrptr); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci writesize = wrptr - buf; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci retval = dlfb_submit_urb(dlfb, urb, writesize); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci dlfb->blank_mode = FB_BLANK_UNBLANK; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return retval; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic int dlfb_ops_mmap(struct fb_info *info, struct vm_area_struct *vma) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci unsigned long start = vma->vm_start; 32762306a36Sopenharmony_ci unsigned long size = vma->vm_end - vma->vm_start; 32862306a36Sopenharmony_ci unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; 32962306a36Sopenharmony_ci unsigned long page, pos; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (info->fbdefio) 33262306a36Sopenharmony_ci return fb_deferred_io_mmap(info, vma); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) 33562306a36Sopenharmony_ci return -EINVAL; 33662306a36Sopenharmony_ci if (size > info->fix.smem_len) 33762306a36Sopenharmony_ci return -EINVAL; 33862306a36Sopenharmony_ci if (offset > info->fix.smem_len - size) 33962306a36Sopenharmony_ci return -EINVAL; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci pos = (unsigned long)info->fix.smem_start + offset; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci dev_dbg(info->dev, "mmap() framebuffer addr:%lu size:%lu\n", 34462306a36Sopenharmony_ci pos, size); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci while (size > 0) { 34762306a36Sopenharmony_ci page = vmalloc_to_pfn((void *)pos); 34862306a36Sopenharmony_ci if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) 34962306a36Sopenharmony_ci return -EAGAIN; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci start += PAGE_SIZE; 35262306a36Sopenharmony_ci pos += PAGE_SIZE; 35362306a36Sopenharmony_ci if (size > PAGE_SIZE) 35462306a36Sopenharmony_ci size -= PAGE_SIZE; 35562306a36Sopenharmony_ci else 35662306a36Sopenharmony_ci size = 0; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci return 0; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci/* 36362306a36Sopenharmony_ci * Trims identical data from front and back of line 36462306a36Sopenharmony_ci * Sets new front buffer address and width 36562306a36Sopenharmony_ci * And returns byte count of identical pixels 36662306a36Sopenharmony_ci * Assumes CPU natural alignment (unsigned long) 36762306a36Sopenharmony_ci * for back and front buffer ptrs and width 36862306a36Sopenharmony_ci */ 36962306a36Sopenharmony_cistatic int dlfb_trim_hline(const u8 *bback, const u8 **bfront, int *width_bytes) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci int j, k; 37262306a36Sopenharmony_ci const unsigned long *back = (const unsigned long *) bback; 37362306a36Sopenharmony_ci const unsigned long *front = (const unsigned long *) *bfront; 37462306a36Sopenharmony_ci const int width = *width_bytes / sizeof(unsigned long); 37562306a36Sopenharmony_ci int identical; 37662306a36Sopenharmony_ci int start = width; 37762306a36Sopenharmony_ci int end = width; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci for (j = 0; j < width; j++) { 38062306a36Sopenharmony_ci if (back[j] != front[j]) { 38162306a36Sopenharmony_ci start = j; 38262306a36Sopenharmony_ci break; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci for (k = width - 1; k > j; k--) { 38762306a36Sopenharmony_ci if (back[k] != front[k]) { 38862306a36Sopenharmony_ci end = k+1; 38962306a36Sopenharmony_ci break; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci identical = start + (width - end); 39462306a36Sopenharmony_ci *bfront = (u8 *) &front[start]; 39562306a36Sopenharmony_ci *width_bytes = (end - start) * sizeof(unsigned long); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci return identical * sizeof(unsigned long); 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci/* 40162306a36Sopenharmony_ci * Render a command stream for an encoded horizontal line segment of pixels. 40262306a36Sopenharmony_ci * 40362306a36Sopenharmony_ci * A command buffer holds several commands. 40462306a36Sopenharmony_ci * It always begins with a fresh command header 40562306a36Sopenharmony_ci * (the protocol doesn't require this, but we enforce it to allow 40662306a36Sopenharmony_ci * multiple buffers to be potentially encoded and sent in parallel). 40762306a36Sopenharmony_ci * A single command encodes one contiguous horizontal line of pixels 40862306a36Sopenharmony_ci * 40962306a36Sopenharmony_ci * The function relies on the client to do all allocation, so that 41062306a36Sopenharmony_ci * rendering can be done directly to output buffers (e.g. USB URBs). 41162306a36Sopenharmony_ci * The function fills the supplied command buffer, providing information 41262306a36Sopenharmony_ci * on where it left off, so the client may call in again with additional 41362306a36Sopenharmony_ci * buffers if the line will take several buffers to complete. 41462306a36Sopenharmony_ci * 41562306a36Sopenharmony_ci * A single command can transmit a maximum of 256 pixels, 41662306a36Sopenharmony_ci * regardless of the compression ratio (protocol design limit). 41762306a36Sopenharmony_ci * To the hardware, 0 for a size byte means 256 41862306a36Sopenharmony_ci * 41962306a36Sopenharmony_ci * Rather than 256 pixel commands which are either rl or raw encoded, 42062306a36Sopenharmony_ci * the rlx command simply assumes alternating raw and rl spans within one cmd. 42162306a36Sopenharmony_ci * This has a slightly larger header overhead, but produces more even results. 42262306a36Sopenharmony_ci * It also processes all data (read and write) in a single pass. 42362306a36Sopenharmony_ci * Performance benchmarks of common cases show it having just slightly better 42462306a36Sopenharmony_ci * compression than 256 pixel raw or rle commands, with similar CPU consumpion. 42562306a36Sopenharmony_ci * But for very rl friendly data, will compress not quite as well. 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_cistatic void dlfb_compress_hline( 42862306a36Sopenharmony_ci const uint16_t **pixel_start_ptr, 42962306a36Sopenharmony_ci const uint16_t *const pixel_end, 43062306a36Sopenharmony_ci uint32_t *device_address_ptr, 43162306a36Sopenharmony_ci uint8_t **command_buffer_ptr, 43262306a36Sopenharmony_ci const uint8_t *const cmd_buffer_end, 43362306a36Sopenharmony_ci unsigned long back_buffer_offset, 43462306a36Sopenharmony_ci int *ident_ptr) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci const uint16_t *pixel = *pixel_start_ptr; 43762306a36Sopenharmony_ci uint32_t dev_addr = *device_address_ptr; 43862306a36Sopenharmony_ci uint8_t *cmd = *command_buffer_ptr; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci while ((pixel_end > pixel) && 44162306a36Sopenharmony_ci (cmd_buffer_end - MIN_RLX_CMD_BYTES > cmd)) { 44262306a36Sopenharmony_ci uint8_t *raw_pixels_count_byte = NULL; 44362306a36Sopenharmony_ci uint8_t *cmd_pixels_count_byte = NULL; 44462306a36Sopenharmony_ci const uint16_t *raw_pixel_start = NULL; 44562306a36Sopenharmony_ci const uint16_t *cmd_pixel_start, *cmd_pixel_end = NULL; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (back_buffer_offset && 44862306a36Sopenharmony_ci *pixel == *(u16 *)((u8 *)pixel + back_buffer_offset)) { 44962306a36Sopenharmony_ci pixel++; 45062306a36Sopenharmony_ci dev_addr += BPP; 45162306a36Sopenharmony_ci (*ident_ptr)++; 45262306a36Sopenharmony_ci continue; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci *cmd++ = 0xAF; 45662306a36Sopenharmony_ci *cmd++ = 0x6B; 45762306a36Sopenharmony_ci *cmd++ = dev_addr >> 16; 45862306a36Sopenharmony_ci *cmd++ = dev_addr >> 8; 45962306a36Sopenharmony_ci *cmd++ = dev_addr; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci cmd_pixels_count_byte = cmd++; /* we'll know this later */ 46262306a36Sopenharmony_ci cmd_pixel_start = pixel; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci raw_pixels_count_byte = cmd++; /* we'll know this later */ 46562306a36Sopenharmony_ci raw_pixel_start = pixel; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci cmd_pixel_end = pixel + min3(MAX_CMD_PIXELS + 1UL, 46862306a36Sopenharmony_ci (unsigned long)(pixel_end - pixel), 46962306a36Sopenharmony_ci (unsigned long)(cmd_buffer_end - 1 - cmd) / BPP); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (back_buffer_offset) { 47262306a36Sopenharmony_ci /* note: the framebuffer may change under us, so we must test for underflow */ 47362306a36Sopenharmony_ci while (cmd_pixel_end - 1 > pixel && 47462306a36Sopenharmony_ci *(cmd_pixel_end - 1) == *(u16 *)((u8 *)(cmd_pixel_end - 1) + back_buffer_offset)) 47562306a36Sopenharmony_ci cmd_pixel_end--; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci while (pixel < cmd_pixel_end) { 47962306a36Sopenharmony_ci const uint16_t * const repeating_pixel = pixel; 48062306a36Sopenharmony_ci u16 pixel_value = *pixel; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci put_unaligned_be16(pixel_value, cmd); 48362306a36Sopenharmony_ci if (back_buffer_offset) 48462306a36Sopenharmony_ci *(u16 *)((u8 *)pixel + back_buffer_offset) = pixel_value; 48562306a36Sopenharmony_ci cmd += 2; 48662306a36Sopenharmony_ci pixel++; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (unlikely((pixel < cmd_pixel_end) && 48962306a36Sopenharmony_ci (*pixel == pixel_value))) { 49062306a36Sopenharmony_ci /* go back and fill in raw pixel count */ 49162306a36Sopenharmony_ci *raw_pixels_count_byte = ((repeating_pixel - 49262306a36Sopenharmony_ci raw_pixel_start) + 1) & 0xFF; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci do { 49562306a36Sopenharmony_ci if (back_buffer_offset) 49662306a36Sopenharmony_ci *(u16 *)((u8 *)pixel + back_buffer_offset) = pixel_value; 49762306a36Sopenharmony_ci pixel++; 49862306a36Sopenharmony_ci } while ((pixel < cmd_pixel_end) && 49962306a36Sopenharmony_ci (*pixel == pixel_value)); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci /* immediately after raw data is repeat byte */ 50262306a36Sopenharmony_ci *cmd++ = ((pixel - repeating_pixel) - 1) & 0xFF; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci /* Then start another raw pixel span */ 50562306a36Sopenharmony_ci raw_pixel_start = pixel; 50662306a36Sopenharmony_ci raw_pixels_count_byte = cmd++; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (pixel > raw_pixel_start) { 51162306a36Sopenharmony_ci /* finalize last RAW span */ 51262306a36Sopenharmony_ci *raw_pixels_count_byte = (pixel-raw_pixel_start) & 0xFF; 51362306a36Sopenharmony_ci } else { 51462306a36Sopenharmony_ci /* undo unused byte */ 51562306a36Sopenharmony_ci cmd--; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci *cmd_pixels_count_byte = (pixel - cmd_pixel_start) & 0xFF; 51962306a36Sopenharmony_ci dev_addr += (u8 *)pixel - (u8 *)cmd_pixel_start; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (cmd_buffer_end - MIN_RLX_CMD_BYTES <= cmd) { 52362306a36Sopenharmony_ci /* Fill leftover bytes with no-ops */ 52462306a36Sopenharmony_ci if (cmd_buffer_end > cmd) 52562306a36Sopenharmony_ci memset(cmd, 0xAF, cmd_buffer_end - cmd); 52662306a36Sopenharmony_ci cmd = (uint8_t *) cmd_buffer_end; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci *command_buffer_ptr = cmd; 53062306a36Sopenharmony_ci *pixel_start_ptr = pixel; 53162306a36Sopenharmony_ci *device_address_ptr = dev_addr; 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci/* 53562306a36Sopenharmony_ci * There are 3 copies of every pixel: The front buffer that the fbdev 53662306a36Sopenharmony_ci * client renders to, the actual framebuffer across the USB bus in hardware 53762306a36Sopenharmony_ci * (that we can only write to, slowly, and can never read), and (optionally) 53862306a36Sopenharmony_ci * our shadow copy that tracks what's been sent to that hardware buffer. 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_cistatic int dlfb_render_hline(struct dlfb_data *dlfb, struct urb **urb_ptr, 54162306a36Sopenharmony_ci const char *front, char **urb_buf_ptr, 54262306a36Sopenharmony_ci u32 byte_offset, u32 byte_width, 54362306a36Sopenharmony_ci int *ident_ptr, int *sent_ptr) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci const u8 *line_start, *line_end, *next_pixel; 54662306a36Sopenharmony_ci u32 dev_addr = dlfb->base16 + byte_offset; 54762306a36Sopenharmony_ci struct urb *urb = *urb_ptr; 54862306a36Sopenharmony_ci u8 *cmd = *urb_buf_ptr; 54962306a36Sopenharmony_ci u8 *cmd_end = (u8 *) urb->transfer_buffer + urb->transfer_buffer_length; 55062306a36Sopenharmony_ci unsigned long back_buffer_offset = 0; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci line_start = (u8 *) (front + byte_offset); 55362306a36Sopenharmony_ci next_pixel = line_start; 55462306a36Sopenharmony_ci line_end = next_pixel + byte_width; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (dlfb->backing_buffer) { 55762306a36Sopenharmony_ci int offset; 55862306a36Sopenharmony_ci const u8 *back_start = (u8 *) (dlfb->backing_buffer 55962306a36Sopenharmony_ci + byte_offset); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci back_buffer_offset = (unsigned long)back_start - (unsigned long)line_start; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci *ident_ptr += dlfb_trim_hline(back_start, &next_pixel, 56462306a36Sopenharmony_ci &byte_width); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci offset = next_pixel - line_start; 56762306a36Sopenharmony_ci line_end = next_pixel + byte_width; 56862306a36Sopenharmony_ci dev_addr += offset; 56962306a36Sopenharmony_ci back_start += offset; 57062306a36Sopenharmony_ci line_start += offset; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci while (next_pixel < line_end) { 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci dlfb_compress_hline((const uint16_t **) &next_pixel, 57662306a36Sopenharmony_ci (const uint16_t *) line_end, &dev_addr, 57762306a36Sopenharmony_ci (u8 **) &cmd, (u8 *) cmd_end, back_buffer_offset, 57862306a36Sopenharmony_ci ident_ptr); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (cmd >= cmd_end) { 58162306a36Sopenharmony_ci int len = cmd - (u8 *) urb->transfer_buffer; 58262306a36Sopenharmony_ci if (dlfb_submit_urb(dlfb, urb, len)) 58362306a36Sopenharmony_ci return 1; /* lost pixels is set */ 58462306a36Sopenharmony_ci *sent_ptr += len; 58562306a36Sopenharmony_ci urb = dlfb_get_urb(dlfb); 58662306a36Sopenharmony_ci if (!urb) 58762306a36Sopenharmony_ci return 1; /* lost_pixels is set */ 58862306a36Sopenharmony_ci *urb_ptr = urb; 58962306a36Sopenharmony_ci cmd = urb->transfer_buffer; 59062306a36Sopenharmony_ci cmd_end = &cmd[urb->transfer_buffer_length]; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci *urb_buf_ptr = cmd; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci return 0; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cistatic int dlfb_handle_damage(struct dlfb_data *dlfb, int x, int y, int width, int height) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci int i, ret; 60262306a36Sopenharmony_ci char *cmd; 60362306a36Sopenharmony_ci cycles_t start_cycles, end_cycles; 60462306a36Sopenharmony_ci int bytes_sent = 0; 60562306a36Sopenharmony_ci int bytes_identical = 0; 60662306a36Sopenharmony_ci struct urb *urb; 60762306a36Sopenharmony_ci int aligned_x; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci start_cycles = get_cycles(); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci mutex_lock(&dlfb->render_mutex); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci aligned_x = DL_ALIGN_DOWN(x, sizeof(unsigned long)); 61462306a36Sopenharmony_ci width = DL_ALIGN_UP(width + (x-aligned_x), sizeof(unsigned long)); 61562306a36Sopenharmony_ci x = aligned_x; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if ((width <= 0) || 61862306a36Sopenharmony_ci (x + width > dlfb->info->var.xres) || 61962306a36Sopenharmony_ci (y + height > dlfb->info->var.yres)) { 62062306a36Sopenharmony_ci ret = -EINVAL; 62162306a36Sopenharmony_ci goto unlock_ret; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (!atomic_read(&dlfb->usb_active)) { 62562306a36Sopenharmony_ci ret = 0; 62662306a36Sopenharmony_ci goto unlock_ret; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci urb = dlfb_get_urb(dlfb); 63062306a36Sopenharmony_ci if (!urb) { 63162306a36Sopenharmony_ci ret = 0; 63262306a36Sopenharmony_ci goto unlock_ret; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci cmd = urb->transfer_buffer; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci for (i = y; i < y + height ; i++) { 63762306a36Sopenharmony_ci const int line_offset = dlfb->info->fix.line_length * i; 63862306a36Sopenharmony_ci const int byte_offset = line_offset + (x * BPP); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (dlfb_render_hline(dlfb, &urb, 64162306a36Sopenharmony_ci (char *) dlfb->info->fix.smem_start, 64262306a36Sopenharmony_ci &cmd, byte_offset, width * BPP, 64362306a36Sopenharmony_ci &bytes_identical, &bytes_sent)) 64462306a36Sopenharmony_ci goto error; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci if (cmd > (char *) urb->transfer_buffer) { 64862306a36Sopenharmony_ci int len; 64962306a36Sopenharmony_ci if (cmd < (char *) urb->transfer_buffer + urb->transfer_buffer_length) 65062306a36Sopenharmony_ci *cmd++ = 0xAF; 65162306a36Sopenharmony_ci /* Send partial buffer remaining before exiting */ 65262306a36Sopenharmony_ci len = cmd - (char *) urb->transfer_buffer; 65362306a36Sopenharmony_ci dlfb_submit_urb(dlfb, urb, len); 65462306a36Sopenharmony_ci bytes_sent += len; 65562306a36Sopenharmony_ci } else 65662306a36Sopenharmony_ci dlfb_urb_completion(urb); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cierror: 65962306a36Sopenharmony_ci atomic_add(bytes_sent, &dlfb->bytes_sent); 66062306a36Sopenharmony_ci atomic_add(bytes_identical, &dlfb->bytes_identical); 66162306a36Sopenharmony_ci atomic_add(width*height*2, &dlfb->bytes_rendered); 66262306a36Sopenharmony_ci end_cycles = get_cycles(); 66362306a36Sopenharmony_ci atomic_add(((unsigned int) ((end_cycles - start_cycles) 66462306a36Sopenharmony_ci >> 10)), /* Kcycles */ 66562306a36Sopenharmony_ci &dlfb->cpu_kcycles_used); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci ret = 0; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ciunlock_ret: 67062306a36Sopenharmony_ci mutex_unlock(&dlfb->render_mutex); 67162306a36Sopenharmony_ci return ret; 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic void dlfb_init_damage(struct dlfb_data *dlfb) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci dlfb->damage_x = INT_MAX; 67762306a36Sopenharmony_ci dlfb->damage_x2 = 0; 67862306a36Sopenharmony_ci dlfb->damage_y = INT_MAX; 67962306a36Sopenharmony_ci dlfb->damage_y2 = 0; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic void dlfb_damage_work(struct work_struct *w) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci struct dlfb_data *dlfb = container_of(w, struct dlfb_data, damage_work); 68562306a36Sopenharmony_ci int x, x2, y, y2; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci spin_lock_irq(&dlfb->damage_lock); 68862306a36Sopenharmony_ci x = dlfb->damage_x; 68962306a36Sopenharmony_ci x2 = dlfb->damage_x2; 69062306a36Sopenharmony_ci y = dlfb->damage_y; 69162306a36Sopenharmony_ci y2 = dlfb->damage_y2; 69262306a36Sopenharmony_ci dlfb_init_damage(dlfb); 69362306a36Sopenharmony_ci spin_unlock_irq(&dlfb->damage_lock); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci if (x < x2 && y < y2) 69662306a36Sopenharmony_ci dlfb_handle_damage(dlfb, x, y, x2 - x, y2 - y); 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic void dlfb_offload_damage(struct dlfb_data *dlfb, int x, int y, int width, int height) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci unsigned long flags; 70262306a36Sopenharmony_ci int x2 = x + width; 70362306a36Sopenharmony_ci int y2 = y + height; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if (x >= x2 || y >= y2) 70662306a36Sopenharmony_ci return; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci spin_lock_irqsave(&dlfb->damage_lock, flags); 70962306a36Sopenharmony_ci dlfb->damage_x = min(x, dlfb->damage_x); 71062306a36Sopenharmony_ci dlfb->damage_x2 = max(x2, dlfb->damage_x2); 71162306a36Sopenharmony_ci dlfb->damage_y = min(y, dlfb->damage_y); 71262306a36Sopenharmony_ci dlfb->damage_y2 = max(y2, dlfb->damage_y2); 71362306a36Sopenharmony_ci spin_unlock_irqrestore(&dlfb->damage_lock, flags); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci schedule_work(&dlfb->damage_work); 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci/* 71962306a36Sopenharmony_ci * Path triggered by usermode clients who write to filesystem 72062306a36Sopenharmony_ci * e.g. cat filename > /dev/fb1 72162306a36Sopenharmony_ci * Not used by X Windows or text-mode console. But useful for testing. 72262306a36Sopenharmony_ci * Slow because of extra copy and we must assume all pixels dirty. 72362306a36Sopenharmony_ci */ 72462306a36Sopenharmony_cistatic ssize_t dlfb_ops_write(struct fb_info *info, const char __user *buf, 72562306a36Sopenharmony_ci size_t count, loff_t *ppos) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci ssize_t result; 72862306a36Sopenharmony_ci struct dlfb_data *dlfb = info->par; 72962306a36Sopenharmony_ci u32 offset = (u32) *ppos; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci result = fb_sys_write(info, buf, count, ppos); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci if (result > 0) { 73462306a36Sopenharmony_ci int start = max((int)(offset / info->fix.line_length), 0); 73562306a36Sopenharmony_ci int lines = min((u32)((result / info->fix.line_length) + 1), 73662306a36Sopenharmony_ci (u32)info->var.yres); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci dlfb_handle_damage(dlfb, 0, start, info->var.xres, 73962306a36Sopenharmony_ci lines); 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci return result; 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci/* hardware has native COPY command (see libdlo), but not worth it for fbcon */ 74662306a36Sopenharmony_cistatic void dlfb_ops_copyarea(struct fb_info *info, 74762306a36Sopenharmony_ci const struct fb_copyarea *area) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci struct dlfb_data *dlfb = info->par; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci sys_copyarea(info, area); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci dlfb_offload_damage(dlfb, area->dx, area->dy, 75562306a36Sopenharmony_ci area->width, area->height); 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic void dlfb_ops_imageblit(struct fb_info *info, 75962306a36Sopenharmony_ci const struct fb_image *image) 76062306a36Sopenharmony_ci{ 76162306a36Sopenharmony_ci struct dlfb_data *dlfb = info->par; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci sys_imageblit(info, image); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci dlfb_offload_damage(dlfb, image->dx, image->dy, 76662306a36Sopenharmony_ci image->width, image->height); 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic void dlfb_ops_fillrect(struct fb_info *info, 77062306a36Sopenharmony_ci const struct fb_fillrect *rect) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci struct dlfb_data *dlfb = info->par; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci sys_fillrect(info, rect); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci dlfb_offload_damage(dlfb, rect->dx, rect->dy, rect->width, 77762306a36Sopenharmony_ci rect->height); 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci/* 78162306a36Sopenharmony_ci * NOTE: fb_defio.c is holding info->fbdefio.mutex 78262306a36Sopenharmony_ci * Touching ANY framebuffer memory that triggers a page fault 78362306a36Sopenharmony_ci * in fb_defio will cause a deadlock, when it also tries to 78462306a36Sopenharmony_ci * grab the same mutex. 78562306a36Sopenharmony_ci */ 78662306a36Sopenharmony_cistatic void dlfb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci struct fb_deferred_io_pageref *pageref; 78962306a36Sopenharmony_ci struct dlfb_data *dlfb = info->par; 79062306a36Sopenharmony_ci struct urb *urb; 79162306a36Sopenharmony_ci char *cmd; 79262306a36Sopenharmony_ci cycles_t start_cycles, end_cycles; 79362306a36Sopenharmony_ci int bytes_sent = 0; 79462306a36Sopenharmony_ci int bytes_identical = 0; 79562306a36Sopenharmony_ci int bytes_rendered = 0; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci mutex_lock(&dlfb->render_mutex); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci if (!fb_defio) 80062306a36Sopenharmony_ci goto unlock_ret; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci if (!atomic_read(&dlfb->usb_active)) 80362306a36Sopenharmony_ci goto unlock_ret; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci start_cycles = get_cycles(); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci urb = dlfb_get_urb(dlfb); 80862306a36Sopenharmony_ci if (!urb) 80962306a36Sopenharmony_ci goto unlock_ret; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci cmd = urb->transfer_buffer; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci /* walk the written page list and render each to device */ 81462306a36Sopenharmony_ci list_for_each_entry(pageref, pagereflist, list) { 81562306a36Sopenharmony_ci if (dlfb_render_hline(dlfb, &urb, (char *) info->fix.smem_start, 81662306a36Sopenharmony_ci &cmd, pageref->offset, PAGE_SIZE, 81762306a36Sopenharmony_ci &bytes_identical, &bytes_sent)) 81862306a36Sopenharmony_ci goto error; 81962306a36Sopenharmony_ci bytes_rendered += PAGE_SIZE; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci if (cmd > (char *) urb->transfer_buffer) { 82362306a36Sopenharmony_ci int len; 82462306a36Sopenharmony_ci if (cmd < (char *) urb->transfer_buffer + urb->transfer_buffer_length) 82562306a36Sopenharmony_ci *cmd++ = 0xAF; 82662306a36Sopenharmony_ci /* Send partial buffer remaining before exiting */ 82762306a36Sopenharmony_ci len = cmd - (char *) urb->transfer_buffer; 82862306a36Sopenharmony_ci dlfb_submit_urb(dlfb, urb, len); 82962306a36Sopenharmony_ci bytes_sent += len; 83062306a36Sopenharmony_ci } else 83162306a36Sopenharmony_ci dlfb_urb_completion(urb); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_cierror: 83462306a36Sopenharmony_ci atomic_add(bytes_sent, &dlfb->bytes_sent); 83562306a36Sopenharmony_ci atomic_add(bytes_identical, &dlfb->bytes_identical); 83662306a36Sopenharmony_ci atomic_add(bytes_rendered, &dlfb->bytes_rendered); 83762306a36Sopenharmony_ci end_cycles = get_cycles(); 83862306a36Sopenharmony_ci atomic_add(((unsigned int) ((end_cycles - start_cycles) 83962306a36Sopenharmony_ci >> 10)), /* Kcycles */ 84062306a36Sopenharmony_ci &dlfb->cpu_kcycles_used); 84162306a36Sopenharmony_ciunlock_ret: 84262306a36Sopenharmony_ci mutex_unlock(&dlfb->render_mutex); 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_cistatic int dlfb_get_edid(struct dlfb_data *dlfb, char *edid, int len) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci int i, ret; 84862306a36Sopenharmony_ci char *rbuf; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci rbuf = kmalloc(2, GFP_KERNEL); 85162306a36Sopenharmony_ci if (!rbuf) 85262306a36Sopenharmony_ci return 0; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci for (i = 0; i < len; i++) { 85562306a36Sopenharmony_ci ret = usb_control_msg(dlfb->udev, 85662306a36Sopenharmony_ci usb_rcvctrlpipe(dlfb->udev, 0), 0x02, 85762306a36Sopenharmony_ci (0x80 | (0x02 << 5)), i << 8, 0xA1, 85862306a36Sopenharmony_ci rbuf, 2, USB_CTRL_GET_TIMEOUT); 85962306a36Sopenharmony_ci if (ret < 2) { 86062306a36Sopenharmony_ci dev_err(&dlfb->udev->dev, 86162306a36Sopenharmony_ci "Read EDID byte %d failed: %d\n", i, ret); 86262306a36Sopenharmony_ci i--; 86362306a36Sopenharmony_ci break; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci edid[i] = rbuf[1]; 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci kfree(rbuf); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci return i; 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cistatic int dlfb_ops_ioctl(struct fb_info *info, unsigned int cmd, 87462306a36Sopenharmony_ci unsigned long arg) 87562306a36Sopenharmony_ci{ 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci struct dlfb_data *dlfb = info->par; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci if (!atomic_read(&dlfb->usb_active)) 88062306a36Sopenharmony_ci return 0; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci /* TODO: Update X server to get this from sysfs instead */ 88362306a36Sopenharmony_ci if (cmd == DLFB_IOCTL_RETURN_EDID) { 88462306a36Sopenharmony_ci void __user *edid = (void __user *)arg; 88562306a36Sopenharmony_ci if (copy_to_user(edid, dlfb->edid, dlfb->edid_size)) 88662306a36Sopenharmony_ci return -EFAULT; 88762306a36Sopenharmony_ci return 0; 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci /* TODO: Help propose a standard fb.h ioctl to report mmap damage */ 89162306a36Sopenharmony_ci if (cmd == DLFB_IOCTL_REPORT_DAMAGE) { 89262306a36Sopenharmony_ci struct dloarea area; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci if (copy_from_user(&area, (void __user *)arg, 89562306a36Sopenharmony_ci sizeof(struct dloarea))) 89662306a36Sopenharmony_ci return -EFAULT; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci /* 89962306a36Sopenharmony_ci * If we have a damage-aware client, turn fb_defio "off" 90062306a36Sopenharmony_ci * To avoid perf imact of unnecessary page fault handling. 90162306a36Sopenharmony_ci * Done by resetting the delay for this fb_info to a very 90262306a36Sopenharmony_ci * long period. Pages will become writable and stay that way. 90362306a36Sopenharmony_ci * Reset to normal value when all clients have closed this fb. 90462306a36Sopenharmony_ci */ 90562306a36Sopenharmony_ci if (info->fbdefio) 90662306a36Sopenharmony_ci info->fbdefio->delay = DL_DEFIO_WRITE_DISABLE; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci if (area.x < 0) 90962306a36Sopenharmony_ci area.x = 0; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci if (area.x > info->var.xres) 91262306a36Sopenharmony_ci area.x = info->var.xres; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci if (area.y < 0) 91562306a36Sopenharmony_ci area.y = 0; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci if (area.y > info->var.yres) 91862306a36Sopenharmony_ci area.y = info->var.yres; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci dlfb_handle_damage(dlfb, area.x, area.y, area.w, area.h); 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci return 0; 92462306a36Sopenharmony_ci} 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci/* taken from vesafb */ 92762306a36Sopenharmony_cistatic int 92862306a36Sopenharmony_cidlfb_ops_setcolreg(unsigned regno, unsigned red, unsigned green, 92962306a36Sopenharmony_ci unsigned blue, unsigned transp, struct fb_info *info) 93062306a36Sopenharmony_ci{ 93162306a36Sopenharmony_ci int err = 0; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci if (regno >= info->cmap.len) 93462306a36Sopenharmony_ci return 1; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci if (regno < 16) { 93762306a36Sopenharmony_ci if (info->var.red.offset == 10) { 93862306a36Sopenharmony_ci /* 1:5:5:5 */ 93962306a36Sopenharmony_ci ((u32 *) (info->pseudo_palette))[regno] = 94062306a36Sopenharmony_ci ((red & 0xf800) >> 1) | 94162306a36Sopenharmony_ci ((green & 0xf800) >> 6) | ((blue & 0xf800) >> 11); 94262306a36Sopenharmony_ci } else { 94362306a36Sopenharmony_ci /* 0:5:6:5 */ 94462306a36Sopenharmony_ci ((u32 *) (info->pseudo_palette))[regno] = 94562306a36Sopenharmony_ci ((red & 0xf800)) | 94662306a36Sopenharmony_ci ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11); 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci } 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci return err; 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci/* 95462306a36Sopenharmony_ci * It's common for several clients to have framebuffer open simultaneously. 95562306a36Sopenharmony_ci * e.g. both fbcon and X. Makes things interesting. 95662306a36Sopenharmony_ci * Assumes caller is holding info->lock (for open and release at least) 95762306a36Sopenharmony_ci */ 95862306a36Sopenharmony_cistatic int dlfb_ops_open(struct fb_info *info, int user) 95962306a36Sopenharmony_ci{ 96062306a36Sopenharmony_ci struct dlfb_data *dlfb = info->par; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci /* 96362306a36Sopenharmony_ci * fbcon aggressively connects to first framebuffer it finds, 96462306a36Sopenharmony_ci * preventing other clients (X) from working properly. Usually 96562306a36Sopenharmony_ci * not what the user wants. Fail by default with option to enable. 96662306a36Sopenharmony_ci */ 96762306a36Sopenharmony_ci if ((user == 0) && (!console)) 96862306a36Sopenharmony_ci return -EBUSY; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci /* If the USB device is gone, we don't accept new opens */ 97162306a36Sopenharmony_ci if (dlfb->virtualized) 97262306a36Sopenharmony_ci return -ENODEV; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci dlfb->fb_count++; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci if (fb_defio && (info->fbdefio == NULL)) { 97762306a36Sopenharmony_ci /* enable defio at last moment if not disabled by client */ 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci struct fb_deferred_io *fbdefio; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci fbdefio = kzalloc(sizeof(struct fb_deferred_io), GFP_KERNEL); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci if (fbdefio) { 98462306a36Sopenharmony_ci fbdefio->delay = DL_DEFIO_WRITE_DELAY; 98562306a36Sopenharmony_ci fbdefio->sort_pagereflist = true; 98662306a36Sopenharmony_ci fbdefio->deferred_io = dlfb_dpy_deferred_io; 98762306a36Sopenharmony_ci } 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci info->fbdefio = fbdefio; 99062306a36Sopenharmony_ci fb_deferred_io_init(info); 99162306a36Sopenharmony_ci } 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci dev_dbg(info->dev, "open, user=%d fb_info=%p count=%d\n", 99462306a36Sopenharmony_ci user, info, dlfb->fb_count); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci return 0; 99762306a36Sopenharmony_ci} 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_cistatic void dlfb_ops_destroy(struct fb_info *info) 100062306a36Sopenharmony_ci{ 100162306a36Sopenharmony_ci struct dlfb_data *dlfb = info->par; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci cancel_work_sync(&dlfb->damage_work); 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci mutex_destroy(&dlfb->render_mutex); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci if (info->cmap.len != 0) 100862306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 100962306a36Sopenharmony_ci if (info->monspecs.modedb) 101062306a36Sopenharmony_ci fb_destroy_modedb(info->monspecs.modedb); 101162306a36Sopenharmony_ci vfree(info->screen_buffer); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci fb_destroy_modelist(&info->modelist); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci while (!list_empty(&dlfb->deferred_free)) { 101662306a36Sopenharmony_ci struct dlfb_deferred_free *d = list_entry(dlfb->deferred_free.next, struct dlfb_deferred_free, list); 101762306a36Sopenharmony_ci list_del(&d->list); 101862306a36Sopenharmony_ci vfree(d->mem); 101962306a36Sopenharmony_ci kfree(d); 102062306a36Sopenharmony_ci } 102162306a36Sopenharmony_ci vfree(dlfb->backing_buffer); 102262306a36Sopenharmony_ci kfree(dlfb->edid); 102362306a36Sopenharmony_ci dlfb_free_urb_list(dlfb); 102462306a36Sopenharmony_ci usb_put_dev(dlfb->udev); 102562306a36Sopenharmony_ci kfree(dlfb); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci /* Assume info structure is freed after this point */ 102862306a36Sopenharmony_ci framebuffer_release(info); 102962306a36Sopenharmony_ci} 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci/* 103262306a36Sopenharmony_ci * Assumes caller is holding info->lock mutex (for open and release at least) 103362306a36Sopenharmony_ci */ 103462306a36Sopenharmony_cistatic int dlfb_ops_release(struct fb_info *info, int user) 103562306a36Sopenharmony_ci{ 103662306a36Sopenharmony_ci struct dlfb_data *dlfb = info->par; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci dlfb->fb_count--; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci if ((dlfb->fb_count == 0) && (info->fbdefio)) { 104162306a36Sopenharmony_ci fb_deferred_io_cleanup(info); 104262306a36Sopenharmony_ci kfree(info->fbdefio); 104362306a36Sopenharmony_ci info->fbdefio = NULL; 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci dev_dbg(info->dev, "release, user=%d count=%d\n", user, dlfb->fb_count); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci return 0; 104962306a36Sopenharmony_ci} 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci/* 105262306a36Sopenharmony_ci * Check whether a video mode is supported by the DisplayLink chip 105362306a36Sopenharmony_ci * We start from monitor's modes, so don't need to filter that here 105462306a36Sopenharmony_ci */ 105562306a36Sopenharmony_cistatic int dlfb_is_valid_mode(struct fb_videomode *mode, struct dlfb_data *dlfb) 105662306a36Sopenharmony_ci{ 105762306a36Sopenharmony_ci if (mode->xres * mode->yres > dlfb->sku_pixel_limit) 105862306a36Sopenharmony_ci return 0; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci return 1; 106162306a36Sopenharmony_ci} 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_cistatic void dlfb_var_color_format(struct fb_var_screeninfo *var) 106462306a36Sopenharmony_ci{ 106562306a36Sopenharmony_ci const struct fb_bitfield red = { 11, 5, 0 }; 106662306a36Sopenharmony_ci const struct fb_bitfield green = { 5, 6, 0 }; 106762306a36Sopenharmony_ci const struct fb_bitfield blue = { 0, 5, 0 }; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci var->bits_per_pixel = 16; 107062306a36Sopenharmony_ci var->red = red; 107162306a36Sopenharmony_ci var->green = green; 107262306a36Sopenharmony_ci var->blue = blue; 107362306a36Sopenharmony_ci} 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_cistatic int dlfb_ops_check_var(struct fb_var_screeninfo *var, 107662306a36Sopenharmony_ci struct fb_info *info) 107762306a36Sopenharmony_ci{ 107862306a36Sopenharmony_ci struct fb_videomode mode; 107962306a36Sopenharmony_ci struct dlfb_data *dlfb = info->par; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci /* set device-specific elements of var unrelated to mode */ 108262306a36Sopenharmony_ci dlfb_var_color_format(var); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci fb_var_to_videomode(&mode, var); 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci if (!dlfb_is_valid_mode(&mode, dlfb)) 108762306a36Sopenharmony_ci return -EINVAL; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci return 0; 109062306a36Sopenharmony_ci} 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_cistatic int dlfb_ops_set_par(struct fb_info *info) 109362306a36Sopenharmony_ci{ 109462306a36Sopenharmony_ci struct dlfb_data *dlfb = info->par; 109562306a36Sopenharmony_ci int result; 109662306a36Sopenharmony_ci u16 *pix_framebuffer; 109762306a36Sopenharmony_ci int i; 109862306a36Sopenharmony_ci struct fb_var_screeninfo fvs; 109962306a36Sopenharmony_ci u32 line_length = info->var.xres * (info->var.bits_per_pixel / 8); 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci /* clear the activate field because it causes spurious miscompares */ 110262306a36Sopenharmony_ci fvs = info->var; 110362306a36Sopenharmony_ci fvs.activate = 0; 110462306a36Sopenharmony_ci fvs.vmode &= ~FB_VMODE_SMOOTH_XPAN; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci if (!memcmp(&dlfb->current_mode, &fvs, sizeof(struct fb_var_screeninfo))) 110762306a36Sopenharmony_ci return 0; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci result = dlfb_realloc_framebuffer(dlfb, info, info->var.yres * line_length); 111062306a36Sopenharmony_ci if (result) 111162306a36Sopenharmony_ci return result; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci result = dlfb_set_video_mode(dlfb, &info->var); 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci if (result) 111662306a36Sopenharmony_ci return result; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci dlfb->current_mode = fvs; 111962306a36Sopenharmony_ci info->fix.line_length = line_length; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci if (dlfb->fb_count == 0) { 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci /* paint greenscreen */ 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci pix_framebuffer = (u16 *)info->screen_buffer; 112662306a36Sopenharmony_ci for (i = 0; i < info->fix.smem_len / 2; i++) 112762306a36Sopenharmony_ci pix_framebuffer[i] = 0x37e6; 112862306a36Sopenharmony_ci } 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci dlfb_handle_damage(dlfb, 0, 0, info->var.xres, info->var.yres); 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci return 0; 113362306a36Sopenharmony_ci} 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci/* To fonzi the jukebox (e.g. make blanking changes take effect) */ 113662306a36Sopenharmony_cistatic char *dlfb_dummy_render(char *buf) 113762306a36Sopenharmony_ci{ 113862306a36Sopenharmony_ci *buf++ = 0xAF; 113962306a36Sopenharmony_ci *buf++ = 0x6A; /* copy */ 114062306a36Sopenharmony_ci *buf++ = 0x00; /* from address*/ 114162306a36Sopenharmony_ci *buf++ = 0x00; 114262306a36Sopenharmony_ci *buf++ = 0x00; 114362306a36Sopenharmony_ci *buf++ = 0x01; /* one pixel */ 114462306a36Sopenharmony_ci *buf++ = 0x00; /* to address */ 114562306a36Sopenharmony_ci *buf++ = 0x00; 114662306a36Sopenharmony_ci *buf++ = 0x00; 114762306a36Sopenharmony_ci return buf; 114862306a36Sopenharmony_ci} 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci/* 115162306a36Sopenharmony_ci * In order to come back from full DPMS off, we need to set the mode again 115262306a36Sopenharmony_ci */ 115362306a36Sopenharmony_cistatic int dlfb_ops_blank(int blank_mode, struct fb_info *info) 115462306a36Sopenharmony_ci{ 115562306a36Sopenharmony_ci struct dlfb_data *dlfb = info->par; 115662306a36Sopenharmony_ci char *bufptr; 115762306a36Sopenharmony_ci struct urb *urb; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci dev_dbg(info->dev, "blank, mode %d --> %d\n", 116062306a36Sopenharmony_ci dlfb->blank_mode, blank_mode); 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci if ((dlfb->blank_mode == FB_BLANK_POWERDOWN) && 116362306a36Sopenharmony_ci (blank_mode != FB_BLANK_POWERDOWN)) { 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci /* returning from powerdown requires a fresh modeset */ 116662306a36Sopenharmony_ci dlfb_set_video_mode(dlfb, &info->var); 116762306a36Sopenharmony_ci } 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci urb = dlfb_get_urb(dlfb); 117062306a36Sopenharmony_ci if (!urb) 117162306a36Sopenharmony_ci return 0; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci bufptr = (char *) urb->transfer_buffer; 117462306a36Sopenharmony_ci bufptr = dlfb_vidreg_lock(bufptr); 117562306a36Sopenharmony_ci bufptr = dlfb_blanking(bufptr, blank_mode); 117662306a36Sopenharmony_ci bufptr = dlfb_vidreg_unlock(bufptr); 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci /* seems like a render op is needed to have blank change take effect */ 117962306a36Sopenharmony_ci bufptr = dlfb_dummy_render(bufptr); 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci dlfb_submit_urb(dlfb, urb, bufptr - 118262306a36Sopenharmony_ci (char *) urb->transfer_buffer); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci dlfb->blank_mode = blank_mode; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci return 0; 118762306a36Sopenharmony_ci} 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_cistatic const struct fb_ops dlfb_ops = { 119062306a36Sopenharmony_ci .owner = THIS_MODULE, 119162306a36Sopenharmony_ci .fb_read = fb_sys_read, 119262306a36Sopenharmony_ci .fb_write = dlfb_ops_write, 119362306a36Sopenharmony_ci .fb_setcolreg = dlfb_ops_setcolreg, 119462306a36Sopenharmony_ci .fb_fillrect = dlfb_ops_fillrect, 119562306a36Sopenharmony_ci .fb_copyarea = dlfb_ops_copyarea, 119662306a36Sopenharmony_ci .fb_imageblit = dlfb_ops_imageblit, 119762306a36Sopenharmony_ci .fb_mmap = dlfb_ops_mmap, 119862306a36Sopenharmony_ci .fb_ioctl = dlfb_ops_ioctl, 119962306a36Sopenharmony_ci .fb_open = dlfb_ops_open, 120062306a36Sopenharmony_ci .fb_release = dlfb_ops_release, 120162306a36Sopenharmony_ci .fb_blank = dlfb_ops_blank, 120262306a36Sopenharmony_ci .fb_check_var = dlfb_ops_check_var, 120362306a36Sopenharmony_ci .fb_set_par = dlfb_ops_set_par, 120462306a36Sopenharmony_ci .fb_destroy = dlfb_ops_destroy, 120562306a36Sopenharmony_ci}; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_cistatic void dlfb_deferred_vfree(struct dlfb_data *dlfb, void *mem) 120962306a36Sopenharmony_ci{ 121062306a36Sopenharmony_ci struct dlfb_deferred_free *d = kmalloc(sizeof(struct dlfb_deferred_free), GFP_KERNEL); 121162306a36Sopenharmony_ci if (!d) 121262306a36Sopenharmony_ci return; 121362306a36Sopenharmony_ci d->mem = mem; 121462306a36Sopenharmony_ci list_add(&d->list, &dlfb->deferred_free); 121562306a36Sopenharmony_ci} 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci/* 121862306a36Sopenharmony_ci * Assumes &info->lock held by caller 121962306a36Sopenharmony_ci * Assumes no active clients have framebuffer open 122062306a36Sopenharmony_ci */ 122162306a36Sopenharmony_cistatic int dlfb_realloc_framebuffer(struct dlfb_data *dlfb, struct fb_info *info, u32 new_len) 122262306a36Sopenharmony_ci{ 122362306a36Sopenharmony_ci u32 old_len = info->fix.smem_len; 122462306a36Sopenharmony_ci const void *old_fb = info->screen_buffer; 122562306a36Sopenharmony_ci unsigned char *new_fb; 122662306a36Sopenharmony_ci unsigned char *new_back = NULL; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci new_len = PAGE_ALIGN(new_len); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci if (new_len > old_len) { 123162306a36Sopenharmony_ci /* 123262306a36Sopenharmony_ci * Alloc system memory for virtual framebuffer 123362306a36Sopenharmony_ci */ 123462306a36Sopenharmony_ci new_fb = vmalloc(new_len); 123562306a36Sopenharmony_ci if (!new_fb) { 123662306a36Sopenharmony_ci dev_err(info->dev, "Virtual framebuffer alloc failed\n"); 123762306a36Sopenharmony_ci return -ENOMEM; 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci memset(new_fb, 0xff, new_len); 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci if (info->screen_buffer) { 124262306a36Sopenharmony_ci memcpy(new_fb, old_fb, old_len); 124362306a36Sopenharmony_ci dlfb_deferred_vfree(dlfb, info->screen_buffer); 124462306a36Sopenharmony_ci } 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci info->screen_buffer = new_fb; 124762306a36Sopenharmony_ci info->fix.smem_len = new_len; 124862306a36Sopenharmony_ci info->fix.smem_start = (unsigned long) new_fb; 124962306a36Sopenharmony_ci info->flags = udlfb_info_flags; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci /* 125262306a36Sopenharmony_ci * Second framebuffer copy to mirror the framebuffer state 125362306a36Sopenharmony_ci * on the physical USB device. We can function without this. 125462306a36Sopenharmony_ci * But with imperfect damage info we may send pixels over USB 125562306a36Sopenharmony_ci * that were, in fact, unchanged - wasting limited USB bandwidth 125662306a36Sopenharmony_ci */ 125762306a36Sopenharmony_ci if (shadow) 125862306a36Sopenharmony_ci new_back = vzalloc(new_len); 125962306a36Sopenharmony_ci if (!new_back) 126062306a36Sopenharmony_ci dev_info(info->dev, 126162306a36Sopenharmony_ci "No shadow/backing buffer allocated\n"); 126262306a36Sopenharmony_ci else { 126362306a36Sopenharmony_ci dlfb_deferred_vfree(dlfb, dlfb->backing_buffer); 126462306a36Sopenharmony_ci dlfb->backing_buffer = new_back; 126562306a36Sopenharmony_ci } 126662306a36Sopenharmony_ci } 126762306a36Sopenharmony_ci return 0; 126862306a36Sopenharmony_ci} 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci/* 127162306a36Sopenharmony_ci * 1) Get EDID from hw, or use sw default 127262306a36Sopenharmony_ci * 2) Parse into various fb_info structs 127362306a36Sopenharmony_ci * 3) Allocate virtual framebuffer memory to back highest res mode 127462306a36Sopenharmony_ci * 127562306a36Sopenharmony_ci * Parses EDID into three places used by various parts of fbdev: 127662306a36Sopenharmony_ci * fb_var_screeninfo contains the timing of the monitor's preferred mode 127762306a36Sopenharmony_ci * fb_info.monspecs is full parsed EDID info, including monspecs.modedb 127862306a36Sopenharmony_ci * fb_info.modelist is a linked list of all monitor & VESA modes which work 127962306a36Sopenharmony_ci * 128062306a36Sopenharmony_ci * If EDID is not readable/valid, then modelist is all VESA modes, 128162306a36Sopenharmony_ci * monspecs is NULL, and fb_var_screeninfo is set to safe VESA mode 128262306a36Sopenharmony_ci * Returns 0 if successful 128362306a36Sopenharmony_ci */ 128462306a36Sopenharmony_cistatic int dlfb_setup_modes(struct dlfb_data *dlfb, 128562306a36Sopenharmony_ci struct fb_info *info, 128662306a36Sopenharmony_ci char *default_edid, size_t default_edid_size) 128762306a36Sopenharmony_ci{ 128862306a36Sopenharmony_ci char *edid; 128962306a36Sopenharmony_ci int i, result = 0, tries = 3; 129062306a36Sopenharmony_ci struct device *dev = info->device; 129162306a36Sopenharmony_ci struct fb_videomode *mode; 129262306a36Sopenharmony_ci const struct fb_videomode *default_vmode = NULL; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci if (info->dev) { 129562306a36Sopenharmony_ci /* only use mutex if info has been registered */ 129662306a36Sopenharmony_ci mutex_lock(&info->lock); 129762306a36Sopenharmony_ci /* parent device is used otherwise */ 129862306a36Sopenharmony_ci dev = info->dev; 129962306a36Sopenharmony_ci } 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci edid = kmalloc(EDID_LENGTH, GFP_KERNEL); 130262306a36Sopenharmony_ci if (!edid) { 130362306a36Sopenharmony_ci result = -ENOMEM; 130462306a36Sopenharmony_ci goto error; 130562306a36Sopenharmony_ci } 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci fb_destroy_modelist(&info->modelist); 130862306a36Sopenharmony_ci memset(&info->monspecs, 0, sizeof(info->monspecs)); 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci /* 131162306a36Sopenharmony_ci * Try to (re)read EDID from hardware first 131262306a36Sopenharmony_ci * EDID data may return, but not parse as valid 131362306a36Sopenharmony_ci * Try again a few times, in case of e.g. analog cable noise 131462306a36Sopenharmony_ci */ 131562306a36Sopenharmony_ci while (tries--) { 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci i = dlfb_get_edid(dlfb, edid, EDID_LENGTH); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci if (i >= EDID_LENGTH) 132062306a36Sopenharmony_ci fb_edid_to_monspecs(edid, &info->monspecs); 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci if (info->monspecs.modedb_len > 0) { 132362306a36Sopenharmony_ci dlfb->edid = edid; 132462306a36Sopenharmony_ci dlfb->edid_size = i; 132562306a36Sopenharmony_ci break; 132662306a36Sopenharmony_ci } 132762306a36Sopenharmony_ci } 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci /* If that fails, use a previously returned EDID if available */ 133062306a36Sopenharmony_ci if (info->monspecs.modedb_len == 0) { 133162306a36Sopenharmony_ci dev_err(dev, "Unable to get valid EDID from device/display\n"); 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci if (dlfb->edid) { 133462306a36Sopenharmony_ci fb_edid_to_monspecs(dlfb->edid, &info->monspecs); 133562306a36Sopenharmony_ci if (info->monspecs.modedb_len > 0) 133662306a36Sopenharmony_ci dev_err(dev, "Using previously queried EDID\n"); 133762306a36Sopenharmony_ci } 133862306a36Sopenharmony_ci } 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci /* If that fails, use the default EDID we were handed */ 134162306a36Sopenharmony_ci if (info->monspecs.modedb_len == 0) { 134262306a36Sopenharmony_ci if (default_edid_size >= EDID_LENGTH) { 134362306a36Sopenharmony_ci fb_edid_to_monspecs(default_edid, &info->monspecs); 134462306a36Sopenharmony_ci if (info->monspecs.modedb_len > 0) { 134562306a36Sopenharmony_ci memcpy(edid, default_edid, default_edid_size); 134662306a36Sopenharmony_ci dlfb->edid = edid; 134762306a36Sopenharmony_ci dlfb->edid_size = default_edid_size; 134862306a36Sopenharmony_ci dev_err(dev, "Using default/backup EDID\n"); 134962306a36Sopenharmony_ci } 135062306a36Sopenharmony_ci } 135162306a36Sopenharmony_ci } 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci /* If we've got modes, let's pick a best default mode */ 135462306a36Sopenharmony_ci if (info->monspecs.modedb_len > 0) { 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci for (i = 0; i < info->monspecs.modedb_len; i++) { 135762306a36Sopenharmony_ci mode = &info->monspecs.modedb[i]; 135862306a36Sopenharmony_ci if (dlfb_is_valid_mode(mode, dlfb)) { 135962306a36Sopenharmony_ci fb_add_videomode(mode, &info->modelist); 136062306a36Sopenharmony_ci } else { 136162306a36Sopenharmony_ci dev_dbg(dev, "Specified mode %dx%d too big\n", 136262306a36Sopenharmony_ci mode->xres, mode->yres); 136362306a36Sopenharmony_ci if (i == 0) 136462306a36Sopenharmony_ci /* if we've removed top/best mode */ 136562306a36Sopenharmony_ci info->monspecs.misc 136662306a36Sopenharmony_ci &= ~FB_MISC_1ST_DETAIL; 136762306a36Sopenharmony_ci } 136862306a36Sopenharmony_ci } 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci default_vmode = fb_find_best_display(&info->monspecs, 137162306a36Sopenharmony_ci &info->modelist); 137262306a36Sopenharmony_ci } 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci /* If everything else has failed, fall back to safe default mode */ 137562306a36Sopenharmony_ci if (default_vmode == NULL) { 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci struct fb_videomode fb_vmode = {0}; 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci /* 138062306a36Sopenharmony_ci * Add the standard VESA modes to our modelist 138162306a36Sopenharmony_ci * Since we don't have EDID, there may be modes that 138262306a36Sopenharmony_ci * overspec monitor and/or are incorrect aspect ratio, etc. 138362306a36Sopenharmony_ci * But at least the user has a chance to choose 138462306a36Sopenharmony_ci */ 138562306a36Sopenharmony_ci for (i = 0; i < VESA_MODEDB_SIZE; i++) { 138662306a36Sopenharmony_ci mode = (struct fb_videomode *)&vesa_modes[i]; 138762306a36Sopenharmony_ci if (dlfb_is_valid_mode(mode, dlfb)) 138862306a36Sopenharmony_ci fb_add_videomode(mode, &info->modelist); 138962306a36Sopenharmony_ci else 139062306a36Sopenharmony_ci dev_dbg(dev, "VESA mode %dx%d too big\n", 139162306a36Sopenharmony_ci mode->xres, mode->yres); 139262306a36Sopenharmony_ci } 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci /* 139562306a36Sopenharmony_ci * default to resolution safe for projectors 139662306a36Sopenharmony_ci * (since they are most common case without EDID) 139762306a36Sopenharmony_ci */ 139862306a36Sopenharmony_ci fb_vmode.xres = 800; 139962306a36Sopenharmony_ci fb_vmode.yres = 600; 140062306a36Sopenharmony_ci fb_vmode.refresh = 60; 140162306a36Sopenharmony_ci default_vmode = fb_find_nearest_mode(&fb_vmode, 140262306a36Sopenharmony_ci &info->modelist); 140362306a36Sopenharmony_ci } 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci /* If we have good mode and no active clients*/ 140662306a36Sopenharmony_ci if ((default_vmode != NULL) && (dlfb->fb_count == 0)) { 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci fb_videomode_to_var(&info->var, default_vmode); 140962306a36Sopenharmony_ci dlfb_var_color_format(&info->var); 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci /* 141262306a36Sopenharmony_ci * with mode size info, we can now alloc our framebuffer. 141362306a36Sopenharmony_ci */ 141462306a36Sopenharmony_ci memcpy(&info->fix, &dlfb_fix, sizeof(dlfb_fix)); 141562306a36Sopenharmony_ci } else 141662306a36Sopenharmony_ci result = -EINVAL; 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_cierror: 141962306a36Sopenharmony_ci if (edid && (dlfb->edid != edid)) 142062306a36Sopenharmony_ci kfree(edid); 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci if (info->dev) 142362306a36Sopenharmony_ci mutex_unlock(&info->lock); 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci return result; 142662306a36Sopenharmony_ci} 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_cistatic ssize_t metrics_bytes_rendered_show(struct device *fbdev, 142962306a36Sopenharmony_ci struct device_attribute *a, char *buf) { 143062306a36Sopenharmony_ci struct fb_info *fb_info = dev_get_drvdata(fbdev); 143162306a36Sopenharmony_ci struct dlfb_data *dlfb = fb_info->par; 143262306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", 143362306a36Sopenharmony_ci atomic_read(&dlfb->bytes_rendered)); 143462306a36Sopenharmony_ci} 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_cistatic ssize_t metrics_bytes_identical_show(struct device *fbdev, 143762306a36Sopenharmony_ci struct device_attribute *a, char *buf) { 143862306a36Sopenharmony_ci struct fb_info *fb_info = dev_get_drvdata(fbdev); 143962306a36Sopenharmony_ci struct dlfb_data *dlfb = fb_info->par; 144062306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", 144162306a36Sopenharmony_ci atomic_read(&dlfb->bytes_identical)); 144262306a36Sopenharmony_ci} 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_cistatic ssize_t metrics_bytes_sent_show(struct device *fbdev, 144562306a36Sopenharmony_ci struct device_attribute *a, char *buf) { 144662306a36Sopenharmony_ci struct fb_info *fb_info = dev_get_drvdata(fbdev); 144762306a36Sopenharmony_ci struct dlfb_data *dlfb = fb_info->par; 144862306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", 144962306a36Sopenharmony_ci atomic_read(&dlfb->bytes_sent)); 145062306a36Sopenharmony_ci} 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_cistatic ssize_t metrics_cpu_kcycles_used_show(struct device *fbdev, 145362306a36Sopenharmony_ci struct device_attribute *a, char *buf) { 145462306a36Sopenharmony_ci struct fb_info *fb_info = dev_get_drvdata(fbdev); 145562306a36Sopenharmony_ci struct dlfb_data *dlfb = fb_info->par; 145662306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", 145762306a36Sopenharmony_ci atomic_read(&dlfb->cpu_kcycles_used)); 145862306a36Sopenharmony_ci} 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_cistatic ssize_t edid_show( 146162306a36Sopenharmony_ci struct file *filp, 146262306a36Sopenharmony_ci struct kobject *kobj, struct bin_attribute *a, 146362306a36Sopenharmony_ci char *buf, loff_t off, size_t count) { 146462306a36Sopenharmony_ci struct device *fbdev = kobj_to_dev(kobj); 146562306a36Sopenharmony_ci struct fb_info *fb_info = dev_get_drvdata(fbdev); 146662306a36Sopenharmony_ci struct dlfb_data *dlfb = fb_info->par; 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci if (dlfb->edid == NULL) 146962306a36Sopenharmony_ci return 0; 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci if ((off >= dlfb->edid_size) || (count > dlfb->edid_size)) 147262306a36Sopenharmony_ci return 0; 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci if (off + count > dlfb->edid_size) 147562306a36Sopenharmony_ci count = dlfb->edid_size - off; 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci memcpy(buf, dlfb->edid, count); 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci return count; 148062306a36Sopenharmony_ci} 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_cistatic ssize_t edid_store( 148362306a36Sopenharmony_ci struct file *filp, 148462306a36Sopenharmony_ci struct kobject *kobj, struct bin_attribute *a, 148562306a36Sopenharmony_ci char *src, loff_t src_off, size_t src_size) { 148662306a36Sopenharmony_ci struct device *fbdev = kobj_to_dev(kobj); 148762306a36Sopenharmony_ci struct fb_info *fb_info = dev_get_drvdata(fbdev); 148862306a36Sopenharmony_ci struct dlfb_data *dlfb = fb_info->par; 148962306a36Sopenharmony_ci int ret; 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci /* We only support write of entire EDID at once, no offset*/ 149262306a36Sopenharmony_ci if ((src_size != EDID_LENGTH) || (src_off != 0)) 149362306a36Sopenharmony_ci return -EINVAL; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci ret = dlfb_setup_modes(dlfb, fb_info, src, src_size); 149662306a36Sopenharmony_ci if (ret) 149762306a36Sopenharmony_ci return ret; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci if (!dlfb->edid || memcmp(src, dlfb->edid, src_size)) 150062306a36Sopenharmony_ci return -EINVAL; 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci ret = dlfb_ops_set_par(fb_info); 150362306a36Sopenharmony_ci if (ret) 150462306a36Sopenharmony_ci return ret; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci return src_size; 150762306a36Sopenharmony_ci} 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_cistatic ssize_t metrics_reset_store(struct device *fbdev, 151062306a36Sopenharmony_ci struct device_attribute *attr, 151162306a36Sopenharmony_ci const char *buf, size_t count) 151262306a36Sopenharmony_ci{ 151362306a36Sopenharmony_ci struct fb_info *fb_info = dev_get_drvdata(fbdev); 151462306a36Sopenharmony_ci struct dlfb_data *dlfb = fb_info->par; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci atomic_set(&dlfb->bytes_rendered, 0); 151762306a36Sopenharmony_ci atomic_set(&dlfb->bytes_identical, 0); 151862306a36Sopenharmony_ci atomic_set(&dlfb->bytes_sent, 0); 151962306a36Sopenharmony_ci atomic_set(&dlfb->cpu_kcycles_used, 0); 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci return count; 152262306a36Sopenharmony_ci} 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_cistatic const struct bin_attribute edid_attr = { 152562306a36Sopenharmony_ci .attr.name = "edid", 152662306a36Sopenharmony_ci .attr.mode = 0666, 152762306a36Sopenharmony_ci .size = EDID_LENGTH, 152862306a36Sopenharmony_ci .read = edid_show, 152962306a36Sopenharmony_ci .write = edid_store 153062306a36Sopenharmony_ci}; 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_cistatic const struct device_attribute fb_device_attrs[] = { 153362306a36Sopenharmony_ci __ATTR_RO(metrics_bytes_rendered), 153462306a36Sopenharmony_ci __ATTR_RO(metrics_bytes_identical), 153562306a36Sopenharmony_ci __ATTR_RO(metrics_bytes_sent), 153662306a36Sopenharmony_ci __ATTR_RO(metrics_cpu_kcycles_used), 153762306a36Sopenharmony_ci __ATTR(metrics_reset, S_IWUSR, NULL, metrics_reset_store), 153862306a36Sopenharmony_ci}; 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci/* 154162306a36Sopenharmony_ci * This is necessary before we can communicate with the display controller. 154262306a36Sopenharmony_ci */ 154362306a36Sopenharmony_cistatic int dlfb_select_std_channel(struct dlfb_data *dlfb) 154462306a36Sopenharmony_ci{ 154562306a36Sopenharmony_ci int ret; 154662306a36Sopenharmony_ci static const u8 set_def_chn[] = { 154762306a36Sopenharmony_ci 0x57, 0xCD, 0xDC, 0xA7, 154862306a36Sopenharmony_ci 0x1C, 0x88, 0x5E, 0x15, 154962306a36Sopenharmony_ci 0x60, 0xFE, 0xC6, 0x97, 155062306a36Sopenharmony_ci 0x16, 0x3D, 0x47, 0xF2 }; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci ret = usb_control_msg_send(dlfb->udev, 0, NR_USB_REQUEST_CHANNEL, 155362306a36Sopenharmony_ci (USB_DIR_OUT | USB_TYPE_VENDOR), 0, 0, 155462306a36Sopenharmony_ci &set_def_chn, sizeof(set_def_chn), USB_CTRL_SET_TIMEOUT, 155562306a36Sopenharmony_ci GFP_KERNEL); 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci return ret; 155862306a36Sopenharmony_ci} 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_cistatic int dlfb_parse_vendor_descriptor(struct dlfb_data *dlfb, 156162306a36Sopenharmony_ci struct usb_interface *intf) 156262306a36Sopenharmony_ci{ 156362306a36Sopenharmony_ci char *desc; 156462306a36Sopenharmony_ci char *buf; 156562306a36Sopenharmony_ci char *desc_end; 156662306a36Sopenharmony_ci int total_len; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci buf = kzalloc(MAX_VENDOR_DESCRIPTOR_SIZE, GFP_KERNEL); 156962306a36Sopenharmony_ci if (!buf) 157062306a36Sopenharmony_ci return false; 157162306a36Sopenharmony_ci desc = buf; 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci total_len = usb_get_descriptor(interface_to_usbdev(intf), 157462306a36Sopenharmony_ci 0x5f, /* vendor specific */ 157562306a36Sopenharmony_ci 0, desc, MAX_VENDOR_DESCRIPTOR_SIZE); 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci /* if not found, look in configuration descriptor */ 157862306a36Sopenharmony_ci if (total_len < 0) { 157962306a36Sopenharmony_ci if (0 == usb_get_extra_descriptor(intf->cur_altsetting, 158062306a36Sopenharmony_ci 0x5f, &desc)) 158162306a36Sopenharmony_ci total_len = (int) desc[0]; 158262306a36Sopenharmony_ci } 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci if (total_len > 5) { 158562306a36Sopenharmony_ci dev_info(&intf->dev, 158662306a36Sopenharmony_ci "vendor descriptor length: %d data: %11ph\n", 158762306a36Sopenharmony_ci total_len, desc); 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci if ((desc[0] != total_len) || /* descriptor length */ 159062306a36Sopenharmony_ci (desc[1] != 0x5f) || /* vendor descriptor type */ 159162306a36Sopenharmony_ci (desc[2] != 0x01) || /* version (2 bytes) */ 159262306a36Sopenharmony_ci (desc[3] != 0x00) || 159362306a36Sopenharmony_ci (desc[4] != total_len - 2)) /* length after type */ 159462306a36Sopenharmony_ci goto unrecognized; 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci desc_end = desc + total_len; 159762306a36Sopenharmony_ci desc += 5; /* the fixed header we've already parsed */ 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci while (desc < desc_end) { 160062306a36Sopenharmony_ci u8 length; 160162306a36Sopenharmony_ci u16 key; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci key = *desc++; 160462306a36Sopenharmony_ci key |= (u16)*desc++ << 8; 160562306a36Sopenharmony_ci length = *desc++; 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci switch (key) { 160862306a36Sopenharmony_ci case 0x0200: { /* max_area */ 160962306a36Sopenharmony_ci u32 max_area = *desc++; 161062306a36Sopenharmony_ci max_area |= (u32)*desc++ << 8; 161162306a36Sopenharmony_ci max_area |= (u32)*desc++ << 16; 161262306a36Sopenharmony_ci max_area |= (u32)*desc++ << 24; 161362306a36Sopenharmony_ci dev_warn(&intf->dev, 161462306a36Sopenharmony_ci "DL chip limited to %d pixel modes\n", 161562306a36Sopenharmony_ci max_area); 161662306a36Sopenharmony_ci dlfb->sku_pixel_limit = max_area; 161762306a36Sopenharmony_ci break; 161862306a36Sopenharmony_ci } 161962306a36Sopenharmony_ci default: 162062306a36Sopenharmony_ci break; 162162306a36Sopenharmony_ci } 162262306a36Sopenharmony_ci desc += length; 162362306a36Sopenharmony_ci } 162462306a36Sopenharmony_ci } else { 162562306a36Sopenharmony_ci dev_info(&intf->dev, "vendor descriptor not available (%d)\n", 162662306a36Sopenharmony_ci total_len); 162762306a36Sopenharmony_ci } 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci goto success; 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ciunrecognized: 163262306a36Sopenharmony_ci /* allow udlfb to load for now even if firmware unrecognized */ 163362306a36Sopenharmony_ci dev_err(&intf->dev, "Unrecognized vendor firmware descriptor\n"); 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_cisuccess: 163662306a36Sopenharmony_ci kfree(buf); 163762306a36Sopenharmony_ci return true; 163862306a36Sopenharmony_ci} 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_cistatic int dlfb_usb_probe(struct usb_interface *intf, 164162306a36Sopenharmony_ci const struct usb_device_id *id) 164262306a36Sopenharmony_ci{ 164362306a36Sopenharmony_ci int i; 164462306a36Sopenharmony_ci const struct device_attribute *attr; 164562306a36Sopenharmony_ci struct dlfb_data *dlfb; 164662306a36Sopenharmony_ci struct fb_info *info; 164762306a36Sopenharmony_ci int retval; 164862306a36Sopenharmony_ci struct usb_device *usbdev = interface_to_usbdev(intf); 164962306a36Sopenharmony_ci static u8 out_ep[] = {OUT_EP_NUM + USB_DIR_OUT, 0}; 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci /* usb initialization */ 165262306a36Sopenharmony_ci dlfb = kzalloc(sizeof(*dlfb), GFP_KERNEL); 165362306a36Sopenharmony_ci if (!dlfb) { 165462306a36Sopenharmony_ci dev_err(&intf->dev, "%s: failed to allocate dlfb\n", __func__); 165562306a36Sopenharmony_ci return -ENOMEM; 165662306a36Sopenharmony_ci } 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci INIT_LIST_HEAD(&dlfb->deferred_free); 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci dlfb->udev = usb_get_dev(usbdev); 166162306a36Sopenharmony_ci usb_set_intfdata(intf, dlfb); 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci if (!usb_check_bulk_endpoints(intf, out_ep)) { 166462306a36Sopenharmony_ci dev_err(&intf->dev, "Invalid DisplayLink device!\n"); 166562306a36Sopenharmony_ci retval = -EINVAL; 166662306a36Sopenharmony_ci goto error; 166762306a36Sopenharmony_ci } 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci dev_dbg(&intf->dev, "console enable=%d\n", console); 167062306a36Sopenharmony_ci dev_dbg(&intf->dev, "fb_defio enable=%d\n", fb_defio); 167162306a36Sopenharmony_ci dev_dbg(&intf->dev, "shadow enable=%d\n", shadow); 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci dlfb->sku_pixel_limit = 2048 * 1152; /* default to maximum */ 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci if (!dlfb_parse_vendor_descriptor(dlfb, intf)) { 167662306a36Sopenharmony_ci dev_err(&intf->dev, 167762306a36Sopenharmony_ci "firmware not recognized, incompatible device?\n"); 167862306a36Sopenharmony_ci retval = -ENODEV; 167962306a36Sopenharmony_ci goto error; 168062306a36Sopenharmony_ci } 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci if (pixel_limit) { 168362306a36Sopenharmony_ci dev_warn(&intf->dev, 168462306a36Sopenharmony_ci "DL chip limit of %d overridden to %d\n", 168562306a36Sopenharmony_ci dlfb->sku_pixel_limit, pixel_limit); 168662306a36Sopenharmony_ci dlfb->sku_pixel_limit = pixel_limit; 168762306a36Sopenharmony_ci } 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci /* allocates framebuffer driver structure, not framebuffer memory */ 169162306a36Sopenharmony_ci info = framebuffer_alloc(0, &dlfb->udev->dev); 169262306a36Sopenharmony_ci if (!info) { 169362306a36Sopenharmony_ci retval = -ENOMEM; 169462306a36Sopenharmony_ci goto error; 169562306a36Sopenharmony_ci } 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci dlfb->info = info; 169862306a36Sopenharmony_ci info->par = dlfb; 169962306a36Sopenharmony_ci info->pseudo_palette = dlfb->pseudo_palette; 170062306a36Sopenharmony_ci dlfb->ops = dlfb_ops; 170162306a36Sopenharmony_ci info->fbops = &dlfb->ops; 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci mutex_init(&dlfb->render_mutex); 170462306a36Sopenharmony_ci dlfb_init_damage(dlfb); 170562306a36Sopenharmony_ci spin_lock_init(&dlfb->damage_lock); 170662306a36Sopenharmony_ci INIT_WORK(&dlfb->damage_work, dlfb_damage_work); 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci INIT_LIST_HEAD(&info->modelist); 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci if (!dlfb_alloc_urb_list(dlfb, WRITES_IN_FLIGHT, MAX_TRANSFER)) { 171162306a36Sopenharmony_ci retval = -ENOMEM; 171262306a36Sopenharmony_ci dev_err(&intf->dev, "unable to allocate urb list\n"); 171362306a36Sopenharmony_ci goto error; 171462306a36Sopenharmony_ci } 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci /* We don't register a new USB class. Our client interface is dlfbev */ 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci retval = fb_alloc_cmap(&info->cmap, 256, 0); 171962306a36Sopenharmony_ci if (retval < 0) { 172062306a36Sopenharmony_ci dev_err(info->device, "cmap allocation failed: %d\n", retval); 172162306a36Sopenharmony_ci goto error; 172262306a36Sopenharmony_ci } 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci retval = dlfb_setup_modes(dlfb, info, NULL, 0); 172562306a36Sopenharmony_ci if (retval != 0) { 172662306a36Sopenharmony_ci dev_err(info->device, 172762306a36Sopenharmony_ci "unable to find common mode for display and adapter\n"); 172862306a36Sopenharmony_ci goto error; 172962306a36Sopenharmony_ci } 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci /* ready to begin using device */ 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci atomic_set(&dlfb->usb_active, 1); 173462306a36Sopenharmony_ci dlfb_select_std_channel(dlfb); 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci dlfb_ops_check_var(&info->var, info); 173762306a36Sopenharmony_ci retval = dlfb_ops_set_par(info); 173862306a36Sopenharmony_ci if (retval) 173962306a36Sopenharmony_ci goto error; 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci retval = register_framebuffer(info); 174262306a36Sopenharmony_ci if (retval < 0) { 174362306a36Sopenharmony_ci dev_err(info->device, "unable to register framebuffer: %d\n", 174462306a36Sopenharmony_ci retval); 174562306a36Sopenharmony_ci goto error; 174662306a36Sopenharmony_ci } 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) { 174962306a36Sopenharmony_ci attr = &fb_device_attrs[i]; 175062306a36Sopenharmony_ci retval = device_create_file(info->dev, attr); 175162306a36Sopenharmony_ci if (retval) 175262306a36Sopenharmony_ci dev_warn(info->device, 175362306a36Sopenharmony_ci "failed to create '%s' attribute: %d\n", 175462306a36Sopenharmony_ci attr->attr.name, retval); 175562306a36Sopenharmony_ci } 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci retval = device_create_bin_file(info->dev, &edid_attr); 175862306a36Sopenharmony_ci if (retval) 175962306a36Sopenharmony_ci dev_warn(info->device, "failed to create '%s' attribute: %d\n", 176062306a36Sopenharmony_ci edid_attr.attr.name, retval); 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci dev_info(info->device, 176362306a36Sopenharmony_ci "%s is DisplayLink USB device (%dx%d, %dK framebuffer memory)\n", 176462306a36Sopenharmony_ci dev_name(info->dev), info->var.xres, info->var.yres, 176562306a36Sopenharmony_ci ((dlfb->backing_buffer) ? 176662306a36Sopenharmony_ci info->fix.smem_len * 2 : info->fix.smem_len) >> 10); 176762306a36Sopenharmony_ci return 0; 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_cierror: 177062306a36Sopenharmony_ci if (dlfb->info) { 177162306a36Sopenharmony_ci dlfb_ops_destroy(dlfb->info); 177262306a36Sopenharmony_ci } else { 177362306a36Sopenharmony_ci usb_put_dev(dlfb->udev); 177462306a36Sopenharmony_ci kfree(dlfb); 177562306a36Sopenharmony_ci } 177662306a36Sopenharmony_ci return retval; 177762306a36Sopenharmony_ci} 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_cistatic void dlfb_usb_disconnect(struct usb_interface *intf) 178062306a36Sopenharmony_ci{ 178162306a36Sopenharmony_ci struct dlfb_data *dlfb; 178262306a36Sopenharmony_ci struct fb_info *info; 178362306a36Sopenharmony_ci int i; 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci dlfb = usb_get_intfdata(intf); 178662306a36Sopenharmony_ci info = dlfb->info; 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci dev_dbg(&intf->dev, "USB disconnect starting\n"); 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci /* we virtualize until all fb clients release. Then we free */ 179162306a36Sopenharmony_ci dlfb->virtualized = true; 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci /* When non-active we'll update virtual framebuffer, but no new urbs */ 179462306a36Sopenharmony_ci atomic_set(&dlfb->usb_active, 0); 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci /* this function will wait for all in-flight urbs to complete */ 179762306a36Sopenharmony_ci dlfb_free_urb_list(dlfb); 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_ci /* remove udlfb's sysfs interfaces */ 180062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) 180162306a36Sopenharmony_ci device_remove_file(info->dev, &fb_device_attrs[i]); 180262306a36Sopenharmony_ci device_remove_bin_file(info->dev, &edid_attr); 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci unregister_framebuffer(info); 180562306a36Sopenharmony_ci} 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_cistatic struct usb_driver dlfb_driver = { 180862306a36Sopenharmony_ci .name = "udlfb", 180962306a36Sopenharmony_ci .probe = dlfb_usb_probe, 181062306a36Sopenharmony_ci .disconnect = dlfb_usb_disconnect, 181162306a36Sopenharmony_ci .id_table = id_table, 181262306a36Sopenharmony_ci}; 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_cimodule_usb_driver(dlfb_driver); 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_cistatic void dlfb_urb_completion(struct urb *urb) 181762306a36Sopenharmony_ci{ 181862306a36Sopenharmony_ci struct urb_node *unode = urb->context; 181962306a36Sopenharmony_ci struct dlfb_data *dlfb = unode->dlfb; 182062306a36Sopenharmony_ci unsigned long flags; 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci switch (urb->status) { 182362306a36Sopenharmony_ci case 0: 182462306a36Sopenharmony_ci /* success */ 182562306a36Sopenharmony_ci break; 182662306a36Sopenharmony_ci case -ECONNRESET: 182762306a36Sopenharmony_ci case -ENOENT: 182862306a36Sopenharmony_ci case -ESHUTDOWN: 182962306a36Sopenharmony_ci /* sync/async unlink faults aren't errors */ 183062306a36Sopenharmony_ci break; 183162306a36Sopenharmony_ci default: 183262306a36Sopenharmony_ci dev_err(&dlfb->udev->dev, 183362306a36Sopenharmony_ci "%s - nonzero write bulk status received: %d\n", 183462306a36Sopenharmony_ci __func__, urb->status); 183562306a36Sopenharmony_ci atomic_set(&dlfb->lost_pixels, 1); 183662306a36Sopenharmony_ci break; 183762306a36Sopenharmony_ci } 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci urb->transfer_buffer_length = dlfb->urbs.size; /* reset to actual */ 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci spin_lock_irqsave(&dlfb->urbs.lock, flags); 184262306a36Sopenharmony_ci list_add_tail(&unode->entry, &dlfb->urbs.list); 184362306a36Sopenharmony_ci dlfb->urbs.available++; 184462306a36Sopenharmony_ci spin_unlock_irqrestore(&dlfb->urbs.lock, flags); 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci up(&dlfb->urbs.limit_sem); 184762306a36Sopenharmony_ci} 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_cistatic void dlfb_free_urb_list(struct dlfb_data *dlfb) 185062306a36Sopenharmony_ci{ 185162306a36Sopenharmony_ci int count = dlfb->urbs.count; 185262306a36Sopenharmony_ci struct list_head *node; 185362306a36Sopenharmony_ci struct urb_node *unode; 185462306a36Sopenharmony_ci struct urb *urb; 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci /* keep waiting and freeing, until we've got 'em all */ 185762306a36Sopenharmony_ci while (count--) { 185862306a36Sopenharmony_ci down(&dlfb->urbs.limit_sem); 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci spin_lock_irq(&dlfb->urbs.lock); 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci node = dlfb->urbs.list.next; /* have reserved one with sem */ 186362306a36Sopenharmony_ci list_del_init(node); 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci spin_unlock_irq(&dlfb->urbs.lock); 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci unode = list_entry(node, struct urb_node, entry); 186862306a36Sopenharmony_ci urb = unode->urb; 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci /* Free each separately allocated piece */ 187162306a36Sopenharmony_ci usb_free_coherent(urb->dev, dlfb->urbs.size, 187262306a36Sopenharmony_ci urb->transfer_buffer, urb->transfer_dma); 187362306a36Sopenharmony_ci usb_free_urb(urb); 187462306a36Sopenharmony_ci kfree(node); 187562306a36Sopenharmony_ci } 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci dlfb->urbs.count = 0; 187862306a36Sopenharmony_ci} 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_cistatic int dlfb_alloc_urb_list(struct dlfb_data *dlfb, int count, size_t size) 188162306a36Sopenharmony_ci{ 188262306a36Sopenharmony_ci struct urb *urb; 188362306a36Sopenharmony_ci struct urb_node *unode; 188462306a36Sopenharmony_ci char *buf; 188562306a36Sopenharmony_ci size_t wanted_size = count * size; 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ci spin_lock_init(&dlfb->urbs.lock); 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ciretry: 189062306a36Sopenharmony_ci dlfb->urbs.size = size; 189162306a36Sopenharmony_ci INIT_LIST_HEAD(&dlfb->urbs.list); 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci sema_init(&dlfb->urbs.limit_sem, 0); 189462306a36Sopenharmony_ci dlfb->urbs.count = 0; 189562306a36Sopenharmony_ci dlfb->urbs.available = 0; 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci while (dlfb->urbs.count * size < wanted_size) { 189862306a36Sopenharmony_ci unode = kzalloc(sizeof(*unode), GFP_KERNEL); 189962306a36Sopenharmony_ci if (!unode) 190062306a36Sopenharmony_ci break; 190162306a36Sopenharmony_ci unode->dlfb = dlfb; 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci urb = usb_alloc_urb(0, GFP_KERNEL); 190462306a36Sopenharmony_ci if (!urb) { 190562306a36Sopenharmony_ci kfree(unode); 190662306a36Sopenharmony_ci break; 190762306a36Sopenharmony_ci } 190862306a36Sopenharmony_ci unode->urb = urb; 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_ci buf = usb_alloc_coherent(dlfb->udev, size, GFP_KERNEL, 191162306a36Sopenharmony_ci &urb->transfer_dma); 191262306a36Sopenharmony_ci if (!buf) { 191362306a36Sopenharmony_ci kfree(unode); 191462306a36Sopenharmony_ci usb_free_urb(urb); 191562306a36Sopenharmony_ci if (size > PAGE_SIZE) { 191662306a36Sopenharmony_ci size /= 2; 191762306a36Sopenharmony_ci dlfb_free_urb_list(dlfb); 191862306a36Sopenharmony_ci goto retry; 191962306a36Sopenharmony_ci } 192062306a36Sopenharmony_ci break; 192162306a36Sopenharmony_ci } 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci /* urb->transfer_buffer_length set to actual before submit */ 192462306a36Sopenharmony_ci usb_fill_bulk_urb(urb, dlfb->udev, 192562306a36Sopenharmony_ci usb_sndbulkpipe(dlfb->udev, OUT_EP_NUM), 192662306a36Sopenharmony_ci buf, size, dlfb_urb_completion, unode); 192762306a36Sopenharmony_ci urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ci list_add_tail(&unode->entry, &dlfb->urbs.list); 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci up(&dlfb->urbs.limit_sem); 193262306a36Sopenharmony_ci dlfb->urbs.count++; 193362306a36Sopenharmony_ci dlfb->urbs.available++; 193462306a36Sopenharmony_ci } 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci return dlfb->urbs.count; 193762306a36Sopenharmony_ci} 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_cistatic struct urb *dlfb_get_urb(struct dlfb_data *dlfb) 194062306a36Sopenharmony_ci{ 194162306a36Sopenharmony_ci int ret; 194262306a36Sopenharmony_ci struct list_head *entry; 194362306a36Sopenharmony_ci struct urb_node *unode; 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci /* Wait for an in-flight buffer to complete and get re-queued */ 194662306a36Sopenharmony_ci ret = down_timeout(&dlfb->urbs.limit_sem, GET_URB_TIMEOUT); 194762306a36Sopenharmony_ci if (ret) { 194862306a36Sopenharmony_ci atomic_set(&dlfb->lost_pixels, 1); 194962306a36Sopenharmony_ci dev_warn(&dlfb->udev->dev, 195062306a36Sopenharmony_ci "wait for urb interrupted: %d available: %d\n", 195162306a36Sopenharmony_ci ret, dlfb->urbs.available); 195262306a36Sopenharmony_ci return NULL; 195362306a36Sopenharmony_ci } 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci spin_lock_irq(&dlfb->urbs.lock); 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci BUG_ON(list_empty(&dlfb->urbs.list)); /* reserved one with limit_sem */ 195862306a36Sopenharmony_ci entry = dlfb->urbs.list.next; 195962306a36Sopenharmony_ci list_del_init(entry); 196062306a36Sopenharmony_ci dlfb->urbs.available--; 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci spin_unlock_irq(&dlfb->urbs.lock); 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci unode = list_entry(entry, struct urb_node, entry); 196562306a36Sopenharmony_ci return unode->urb; 196662306a36Sopenharmony_ci} 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_cistatic int dlfb_submit_urb(struct dlfb_data *dlfb, struct urb *urb, size_t len) 196962306a36Sopenharmony_ci{ 197062306a36Sopenharmony_ci int ret; 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci BUG_ON(len > dlfb->urbs.size); 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci urb->transfer_buffer_length = len; /* set to actual payload len */ 197562306a36Sopenharmony_ci ret = usb_submit_urb(urb, GFP_KERNEL); 197662306a36Sopenharmony_ci if (ret) { 197762306a36Sopenharmony_ci dlfb_urb_completion(urb); /* because no one else will */ 197862306a36Sopenharmony_ci atomic_set(&dlfb->lost_pixels, 1); 197962306a36Sopenharmony_ci dev_err(&dlfb->udev->dev, "submit urb error: %d\n", ret); 198062306a36Sopenharmony_ci } 198162306a36Sopenharmony_ci return ret; 198262306a36Sopenharmony_ci} 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_cimodule_param(console, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); 198562306a36Sopenharmony_ciMODULE_PARM_DESC(console, "Allow fbcon to open framebuffer"); 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_cimodule_param(fb_defio, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); 198862306a36Sopenharmony_ciMODULE_PARM_DESC(fb_defio, "Page fault detection of mmap writes"); 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_cimodule_param(shadow, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); 199162306a36Sopenharmony_ciMODULE_PARM_DESC(shadow, "Shadow vid mem. Disable to save mem but lose perf"); 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_cimodule_param(pixel_limit, int, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); 199462306a36Sopenharmony_ciMODULE_PARM_DESC(pixel_limit, "Force limit on max mode (in x*y pixels)"); 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ciMODULE_AUTHOR("Roberto De Ioris <roberto@unbit.it>, " 199762306a36Sopenharmony_ci "Jaya Kumar <jayakumar.lkml@gmail.com>, " 199862306a36Sopenharmony_ci "Bernie Thompson <bernie@plugable.com>"); 199962306a36Sopenharmony_ciMODULE_DESCRIPTION("DisplayLink kernel framebuffer driver"); 200062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 200162306a36Sopenharmony_ci 2002