18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * udlfb.c -- Framebuffer driver for DisplayLink USB controller 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it> 68c2ecf20Sopenharmony_ci * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com> 78c2ecf20Sopenharmony_ci * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Layout is based on skeletonfb by James Simmons and Geert Uytterhoeven, 108c2ecf20Sopenharmony_ci * usb-skeleton by GregKH. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Device-specific portions based on information from Displaylink, with work 138c2ecf20Sopenharmony_ci * from Florian Echtler, Henrik Bjerregaard Pedersen, and others. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/usb.h> 208c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 218c2ecf20Sopenharmony_ci#include <linux/mm.h> 228c2ecf20Sopenharmony_ci#include <linux/fb.h> 238c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <linux/delay.h> 268c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 278c2ecf20Sopenharmony_ci#include <video/udlfb.h> 288c2ecf20Sopenharmony_ci#include "edid.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define OUT_EP_NUM 1 /* The endpoint number we will use */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic const struct fb_fix_screeninfo dlfb_fix = { 338c2ecf20Sopenharmony_ci .id = "udlfb", 348c2ecf20Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 358c2ecf20Sopenharmony_ci .visual = FB_VISUAL_TRUECOLOR, 368c2ecf20Sopenharmony_ci .xpanstep = 0, 378c2ecf20Sopenharmony_ci .ypanstep = 0, 388c2ecf20Sopenharmony_ci .ywrapstep = 0, 398c2ecf20Sopenharmony_ci .accel = FB_ACCEL_NONE, 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic const u32 udlfb_info_flags = FBINFO_DEFAULT | FBINFO_READS_FAST | 438c2ecf20Sopenharmony_ci FBINFO_VIRTFB | 448c2ecf20Sopenharmony_ci FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_FILLRECT | 458c2ecf20Sopenharmony_ci FBINFO_HWACCEL_COPYAREA | FBINFO_MISC_ALWAYS_SETPAR; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci * There are many DisplayLink-based graphics products, all with unique PIDs. 498c2ecf20Sopenharmony_ci * So we match on DisplayLink's VID + Vendor-Defined Interface Class (0xff) 508c2ecf20Sopenharmony_ci * We also require a match on SubClass (0x00) and Protocol (0x00), 518c2ecf20Sopenharmony_ci * which is compatible with all known USB 2.0 era graphics chips and firmware, 528c2ecf20Sopenharmony_ci * but allows DisplayLink to increment those for any future incompatible chips 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table[] = { 558c2ecf20Sopenharmony_ci {.idVendor = 0x17e9, 568c2ecf20Sopenharmony_ci .bInterfaceClass = 0xff, 578c2ecf20Sopenharmony_ci .bInterfaceSubClass = 0x00, 588c2ecf20Sopenharmony_ci .bInterfaceProtocol = 0x00, 598c2ecf20Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_VENDOR | 608c2ecf20Sopenharmony_ci USB_DEVICE_ID_MATCH_INT_CLASS | 618c2ecf20Sopenharmony_ci USB_DEVICE_ID_MATCH_INT_SUBCLASS | 628c2ecf20Sopenharmony_ci USB_DEVICE_ID_MATCH_INT_PROTOCOL, 638c2ecf20Sopenharmony_ci }, 648c2ecf20Sopenharmony_ci {}, 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* module options */ 698c2ecf20Sopenharmony_cistatic bool console = true; /* Allow fbcon to open framebuffer */ 708c2ecf20Sopenharmony_cistatic bool fb_defio = true; /* Detect mmap writes using page faults */ 718c2ecf20Sopenharmony_cistatic bool shadow = true; /* Optionally disable shadow framebuffer */ 728c2ecf20Sopenharmony_cistatic int pixel_limit; /* Optionally force a pixel resolution limit */ 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistruct dlfb_deferred_free { 758c2ecf20Sopenharmony_ci struct list_head list; 768c2ecf20Sopenharmony_ci void *mem; 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic int dlfb_realloc_framebuffer(struct dlfb_data *dlfb, struct fb_info *info, u32 new_len); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* dlfb keeps a list of urbs for efficient bulk transfers */ 828c2ecf20Sopenharmony_cistatic void dlfb_urb_completion(struct urb *urb); 838c2ecf20Sopenharmony_cistatic struct urb *dlfb_get_urb(struct dlfb_data *dlfb); 848c2ecf20Sopenharmony_cistatic int dlfb_submit_urb(struct dlfb_data *dlfb, struct urb * urb, size_t len); 858c2ecf20Sopenharmony_cistatic int dlfb_alloc_urb_list(struct dlfb_data *dlfb, int count, size_t size); 868c2ecf20Sopenharmony_cistatic void dlfb_free_urb_list(struct dlfb_data *dlfb); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* 898c2ecf20Sopenharmony_ci * All DisplayLink bulk operations start with 0xAF, followed by specific code 908c2ecf20Sopenharmony_ci * All operations are written to buffers which then later get sent to device 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_cistatic char *dlfb_set_register(char *buf, u8 reg, u8 val) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci *buf++ = 0xAF; 958c2ecf20Sopenharmony_ci *buf++ = 0x20; 968c2ecf20Sopenharmony_ci *buf++ = reg; 978c2ecf20Sopenharmony_ci *buf++ = val; 988c2ecf20Sopenharmony_ci return buf; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic char *dlfb_vidreg_lock(char *buf) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci return dlfb_set_register(buf, 0xFF, 0x00); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic char *dlfb_vidreg_unlock(char *buf) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci return dlfb_set_register(buf, 0xFF, 0xFF); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* 1128c2ecf20Sopenharmony_ci * Map FB_BLANK_* to DisplayLink register 1138c2ecf20Sopenharmony_ci * DLReg FB_BLANK_* 1148c2ecf20Sopenharmony_ci * ----- ----------------------------- 1158c2ecf20Sopenharmony_ci * 0x00 FB_BLANK_UNBLANK (0) 1168c2ecf20Sopenharmony_ci * 0x01 FB_BLANK (1) 1178c2ecf20Sopenharmony_ci * 0x03 FB_BLANK_VSYNC_SUSPEND (2) 1188c2ecf20Sopenharmony_ci * 0x05 FB_BLANK_HSYNC_SUSPEND (3) 1198c2ecf20Sopenharmony_ci * 0x07 FB_BLANK_POWERDOWN (4) Note: requires modeset to come back 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_cistatic char *dlfb_blanking(char *buf, int fb_blank) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci u8 reg; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci switch (fb_blank) { 1268c2ecf20Sopenharmony_ci case FB_BLANK_POWERDOWN: 1278c2ecf20Sopenharmony_ci reg = 0x07; 1288c2ecf20Sopenharmony_ci break; 1298c2ecf20Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 1308c2ecf20Sopenharmony_ci reg = 0x05; 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 1338c2ecf20Sopenharmony_ci reg = 0x03; 1348c2ecf20Sopenharmony_ci break; 1358c2ecf20Sopenharmony_ci case FB_BLANK_NORMAL: 1368c2ecf20Sopenharmony_ci reg = 0x01; 1378c2ecf20Sopenharmony_ci break; 1388c2ecf20Sopenharmony_ci default: 1398c2ecf20Sopenharmony_ci reg = 0x00; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci buf = dlfb_set_register(buf, 0x1F, reg); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return buf; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic char *dlfb_set_color_depth(char *buf, u8 selection) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci return dlfb_set_register(buf, 0x00, selection); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic char *dlfb_set_base16bpp(char *wrptr, u32 base) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci /* the base pointer is 16 bits wide, 0x20 is hi byte. */ 1558c2ecf20Sopenharmony_ci wrptr = dlfb_set_register(wrptr, 0x20, base >> 16); 1568c2ecf20Sopenharmony_ci wrptr = dlfb_set_register(wrptr, 0x21, base >> 8); 1578c2ecf20Sopenharmony_ci return dlfb_set_register(wrptr, 0x22, base); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/* 1618c2ecf20Sopenharmony_ci * DisplayLink HW has separate 16bpp and 8bpp framebuffers. 1628c2ecf20Sopenharmony_ci * In 24bpp modes, the low 323 RGB bits go in the 8bpp framebuffer 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_cistatic char *dlfb_set_base8bpp(char *wrptr, u32 base) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci wrptr = dlfb_set_register(wrptr, 0x26, base >> 16); 1678c2ecf20Sopenharmony_ci wrptr = dlfb_set_register(wrptr, 0x27, base >> 8); 1688c2ecf20Sopenharmony_ci return dlfb_set_register(wrptr, 0x28, base); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic char *dlfb_set_register_16(char *wrptr, u8 reg, u16 value) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci wrptr = dlfb_set_register(wrptr, reg, value >> 8); 1748c2ecf20Sopenharmony_ci return dlfb_set_register(wrptr, reg+1, value); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci/* 1788c2ecf20Sopenharmony_ci * This is kind of weird because the controller takes some 1798c2ecf20Sopenharmony_ci * register values in a different byte order than other registers. 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_cistatic char *dlfb_set_register_16be(char *wrptr, u8 reg, u16 value) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci wrptr = dlfb_set_register(wrptr, reg, value); 1848c2ecf20Sopenharmony_ci return dlfb_set_register(wrptr, reg+1, value >> 8); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci/* 1888c2ecf20Sopenharmony_ci * LFSR is linear feedback shift register. The reason we have this is 1898c2ecf20Sopenharmony_ci * because the display controller needs to minimize the clock depth of 1908c2ecf20Sopenharmony_ci * various counters used in the display path. So this code reverses the 1918c2ecf20Sopenharmony_ci * provided value into the lfsr16 value by counting backwards to get 1928c2ecf20Sopenharmony_ci * the value that needs to be set in the hardware comparator to get the 1938c2ecf20Sopenharmony_ci * same actual count. This makes sense once you read above a couple of 1948c2ecf20Sopenharmony_ci * times and think about it from a hardware perspective. 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_cistatic u16 dlfb_lfsr16(u16 actual_count) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci u32 lv = 0xFFFF; /* This is the lfsr value that the hw starts with */ 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci while (actual_count--) { 2018c2ecf20Sopenharmony_ci lv = ((lv << 1) | 2028c2ecf20Sopenharmony_ci (((lv >> 15) ^ (lv >> 4) ^ (lv >> 2) ^ (lv >> 1)) & 1)) 2038c2ecf20Sopenharmony_ci & 0xFFFF; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return (u16) lv; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/* 2108c2ecf20Sopenharmony_ci * This does LFSR conversion on the value that is to be written. 2118c2ecf20Sopenharmony_ci * See LFSR explanation above for more detail. 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_cistatic char *dlfb_set_register_lfsr16(char *wrptr, u8 reg, u16 value) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci return dlfb_set_register_16(wrptr, reg, dlfb_lfsr16(value)); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci/* 2198c2ecf20Sopenharmony_ci * This takes a standard fbdev screeninfo struct and all of its monitor mode 2208c2ecf20Sopenharmony_ci * details and converts them into the DisplayLink equivalent register commands. 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_cistatic char *dlfb_set_vid_cmds(char *wrptr, struct fb_var_screeninfo *var) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci u16 xds, yds; 2258c2ecf20Sopenharmony_ci u16 xde, yde; 2268c2ecf20Sopenharmony_ci u16 yec; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* x display start */ 2298c2ecf20Sopenharmony_ci xds = var->left_margin + var->hsync_len; 2308c2ecf20Sopenharmony_ci wrptr = dlfb_set_register_lfsr16(wrptr, 0x01, xds); 2318c2ecf20Sopenharmony_ci /* x display end */ 2328c2ecf20Sopenharmony_ci xde = xds + var->xres; 2338c2ecf20Sopenharmony_ci wrptr = dlfb_set_register_lfsr16(wrptr, 0x03, xde); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* y display start */ 2368c2ecf20Sopenharmony_ci yds = var->upper_margin + var->vsync_len; 2378c2ecf20Sopenharmony_ci wrptr = dlfb_set_register_lfsr16(wrptr, 0x05, yds); 2388c2ecf20Sopenharmony_ci /* y display end */ 2398c2ecf20Sopenharmony_ci yde = yds + var->yres; 2408c2ecf20Sopenharmony_ci wrptr = dlfb_set_register_lfsr16(wrptr, 0x07, yde); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* x end count is active + blanking - 1 */ 2438c2ecf20Sopenharmony_ci wrptr = dlfb_set_register_lfsr16(wrptr, 0x09, 2448c2ecf20Sopenharmony_ci xde + var->right_margin - 1); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* libdlo hardcodes hsync start to 1 */ 2478c2ecf20Sopenharmony_ci wrptr = dlfb_set_register_lfsr16(wrptr, 0x0B, 1); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* hsync end is width of sync pulse + 1 */ 2508c2ecf20Sopenharmony_ci wrptr = dlfb_set_register_lfsr16(wrptr, 0x0D, var->hsync_len + 1); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* hpixels is active pixels */ 2538c2ecf20Sopenharmony_ci wrptr = dlfb_set_register_16(wrptr, 0x0F, var->xres); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* yendcount is vertical active + vertical blanking */ 2568c2ecf20Sopenharmony_ci yec = var->yres + var->upper_margin + var->lower_margin + 2578c2ecf20Sopenharmony_ci var->vsync_len; 2588c2ecf20Sopenharmony_ci wrptr = dlfb_set_register_lfsr16(wrptr, 0x11, yec); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* libdlo hardcodes vsync start to 0 */ 2618c2ecf20Sopenharmony_ci wrptr = dlfb_set_register_lfsr16(wrptr, 0x13, 0); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* vsync end is width of vsync pulse */ 2648c2ecf20Sopenharmony_ci wrptr = dlfb_set_register_lfsr16(wrptr, 0x15, var->vsync_len); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* vpixels is active pixels */ 2678c2ecf20Sopenharmony_ci wrptr = dlfb_set_register_16(wrptr, 0x17, var->yres); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* convert picoseconds to 5kHz multiple for pclk5k = x * 1E12/5k */ 2708c2ecf20Sopenharmony_ci wrptr = dlfb_set_register_16be(wrptr, 0x1B, 2718c2ecf20Sopenharmony_ci 200*1000*1000/var->pixclock); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return wrptr; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci/* 2778c2ecf20Sopenharmony_ci * This takes a standard fbdev screeninfo struct that was fetched or prepared 2788c2ecf20Sopenharmony_ci * and then generates the appropriate command sequence that then drives the 2798c2ecf20Sopenharmony_ci * display controller. 2808c2ecf20Sopenharmony_ci */ 2818c2ecf20Sopenharmony_cistatic int dlfb_set_video_mode(struct dlfb_data *dlfb, 2828c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci char *buf; 2858c2ecf20Sopenharmony_ci char *wrptr; 2868c2ecf20Sopenharmony_ci int retval; 2878c2ecf20Sopenharmony_ci int writesize; 2888c2ecf20Sopenharmony_ci struct urb *urb; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (!atomic_read(&dlfb->usb_active)) 2918c2ecf20Sopenharmony_ci return -EPERM; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci urb = dlfb_get_urb(dlfb); 2948c2ecf20Sopenharmony_ci if (!urb) 2958c2ecf20Sopenharmony_ci return -ENOMEM; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci buf = (char *) urb->transfer_buffer; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* 3008c2ecf20Sopenharmony_ci * This first section has to do with setting the base address on the 3018c2ecf20Sopenharmony_ci * controller * associated with the display. There are 2 base 3028c2ecf20Sopenharmony_ci * pointers, currently, we only * use the 16 bpp segment. 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_ci wrptr = dlfb_vidreg_lock(buf); 3058c2ecf20Sopenharmony_ci wrptr = dlfb_set_color_depth(wrptr, 0x00); 3068c2ecf20Sopenharmony_ci /* set base for 16bpp segment to 0 */ 3078c2ecf20Sopenharmony_ci wrptr = dlfb_set_base16bpp(wrptr, 0); 3088c2ecf20Sopenharmony_ci /* set base for 8bpp segment to end of fb */ 3098c2ecf20Sopenharmony_ci wrptr = dlfb_set_base8bpp(wrptr, dlfb->info->fix.smem_len); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci wrptr = dlfb_set_vid_cmds(wrptr, var); 3128c2ecf20Sopenharmony_ci wrptr = dlfb_blanking(wrptr, FB_BLANK_UNBLANK); 3138c2ecf20Sopenharmony_ci wrptr = dlfb_vidreg_unlock(wrptr); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci writesize = wrptr - buf; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci retval = dlfb_submit_urb(dlfb, urb, writesize); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci dlfb->blank_mode = FB_BLANK_UNBLANK; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return retval; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic int dlfb_ops_mmap(struct fb_info *info, struct vm_area_struct *vma) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci unsigned long start = vma->vm_start; 3278c2ecf20Sopenharmony_ci unsigned long size = vma->vm_end - vma->vm_start; 3288c2ecf20Sopenharmony_ci unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; 3298c2ecf20Sopenharmony_ci unsigned long page, pos; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) 3328c2ecf20Sopenharmony_ci return -EINVAL; 3338c2ecf20Sopenharmony_ci if (size > info->fix.smem_len) 3348c2ecf20Sopenharmony_ci return -EINVAL; 3358c2ecf20Sopenharmony_ci if (offset > info->fix.smem_len - size) 3368c2ecf20Sopenharmony_ci return -EINVAL; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci pos = (unsigned long)info->fix.smem_start + offset; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci dev_dbg(info->dev, "mmap() framebuffer addr:%lu size:%lu\n", 3418c2ecf20Sopenharmony_ci pos, size); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci while (size > 0) { 3448c2ecf20Sopenharmony_ci page = vmalloc_to_pfn((void *)pos); 3458c2ecf20Sopenharmony_ci if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) 3468c2ecf20Sopenharmony_ci return -EAGAIN; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci start += PAGE_SIZE; 3498c2ecf20Sopenharmony_ci pos += PAGE_SIZE; 3508c2ecf20Sopenharmony_ci if (size > PAGE_SIZE) 3518c2ecf20Sopenharmony_ci size -= PAGE_SIZE; 3528c2ecf20Sopenharmony_ci else 3538c2ecf20Sopenharmony_ci size = 0; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci return 0; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci/* 3608c2ecf20Sopenharmony_ci * Trims identical data from front and back of line 3618c2ecf20Sopenharmony_ci * Sets new front buffer address and width 3628c2ecf20Sopenharmony_ci * And returns byte count of identical pixels 3638c2ecf20Sopenharmony_ci * Assumes CPU natural alignment (unsigned long) 3648c2ecf20Sopenharmony_ci * for back and front buffer ptrs and width 3658c2ecf20Sopenharmony_ci */ 3668c2ecf20Sopenharmony_cistatic int dlfb_trim_hline(const u8 *bback, const u8 **bfront, int *width_bytes) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci int j, k; 3698c2ecf20Sopenharmony_ci const unsigned long *back = (const unsigned long *) bback; 3708c2ecf20Sopenharmony_ci const unsigned long *front = (const unsigned long *) *bfront; 3718c2ecf20Sopenharmony_ci const int width = *width_bytes / sizeof(unsigned long); 3728c2ecf20Sopenharmony_ci int identical = width; 3738c2ecf20Sopenharmony_ci int start = width; 3748c2ecf20Sopenharmony_ci int end = width; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci for (j = 0; j < width; j++) { 3778c2ecf20Sopenharmony_ci if (back[j] != front[j]) { 3788c2ecf20Sopenharmony_ci start = j; 3798c2ecf20Sopenharmony_ci break; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci for (k = width - 1; k > j; k--) { 3848c2ecf20Sopenharmony_ci if (back[k] != front[k]) { 3858c2ecf20Sopenharmony_ci end = k+1; 3868c2ecf20Sopenharmony_ci break; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci identical = start + (width - end); 3918c2ecf20Sopenharmony_ci *bfront = (u8 *) &front[start]; 3928c2ecf20Sopenharmony_ci *width_bytes = (end - start) * sizeof(unsigned long); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci return identical * sizeof(unsigned long); 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci/* 3988c2ecf20Sopenharmony_ci * Render a command stream for an encoded horizontal line segment of pixels. 3998c2ecf20Sopenharmony_ci * 4008c2ecf20Sopenharmony_ci * A command buffer holds several commands. 4018c2ecf20Sopenharmony_ci * It always begins with a fresh command header 4028c2ecf20Sopenharmony_ci * (the protocol doesn't require this, but we enforce it to allow 4038c2ecf20Sopenharmony_ci * multiple buffers to be potentially encoded and sent in parallel). 4048c2ecf20Sopenharmony_ci * A single command encodes one contiguous horizontal line of pixels 4058c2ecf20Sopenharmony_ci * 4068c2ecf20Sopenharmony_ci * The function relies on the client to do all allocation, so that 4078c2ecf20Sopenharmony_ci * rendering can be done directly to output buffers (e.g. USB URBs). 4088c2ecf20Sopenharmony_ci * The function fills the supplied command buffer, providing information 4098c2ecf20Sopenharmony_ci * on where it left off, so the client may call in again with additional 4108c2ecf20Sopenharmony_ci * buffers if the line will take several buffers to complete. 4118c2ecf20Sopenharmony_ci * 4128c2ecf20Sopenharmony_ci * A single command can transmit a maximum of 256 pixels, 4138c2ecf20Sopenharmony_ci * regardless of the compression ratio (protocol design limit). 4148c2ecf20Sopenharmony_ci * To the hardware, 0 for a size byte means 256 4158c2ecf20Sopenharmony_ci * 4168c2ecf20Sopenharmony_ci * Rather than 256 pixel commands which are either rl or raw encoded, 4178c2ecf20Sopenharmony_ci * the rlx command simply assumes alternating raw and rl spans within one cmd. 4188c2ecf20Sopenharmony_ci * This has a slightly larger header overhead, but produces more even results. 4198c2ecf20Sopenharmony_ci * It also processes all data (read and write) in a single pass. 4208c2ecf20Sopenharmony_ci * Performance benchmarks of common cases show it having just slightly better 4218c2ecf20Sopenharmony_ci * compression than 256 pixel raw or rle commands, with similar CPU consumpion. 4228c2ecf20Sopenharmony_ci * But for very rl friendly data, will compress not quite as well. 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_cistatic void dlfb_compress_hline( 4258c2ecf20Sopenharmony_ci const uint16_t **pixel_start_ptr, 4268c2ecf20Sopenharmony_ci const uint16_t *const pixel_end, 4278c2ecf20Sopenharmony_ci uint32_t *device_address_ptr, 4288c2ecf20Sopenharmony_ci uint8_t **command_buffer_ptr, 4298c2ecf20Sopenharmony_ci const uint8_t *const cmd_buffer_end, 4308c2ecf20Sopenharmony_ci unsigned long back_buffer_offset, 4318c2ecf20Sopenharmony_ci int *ident_ptr) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci const uint16_t *pixel = *pixel_start_ptr; 4348c2ecf20Sopenharmony_ci uint32_t dev_addr = *device_address_ptr; 4358c2ecf20Sopenharmony_ci uint8_t *cmd = *command_buffer_ptr; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci while ((pixel_end > pixel) && 4388c2ecf20Sopenharmony_ci (cmd_buffer_end - MIN_RLX_CMD_BYTES > cmd)) { 4398c2ecf20Sopenharmony_ci uint8_t *raw_pixels_count_byte = NULL; 4408c2ecf20Sopenharmony_ci uint8_t *cmd_pixels_count_byte = NULL; 4418c2ecf20Sopenharmony_ci const uint16_t *raw_pixel_start = NULL; 4428c2ecf20Sopenharmony_ci const uint16_t *cmd_pixel_start, *cmd_pixel_end = NULL; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (back_buffer_offset && 4458c2ecf20Sopenharmony_ci *pixel == *(u16 *)((u8 *)pixel + back_buffer_offset)) { 4468c2ecf20Sopenharmony_ci pixel++; 4478c2ecf20Sopenharmony_ci dev_addr += BPP; 4488c2ecf20Sopenharmony_ci (*ident_ptr)++; 4498c2ecf20Sopenharmony_ci continue; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci *cmd++ = 0xAF; 4538c2ecf20Sopenharmony_ci *cmd++ = 0x6B; 4548c2ecf20Sopenharmony_ci *cmd++ = dev_addr >> 16; 4558c2ecf20Sopenharmony_ci *cmd++ = dev_addr >> 8; 4568c2ecf20Sopenharmony_ci *cmd++ = dev_addr; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci cmd_pixels_count_byte = cmd++; /* we'll know this later */ 4598c2ecf20Sopenharmony_ci cmd_pixel_start = pixel; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci raw_pixels_count_byte = cmd++; /* we'll know this later */ 4628c2ecf20Sopenharmony_ci raw_pixel_start = pixel; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci cmd_pixel_end = pixel + min3(MAX_CMD_PIXELS + 1UL, 4658c2ecf20Sopenharmony_ci (unsigned long)(pixel_end - pixel), 4668c2ecf20Sopenharmony_ci (unsigned long)(cmd_buffer_end - 1 - cmd) / BPP); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (back_buffer_offset) { 4698c2ecf20Sopenharmony_ci /* note: the framebuffer may change under us, so we must test for underflow */ 4708c2ecf20Sopenharmony_ci while (cmd_pixel_end - 1 > pixel && 4718c2ecf20Sopenharmony_ci *(cmd_pixel_end - 1) == *(u16 *)((u8 *)(cmd_pixel_end - 1) + back_buffer_offset)) 4728c2ecf20Sopenharmony_ci cmd_pixel_end--; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci while (pixel < cmd_pixel_end) { 4768c2ecf20Sopenharmony_ci const uint16_t * const repeating_pixel = pixel; 4778c2ecf20Sopenharmony_ci u16 pixel_value = *pixel; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci put_unaligned_be16(pixel_value, cmd); 4808c2ecf20Sopenharmony_ci if (back_buffer_offset) 4818c2ecf20Sopenharmony_ci *(u16 *)((u8 *)pixel + back_buffer_offset) = pixel_value; 4828c2ecf20Sopenharmony_ci cmd += 2; 4838c2ecf20Sopenharmony_ci pixel++; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (unlikely((pixel < cmd_pixel_end) && 4868c2ecf20Sopenharmony_ci (*pixel == pixel_value))) { 4878c2ecf20Sopenharmony_ci /* go back and fill in raw pixel count */ 4888c2ecf20Sopenharmony_ci *raw_pixels_count_byte = ((repeating_pixel - 4898c2ecf20Sopenharmony_ci raw_pixel_start) + 1) & 0xFF; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci do { 4928c2ecf20Sopenharmony_ci if (back_buffer_offset) 4938c2ecf20Sopenharmony_ci *(u16 *)((u8 *)pixel + back_buffer_offset) = pixel_value; 4948c2ecf20Sopenharmony_ci pixel++; 4958c2ecf20Sopenharmony_ci } while ((pixel < cmd_pixel_end) && 4968c2ecf20Sopenharmony_ci (*pixel == pixel_value)); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* immediately after raw data is repeat byte */ 4998c2ecf20Sopenharmony_ci *cmd++ = ((pixel - repeating_pixel) - 1) & 0xFF; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* Then start another raw pixel span */ 5028c2ecf20Sopenharmony_ci raw_pixel_start = pixel; 5038c2ecf20Sopenharmony_ci raw_pixels_count_byte = cmd++; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (pixel > raw_pixel_start) { 5088c2ecf20Sopenharmony_ci /* finalize last RAW span */ 5098c2ecf20Sopenharmony_ci *raw_pixels_count_byte = (pixel-raw_pixel_start) & 0xFF; 5108c2ecf20Sopenharmony_ci } else { 5118c2ecf20Sopenharmony_ci /* undo unused byte */ 5128c2ecf20Sopenharmony_ci cmd--; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci *cmd_pixels_count_byte = (pixel - cmd_pixel_start) & 0xFF; 5168c2ecf20Sopenharmony_ci dev_addr += (u8 *)pixel - (u8 *)cmd_pixel_start; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci if (cmd_buffer_end - MIN_RLX_CMD_BYTES <= cmd) { 5208c2ecf20Sopenharmony_ci /* Fill leftover bytes with no-ops */ 5218c2ecf20Sopenharmony_ci if (cmd_buffer_end > cmd) 5228c2ecf20Sopenharmony_ci memset(cmd, 0xAF, cmd_buffer_end - cmd); 5238c2ecf20Sopenharmony_ci cmd = (uint8_t *) cmd_buffer_end; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci *command_buffer_ptr = cmd; 5278c2ecf20Sopenharmony_ci *pixel_start_ptr = pixel; 5288c2ecf20Sopenharmony_ci *device_address_ptr = dev_addr; 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci/* 5328c2ecf20Sopenharmony_ci * There are 3 copies of every pixel: The front buffer that the fbdev 5338c2ecf20Sopenharmony_ci * client renders to, the actual framebuffer across the USB bus in hardware 5348c2ecf20Sopenharmony_ci * (that we can only write to, slowly, and can never read), and (optionally) 5358c2ecf20Sopenharmony_ci * our shadow copy that tracks what's been sent to that hardware buffer. 5368c2ecf20Sopenharmony_ci */ 5378c2ecf20Sopenharmony_cistatic int dlfb_render_hline(struct dlfb_data *dlfb, struct urb **urb_ptr, 5388c2ecf20Sopenharmony_ci const char *front, char **urb_buf_ptr, 5398c2ecf20Sopenharmony_ci u32 byte_offset, u32 byte_width, 5408c2ecf20Sopenharmony_ci int *ident_ptr, int *sent_ptr) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci const u8 *line_start, *line_end, *next_pixel; 5438c2ecf20Sopenharmony_ci u32 dev_addr = dlfb->base16 + byte_offset; 5448c2ecf20Sopenharmony_ci struct urb *urb = *urb_ptr; 5458c2ecf20Sopenharmony_ci u8 *cmd = *urb_buf_ptr; 5468c2ecf20Sopenharmony_ci u8 *cmd_end = (u8 *) urb->transfer_buffer + urb->transfer_buffer_length; 5478c2ecf20Sopenharmony_ci unsigned long back_buffer_offset = 0; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci line_start = (u8 *) (front + byte_offset); 5508c2ecf20Sopenharmony_ci next_pixel = line_start; 5518c2ecf20Sopenharmony_ci line_end = next_pixel + byte_width; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (dlfb->backing_buffer) { 5548c2ecf20Sopenharmony_ci int offset; 5558c2ecf20Sopenharmony_ci const u8 *back_start = (u8 *) (dlfb->backing_buffer 5568c2ecf20Sopenharmony_ci + byte_offset); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci back_buffer_offset = (unsigned long)back_start - (unsigned long)line_start; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci *ident_ptr += dlfb_trim_hline(back_start, &next_pixel, 5618c2ecf20Sopenharmony_ci &byte_width); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci offset = next_pixel - line_start; 5648c2ecf20Sopenharmony_ci line_end = next_pixel + byte_width; 5658c2ecf20Sopenharmony_ci dev_addr += offset; 5668c2ecf20Sopenharmony_ci back_start += offset; 5678c2ecf20Sopenharmony_ci line_start += offset; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci while (next_pixel < line_end) { 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci dlfb_compress_hline((const uint16_t **) &next_pixel, 5738c2ecf20Sopenharmony_ci (const uint16_t *) line_end, &dev_addr, 5748c2ecf20Sopenharmony_ci (u8 **) &cmd, (u8 *) cmd_end, back_buffer_offset, 5758c2ecf20Sopenharmony_ci ident_ptr); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (cmd >= cmd_end) { 5788c2ecf20Sopenharmony_ci int len = cmd - (u8 *) urb->transfer_buffer; 5798c2ecf20Sopenharmony_ci if (dlfb_submit_urb(dlfb, urb, len)) 5808c2ecf20Sopenharmony_ci return 1; /* lost pixels is set */ 5818c2ecf20Sopenharmony_ci *sent_ptr += len; 5828c2ecf20Sopenharmony_ci urb = dlfb_get_urb(dlfb); 5838c2ecf20Sopenharmony_ci if (!urb) 5848c2ecf20Sopenharmony_ci return 1; /* lost_pixels is set */ 5858c2ecf20Sopenharmony_ci *urb_ptr = urb; 5868c2ecf20Sopenharmony_ci cmd = urb->transfer_buffer; 5878c2ecf20Sopenharmony_ci cmd_end = &cmd[urb->transfer_buffer_length]; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci *urb_buf_ptr = cmd; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci return 0; 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic int dlfb_handle_damage(struct dlfb_data *dlfb, int x, int y, int width, int height) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci int i, ret; 5998c2ecf20Sopenharmony_ci char *cmd; 6008c2ecf20Sopenharmony_ci cycles_t start_cycles, end_cycles; 6018c2ecf20Sopenharmony_ci int bytes_sent = 0; 6028c2ecf20Sopenharmony_ci int bytes_identical = 0; 6038c2ecf20Sopenharmony_ci struct urb *urb; 6048c2ecf20Sopenharmony_ci int aligned_x; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci start_cycles = get_cycles(); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci mutex_lock(&dlfb->render_mutex); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci aligned_x = DL_ALIGN_DOWN(x, sizeof(unsigned long)); 6118c2ecf20Sopenharmony_ci width = DL_ALIGN_UP(width + (x-aligned_x), sizeof(unsigned long)); 6128c2ecf20Sopenharmony_ci x = aligned_x; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci if ((width <= 0) || 6158c2ecf20Sopenharmony_ci (x + width > dlfb->info->var.xres) || 6168c2ecf20Sopenharmony_ci (y + height > dlfb->info->var.yres)) { 6178c2ecf20Sopenharmony_ci ret = -EINVAL; 6188c2ecf20Sopenharmony_ci goto unlock_ret; 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (!atomic_read(&dlfb->usb_active)) { 6228c2ecf20Sopenharmony_ci ret = 0; 6238c2ecf20Sopenharmony_ci goto unlock_ret; 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci urb = dlfb_get_urb(dlfb); 6278c2ecf20Sopenharmony_ci if (!urb) { 6288c2ecf20Sopenharmony_ci ret = 0; 6298c2ecf20Sopenharmony_ci goto unlock_ret; 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci cmd = urb->transfer_buffer; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci for (i = y; i < y + height ; i++) { 6348c2ecf20Sopenharmony_ci const int line_offset = dlfb->info->fix.line_length * i; 6358c2ecf20Sopenharmony_ci const int byte_offset = line_offset + (x * BPP); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci if (dlfb_render_hline(dlfb, &urb, 6388c2ecf20Sopenharmony_ci (char *) dlfb->info->fix.smem_start, 6398c2ecf20Sopenharmony_ci &cmd, byte_offset, width * BPP, 6408c2ecf20Sopenharmony_ci &bytes_identical, &bytes_sent)) 6418c2ecf20Sopenharmony_ci goto error; 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci if (cmd > (char *) urb->transfer_buffer) { 6458c2ecf20Sopenharmony_ci int len; 6468c2ecf20Sopenharmony_ci if (cmd < (char *) urb->transfer_buffer + urb->transfer_buffer_length) 6478c2ecf20Sopenharmony_ci *cmd++ = 0xAF; 6488c2ecf20Sopenharmony_ci /* Send partial buffer remaining before exiting */ 6498c2ecf20Sopenharmony_ci len = cmd - (char *) urb->transfer_buffer; 6508c2ecf20Sopenharmony_ci dlfb_submit_urb(dlfb, urb, len); 6518c2ecf20Sopenharmony_ci bytes_sent += len; 6528c2ecf20Sopenharmony_ci } else 6538c2ecf20Sopenharmony_ci dlfb_urb_completion(urb); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cierror: 6568c2ecf20Sopenharmony_ci atomic_add(bytes_sent, &dlfb->bytes_sent); 6578c2ecf20Sopenharmony_ci atomic_add(bytes_identical, &dlfb->bytes_identical); 6588c2ecf20Sopenharmony_ci atomic_add(width*height*2, &dlfb->bytes_rendered); 6598c2ecf20Sopenharmony_ci end_cycles = get_cycles(); 6608c2ecf20Sopenharmony_ci atomic_add(((unsigned int) ((end_cycles - start_cycles) 6618c2ecf20Sopenharmony_ci >> 10)), /* Kcycles */ 6628c2ecf20Sopenharmony_ci &dlfb->cpu_kcycles_used); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci ret = 0; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ciunlock_ret: 6678c2ecf20Sopenharmony_ci mutex_unlock(&dlfb->render_mutex); 6688c2ecf20Sopenharmony_ci return ret; 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cistatic void dlfb_init_damage(struct dlfb_data *dlfb) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci dlfb->damage_x = INT_MAX; 6748c2ecf20Sopenharmony_ci dlfb->damage_x2 = 0; 6758c2ecf20Sopenharmony_ci dlfb->damage_y = INT_MAX; 6768c2ecf20Sopenharmony_ci dlfb->damage_y2 = 0; 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic void dlfb_damage_work(struct work_struct *w) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci struct dlfb_data *dlfb = container_of(w, struct dlfb_data, damage_work); 6828c2ecf20Sopenharmony_ci int x, x2, y, y2; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci spin_lock_irq(&dlfb->damage_lock); 6858c2ecf20Sopenharmony_ci x = dlfb->damage_x; 6868c2ecf20Sopenharmony_ci x2 = dlfb->damage_x2; 6878c2ecf20Sopenharmony_ci y = dlfb->damage_y; 6888c2ecf20Sopenharmony_ci y2 = dlfb->damage_y2; 6898c2ecf20Sopenharmony_ci dlfb_init_damage(dlfb); 6908c2ecf20Sopenharmony_ci spin_unlock_irq(&dlfb->damage_lock); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci if (x < x2 && y < y2) 6938c2ecf20Sopenharmony_ci dlfb_handle_damage(dlfb, x, y, x2 - x, y2 - y); 6948c2ecf20Sopenharmony_ci} 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cistatic void dlfb_offload_damage(struct dlfb_data *dlfb, int x, int y, int width, int height) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci unsigned long flags; 6998c2ecf20Sopenharmony_ci int x2 = x + width; 7008c2ecf20Sopenharmony_ci int y2 = y + height; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci if (x >= x2 || y >= y2) 7038c2ecf20Sopenharmony_ci return; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci spin_lock_irqsave(&dlfb->damage_lock, flags); 7068c2ecf20Sopenharmony_ci dlfb->damage_x = min(x, dlfb->damage_x); 7078c2ecf20Sopenharmony_ci dlfb->damage_x2 = max(x2, dlfb->damage_x2); 7088c2ecf20Sopenharmony_ci dlfb->damage_y = min(y, dlfb->damage_y); 7098c2ecf20Sopenharmony_ci dlfb->damage_y2 = max(y2, dlfb->damage_y2); 7108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dlfb->damage_lock, flags); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci schedule_work(&dlfb->damage_work); 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci/* 7168c2ecf20Sopenharmony_ci * Path triggered by usermode clients who write to filesystem 7178c2ecf20Sopenharmony_ci * e.g. cat filename > /dev/fb1 7188c2ecf20Sopenharmony_ci * Not used by X Windows or text-mode console. But useful for testing. 7198c2ecf20Sopenharmony_ci * Slow because of extra copy and we must assume all pixels dirty. 7208c2ecf20Sopenharmony_ci */ 7218c2ecf20Sopenharmony_cistatic ssize_t dlfb_ops_write(struct fb_info *info, const char __user *buf, 7228c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 7238c2ecf20Sopenharmony_ci{ 7248c2ecf20Sopenharmony_ci ssize_t result; 7258c2ecf20Sopenharmony_ci struct dlfb_data *dlfb = info->par; 7268c2ecf20Sopenharmony_ci u32 offset = (u32) *ppos; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci result = fb_sys_write(info, buf, count, ppos); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci if (result > 0) { 7318c2ecf20Sopenharmony_ci int start = max((int)(offset / info->fix.line_length), 0); 7328c2ecf20Sopenharmony_ci int lines = min((u32)((result / info->fix.line_length) + 1), 7338c2ecf20Sopenharmony_ci (u32)info->var.yres); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci dlfb_handle_damage(dlfb, 0, start, info->var.xres, 7368c2ecf20Sopenharmony_ci lines); 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci return result; 7408c2ecf20Sopenharmony_ci} 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci/* hardware has native COPY command (see libdlo), but not worth it for fbcon */ 7438c2ecf20Sopenharmony_cistatic void dlfb_ops_copyarea(struct fb_info *info, 7448c2ecf20Sopenharmony_ci const struct fb_copyarea *area) 7458c2ecf20Sopenharmony_ci{ 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci struct dlfb_data *dlfb = info->par; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci sys_copyarea(info, area); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci dlfb_offload_damage(dlfb, area->dx, area->dy, 7528c2ecf20Sopenharmony_ci area->width, area->height); 7538c2ecf20Sopenharmony_ci} 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_cistatic void dlfb_ops_imageblit(struct fb_info *info, 7568c2ecf20Sopenharmony_ci const struct fb_image *image) 7578c2ecf20Sopenharmony_ci{ 7588c2ecf20Sopenharmony_ci struct dlfb_data *dlfb = info->par; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci sys_imageblit(info, image); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci dlfb_offload_damage(dlfb, image->dx, image->dy, 7638c2ecf20Sopenharmony_ci image->width, image->height); 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_cistatic void dlfb_ops_fillrect(struct fb_info *info, 7678c2ecf20Sopenharmony_ci const struct fb_fillrect *rect) 7688c2ecf20Sopenharmony_ci{ 7698c2ecf20Sopenharmony_ci struct dlfb_data *dlfb = info->par; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci sys_fillrect(info, rect); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci dlfb_offload_damage(dlfb, rect->dx, rect->dy, rect->width, 7748c2ecf20Sopenharmony_ci rect->height); 7758c2ecf20Sopenharmony_ci} 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci/* 7788c2ecf20Sopenharmony_ci * NOTE: fb_defio.c is holding info->fbdefio.mutex 7798c2ecf20Sopenharmony_ci * Touching ANY framebuffer memory that triggers a page fault 7808c2ecf20Sopenharmony_ci * in fb_defio will cause a deadlock, when it also tries to 7818c2ecf20Sopenharmony_ci * grab the same mutex. 7828c2ecf20Sopenharmony_ci */ 7838c2ecf20Sopenharmony_cistatic void dlfb_dpy_deferred_io(struct fb_info *info, 7848c2ecf20Sopenharmony_ci struct list_head *pagelist) 7858c2ecf20Sopenharmony_ci{ 7868c2ecf20Sopenharmony_ci struct page *cur; 7878c2ecf20Sopenharmony_ci struct fb_deferred_io *fbdefio = info->fbdefio; 7888c2ecf20Sopenharmony_ci struct dlfb_data *dlfb = info->par; 7898c2ecf20Sopenharmony_ci struct urb *urb; 7908c2ecf20Sopenharmony_ci char *cmd; 7918c2ecf20Sopenharmony_ci cycles_t start_cycles, end_cycles; 7928c2ecf20Sopenharmony_ci int bytes_sent = 0; 7938c2ecf20Sopenharmony_ci int bytes_identical = 0; 7948c2ecf20Sopenharmony_ci int bytes_rendered = 0; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci mutex_lock(&dlfb->render_mutex); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (!fb_defio) 7998c2ecf20Sopenharmony_ci goto unlock_ret; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci if (!atomic_read(&dlfb->usb_active)) 8028c2ecf20Sopenharmony_ci goto unlock_ret; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci start_cycles = get_cycles(); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci urb = dlfb_get_urb(dlfb); 8078c2ecf20Sopenharmony_ci if (!urb) 8088c2ecf20Sopenharmony_ci goto unlock_ret; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci cmd = urb->transfer_buffer; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci /* walk the written page list and render each to device */ 8138c2ecf20Sopenharmony_ci list_for_each_entry(cur, &fbdefio->pagelist, lru) { 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci if (dlfb_render_hline(dlfb, &urb, (char *) info->fix.smem_start, 8168c2ecf20Sopenharmony_ci &cmd, cur->index << PAGE_SHIFT, 8178c2ecf20Sopenharmony_ci PAGE_SIZE, &bytes_identical, &bytes_sent)) 8188c2ecf20Sopenharmony_ci goto error; 8198c2ecf20Sopenharmony_ci bytes_rendered += PAGE_SIZE; 8208c2ecf20Sopenharmony_ci } 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci if (cmd > (char *) urb->transfer_buffer) { 8238c2ecf20Sopenharmony_ci int len; 8248c2ecf20Sopenharmony_ci if (cmd < (char *) urb->transfer_buffer + urb->transfer_buffer_length) 8258c2ecf20Sopenharmony_ci *cmd++ = 0xAF; 8268c2ecf20Sopenharmony_ci /* Send partial buffer remaining before exiting */ 8278c2ecf20Sopenharmony_ci len = cmd - (char *) urb->transfer_buffer; 8288c2ecf20Sopenharmony_ci dlfb_submit_urb(dlfb, urb, len); 8298c2ecf20Sopenharmony_ci bytes_sent += len; 8308c2ecf20Sopenharmony_ci } else 8318c2ecf20Sopenharmony_ci dlfb_urb_completion(urb); 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_cierror: 8348c2ecf20Sopenharmony_ci atomic_add(bytes_sent, &dlfb->bytes_sent); 8358c2ecf20Sopenharmony_ci atomic_add(bytes_identical, &dlfb->bytes_identical); 8368c2ecf20Sopenharmony_ci atomic_add(bytes_rendered, &dlfb->bytes_rendered); 8378c2ecf20Sopenharmony_ci end_cycles = get_cycles(); 8388c2ecf20Sopenharmony_ci atomic_add(((unsigned int) ((end_cycles - start_cycles) 8398c2ecf20Sopenharmony_ci >> 10)), /* Kcycles */ 8408c2ecf20Sopenharmony_ci &dlfb->cpu_kcycles_used); 8418c2ecf20Sopenharmony_ciunlock_ret: 8428c2ecf20Sopenharmony_ci mutex_unlock(&dlfb->render_mutex); 8438c2ecf20Sopenharmony_ci} 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_cistatic int dlfb_get_edid(struct dlfb_data *dlfb, char *edid, int len) 8468c2ecf20Sopenharmony_ci{ 8478c2ecf20Sopenharmony_ci int i, ret; 8488c2ecf20Sopenharmony_ci char *rbuf; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci rbuf = kmalloc(2, GFP_KERNEL); 8518c2ecf20Sopenharmony_ci if (!rbuf) 8528c2ecf20Sopenharmony_ci return 0; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 8558c2ecf20Sopenharmony_ci ret = usb_control_msg(dlfb->udev, 8568c2ecf20Sopenharmony_ci usb_rcvctrlpipe(dlfb->udev, 0), 0x02, 8578c2ecf20Sopenharmony_ci (0x80 | (0x02 << 5)), i << 8, 0xA1, 8588c2ecf20Sopenharmony_ci rbuf, 2, USB_CTRL_GET_TIMEOUT); 8598c2ecf20Sopenharmony_ci if (ret < 2) { 8608c2ecf20Sopenharmony_ci dev_err(&dlfb->udev->dev, 8618c2ecf20Sopenharmony_ci "Read EDID byte %d failed: %d\n", i, ret); 8628c2ecf20Sopenharmony_ci i--; 8638c2ecf20Sopenharmony_ci break; 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci edid[i] = rbuf[1]; 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci kfree(rbuf); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci return i; 8718c2ecf20Sopenharmony_ci} 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_cistatic int dlfb_ops_ioctl(struct fb_info *info, unsigned int cmd, 8748c2ecf20Sopenharmony_ci unsigned long arg) 8758c2ecf20Sopenharmony_ci{ 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci struct dlfb_data *dlfb = info->par; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci if (!atomic_read(&dlfb->usb_active)) 8808c2ecf20Sopenharmony_ci return 0; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci /* TODO: Update X server to get this from sysfs instead */ 8838c2ecf20Sopenharmony_ci if (cmd == DLFB_IOCTL_RETURN_EDID) { 8848c2ecf20Sopenharmony_ci void __user *edid = (void __user *)arg; 8858c2ecf20Sopenharmony_ci if (copy_to_user(edid, dlfb->edid, dlfb->edid_size)) 8868c2ecf20Sopenharmony_ci return -EFAULT; 8878c2ecf20Sopenharmony_ci return 0; 8888c2ecf20Sopenharmony_ci } 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci /* TODO: Help propose a standard fb.h ioctl to report mmap damage */ 8918c2ecf20Sopenharmony_ci if (cmd == DLFB_IOCTL_REPORT_DAMAGE) { 8928c2ecf20Sopenharmony_ci struct dloarea area; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci if (copy_from_user(&area, (void __user *)arg, 8958c2ecf20Sopenharmony_ci sizeof(struct dloarea))) 8968c2ecf20Sopenharmony_ci return -EFAULT; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci /* 8998c2ecf20Sopenharmony_ci * If we have a damage-aware client, turn fb_defio "off" 9008c2ecf20Sopenharmony_ci * To avoid perf imact of unnecessary page fault handling. 9018c2ecf20Sopenharmony_ci * Done by resetting the delay for this fb_info to a very 9028c2ecf20Sopenharmony_ci * long period. Pages will become writable and stay that way. 9038c2ecf20Sopenharmony_ci * Reset to normal value when all clients have closed this fb. 9048c2ecf20Sopenharmony_ci */ 9058c2ecf20Sopenharmony_ci if (info->fbdefio) 9068c2ecf20Sopenharmony_ci info->fbdefio->delay = DL_DEFIO_WRITE_DISABLE; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci if (area.x < 0) 9098c2ecf20Sopenharmony_ci area.x = 0; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci if (area.x > info->var.xres) 9128c2ecf20Sopenharmony_ci area.x = info->var.xres; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci if (area.y < 0) 9158c2ecf20Sopenharmony_ci area.y = 0; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci if (area.y > info->var.yres) 9188c2ecf20Sopenharmony_ci area.y = info->var.yres; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci dlfb_handle_damage(dlfb, area.x, area.y, area.w, area.h); 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci return 0; 9248c2ecf20Sopenharmony_ci} 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci/* taken from vesafb */ 9278c2ecf20Sopenharmony_cistatic int 9288c2ecf20Sopenharmony_cidlfb_ops_setcolreg(unsigned regno, unsigned red, unsigned green, 9298c2ecf20Sopenharmony_ci unsigned blue, unsigned transp, struct fb_info *info) 9308c2ecf20Sopenharmony_ci{ 9318c2ecf20Sopenharmony_ci int err = 0; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci if (regno >= info->cmap.len) 9348c2ecf20Sopenharmony_ci return 1; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci if (regno < 16) { 9378c2ecf20Sopenharmony_ci if (info->var.red.offset == 10) { 9388c2ecf20Sopenharmony_ci /* 1:5:5:5 */ 9398c2ecf20Sopenharmony_ci ((u32 *) (info->pseudo_palette))[regno] = 9408c2ecf20Sopenharmony_ci ((red & 0xf800) >> 1) | 9418c2ecf20Sopenharmony_ci ((green & 0xf800) >> 6) | ((blue & 0xf800) >> 11); 9428c2ecf20Sopenharmony_ci } else { 9438c2ecf20Sopenharmony_ci /* 0:5:6:5 */ 9448c2ecf20Sopenharmony_ci ((u32 *) (info->pseudo_palette))[regno] = 9458c2ecf20Sopenharmony_ci ((red & 0xf800)) | 9468c2ecf20Sopenharmony_ci ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11); 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci return err; 9518c2ecf20Sopenharmony_ci} 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci/* 9548c2ecf20Sopenharmony_ci * It's common for several clients to have framebuffer open simultaneously. 9558c2ecf20Sopenharmony_ci * e.g. both fbcon and X. Makes things interesting. 9568c2ecf20Sopenharmony_ci * Assumes caller is holding info->lock (for open and release at least) 9578c2ecf20Sopenharmony_ci */ 9588c2ecf20Sopenharmony_cistatic int dlfb_ops_open(struct fb_info *info, int user) 9598c2ecf20Sopenharmony_ci{ 9608c2ecf20Sopenharmony_ci struct dlfb_data *dlfb = info->par; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci /* 9638c2ecf20Sopenharmony_ci * fbcon aggressively connects to first framebuffer it finds, 9648c2ecf20Sopenharmony_ci * preventing other clients (X) from working properly. Usually 9658c2ecf20Sopenharmony_ci * not what the user wants. Fail by default with option to enable. 9668c2ecf20Sopenharmony_ci */ 9678c2ecf20Sopenharmony_ci if ((user == 0) && (!console)) 9688c2ecf20Sopenharmony_ci return -EBUSY; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci /* If the USB device is gone, we don't accept new opens */ 9718c2ecf20Sopenharmony_ci if (dlfb->virtualized) 9728c2ecf20Sopenharmony_ci return -ENODEV; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci dlfb->fb_count++; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci if (fb_defio && (info->fbdefio == NULL)) { 9778c2ecf20Sopenharmony_ci /* enable defio at last moment if not disabled by client */ 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci struct fb_deferred_io *fbdefio; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci fbdefio = kzalloc(sizeof(struct fb_deferred_io), GFP_KERNEL); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci if (fbdefio) { 9848c2ecf20Sopenharmony_ci fbdefio->delay = DL_DEFIO_WRITE_DELAY; 9858c2ecf20Sopenharmony_ci fbdefio->deferred_io = dlfb_dpy_deferred_io; 9868c2ecf20Sopenharmony_ci } 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci info->fbdefio = fbdefio; 9898c2ecf20Sopenharmony_ci fb_deferred_io_init(info); 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci dev_dbg(info->dev, "open, user=%d fb_info=%p count=%d\n", 9938c2ecf20Sopenharmony_ci user, info, dlfb->fb_count); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci return 0; 9968c2ecf20Sopenharmony_ci} 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_cistatic void dlfb_ops_destroy(struct fb_info *info) 9998c2ecf20Sopenharmony_ci{ 10008c2ecf20Sopenharmony_ci struct dlfb_data *dlfb = info->par; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci cancel_work_sync(&dlfb->damage_work); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci mutex_destroy(&dlfb->render_mutex); 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci if (info->cmap.len != 0) 10078c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 10088c2ecf20Sopenharmony_ci if (info->monspecs.modedb) 10098c2ecf20Sopenharmony_ci fb_destroy_modedb(info->monspecs.modedb); 10108c2ecf20Sopenharmony_ci vfree(info->screen_base); 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci fb_destroy_modelist(&info->modelist); 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci while (!list_empty(&dlfb->deferred_free)) { 10158c2ecf20Sopenharmony_ci struct dlfb_deferred_free *d = list_entry(dlfb->deferred_free.next, struct dlfb_deferred_free, list); 10168c2ecf20Sopenharmony_ci list_del(&d->list); 10178c2ecf20Sopenharmony_ci vfree(d->mem); 10188c2ecf20Sopenharmony_ci kfree(d); 10198c2ecf20Sopenharmony_ci } 10208c2ecf20Sopenharmony_ci vfree(dlfb->backing_buffer); 10218c2ecf20Sopenharmony_ci kfree(dlfb->edid); 10228c2ecf20Sopenharmony_ci dlfb_free_urb_list(dlfb); 10238c2ecf20Sopenharmony_ci usb_put_dev(dlfb->udev); 10248c2ecf20Sopenharmony_ci kfree(dlfb); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci /* Assume info structure is freed after this point */ 10278c2ecf20Sopenharmony_ci framebuffer_release(info); 10288c2ecf20Sopenharmony_ci} 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci/* 10318c2ecf20Sopenharmony_ci * Assumes caller is holding info->lock mutex (for open and release at least) 10328c2ecf20Sopenharmony_ci */ 10338c2ecf20Sopenharmony_cistatic int dlfb_ops_release(struct fb_info *info, int user) 10348c2ecf20Sopenharmony_ci{ 10358c2ecf20Sopenharmony_ci struct dlfb_data *dlfb = info->par; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci dlfb->fb_count--; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci if ((dlfb->fb_count == 0) && (info->fbdefio)) { 10408c2ecf20Sopenharmony_ci fb_deferred_io_cleanup(info); 10418c2ecf20Sopenharmony_ci kfree(info->fbdefio); 10428c2ecf20Sopenharmony_ci info->fbdefio = NULL; 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci dev_dbg(info->dev, "release, user=%d count=%d\n", user, dlfb->fb_count); 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci return 0; 10488c2ecf20Sopenharmony_ci} 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci/* 10518c2ecf20Sopenharmony_ci * Check whether a video mode is supported by the DisplayLink chip 10528c2ecf20Sopenharmony_ci * We start from monitor's modes, so don't need to filter that here 10538c2ecf20Sopenharmony_ci */ 10548c2ecf20Sopenharmony_cistatic int dlfb_is_valid_mode(struct fb_videomode *mode, struct dlfb_data *dlfb) 10558c2ecf20Sopenharmony_ci{ 10568c2ecf20Sopenharmony_ci if (mode->xres * mode->yres > dlfb->sku_pixel_limit) 10578c2ecf20Sopenharmony_ci return 0; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci return 1; 10608c2ecf20Sopenharmony_ci} 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_cistatic void dlfb_var_color_format(struct fb_var_screeninfo *var) 10638c2ecf20Sopenharmony_ci{ 10648c2ecf20Sopenharmony_ci const struct fb_bitfield red = { 11, 5, 0 }; 10658c2ecf20Sopenharmony_ci const struct fb_bitfield green = { 5, 6, 0 }; 10668c2ecf20Sopenharmony_ci const struct fb_bitfield blue = { 0, 5, 0 }; 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci var->bits_per_pixel = 16; 10698c2ecf20Sopenharmony_ci var->red = red; 10708c2ecf20Sopenharmony_ci var->green = green; 10718c2ecf20Sopenharmony_ci var->blue = blue; 10728c2ecf20Sopenharmony_ci} 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_cistatic int dlfb_ops_check_var(struct fb_var_screeninfo *var, 10758c2ecf20Sopenharmony_ci struct fb_info *info) 10768c2ecf20Sopenharmony_ci{ 10778c2ecf20Sopenharmony_ci struct fb_videomode mode; 10788c2ecf20Sopenharmony_ci struct dlfb_data *dlfb = info->par; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci /* set device-specific elements of var unrelated to mode */ 10818c2ecf20Sopenharmony_ci dlfb_var_color_format(var); 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci fb_var_to_videomode(&mode, var); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci if (!dlfb_is_valid_mode(&mode, dlfb)) 10868c2ecf20Sopenharmony_ci return -EINVAL; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci return 0; 10898c2ecf20Sopenharmony_ci} 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_cistatic int dlfb_ops_set_par(struct fb_info *info) 10928c2ecf20Sopenharmony_ci{ 10938c2ecf20Sopenharmony_ci struct dlfb_data *dlfb = info->par; 10948c2ecf20Sopenharmony_ci int result; 10958c2ecf20Sopenharmony_ci u16 *pix_framebuffer; 10968c2ecf20Sopenharmony_ci int i; 10978c2ecf20Sopenharmony_ci struct fb_var_screeninfo fvs; 10988c2ecf20Sopenharmony_ci u32 line_length = info->var.xres * (info->var.bits_per_pixel / 8); 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci /* clear the activate field because it causes spurious miscompares */ 11018c2ecf20Sopenharmony_ci fvs = info->var; 11028c2ecf20Sopenharmony_ci fvs.activate = 0; 11038c2ecf20Sopenharmony_ci fvs.vmode &= ~FB_VMODE_SMOOTH_XPAN; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci if (!memcmp(&dlfb->current_mode, &fvs, sizeof(struct fb_var_screeninfo))) 11068c2ecf20Sopenharmony_ci return 0; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci result = dlfb_realloc_framebuffer(dlfb, info, info->var.yres * line_length); 11098c2ecf20Sopenharmony_ci if (result) 11108c2ecf20Sopenharmony_ci return result; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci result = dlfb_set_video_mode(dlfb, &info->var); 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci if (result) 11158c2ecf20Sopenharmony_ci return result; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci dlfb->current_mode = fvs; 11188c2ecf20Sopenharmony_ci info->fix.line_length = line_length; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci if (dlfb->fb_count == 0) { 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci /* paint greenscreen */ 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci pix_framebuffer = (u16 *) info->screen_base; 11258c2ecf20Sopenharmony_ci for (i = 0; i < info->fix.smem_len / 2; i++) 11268c2ecf20Sopenharmony_ci pix_framebuffer[i] = 0x37e6; 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci dlfb_handle_damage(dlfb, 0, 0, info->var.xres, info->var.yres); 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci return 0; 11328c2ecf20Sopenharmony_ci} 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci/* To fonzi the jukebox (e.g. make blanking changes take effect) */ 11358c2ecf20Sopenharmony_cistatic char *dlfb_dummy_render(char *buf) 11368c2ecf20Sopenharmony_ci{ 11378c2ecf20Sopenharmony_ci *buf++ = 0xAF; 11388c2ecf20Sopenharmony_ci *buf++ = 0x6A; /* copy */ 11398c2ecf20Sopenharmony_ci *buf++ = 0x00; /* from address*/ 11408c2ecf20Sopenharmony_ci *buf++ = 0x00; 11418c2ecf20Sopenharmony_ci *buf++ = 0x00; 11428c2ecf20Sopenharmony_ci *buf++ = 0x01; /* one pixel */ 11438c2ecf20Sopenharmony_ci *buf++ = 0x00; /* to address */ 11448c2ecf20Sopenharmony_ci *buf++ = 0x00; 11458c2ecf20Sopenharmony_ci *buf++ = 0x00; 11468c2ecf20Sopenharmony_ci return buf; 11478c2ecf20Sopenharmony_ci} 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci/* 11508c2ecf20Sopenharmony_ci * In order to come back from full DPMS off, we need to set the mode again 11518c2ecf20Sopenharmony_ci */ 11528c2ecf20Sopenharmony_cistatic int dlfb_ops_blank(int blank_mode, struct fb_info *info) 11538c2ecf20Sopenharmony_ci{ 11548c2ecf20Sopenharmony_ci struct dlfb_data *dlfb = info->par; 11558c2ecf20Sopenharmony_ci char *bufptr; 11568c2ecf20Sopenharmony_ci struct urb *urb; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci dev_dbg(info->dev, "blank, mode %d --> %d\n", 11598c2ecf20Sopenharmony_ci dlfb->blank_mode, blank_mode); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci if ((dlfb->blank_mode == FB_BLANK_POWERDOWN) && 11628c2ecf20Sopenharmony_ci (blank_mode != FB_BLANK_POWERDOWN)) { 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci /* returning from powerdown requires a fresh modeset */ 11658c2ecf20Sopenharmony_ci dlfb_set_video_mode(dlfb, &info->var); 11668c2ecf20Sopenharmony_ci } 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci urb = dlfb_get_urb(dlfb); 11698c2ecf20Sopenharmony_ci if (!urb) 11708c2ecf20Sopenharmony_ci return 0; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci bufptr = (char *) urb->transfer_buffer; 11738c2ecf20Sopenharmony_ci bufptr = dlfb_vidreg_lock(bufptr); 11748c2ecf20Sopenharmony_ci bufptr = dlfb_blanking(bufptr, blank_mode); 11758c2ecf20Sopenharmony_ci bufptr = dlfb_vidreg_unlock(bufptr); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci /* seems like a render op is needed to have blank change take effect */ 11788c2ecf20Sopenharmony_ci bufptr = dlfb_dummy_render(bufptr); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci dlfb_submit_urb(dlfb, urb, bufptr - 11818c2ecf20Sopenharmony_ci (char *) urb->transfer_buffer); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci dlfb->blank_mode = blank_mode; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci return 0; 11868c2ecf20Sopenharmony_ci} 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_cistatic const struct fb_ops dlfb_ops = { 11898c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 11908c2ecf20Sopenharmony_ci .fb_read = fb_sys_read, 11918c2ecf20Sopenharmony_ci .fb_write = dlfb_ops_write, 11928c2ecf20Sopenharmony_ci .fb_setcolreg = dlfb_ops_setcolreg, 11938c2ecf20Sopenharmony_ci .fb_fillrect = dlfb_ops_fillrect, 11948c2ecf20Sopenharmony_ci .fb_copyarea = dlfb_ops_copyarea, 11958c2ecf20Sopenharmony_ci .fb_imageblit = dlfb_ops_imageblit, 11968c2ecf20Sopenharmony_ci .fb_mmap = dlfb_ops_mmap, 11978c2ecf20Sopenharmony_ci .fb_ioctl = dlfb_ops_ioctl, 11988c2ecf20Sopenharmony_ci .fb_open = dlfb_ops_open, 11998c2ecf20Sopenharmony_ci .fb_release = dlfb_ops_release, 12008c2ecf20Sopenharmony_ci .fb_blank = dlfb_ops_blank, 12018c2ecf20Sopenharmony_ci .fb_check_var = dlfb_ops_check_var, 12028c2ecf20Sopenharmony_ci .fb_set_par = dlfb_ops_set_par, 12038c2ecf20Sopenharmony_ci .fb_destroy = dlfb_ops_destroy, 12048c2ecf20Sopenharmony_ci}; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_cistatic void dlfb_deferred_vfree(struct dlfb_data *dlfb, void *mem) 12088c2ecf20Sopenharmony_ci{ 12098c2ecf20Sopenharmony_ci struct dlfb_deferred_free *d = kmalloc(sizeof(struct dlfb_deferred_free), GFP_KERNEL); 12108c2ecf20Sopenharmony_ci if (!d) 12118c2ecf20Sopenharmony_ci return; 12128c2ecf20Sopenharmony_ci d->mem = mem; 12138c2ecf20Sopenharmony_ci list_add(&d->list, &dlfb->deferred_free); 12148c2ecf20Sopenharmony_ci} 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci/* 12178c2ecf20Sopenharmony_ci * Assumes &info->lock held by caller 12188c2ecf20Sopenharmony_ci * Assumes no active clients have framebuffer open 12198c2ecf20Sopenharmony_ci */ 12208c2ecf20Sopenharmony_cistatic int dlfb_realloc_framebuffer(struct dlfb_data *dlfb, struct fb_info *info, u32 new_len) 12218c2ecf20Sopenharmony_ci{ 12228c2ecf20Sopenharmony_ci u32 old_len = info->fix.smem_len; 12238c2ecf20Sopenharmony_ci const void *old_fb = (const void __force *)info->screen_base; 12248c2ecf20Sopenharmony_ci unsigned char *new_fb; 12258c2ecf20Sopenharmony_ci unsigned char *new_back = NULL; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci new_len = PAGE_ALIGN(new_len); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci if (new_len > old_len) { 12308c2ecf20Sopenharmony_ci /* 12318c2ecf20Sopenharmony_ci * Alloc system memory for virtual framebuffer 12328c2ecf20Sopenharmony_ci */ 12338c2ecf20Sopenharmony_ci new_fb = vmalloc(new_len); 12348c2ecf20Sopenharmony_ci if (!new_fb) { 12358c2ecf20Sopenharmony_ci dev_err(info->dev, "Virtual framebuffer alloc failed\n"); 12368c2ecf20Sopenharmony_ci return -ENOMEM; 12378c2ecf20Sopenharmony_ci } 12388c2ecf20Sopenharmony_ci memset(new_fb, 0xff, new_len); 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci if (info->screen_base) { 12418c2ecf20Sopenharmony_ci memcpy(new_fb, old_fb, old_len); 12428c2ecf20Sopenharmony_ci dlfb_deferred_vfree(dlfb, (void __force *)info->screen_base); 12438c2ecf20Sopenharmony_ci } 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci info->screen_base = (char __iomem *)new_fb; 12468c2ecf20Sopenharmony_ci info->fix.smem_len = new_len; 12478c2ecf20Sopenharmony_ci info->fix.smem_start = (unsigned long) new_fb; 12488c2ecf20Sopenharmony_ci info->flags = udlfb_info_flags; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci /* 12518c2ecf20Sopenharmony_ci * Second framebuffer copy to mirror the framebuffer state 12528c2ecf20Sopenharmony_ci * on the physical USB device. We can function without this. 12538c2ecf20Sopenharmony_ci * But with imperfect damage info we may send pixels over USB 12548c2ecf20Sopenharmony_ci * that were, in fact, unchanged - wasting limited USB bandwidth 12558c2ecf20Sopenharmony_ci */ 12568c2ecf20Sopenharmony_ci if (shadow) 12578c2ecf20Sopenharmony_ci new_back = vzalloc(new_len); 12588c2ecf20Sopenharmony_ci if (!new_back) 12598c2ecf20Sopenharmony_ci dev_info(info->dev, 12608c2ecf20Sopenharmony_ci "No shadow/backing buffer allocated\n"); 12618c2ecf20Sopenharmony_ci else { 12628c2ecf20Sopenharmony_ci dlfb_deferred_vfree(dlfb, dlfb->backing_buffer); 12638c2ecf20Sopenharmony_ci dlfb->backing_buffer = new_back; 12648c2ecf20Sopenharmony_ci } 12658c2ecf20Sopenharmony_ci } 12668c2ecf20Sopenharmony_ci return 0; 12678c2ecf20Sopenharmony_ci} 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci/* 12708c2ecf20Sopenharmony_ci * 1) Get EDID from hw, or use sw default 12718c2ecf20Sopenharmony_ci * 2) Parse into various fb_info structs 12728c2ecf20Sopenharmony_ci * 3) Allocate virtual framebuffer memory to back highest res mode 12738c2ecf20Sopenharmony_ci * 12748c2ecf20Sopenharmony_ci * Parses EDID into three places used by various parts of fbdev: 12758c2ecf20Sopenharmony_ci * fb_var_screeninfo contains the timing of the monitor's preferred mode 12768c2ecf20Sopenharmony_ci * fb_info.monspecs is full parsed EDID info, including monspecs.modedb 12778c2ecf20Sopenharmony_ci * fb_info.modelist is a linked list of all monitor & VESA modes which work 12788c2ecf20Sopenharmony_ci * 12798c2ecf20Sopenharmony_ci * If EDID is not readable/valid, then modelist is all VESA modes, 12808c2ecf20Sopenharmony_ci * monspecs is NULL, and fb_var_screeninfo is set to safe VESA mode 12818c2ecf20Sopenharmony_ci * Returns 0 if successful 12828c2ecf20Sopenharmony_ci */ 12838c2ecf20Sopenharmony_cistatic int dlfb_setup_modes(struct dlfb_data *dlfb, 12848c2ecf20Sopenharmony_ci struct fb_info *info, 12858c2ecf20Sopenharmony_ci char *default_edid, size_t default_edid_size) 12868c2ecf20Sopenharmony_ci{ 12878c2ecf20Sopenharmony_ci char *edid; 12888c2ecf20Sopenharmony_ci int i, result = 0, tries = 3; 12898c2ecf20Sopenharmony_ci struct device *dev = info->device; 12908c2ecf20Sopenharmony_ci struct fb_videomode *mode; 12918c2ecf20Sopenharmony_ci const struct fb_videomode *default_vmode = NULL; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci if (info->dev) { 12948c2ecf20Sopenharmony_ci /* only use mutex if info has been registered */ 12958c2ecf20Sopenharmony_ci mutex_lock(&info->lock); 12968c2ecf20Sopenharmony_ci /* parent device is used otherwise */ 12978c2ecf20Sopenharmony_ci dev = info->dev; 12988c2ecf20Sopenharmony_ci } 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci edid = kmalloc(EDID_LENGTH, GFP_KERNEL); 13018c2ecf20Sopenharmony_ci if (!edid) { 13028c2ecf20Sopenharmony_ci result = -ENOMEM; 13038c2ecf20Sopenharmony_ci goto error; 13048c2ecf20Sopenharmony_ci } 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci fb_destroy_modelist(&info->modelist); 13078c2ecf20Sopenharmony_ci memset(&info->monspecs, 0, sizeof(info->monspecs)); 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci /* 13108c2ecf20Sopenharmony_ci * Try to (re)read EDID from hardware first 13118c2ecf20Sopenharmony_ci * EDID data may return, but not parse as valid 13128c2ecf20Sopenharmony_ci * Try again a few times, in case of e.g. analog cable noise 13138c2ecf20Sopenharmony_ci */ 13148c2ecf20Sopenharmony_ci while (tries--) { 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci i = dlfb_get_edid(dlfb, edid, EDID_LENGTH); 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci if (i >= EDID_LENGTH) 13198c2ecf20Sopenharmony_ci fb_edid_to_monspecs(edid, &info->monspecs); 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci if (info->monspecs.modedb_len > 0) { 13228c2ecf20Sopenharmony_ci dlfb->edid = edid; 13238c2ecf20Sopenharmony_ci dlfb->edid_size = i; 13248c2ecf20Sopenharmony_ci break; 13258c2ecf20Sopenharmony_ci } 13268c2ecf20Sopenharmony_ci } 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci /* If that fails, use a previously returned EDID if available */ 13298c2ecf20Sopenharmony_ci if (info->monspecs.modedb_len == 0) { 13308c2ecf20Sopenharmony_ci dev_err(dev, "Unable to get valid EDID from device/display\n"); 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci if (dlfb->edid) { 13338c2ecf20Sopenharmony_ci fb_edid_to_monspecs(dlfb->edid, &info->monspecs); 13348c2ecf20Sopenharmony_ci if (info->monspecs.modedb_len > 0) 13358c2ecf20Sopenharmony_ci dev_err(dev, "Using previously queried EDID\n"); 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci } 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci /* If that fails, use the default EDID we were handed */ 13408c2ecf20Sopenharmony_ci if (info->monspecs.modedb_len == 0) { 13418c2ecf20Sopenharmony_ci if (default_edid_size >= EDID_LENGTH) { 13428c2ecf20Sopenharmony_ci fb_edid_to_monspecs(default_edid, &info->monspecs); 13438c2ecf20Sopenharmony_ci if (info->monspecs.modedb_len > 0) { 13448c2ecf20Sopenharmony_ci memcpy(edid, default_edid, default_edid_size); 13458c2ecf20Sopenharmony_ci dlfb->edid = edid; 13468c2ecf20Sopenharmony_ci dlfb->edid_size = default_edid_size; 13478c2ecf20Sopenharmony_ci dev_err(dev, "Using default/backup EDID\n"); 13488c2ecf20Sopenharmony_ci } 13498c2ecf20Sopenharmony_ci } 13508c2ecf20Sopenharmony_ci } 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci /* If we've got modes, let's pick a best default mode */ 13538c2ecf20Sopenharmony_ci if (info->monspecs.modedb_len > 0) { 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci for (i = 0; i < info->monspecs.modedb_len; i++) { 13568c2ecf20Sopenharmony_ci mode = &info->monspecs.modedb[i]; 13578c2ecf20Sopenharmony_ci if (dlfb_is_valid_mode(mode, dlfb)) { 13588c2ecf20Sopenharmony_ci fb_add_videomode(mode, &info->modelist); 13598c2ecf20Sopenharmony_ci } else { 13608c2ecf20Sopenharmony_ci dev_dbg(dev, "Specified mode %dx%d too big\n", 13618c2ecf20Sopenharmony_ci mode->xres, mode->yres); 13628c2ecf20Sopenharmony_ci if (i == 0) 13638c2ecf20Sopenharmony_ci /* if we've removed top/best mode */ 13648c2ecf20Sopenharmony_ci info->monspecs.misc 13658c2ecf20Sopenharmony_ci &= ~FB_MISC_1ST_DETAIL; 13668c2ecf20Sopenharmony_ci } 13678c2ecf20Sopenharmony_ci } 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci default_vmode = fb_find_best_display(&info->monspecs, 13708c2ecf20Sopenharmony_ci &info->modelist); 13718c2ecf20Sopenharmony_ci } 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci /* If everything else has failed, fall back to safe default mode */ 13748c2ecf20Sopenharmony_ci if (default_vmode == NULL) { 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci struct fb_videomode fb_vmode = {0}; 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci /* 13798c2ecf20Sopenharmony_ci * Add the standard VESA modes to our modelist 13808c2ecf20Sopenharmony_ci * Since we don't have EDID, there may be modes that 13818c2ecf20Sopenharmony_ci * overspec monitor and/or are incorrect aspect ratio, etc. 13828c2ecf20Sopenharmony_ci * But at least the user has a chance to choose 13838c2ecf20Sopenharmony_ci */ 13848c2ecf20Sopenharmony_ci for (i = 0; i < VESA_MODEDB_SIZE; i++) { 13858c2ecf20Sopenharmony_ci mode = (struct fb_videomode *)&vesa_modes[i]; 13868c2ecf20Sopenharmony_ci if (dlfb_is_valid_mode(mode, dlfb)) 13878c2ecf20Sopenharmony_ci fb_add_videomode(mode, &info->modelist); 13888c2ecf20Sopenharmony_ci else 13898c2ecf20Sopenharmony_ci dev_dbg(dev, "VESA mode %dx%d too big\n", 13908c2ecf20Sopenharmony_ci mode->xres, mode->yres); 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci /* 13948c2ecf20Sopenharmony_ci * default to resolution safe for projectors 13958c2ecf20Sopenharmony_ci * (since they are most common case without EDID) 13968c2ecf20Sopenharmony_ci */ 13978c2ecf20Sopenharmony_ci fb_vmode.xres = 800; 13988c2ecf20Sopenharmony_ci fb_vmode.yres = 600; 13998c2ecf20Sopenharmony_ci fb_vmode.refresh = 60; 14008c2ecf20Sopenharmony_ci default_vmode = fb_find_nearest_mode(&fb_vmode, 14018c2ecf20Sopenharmony_ci &info->modelist); 14028c2ecf20Sopenharmony_ci } 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci /* If we have good mode and no active clients*/ 14058c2ecf20Sopenharmony_ci if ((default_vmode != NULL) && (dlfb->fb_count == 0)) { 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci fb_videomode_to_var(&info->var, default_vmode); 14088c2ecf20Sopenharmony_ci dlfb_var_color_format(&info->var); 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci /* 14118c2ecf20Sopenharmony_ci * with mode size info, we can now alloc our framebuffer. 14128c2ecf20Sopenharmony_ci */ 14138c2ecf20Sopenharmony_ci memcpy(&info->fix, &dlfb_fix, sizeof(dlfb_fix)); 14148c2ecf20Sopenharmony_ci } else 14158c2ecf20Sopenharmony_ci result = -EINVAL; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_cierror: 14188c2ecf20Sopenharmony_ci if (edid && (dlfb->edid != edid)) 14198c2ecf20Sopenharmony_ci kfree(edid); 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci if (info->dev) 14228c2ecf20Sopenharmony_ci mutex_unlock(&info->lock); 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci return result; 14258c2ecf20Sopenharmony_ci} 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_cistatic ssize_t metrics_bytes_rendered_show(struct device *fbdev, 14288c2ecf20Sopenharmony_ci struct device_attribute *a, char *buf) { 14298c2ecf20Sopenharmony_ci struct fb_info *fb_info = dev_get_drvdata(fbdev); 14308c2ecf20Sopenharmony_ci struct dlfb_data *dlfb = fb_info->par; 14318c2ecf20Sopenharmony_ci return sysfs_emit(buf, "%u\n", 14328c2ecf20Sopenharmony_ci atomic_read(&dlfb->bytes_rendered)); 14338c2ecf20Sopenharmony_ci} 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_cistatic ssize_t metrics_bytes_identical_show(struct device *fbdev, 14368c2ecf20Sopenharmony_ci struct device_attribute *a, char *buf) { 14378c2ecf20Sopenharmony_ci struct fb_info *fb_info = dev_get_drvdata(fbdev); 14388c2ecf20Sopenharmony_ci struct dlfb_data *dlfb = fb_info->par; 14398c2ecf20Sopenharmony_ci return sysfs_emit(buf, "%u\n", 14408c2ecf20Sopenharmony_ci atomic_read(&dlfb->bytes_identical)); 14418c2ecf20Sopenharmony_ci} 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_cistatic ssize_t metrics_bytes_sent_show(struct device *fbdev, 14448c2ecf20Sopenharmony_ci struct device_attribute *a, char *buf) { 14458c2ecf20Sopenharmony_ci struct fb_info *fb_info = dev_get_drvdata(fbdev); 14468c2ecf20Sopenharmony_ci struct dlfb_data *dlfb = fb_info->par; 14478c2ecf20Sopenharmony_ci return sysfs_emit(buf, "%u\n", 14488c2ecf20Sopenharmony_ci atomic_read(&dlfb->bytes_sent)); 14498c2ecf20Sopenharmony_ci} 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_cistatic ssize_t metrics_cpu_kcycles_used_show(struct device *fbdev, 14528c2ecf20Sopenharmony_ci struct device_attribute *a, char *buf) { 14538c2ecf20Sopenharmony_ci struct fb_info *fb_info = dev_get_drvdata(fbdev); 14548c2ecf20Sopenharmony_ci struct dlfb_data *dlfb = fb_info->par; 14558c2ecf20Sopenharmony_ci return sysfs_emit(buf, "%u\n", 14568c2ecf20Sopenharmony_ci atomic_read(&dlfb->cpu_kcycles_used)); 14578c2ecf20Sopenharmony_ci} 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_cistatic ssize_t edid_show( 14608c2ecf20Sopenharmony_ci struct file *filp, 14618c2ecf20Sopenharmony_ci struct kobject *kobj, struct bin_attribute *a, 14628c2ecf20Sopenharmony_ci char *buf, loff_t off, size_t count) { 14638c2ecf20Sopenharmony_ci struct device *fbdev = kobj_to_dev(kobj); 14648c2ecf20Sopenharmony_ci struct fb_info *fb_info = dev_get_drvdata(fbdev); 14658c2ecf20Sopenharmony_ci struct dlfb_data *dlfb = fb_info->par; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci if (dlfb->edid == NULL) 14688c2ecf20Sopenharmony_ci return 0; 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci if ((off >= dlfb->edid_size) || (count > dlfb->edid_size)) 14718c2ecf20Sopenharmony_ci return 0; 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci if (off + count > dlfb->edid_size) 14748c2ecf20Sopenharmony_ci count = dlfb->edid_size - off; 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci memcpy(buf, dlfb->edid, count); 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci return count; 14798c2ecf20Sopenharmony_ci} 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_cistatic ssize_t edid_store( 14828c2ecf20Sopenharmony_ci struct file *filp, 14838c2ecf20Sopenharmony_ci struct kobject *kobj, struct bin_attribute *a, 14848c2ecf20Sopenharmony_ci char *src, loff_t src_off, size_t src_size) { 14858c2ecf20Sopenharmony_ci struct device *fbdev = kobj_to_dev(kobj); 14868c2ecf20Sopenharmony_ci struct fb_info *fb_info = dev_get_drvdata(fbdev); 14878c2ecf20Sopenharmony_ci struct dlfb_data *dlfb = fb_info->par; 14888c2ecf20Sopenharmony_ci int ret; 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci /* We only support write of entire EDID at once, no offset*/ 14918c2ecf20Sopenharmony_ci if ((src_size != EDID_LENGTH) || (src_off != 0)) 14928c2ecf20Sopenharmony_ci return -EINVAL; 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci ret = dlfb_setup_modes(dlfb, fb_info, src, src_size); 14958c2ecf20Sopenharmony_ci if (ret) 14968c2ecf20Sopenharmony_ci return ret; 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci if (!dlfb->edid || memcmp(src, dlfb->edid, src_size)) 14998c2ecf20Sopenharmony_ci return -EINVAL; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci ret = dlfb_ops_set_par(fb_info); 15028c2ecf20Sopenharmony_ci if (ret) 15038c2ecf20Sopenharmony_ci return ret; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci return src_size; 15068c2ecf20Sopenharmony_ci} 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_cistatic ssize_t metrics_reset_store(struct device *fbdev, 15098c2ecf20Sopenharmony_ci struct device_attribute *attr, 15108c2ecf20Sopenharmony_ci const char *buf, size_t count) 15118c2ecf20Sopenharmony_ci{ 15128c2ecf20Sopenharmony_ci struct fb_info *fb_info = dev_get_drvdata(fbdev); 15138c2ecf20Sopenharmony_ci struct dlfb_data *dlfb = fb_info->par; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci atomic_set(&dlfb->bytes_rendered, 0); 15168c2ecf20Sopenharmony_ci atomic_set(&dlfb->bytes_identical, 0); 15178c2ecf20Sopenharmony_ci atomic_set(&dlfb->bytes_sent, 0); 15188c2ecf20Sopenharmony_ci atomic_set(&dlfb->cpu_kcycles_used, 0); 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci return count; 15218c2ecf20Sopenharmony_ci} 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_cistatic const struct bin_attribute edid_attr = { 15248c2ecf20Sopenharmony_ci .attr.name = "edid", 15258c2ecf20Sopenharmony_ci .attr.mode = 0666, 15268c2ecf20Sopenharmony_ci .size = EDID_LENGTH, 15278c2ecf20Sopenharmony_ci .read = edid_show, 15288c2ecf20Sopenharmony_ci .write = edid_store 15298c2ecf20Sopenharmony_ci}; 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_cistatic const struct device_attribute fb_device_attrs[] = { 15328c2ecf20Sopenharmony_ci __ATTR_RO(metrics_bytes_rendered), 15338c2ecf20Sopenharmony_ci __ATTR_RO(metrics_bytes_identical), 15348c2ecf20Sopenharmony_ci __ATTR_RO(metrics_bytes_sent), 15358c2ecf20Sopenharmony_ci __ATTR_RO(metrics_cpu_kcycles_used), 15368c2ecf20Sopenharmony_ci __ATTR(metrics_reset, S_IWUSR, NULL, metrics_reset_store), 15378c2ecf20Sopenharmony_ci}; 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci/* 15408c2ecf20Sopenharmony_ci * This is necessary before we can communicate with the display controller. 15418c2ecf20Sopenharmony_ci */ 15428c2ecf20Sopenharmony_cistatic int dlfb_select_std_channel(struct dlfb_data *dlfb) 15438c2ecf20Sopenharmony_ci{ 15448c2ecf20Sopenharmony_ci int ret; 15458c2ecf20Sopenharmony_ci void *buf; 15468c2ecf20Sopenharmony_ci static const u8 set_def_chn[] = { 15478c2ecf20Sopenharmony_ci 0x57, 0xCD, 0xDC, 0xA7, 15488c2ecf20Sopenharmony_ci 0x1C, 0x88, 0x5E, 0x15, 15498c2ecf20Sopenharmony_ci 0x60, 0xFE, 0xC6, 0x97, 15508c2ecf20Sopenharmony_ci 0x16, 0x3D, 0x47, 0xF2 }; 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci buf = kmemdup(set_def_chn, sizeof(set_def_chn), GFP_KERNEL); 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci if (!buf) 15558c2ecf20Sopenharmony_ci return -ENOMEM; 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci ret = usb_control_msg(dlfb->udev, usb_sndctrlpipe(dlfb->udev, 0), 15588c2ecf20Sopenharmony_ci NR_USB_REQUEST_CHANNEL, 15598c2ecf20Sopenharmony_ci (USB_DIR_OUT | USB_TYPE_VENDOR), 0, 0, 15608c2ecf20Sopenharmony_ci buf, sizeof(set_def_chn), USB_CTRL_SET_TIMEOUT); 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci kfree(buf); 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci return ret; 15658c2ecf20Sopenharmony_ci} 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_cistatic int dlfb_parse_vendor_descriptor(struct dlfb_data *dlfb, 15688c2ecf20Sopenharmony_ci struct usb_interface *intf) 15698c2ecf20Sopenharmony_ci{ 15708c2ecf20Sopenharmony_ci char *desc; 15718c2ecf20Sopenharmony_ci char *buf; 15728c2ecf20Sopenharmony_ci char *desc_end; 15738c2ecf20Sopenharmony_ci int total_len; 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci buf = kzalloc(MAX_VENDOR_DESCRIPTOR_SIZE, GFP_KERNEL); 15768c2ecf20Sopenharmony_ci if (!buf) 15778c2ecf20Sopenharmony_ci return false; 15788c2ecf20Sopenharmony_ci desc = buf; 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci total_len = usb_get_descriptor(interface_to_usbdev(intf), 15818c2ecf20Sopenharmony_ci 0x5f, /* vendor specific */ 15828c2ecf20Sopenharmony_ci 0, desc, MAX_VENDOR_DESCRIPTOR_SIZE); 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci /* if not found, look in configuration descriptor */ 15858c2ecf20Sopenharmony_ci if (total_len < 0) { 15868c2ecf20Sopenharmony_ci if (0 == usb_get_extra_descriptor(intf->cur_altsetting, 15878c2ecf20Sopenharmony_ci 0x5f, &desc)) 15888c2ecf20Sopenharmony_ci total_len = (int) desc[0]; 15898c2ecf20Sopenharmony_ci } 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci if (total_len > 5) { 15928c2ecf20Sopenharmony_ci dev_info(&intf->dev, 15938c2ecf20Sopenharmony_ci "vendor descriptor length: %d data: %11ph\n", 15948c2ecf20Sopenharmony_ci total_len, desc); 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci if ((desc[0] != total_len) || /* descriptor length */ 15978c2ecf20Sopenharmony_ci (desc[1] != 0x5f) || /* vendor descriptor type */ 15988c2ecf20Sopenharmony_ci (desc[2] != 0x01) || /* version (2 bytes) */ 15998c2ecf20Sopenharmony_ci (desc[3] != 0x00) || 16008c2ecf20Sopenharmony_ci (desc[4] != total_len - 2)) /* length after type */ 16018c2ecf20Sopenharmony_ci goto unrecognized; 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci desc_end = desc + total_len; 16048c2ecf20Sopenharmony_ci desc += 5; /* the fixed header we've already parsed */ 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci while (desc < desc_end) { 16078c2ecf20Sopenharmony_ci u8 length; 16088c2ecf20Sopenharmony_ci u16 key; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci key = *desc++; 16118c2ecf20Sopenharmony_ci key |= (u16)*desc++ << 8; 16128c2ecf20Sopenharmony_ci length = *desc++; 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci switch (key) { 16158c2ecf20Sopenharmony_ci case 0x0200: { /* max_area */ 16168c2ecf20Sopenharmony_ci u32 max_area = *desc++; 16178c2ecf20Sopenharmony_ci max_area |= (u32)*desc++ << 8; 16188c2ecf20Sopenharmony_ci max_area |= (u32)*desc++ << 16; 16198c2ecf20Sopenharmony_ci max_area |= (u32)*desc++ << 24; 16208c2ecf20Sopenharmony_ci dev_warn(&intf->dev, 16218c2ecf20Sopenharmony_ci "DL chip limited to %d pixel modes\n", 16228c2ecf20Sopenharmony_ci max_area); 16238c2ecf20Sopenharmony_ci dlfb->sku_pixel_limit = max_area; 16248c2ecf20Sopenharmony_ci break; 16258c2ecf20Sopenharmony_ci } 16268c2ecf20Sopenharmony_ci default: 16278c2ecf20Sopenharmony_ci break; 16288c2ecf20Sopenharmony_ci } 16298c2ecf20Sopenharmony_ci desc += length; 16308c2ecf20Sopenharmony_ci } 16318c2ecf20Sopenharmony_ci } else { 16328c2ecf20Sopenharmony_ci dev_info(&intf->dev, "vendor descriptor not available (%d)\n", 16338c2ecf20Sopenharmony_ci total_len); 16348c2ecf20Sopenharmony_ci } 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci goto success; 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ciunrecognized: 16398c2ecf20Sopenharmony_ci /* allow udlfb to load for now even if firmware unrecognized */ 16408c2ecf20Sopenharmony_ci dev_err(&intf->dev, "Unrecognized vendor firmware descriptor\n"); 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_cisuccess: 16438c2ecf20Sopenharmony_ci kfree(buf); 16448c2ecf20Sopenharmony_ci return true; 16458c2ecf20Sopenharmony_ci} 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_cistatic int dlfb_usb_probe(struct usb_interface *intf, 16488c2ecf20Sopenharmony_ci const struct usb_device_id *id) 16498c2ecf20Sopenharmony_ci{ 16508c2ecf20Sopenharmony_ci int i; 16518c2ecf20Sopenharmony_ci const struct device_attribute *attr; 16528c2ecf20Sopenharmony_ci struct dlfb_data *dlfb; 16538c2ecf20Sopenharmony_ci struct fb_info *info; 16548c2ecf20Sopenharmony_ci int retval; 16558c2ecf20Sopenharmony_ci struct usb_device *usbdev = interface_to_usbdev(intf); 16568c2ecf20Sopenharmony_ci static u8 out_ep[] = {OUT_EP_NUM + USB_DIR_OUT, 0}; 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci /* usb initialization */ 16598c2ecf20Sopenharmony_ci dlfb = kzalloc(sizeof(*dlfb), GFP_KERNEL); 16608c2ecf20Sopenharmony_ci if (!dlfb) { 16618c2ecf20Sopenharmony_ci dev_err(&intf->dev, "%s: failed to allocate dlfb\n", __func__); 16628c2ecf20Sopenharmony_ci return -ENOMEM; 16638c2ecf20Sopenharmony_ci } 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dlfb->deferred_free); 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci dlfb->udev = usb_get_dev(usbdev); 16688c2ecf20Sopenharmony_ci usb_set_intfdata(intf, dlfb); 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci if (!usb_check_bulk_endpoints(intf, out_ep)) { 16718c2ecf20Sopenharmony_ci dev_err(&intf->dev, "Invalid DisplayLink device!\n"); 16728c2ecf20Sopenharmony_ci retval = -EINVAL; 16738c2ecf20Sopenharmony_ci goto error; 16748c2ecf20Sopenharmony_ci } 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci dev_dbg(&intf->dev, "console enable=%d\n", console); 16778c2ecf20Sopenharmony_ci dev_dbg(&intf->dev, "fb_defio enable=%d\n", fb_defio); 16788c2ecf20Sopenharmony_ci dev_dbg(&intf->dev, "shadow enable=%d\n", shadow); 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci dlfb->sku_pixel_limit = 2048 * 1152; /* default to maximum */ 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci if (!dlfb_parse_vendor_descriptor(dlfb, intf)) { 16838c2ecf20Sopenharmony_ci dev_err(&intf->dev, 16848c2ecf20Sopenharmony_ci "firmware not recognized, incompatible device?\n"); 16858c2ecf20Sopenharmony_ci retval = -ENODEV; 16868c2ecf20Sopenharmony_ci goto error; 16878c2ecf20Sopenharmony_ci } 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci if (pixel_limit) { 16908c2ecf20Sopenharmony_ci dev_warn(&intf->dev, 16918c2ecf20Sopenharmony_ci "DL chip limit of %d overridden to %d\n", 16928c2ecf20Sopenharmony_ci dlfb->sku_pixel_limit, pixel_limit); 16938c2ecf20Sopenharmony_ci dlfb->sku_pixel_limit = pixel_limit; 16948c2ecf20Sopenharmony_ci } 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci /* allocates framebuffer driver structure, not framebuffer memory */ 16988c2ecf20Sopenharmony_ci info = framebuffer_alloc(0, &dlfb->udev->dev); 16998c2ecf20Sopenharmony_ci if (!info) { 17008c2ecf20Sopenharmony_ci retval = -ENOMEM; 17018c2ecf20Sopenharmony_ci goto error; 17028c2ecf20Sopenharmony_ci } 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci dlfb->info = info; 17058c2ecf20Sopenharmony_ci info->par = dlfb; 17068c2ecf20Sopenharmony_ci info->pseudo_palette = dlfb->pseudo_palette; 17078c2ecf20Sopenharmony_ci dlfb->ops = dlfb_ops; 17088c2ecf20Sopenharmony_ci info->fbops = &dlfb->ops; 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci mutex_init(&dlfb->render_mutex); 17118c2ecf20Sopenharmony_ci dlfb_init_damage(dlfb); 17128c2ecf20Sopenharmony_ci spin_lock_init(&dlfb->damage_lock); 17138c2ecf20Sopenharmony_ci INIT_WORK(&dlfb->damage_work, dlfb_damage_work); 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&info->modelist); 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci if (!dlfb_alloc_urb_list(dlfb, WRITES_IN_FLIGHT, MAX_TRANSFER)) { 17188c2ecf20Sopenharmony_ci retval = -ENOMEM; 17198c2ecf20Sopenharmony_ci dev_err(&intf->dev, "unable to allocate urb list\n"); 17208c2ecf20Sopenharmony_ci goto error; 17218c2ecf20Sopenharmony_ci } 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci /* We don't register a new USB class. Our client interface is dlfbev */ 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci retval = fb_alloc_cmap(&info->cmap, 256, 0); 17268c2ecf20Sopenharmony_ci if (retval < 0) { 17278c2ecf20Sopenharmony_ci dev_err(info->device, "cmap allocation failed: %d\n", retval); 17288c2ecf20Sopenharmony_ci goto error; 17298c2ecf20Sopenharmony_ci } 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci retval = dlfb_setup_modes(dlfb, info, NULL, 0); 17328c2ecf20Sopenharmony_ci if (retval != 0) { 17338c2ecf20Sopenharmony_ci dev_err(info->device, 17348c2ecf20Sopenharmony_ci "unable to find common mode for display and adapter\n"); 17358c2ecf20Sopenharmony_ci goto error; 17368c2ecf20Sopenharmony_ci } 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci /* ready to begin using device */ 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci atomic_set(&dlfb->usb_active, 1); 17418c2ecf20Sopenharmony_ci dlfb_select_std_channel(dlfb); 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci dlfb_ops_check_var(&info->var, info); 17448c2ecf20Sopenharmony_ci retval = dlfb_ops_set_par(info); 17458c2ecf20Sopenharmony_ci if (retval) 17468c2ecf20Sopenharmony_ci goto error; 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci retval = register_framebuffer(info); 17498c2ecf20Sopenharmony_ci if (retval < 0) { 17508c2ecf20Sopenharmony_ci dev_err(info->device, "unable to register framebuffer: %d\n", 17518c2ecf20Sopenharmony_ci retval); 17528c2ecf20Sopenharmony_ci goto error; 17538c2ecf20Sopenharmony_ci } 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) { 17568c2ecf20Sopenharmony_ci attr = &fb_device_attrs[i]; 17578c2ecf20Sopenharmony_ci retval = device_create_file(info->dev, attr); 17588c2ecf20Sopenharmony_ci if (retval) 17598c2ecf20Sopenharmony_ci dev_warn(info->device, 17608c2ecf20Sopenharmony_ci "failed to create '%s' attribute: %d\n", 17618c2ecf20Sopenharmony_ci attr->attr.name, retval); 17628c2ecf20Sopenharmony_ci } 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci retval = device_create_bin_file(info->dev, &edid_attr); 17658c2ecf20Sopenharmony_ci if (retval) 17668c2ecf20Sopenharmony_ci dev_warn(info->device, "failed to create '%s' attribute: %d\n", 17678c2ecf20Sopenharmony_ci edid_attr.attr.name, retval); 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci dev_info(info->device, 17708c2ecf20Sopenharmony_ci "%s is DisplayLink USB device (%dx%d, %dK framebuffer memory)\n", 17718c2ecf20Sopenharmony_ci dev_name(info->dev), info->var.xres, info->var.yres, 17728c2ecf20Sopenharmony_ci ((dlfb->backing_buffer) ? 17738c2ecf20Sopenharmony_ci info->fix.smem_len * 2 : info->fix.smem_len) >> 10); 17748c2ecf20Sopenharmony_ci return 0; 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_cierror: 17778c2ecf20Sopenharmony_ci if (dlfb->info) { 17788c2ecf20Sopenharmony_ci dlfb_ops_destroy(dlfb->info); 17798c2ecf20Sopenharmony_ci } else { 17808c2ecf20Sopenharmony_ci usb_put_dev(dlfb->udev); 17818c2ecf20Sopenharmony_ci kfree(dlfb); 17828c2ecf20Sopenharmony_ci } 17838c2ecf20Sopenharmony_ci return retval; 17848c2ecf20Sopenharmony_ci} 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_cistatic void dlfb_usb_disconnect(struct usb_interface *intf) 17878c2ecf20Sopenharmony_ci{ 17888c2ecf20Sopenharmony_ci struct dlfb_data *dlfb; 17898c2ecf20Sopenharmony_ci struct fb_info *info; 17908c2ecf20Sopenharmony_ci int i; 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci dlfb = usb_get_intfdata(intf); 17938c2ecf20Sopenharmony_ci info = dlfb->info; 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci dev_dbg(&intf->dev, "USB disconnect starting\n"); 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci /* we virtualize until all fb clients release. Then we free */ 17988c2ecf20Sopenharmony_ci dlfb->virtualized = true; 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci /* When non-active we'll update virtual framebuffer, but no new urbs */ 18018c2ecf20Sopenharmony_ci atomic_set(&dlfb->usb_active, 0); 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci /* this function will wait for all in-flight urbs to complete */ 18048c2ecf20Sopenharmony_ci dlfb_free_urb_list(dlfb); 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci /* remove udlfb's sysfs interfaces */ 18078c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) 18088c2ecf20Sopenharmony_ci device_remove_file(info->dev, &fb_device_attrs[i]); 18098c2ecf20Sopenharmony_ci device_remove_bin_file(info->dev, &edid_attr); 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_ci unregister_framebuffer(info); 18128c2ecf20Sopenharmony_ci} 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_cistatic struct usb_driver dlfb_driver = { 18158c2ecf20Sopenharmony_ci .name = "udlfb", 18168c2ecf20Sopenharmony_ci .probe = dlfb_usb_probe, 18178c2ecf20Sopenharmony_ci .disconnect = dlfb_usb_disconnect, 18188c2ecf20Sopenharmony_ci .id_table = id_table, 18198c2ecf20Sopenharmony_ci}; 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_cimodule_usb_driver(dlfb_driver); 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_cistatic void dlfb_urb_completion(struct urb *urb) 18248c2ecf20Sopenharmony_ci{ 18258c2ecf20Sopenharmony_ci struct urb_node *unode = urb->context; 18268c2ecf20Sopenharmony_ci struct dlfb_data *dlfb = unode->dlfb; 18278c2ecf20Sopenharmony_ci unsigned long flags; 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci switch (urb->status) { 18308c2ecf20Sopenharmony_ci case 0: 18318c2ecf20Sopenharmony_ci /* success */ 18328c2ecf20Sopenharmony_ci break; 18338c2ecf20Sopenharmony_ci case -ECONNRESET: 18348c2ecf20Sopenharmony_ci case -ENOENT: 18358c2ecf20Sopenharmony_ci case -ESHUTDOWN: 18368c2ecf20Sopenharmony_ci /* sync/async unlink faults aren't errors */ 18378c2ecf20Sopenharmony_ci break; 18388c2ecf20Sopenharmony_ci default: 18398c2ecf20Sopenharmony_ci dev_err(&dlfb->udev->dev, 18408c2ecf20Sopenharmony_ci "%s - nonzero write bulk status received: %d\n", 18418c2ecf20Sopenharmony_ci __func__, urb->status); 18428c2ecf20Sopenharmony_ci atomic_set(&dlfb->lost_pixels, 1); 18438c2ecf20Sopenharmony_ci break; 18448c2ecf20Sopenharmony_ci } 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci urb->transfer_buffer_length = dlfb->urbs.size; /* reset to actual */ 18478c2ecf20Sopenharmony_ci 18488c2ecf20Sopenharmony_ci spin_lock_irqsave(&dlfb->urbs.lock, flags); 18498c2ecf20Sopenharmony_ci list_add_tail(&unode->entry, &dlfb->urbs.list); 18508c2ecf20Sopenharmony_ci dlfb->urbs.available++; 18518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dlfb->urbs.lock, flags); 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci up(&dlfb->urbs.limit_sem); 18548c2ecf20Sopenharmony_ci} 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_cistatic void dlfb_free_urb_list(struct dlfb_data *dlfb) 18578c2ecf20Sopenharmony_ci{ 18588c2ecf20Sopenharmony_ci int count = dlfb->urbs.count; 18598c2ecf20Sopenharmony_ci struct list_head *node; 18608c2ecf20Sopenharmony_ci struct urb_node *unode; 18618c2ecf20Sopenharmony_ci struct urb *urb; 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci /* keep waiting and freeing, until we've got 'em all */ 18648c2ecf20Sopenharmony_ci while (count--) { 18658c2ecf20Sopenharmony_ci down(&dlfb->urbs.limit_sem); 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci spin_lock_irq(&dlfb->urbs.lock); 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci node = dlfb->urbs.list.next; /* have reserved one with sem */ 18708c2ecf20Sopenharmony_ci list_del_init(node); 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci spin_unlock_irq(&dlfb->urbs.lock); 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci unode = list_entry(node, struct urb_node, entry); 18758c2ecf20Sopenharmony_ci urb = unode->urb; 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci /* Free each separately allocated piece */ 18788c2ecf20Sopenharmony_ci usb_free_coherent(urb->dev, dlfb->urbs.size, 18798c2ecf20Sopenharmony_ci urb->transfer_buffer, urb->transfer_dma); 18808c2ecf20Sopenharmony_ci usb_free_urb(urb); 18818c2ecf20Sopenharmony_ci kfree(node); 18828c2ecf20Sopenharmony_ci } 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci dlfb->urbs.count = 0; 18858c2ecf20Sopenharmony_ci} 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_cistatic int dlfb_alloc_urb_list(struct dlfb_data *dlfb, int count, size_t size) 18888c2ecf20Sopenharmony_ci{ 18898c2ecf20Sopenharmony_ci struct urb *urb; 18908c2ecf20Sopenharmony_ci struct urb_node *unode; 18918c2ecf20Sopenharmony_ci char *buf; 18928c2ecf20Sopenharmony_ci size_t wanted_size = count * size; 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci spin_lock_init(&dlfb->urbs.lock); 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ciretry: 18978c2ecf20Sopenharmony_ci dlfb->urbs.size = size; 18988c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dlfb->urbs.list); 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci sema_init(&dlfb->urbs.limit_sem, 0); 19018c2ecf20Sopenharmony_ci dlfb->urbs.count = 0; 19028c2ecf20Sopenharmony_ci dlfb->urbs.available = 0; 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci while (dlfb->urbs.count * size < wanted_size) { 19058c2ecf20Sopenharmony_ci unode = kzalloc(sizeof(*unode), GFP_KERNEL); 19068c2ecf20Sopenharmony_ci if (!unode) 19078c2ecf20Sopenharmony_ci break; 19088c2ecf20Sopenharmony_ci unode->dlfb = dlfb; 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci urb = usb_alloc_urb(0, GFP_KERNEL); 19118c2ecf20Sopenharmony_ci if (!urb) { 19128c2ecf20Sopenharmony_ci kfree(unode); 19138c2ecf20Sopenharmony_ci break; 19148c2ecf20Sopenharmony_ci } 19158c2ecf20Sopenharmony_ci unode->urb = urb; 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci buf = usb_alloc_coherent(dlfb->udev, size, GFP_KERNEL, 19188c2ecf20Sopenharmony_ci &urb->transfer_dma); 19198c2ecf20Sopenharmony_ci if (!buf) { 19208c2ecf20Sopenharmony_ci kfree(unode); 19218c2ecf20Sopenharmony_ci usb_free_urb(urb); 19228c2ecf20Sopenharmony_ci if (size > PAGE_SIZE) { 19238c2ecf20Sopenharmony_ci size /= 2; 19248c2ecf20Sopenharmony_ci dlfb_free_urb_list(dlfb); 19258c2ecf20Sopenharmony_ci goto retry; 19268c2ecf20Sopenharmony_ci } 19278c2ecf20Sopenharmony_ci break; 19288c2ecf20Sopenharmony_ci } 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci /* urb->transfer_buffer_length set to actual before submit */ 19318c2ecf20Sopenharmony_ci usb_fill_bulk_urb(urb, dlfb->udev, 19328c2ecf20Sopenharmony_ci usb_sndbulkpipe(dlfb->udev, OUT_EP_NUM), 19338c2ecf20Sopenharmony_ci buf, size, dlfb_urb_completion, unode); 19348c2ecf20Sopenharmony_ci urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci list_add_tail(&unode->entry, &dlfb->urbs.list); 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci up(&dlfb->urbs.limit_sem); 19398c2ecf20Sopenharmony_ci dlfb->urbs.count++; 19408c2ecf20Sopenharmony_ci dlfb->urbs.available++; 19418c2ecf20Sopenharmony_ci } 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci return dlfb->urbs.count; 19448c2ecf20Sopenharmony_ci} 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_cistatic struct urb *dlfb_get_urb(struct dlfb_data *dlfb) 19478c2ecf20Sopenharmony_ci{ 19488c2ecf20Sopenharmony_ci int ret; 19498c2ecf20Sopenharmony_ci struct list_head *entry; 19508c2ecf20Sopenharmony_ci struct urb_node *unode; 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci /* Wait for an in-flight buffer to complete and get re-queued */ 19538c2ecf20Sopenharmony_ci ret = down_timeout(&dlfb->urbs.limit_sem, GET_URB_TIMEOUT); 19548c2ecf20Sopenharmony_ci if (ret) { 19558c2ecf20Sopenharmony_ci atomic_set(&dlfb->lost_pixels, 1); 19568c2ecf20Sopenharmony_ci dev_warn(&dlfb->udev->dev, 19578c2ecf20Sopenharmony_ci "wait for urb interrupted: %d available: %d\n", 19588c2ecf20Sopenharmony_ci ret, dlfb->urbs.available); 19598c2ecf20Sopenharmony_ci return NULL; 19608c2ecf20Sopenharmony_ci } 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_ci spin_lock_irq(&dlfb->urbs.lock); 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci BUG_ON(list_empty(&dlfb->urbs.list)); /* reserved one with limit_sem */ 19658c2ecf20Sopenharmony_ci entry = dlfb->urbs.list.next; 19668c2ecf20Sopenharmony_ci list_del_init(entry); 19678c2ecf20Sopenharmony_ci dlfb->urbs.available--; 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci spin_unlock_irq(&dlfb->urbs.lock); 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci unode = list_entry(entry, struct urb_node, entry); 19728c2ecf20Sopenharmony_ci return unode->urb; 19738c2ecf20Sopenharmony_ci} 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_cistatic int dlfb_submit_urb(struct dlfb_data *dlfb, struct urb *urb, size_t len) 19768c2ecf20Sopenharmony_ci{ 19778c2ecf20Sopenharmony_ci int ret; 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci BUG_ON(len > dlfb->urbs.size); 19808c2ecf20Sopenharmony_ci 19818c2ecf20Sopenharmony_ci urb->transfer_buffer_length = len; /* set to actual payload len */ 19828c2ecf20Sopenharmony_ci ret = usb_submit_urb(urb, GFP_KERNEL); 19838c2ecf20Sopenharmony_ci if (ret) { 19848c2ecf20Sopenharmony_ci dlfb_urb_completion(urb); /* because no one else will */ 19858c2ecf20Sopenharmony_ci atomic_set(&dlfb->lost_pixels, 1); 19868c2ecf20Sopenharmony_ci dev_err(&dlfb->udev->dev, "submit urb error: %d\n", ret); 19878c2ecf20Sopenharmony_ci } 19888c2ecf20Sopenharmony_ci return ret; 19898c2ecf20Sopenharmony_ci} 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_cimodule_param(console, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); 19928c2ecf20Sopenharmony_ciMODULE_PARM_DESC(console, "Allow fbcon to open framebuffer"); 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_cimodule_param(fb_defio, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); 19958c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fb_defio, "Page fault detection of mmap writes"); 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_cimodule_param(shadow, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); 19988c2ecf20Sopenharmony_ciMODULE_PARM_DESC(shadow, "Shadow vid mem. Disable to save mem but lose perf"); 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_cimodule_param(pixel_limit, int, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); 20018c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pixel_limit, "Force limit on max mode (in x*y pixels)"); 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ciMODULE_AUTHOR("Roberto De Ioris <roberto@unbit.it>, " 20048c2ecf20Sopenharmony_ci "Jaya Kumar <jayakumar.lkml@gmail.com>, " 20058c2ecf20Sopenharmony_ci "Bernie Thompson <bernie@plugable.com>"); 20068c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DisplayLink kernel framebuffer driver"); 20078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 20088c2ecf20Sopenharmony_ci 2009