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