aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--Documentation/ABI/testing/sysfs-driver-hid-picolcd17
-rw-r--r--drivers/hid/Kconfig7
-rw-r--r--drivers/hid/hid-picolcd.c567
-rw-r--r--drivers/hid/usbhid/hid-core.c1
4 files changed, 590 insertions, 2 deletions
diff --git a/Documentation/ABI/testing/sysfs-driver-hid-picolcd b/Documentation/ABI/testing/sysfs-driver-hid-picolcd
index 6fb4f21469f7..14f52d70621b 100644
--- a/Documentation/ABI/testing/sysfs-driver-hid-picolcd
+++ b/Documentation/ABI/testing/sysfs-driver-hid-picolcd
@@ -15,3 +15,20 @@ Description: Make it possible to switch the PicoLCD device between LCD
15 disconnected and reconnects after above delay (default value 15 disconnected and reconnects after above delay (default value
16 is 5 seconds though this default should not be relied on). 16 is 5 seconds though this default should not be relied on).
17 17
18
19What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/fb_update_rate
20Date: March 2010
21Contact: Bruno Prémont <bonbons@linux-vserver.org>
22Description: Make it possible to adjust defio refresh rate.
23
24 Reading: returns list of available refresh rates (expressed in Hz),
25 the active refresh rate being enclosed in brackets ('[' and ']')
26
27 Writing: accepts new refresh rate expressed in integer Hz
28 within permitted rates.
29
30 Note: As device can barely do 2 complete refreshes a second
31 it only makes sense to adjust this value if only one or two
32 tiles get changed and it's not appropriate to expect the application
33 to flush it's tiny changes explicitely at higher than default rate.
34
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 138ba6a277ee..a813ea9c792d 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -265,6 +265,11 @@ config HID_PETALYNX
265config HID_PICOLCD 265config HID_PICOLCD
266 tristate "PicoLCD (graphic version)" 266 tristate "PicoLCD (graphic version)"
267 depends on USB_HID 267 depends on USB_HID
268 select FB_DEFERRED_IO if FB
269 select FB_SYS_FILLRECT if FB
270 select FB_SYS_COPYAREA if FB
271 select FB_SYS_IMAGEBLIT if FB
272 select FB_SYS_FOPS if FB
268 ---help--- 273 ---help---
269 This provides support for Minibox PicoLCD devices, currently 274 This provides support for Minibox PicoLCD devices, currently
270 only the graphical ones are supported. 275 only the graphical ones are supported.
@@ -272,8 +277,8 @@ config HID_PICOLCD
272 This includes support for the following device features: 277 This includes support for the following device features:
273 - Keypad 278 - Keypad
274 - Switching between Firmware and Flash mode 279 - Switching between Firmware and Flash mode
275 Features that are not (yet) supported:
276 - Framebuffer for monochrome 256x64 display 280 - Framebuffer for monochrome 256x64 display
281 Features that are not (yet) supported:
277 - Backlight control 282 - Backlight control
278 - Contrast control 283 - Contrast control
279 - IR 284 - IR
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);
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 56d06cd8075b..3e7909b0f129 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -623,6 +623,7 @@ int usbhid_wait_io(struct hid_device *hid)
623 623
624 return 0; 624 return 0;
625} 625}
626EXPORT_SYMBOL_GPL(usbhid_wait_io);
626 627
627static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle) 628static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle)
628{ 629{