blob: 0ab9d15f3439085c4d39bd2d1e5ecab694657a86 [file] [log] [blame]
Roberto De Ioris88e58b12009-06-03 14:03:06 -07001/*****************************************************************************
2 * DLFB Kernel Driver *
3 * Version 0.2 (udlfb) *
4 * (C) 2009 Roberto De Ioris <roberto@unbit.it> *
5 * *
6 * This file is licensed under the GPLv2. See COPYING in the package. *
7 * Based on the amazing work of Florian Echtler and libdlo 0.1 *
8 * *
9 * *
Roberto De Ioris7316bc52009-06-10 23:02:19 -070010 * 10.06.09 release 0.2.3 (edid ioctl, fallback for unsupported modes) *
11 * 05.06.09 release 0.2.2 (real screen blanking, rle compression, double buffer) *
Roberto De Ioris88e58b12009-06-03 14:03:06 -070012 * 31.05.09 release 0.2 *
13 * 22.05.09 First public (ugly) release *
14 *****************************************************************************/
15
16#include <linux/module.h>
17#include <linux/kernel.h>
18#include <linux/init.h>
19#include <linux/usb.h>
20#include <linux/uaccess.h>
21#include <linux/mm.h>
22#include <linux/fb.h>
23#include <linux/mutex.h>
24
25#include "udlfb.h"
26
27#define DRIVER_VERSION "DLFB 0.2"
28
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -070029/* memory functions taken from vfb */
Roberto De Ioris88e58b12009-06-03 14:03:06 -070030
31static void *rvmalloc(unsigned long size)
32{
33 void *mem;
34 unsigned long adr;
35
36 size = PAGE_ALIGN(size);
37 mem = vmalloc_32(size);
38 if (!mem)
39 return NULL;
40
41 memset(mem, 0, size); /* Clear the ram out, no junk to the user */
42 adr = (unsigned long)mem;
43 while (size > 0) {
44 SetPageReserved(vmalloc_to_page((void *)adr));
45 adr += PAGE_SIZE;
46 size -= PAGE_SIZE;
47 }
48
49 return mem;
50}
51
52static void rvfree(void *mem, unsigned long size)
53{
54 unsigned long adr;
55
56 if (!mem)
57 return;
58
59 adr = (unsigned long)mem;
60 while ((long)size > 0) {
61 ClearPageReserved(vmalloc_to_page((void *)adr));
62 adr += PAGE_SIZE;
63 size -= PAGE_SIZE;
64 }
65 vfree(mem);
66}
67
68static int dlfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
69{
70 unsigned long start = vma->vm_start;
71 unsigned long size = vma->vm_end - vma->vm_start;
72 unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
73 unsigned long page, pos;
74
75 printk("MMAP: %lu %u\n", offset + size, info->fix.smem_len);
76
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -070077 if (offset + size > info->fix.smem_len)
Roberto De Ioris88e58b12009-06-03 14:03:06 -070078 return -EINVAL;
Roberto De Ioris88e58b12009-06-03 14:03:06 -070079
80 pos = (unsigned long)info->fix.smem_start + offset;
81
82 while (size > 0) {
83 page = vmalloc_to_pfn((void *)pos);
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -070084 if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
Roberto De Ioris88e58b12009-06-03 14:03:06 -070085 return -EAGAIN;
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -070086
Roberto De Ioris88e58b12009-06-03 14:03:06 -070087 start += PAGE_SIZE;
88 pos += PAGE_SIZE;
89 if (size > PAGE_SIZE)
90 size -= PAGE_SIZE;
91 else
92 size = 0;
93 }
94
95 vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
96 return 0;
97
98}
99
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700100/* ioctl structure */
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700101struct dloarea {
102 int x, y;
103 int w, h;
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700104 int x2, y2;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700105};
106
107/*
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700108static struct usb_device_id id_table [] = {
109 { USB_DEVICE(0x17e9, 0x023d) },
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700110 { }
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700111};
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700112*/
113
114static struct usb_device_id id_table[] = {
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700115 {.idVendor = 0x17e9, .match_flags = USB_DEVICE_ID_MATCH_VENDOR,},
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700116 {},
117};
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700118MODULE_DEVICE_TABLE(usb, id_table);
119
120static struct usb_driver dlfb_driver;
121
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700122// thanks to Henrik Bjerregaard Pedersen for this function
123static char *rle_compress16(uint16_t * src, char *dst, int rem)
124{
125
126 int rl;
127 uint16_t pix0;
128 char *end_if_raw = dst + 6 + 2 * rem;
129
130 dst += 6; // header will be filled in if RLE is worth it
131
132 while (rem && dst < end_if_raw) {
133 char *start = (char *)src;
134
135 pix0 = *src++;
136 rl = 1;
137 rem--;
138 while (rem && *src == pix0)
139 rem--, rl++, src++;
140 *dst++ = rl;
141 *dst++ = start[1];
142 *dst++ = start[0];
143 }
144
145 return dst;
146}
147
148/*
149Thanks to Henrik Bjerregaard Pedersen for rle implementation and code refactoring.
150Next step is huffman compression.
151*/
152
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700153static int
154image_blit(struct dlfb_data *dev_info, int x, int y, int width, int height,
155 char *data)
156{
157
158 int i, j, base;
159 int rem = width;
160 int ret;
161
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700162 int firstdiff, thistime;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700163
164 char *bufptr;
165
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700166 if (x + width > dev_info->info->var.xres)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700167 return -EINVAL;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700168
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700169 if (y + height > dev_info->info->var.yres)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700170 return -EINVAL;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700171
172 mutex_lock(&dev_info->bulk_mutex);
173
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700174 base =
175 dev_info->base16 + ((dev_info->info->var.xres * 2 * y) + (x * 2));
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700176
177 data += (dev_info->info->var.xres * 2 * y) + (x * 2);
178
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700179 /* printk("IMAGE_BLIT\n"); */
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700180
181 bufptr = dev_info->buf;
182
183 for (i = y; i < y + height; i++) {
184
185 if (dev_info->bufend - bufptr < BUF_HIGH_WATER_MARK) {
186 ret = dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
187 bufptr = dev_info->buf;
188 }
189
190 rem = width;
191
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700192 /* printk("WRITING LINE %d\n", i); */
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700193
194 while (rem) {
195
196 if (dev_info->bufend - bufptr < BUF_HIGH_WATER_MARK) {
197 ret =
198 dlfb_bulk_msg(dev_info,
199 bufptr - dev_info->buf);
200 bufptr = dev_info->buf;
201 }
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700202 // number of pixels to consider this time
203 thistime = rem;
204 if (thistime > 255)
205 thistime = 255;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700206
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700207 // find position of first pixel that has changed
208 firstdiff = -1;
209 for (j = 0; j < thistime * 2; j++) {
210 if (dev_info->backing_buffer
211 [base - dev_info->base16 + j] != data[j]) {
212 firstdiff = j / 2;
213 break;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700214 }
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700215 }
216
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700217 if (firstdiff >= 0) {
218 char *end_of_rle;
219
220 end_of_rle =
221 rle_compress16((uint16_t *) (data +
222 firstdiff * 2),
223 bufptr,
224 thistime - firstdiff);
225
226 if (end_of_rle <
227 bufptr + 6 + 2 * (thistime - firstdiff)) {
228 bufptr[0] = 0xAF;
229 bufptr[1] = 0x69;
230
231 bufptr[2] =
232 (char)((base +
233 firstdiff * 2) >> 16);
234 bufptr[3] =
235 (char)((base + firstdiff * 2) >> 8);
236 bufptr[4] =
237 (char)(base + firstdiff * 2);
238 bufptr[5] = thistime - firstdiff;
239
240 bufptr = end_of_rle;
241
242 } else {
243 // fallback to raw (or some other encoding?)
244 *bufptr++ = 0xAF;
245 *bufptr++ = 0x68;
246
247 *bufptr++ =
248 (char)((base +
249 firstdiff * 2) >> 16);
250 *bufptr++ =
251 (char)((base + firstdiff * 2) >> 8);
252 *bufptr++ =
253 (char)(base + firstdiff * 2);
254 *bufptr++ = thistime - firstdiff;
255 // PUT COMPRESSION HERE
256 for (j = firstdiff * 2;
257 j < thistime * 2; j += 2) {
258 *bufptr++ = data[j + 1];
259 *bufptr++ = data[j];
260 }
261 }
262 }
263
264 base += thistime * 2;
265 data += thistime * 2;
266 rem -= thistime;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700267 }
268
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700269 memcpy(dev_info->backing_buffer + (base - dev_info->base16) -
270 (width * 2), data - (width * 2), width * 2);
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700271
272 base += (dev_info->info->var.xres * 2) - (width * 2);
273 data += (dev_info->info->var.xres * 2) - (width * 2);
274
275 }
276
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700277 if (bufptr > dev_info->buf) {
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700278 ret = dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700279 }
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700280
281 mutex_unlock(&dev_info->bulk_mutex);
282
283 return base;
284
285}
286
287static int
288draw_rect(struct dlfb_data *dev_info, int x, int y, int width, int height,
289 unsigned char red, unsigned char green, unsigned char blue)
290{
291
292 int i, j, base;
293 int ret;
294 unsigned short col =
295 (((((red) & 0xF8) | ((green) >> 5)) & 0xFF) << 8) +
296 (((((green) & 0x1C) << 3) | ((blue) >> 3)) & 0xFF);
297 int rem = width;
298
299 char *bufptr;
300
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700301 if (x + width > dev_info->info->var.xres)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700302 return -EINVAL;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700303
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700304 if (y + height > dev_info->info->var.yres)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700305 return -EINVAL;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700306
307 mutex_lock(&dev_info->bulk_mutex);
308
309 base = dev_info->base16 + (dev_info->info->var.xres * 2 * y) + (x * 2);
310
311 bufptr = dev_info->buf;
312
313 for (i = y; i < y + height; i++) {
314
315 for (j = 0; j < width * 2; j += 2) {
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700316 dev_info->backing_buffer[base - dev_info->base16 + j] =
317 (char)(col >> 8);
318 dev_info->backing_buffer[base - dev_info->base16 + j +
319 1] = (char)(col);
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700320 }
321 if (dev_info->bufend - bufptr < BUF_HIGH_WATER_MARK) {
322 ret = dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
323 bufptr = dev_info->buf;
324 }
325
326 rem = width;
327
328 while (rem) {
329
330 if (dev_info->bufend - bufptr < BUF_HIGH_WATER_MARK) {
331 ret =
332 dlfb_bulk_msg(dev_info,
333 bufptr - dev_info->buf);
334 bufptr = dev_info->buf;
335 }
336
337 *bufptr++ = 0xAF;
338 *bufptr++ = 0x69;
339
340 *bufptr++ = (char)(base >> 16);
341 *bufptr++ = (char)(base >> 8);
342 *bufptr++ = (char)(base);
343
344 if (rem > 255) {
345 *bufptr++ = 255;
346 *bufptr++ = 255;
347 rem -= 255;
348 base += 255 * 2;
349 } else {
350 *bufptr++ = rem;
351 *bufptr++ = rem;
352 base += rem * 2;
353 rem = 0;
354 }
355
356 *bufptr++ = (char)(col >> 8);
357 *bufptr++ = (char)(col);
358
359 }
360
361 base += (dev_info->info->var.xres * 2) - (width * 2);
362
363 }
364
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700365 if (bufptr > dev_info->buf)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700366 ret = dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700367
368 mutex_unlock(&dev_info->bulk_mutex);
369
370 return 1;
371}
372
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700373static void swapfb(struct dlfb_data *dev_info)
374{
375
376 int tmpbase;
377 char *bufptr;
378
379 mutex_lock(&dev_info->bulk_mutex);
380
381 tmpbase = dev_info->base16;
382
383 dev_info->base16 = dev_info->base16d;
384 dev_info->base16d = tmpbase;
385
386 bufptr = dev_info->buf;
387
388 bufptr = dlfb_set_register(bufptr, 0xFF, 0x00);
389
390 // set addresses
391 bufptr =
392 dlfb_set_register(bufptr, 0x20, (char)(dev_info->base16 >> 16));
393 bufptr = dlfb_set_register(bufptr, 0x21, (char)(dev_info->base16 >> 8));
394 bufptr = dlfb_set_register(bufptr, 0x22, (char)(dev_info->base16));
395
396 bufptr = dlfb_set_register(bufptr, 0xFF, 0x00);
397
398 dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
399
400 mutex_unlock(&dev_info->bulk_mutex);
401}
402
403static int copyfb(struct dlfb_data *dev_info)
404{
405 int base;
406 int source;
407 int rem;
408 int i, ret;
409
410 char *bufptr;
411
412 base = dev_info->base16d;
413
414 mutex_lock(&dev_info->bulk_mutex);
415
416 source = dev_info->base16;
417
418 bufptr = dev_info->buf;
419
420 for (i = 0; i < dev_info->info->var.yres; i++) {
421
422 if (dev_info->bufend - bufptr < BUF_HIGH_WATER_MARK) {
423 ret = dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
424 bufptr = dev_info->buf;
425 }
426
427 rem = dev_info->info->var.xres;
428
429 while (rem) {
430
431 if (dev_info->bufend - bufptr < BUF_HIGH_WATER_MARK) {
432 ret =
433 dlfb_bulk_msg(dev_info,
434 bufptr - dev_info->buf);
435 bufptr = dev_info->buf;
436
437 }
438
439 *bufptr++ = 0xAF;
440 *bufptr++ = 0x6A;
441
442 *bufptr++ = (char)(base >> 16);
443 *bufptr++ = (char)(base >> 8);
444 *bufptr++ = (char)(base);
445
446 if (rem > 255) {
447 *bufptr++ = 255;
448 *bufptr++ = (char)(source >> 16);
449 *bufptr++ = (char)(source >> 8);
450 *bufptr++ = (char)(source);
451
452 rem -= 255;
453 base += 255 * 2;
454 source += 255 * 2;
455
456 } else {
457 *bufptr++ = rem;
458 *bufptr++ = (char)(source >> 16);
459 *bufptr++ = (char)(source >> 8);
460 *bufptr++ = (char)(source);
461
462 base += rem * 2;
463 source += rem * 2;
464 rem = 0;
465 }
466 }
467 }
468
469 if (bufptr > dev_info->buf)
470 ret = dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
471
472 mutex_unlock(&dev_info->bulk_mutex);
473
474 return 1;
475
476}
477
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700478static int
479copyarea(struct dlfb_data *dev_info, int dx, int dy, int sx, int sy,
480 int width, int height)
481{
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700482 int base;
483 int source;
484 int rem;
485 int i, ret;
486
487 char *bufptr;
488
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700489 if (dx + width > dev_info->info->var.xres)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700490 return -EINVAL;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700491
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700492 if (dy + height > dev_info->info->var.yres)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700493 return -EINVAL;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700494
495 mutex_lock(&dev_info->bulk_mutex);
496
497 base =
498 dev_info->base16 + (dev_info->info->var.xres * 2 * dy) + (dx * 2);
499 source = (dev_info->info->var.xres * 2 * sy) + (sx * 2);
500
501 bufptr = dev_info->buf;
502
503 for (i = sy; i < sy + height; i++) {
504
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700505 memcpy(dev_info->backing_buffer + base - dev_info->base16,
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700506 dev_info->backing_buffer + source, width * 2);
507
508 if (dev_info->bufend - bufptr < BUF_HIGH_WATER_MARK) {
509 ret = dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
510 bufptr = dev_info->buf;
511 }
512
513 rem = width;
514
515 while (rem) {
516
517 if (dev_info->bufend - bufptr < BUF_HIGH_WATER_MARK) {
518 ret =
519 dlfb_bulk_msg(dev_info,
520 bufptr - dev_info->buf);
521 bufptr = dev_info->buf;
522 }
523
524 *bufptr++ = 0xAF;
525 *bufptr++ = 0x6A;
526
527 *bufptr++ = (char)(base >> 16);
528 *bufptr++ = (char)(base >> 8);
529 *bufptr++ = (char)(base);
530
531 if (rem > 255) {
532 *bufptr++ = 255;
533 *bufptr++ = (char)(source >> 16);
534 *bufptr++ = (char)(source >> 8);
535 *bufptr++ = (char)(source);
536
537 rem -= 255;
538 base += 255 * 2;
539 source += 255 * 2;
540
541 } else {
542 *bufptr++ = rem;
543 *bufptr++ = (char)(source >> 16);
544 *bufptr++ = (char)(source >> 8);
545 *bufptr++ = (char)(source);
546
547 base += rem * 2;
548 source += rem * 2;
549 rem = 0;
550 }
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700551 }
552
553 base += (dev_info->info->var.xres * 2) - (width * 2);
554 source += (dev_info->info->var.xres * 2) - (width * 2);
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700555 }
556
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700557 if (bufptr > dev_info->buf)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700558 ret = dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700559
560 mutex_unlock(&dev_info->bulk_mutex);
561
562 return 1;
563}
564
Greg Kroah-Hartman4b6a4852009-06-03 14:47:21 -0700565static void dlfb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700566{
567
568 struct dlfb_data *dev = info->par;
569
570 copyarea(dev, area->dx, area->dy, area->sx, area->sy, area->width,
571 area->height);
572
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700573 /* printk("COPY AREA %d %d %d %d %d %d !!!\n", area->dx, area->dy, area->sx, area->sy, area->width, area->height); */
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700574
575}
576
Greg Kroah-Hartman4b6a4852009-06-03 14:47:21 -0700577static void dlfb_imageblit(struct fb_info *info, const struct fb_image *image)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700578{
579
580 int ret;
581 struct dlfb_data *dev = info->par;
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700582 /* printk("IMAGE BLIT (1) %d %d %d %d DEPTH %d {%p}!!!\n", image->dx, image->dy, image->width, image->height, image->depth, dev->udev); */
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700583 cfb_imageblit(info, image);
584 ret =
585 image_blit(dev, image->dx, image->dy, image->width, image->height,
586 info->screen_base);
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700587 /* printk("IMAGE BLIT (2) %d %d %d %d DEPTH %d {%p} %d!!!\n", image->dx, image->dy, image->width, image->height, image->depth, dev->udev, ret); */
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700588}
589
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700590static void dlfb_fillrect(struct fb_info *info,
591 const struct fb_fillrect *region)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700592{
593
594 unsigned char red, green, blue;
595 struct dlfb_data *dev = info->par;
596
597 memcpy(&red, &region->color, 1);
598 memcpy(&green, &region->color + 1, 1);
599 memcpy(&blue, &region->color + 2, 1);
600 draw_rect(dev, region->dx, region->dy, region->width, region->height,
601 red, green, blue);
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700602 /* printk("FILL RECT %d %d !!!\n", region->dx, region->dy); */
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700603
604}
605
606static int dlfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
607{
608
609 struct dlfb_data *dev_info = info->par;
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700610 struct dloarea *area = NULL;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700611
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700612 if (cmd == 0xAD) {
613 char *edid = (char *)arg;
614 dlfb_edid(dev_info);
615 if (copy_to_user(edid, dev_info->edid, 128)) {
616 return -EFAULT;
617 }
618 return 0;
619 }
620
621 if (cmd == 0xAA || cmd == 0xAB || cmd == 0xAC) {
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700622
623 area = (struct dloarea *)arg;
624
625 if (area->x < 0)
626 area->x = 0;
627
628 if (area->x > info->var.xres)
629 area->x = info->var.xres;
630
631 if (area->y < 0)
632 area->y = 0;
633
634 if (area->y > info->var.yres)
635 area->y = info->var.yres;
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700636 }
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700637
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700638 if (cmd == 0xAA) {
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700639 image_blit(dev_info, area->x, area->y, area->w, area->h,
640 info->screen_base);
641 }
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700642 if (cmd == 0xAC) {
643 copyfb(dev_info);
644 image_blit(dev_info, area->x, area->y, area->w, area->h,
645 info->screen_base);
646 swapfb(dev_info);
647 } else if (cmd == 0xAB) {
648
649 if (area->x2 < 0)
650 area->x2 = 0;
651
652 if (area->y2 < 0)
653 area->y2 = 0;
654
655 copyarea(dev_info,
656 area->x2, area->y2, area->x, area->y, area->w,
657 area->h);
658 }
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700659 return 0;
660}
661
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700662/* taken from vesafb */
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700663
664static int
665dlfb_setcolreg(unsigned regno, unsigned red, unsigned green,
666 unsigned blue, unsigned transp, struct fb_info *info)
667{
668 int err = 0;
669
670 if (regno >= info->cmap.len)
671 return 1;
672
673 if (regno < 16) {
674 if (info->var.red.offset == 10) {
675 /* 1:5:5:5 */
676 ((u32 *) (info->pseudo_palette))[regno] =
677 ((red & 0xf800) >> 1) |
678 ((green & 0xf800) >> 6) | ((blue & 0xf800) >> 11);
679 } else {
680 /* 0:5:6:5 */
681 ((u32 *) (info->pseudo_palette))[regno] =
682 ((red & 0xf800)) |
683 ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11);
684 }
685 }
686
687 return err;
688}
689
690static int dlfb_release(struct fb_info *info, int user)
691{
692 struct dlfb_data *dev_info = info->par;
693 image_blit(dev_info, 0, 0, info->var.xres, info->var.yres,
694 info->screen_base);
695 return 0;
696}
697
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700698static int dlfb_blank(int blank_mode, struct fb_info *info)
699{
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700700 struct dlfb_data *dev_info = info->par;
701 char *bufptr = dev_info->buf;
702
703 bufptr = dlfb_set_register(bufptr, 0xFF, 0x00);
704 if (blank_mode != FB_BLANK_UNBLANK) {
705 bufptr = dlfb_set_register(bufptr, 0x1F, 0x01);
706 } else {
707 bufptr = dlfb_set_register(bufptr, 0x1F, 0x00);
708 }
709 bufptr = dlfb_set_register(bufptr, 0xFF, 0xFF);
710
711 dlfb_bulk_msg(dev_info, bufptr - dev_info->buf);
712
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700713 return 0;
714}
715
716static struct fb_ops dlfb_ops = {
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700717 .fb_setcolreg = dlfb_setcolreg,
718 .fb_fillrect = dlfb_fillrect,
719 .fb_copyarea = dlfb_copyarea,
720 .fb_imageblit = dlfb_imageblit,
721 .fb_mmap = dlfb_mmap,
722 .fb_ioctl = dlfb_ioctl,
723 .fb_release = dlfb_release,
724 .fb_blank = dlfb_blank,
725};
726
727static int
728dlfb_probe(struct usb_interface *interface, const struct usb_device_id *id)
729{
730 struct dlfb_data *dev_info;
731 struct fb_info *info;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700732
733 int ret;
734 char rbuf[4];
735
736 dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL);
737 if (dev_info == NULL) {
738 printk("cannot allocate dev_info structure.\n");
739 return -ENOMEM;
740 }
741
742 mutex_init(&dev_info->bulk_mutex);
743
744 dev_info->udev = usb_get_dev(interface_to_usbdev(interface));
745 dev_info->interface = interface;
746
747 printk("DisplayLink device attached\n");
748
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700749 /* add framebuffer info to usb interface */
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700750 usb_set_intfdata(interface, dev_info);
751
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700752 dev_info->buf = kmalloc(BUF_SIZE, GFP_KERNEL);
753 /* usb_buffer_alloc(dev_info->udev, BUF_SIZE , GFP_KERNEL, &dev_info->tx_urb->transfer_dma); */
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700754
755 if (dev_info->buf == NULL) {
756 printk("unable to allocate memory for dlfb commands\n");
757 goto out;
758 }
759 dev_info->bufend = dev_info->buf + BUF_SIZE;
760
761 dev_info->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
762 usb_fill_bulk_urb(dev_info->tx_urb, dev_info->udev,
763 usb_sndbulkpipe(dev_info->udev, 1), dev_info->buf, 0,
764 dlfb_bulk_callback, dev_info);
765
766 ret =
767 usb_control_msg(dev_info->udev, usb_rcvctrlpipe(dev_info->udev, 0),
768 (0x06), (0x80 | (0x02 << 5)), 0, 0, rbuf, 4, 0);
769 printk("ret control msg 0: %d %x%x%x%x\n", ret, rbuf[0], rbuf[1],
770 rbuf[2], rbuf[3]);
771
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700772 dlfb_edid(dev_info);
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700773
774 info = framebuffer_alloc(sizeof(u32) * 256, &dev_info->udev->dev);
775
776 if (!info) {
777 printk("non posso allocare il framebuffer displaylink");
778 goto out;
779 }
780
781 fb_parse_edid(dev_info->edid, &info->var);
782
783 printk("EDID XRES %d YRES %d\n", info->var.xres, info->var.yres);
784
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700785 if (dlfb_set_video_mode(dev_info, info->var.xres, info->var.yres) != 0) {
786 info->var.xres = 1280;
787 info->var.yres = 1024;
788 if (dlfb_set_video_mode
789 (dev_info, info->var.xres, info->var.yres) != 0) {
790 goto out;
791 }
792 }
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700793
794 printk("found valid mode...%d\n", info->var.pixclock);
795
796 info->pseudo_palette = info->par;
797 info->par = dev_info;
798
799 dev_info->info = info;
800
801 info->flags =
802 FBINFO_DEFAULT | FBINFO_READS_FAST | FBINFO_HWACCEL_IMAGEBLIT |
803 FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT;
804 info->fbops = &dlfb_ops;
805 info->screen_base = rvmalloc(dev_info->screen_size);
806
807 if (info->screen_base == NULL) {
808 printk
809 ("cannot allocate framebuffer virtual memory of %d bytes\n",
810 dev_info->screen_size);
811 goto out0;
812 }
813
814 printk("screen base allocated !!!\n");
815
816 dev_info->backing_buffer = kzalloc(dev_info->screen_size, GFP_KERNEL);
817
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700818 if (!dev_info->backing_buffer)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700819 printk("non posso allocare il backing buffer\n");
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700820
821 /* info->var = dev_info->si; */
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700822
823 info->var.bits_per_pixel = 16;
824 info->var.activate = FB_ACTIVATE_TEST;
825 info->var.vmode = FB_VMODE_NONINTERLACED;
826
827 info->var.red.offset = 11;
828 info->var.red.length = 5;
829 info->var.red.msb_right = 0;
830
831 info->var.green.offset = 5;
832 info->var.green.length = 6;
833 info->var.green.msb_right = 0;
834
835 info->var.blue.offset = 0;
836 info->var.blue.length = 5;
837 info->var.blue.msb_right = 0;
838
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700839 /* info->var.pixclock = (10000000 / FB_W * 1000 / FB_H)/2 ; */
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700840
841 info->fix.smem_start = (unsigned long)info->screen_base;
842 info->fix.smem_len = PAGE_ALIGN(dev_info->screen_size);
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700843 if (strlen(dev_info->udev->product) > 15) {
844 memcpy(info->fix.id, dev_info->udev->product, 15);
845 } else {
846 memcpy(info->fix.id, dev_info->udev->product,
847 strlen(dev_info->udev->product));
848 }
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700849 info->fix.type = FB_TYPE_PACKED_PIXELS;
850 info->fix.visual = FB_VISUAL_TRUECOLOR;
851 info->fix.accel = info->flags;
852 info->fix.line_length = dev_info->line_length;
853
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700854 if (fb_alloc_cmap(&info->cmap, 256, 0) < 0)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700855 goto out1;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700856
857 printk("colormap allocated\n");
Greg Kroah-Hartmanf05e0572009-06-03 14:47:08 -0700858 if (register_framebuffer(info) < 0)
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700859 goto out2;
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700860
861 draw_rect(dev_info, 0, 0, dev_info->info->var.xres,
862 dev_info->info->var.yres, 0x30, 0xff, 0x30);
863
864 return 0;
865
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700866out2:
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700867 fb_dealloc_cmap(&info->cmap);
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700868out1:
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700869 rvfree(info->screen_base, dev_info->screen_size);
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700870out0:
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700871 framebuffer_release(info);
Roberto De Ioris7316bc52009-06-10 23:02:19 -0700872out:
Roberto De Ioris88e58b12009-06-03 14:03:06 -0700873 usb_set_intfdata(interface, NULL);
874 usb_put_dev(dev_info->udev);
875 kfree(dev_info);
876 return -ENOMEM;
877
878}
879
880static void dlfb_disconnect(struct usb_interface *interface)
881{
882 struct dlfb_data *dev_info = usb_get_intfdata(interface);
883
884 mutex_unlock(&dev_info->bulk_mutex);
885
886 usb_kill_urb(dev_info->tx_urb);
887 usb_free_urb(dev_info->tx_urb);
888 usb_set_intfdata(interface, NULL);
889 usb_put_dev(dev_info->udev);
890
891 if (dev_info->info) {
892 unregister_framebuffer(dev_info->info);
893 fb_dealloc_cmap(&dev_info->info->cmap);
894 rvfree(dev_info->info->screen_base, dev_info->screen_size);
895 kfree(dev_info->backing_buffer);
896 framebuffer_release(dev_info->info);
897
898 }
899
900 kfree(dev_info);
901
902 printk("DisplayLink device disconnected\n");
903}
904
905static struct usb_driver dlfb_driver = {
906 .name = "udlfb",
907 .probe = dlfb_probe,
908 .disconnect = dlfb_disconnect,
909 .id_table = id_table,
910};
911
912static int __init dlfb_init(void)
913{
914 int res;
915
916 dlfb_init_modes();
917
918 res = usb_register(&dlfb_driver);
919 if (res)
920 err("usb_register failed. Error number %d", res);
921
922 printk("VMODES initialized\n");
923
924 return res;
925}
926
927static void __exit dlfb_exit(void)
928{
929 usb_deregister(&dlfb_driver);
930}
931
932module_init(dlfb_init);
933module_exit(dlfb_exit);
934
935MODULE_AUTHOR("Roberto De Ioris <roberto@unbit.it>");
936MODULE_DESCRIPTION(DRIVER_VERSION);
937MODULE_LICENSE("GPL");