aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/hid-picolcd_fb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hid/hid-picolcd_fb.c')
-rw-r--r--drivers/hid/hid-picolcd_fb.c216
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 */
102static int picolcd_fb_send_tile(struct hid_device *hdev, int chip, int tile) 102static 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)
190int picolcd_fb_reset(struct picolcd_data *data, int clear) 197int 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 }
286out: 294out:
@@ -337,20 +345,22 @@ static ssize_t picolcd_fb_write(struct fb_info *info, const char __user *buf,
337 345
338static int picolcd_fb_blank(int blank, struct fb_info *info) 346static 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
346static void picolcd_fb_destroy(struct fb_info *info) 352static 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
356static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 366static 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
378static int picolcd_set_par(struct fb_info *info) 388static 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
573err_sysfs: 582err_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);
576err_cleanup: 585err_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
582err_nomem: 588err_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
589void picolcd_exit_framebuffer(struct picolcd_data *data) 595void 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}