diff options
Diffstat (limited to 'drivers/hid/hid-picolcd_fb.c')
-rw-r--r-- | drivers/hid/hid-picolcd_fb.c | 216 |
1 files changed, 113 insertions, 103 deletions
diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c index cf295c56917..0008a512211 100644 --- a/drivers/hid/hid-picolcd_fb.c +++ b/drivers/hid/hid-picolcd_fb.c | |||
@@ -99,19 +99,26 @@ static const struct fb_var_screeninfo picolcdfb_var = { | |||
99 | }; | 99 | }; |
100 | 100 | ||
101 | /* Send a given tile to PicoLCD */ | 101 | /* Send a given tile to PicoLCD */ |
102 | static int picolcd_fb_send_tile(struct hid_device *hdev, int chip, int tile) | 102 | static int picolcd_fb_send_tile(struct picolcd_data *data, u8 *vbitmap, |
103 | int chip, int tile) | ||
103 | { | 104 | { |
104 | struct picolcd_data *data = hid_get_drvdata(hdev); | 105 | struct hid_report *report1, *report2; |
105 | struct hid_report *report1 = picolcd_out_report(REPORT_LCD_CMD_DATA, hdev); | ||
106 | struct hid_report *report2 = picolcd_out_report(REPORT_LCD_DATA, hdev); | ||
107 | unsigned long flags; | 106 | unsigned long flags; |
108 | u8 *tdata; | 107 | u8 *tdata; |
109 | int i; | 108 | int i; |
110 | 109 | ||
111 | if (!report1 || report1->maxfield != 1 || !report2 || report2->maxfield != 1) | 110 | report1 = picolcd_out_report(REPORT_LCD_CMD_DATA, data->hdev); |
111 | if (!report1 || report1->maxfield != 1) | ||
112 | return -ENODEV; | ||
113 | report2 = picolcd_out_report(REPORT_LCD_DATA, data->hdev); | ||
114 | if (!report2 || report2->maxfield != 1) | ||
112 | return -ENODEV; | 115 | return -ENODEV; |
113 | 116 | ||
114 | spin_lock_irqsave(&data->lock, flags); | 117 | spin_lock_irqsave(&data->lock, flags); |
118 | if ((data->status & PICOLCD_FAILED)) { | ||
119 | spin_unlock_irqrestore(&data->lock, flags); | ||
120 | return -ENODEV; | ||
121 | } | ||
115 | hid_set_field(report1->field[0], 0, chip << 2); | 122 | hid_set_field(report1->field[0], 0, chip << 2); |
116 | hid_set_field(report1->field[0], 1, 0x02); | 123 | hid_set_field(report1->field[0], 1, 0x02); |
117 | hid_set_field(report1->field[0], 2, 0x00); | 124 | hid_set_field(report1->field[0], 2, 0x00); |
@@ -129,7 +136,7 @@ static int picolcd_fb_send_tile(struct hid_device *hdev, int chip, int tile) | |||
129 | hid_set_field(report2->field[0], 2, 0x00); | 136 | hid_set_field(report2->field[0], 2, 0x00); |
130 | hid_set_field(report2->field[0], 3, 32); | 137 | hid_set_field(report2->field[0], 3, 32); |
131 | 138 | ||
132 | tdata = data->fb_vbitmap + (tile * 4 + chip) * 64; | 139 | tdata = vbitmap + (tile * 4 + chip) * 64; |
133 | for (i = 0; i < 64; i++) | 140 | for (i = 0; i < 64; i++) |
134 | if (i < 32) | 141 | if (i < 32) |
135 | hid_set_field(report1->field[0], 11 + i, tdata[i]); | 142 | hid_set_field(report1->field[0], 11 + i, tdata[i]); |
@@ -190,6 +197,7 @@ void picolcd_fb_refresh(struct picolcd_data *data) | |||
190 | int picolcd_fb_reset(struct picolcd_data *data, int clear) | 197 | int picolcd_fb_reset(struct picolcd_data *data, int clear) |
191 | { | 198 | { |
192 | struct hid_report *report = picolcd_out_report(REPORT_LCD_CMD, data->hdev); | 199 | struct hid_report *report = picolcd_out_report(REPORT_LCD_CMD, data->hdev); |
200 | struct picolcd_fb_data *fbdata = data->fb_info->par; | ||
193 | int i, j; | 201 | int i, j; |
194 | unsigned long flags; | 202 | unsigned long flags; |
195 | static const u8 mapcmd[8] = { 0x00, 0x02, 0x00, 0x64, 0x3f, 0x00, 0x64, 0xc0 }; | 203 | static const u8 mapcmd[8] = { 0x00, 0x02, 0x00, 0x64, 0x3f, 0x00, 0x64, 0xc0 }; |
@@ -208,20 +216,19 @@ int picolcd_fb_reset(struct picolcd_data *data, int clear) | |||
208 | hid_set_field(report->field[0], j, 0); | 216 | hid_set_field(report->field[0], j, 0); |
209 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | 217 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); |
210 | } | 218 | } |
211 | |||
212 | data->status |= PICOLCD_READY_FB; | ||
213 | spin_unlock_irqrestore(&data->lock, flags); | 219 | spin_unlock_irqrestore(&data->lock, flags); |
214 | 220 | ||
215 | if (data->fb_bitmap) { | 221 | if (clear) { |
216 | if (clear) { | 222 | memset(fbdata->vbitmap, 0, PICOLCDFB_SIZE); |
217 | memset(data->fb_vbitmap, 0, PICOLCDFB_SIZE); | 223 | memset(fbdata->bitmap, 0, PICOLCDFB_SIZE*fbdata->bpp); |
218 | memset(data->fb_bitmap, 0, PICOLCDFB_SIZE*data->fb_bpp); | ||
219 | } | ||
220 | data->fb_force = 1; | ||
221 | } | 224 | } |
225 | fbdata->force = 1; | ||
222 | 226 | ||
223 | /* schedule first output of framebuffer */ | 227 | /* schedule first output of framebuffer */ |
224 | picolcd_fb_refresh(data); | 228 | if (fbdata->ready) |
229 | schedule_delayed_work(&data->fb_info->deferred_work, 0); | ||
230 | else | ||
231 | fbdata->ready = 1; | ||
225 | 232 | ||
226 | return 0; | 233 | return 0; |
227 | } | 234 | } |
@@ -231,20 +238,15 @@ static void picolcd_fb_update(struct fb_info *info) | |||
231 | { | 238 | { |
232 | int chip, tile, n; | 239 | int chip, tile, n; |
233 | unsigned long flags; | 240 | unsigned long flags; |
241 | struct picolcd_fb_data *fbdata = info->par; | ||
234 | struct picolcd_data *data; | 242 | struct picolcd_data *data; |
235 | 243 | ||
236 | mutex_lock(&info->lock); | 244 | mutex_lock(&info->lock); |
237 | data = info->par; | ||
238 | if (!data) | ||
239 | goto out; | ||
240 | 245 | ||
241 | spin_lock_irqsave(&data->lock, flags); | 246 | spin_lock_irqsave(&fbdata->lock, flags); |
242 | if (!(data->status & PICOLCD_READY_FB)) { | 247 | if (!fbdata->ready && fbdata->picolcd) |
243 | spin_unlock_irqrestore(&data->lock, flags); | 248 | picolcd_fb_reset(fbdata->picolcd, 0); |
244 | picolcd_fb_reset(data, 0); | 249 | spin_unlock_irqrestore(&fbdata->lock, flags); |
245 | } else { | ||
246 | spin_unlock_irqrestore(&data->lock, flags); | ||
247 | } | ||
248 | 250 | ||
249 | /* | 251 | /* |
250 | * Translate the framebuffer into the format needed by the PicoLCD. | 252 | * Translate the framebuffer into the format needed by the PicoLCD. |
@@ -255,32 +257,38 @@ static void picolcd_fb_update(struct fb_info *info) | |||
255 | */ | 257 | */ |
256 | n = 0; | 258 | n = 0; |
257 | for (chip = 0; chip < 4; chip++) | 259 | for (chip = 0; chip < 4; chip++) |
258 | for (tile = 0; tile < 8; tile++) | 260 | for (tile = 0; tile < 8; tile++) { |
259 | if (picolcd_fb_update_tile(data->fb_vbitmap, | 261 | if (!fbdata->force && !picolcd_fb_update_tile( |
260 | data->fb_bitmap, data->fb_bpp, chip, tile) || | 262 | fbdata->vbitmap, fbdata->bitmap, |
261 | data->fb_force) { | 263 | fbdata->bpp, chip, tile)) |
262 | n += 2; | 264 | continue; |
263 | if (n >= HID_OUTPUT_FIFO_SIZE / 2) { | 265 | n += 2; |
264 | mutex_unlock(&info->lock); | 266 | if (n >= HID_OUTPUT_FIFO_SIZE / 2) { |
265 | usbhid_wait_io(data->hdev); | 267 | spin_lock_irqsave(&fbdata->lock, flags); |
266 | mutex_lock(&info->lock); | 268 | data = fbdata->picolcd; |
267 | data = info->par; | 269 | spin_unlock_irqrestore(&fbdata->lock, flags); |
268 | if (!data) | 270 | mutex_unlock(&info->lock); |
269 | goto out; | 271 | if (!data) |
270 | spin_lock_irqsave(&data->lock, flags); | 272 | return; |
271 | if (data->status & PICOLCD_FAILED) { | 273 | usbhid_wait_io(data->hdev); |
272 | spin_unlock_irqrestore(&data->lock, flags); | 274 | mutex_lock(&info->lock); |
273 | goto out; | 275 | n = 0; |
274 | } | ||
275 | spin_unlock_irqrestore(&data->lock, flags); | ||
276 | n = 0; | ||
277 | } | ||
278 | picolcd_fb_send_tile(data->hdev, chip, tile); | ||
279 | } | 276 | } |
280 | data->fb_force = false; | 277 | spin_lock_irqsave(&fbdata->lock, flags); |
278 | data = fbdata->picolcd; | ||
279 | spin_unlock_irqrestore(&fbdata->lock, flags); | ||
280 | if (!data || picolcd_fb_send_tile(data, | ||
281 | fbdata->vbitmap, chip, tile)) | ||
282 | goto out; | ||
283 | } | ||
284 | fbdata->force = false; | ||
281 | if (n) { | 285 | if (n) { |
286 | spin_lock_irqsave(&fbdata->lock, flags); | ||
287 | data = fbdata->picolcd; | ||
288 | spin_unlock_irqrestore(&fbdata->lock, flags); | ||
282 | mutex_unlock(&info->lock); | 289 | mutex_unlock(&info->lock); |
283 | usbhid_wait_io(data->hdev); | 290 | if (data) |
291 | usbhid_wait_io(data->hdev); | ||
284 | return; | 292 | return; |
285 | } | 293 | } |
286 | out: | 294 | out: |
@@ -337,20 +345,22 @@ static ssize_t picolcd_fb_write(struct fb_info *info, const char __user *buf, | |||
337 | 345 | ||
338 | static int picolcd_fb_blank(int blank, struct fb_info *info) | 346 | static int picolcd_fb_blank(int blank, struct fb_info *info) |
339 | { | 347 | { |
340 | if (!info->par) | ||
341 | return -ENODEV; | ||
342 | /* We let fb notification do this for us via lcd/backlight device */ | 348 | /* We let fb notification do this for us via lcd/backlight device */ |
343 | return 0; | 349 | return 0; |
344 | } | 350 | } |
345 | 351 | ||
346 | static void picolcd_fb_destroy(struct fb_info *info) | 352 | static void picolcd_fb_destroy(struct fb_info *info) |
347 | { | 353 | { |
354 | struct picolcd_fb_data *fbdata = info->par; | ||
355 | |||
348 | /* make sure no work is deferred */ | 356 | /* make sure no work is deferred */ |
349 | fb_deferred_io_cleanup(info); | 357 | fb_deferred_io_cleanup(info); |
350 | 358 | ||
359 | /* No thridparty should ever unregister our framebuffer! */ | ||
360 | WARN_ON(fbdata->picolcd != NULL); | ||
361 | |||
351 | vfree((u8 *)info->fix.smem_start); | 362 | vfree((u8 *)info->fix.smem_start); |
352 | framebuffer_release(info); | 363 | framebuffer_release(info); |
353 | printk(KERN_DEBUG "picolcd_fb_destroy(%p)\n", info); | ||
354 | } | 364 | } |
355 | 365 | ||
356 | static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | 366 | static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) |
@@ -377,17 +387,15 @@ static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *i | |||
377 | 387 | ||
378 | static int picolcd_set_par(struct fb_info *info) | 388 | static int picolcd_set_par(struct fb_info *info) |
379 | { | 389 | { |
380 | struct picolcd_data *data = info->par; | 390 | struct picolcd_fb_data *fbdata = info->par; |
381 | u8 *tmp_fb, *o_fb; | 391 | u8 *tmp_fb, *o_fb; |
382 | if (!data) | 392 | if (info->var.bits_per_pixel == fbdata->bpp) |
383 | return -ENODEV; | ||
384 | if (info->var.bits_per_pixel == data->fb_bpp) | ||
385 | return 0; | 393 | return 0; |
386 | /* switch between 1/8 bit depths */ | 394 | /* switch between 1/8 bit depths */ |
387 | if (info->var.bits_per_pixel != 1 && info->var.bits_per_pixel != 8) | 395 | if (info->var.bits_per_pixel != 1 && info->var.bits_per_pixel != 8) |
388 | return -EINVAL; | 396 | return -EINVAL; |
389 | 397 | ||
390 | o_fb = data->fb_bitmap; | 398 | o_fb = fbdata->bitmap; |
391 | tmp_fb = kmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel, GFP_KERNEL); | 399 | tmp_fb = kmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel, GFP_KERNEL); |
392 | if (!tmp_fb) | 400 | if (!tmp_fb) |
393 | return -ENOMEM; | 401 | return -ENOMEM; |
@@ -416,7 +424,7 @@ static int picolcd_set_par(struct fb_info *info) | |||
416 | } | 424 | } |
417 | 425 | ||
418 | kfree(tmp_fb); | 426 | kfree(tmp_fb); |
419 | data->fb_bpp = info->var.bits_per_pixel; | 427 | fbdata->bpp = info->var.bits_per_pixel; |
420 | return 0; | 428 | return 0; |
421 | } | 429 | } |
422 | 430 | ||
@@ -454,7 +462,8 @@ static ssize_t picolcd_fb_update_rate_show(struct device *dev, | |||
454 | struct device_attribute *attr, char *buf) | 462 | struct device_attribute *attr, char *buf) |
455 | { | 463 | { |
456 | struct picolcd_data *data = dev_get_drvdata(dev); | 464 | struct picolcd_data *data = dev_get_drvdata(dev); |
457 | unsigned i, fb_update_rate = data->fb_update_rate; | 465 | struct picolcd_fb_data *fbdata = data->fb_info->par; |
466 | unsigned i, fb_update_rate = fbdata->update_rate; | ||
458 | size_t ret = 0; | 467 | size_t ret = 0; |
459 | 468 | ||
460 | for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++) | 469 | for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++) |
@@ -473,6 +482,7 @@ static ssize_t picolcd_fb_update_rate_store(struct device *dev, | |||
473 | struct device_attribute *attr, const char *buf, size_t count) | 482 | struct device_attribute *attr, const char *buf, size_t count) |
474 | { | 483 | { |
475 | struct picolcd_data *data = dev_get_drvdata(dev); | 484 | struct picolcd_data *data = dev_get_drvdata(dev); |
485 | struct picolcd_fb_data *fbdata = data->fb_info->par; | ||
476 | int i; | 486 | int i; |
477 | unsigned u; | 487 | unsigned u; |
478 | 488 | ||
@@ -488,8 +498,8 @@ static ssize_t picolcd_fb_update_rate_store(struct device *dev, | |||
488 | else if (u == 0) | 498 | else if (u == 0) |
489 | u = PICOLCDFB_UPDATE_RATE_DEFAULT; | 499 | u = PICOLCDFB_UPDATE_RATE_DEFAULT; |
490 | 500 | ||
491 | data->fb_update_rate = u; | 501 | fbdata->update_rate = u; |
492 | data->fb_info->fbdefio->delay = HZ / data->fb_update_rate; | 502 | data->fb_info->fbdefio->delay = HZ / fbdata->update_rate; |
493 | return count; | 503 | return count; |
494 | } | 504 | } |
495 | 505 | ||
@@ -501,30 +511,18 @@ int picolcd_init_framebuffer(struct picolcd_data *data) | |||
501 | { | 511 | { |
502 | struct device *dev = &data->hdev->dev; | 512 | struct device *dev = &data->hdev->dev; |
503 | struct fb_info *info = NULL; | 513 | struct fb_info *info = NULL; |
514 | struct picolcd_fb_data *fbdata = NULL; | ||
504 | int i, error = -ENOMEM; | 515 | int i, error = -ENOMEM; |
505 | u8 *fb_vbitmap = NULL; | ||
506 | u8 *fb_bitmap = NULL; | ||
507 | u32 *palette; | 516 | u32 *palette; |
508 | 517 | ||
509 | fb_bitmap = vmalloc(PICOLCDFB_SIZE*8); | ||
510 | if (fb_bitmap == NULL) { | ||
511 | dev_err(dev, "can't get a free page for framebuffer\n"); | ||
512 | goto err_nomem; | ||
513 | } | ||
514 | |||
515 | fb_vbitmap = kmalloc(PICOLCDFB_SIZE, GFP_KERNEL); | ||
516 | if (fb_vbitmap == NULL) { | ||
517 | dev_err(dev, "can't alloc vbitmap image buffer\n"); | ||
518 | goto err_nomem; | ||
519 | } | ||
520 | |||
521 | data->fb_update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT; | ||
522 | /* The extra memory is: | 518 | /* The extra memory is: |
523 | * - 256*u32 for pseudo_palette | 519 | * - 256*u32 for pseudo_palette |
524 | * - struct fb_deferred_io | 520 | * - struct fb_deferred_io |
525 | */ | 521 | */ |
526 | info = framebuffer_alloc(256 * sizeof(u32) + | 522 | info = framebuffer_alloc(256 * sizeof(u32) + |
527 | sizeof(struct fb_deferred_io), dev); | 523 | sizeof(struct fb_deferred_io) + |
524 | sizeof(struct picolcd_fb_data) + | ||
525 | PICOLCDFB_SIZE, dev); | ||
528 | if (info == NULL) { | 526 | if (info == NULL) { |
529 | dev_err(dev, "failed to allocate a framebuffer\n"); | 527 | dev_err(dev, "failed to allocate a framebuffer\n"); |
530 | goto err_nomem; | 528 | goto err_nomem; |
@@ -532,74 +530,86 @@ int picolcd_init_framebuffer(struct picolcd_data *data) | |||
532 | 530 | ||
533 | info->fbdefio = info->par; | 531 | info->fbdefio = info->par; |
534 | *info->fbdefio = picolcd_fb_defio; | 532 | *info->fbdefio = picolcd_fb_defio; |
535 | palette = info->par + sizeof(struct fb_deferred_io); | 533 | info->par += sizeof(struct fb_deferred_io); |
534 | palette = info->par; | ||
535 | info->par += 256 * sizeof(u32); | ||
536 | for (i = 0; i < 256; i++) | 536 | for (i = 0; i < 256; i++) |
537 | palette[i] = i > 0 && i < 16 ? 0xff : 0; | 537 | palette[i] = i > 0 && i < 16 ? 0xff : 0; |
538 | info->pseudo_palette = palette; | 538 | info->pseudo_palette = palette; |
539 | info->screen_base = (char __force __iomem *)fb_bitmap; | ||
540 | info->fbops = &picolcdfb_ops; | 539 | info->fbops = &picolcdfb_ops; |
541 | info->var = picolcdfb_var; | 540 | info->var = picolcdfb_var; |
542 | info->fix = picolcdfb_fix; | 541 | info->fix = picolcdfb_fix; |
543 | info->fix.smem_len = PICOLCDFB_SIZE*8; | 542 | info->fix.smem_len = PICOLCDFB_SIZE*8; |
544 | info->fix.smem_start = (unsigned long)fb_bitmap; | ||
545 | info->par = data; | ||
546 | info->flags = FBINFO_FLAG_DEFAULT; | 543 | info->flags = FBINFO_FLAG_DEFAULT; |
547 | 544 | ||
548 | data->fb_vbitmap = fb_vbitmap; | 545 | fbdata = info->par; |
549 | data->fb_bitmap = fb_bitmap; | 546 | spin_lock_init(&fbdata->lock); |
550 | data->fb_bpp = picolcdfb_var.bits_per_pixel; | 547 | fbdata->picolcd = data; |
548 | fbdata->update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT; | ||
549 | fbdata->bpp = picolcdfb_var.bits_per_pixel; | ||
550 | fbdata->force = 1; | ||
551 | fbdata->vbitmap = info->par + sizeof(struct picolcd_fb_data); | ||
552 | fbdata->bitmap = vmalloc(PICOLCDFB_SIZE*8); | ||
553 | if (fbdata->bitmap == NULL) { | ||
554 | dev_err(dev, "can't get a free page for framebuffer\n"); | ||
555 | goto err_nomem; | ||
556 | } | ||
557 | info->screen_base = (char __force __iomem *)fbdata->bitmap; | ||
558 | info->fix.smem_start = (unsigned long)fbdata->bitmap; | ||
559 | memset(fbdata->vbitmap, 0xff, PICOLCDFB_SIZE); | ||
560 | data->fb_info = info; | ||
561 | |||
551 | error = picolcd_fb_reset(data, 1); | 562 | error = picolcd_fb_reset(data, 1); |
552 | if (error) { | 563 | if (error) { |
553 | dev_err(dev, "failed to configure display\n"); | 564 | dev_err(dev, "failed to configure display\n"); |
554 | goto err_cleanup; | 565 | goto err_cleanup; |
555 | } | 566 | } |
567 | |||
556 | error = device_create_file(dev, &dev_attr_fb_update_rate); | 568 | error = device_create_file(dev, &dev_attr_fb_update_rate); |
557 | if (error) { | 569 | if (error) { |
558 | dev_err(dev, "failed to create sysfs attributes\n"); | 570 | dev_err(dev, "failed to create sysfs attributes\n"); |
559 | goto err_cleanup; | 571 | goto err_cleanup; |
560 | } | 572 | } |
573 | |||
561 | fb_deferred_io_init(info); | 574 | fb_deferred_io_init(info); |
562 | data->fb_info = info; | ||
563 | error = register_framebuffer(info); | 575 | error = register_framebuffer(info); |
564 | if (error) { | 576 | if (error) { |
565 | dev_err(dev, "failed to register framebuffer\n"); | 577 | dev_err(dev, "failed to register framebuffer\n"); |
566 | goto err_sysfs; | 578 | goto err_sysfs; |
567 | } | 579 | } |
568 | /* schedule first output of framebuffer */ | ||
569 | data->fb_force = 1; | ||
570 | schedule_delayed_work(&info->deferred_work, 0); | ||
571 | return 0; | 580 | return 0; |
572 | 581 | ||
573 | err_sysfs: | 582 | err_sysfs: |
574 | fb_deferred_io_cleanup(info); | ||
575 | device_remove_file(dev, &dev_attr_fb_update_rate); | 583 | device_remove_file(dev, &dev_attr_fb_update_rate); |
584 | fb_deferred_io_cleanup(info); | ||
576 | err_cleanup: | 585 | err_cleanup: |
577 | data->fb_vbitmap = NULL; | ||
578 | data->fb_bitmap = NULL; | ||
579 | data->fb_bpp = 0; | ||
580 | data->fb_info = NULL; | 586 | data->fb_info = NULL; |
581 | 587 | ||
582 | err_nomem: | 588 | err_nomem: |
589 | if (fbdata) | ||
590 | vfree(fbdata->bitmap); | ||
583 | framebuffer_release(info); | 591 | framebuffer_release(info); |
584 | vfree(fb_bitmap); | ||
585 | kfree(fb_vbitmap); | ||
586 | return error; | 592 | return error; |
587 | } | 593 | } |
588 | 594 | ||
589 | void picolcd_exit_framebuffer(struct picolcd_data *data) | 595 | void picolcd_exit_framebuffer(struct picolcd_data *data) |
590 | { | 596 | { |
591 | struct fb_info *info = data->fb_info; | 597 | struct fb_info *info = data->fb_info; |
592 | u8 *fb_vbitmap = data->fb_vbitmap; | 598 | struct picolcd_fb_data *fbdata = info->par; |
593 | 599 | unsigned long flags; | |
594 | if (!info) | ||
595 | return; | ||
596 | 600 | ||
597 | device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate); | 601 | device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate); |
598 | info->par = NULL; | 602 | |
603 | /* disconnect framebuffer from HID dev */ | ||
604 | spin_lock_irqsave(&fbdata->lock, flags); | ||
605 | fbdata->picolcd = NULL; | ||
606 | spin_unlock_irqrestore(&fbdata->lock, flags); | ||
607 | |||
608 | /* make sure there is no running update - thus that fbdata->picolcd | ||
609 | * once obtained under lock is guaranteed not to get free() under | ||
610 | * the feet of the deferred work */ | ||
611 | flush_delayed_work_sync(&info->deferred_work); | ||
612 | |||
613 | data->fb_info = NULL; | ||
599 | unregister_framebuffer(info); | 614 | unregister_framebuffer(info); |
600 | data->fb_vbitmap = NULL; | ||
601 | data->fb_bitmap = NULL; | ||
602 | data->fb_bpp = 0; | ||
603 | data->fb_info = NULL; | ||
604 | kfree(fb_vbitmap); | ||
605 | } | 615 | } |