blob: 8f6223c8303a6f9a390fa6158e6268eac738de9f [file] [log] [blame]
Bernie Thompson59277b62009-11-24 15:52:21 -08001/*
2 * udlfb.c -- Framebuffer driver for DisplayLink USB controller
3 *
4 * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
5 * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
Bernie Thompson2469d5d2010-02-15 06:46:13 -08006 * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
Bernie Thompson59277b62009-11-24 15:52:21 -08007 *
8 * This file is subject to the terms and conditions of the GNU General Public
9 * License v2. See the file COPYING in the main directory of this archive for
10 * more details.
11 *
12 * Layout is based on skeletonfb by James Simmons and Geert Uytterhoeven,
13 * usb-skeleton by GregKH.
14 *
15 * Device-specific portions based on information from Displaylink, with work
16 * from Florian Echtler, Henrik Bjerregaard Pedersen, and others.
17 */
Roberto De Ioris88e58b12009-06-03 14:03:06 -070018
19#include <linux/module.h>
20#include <linux/kernel.h>
21#include <linux/init.h>
22#include <linux/usb.h>
23#include <linux/uaccess.h>
24#include <linux/mm.h>
25#include <linux/fb.h>
Amit Kucheriafb299002009-07-27 12:01:03 +030026#include <linux/vmalloc.h>
Roberto De Ioris88e58b12009-06-03 14:03:06 -070027
28#include "udlfb.h"
29
Bernie Thompson59277b62009-11-24 15:52:21 -080030static struct fb_fix_screeninfo dlfb_fix = {
Bernie Thompson2469d5d2010-02-15 06:46:13 -080031 .id = "udlfb",
Bernie Thompson1d31a9e2010-02-15 06:45:43 -080032 .type = FB_TYPE_PACKED_PIXELS,
33 .visual = FB_VISUAL_TRUECOLOR,
34 .xpanstep = 0,
35 .ypanstep = 0,
36 .ywrapstep = 0,
37 .accel = FB_ACCEL_NONE,
Bernie Thompson59277b62009-11-24 15:52:21 -080038};
Roberto De Ioris88e58b12009-06-03 14:03:06 -070039
Bernie Thompson2469d5d2010-02-15 06:46:13 -080040static const u32 udlfb_info_flags = FBINFO_DEFAULT | FBINFO_READS_FAST |
41#ifdef FBINFO_VIRTFB
42 FBINFO_VIRTFB |
43#endif
44 FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_FILLRECT |
45 FBINFO_HWACCEL_COPYAREA | FBINFO_MISC_ALWAYS_SETPAR;
46
Bernie Thompsoncc403dc2010-02-15 06:45:49 -080047/*
48 * There are many DisplayLink-based products, all with unique PIDs. We are able
49 * to support all volume ones (circa 2009) with a single driver, so we match
50 * globally on VID. TODO: Probe() needs to detect when we might be running
51 * "future" chips, and bail on those, so a compatible driver can match.
52 */
53static struct usb_device_id id_table[] = {
54 {.idVendor = 0x17e9, .match_flags = USB_DEVICE_ID_MATCH_VENDOR,},
55 {},
56};
57MODULE_DEVICE_TABLE(usb, id_table);
Bernie Thompson59277b62009-11-24 15:52:21 -080058
Bernie Thompsondd8015f2010-02-15 06:46:35 -080059#ifndef CONFIG_FB_DEFERRED_IO
60#warning message "kernel FB_DEFFERRED_IO option to support generic fbdev apps"
61#endif
62
63#ifndef CONFIG_FB_SYS_IMAGEBLIT
64#ifndef CONFIG_FB_SYS_IMAGEBLIT_MODULE
65#warning message "FB_SYS_* in kernel or module option to support fb console"
66#endif
67#endif
68
69#ifndef CONFIG_FB_MODE_HELPERS
70#warning message "kernel FB_MODE_HELPERS required. Expect build break"
71#endif
72
Bernie Thompson4a4854d2010-02-15 06:45:55 -080073/* dlfb keeps a list of urbs for efficient bulk transfers */
74static void dlfb_urb_completion(struct urb *urb);
75static struct urb *dlfb_get_urb(struct dlfb_data *dev);
76static int dlfb_submit_urb(struct dlfb_data *dev, struct urb * urb, size_t len);
77static int dlfb_alloc_urb_list(struct dlfb_data *dev, int count, size_t size);
78static void dlfb_free_urb_list(struct dlfb_data *dev);
79
Bernie Thompson3e8f3d62010-02-15 06:46:26 -080080/* other symbols with dependents */
81#ifdef CONFIG_FB_DEFERRED_IO
82static struct fb_deferred_io dlfb_defio;
83#endif
84
Bernie Thompson59277b62009-11-24 15:52:21 -080085/*
Bernie Thompsonbd808162010-02-15 06:46:48 -080086 * All DisplayLink bulk operations start with 0xAF, followed by specific code
87 * All operations are written to buffers which then later get sent to device
Bernie Thompson59277b62009-11-24 15:52:21 -080088 */
Bernie Thompson45742032010-02-15 06:46:04 -080089static char *dlfb_set_register(char *buf, u8 reg, u8 val)
Roberto De Ioris88e58b12009-06-03 14:03:06 -070090{
Bernie Thompson1d31a9e2010-02-15 06:45:43 -080091 *buf++ = 0xAF;
92 *buf++ = 0x20;
93 *buf++ = reg;
94 *buf++ = val;
95 return buf;
Roberto De Ioris88e58b12009-06-03 14:03:06 -070096}
97
Bernie Thompson45742032010-02-15 06:46:04 -080098static char *dlfb_vidreg_lock(char *buf)
Roberto De Ioris88e58b12009-06-03 14:03:06 -070099{
Bernie Thompson45742032010-02-15 06:46:04 -0800100 return dlfb_set_register(buf, 0xFF, 0x00);
Bernie Thompson59277b62009-11-24 15:52:21 -0800101}
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700102
Bernie Thompson45742032010-02-15 06:46:04 -0800103static char *dlfb_vidreg_unlock(char *buf)
Bernie Thompson59277b62009-11-24 15:52:21 -0800104{
Bernie Thompson45742032010-02-15 06:46:04 -0800105 return dlfb_set_register(buf, 0xFF, 0xFF);
Bernie Thompson59277b62009-11-24 15:52:21 -0800106}
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700107
Bernie Thompson59277b62009-11-24 15:52:21 -0800108/*
Bernie Thompson530f43a2010-02-15 06:46:21 -0800109 * On/Off for driving the DisplayLink framebuffer to the display
Bernie Thompson59277b62009-11-24 15:52:21 -0800110 */
Bernie Thompson530f43a2010-02-15 06:46:21 -0800111static char *dlfb_enable_hvsync(char *buf, bool enable)
Bernie Thompson59277b62009-11-24 15:52:21 -0800112{
Bernie Thompson530f43a2010-02-15 06:46:21 -0800113 if (enable)
114 return dlfb_set_register(buf, 0x1F, 0x00);
115 else
116 return dlfb_set_register(buf, 0x1F, 0x01);
Bernie Thompson59277b62009-11-24 15:52:21 -0800117}
118
Bernie Thompson45742032010-02-15 06:46:04 -0800119static char *dlfb_set_color_depth(char *buf, u8 selection)
Bernie Thompson59277b62009-11-24 15:52:21 -0800120{
Bernie Thompson45742032010-02-15 06:46:04 -0800121 return dlfb_set_register(buf, 0x00, selection);
Bernie Thompson59277b62009-11-24 15:52:21 -0800122}
123
Bernie Thompson45742032010-02-15 06:46:04 -0800124static char *dlfb_set_base16bpp(char *wrptr, u32 base)
Bernie Thompson59277b62009-11-24 15:52:21 -0800125{
Bernie Thompson1d31a9e2010-02-15 06:45:43 -0800126 /* the base pointer is 16 bits wide, 0x20 is hi byte. */
Bernie Thompson45742032010-02-15 06:46:04 -0800127 wrptr = dlfb_set_register(wrptr, 0x20, base >> 16);
128 wrptr = dlfb_set_register(wrptr, 0x21, base >> 8);
129 return dlfb_set_register(wrptr, 0x22, base);
Bernie Thompson59277b62009-11-24 15:52:21 -0800130}
131
Bernie Thompsonbd808162010-02-15 06:46:48 -0800132/*
133 * DisplayLink HW has separate 16bpp and 8bpp framebuffers.
134 * In 24bpp modes, the low 323 RGB bits go in the 8bpp framebuffer
135 */
Bernie Thompson45742032010-02-15 06:46:04 -0800136static char *dlfb_set_base8bpp(char *wrptr, u32 base)
Bernie Thompson59277b62009-11-24 15:52:21 -0800137{
Bernie Thompson45742032010-02-15 06:46:04 -0800138 wrptr = dlfb_set_register(wrptr, 0x26, base >> 16);
139 wrptr = dlfb_set_register(wrptr, 0x27, base >> 8);
140 return dlfb_set_register(wrptr, 0x28, base);
Bernie Thompson59277b62009-11-24 15:52:21 -0800141}
142
Bernie Thompson45742032010-02-15 06:46:04 -0800143static char *dlfb_set_register_16(char *wrptr, u8 reg, u16 value)
Bernie Thompson59277b62009-11-24 15:52:21 -0800144{
Bernie Thompson45742032010-02-15 06:46:04 -0800145 wrptr = dlfb_set_register(wrptr, reg, value >> 8);
146 return dlfb_set_register(wrptr, reg+1, value);
Bernie Thompson59277b62009-11-24 15:52:21 -0800147}
148
149/*
150 * This is kind of weird because the controller takes some
151 * register values in a different byte order than other registers.
152 */
Bernie Thompson45742032010-02-15 06:46:04 -0800153static char *dlfb_set_register_16be(char *wrptr, u8 reg, u16 value)
Bernie Thompson59277b62009-11-24 15:52:21 -0800154{
Bernie Thompson45742032010-02-15 06:46:04 -0800155 wrptr = dlfb_set_register(wrptr, reg, value);
156 return dlfb_set_register(wrptr, reg+1, value >> 8);
Bernie Thompson59277b62009-11-24 15:52:21 -0800157}
158
159/*
160 * LFSR is linear feedback shift register. The reason we have this is
161 * because the display controller needs to minimize the clock depth of
162 * various counters used in the display path. So this code reverses the
163 * provided value into the lfsr16 value by counting backwards to get
164 * the value that needs to be set in the hardware comparator to get the
165 * same actual count. This makes sense once you read above a couple of
166 * times and think about it from a hardware perspective.
167 */
Bernie Thompsonbd808162010-02-15 06:46:48 -0800168static u16 dlfb_lfsr16(u16 actual_count)
Bernie Thompson59277b62009-11-24 15:52:21 -0800169{
170 u32 lv = 0xFFFF; /* This is the lfsr value that the hw starts with */
171
172 while (actual_count--) {
173 lv = ((lv << 1) |
174 (((lv >> 15) ^ (lv >> 4) ^ (lv >> 2) ^ (lv >> 1)) & 1))
175 & 0xFFFF;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700176 }
Bernie Thompson59277b62009-11-24 15:52:21 -0800177
178 return (u16) lv;
179}
180
181/*
182 * This does LFSR conversion on the value that is to be written.
183 * See LFSR explanation above for more detail.
184 */
Bernie Thompson45742032010-02-15 06:46:04 -0800185static char *dlfb_set_register_lfsr16(char *wrptr, u8 reg, u16 value)
Bernie Thompson59277b62009-11-24 15:52:21 -0800186{
Bernie Thompsonbd808162010-02-15 06:46:48 -0800187 return dlfb_set_register_16(wrptr, reg, dlfb_lfsr16(value));
Bernie Thompson59277b62009-11-24 15:52:21 -0800188}
189
190/*
191 * This takes a standard fbdev screeninfo struct and all of its monitor mode
192 * details and converts them into the DisplayLink equivalent register commands.
193 */
Bernie Thompson45742032010-02-15 06:46:04 -0800194static char *dlfb_set_vid_cmds(char *wrptr, struct fb_var_screeninfo *var)
Bernie Thompson59277b62009-11-24 15:52:21 -0800195{
196 u16 xds, yds;
197 u16 xde, yde;
198 u16 yec;
199
Bernie Thompson59277b62009-11-24 15:52:21 -0800200 /* x display start */
201 xds = var->left_margin + var->hsync_len;
Bernie Thompson45742032010-02-15 06:46:04 -0800202 wrptr = dlfb_set_register_lfsr16(wrptr, 0x01, xds);
Bernie Thompson59277b62009-11-24 15:52:21 -0800203 /* x display end */
204 xde = xds + var->xres;
Bernie Thompson45742032010-02-15 06:46:04 -0800205 wrptr = dlfb_set_register_lfsr16(wrptr, 0x03, xde);
Bernie Thompson59277b62009-11-24 15:52:21 -0800206
207 /* y display start */
208 yds = var->upper_margin + var->vsync_len;
Bernie Thompson45742032010-02-15 06:46:04 -0800209 wrptr = dlfb_set_register_lfsr16(wrptr, 0x05, yds);
Bernie Thompson59277b62009-11-24 15:52:21 -0800210 /* y display end */
211 yde = yds + var->yres;
Bernie Thompson45742032010-02-15 06:46:04 -0800212 wrptr = dlfb_set_register_lfsr16(wrptr, 0x07, yde);
Bernie Thompson59277b62009-11-24 15:52:21 -0800213
214 /* x end count is active + blanking - 1 */
Bernie Thompson45742032010-02-15 06:46:04 -0800215 wrptr = dlfb_set_register_lfsr16(wrptr, 0x09,
216 xde + var->right_margin - 1);
Bernie Thompson59277b62009-11-24 15:52:21 -0800217
218 /* libdlo hardcodes hsync start to 1 */
Bernie Thompson45742032010-02-15 06:46:04 -0800219 wrptr = dlfb_set_register_lfsr16(wrptr, 0x0B, 1);
Bernie Thompson59277b62009-11-24 15:52:21 -0800220
221 /* hsync end is width of sync pulse + 1 */
Bernie Thompson45742032010-02-15 06:46:04 -0800222 wrptr = dlfb_set_register_lfsr16(wrptr, 0x0D, var->hsync_len + 1);
Bernie Thompson59277b62009-11-24 15:52:21 -0800223
224 /* hpixels is active pixels */
Bernie Thompson45742032010-02-15 06:46:04 -0800225 wrptr = dlfb_set_register_16(wrptr, 0x0F, var->xres);
Bernie Thompson59277b62009-11-24 15:52:21 -0800226
227 /* yendcount is vertical active + vertical blanking */
228 yec = var->yres + var->upper_margin + var->lower_margin +
229 var->vsync_len;
Bernie Thompson45742032010-02-15 06:46:04 -0800230 wrptr = dlfb_set_register_lfsr16(wrptr, 0x11, yec);
Bernie Thompson59277b62009-11-24 15:52:21 -0800231
232 /* libdlo hardcodes vsync start to 0 */
Bernie Thompson45742032010-02-15 06:46:04 -0800233 wrptr = dlfb_set_register_lfsr16(wrptr, 0x13, 0);
Bernie Thompson59277b62009-11-24 15:52:21 -0800234
235 /* vsync end is width of vsync pulse */
Bernie Thompson45742032010-02-15 06:46:04 -0800236 wrptr = dlfb_set_register_lfsr16(wrptr, 0x15, var->vsync_len);
Bernie Thompson59277b62009-11-24 15:52:21 -0800237
238 /* vpixels is active pixels */
Bernie Thompson45742032010-02-15 06:46:04 -0800239 wrptr = dlfb_set_register_16(wrptr, 0x17, var->yres);
Bernie Thompson59277b62009-11-24 15:52:21 -0800240
241 /* convert picoseconds to 5kHz multiple for pclk5k = x * 1E12/5k */
Bernie Thompson45742032010-02-15 06:46:04 -0800242 wrptr = dlfb_set_register_16be(wrptr, 0x1B,
243 200*1000*1000/var->pixclock);
Bernie Thompson59277b62009-11-24 15:52:21 -0800244
245 return wrptr;
246}
247
248/*
249 * This takes a standard fbdev screeninfo struct that was fetched or prepared
250 * and then generates the appropriate command sequence that then drives the
251 * display controller.
252 */
253static int dlfb_set_video_mode(struct dlfb_data *dev,
254 struct fb_var_screeninfo *var)
255{
256 char *buf;
257 char *wrptr;
258 int retval = 0;
259 int writesize;
Bernie Thompson530f43a2010-02-15 06:46:21 -0800260 struct urb *urb;
Bernie Thompson59277b62009-11-24 15:52:21 -0800261
Bernie Thompson530f43a2010-02-15 06:46:21 -0800262 if (!atomic_read(&dev->usb_active))
263 return -EPERM;
264
265 urb = dlfb_get_urb(dev);
266 if (!urb)
267 return -ENOMEM;
268 buf = (char *) urb->transfer_buffer;
Bernie Thompson59277b62009-11-24 15:52:21 -0800269
270 /*
271 * This first section has to do with setting the base address on the
272 * controller * associated with the display. There are 2 base
273 * pointers, currently, we only * use the 16 bpp segment.
274 */
Bernie Thompson45742032010-02-15 06:46:04 -0800275 wrptr = dlfb_vidreg_lock(buf);
276 wrptr = dlfb_set_color_depth(wrptr, 0x00);
Bernie Thompson59277b62009-11-24 15:52:21 -0800277 /* set base for 16bpp segment to 0 */
Bernie Thompson45742032010-02-15 06:46:04 -0800278 wrptr = dlfb_set_base16bpp(wrptr, 0);
Bernie Thompson59277b62009-11-24 15:52:21 -0800279 /* set base for 8bpp segment to end of fb */
Bernie Thompson45742032010-02-15 06:46:04 -0800280 wrptr = dlfb_set_base8bpp(wrptr, dev->info->fix.smem_len);
Bernie Thompson59277b62009-11-24 15:52:21 -0800281
Bernie Thompson45742032010-02-15 06:46:04 -0800282 wrptr = dlfb_set_vid_cmds(wrptr, var);
Bernie Thompson530f43a2010-02-15 06:46:21 -0800283 wrptr = dlfb_enable_hvsync(wrptr, true);
Bernie Thompson45742032010-02-15 06:46:04 -0800284 wrptr = dlfb_vidreg_unlock(wrptr);
Bernie Thompson59277b62009-11-24 15:52:21 -0800285
286 writesize = wrptr - buf;
287
Bernie Thompson530f43a2010-02-15 06:46:21 -0800288 retval = dlfb_submit_urb(dev, urb, writesize);
Bernie Thompson59277b62009-11-24 15:52:21 -0800289
Bernie Thompson59277b62009-11-24 15:52:21 -0800290 return retval;
291}
292
Bernie Thompson45742032010-02-15 06:46:04 -0800293static int dlfb_ops_mmap(struct fb_info *info, struct vm_area_struct *vma)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700294{
295 unsigned long start = vma->vm_start;
296 unsigned long size = vma->vm_end - vma->vm_start;
297 unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
298 unsigned long page, pos;
Bernie Thompsonbd808162010-02-15 06:46:48 -0800299 struct dlfb_data *dev = info->par;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700300
Bernie Thompsonbd808162010-02-15 06:46:48 -0800301 dl_notice("MMAP: %lu %u\n", offset + size, info->fix.smem_len);
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700302
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700303 if (offset + size > info->fix.smem_len)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700304 return -EINVAL;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700305
306 pos = (unsigned long)info->fix.smem_start + offset;
307
308 while (size > 0) {
309 page = vmalloc_to_pfn((void *)pos);
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700310 if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700311 return -EAGAIN;
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700312
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700313 start += PAGE_SIZE;
314 pos += PAGE_SIZE;
315 if (size > PAGE_SIZE)
316 size -= PAGE_SIZE;
317 else
318 size = 0;
319 }
320
321 vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
322 return 0;
323
324}
325
Bernie Thompson530f43a2010-02-15 06:46:21 -0800326/*
327 * Trims identical data from front and back of line
328 * Sets new front buffer address and width
329 * And returns byte count of identical pixels
330 * Assumes CPU natural alignment (unsigned long)
331 * for back and front buffer ptrs and width
332 */
333static int dlfb_trim_hline(const u8 *bback, const u8 **bfront, int *width_bytes)
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700334{
Bernie Thompson530f43a2010-02-15 06:46:21 -0800335 int j, k;
336 const unsigned long *back = (const unsigned long *) bback;
337 const unsigned long *front = (const unsigned long *) *bfront;
338 const int width = *width_bytes / sizeof(unsigned long);
339 int identical = width;
340 int start = width;
341 int end = width;
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700342
Bernie Thompson530f43a2010-02-15 06:46:21 -0800343 prefetch((void *) front);
344 prefetch((void *) back);
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700345
Bernie Thompson530f43a2010-02-15 06:46:21 -0800346 for (j = 0; j < width; j++) {
347 if (back[j] != front[j]) {
348 start = j;
349 break;
350 }
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700351 }
352
Bernie Thompson530f43a2010-02-15 06:46:21 -0800353 for (k = width - 1; k > j; k--) {
354 if (back[k] != front[k]) {
355 end = k+1;
356 break;
357 }
358 }
359
360 identical = start + (width - end);
361 *bfront = (u8 *) &front[start];
362 *width_bytes = (end - start) * sizeof(unsigned long);
363
364 return identical * sizeof(unsigned long);
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700365}
366
367/*
Bernie Thompson530f43a2010-02-15 06:46:21 -0800368Render a command stream for an encoded horizontal line segment of pixels.
369
370A command buffer holds several commands.
371It always begins with a fresh command header
372(the protocol doesn't require this, but we enforce it to allow
373multiple buffers to be potentially encoded and sent in parallel).
374A single command encodes one contiguous horizontal line of pixels
375
376The function relies on the client to do all allocation, so that
377rendering can be done directly to output buffers (e.g. USB URBs).
378The function fills the supplied command buffer, providing information
379on where it left off, so the client may call in again with additional
380buffers if the line will take several buffers to complete.
381
382A single command can transmit a maximum of 256 pixels,
383regardless of the compression ratio (protocol design limit).
384To the hardware, 0 for a size byte means 256
385
386Rather than 256 pixel commands which are either rl or raw encoded,
387the rlx command simply assumes alternating raw and rl spans within one cmd.
388This has a slightly larger header overhead, but produces more even results.
389It also processes all data (read and write) in a single pass.
390Performance benchmarks of common cases show it having just slightly better
391compression than 256 pixel raw -or- rle commands, with similar CPU consumpion.
392But for very rl friendly data, will compress not quite as well.
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700393*/
Bernie Thompson530f43a2010-02-15 06:46:21 -0800394static void dlfb_compress_hline(
395 const uint16_t **pixel_start_ptr,
396 const uint16_t *const pixel_end,
397 uint32_t *device_address_ptr,
398 uint8_t **command_buffer_ptr,
399 const uint8_t *const cmd_buffer_end)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700400{
Bernie Thompson530f43a2010-02-15 06:46:21 -0800401 const uint16_t *pixel = *pixel_start_ptr;
402 uint32_t dev_addr = *device_address_ptr;
403 uint8_t *cmd = *command_buffer_ptr;
404 const int bpp = 2;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700405
Bernie Thompson530f43a2010-02-15 06:46:21 -0800406 while ((pixel_end > pixel) &&
407 (cmd_buffer_end - MIN_RLX_CMD_BYTES > cmd)) {
408 uint8_t *raw_pixels_count_byte = 0;
409 uint8_t *cmd_pixels_count_byte = 0;
410 const uint16_t *raw_pixel_start = 0;
411 const uint16_t *cmd_pixel_start, *cmd_pixel_end = 0;
412 const uint32_t be_dev_addr = cpu_to_be32(dev_addr);
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700413
Bernie Thompson530f43a2010-02-15 06:46:21 -0800414 prefetchw((void *) cmd); /* pull in one cache line at least */
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700415
Bernie Thompson530f43a2010-02-15 06:46:21 -0800416 *cmd++ = 0xAF;
417 *cmd++ = 0x6B;
418 *cmd++ = (uint8_t) ((be_dev_addr >> 8) & 0xFF);
419 *cmd++ = (uint8_t) ((be_dev_addr >> 16) & 0xFF);
420 *cmd++ = (uint8_t) ((be_dev_addr >> 24) & 0xFF);
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700421
Bernie Thompson530f43a2010-02-15 06:46:21 -0800422 cmd_pixels_count_byte = cmd++; /* we'll know this later */
423 cmd_pixel_start = pixel;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700424
Bernie Thompson530f43a2010-02-15 06:46:21 -0800425 raw_pixels_count_byte = cmd++; /* we'll know this later */
426 raw_pixel_start = pixel;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700427
Bernie Thompson530f43a2010-02-15 06:46:21 -0800428 cmd_pixel_end = pixel + min(MAX_CMD_PIXELS + 1,
429 min((int)(pixel_end - pixel),
430 (int)(cmd_buffer_end - cmd) / bpp));
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700431
Bernie Thompson530f43a2010-02-15 06:46:21 -0800432 prefetch_range((void *) pixel, (cmd_pixel_end - pixel) * bpp);
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700433
Bernie Thompson530f43a2010-02-15 06:46:21 -0800434 while (pixel < cmd_pixel_end) {
435 const uint16_t * const repeating_pixel = pixel;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700436
Bernie Thompson530f43a2010-02-15 06:46:21 -0800437 *(uint16_t *)cmd = cpu_to_be16p(pixel);
438 cmd += 2;
439 pixel++;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700440
Bernie Thompson530f43a2010-02-15 06:46:21 -0800441 if (unlikely((pixel < cmd_pixel_end) &&
442 (*pixel == *repeating_pixel))) {
443 /* go back and fill in raw pixel count */
444 *raw_pixels_count_byte = ((repeating_pixel -
445 raw_pixel_start) + 1) & 0xFF;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700446
Bernie Thompson530f43a2010-02-15 06:46:21 -0800447 while ((pixel < cmd_pixel_end)
448 && (*pixel == *repeating_pixel)) {
449 pixel++;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700450 }
Bernie Thompson59277b62009-11-24 15:52:21 -0800451
Bernie Thompson530f43a2010-02-15 06:46:21 -0800452 /* immediately after raw data is repeat byte */
453 *cmd++ = ((pixel - repeating_pixel) - 1) & 0xFF;
Bernie Thompson59277b62009-11-24 15:52:21 -0800454
Bernie Thompson530f43a2010-02-15 06:46:21 -0800455 /* Then start another raw pixel span */
456 raw_pixel_start = pixel;
457 raw_pixels_count_byte = cmd++;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700458 }
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700459 }
460
Bernie Thompson530f43a2010-02-15 06:46:21 -0800461 if (pixel > raw_pixel_start) {
462 /* finalize last RAW span */
463 *raw_pixels_count_byte = (pixel-raw_pixel_start) & 0xFF;
464 }
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700465
Bernie Thompson530f43a2010-02-15 06:46:21 -0800466 *cmd_pixels_count_byte = (pixel - cmd_pixel_start) & 0xFF;
467 dev_addr += (pixel - cmd_pixel_start) * bpp;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700468 }
469
Bernie Thompson530f43a2010-02-15 06:46:21 -0800470 if (cmd_buffer_end <= MIN_RLX_CMD_BYTES + cmd) {
471 /* Fill leftover bytes with no-ops */
472 if (cmd_buffer_end > cmd)
473 memset(cmd, 0xAF, cmd_buffer_end - cmd);
474 cmd = (uint8_t *) cmd_buffer_end;
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700475 }
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700476
Bernie Thompson530f43a2010-02-15 06:46:21 -0800477 *command_buffer_ptr = cmd;
478 *pixel_start_ptr = pixel;
479 *device_address_ptr = dev_addr;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700480
Bernie Thompson530f43a2010-02-15 06:46:21 -0800481 return;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700482}
483
Bernie Thompson530f43a2010-02-15 06:46:21 -0800484/*
485 * There are 3 copies of every pixel: The front buffer that the fbdev
486 * client renders to, the actual framebuffer across the USB bus in hardware
487 * (that we can only write to, slowly, and can never read), and (optionally)
488 * our shadow copy that tracks what's been sent to that hardware buffer.
489 */
490static void dlfb_render_hline(struct dlfb_data *dev, struct urb **urb_ptr,
491 const char *front, char **urb_buf_ptr,
492 u32 byte_offset, u32 byte_width,
493 int *ident_ptr, int *sent_ptr)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700494{
Bernie Thompson530f43a2010-02-15 06:46:21 -0800495 const u8 *line_start, *line_end, *next_pixel;
496 u32 dev_addr = dev->base16 + byte_offset;
497 struct urb *urb = *urb_ptr;
498 u8 *cmd = *urb_buf_ptr;
499 u8 *cmd_end = (u8 *) urb->transfer_buffer + urb->transfer_buffer_length;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700500
Bernie Thompson530f43a2010-02-15 06:46:21 -0800501 line_start = (u8 *) (front + byte_offset);
502 next_pixel = line_start;
503 line_end = next_pixel + byte_width;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700504
Bernie Thompson530f43a2010-02-15 06:46:21 -0800505 if (dev->backing_buffer) {
506 int offset;
507 const u8 *back_start = (u8 *) (dev->backing_buffer
508 + byte_offset);
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700509
Bernie Thompson530f43a2010-02-15 06:46:21 -0800510 *ident_ptr += dlfb_trim_hline(back_start, &next_pixel,
511 &byte_width);
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700512
Bernie Thompson530f43a2010-02-15 06:46:21 -0800513 offset = next_pixel - line_start;
514 line_end = next_pixel + byte_width;
515 dev_addr += offset;
516 back_start += offset;
517 line_start += offset;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700518
Bernie Thompson530f43a2010-02-15 06:46:21 -0800519 memcpy((char *)back_start, (char *) line_start,
520 byte_width);
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700521 }
522
Bernie Thompson530f43a2010-02-15 06:46:21 -0800523 while (next_pixel < line_end) {
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700524
Bernie Thompson530f43a2010-02-15 06:46:21 -0800525 dlfb_compress_hline((const uint16_t **) &next_pixel,
526 (const uint16_t *) line_end, &dev_addr,
527 (u8 **) &cmd, (u8 *) cmd_end);
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700528
Bernie Thompson530f43a2010-02-15 06:46:21 -0800529 if (cmd >= cmd_end) {
530 int len = cmd - (u8 *) urb->transfer_buffer;
531 if (dlfb_submit_urb(dev, urb, len))
532 return; /* lost pixels is set */
533 *sent_ptr += len;
534 urb = dlfb_get_urb(dev);
535 if (!urb)
536 return; /* lost_pixels is set */
537 *urb_ptr = urb;
538 cmd = urb->transfer_buffer;
539 cmd_end = &cmd[urb->transfer_buffer_length];
540 }
541 }
542
543 *urb_buf_ptr = cmd;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700544}
545
Bernie Thompson530f43a2010-02-15 06:46:21 -0800546int dlfb_handle_damage(struct dlfb_data *dev, int x, int y,
547 int width, int height, char *data)
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700548{
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700549 int i, ret;
Bernie Thompson530f43a2010-02-15 06:46:21 -0800550 char *cmd;
551 cycles_t start_cycles, end_cycles;
552 int bytes_sent = 0;
553 int bytes_identical = 0;
554 struct urb *urb;
555 int aligned_x;
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700556
Bernie Thompson530f43a2010-02-15 06:46:21 -0800557 start_cycles = get_cycles();
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700558
Bernie Thompson530f43a2010-02-15 06:46:21 -0800559 aligned_x = DL_ALIGN_DOWN(x, sizeof(unsigned long));
560 width = DL_ALIGN_UP(width + (x-aligned_x), sizeof(unsigned long));
561 x = aligned_x;
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700562
Bernie Thompson530f43a2010-02-15 06:46:21 -0800563 if ((width <= 0) ||
564 (x + width > dev->info->var.xres) ||
565 (y + height > dev->info->var.yres))
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700566 return -EINVAL;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700567
Bernie Thompson530f43a2010-02-15 06:46:21 -0800568 if (!atomic_read(&dev->usb_active))
569 return 0;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700570
Bernie Thompson530f43a2010-02-15 06:46:21 -0800571 urb = dlfb_get_urb(dev);
572 if (!urb)
573 return 0;
574 cmd = urb->transfer_buffer;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700575
Bernie Thompson530f43a2010-02-15 06:46:21 -0800576 for (i = y; i < y + height ; i++) {
577 const int line_offset = dev->info->fix.line_length * i;
578 const int byte_offset = line_offset + (x * BPP);
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700579
Bernie Thompson530f43a2010-02-15 06:46:21 -0800580 dlfb_render_hline(dev, &urb, (char *) dev->info->fix.smem_start,
581 &cmd, byte_offset, width * BPP,
582 &bytes_identical, &bytes_sent);
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700583 }
584
Bernie Thompson530f43a2010-02-15 06:46:21 -0800585 if (cmd > (char *) urb->transfer_buffer) {
586 /* Send partial buffer remaining before exiting */
587 int len = cmd - (char *) urb->transfer_buffer;
588 ret = dlfb_submit_urb(dev, urb, len);
589 bytes_sent += len;
590 } else
591 dlfb_urb_completion(urb);
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700592
Bernie Thompson530f43a2010-02-15 06:46:21 -0800593 atomic_add(bytes_sent, &dev->bytes_sent);
594 atomic_add(bytes_identical, &dev->bytes_identical);
595 atomic_add(width*height*2, &dev->bytes_rendered);
596 end_cycles = get_cycles();
597 atomic_add(((unsigned int) ((end_cycles - start_cycles)
598 >> 10)), /* Kcycles */
599 &dev->cpu_kcycles_used);
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700600
Bernie Thompson530f43a2010-02-15 06:46:21 -0800601 return 0;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700602}
603
Bernie Thompson530f43a2010-02-15 06:46:21 -0800604/* hardware has native COPY command (see libdlo), but not worth it for fbcon */
Bernie Thompson45742032010-02-15 06:46:04 -0800605static void dlfb_ops_copyarea(struct fb_info *info,
Bernie Thompson530f43a2010-02-15 06:46:21 -0800606 const struct fb_copyarea *area)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700607{
Bernie Thompson530f43a2010-02-15 06:46:21 -0800608
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700609 struct dlfb_data *dev = info->par;
610
Bernie Thompson530f43a2010-02-15 06:46:21 -0800611#if defined CONFIG_FB_SYS_COPYAREA || defined CONFIG_FB_SYS_COPYAREA_MODULE
612
613 sys_copyarea(info, area);
614
615 dlfb_handle_damage(dev, area->dx, area->dy,
616 area->width, area->height, info->screen_base);
617#endif
618 atomic_inc(&dev->copy_count);
619
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700620}
621
Bernie Thompson45742032010-02-15 06:46:04 -0800622static void dlfb_ops_imageblit(struct fb_info *info,
Bernie Thompson530f43a2010-02-15 06:46:21 -0800623 const struct fb_image *image)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700624{
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700625 struct dlfb_data *dev = info->par;
Bernie Thompson530f43a2010-02-15 06:46:21 -0800626
627#if defined CONFIG_FB_SYS_IMAGEBLIT || defined CONFIG_FB_SYS_IMAGEBLIT_MODULE
628
629 sys_imageblit(info, image);
630
631 dlfb_handle_damage(dev, image->dx, image->dy,
632 image->width, image->height, info->screen_base);
633
634#endif
635
636 atomic_inc(&dev->blit_count);
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700637}
638
Bernie Thompson45742032010-02-15 06:46:04 -0800639static void dlfb_ops_fillrect(struct fb_info *info,
Bernie Thompson530f43a2010-02-15 06:46:21 -0800640 const struct fb_fillrect *rect)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700641{
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700642 struct dlfb_data *dev = info->par;
643
Bernie Thompson530f43a2010-02-15 06:46:21 -0800644#if defined CONFIG_FB_SYS_FILLRECT || defined CONFIG_FB_SYS_FILLRECT_MODULE
645
646 sys_fillrect(info, rect);
647
648 dlfb_handle_damage(dev, rect->dx, rect->dy, rect->width,
649 rect->height, info->screen_base);
650#endif
651
652 atomic_inc(&dev->fill_count);
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700653
654}
655
Bernie Thompson7d9485e2010-02-15 06:46:08 -0800656static void dlfb_get_edid(struct dlfb_data *dev)
657{
658 int i;
659 int ret;
660 char rbuf[2];
661
662 for (i = 0; i < sizeof(dev->edid); i++) {
663 ret = usb_control_msg(dev->udev,
664 usb_rcvctrlpipe(dev->udev, 0), (0x02),
665 (0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2,
666 0);
667 dev->edid[i] = rbuf[1];
668 }
669}
670
Bernie Thompson45742032010-02-15 06:46:04 -0800671static int dlfb_ops_ioctl(struct fb_info *info, unsigned int cmd,
Bernie Thompson530f43a2010-02-15 06:46:21 -0800672 unsigned long arg)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700673{
Bernie Thompson530f43a2010-02-15 06:46:21 -0800674
675 struct dlfb_data *dev = info->par;
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700676 struct dloarea *area = NULL;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700677
Bernie Thompson530f43a2010-02-15 06:46:21 -0800678 if (!atomic_read(&dev->usb_active))
679 return 0;
680
681 /* TODO: Update X server to get this from sysfs instead */
682 if (cmd == DLFB_IOCTL_RETURN_EDID) {
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700683 char *edid = (char *)arg;
Bernie Thompson530f43a2010-02-15 06:46:21 -0800684 dlfb_get_edid(dev);
685 if (copy_to_user(edid, dev->edid, sizeof(dev->edid)))
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700686 return -EFAULT;
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700687 return 0;
688 }
689
Bernie Thompson530f43a2010-02-15 06:46:21 -0800690 /* TODO: Help propose a standard fb.h ioctl to report mmap damage */
691 if (cmd == DLFB_IOCTL_REPORT_DAMAGE) {
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700692
693 area = (struct dloarea *)arg;
694
695 if (area->x < 0)
696 area->x = 0;
697
698 if (area->x > info->var.xres)
699 area->x = info->var.xres;
700
701 if (area->y < 0)
702 area->y = 0;
703
704 if (area->y > info->var.yres)
705 area->y = info->var.yres;
706
Bernie Thompson530f43a2010-02-15 06:46:21 -0800707 atomic_set(&dev->use_defio, 0);
708
709 dlfb_handle_damage(dev, area->x, area->y, area->w, area->h,
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700710 info->screen_base);
Bernie Thompson530f43a2010-02-15 06:46:21 -0800711 atomic_inc(&dev->damage_count);
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700712 }
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700713
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700714 return 0;
715}
716
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700717/* taken from vesafb */
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700718static int
Bernie Thompson45742032010-02-15 06:46:04 -0800719dlfb_ops_setcolreg(unsigned regno, unsigned red, unsigned green,
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700720 unsigned blue, unsigned transp, struct fb_info *info)
721{
722 int err = 0;
723
724 if (regno >= info->cmap.len)
725 return 1;
726
727 if (regno < 16) {
728 if (info->var.red.offset == 10) {
729 /* 1:5:5:5 */
730 ((u32 *) (info->pseudo_palette))[regno] =
731 ((red & 0xf800) >> 1) |
732 ((green & 0xf800) >> 6) | ((blue & 0xf800) >> 11);
733 } else {
734 /* 0:5:6:5 */
735 ((u32 *) (info->pseudo_palette))[regno] =
736 ((red & 0xf800)) |
737 ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11);
738 }
739 }
740
741 return err;
742}
743
Bernie Thompson3e8f3d62010-02-15 06:46:26 -0800744/*
745 * It's common for several clients to have framebuffer open simultaneously.
746 * e.g. both fbcon and X. Makes things interesting.
747 */
748static int dlfb_ops_open(struct fb_info *info, int user)
749{
750 struct dlfb_data *dev = info->par;
751
752/* if (user == 0)
753 * We could special case kernel mode clients (fbcon) here
754 */
755
756 mutex_lock(&dev->fb_open_lock);
757
758 dev->fb_count++;
759
760#ifdef CONFIG_FB_DEFERRED_IO
761 if ((atomic_read(&dev->use_defio)) && (info->fbdefio == NULL)) {
762 /* enable defio */
763 info->fbdefio = &dlfb_defio;
764 fb_deferred_io_init(info);
765 }
766#endif
767
768 dl_notice("open /dev/fb%d user=%d fb_info=%p count=%d\n",
769 info->node, user, info, dev->fb_count);
770
771 mutex_unlock(&dev->fb_open_lock);
772
773 return 0;
774}
775
Bernie Thompson45742032010-02-15 06:46:04 -0800776static int dlfb_ops_release(struct fb_info *info, int user)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700777{
Bernie Thompson3e8f3d62010-02-15 06:46:26 -0800778 struct dlfb_data *dev = info->par;
779
780 mutex_lock(&dev->fb_open_lock);
781
782 dev->fb_count--;
783
784#ifdef CONFIG_FB_DEFERRED_IO
785 if ((dev->fb_count == 0) && (info->fbdefio)) {
786 fb_deferred_io_cleanup(info);
787 info->fbdefio = NULL;
788 info->fbops->fb_mmap = dlfb_ops_mmap;
789 }
790#endif
791
792 dl_notice("release /dev/fb%d user=%d count=%d\n",
793 info->node, user, dev->fb_count);
794
795 mutex_unlock(&dev->fb_open_lock);
796
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700797 return 0;
798}
799
Bernie Thompson4a4854d2010-02-15 06:45:55 -0800800/*
801 * Called when all client interfaces to start transactions have been disabled,
802 * and all references to our device instance (dlfb_data) are released.
803 * Every transaction must have a reference, so we know are fully spun down
804 */
805static void dlfb_delete(struct kref *kref)
806{
807 struct dlfb_data *dev = container_of(kref, struct dlfb_data, kref);
808
809 if (dev->backing_buffer)
810 vfree(dev->backing_buffer);
811
Bernie Thompson3e8f3d62010-02-15 06:46:26 -0800812 mutex_destroy(&dev->fb_open_lock);
813
Bernie Thompson4a4854d2010-02-15 06:45:55 -0800814 kfree(dev);
815}
816
Bernie Thompson7d9485e2010-02-15 06:46:08 -0800817/*
Bernie Thompson2469d5d2010-02-15 06:46:13 -0800818 * Called by fbdev as last part of unregister_framebuffer() process
819 * No new clients can open connections. Deallocate everything fb_info.
820 */
821static void dlfb_ops_destroy(struct fb_info *info)
822{
823 struct dlfb_data *dev = info->par;
824
825 if (info->cmap.len != 0)
826 fb_dealloc_cmap(&info->cmap);
827 if (info->monspecs.modedb)
828 fb_destroy_modedb(info->monspecs.modedb);
829 if (info->screen_base)
830 vfree(info->screen_base);
831
832 fb_destroy_modelist(&info->modelist);
833
834 framebuffer_release(info);
835
836 /* ref taken before register_framebuffer() for dlfb_data clients */
837 kref_put(&dev->kref, dlfb_delete);
838}
839
840/*
Bernie Thompson7d9485e2010-02-15 06:46:08 -0800841 * Check whether a video mode is supported by the DisplayLink chip
842 * We start from monitor's modes, so don't need to filter that here
843 */
844static int dlfb_is_valid_mode(struct fb_videomode *mode,
845 struct fb_info *info)
846{
847 struct dlfb_data *dev = info->par;
848
849 if (mode->xres * mode->yres > dev->sku_pixel_limit)
850 return 0;
851
852 return 1;
853}
854
855static void dlfb_var_color_format(struct fb_var_screeninfo *var)
856{
857 const struct fb_bitfield red = { 11, 5, 0 };
858 const struct fb_bitfield green = { 5, 6, 0 };
859 const struct fb_bitfield blue = { 0, 5, 0 };
860
861 var->bits_per_pixel = 16;
862 var->red = red;
863 var->green = green;
864 var->blue = blue;
865}
866
Bernie Thompson2469d5d2010-02-15 06:46:13 -0800867static int dlfb_ops_check_var(struct fb_var_screeninfo *var,
868 struct fb_info *info)
869{
870 struct fb_videomode mode;
871
872 /* TODO: support dynamically changing framebuffer size */
873 if ((var->xres * var->yres * 2) > info->fix.smem_len)
874 return -EINVAL;
875
876 /* set device-specific elements of var unrelated to mode */
877 dlfb_var_color_format(var);
878
879 fb_var_to_videomode(&mode, var);
880
881 if (!dlfb_is_valid_mode(&mode, info))
882 return -EINVAL;
883
884 return 0;
885}
886
887static int dlfb_ops_set_par(struct fb_info *info)
888{
889 struct dlfb_data *dev = info->par;
890
891 dl_notice("set_par mode %dx%d\n", info->var.xres, info->var.yres);
892
893 return dlfb_set_video_mode(dev, &info->var);
894}
895
Bernie Thompson45742032010-02-15 06:46:04 -0800896static int dlfb_ops_blank(int blank_mode, struct fb_info *info)
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700897{
Bernie Thompson530f43a2010-02-15 06:46:21 -0800898 struct dlfb_data *dev = info->par;
899 char *bufptr;
900 struct urb *urb;
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700901
Bernie Thompson530f43a2010-02-15 06:46:21 -0800902 urb = dlfb_get_urb(dev);
903 if (!urb)
904 return 0;
905 bufptr = (char *) urb->transfer_buffer;
906
907 /* overloading usb_active. UNBLANK can conflict with teardown */
908
909 bufptr = dlfb_vidreg_lock(bufptr);
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700910 if (blank_mode != FB_BLANK_UNBLANK) {
Bernie Thompson530f43a2010-02-15 06:46:21 -0800911 atomic_set(&dev->usb_active, 0);
912 bufptr = dlfb_enable_hvsync(bufptr, false);
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700913 } else {
Bernie Thompson530f43a2010-02-15 06:46:21 -0800914 atomic_set(&dev->usb_active, 1);
915 bufptr = dlfb_enable_hvsync(bufptr, true);
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700916 }
Bernie Thompson530f43a2010-02-15 06:46:21 -0800917 bufptr = dlfb_vidreg_unlock(bufptr);
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700918
Bernie Thompson530f43a2010-02-15 06:46:21 -0800919 dlfb_submit_urb(dev, urb, bufptr - (char *) urb->transfer_buffer);
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700920
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700921 return 0;
922}
923
924static struct fb_ops dlfb_ops = {
Bernie Thompson2469d5d2010-02-15 06:46:13 -0800925 .owner = THIS_MODULE,
Bernie Thompson45742032010-02-15 06:46:04 -0800926 .fb_setcolreg = dlfb_ops_setcolreg,
927 .fb_fillrect = dlfb_ops_fillrect,
928 .fb_copyarea = dlfb_ops_copyarea,
929 .fb_imageblit = dlfb_ops_imageblit,
930 .fb_mmap = dlfb_ops_mmap,
931 .fb_ioctl = dlfb_ops_ioctl,
Bernie Thompson3e8f3d62010-02-15 06:46:26 -0800932 .fb_open = dlfb_ops_open,
Bernie Thompson45742032010-02-15 06:46:04 -0800933 .fb_release = dlfb_ops_release,
934 .fb_blank = dlfb_ops_blank,
Bernie Thompson2469d5d2010-02-15 06:46:13 -0800935 .fb_check_var = dlfb_ops_check_var,
936 .fb_set_par = dlfb_ops_set_par,
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700937};
938
Bernie Thompsoncc403dc2010-02-15 06:45:49 -0800939/*
Bernie Thompson7d9485e2010-02-15 06:46:08 -0800940 * Calls dlfb_get_edid() to query the EDID of attached monitor via usb cmds
941 * Then parses EDID into three places used by various parts of fbdev:
942 * fb_var_screeninfo contains the timing of the monitor's preferred mode
943 * fb_info.monspecs is full parsed EDID info, including monspecs.modedb
944 * fb_info.modelist is a linked list of all monitor & VESA modes which work
945 *
946 * If EDID is not readable/valid, then modelist is all VESA modes,
947 * monspecs is NULL, and fb_var_screeninfo is set to safe VESA mode
948 * Returns 0 if EDID parses successfully
949 */
950static int dlfb_parse_edid(struct dlfb_data *dev,
951 struct fb_var_screeninfo *var,
952 struct fb_info *info)
953{
954 int i;
955 const struct fb_videomode *default_vmode = NULL;
956 int result = 0;
957
958 fb_destroy_modelist(&info->modelist);
959 memset(&info->monspecs, 0, sizeof(info->monspecs));
960
961 dlfb_get_edid(dev);
962 fb_edid_to_monspecs(dev->edid, &info->monspecs);
963
964 if (info->monspecs.modedb_len > 0) {
965
966 for (i = 0; i < info->monspecs.modedb_len; i++) {
967 if (dlfb_is_valid_mode(&info->monspecs.modedb[i], info))
968 fb_add_videomode(&info->monspecs.modedb[i],
969 &info->modelist);
970 }
971
972 default_vmode = fb_find_best_display(&info->monspecs,
973 &info->modelist);
974 } else {
975 struct fb_videomode fb_vmode = {0};
976
977 dl_err("Unable to get valid EDID from device/display\n");
978 result = 1;
979
980 /*
981 * Add the standard VESA modes to our modelist
982 * Since we don't have EDID, there may be modes that
983 * overspec monitor and/or are incorrect aspect ratio, etc.
984 * But at least the user has a chance to choose
985 */
986 for (i = 0; i < VESA_MODEDB_SIZE; i++) {
987 if (dlfb_is_valid_mode((struct fb_videomode *)
988 &vesa_modes[i], info))
989 fb_add_videomode(&vesa_modes[i],
990 &info->modelist);
991 }
992
993 /*
994 * default to resolution safe for projectors
995 * (since they are most common case without EDID)
996 */
997 fb_vmode.xres = 800;
998 fb_vmode.yres = 600;
999 fb_vmode.refresh = 60;
1000 default_vmode = fb_find_nearest_mode(&fb_vmode,
1001 &info->modelist);
1002 }
1003
1004 fb_videomode_to_var(var, default_vmode);
1005 dlfb_var_color_format(var);
1006
1007 return result;
1008}
1009
1010static ssize_t metrics_bytes_rendered_show(struct device *fbdev,
1011 struct device_attribute *a, char *buf) {
1012 struct fb_info *fb_info = dev_get_drvdata(fbdev);
1013 struct dlfb_data *dev = fb_info->par;
1014 return snprintf(buf, PAGE_SIZE, "%u\n",
1015 atomic_read(&dev->bytes_rendered));
1016}
1017
1018static ssize_t metrics_bytes_identical_show(struct device *fbdev,
1019 struct device_attribute *a, char *buf) {
1020 struct fb_info *fb_info = dev_get_drvdata(fbdev);
1021 struct dlfb_data *dev = fb_info->par;
1022 return snprintf(buf, PAGE_SIZE, "%u\n",
1023 atomic_read(&dev->bytes_identical));
1024}
1025
1026static ssize_t metrics_bytes_sent_show(struct device *fbdev,
1027 struct device_attribute *a, char *buf) {
1028 struct fb_info *fb_info = dev_get_drvdata(fbdev);
1029 struct dlfb_data *dev = fb_info->par;
1030 return snprintf(buf, PAGE_SIZE, "%u\n",
1031 atomic_read(&dev->bytes_sent));
1032}
1033
1034static ssize_t metrics_cpu_kcycles_used_show(struct device *fbdev,
1035 struct device_attribute *a, char *buf) {
1036 struct fb_info *fb_info = dev_get_drvdata(fbdev);
1037 struct dlfb_data *dev = fb_info->par;
1038 return snprintf(buf, PAGE_SIZE, "%u\n",
1039 atomic_read(&dev->cpu_kcycles_used));
1040}
1041
1042static ssize_t metrics_misc_show(struct device *fbdev,
1043 struct device_attribute *a, char *buf) {
1044 struct fb_info *fb_info = dev_get_drvdata(fbdev);
1045 struct dlfb_data *dev = fb_info->par;
1046 return snprintf(buf, PAGE_SIZE,
1047 "Calls to\ndamage: %u\nblit: %u\n"
1048 "defio faults: %u\ncopy: %u\n"
1049 "fill: %u\n\n"
1050 "active framebuffer clients: %d\n"
1051 "urbs available %d(%d)\n"
1052 "Shadow framebuffer in use? %s\n"
1053 "Any lost pixels? %s\n",
1054 atomic_read(&dev->damage_count),
1055 atomic_read(&dev->blit_count),
1056 atomic_read(&dev->defio_fault_count),
1057 atomic_read(&dev->copy_count),
1058 atomic_read(&dev->fill_count),
1059 dev->fb_count,
1060 dev->urbs.available, dev->urbs.limit_sem.count,
1061 (dev->backing_buffer) ? "yes" : "no",
1062 atomic_read(&dev->lost_pixels) ? "yes" : "no");
1063}
1064
1065static ssize_t edid_show(struct kobject *kobj, struct bin_attribute *a,
1066 char *buf, loff_t off, size_t count) {
1067 struct device *fbdev = container_of(kobj, struct device, kobj);
1068 struct fb_info *fb_info = dev_get_drvdata(fbdev);
1069 struct dlfb_data *dev = fb_info->par;
1070 char *edid = &dev->edid[0];
1071 const size_t size = sizeof(dev->edid);
1072
1073 if (dlfb_parse_edid(dev, &fb_info->var, fb_info))
1074 return 0;
1075
1076 if (off >= size)
1077 return 0;
1078
1079 if (off + count > size)
1080 count = size - off;
1081 memcpy(buf, edid + off, count);
1082
1083 return count;
1084}
1085
1086
1087static ssize_t metrics_reset_store(struct device *fbdev,
1088 struct device_attribute *attr,
1089 const char *buf, size_t count)
1090{
1091 struct fb_info *fb_info = dev_get_drvdata(fbdev);
1092 struct dlfb_data *dev = fb_info->par;
1093
1094 atomic_set(&dev->bytes_rendered, 0);
1095 atomic_set(&dev->bytes_identical, 0);
1096 atomic_set(&dev->bytes_sent, 0);
1097 atomic_set(&dev->cpu_kcycles_used, 0);
1098 atomic_set(&dev->blit_count, 0);
1099 atomic_set(&dev->copy_count, 0);
1100 atomic_set(&dev->fill_count, 0);
1101 atomic_set(&dev->defio_fault_count, 0);
1102 atomic_set(&dev->damage_count, 0);
1103
1104 return count;
1105}
1106
1107static ssize_t use_defio_show(struct device *fbdev,
1108 struct device_attribute *a, char *buf) {
1109 struct fb_info *fb_info = dev_get_drvdata(fbdev);
1110 struct dlfb_data *dev = fb_info->par;
1111 return snprintf(buf, PAGE_SIZE, "%d\n",
1112 atomic_read(&dev->use_defio));
1113}
1114
1115static ssize_t use_defio_store(struct device *fbdev,
1116 struct device_attribute *attr,
1117 const char *buf, size_t count)
1118{
1119 struct fb_info *fb_info = dev_get_drvdata(fbdev);
1120 struct dlfb_data *dev = fb_info->par;
1121
1122 if (count > 0) {
1123 if (buf[0] == '0')
1124 atomic_set(&dev->use_defio, 0);
1125 if (buf[0] == '1')
1126 atomic_set(&dev->use_defio, 1);
1127 }
1128 return count;
1129}
1130
1131static struct bin_attribute edid_attr = {
1132 .attr.name = "edid",
1133 .attr.mode = 0444,
1134 .size = 128,
1135 .read = edid_show,
1136};
1137
1138static struct device_attribute fb_device_attrs[] = {
1139 __ATTR_RO(metrics_bytes_rendered),
1140 __ATTR_RO(metrics_bytes_identical),
1141 __ATTR_RO(metrics_bytes_sent),
1142 __ATTR_RO(metrics_cpu_kcycles_used),
1143 __ATTR_RO(metrics_misc),
1144 __ATTR(metrics_reset, S_IWUGO, NULL, metrics_reset_store),
1145 __ATTR_RW(use_defio),
1146};
1147
Bernie Thompson3e8f3d62010-02-15 06:46:26 -08001148#ifdef CONFIG_FB_DEFERRED_IO
1149static void dlfb_dpy_deferred_io(struct fb_info *info,
1150 struct list_head *pagelist)
1151{
1152 struct page *cur;
1153 struct fb_deferred_io *fbdefio = info->fbdefio;
1154 struct dlfb_data *dev = info->par;
1155 struct urb *urb;
1156 char *cmd;
1157 cycles_t start_cycles, end_cycles;
1158 int bytes_sent = 0;
1159 int bytes_identical = 0;
1160 int bytes_rendered = 0;
1161 int fault_count = 0;
1162
1163 if (!atomic_read(&dev->use_defio))
1164 return;
1165
1166 if (!atomic_read(&dev->usb_active))
1167 return;
1168
1169 start_cycles = get_cycles();
1170
1171 urb = dlfb_get_urb(dev);
1172 if (!urb)
1173 return;
1174 cmd = urb->transfer_buffer;
1175
1176 /* walk the written page list and render each to device */
1177 list_for_each_entry(cur, &fbdefio->pagelist, lru) {
1178 dlfb_render_hline(dev, &urb, (char *) info->fix.smem_start,
1179 &cmd, cur->index << PAGE_SHIFT,
1180 PAGE_SIZE, &bytes_identical, &bytes_sent);
1181 bytes_rendered += PAGE_SIZE;
1182 fault_count++;
1183 }
1184
1185 if (cmd > (char *) urb->transfer_buffer) {
1186 /* Send partial buffer remaining before exiting */
1187 int len = cmd - (char *) urb->transfer_buffer;
1188 dlfb_submit_urb(dev, urb, len);
1189 bytes_sent += len;
1190 } else
1191 dlfb_urb_completion(urb);
1192
1193 atomic_add(fault_count, &dev->defio_fault_count);
1194 atomic_add(bytes_sent, &dev->bytes_sent);
1195 atomic_add(bytes_identical, &dev->bytes_identical);
1196 atomic_add(bytes_rendered, &dev->bytes_rendered);
1197 end_cycles = get_cycles();
1198 atomic_add(((unsigned int) ((end_cycles - start_cycles)
1199 >> 10)), /* Kcycles */
1200 &dev->cpu_kcycles_used);
1201}
1202
1203static struct fb_deferred_io dlfb_defio = {
1204 .delay = 5,
1205 .deferred_io = dlfb_dpy_deferred_io,
1206};
1207
1208#endif
1209
Bernie Thompson7d9485e2010-02-15 06:46:08 -08001210/*
Bernie Thompsoncc403dc2010-02-15 06:45:49 -08001211 * This is necessary before we can communicate with the display controller.
1212 */
1213static int dlfb_select_std_channel(struct dlfb_data *dev)
1214{
1215 int ret;
1216 u8 set_def_chn[] = { 0x57, 0xCD, 0xDC, 0xA7,
1217 0x1C, 0x88, 0x5E, 0x15,
1218 0x60, 0xFE, 0xC6, 0x97,
1219 0x16, 0x3D, 0x47, 0xF2 };
1220
1221 ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
1222 NR_USB_REQUEST_CHANNEL,
1223 (USB_DIR_OUT | USB_TYPE_VENDOR), 0, 0,
1224 set_def_chn, sizeof(set_def_chn), USB_CTRL_SET_TIMEOUT);
1225 return ret;
1226}
1227
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001228
1229static int dlfb_usb_probe(struct usb_interface *interface,
Bernie Thompson59277b62009-11-24 15:52:21 -08001230 const struct usb_device_id *id)
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001231{
Bernie Thompson59277b62009-11-24 15:52:21 -08001232 struct usb_device *usbdev;
1233 struct dlfb_data *dev;
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001234 struct fb_info *info;
Bernie Thompson59277b62009-11-24 15:52:21 -08001235 int videomemorysize;
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001236 int i;
Bernie Thompson59277b62009-11-24 15:52:21 -08001237 unsigned char *videomemory;
1238 int retval = -ENOMEM;
1239 struct fb_var_screeninfo *var;
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001240 int registered = 0;
1241 u16 *pix_framebuffer;
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001242
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001243 /* usb initialization */
1244
1245 usbdev = interface_to_usbdev(interface);
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001246
Bernie Thompson59277b62009-11-24 15:52:21 -08001247 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
1248 if (dev == NULL) {
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001249 err("dlfb_usb_probe: failed alloc of dev struct\n");
1250 goto error;
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001251 }
1252
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001253 /* we need to wait for both usb and fbdev to spin down on disconnect */
1254 kref_init(&dev->kref); /* matching kref_put in usb .disconnect fn */
1255 kref_get(&dev->kref); /* matching kref_put in .fb_destroy function*/
1256
Bernie Thompson59277b62009-11-24 15:52:21 -08001257 dev->udev = usbdev;
Bernie Thompson4a4854d2010-02-15 06:45:55 -08001258 dev->gdev = &usbdev->dev; /* our generic struct device * */
Bernie Thompson59277b62009-11-24 15:52:21 -08001259 usb_set_intfdata(interface, dev);
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001260
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001261 if (!dlfb_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) {
1262 retval = -ENOMEM;
1263 dl_err("dlfb_alloc_urb_list failed\n");
1264 goto error;
1265 }
1266
1267 mutex_init(&dev->fb_open_lock);
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001268
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001269 /* We don't register a new USB class. Our client interface is fbdev */
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001270
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001271 /* allocates framebuffer driver structure, not framebuffer memory */
1272 info = framebuffer_alloc(0, &usbdev->dev);
1273 if (!info) {
1274 retval = -ENOMEM;
1275 dl_err("framebuffer_alloc failed\n");
1276 goto error;
1277 }
Bernie Thompson59277b62009-11-24 15:52:21 -08001278 dev->info = info;
1279 info->par = dev;
1280 info->pseudo_palette = dev->pseudo_palette;
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001281 info->fbops = &dlfb_ops;
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001282
Bernie Thompson59277b62009-11-24 15:52:21 -08001283 var = &info->var;
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001284
1285 /* TODO set limit based on actual SKU detection */
1286 dev->sku_pixel_limit = 2048 * 1152;
1287
1288 INIT_LIST_HEAD(&info->modelist);
1289 dlfb_parse_edid(dev, var, info);
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001290
Bernie Thompson59277b62009-11-24 15:52:21 -08001291 /*
1292 * ok, now that we've got the size info, we can alloc our framebuffer.
Bernie Thompson59277b62009-11-24 15:52:21 -08001293 */
Bernie Thompson59277b62009-11-24 15:52:21 -08001294 info->fix = dlfb_fix;
1295 info->fix.line_length = var->xres * (var->bits_per_pixel / 8);
1296 videomemorysize = info->fix.line_length * var->yres;
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001297
Bernie Thompson59277b62009-11-24 15:52:21 -08001298 /*
1299 * The big chunk of system memory we use as a virtual framebuffer.
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001300 * TODO: Handle fbcon cursor code calling blit in interrupt context
Bernie Thompson59277b62009-11-24 15:52:21 -08001301 */
1302 videomemory = vmalloc(videomemorysize);
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001303 if (!videomemory) {
1304 retval = -ENOMEM;
1305 dl_err("Virtual framebuffer alloc failed\n");
1306 goto error;
1307 }
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001308
Bernie Thompson59277b62009-11-24 15:52:21 -08001309 info->screen_base = videomemory;
1310 info->fix.smem_len = PAGE_ALIGN(videomemorysize);
1311 info->fix.smem_start = (unsigned long) videomemory;
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001312 info->flags = udlfb_info_flags;
1313
Bernie Thompson59277b62009-11-24 15:52:21 -08001314
1315 /*
1316 * Second framebuffer copy, mirroring the state of the framebuffer
1317 * on the physical USB device. We can function without this.
1318 * But with imperfect damage info we may end up sending pixels over USB
1319 * that were, in fact, unchanged -- wasting limited USB bandwidth
1320 */
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001321 dev->backing_buffer = vmalloc(videomemorysize);
Bernie Thompson59277b62009-11-24 15:52:21 -08001322 if (!dev->backing_buffer)
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001323 dl_warn("No shadow/backing buffer allcoated\n");
1324 else
1325 memset(dev->backing_buffer, 0, videomemorysize);
Bernie Thompson59277b62009-11-24 15:52:21 -08001326
1327 retval = fb_alloc_cmap(&info->cmap, 256, 0);
1328 if (retval < 0) {
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001329 dl_err("fb_alloc_cmap failed %x\n", retval);
1330 goto error;
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001331 }
1332
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001333 /* ready to begin using device */
1334
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001335#ifdef CONFIG_FB_DEFERRED_IO
1336 atomic_set(&dev->use_defio, 1);
1337#endif
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001338 atomic_set(&dev->usb_active, 1);
Bernie Thompson59277b62009-11-24 15:52:21 -08001339 dlfb_select_std_channel(dev);
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001340
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001341 dlfb_ops_check_var(var, info);
1342 dlfb_ops_set_par(info);
1343
1344 /* paint greenscreen */
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001345 pix_framebuffer = (u16 *) videomemory;
1346 for (i = 0; i < videomemorysize / 2; i++)
1347 pix_framebuffer[i] = 0x37e6;
1348
1349 dlfb_handle_damage(dev, 0, 0, info->var.xres, info->var.yres,
1350 videomemory);
Bernie Thompson530f43a2010-02-15 06:46:21 -08001351
Bernie Thompson59277b62009-11-24 15:52:21 -08001352 retval = register_framebuffer(info);
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001353 if (retval < 0) {
1354 dl_err("register_framebuffer failed %d\n", retval);
1355 goto error;
1356 }
1357 registered = 1;
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001358
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001359 for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++)
1360 device_create_file(info->dev, &fb_device_attrs[i]);
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -07001361
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001362 device_create_bin_file(info->dev, &edid_attr);
1363
1364 dl_err("DisplayLink USB device /dev/fb%d attached. %dx%d resolution."
1365 " Using %dK framebuffer memory\n", info->node,
1366 var->xres, var->yres,
1367 ((dev->backing_buffer) ?
1368 videomemorysize * 2 : videomemorysize) >> 10);
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001369 return 0;
1370
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001371error:
1372 if (dev) {
1373 if (registered) {
1374 unregister_framebuffer(info);
1375 dlfb_ops_destroy(info);
1376 } else
1377 kref_put(&dev->kref, dlfb_delete);
1378
1379 if (dev->urbs.count > 0)
1380 dlfb_free_urb_list(dev);
1381 kref_put(&dev->kref, dlfb_delete); /* last ref from kref_init */
1382
1383 /* dev has been deallocated. Do not dereference */
1384 }
1385
Bernie Thompson59277b62009-11-24 15:52:21 -08001386 return retval;
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001387}
1388
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001389static void dlfb_usb_disconnect(struct usb_interface *interface)
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001390{
Bernie Thompson59277b62009-11-24 15:52:21 -08001391 struct dlfb_data *dev;
1392 struct fb_info *info;
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001393 int i;
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001394
Bernie Thompson59277b62009-11-24 15:52:21 -08001395 dev = usb_get_intfdata(interface);
Bernie Thompson59277b62009-11-24 15:52:21 -08001396 info = dev->info;
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001397
1398 /* when non-active we'll update virtual framebuffer, but no new urbs */
1399 atomic_set(&dev->usb_active, 0);
1400
1401 usb_set_intfdata(interface, NULL);
1402
1403 for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++)
1404 device_remove_file(info->dev, &fb_device_attrs[i]);
1405
1406 device_remove_bin_file(info->dev, &edid_attr);
1407
1408 /* this function will wait for all in-flight urbs to complete */
1409 dlfb_free_urb_list(dev);
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001410
Bernie Thompson59277b62009-11-24 15:52:21 -08001411 if (info) {
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001412 dl_notice("Detaching /dev/fb%d\n", info->node);
Bernie Thompson59277b62009-11-24 15:52:21 -08001413 unregister_framebuffer(info);
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001414 dlfb_ops_destroy(info);
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001415 }
1416
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001417 /* release reference taken by kref_init in probe() */
1418 kref_put(&dev->kref, dlfb_delete);
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001419
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001420 /* consider dlfb_data freed */
1421
1422 return;
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001423}
1424
1425static struct usb_driver dlfb_driver = {
1426 .name = "udlfb",
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001427 .probe = dlfb_usb_probe,
1428 .disconnect = dlfb_usb_disconnect,
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001429 .id_table = id_table,
1430};
1431
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001432static int __init dlfb_module_init(void)
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001433{
1434 int res;
1435
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001436 res = usb_register(&dlfb_driver);
1437 if (res)
1438 err("usb_register failed. Error number %d", res);
1439
1440 printk("VMODES initialized\n");
1441
1442 return res;
1443}
1444
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001445static void __exit dlfb_module_exit(void)
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001446{
1447 usb_deregister(&dlfb_driver);
1448}
1449
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001450module_init(dlfb_module_init);
1451module_exit(dlfb_module_exit);
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001452
Bernie Thompson4a4854d2010-02-15 06:45:55 -08001453static void dlfb_urb_completion(struct urb *urb)
1454{
1455 struct urb_node *unode = urb->context;
1456 struct dlfb_data *dev = unode->dev;
1457 unsigned long flags;
1458
1459 /* sync/async unlink faults aren't errors */
1460 if (urb->status) {
1461 if (!(urb->status == -ENOENT ||
1462 urb->status == -ECONNRESET ||
1463 urb->status == -ESHUTDOWN)) {
1464 dl_err("%s - nonzero write bulk status received: %d\n",
1465 __func__, urb->status);
1466 atomic_set(&dev->lost_pixels, 1);
1467 }
1468 }
1469
1470 urb->transfer_buffer_length = dev->urbs.size; /* reset to actual */
1471
1472 spin_lock_irqsave(&dev->urbs.lock, flags);
1473 list_add_tail(&unode->entry, &dev->urbs.list);
1474 dev->urbs.available++;
1475 spin_unlock_irqrestore(&dev->urbs.lock, flags);
1476
1477 up(&dev->urbs.limit_sem);
1478}
1479
1480static void dlfb_free_urb_list(struct dlfb_data *dev)
1481{
1482 int count = dev->urbs.count;
1483 struct list_head *node;
1484 struct urb_node *unode;
1485 struct urb *urb;
1486 int ret;
1487 unsigned long flags;
1488
1489 dl_notice("Waiting for completes and freeing all render urbs\n");
1490
1491 /* keep waiting and freeing, until we've got 'em all */
1492 while (count--) {
1493 /* Timeout means a memory leak and/or fault */
1494 ret = down_timeout(&dev->urbs.limit_sem, FREE_URB_TIMEOUT);
1495 if (ret) {
1496 BUG_ON(ret);
1497 break;
1498 }
1499 spin_lock_irqsave(&dev->urbs.lock, flags);
1500
1501 node = dev->urbs.list.next; /* have reserved one with sem */
1502 list_del_init(node);
1503
1504 spin_unlock_irqrestore(&dev->urbs.lock, flags);
1505
1506 unode = list_entry(node, struct urb_node, entry);
1507 urb = unode->urb;
1508
1509 /* Free each separately allocated piece */
1510 usb_buffer_free(urb->dev, dev->urbs.size,
1511 urb->transfer_buffer, urb->transfer_dma);
1512 usb_free_urb(urb);
1513 kfree(node);
1514 }
1515
1516 kref_put(&dev->kref, dlfb_delete);
1517
1518}
1519
1520static int dlfb_alloc_urb_list(struct dlfb_data *dev, int count, size_t size)
1521{
1522 int i = 0;
1523 struct urb *urb;
1524 struct urb_node *unode;
1525 char *buf;
1526
1527 spin_lock_init(&dev->urbs.lock);
1528
1529 dev->urbs.size = size;
1530 INIT_LIST_HEAD(&dev->urbs.list);
1531
1532 while (i < count) {
1533 unode = kzalloc(sizeof(struct urb_node), GFP_KERNEL);
1534 if (!unode)
1535 break;
1536 unode->dev = dev;
1537
1538 urb = usb_alloc_urb(0, GFP_KERNEL);
1539 if (!urb) {
1540 kfree(unode);
1541 break;
1542 }
1543 unode->urb = urb;
1544
1545 buf = usb_buffer_alloc(dev->udev, MAX_TRANSFER, GFP_KERNEL,
1546 &urb->transfer_dma);
1547 if (!buf) {
1548 kfree(unode);
1549 usb_free_urb(urb);
1550 break;
1551 }
1552
1553 /* urb->transfer_buffer_length set to actual before submit */
1554 usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, 1),
1555 buf, size, dlfb_urb_completion, unode);
1556 urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1557
1558 list_add_tail(&unode->entry, &dev->urbs.list);
1559
1560 i++;
1561 }
1562
1563 sema_init(&dev->urbs.limit_sem, i);
1564 dev->urbs.count = i;
1565 dev->urbs.available = i;
1566
1567 kref_get(&dev->kref); /* released in free_render_urbs() */
1568
1569 dl_notice("allocated %d %d byte urbs \n", i, (int) size);
1570
1571 return i;
1572}
1573
1574static struct urb *dlfb_get_urb(struct dlfb_data *dev)
1575{
1576 int ret = 0;
1577 struct list_head *entry;
1578 struct urb_node *unode;
1579 struct urb *urb = NULL;
1580 unsigned long flags;
1581
1582 /* Wait for an in-flight buffer to complete and get re-queued */
1583 ret = down_timeout(&dev->urbs.limit_sem, GET_URB_TIMEOUT);
1584 if (ret) {
1585 atomic_set(&dev->lost_pixels, 1);
1586 dl_err("wait for urb interrupted: %x\n", ret);
1587 goto error;
1588 }
1589
1590 spin_lock_irqsave(&dev->urbs.lock, flags);
1591
1592 BUG_ON(list_empty(&dev->urbs.list)); /* reserved one with limit_sem */
1593 entry = dev->urbs.list.next;
1594 list_del_init(entry);
1595 dev->urbs.available--;
1596
1597 spin_unlock_irqrestore(&dev->urbs.lock, flags);
1598
1599 unode = list_entry(entry, struct urb_node, entry);
1600 urb = unode->urb;
1601
1602error:
1603 return urb;
1604}
1605
1606static int dlfb_submit_urb(struct dlfb_data *dev, struct urb *urb, size_t len)
1607{
1608 int ret;
1609
1610 BUG_ON(len > dev->urbs.size);
1611
1612 urb->transfer_buffer_length = len; /* set to actual payload len */
1613 ret = usb_submit_urb(urb, GFP_KERNEL);
1614 if (ret) {
1615 dlfb_urb_completion(urb); /* because no one else will */
1616 atomic_set(&dev->lost_pixels, 1);
1617 dl_err("usb_submit_urb error %x\n", ret);
1618 }
1619 return ret;
1620}
1621
Bernie Thompson59277b62009-11-24 15:52:21 -08001622MODULE_AUTHOR("Roberto De Ioris <roberto@unbit.it>, "
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001623 "Jaya Kumar <jayakumar.lkml@gmail.com>, "
1624 "Bernie Thompson <bernie@plugable.com>");
1625MODULE_DESCRIPTION("DisplayLink kernel framebuffer driver");
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001626MODULE_LICENSE("GPL");
Bernie Thompson2469d5d2010-02-15 06:46:13 -08001627