aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/hid/hid-picolcd.h6
-rw-r--r--drivers/hid/hid-picolcd_core.c1
-rw-r--r--drivers/hid/hid-picolcd_fb.c114
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
180void picolcd_exit_framebuffer(struct picolcd_data *data); 179void picolcd_exit_framebuffer(struct picolcd_data *data);
181 180
182void picolcd_fb_unload(void);
183
184void picolcd_fb_refresh(struct picolcd_data *data); 181void 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)
195static inline void picolcd_exit_framebuffer(struct picolcd_data *data) 192static inline void picolcd_exit_framebuffer(struct picolcd_data *data)
196{ 193{
197} 194}
198static inline void picolcd_fb_unload(void)
199{
200}
201static inline void picolcd_fb_refresh(struct picolcd_data *data) 195static 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)
695static void __exit picolcd_exit(void) 695static 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
701module_init(picolcd_init); 700module_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
328static void picolcd_fb_destroy(struct fb_info *info) 328static 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
350static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 343static 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 */
422struct picolcd_fb_cleanup_item {
423 struct fb_info *info;
424 struct picolcd_fb_cleanup_item *next;
425};
426static struct picolcd_fb_cleanup_item *fb_pending;
427static DEFINE_SPINLOCK(fb_pending_lock);
428
429static 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
452static DECLARE_WORK(picolcd_fb_cleanup, picolcd_fb_do_cleanup);
453
454static 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
463static 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 */
483static struct fb_ops picolcdfb_ops = { 411static 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
669void picolcd_fb_unload()
670{
671 flush_work_sync(&picolcd_fb_cleanup);
672 WARN_ON(fb_pending);
673}