diff options
-rw-r--r-- | drivers/hid/hid-picolcd.h | 6 | ||||
-rw-r--r-- | drivers/hid/hid-picolcd_core.c | 1 | ||||
-rw-r--r-- | drivers/hid/hid-picolcd_fb.c | 114 |
3 files changed, 18 insertions, 103 deletions
diff --git a/drivers/hid/hid-picolcd.h b/drivers/hid/hid-picolcd.h index 4ddb3188d5c8..9200be165526 100644 --- a/drivers/hid/hid-picolcd.h +++ b/drivers/hid/hid-picolcd.h | |||
@@ -96,7 +96,6 @@ struct picolcd_data { | |||
96 | u8 *fb_vbitmap; /* local copy of what was sent to PicoLCD */ | 96 | u8 *fb_vbitmap; /* local copy of what was sent to PicoLCD */ |
97 | u8 *fb_bitmap; /* framebuffer */ | 97 | u8 *fb_bitmap; /* framebuffer */ |
98 | struct fb_info *fb_info; | 98 | struct fb_info *fb_info; |
99 | struct fb_deferred_io fb_defio; | ||
100 | #endif /* CONFIG_HID_PICOLCD_FB */ | 99 | #endif /* CONFIG_HID_PICOLCD_FB */ |
101 | #ifdef CONFIG_HID_PICOLCD_LCD | 100 | #ifdef CONFIG_HID_PICOLCD_LCD |
102 | struct lcd_device *lcd; | 101 | struct lcd_device *lcd; |
@@ -179,8 +178,6 @@ int picolcd_init_framebuffer(struct picolcd_data *data); | |||
179 | 178 | ||
180 | void picolcd_exit_framebuffer(struct picolcd_data *data); | 179 | void picolcd_exit_framebuffer(struct picolcd_data *data); |
181 | 180 | ||
182 | void picolcd_fb_unload(void); | ||
183 | |||
184 | void picolcd_fb_refresh(struct picolcd_data *data); | 181 | void picolcd_fb_refresh(struct picolcd_data *data); |
185 | #define picolcd_fbinfo(d) ((d)->fb_info) | 182 | #define picolcd_fbinfo(d) ((d)->fb_info) |
186 | #else | 183 | #else |
@@ -195,9 +192,6 @@ static inline int picolcd_init_framebuffer(struct picolcd_data *data) | |||
195 | static inline void picolcd_exit_framebuffer(struct picolcd_data *data) | 192 | static inline void picolcd_exit_framebuffer(struct picolcd_data *data) |
196 | { | 193 | { |
197 | } | 194 | } |
198 | static inline void picolcd_fb_unload(void) | ||
199 | { | ||
200 | } | ||
201 | static inline void picolcd_fb_refresh(struct picolcd_data *data) | 195 | static inline void picolcd_fb_refresh(struct picolcd_data *data) |
202 | { | 196 | { |
203 | } | 197 | } |
diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c index 36e7548a6e19..8d5d341adeab 100644 --- a/drivers/hid/hid-picolcd_core.c +++ b/drivers/hid/hid-picolcd_core.c | |||
@@ -695,7 +695,6 @@ static int __init picolcd_init(void) | |||
695 | static void __exit picolcd_exit(void) | 695 | static void __exit picolcd_exit(void) |
696 | { | 696 | { |
697 | hid_unregister_driver(&picolcd_driver); | 697 | hid_unregister_driver(&picolcd_driver); |
698 | picolcd_fb_unload(); | ||
699 | } | 698 | } |
700 | 699 | ||
701 | module_init(picolcd_init); | 700 | module_init(picolcd_init); |
diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c index b2124f566c61..602786c18b72 100644 --- a/drivers/hid/hid-picolcd_fb.c +++ b/drivers/hid/hid-picolcd_fb.c | |||
@@ -256,7 +256,7 @@ static void picolcd_fb_update(struct picolcd_data *data) | |||
256 | data->fb_bitmap, data->fb_bpp, chip, tile) || | 256 | data->fb_bitmap, data->fb_bpp, chip, tile) || |
257 | data->fb_force) { | 257 | data->fb_force) { |
258 | n += 2; | 258 | n += 2; |
259 | if (!data->fb_info->par) | 259 | if (data->status & PICOLCD_FAILED) |
260 | return; /* device lost! */ | 260 | return; /* device lost! */ |
261 | if (n >= HID_OUTPUT_FIFO_SIZE / 2) { | 261 | if (n >= HID_OUTPUT_FIFO_SIZE / 2) { |
262 | usbhid_wait_io(data->hdev); | 262 | usbhid_wait_io(data->hdev); |
@@ -327,24 +327,17 @@ static int picolcd_fb_blank(int blank, struct fb_info *info) | |||
327 | 327 | ||
328 | static void picolcd_fb_destroy(struct fb_info *info) | 328 | static void picolcd_fb_destroy(struct fb_info *info) |
329 | { | 329 | { |
330 | struct picolcd_data *data = info->par; | 330 | struct picolcd_data *data; |
331 | u32 *ref_cnt = info->pseudo_palette; | ||
332 | int may_release; | ||
333 | 331 | ||
332 | /* make sure no work is deferred */ | ||
333 | cancel_delayed_work_sync(&info->deferred_work); | ||
334 | data = info->par; | ||
334 | info->par = NULL; | 335 | info->par = NULL; |
335 | if (data) | 336 | if (data) |
336 | data->fb_info = NULL; | 337 | data->fb_info = NULL; |
337 | fb_deferred_io_cleanup(info); | ||
338 | 338 | ||
339 | ref_cnt--; | 339 | vfree((u8 *)info->fix.smem_start); |
340 | mutex_lock(&info->lock); | 340 | framebuffer_release(info); |
341 | (*ref_cnt)--; | ||
342 | may_release = !*ref_cnt; | ||
343 | mutex_unlock(&info->lock); | ||
344 | if (may_release) { | ||
345 | vfree((u8 *)info->fix.smem_start); | ||
346 | framebuffer_release(info); | ||
347 | } | ||
348 | } | 341 | } |
349 | 342 | ||
350 | static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | 343 | static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) |
@@ -414,77 +407,10 @@ static int picolcd_set_par(struct fb_info *info) | |||
414 | return 0; | 407 | return 0; |
415 | } | 408 | } |
416 | 409 | ||
417 | /* Do refcounting on our FB and cleanup per worker if FB is | ||
418 | * closed after unplug of our device | ||
419 | * (fb_release holds info->lock and still touches info after | ||
420 | * we return so we can't release it immediately. | ||
421 | */ | ||
422 | struct picolcd_fb_cleanup_item { | ||
423 | struct fb_info *info; | ||
424 | struct picolcd_fb_cleanup_item *next; | ||
425 | }; | ||
426 | static struct picolcd_fb_cleanup_item *fb_pending; | ||
427 | static DEFINE_SPINLOCK(fb_pending_lock); | ||
428 | |||
429 | static void picolcd_fb_do_cleanup(struct work_struct *data) | ||
430 | { | ||
431 | struct picolcd_fb_cleanup_item *item; | ||
432 | unsigned long flags; | ||
433 | |||
434 | do { | ||
435 | spin_lock_irqsave(&fb_pending_lock, flags); | ||
436 | item = fb_pending; | ||
437 | fb_pending = item ? item->next : NULL; | ||
438 | spin_unlock_irqrestore(&fb_pending_lock, flags); | ||
439 | |||
440 | if (item) { | ||
441 | u8 *fb = (u8 *)item->info->fix.smem_start; | ||
442 | /* make sure we do not race against fb core when | ||
443 | * releasing */ | ||
444 | mutex_lock(&item->info->lock); | ||
445 | mutex_unlock(&item->info->lock); | ||
446 | framebuffer_release(item->info); | ||
447 | vfree(fb); | ||
448 | } | ||
449 | } while (item); | ||
450 | } | ||
451 | |||
452 | static DECLARE_WORK(picolcd_fb_cleanup, picolcd_fb_do_cleanup); | ||
453 | |||
454 | static int picolcd_fb_open(struct fb_info *info, int u) | ||
455 | { | ||
456 | u32 *ref_cnt = info->pseudo_palette; | ||
457 | ref_cnt--; | ||
458 | |||
459 | (*ref_cnt)++; | ||
460 | return 0; | ||
461 | } | ||
462 | |||
463 | static int picolcd_fb_release(struct fb_info *info, int u) | ||
464 | { | ||
465 | u32 *ref_cnt = info->pseudo_palette; | ||
466 | ref_cnt--; | ||
467 | |||
468 | (*ref_cnt)++; | ||
469 | if (!*ref_cnt) { | ||
470 | unsigned long flags; | ||
471 | struct picolcd_fb_cleanup_item *item = (struct picolcd_fb_cleanup_item *)ref_cnt; | ||
472 | item--; | ||
473 | spin_lock_irqsave(&fb_pending_lock, flags); | ||
474 | item->next = fb_pending; | ||
475 | fb_pending = item; | ||
476 | spin_unlock_irqrestore(&fb_pending_lock, flags); | ||
477 | schedule_work(&picolcd_fb_cleanup); | ||
478 | } | ||
479 | return 0; | ||
480 | } | ||
481 | |||
482 | /* Note this can't be const because of struct fb_info definition */ | 410 | /* Note this can't be const because of struct fb_info definition */ |
483 | static struct fb_ops picolcdfb_ops = { | 411 | static struct fb_ops picolcdfb_ops = { |
484 | .owner = THIS_MODULE, | 412 | .owner = THIS_MODULE, |
485 | .fb_destroy = picolcd_fb_destroy, | 413 | .fb_destroy = picolcd_fb_destroy, |
486 | .fb_open = picolcd_fb_open, | ||
487 | .fb_release = picolcd_fb_release, | ||
488 | .fb_read = fb_sys_read, | 414 | .fb_read = fb_sys_read, |
489 | .fb_write = picolcd_fb_write, | 415 | .fb_write = picolcd_fb_write, |
490 | .fb_blank = picolcd_fb_blank, | 416 | .fb_blank = picolcd_fb_blank, |
@@ -550,7 +476,7 @@ static ssize_t picolcd_fb_update_rate_store(struct device *dev, | |||
550 | u = PICOLCDFB_UPDATE_RATE_DEFAULT; | 476 | u = PICOLCDFB_UPDATE_RATE_DEFAULT; |
551 | 477 | ||
552 | data->fb_update_rate = u; | 478 | data->fb_update_rate = u; |
553 | data->fb_defio.delay = HZ / data->fb_update_rate; | 479 | data->fb_info->fbdefio->delay = HZ / data->fb_update_rate; |
554 | return count; | 480 | return count; |
555 | } | 481 | } |
556 | 482 | ||
@@ -580,25 +506,23 @@ int picolcd_init_framebuffer(struct picolcd_data *data) | |||
580 | } | 506 | } |
581 | 507 | ||
582 | data->fb_update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT; | 508 | data->fb_update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT; |
583 | data->fb_defio = picolcd_fb_defio; | ||
584 | /* The extra memory is: | 509 | /* The extra memory is: |
585 | * - struct picolcd_fb_cleanup_item | ||
586 | * - u32 for ref_count | ||
587 | * - 256*u32 for pseudo_palette | 510 | * - 256*u32 for pseudo_palette |
511 | * - struct fb_deferred_io | ||
588 | */ | 512 | */ |
589 | info = framebuffer_alloc(257 * sizeof(u32) + sizeof(struct picolcd_fb_cleanup_item), dev); | 513 | info = framebuffer_alloc(256 * sizeof(u32) + |
514 | sizeof(struct fb_deferred_io), dev); | ||
590 | if (info == NULL) { | 515 | if (info == NULL) { |
591 | dev_err(dev, "failed to allocate a framebuffer\n"); | 516 | dev_err(dev, "failed to allocate a framebuffer\n"); |
592 | goto err_nomem; | 517 | goto err_nomem; |
593 | } | 518 | } |
594 | 519 | ||
595 | palette = info->par + sizeof(struct picolcd_fb_cleanup_item); | 520 | info->fbdefio = info->par; |
596 | *palette = 1; | 521 | *info->fbdefio = picolcd_fb_defio; |
597 | palette++; | 522 | palette = info->par + sizeof(struct fb_deferred_io); |
598 | for (i = 0; i < 256; i++) | 523 | for (i = 0; i < 256; i++) |
599 | palette[i] = i > 0 && i < 16 ? 0xff : 0; | 524 | palette[i] = i > 0 && i < 16 ? 0xff : 0; |
600 | info->pseudo_palette = palette; | 525 | info->pseudo_palette = palette; |
601 | info->fbdefio = &data->fb_defio; | ||
602 | info->screen_base = (char __force __iomem *)fb_bitmap; | 526 | info->screen_base = (char __force __iomem *)fb_bitmap; |
603 | info->fbops = &picolcdfb_ops; | 527 | info->fbops = &picolcdfb_ops; |
604 | info->var = picolcdfb_var; | 528 | info->var = picolcdfb_var; |
@@ -658,6 +582,10 @@ void picolcd_exit_framebuffer(struct picolcd_data *data) | |||
658 | return; | 582 | return; |
659 | 583 | ||
660 | device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate); | 584 | device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate); |
585 | mutex_lock(&info->lock); | ||
586 | fb_deferred_io_cleanup(info); | ||
587 | info->par = NULL; | ||
588 | mutex_unlock(&info->lock); | ||
661 | unregister_framebuffer(info); | 589 | unregister_framebuffer(info); |
662 | data->fb_vbitmap = NULL; | 590 | data->fb_vbitmap = NULL; |
663 | data->fb_bitmap = NULL; | 591 | data->fb_bitmap = NULL; |
@@ -665,9 +593,3 @@ void picolcd_exit_framebuffer(struct picolcd_data *data) | |||
665 | data->fb_info = NULL; | 593 | data->fb_info = NULL; |
666 | kfree(fb_vbitmap); | 594 | kfree(fb_vbitmap); |
667 | } | 595 | } |
668 | |||
669 | void picolcd_fb_unload() | ||
670 | { | ||
671 | flush_work_sync(&picolcd_fb_cleanup); | ||
672 | WARN_ON(fb_pending); | ||
673 | } | ||