aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/hid-picolcd.c
diff options
context:
space:
mode:
authorBruno Prémont <bonbons@linux-vserver.org>2010-03-30 16:34:30 -0400
committerJiri Kosina <jkosina@suse.cz>2010-03-31 05:21:29 -0400
commitb8c21cf697d165999cc21a90e6caa73690ac6190 (patch)
tree795001ad27187193af60ca856a19790c9c29e122 /drivers/hid/hid-picolcd.c
parent236db47c2b3b69464d50c695ab2ddd516cf64520 (diff)
HID: add framebuffer support to PicoLCD device
Add framebuffer support to PicoLCD device with use of deferred-io. Only changed areas of framebuffer get sent to device in order to save USB bandwidth and especially resources on PicoLCD device or allow higher refresh rate for a small area. Changed tiles are determined while updating shadow framebuffer. Signed-off-by: Bruno Prémont <bonbons@linux-vserver.org> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/hid-picolcd.c')
-rw-r--r--drivers/hid/hid-picolcd.c567
1 files changed, 566 insertions, 1 deletions
diff --git a/drivers/hid/hid-picolcd.c b/drivers/hid/hid-picolcd.c
index c7855f388898..e14464789e10 100644
--- a/drivers/hid/hid-picolcd.c
+++ b/drivers/hid/hid-picolcd.c
@@ -24,6 +24,9 @@
24#include "usbhid/usbhid.h" 24#include "usbhid/usbhid.h"
25#include <linux/usb.h> 25#include <linux/usb.h>
26 26
27#include <linux/fb.h>
28#include <linux/vmalloc.h>
29
27#include <linux/seq_file.h> 30#include <linux/seq_file.h>
28#include <linux/debugfs.h> 31#include <linux/debugfs.h>
29 32
@@ -69,6 +72,59 @@
69#define REPORT_HOOK_VERSION 0xf7 /* LCD: IN[2], OUT[1] */ 72#define REPORT_HOOK_VERSION 0xf7 /* LCD: IN[2], OUT[1] */
70#define REPORT_EXIT_FLASHER 0xff /* Bootloader: OUT[2] */ 73#define REPORT_EXIT_FLASHER 0xff /* Bootloader: OUT[2] */
71 74
75#if defined(CONFIG_FB) || defined(CONFIG_FB_MODULE)
76/* Framebuffer
77 *
78 * The PicoLCD use a Topway LCD module of 256x64 pixel
79 * This display area is tiled over 4 controllers with 8 tiles
80 * each. Each tile has 8x64 pixel, each data byte representing
81 * a 1-bit wide vertical line of the tile.
82 *
83 * The display can be updated at a tile granularity.
84 *
85 * Chip 1 Chip 2 Chip 3 Chip 4
86 * +----------------+----------------+----------------+----------------+
87 * | Tile 1 | Tile 1 | Tile 1 | Tile 1 |
88 * +----------------+----------------+----------------+----------------+
89 * | Tile 2 | Tile 2 | Tile 2 | Tile 2 |
90 * +----------------+----------------+----------------+----------------+
91 * ...
92 * +----------------+----------------+----------------+----------------+
93 * | Tile 8 | Tile 8 | Tile 8 | Tile 8 |
94 * +----------------+----------------+----------------+----------------+
95 */
96#define PICOLCDFB_NAME "picolcdfb"
97#define PICOLCDFB_WIDTH (256)
98#define PICOLCDFB_HEIGHT (64)
99#define PICOLCDFB_SIZE (PICOLCDFB_WIDTH * PICOLCDFB_HEIGHT / 8)
100
101#define PICOLCDFB_UPDATE_RATE_LIMIT 10
102#define PICOLCDFB_UPDATE_RATE_DEFAULT 2
103
104/* Framebuffer visual structures */
105static const struct fb_fix_screeninfo picolcdfb_fix = {
106 .id = PICOLCDFB_NAME,
107 .type = FB_TYPE_PACKED_PIXELS,
108 .visual = FB_VISUAL_MONO01,
109 .xpanstep = 0,
110 .ypanstep = 0,
111 .ywrapstep = 0,
112 .line_length = PICOLCDFB_WIDTH / 8,
113 .accel = FB_ACCEL_NONE,
114};
115
116static const struct fb_var_screeninfo picolcdfb_var = {
117 .xres = PICOLCDFB_WIDTH,
118 .yres = PICOLCDFB_HEIGHT,
119 .xres_virtual = PICOLCDFB_WIDTH,
120 .yres_virtual = PICOLCDFB_HEIGHT,
121 .width = 103,
122 .height = 26,
123 .bits_per_pixel = 1,
124 .grayscale = 1,
125};
126#endif /* CONFIG_FB */
127
72/* Input device 128/* Input device
73 * 129 *
74 * The PicoLCD has an IR receiver header, a built-in keypad with 5 keys 130 * The PicoLCD has an IR receiver header, a built-in keypad with 5 keys
@@ -118,6 +174,16 @@ struct picolcd_data {
118 struct input_dev *input_cir; 174 struct input_dev *input_cir;
119 unsigned short keycode[PICOLCD_KEYS]; 175 unsigned short keycode[PICOLCD_KEYS];
120 176
177#if defined(CONFIG_FB) || defined(CONFIG_FB_MODULE)
178 /* Framebuffer stuff */
179 u8 fb_update_rate;
180 u8 fb_bpp;
181 u8 *fb_vbitmap; /* local copy of what was sent to PicoLCD */
182 u8 *fb_bitmap; /* framebuffer */
183 struct fb_info *fb_info;
184 struct fb_deferred_io fb_defio;
185#endif /* CONFIG_FB */
186
121 /* Housekeeping stuff */ 187 /* Housekeeping stuff */
122 spinlock_t lock; 188 spinlock_t lock;
123 struct mutex mutex; 189 struct mutex mutex;
@@ -125,6 +191,7 @@ struct picolcd_data {
125 int status; 191 int status;
126#define PICOLCD_BOOTLOADER 1 192#define PICOLCD_BOOTLOADER 1
127#define PICOLCD_FAILED 2 193#define PICOLCD_FAILED 2
194#define PICOLCD_READY_FB 4
128}; 195};
129 196
130 197
@@ -197,6 +264,486 @@ static struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev,
197 return work; 264 return work;
198} 265}
199 266
267#if defined(CONFIG_FB) || defined(CONFIG_FB_MODULE)
268/* Send a given tile to PicoLCD */
269static int picolcd_fb_send_tile(struct hid_device *hdev, int chip, int tile)
270{
271 struct picolcd_data *data = hid_get_drvdata(hdev);
272 struct hid_report *report1 = picolcd_out_report(REPORT_LCD_CMD_DATA, hdev);
273 struct hid_report *report2 = picolcd_out_report(REPORT_LCD_DATA, hdev);
274 unsigned long flags;
275 u8 *tdata;
276 int i;
277
278 if (!report1 || report1->maxfield != 1 || !report2 || report2->maxfield != 1)
279 return -ENODEV;
280
281 spin_lock_irqsave(&data->lock, flags);
282 hid_set_field(report1->field[0], 0, chip << 2);
283 hid_set_field(report1->field[0], 1, 0x02);
284 hid_set_field(report1->field[0], 2, 0x00);
285 hid_set_field(report1->field[0], 3, 0x00);
286 hid_set_field(report1->field[0], 4, 0xb8 | tile);
287 hid_set_field(report1->field[0], 5, 0x00);
288 hid_set_field(report1->field[0], 6, 0x00);
289 hid_set_field(report1->field[0], 7, 0x40);
290 hid_set_field(report1->field[0], 8, 0x00);
291 hid_set_field(report1->field[0], 9, 0x00);
292 hid_set_field(report1->field[0], 10, 32);
293
294 hid_set_field(report2->field[0], 0, (chip << 2) | 0x01);
295 hid_set_field(report2->field[0], 1, 0x00);
296 hid_set_field(report2->field[0], 2, 0x00);
297 hid_set_field(report2->field[0], 3, 32);
298
299 tdata = data->fb_vbitmap + (tile * 4 + chip) * 64;
300 for (i = 0; i < 64; i++)
301 if (i < 32)
302 hid_set_field(report1->field[0], 11 + i, tdata[i]);
303 else
304 hid_set_field(report2->field[0], 4 + i - 32, tdata[i]);
305
306 usbhid_submit_report(data->hdev, report1, USB_DIR_OUT);
307 usbhid_submit_report(data->hdev, report2, USB_DIR_OUT);
308 spin_unlock_irqrestore(&data->lock, flags);
309 return 0;
310}
311
312/* Translate a single tile*/
313static int picolcd_fb_update_tile(u8 *vbitmap, const u8 *bitmap, int bpp,
314 int chip, int tile)
315{
316 int i, b, changed = 0;
317 u8 tdata[64];
318 u8 *vdata = vbitmap + (tile * 4 + chip) * 64;
319
320 if (bpp == 1) {
321 for (b = 7; b >= 0; b--) {
322 const u8 *bdata = bitmap + tile * 256 + chip * 8 + b * 32;
323 for (i = 0; i < 64; i++) {
324 tdata[i] <<= 1;
325 tdata[i] |= (bdata[i/8] >> (7 - i % 8)) & 0x01;
326 }
327 }
328 } else if (bpp == 8) {
329 for (b = 7; b >= 0; b--) {
330 const u8 *bdata = bitmap + (tile * 256 + chip * 8 + b * 32) * 8;
331 for (i = 0; i < 64; i++) {
332 tdata[i] <<= 1;
333 tdata[i] |= (bdata[i] & 0x80) ? 0x01 : 0x00;
334 }
335 }
336 } else {
337 /* Oops, we should never get here! */
338 WARN_ON(1);
339 return 0;
340 }
341
342 for (i = 0; i < 64; i++)
343 if (tdata[i] != vdata[i]) {
344 changed = 1;
345 vdata[i] = tdata[i];
346 }
347 return changed;
348}
349
350/* Reconfigure LCD display */
351static int picolcd_fb_reset(struct picolcd_data *data, int clear)
352{
353 struct hid_report *report = picolcd_out_report(REPORT_LCD_CMD, data->hdev);
354 int i, j;
355 unsigned long flags;
356 static const u8 mapcmd[8] = { 0x00, 0x02, 0x00, 0x64, 0x3f, 0x00, 0x64, 0xc0 };
357
358 if (!report || report->maxfield != 1)
359 return -ENODEV;
360
361 spin_lock_irqsave(&data->lock, flags);
362 for (i = 0; i < 4; i++) {
363 for (j = 0; j < report->field[0]->maxusage; j++)
364 if (j == 0)
365 hid_set_field(report->field[0], j, i << 2);
366 else if (j < sizeof(mapcmd))
367 hid_set_field(report->field[0], j, mapcmd[j]);
368 else
369 hid_set_field(report->field[0], j, 0);
370 usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
371 }
372
373 data->status |= PICOLCD_READY_FB;
374 spin_unlock_irqrestore(&data->lock, flags);
375
376 if (data->fb_bitmap) {
377 if (clear) {
378 memset(data->fb_vbitmap, 0xff, PICOLCDFB_SIZE);
379 memset(data->fb_bitmap, 0, PICOLCDFB_SIZE*data->fb_bpp);
380 } else {
381 /* invert 1 byte in each tile to force resend */
382 for (i = 0; i < PICOLCDFB_SIZE; i += 64)
383 data->fb_vbitmap[i] = ~data->fb_vbitmap[i];
384 }
385 }
386
387 /* schedule first output of framebuffer */
388 if (data->fb_info)
389 schedule_delayed_work(&data->fb_info->deferred_work, 0);
390
391 return 0;
392}
393
394/* Update fb_vbitmap from the screen_base and send changed tiles to device */
395static void picolcd_fb_update(struct picolcd_data *data)
396{
397 int chip, tile, n;
398 unsigned long flags;
399
400 spin_lock_irqsave(&data->lock, flags);
401 if (!(data->status & PICOLCD_READY_FB)) {
402 spin_unlock_irqrestore(&data->lock, flags);
403 picolcd_fb_reset(data, 0);
404 } else {
405 spin_unlock_irqrestore(&data->lock, flags);
406 }
407
408 /*
409 * Translate the framebuffer into the format needed by the PicoLCD.
410 * See display layout above.
411 * Do this one tile after the other and push those tiles that changed.
412 *
413 * Wait for our IO to complete as otherwise we might flood the queue!
414 */
415 n = 0;
416 for (chip = 0; chip < 4; chip++)
417 for (tile = 0; tile < 8; tile++)
418 if (picolcd_fb_update_tile(data->fb_vbitmap,
419 data->fb_bitmap, data->fb_bpp, chip, tile)) {
420 n += 2;
421 if (n >= HID_OUTPUT_FIFO_SIZE / 2) {
422 usbhid_wait_io(data->hdev);
423 n = 0;
424 }
425 picolcd_fb_send_tile(data->hdev, chip, tile);
426 }
427 if (n)
428 usbhid_wait_io(data->hdev);
429}
430
431/* Stub to call the system default and update the image on the picoLCD */
432static void picolcd_fb_fillrect(struct fb_info *info,
433 const struct fb_fillrect *rect)
434{
435 if (!info->par)
436 return;
437 sys_fillrect(info, rect);
438
439 schedule_delayed_work(&info->deferred_work, 0);
440}
441
442/* Stub to call the system default and update the image on the picoLCD */
443static void picolcd_fb_copyarea(struct fb_info *info,
444 const struct fb_copyarea *area)
445{
446 if (!info->par)
447 return;
448 sys_copyarea(info, area);
449
450 schedule_delayed_work(&info->deferred_work, 0);
451}
452
453/* Stub to call the system default and update the image on the picoLCD */
454static void picolcd_fb_imageblit(struct fb_info *info, const struct fb_image *image)
455{
456 if (!info->par)
457 return;
458 sys_imageblit(info, image);
459
460 schedule_delayed_work(&info->deferred_work, 0);
461}
462
463/*
464 * this is the slow path from userspace. they can seek and write to
465 * the fb. it's inefficient to do anything less than a full screen draw
466 */
467static ssize_t picolcd_fb_write(struct fb_info *info, const char __user *buf,
468 size_t count, loff_t *ppos)
469{
470 ssize_t ret;
471 if (!info->par)
472 return -ENODEV;
473 ret = fb_sys_write(info, buf, count, ppos);
474 if (ret >= 0)
475 schedule_delayed_work(&info->deferred_work, 0);
476 return ret;
477}
478
479static int picolcd_fb_blank(int blank, struct fb_info *info)
480{
481 if (!info->par)
482 return -ENODEV;
483 /* We let fb notification do this for us via lcd/backlight device */
484 return 0;
485}
486
487static void picolcd_fb_destroy(struct fb_info *info)
488{
489 struct picolcd_data *data = info->par;
490 info->par = NULL;
491 if (data)
492 data->fb_info = NULL;
493 fb_deferred_io_cleanup(info);
494 framebuffer_release(info);
495}
496
497static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
498{
499 __u32 bpp = var->bits_per_pixel;
500 __u32 activate = var->activate;
501
502 /* only allow 1/8 bit depth (8-bit is grayscale) */
503 *var = picolcdfb_var;
504 var->activate = activate;
505 if (bpp >= 8)
506 var->bits_per_pixel = 8;
507 else
508 var->bits_per_pixel = 1;
509 return 0;
510}
511
512static int picolcd_set_par(struct fb_info *info)
513{
514 struct picolcd_data *data = info->par;
515 u8 *o_fb, *n_fb;
516 if (info->var.bits_per_pixel == data->fb_bpp)
517 return 0;
518 /* switch between 1/8 bit depths */
519 if (info->var.bits_per_pixel != 1 && info->var.bits_per_pixel != 8)
520 return -EINVAL;
521
522 o_fb = data->fb_bitmap;
523 n_fb = vmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel);
524 if (!n_fb)
525 return -ENOMEM;
526
527 fb_deferred_io_cleanup(info);
528 /* translate FB content to new bits-per-pixel */
529 if (info->var.bits_per_pixel == 1) {
530 int i, b;
531 for (i = 0; i < PICOLCDFB_SIZE; i++) {
532 u8 p = 0;
533 for (b = 0; b < 8; b++) {
534 p <<= 1;
535 p |= o_fb[i*8+b] ? 0x01 : 0x00;
536 }
537 }
538 info->fix.visual = FB_VISUAL_MONO01;
539 info->fix.line_length = PICOLCDFB_WIDTH / 8;
540 } else {
541 int i;
542 for (i = 0; i < PICOLCDFB_SIZE * 8; i++)
543 n_fb[i] = o_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00;
544 info->fix.visual = FB_VISUAL_TRUECOLOR;
545 info->fix.line_length = PICOLCDFB_WIDTH;
546 }
547
548 data->fb_bitmap = n_fb;
549 data->fb_bpp = info->var.bits_per_pixel;
550 info->screen_base = (char __force __iomem *)n_fb;
551 info->fix.smem_start = (unsigned long)n_fb;
552 info->fix.smem_len = PICOLCDFB_SIZE*data->fb_bpp;
553 fb_deferred_io_init(info);
554 vfree(o_fb);
555 return 0;
556}
557
558/* Note this can't be const because of struct fb_info definition */
559static struct fb_ops picolcdfb_ops = {
560 .owner = THIS_MODULE,
561 .fb_destroy = picolcd_fb_destroy,
562 .fb_read = fb_sys_read,
563 .fb_write = picolcd_fb_write,
564 .fb_blank = picolcd_fb_blank,
565 .fb_fillrect = picolcd_fb_fillrect,
566 .fb_copyarea = picolcd_fb_copyarea,
567 .fb_imageblit = picolcd_fb_imageblit,
568 .fb_check_var = picolcd_fb_check_var,
569 .fb_set_par = picolcd_set_par,
570};
571
572
573/* Callback from deferred IO workqueue */
574static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist)
575{
576 picolcd_fb_update(info->par);
577}
578
579static const struct fb_deferred_io picolcd_fb_defio = {
580 .delay = HZ / PICOLCDFB_UPDATE_RATE_DEFAULT,
581 .deferred_io = picolcd_fb_deferred_io,
582};
583
584
585/*
586 * The "fb_update_rate" sysfs attribute
587 */
588static ssize_t picolcd_fb_update_rate_show(struct device *dev,
589 struct device_attribute *attr, char *buf)
590{
591 struct picolcd_data *data = dev_get_drvdata(dev);
592 unsigned i, fb_update_rate = data->fb_update_rate;
593 size_t ret = 0;
594
595 for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++)
596 if (ret >= PAGE_SIZE)
597 break;
598 else if (i == fb_update_rate)
599 ret += snprintf(buf+ret, PAGE_SIZE-ret, "[%u] ", i);
600 else
601 ret += snprintf(buf+ret, PAGE_SIZE-ret, "%u ", i);
602 if (ret > 0)
603 buf[min(ret, (size_t)PAGE_SIZE)-1] = '\n';
604 return ret;
605}
606
607static ssize_t picolcd_fb_update_rate_store(struct device *dev,
608 struct device_attribute *attr, const char *buf, size_t count)
609{
610 struct picolcd_data *data = dev_get_drvdata(dev);
611 int i;
612 unsigned u;
613
614 if (count < 1 || count > 10)
615 return -EINVAL;
616
617 i = sscanf(buf, "%u", &u);
618 if (i != 1)
619 return -EINVAL;
620
621 if (u > PICOLCDFB_UPDATE_RATE_LIMIT)
622 return -ERANGE;
623 else if (u == 0)
624 u = PICOLCDFB_UPDATE_RATE_DEFAULT;
625
626 data->fb_update_rate = u;
627 data->fb_defio.delay = HZ / data->fb_update_rate;
628 return count;
629}
630
631static DEVICE_ATTR(fb_update_rate, 0666, picolcd_fb_update_rate_show,
632 picolcd_fb_update_rate_store);
633
634/* initialize Framebuffer device */
635static int picolcd_init_framebuffer(struct picolcd_data *data)
636{
637 struct device *dev = &data->hdev->dev;
638 struct fb_info *info = NULL;
639 int error = -ENOMEM;
640 u8 *fb_vbitmap = NULL;
641 u8 *fb_bitmap = NULL;
642
643 fb_bitmap = vmalloc(PICOLCDFB_SIZE*picolcdfb_var.bits_per_pixel);
644 if (fb_bitmap == NULL) {
645 dev_err(dev, "can't get a free page for framebuffer\n");
646 goto err_nomem;
647 }
648
649 fb_vbitmap = kmalloc(PICOLCDFB_SIZE, GFP_KERNEL);
650 if (fb_vbitmap == NULL) {
651 dev_err(dev, "can't alloc vbitmap image buffer\n");
652 goto err_nomem;
653 }
654
655 data->fb_update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT;
656 data->fb_defio = picolcd_fb_defio;
657 info = framebuffer_alloc(0, dev);
658 if (info == NULL) {
659 dev_err(dev, "failed to allocate a framebuffer\n");
660 goto err_nomem;
661 }
662
663 info->fbdefio = &data->fb_defio;
664 info->screen_base = (char __force __iomem *)fb_bitmap;
665 info->fbops = &picolcdfb_ops;
666 info->var = picolcdfb_var;
667 info->fix = picolcdfb_fix;
668 info->fix.smem_len = PICOLCDFB_SIZE;
669 info->fix.smem_start = (unsigned long)fb_bitmap;
670 info->par = data;
671 info->flags = FBINFO_FLAG_DEFAULT;
672
673 data->fb_vbitmap = fb_vbitmap;
674 data->fb_bitmap = fb_bitmap;
675 data->fb_bpp = picolcdfb_var.bits_per_pixel;
676 error = picolcd_fb_reset(data, 1);
677 if (error) {
678 dev_err(dev, "failed to configure display\n");
679 goto err_cleanup;
680 }
681 error = device_create_file(dev, &dev_attr_fb_update_rate);
682 if (error) {
683 dev_err(dev, "failed to create sysfs attributes\n");
684 goto err_cleanup;
685 }
686 data->fb_info = info;
687 error = register_framebuffer(info);
688 if (error) {
689 dev_err(dev, "failed to register framebuffer\n");
690 goto err_sysfs;
691 }
692 fb_deferred_io_init(info);
693 /* schedule first output of framebuffer */
694 schedule_delayed_work(&info->deferred_work, 0);
695 return 0;
696
697err_sysfs:
698 device_remove_file(dev, &dev_attr_fb_update_rate);
699err_cleanup:
700 data->fb_vbitmap = NULL;
701 data->fb_bitmap = NULL;
702 data->fb_bpp = 0;
703 data->fb_info = NULL;
704
705err_nomem:
706 framebuffer_release(info);
707 vfree(fb_bitmap);
708 kfree(fb_vbitmap);
709 return error;
710}
711
712static void picolcd_exit_framebuffer(struct picolcd_data *data)
713{
714 struct fb_info *info = data->fb_info;
715 u8 *fb_vbitmap = data->fb_vbitmap;
716 u8 *fb_bitmap = data->fb_bitmap;
717
718 if (!info)
719 return;
720
721 data->fb_vbitmap = NULL;
722 data->fb_bitmap = NULL;
723 data->fb_bpp = 0;
724 data->fb_info = NULL;
725 device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate);
726 fb_deferred_io_cleanup(info);
727 unregister_framebuffer(info);
728 vfree(fb_bitmap);
729 kfree(fb_vbitmap);
730}
731
732
733#else
734static inline int picolcd_fb_reset(struct picolcd_data *data, int clear)
735{
736 return 0;
737}
738static inline int picolcd_init_framebuffer(struct picolcd_data *data)
739{
740 return 0;
741}
742static void picolcd_exit_framebuffer(struct picolcd_data *data)
743{
744}
745#endif /* CONFIG_FB */
746
200/* 747/*
201 * input class device 748 * input class device
202 */ 749 */
@@ -314,6 +861,7 @@ static int picolcd_reset(struct hid_device *hdev)
314 struct picolcd_data *data = hid_get_drvdata(hdev); 861 struct picolcd_data *data = hid_get_drvdata(hdev);
315 struct hid_report *report = picolcd_out_report(REPORT_RESET, hdev); 862 struct hid_report *report = picolcd_out_report(REPORT_RESET, hdev);
316 unsigned long flags; 863 unsigned long flags;
864 int error;
317 865
318 if (!data || !report || report->maxfield != 1) 866 if (!data || !report || report->maxfield != 1)
319 return -ENODEV; 867 return -ENODEV;
@@ -327,7 +875,16 @@ static int picolcd_reset(struct hid_device *hdev)
327 usbhid_submit_report(hdev, report, USB_DIR_OUT); 875 usbhid_submit_report(hdev, report, USB_DIR_OUT);
328 spin_unlock_irqrestore(&data->lock, flags); 876 spin_unlock_irqrestore(&data->lock, flags);
329 877
330 return picolcd_check_version(hdev); 878 error = picolcd_check_version(hdev);
879 if (error)
880 return error;
881
882#if defined(CONFIG_FB) || defined(CONFIG_FB_MODULE)
883 if (data->fb_info)
884 schedule_delayed_work(&data->fb_info->deferred_work, 0);
885#endif /* CONFIG_FB */
886
887 return 0;
331} 888}
332 889
333/* 890/*
@@ -1005,6 +1562,11 @@ static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)
1005 if (error) 1562 if (error)
1006 goto err; 1563 goto err;
1007 1564
1565 /* Set up the framebuffer device */
1566 error = picolcd_init_framebuffer(data);
1567 if (error)
1568 goto err;
1569
1008#ifdef CONFIG_DEBUG_FS 1570#ifdef CONFIG_DEBUG_FS
1009 report = picolcd_out_report(REPORT_READ_MEMORY, hdev); 1571 report = picolcd_out_report(REPORT_READ_MEMORY, hdev);
1010 if (report && report->maxfield == 1 && report->field[0]->report_size == 8) 1572 if (report && report->maxfield == 1 && report->field[0]->report_size == 8)
@@ -1014,6 +1576,7 @@ static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)
1014#endif 1576#endif
1015 return 0; 1577 return 0;
1016err: 1578err:
1579 picolcd_exit_framebuffer(data);
1017 picolcd_exit_cir(data); 1580 picolcd_exit_cir(data);
1018 picolcd_exit_keys(data); 1581 picolcd_exit_keys(data);
1019 return error; 1582 return error;
@@ -1143,6 +1706,8 @@ static void picolcd_remove(struct hid_device *hdev)
1143 complete(&data->pending->ready); 1706 complete(&data->pending->ready);
1144 spin_unlock_irqrestore(&data->lock, flags); 1707 spin_unlock_irqrestore(&data->lock, flags);
1145 1708
1709 /* Clean up the framebuffer */
1710 picolcd_exit_framebuffer(data);
1146 /* Cleanup input */ 1711 /* Cleanup input */
1147 picolcd_exit_cir(data); 1712 picolcd_exit_cir(data);
1148 picolcd_exit_keys(data); 1713 picolcd_exit_keys(data);