1 /*************************************************************************** 2 * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> * 3 * * 4 * Based on Logitech G13 driver (v0.4) * 5 * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> * 6 * * 7 * This program is free software: you can redistribute it and/or modify * 8 * it under the terms of the GNU General Public License as published by * 9 * the Free Software Foundation, version 2 of the License. * 10 * * 11 * This driver is distributed in the hope that it will be useful, but * 12 * WITHOUT ANY WARRANTY; without even the implied warranty of * 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * 14 * General Public License for more details. * 15 * * 16 * You should have received a copy of the GNU General Public License * 17 * along with this software. If not see <http://www.gnu.org/licenses/>. * 18 ***************************************************************************/ 19 20 #include <linux/hid.h> 21 #include <linux/vmalloc.h> 22 #include "usbhid/usbhid.h" 23 #include <linux/usb.h> 24 25 #include <linux/fb.h> 26 #include <linux/module.h> 27 28 #include "hid-picolcd.h" 29 30 /* Framebuffer 31 * 32 * The PicoLCD use a Topway LCD module of 256x64 pixel 33 * This display area is tiled over 4 controllers with 8 tiles 34 * each. Each tile has 8x64 pixel, each data byte representing 35 * a 1-bit wide vertical line of the tile. 36 * 37 * The display can be updated at a tile granularity. 38 * 39 * Chip 1 Chip 2 Chip 3 Chip 4 40 * +----------------+----------------+----------------+----------------+ 41 * | Tile 1 | Tile 1 | Tile 1 | Tile 1 | 42 * +----------------+----------------+----------------+----------------+ 43 * | Tile 2 | Tile 2 | Tile 2 | Tile 2 | 44 * +----------------+----------------+----------------+----------------+ 45 * ... 46 * +----------------+----------------+----------------+----------------+ 47 * | Tile 8 | Tile 8 | Tile 8 | Tile 8 | 48 * +----------------+----------------+----------------+----------------+ 49 */ 50 #define PICOLCDFB_NAME "picolcdfb" 51 #define PICOLCDFB_WIDTH (256) 52 #define PICOLCDFB_HEIGHT (64) 53 #define PICOLCDFB_SIZE (PICOLCDFB_WIDTH * PICOLCDFB_HEIGHT / 8) 54 55 #define PICOLCDFB_UPDATE_RATE_LIMIT 10 56 #define PICOLCDFB_UPDATE_RATE_DEFAULT 2 57 58 /* Framebuffer visual structures */ 59 static const struct fb_fix_screeninfo picolcdfb_fix = { 60 .id = PICOLCDFB_NAME, 61 .type = FB_TYPE_PACKED_PIXELS, 62 .visual = FB_VISUAL_MONO01, 63 .xpanstep = 0, 64 .ypanstep = 0, 65 .ywrapstep = 0, 66 .line_length = PICOLCDFB_WIDTH / 8, 67 .accel = FB_ACCEL_NONE, 68 }; 69 70 static const struct fb_var_screeninfo picolcdfb_var = { 71 .xres = PICOLCDFB_WIDTH, 72 .yres = PICOLCDFB_HEIGHT, 73 .xres_virtual = PICOLCDFB_WIDTH, 74 .yres_virtual = PICOLCDFB_HEIGHT, 75 .width = 103, 76 .height = 26, 77 .bits_per_pixel = 1, 78 .grayscale = 1, 79 .red = { 80 .offset = 0, 81 .length = 1, 82 .msb_right = 0, 83 }, 84 .green = { 85 .offset = 0, 86 .length = 1, 87 .msb_right = 0, 88 }, 89 .blue = { 90 .offset = 0, 91 .length = 1, 92 .msb_right = 0, 93 }, 94 .transp = { 95 .offset = 0, 96 .length = 0, 97 .msb_right = 0, 98 }, 99 }; 100 101 /* Send a given tile to PicoLCD */ 102 static int picolcd_fb_send_tile(struct picolcd_data *data, u8 *vbitmap, 103 int chip, int tile) 104 { 105 struct hid_report *report1, *report2; 106 unsigned long flags; 107 u8 *tdata; 108 int i; 109 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) 115 return -ENODEV; 116 117 spin_lock_irqsave(&data->lock, flags); 118 if ((data->status & PICOLCD_FAILED)) { 119 spin_unlock_irqrestore(&data->lock, flags); 120 return -ENODEV; 121 } 122 hid_set_field(report1->field[0], 0, chip << 2); 123 hid_set_field(report1->field[0], 1, 0x02); 124 hid_set_field(report1->field[0], 2, 0x00); 125 hid_set_field(report1->field[0], 3, 0x00); 126 hid_set_field(report1->field[0], 4, 0xb8 | tile); 127 hid_set_field(report1->field[0], 5, 0x00); 128 hid_set_field(report1->field[0], 6, 0x00); 129 hid_set_field(report1->field[0], 7, 0x40); 130 hid_set_field(report1->field[0], 8, 0x00); 131 hid_set_field(report1->field[0], 9, 0x00); 132 hid_set_field(report1->field[0], 10, 32); 133 134 hid_set_field(report2->field[0], 0, (chip << 2) | 0x01); 135 hid_set_field(report2->field[0], 1, 0x00); 136 hid_set_field(report2->field[0], 2, 0x00); 137 hid_set_field(report2->field[0], 3, 32); 138 139 tdata = vbitmap + (tile * 4 + chip) * 64; 140 for (i = 0; i < 64; i++) 141 if (i < 32) 142 hid_set_field(report1->field[0], 11 + i, tdata[i]); 143 else 144 hid_set_field(report2->field[0], 4 + i - 32, tdata[i]); 145 146 usbhid_submit_report(data->hdev, report1, USB_DIR_OUT); 147 usbhid_submit_report(data->hdev, report2, USB_DIR_OUT); 148 spin_unlock_irqrestore(&data->lock, flags); 149 return 0; 150 } 151 152 /* Translate a single tile*/ 153 static int picolcd_fb_update_tile(u8 *vbitmap, const u8 *bitmap, int bpp, 154 int chip, int tile) 155 { 156 int i, b, changed = 0; 157 u8 tdata[64]; 158 u8 *vdata = vbitmap + (tile * 4 + chip) * 64; 159 160 if (bpp == 1) { 161 for (b = 7; b >= 0; b--) { 162 const u8 *bdata = bitmap + tile * 256 + chip * 8 + b * 32; 163 for (i = 0; i < 64; i++) { 164 tdata[i] <<= 1; 165 tdata[i] |= (bdata[i/8] >> (i % 8)) & 0x01; 166 } 167 } 168 } else if (bpp == 8) { 169 for (b = 7; b >= 0; b--) { 170 const u8 *bdata = bitmap + (tile * 256 + chip * 8 + b * 32) * 8; 171 for (i = 0; i < 64; i++) { 172 tdata[i] <<= 1; 173 tdata[i] |= (bdata[i] & 0x80) ? 0x01 : 0x00; 174 } 175 } 176 } else { 177 /* Oops, we should never get here! */ 178 WARN_ON(1); 179 return 0; 180 } 181 182 for (i = 0; i < 64; i++) 183 if (tdata[i] != vdata[i]) { 184 changed = 1; 185 vdata[i] = tdata[i]; 186 } 187 return changed; 188 } 189 190 void picolcd_fb_refresh(struct picolcd_data *data) 191 { 192 if (data->fb_info) 193 schedule_delayed_work(&data->fb_info->deferred_work, 0); 194 } 195 196 /* Reconfigure LCD display */ 197 int picolcd_fb_reset(struct picolcd_data *data, int clear) 198 { 199 struct hid_report *report = picolcd_out_report(REPORT_LCD_CMD, data->hdev); 200 struct picolcd_fb_data *fbdata = data->fb_info->par; 201 int i, j; 202 unsigned long flags; 203 static const u8 mapcmd[8] = { 0x00, 0x02, 0x00, 0x64, 0x3f, 0x00, 0x64, 0xc0 }; 204 205 if (!report || report->maxfield != 1) 206 return -ENODEV; 207 208 spin_lock_irqsave(&data->lock, flags); 209 for (i = 0; i < 4; i++) { 210 for (j = 0; j < report->field[0]->maxusage; j++) 211 if (j == 0) 212 hid_set_field(report->field[0], j, i << 2); 213 else if (j < sizeof(mapcmd)) 214 hid_set_field(report->field[0], j, mapcmd[j]); 215 else 216 hid_set_field(report->field[0], j, 0); 217 usbhid_submit_report(data->hdev, report, USB_DIR_OUT); 218 } 219 spin_unlock_irqrestore(&data->lock, flags); 220 221 if (clear) { 222 memset(fbdata->vbitmap, 0, PICOLCDFB_SIZE); 223 memset(fbdata->bitmap, 0, PICOLCDFB_SIZE*fbdata->bpp); 224 } 225 fbdata->force = 1; 226 227 /* schedule first output of framebuffer */ 228 if (fbdata->ready) 229 schedule_delayed_work(&data->fb_info->deferred_work, 0); 230 else 231 fbdata->ready = 1; 232 233 return 0; 234 } 235 236 /* Update fb_vbitmap from the screen_base and send changed tiles to device */ 237 static void picolcd_fb_update(struct fb_info *info) 238 { 239 int chip, tile, n; 240 unsigned long flags; 241 struct picolcd_fb_data *fbdata = info->par; 242 struct picolcd_data *data; 243 244 mutex_lock(&info->lock); 245 246 spin_lock_irqsave(&fbdata->lock, flags); 247 if (!fbdata->ready && fbdata->picolcd) 248 picolcd_fb_reset(fbdata->picolcd, 0); 249 spin_unlock_irqrestore(&fbdata->lock, flags); 250 251 /* 252 * Translate the framebuffer into the format needed by the PicoLCD. 253 * See display layout above. 254 * Do this one tile after the other and push those tiles that changed. 255 * 256 * Wait for our IO to complete as otherwise we might flood the queue! 257 */ 258 n = 0; 259 for (chip = 0; chip < 4; chip++) 260 for (tile = 0; tile < 8; tile++) { 261 if (!fbdata->force && !picolcd_fb_update_tile( 262 fbdata->vbitmap, fbdata->bitmap, 263 fbdata->bpp, chip, tile)) 264 continue; 265 n += 2; 266 if (n >= HID_OUTPUT_FIFO_SIZE / 2) { 267 spin_lock_irqsave(&fbdata->lock, flags); 268 data = fbdata->picolcd; 269 spin_unlock_irqrestore(&fbdata->lock, flags); 270 mutex_unlock(&info->lock); 271 if (!data) 272 return; 273 usbhid_wait_io(data->hdev); 274 mutex_lock(&info->lock); 275 n = 0; 276 } 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; 285 if (n) { 286 spin_lock_irqsave(&fbdata->lock, flags); 287 data = fbdata->picolcd; 288 spin_unlock_irqrestore(&fbdata->lock, flags); 289 mutex_unlock(&info->lock); 290 if (data) 291 usbhid_wait_io(data->hdev); 292 return; 293 } 294 out: 295 mutex_unlock(&info->lock); 296 } 297 298 /* Stub to call the system default and update the image on the picoLCD */ 299 static void picolcd_fb_fillrect(struct fb_info *info, 300 const struct fb_fillrect *rect) 301 { 302 if (!info->par) 303 return; 304 sys_fillrect(info, rect); 305 306 schedule_delayed_work(&info->deferred_work, 0); 307 } 308 309 /* Stub to call the system default and update the image on the picoLCD */ 310 static void picolcd_fb_copyarea(struct fb_info *info, 311 const struct fb_copyarea *area) 312 { 313 if (!info->par) 314 return; 315 sys_copyarea(info, area); 316 317 schedule_delayed_work(&info->deferred_work, 0); 318 } 319 320 /* Stub to call the system default and update the image on the picoLCD */ 321 static void picolcd_fb_imageblit(struct fb_info *info, const struct fb_image *image) 322 { 323 if (!info->par) 324 return; 325 sys_imageblit(info, image); 326 327 schedule_delayed_work(&info->deferred_work, 0); 328 } 329 330 /* 331 * this is the slow path from userspace. they can seek and write to 332 * the fb. it's inefficient to do anything less than a full screen draw 333 */ 334 static ssize_t picolcd_fb_write(struct fb_info *info, const char __user *buf, 335 size_t count, loff_t *ppos) 336 { 337 ssize_t ret; 338 if (!info->par) 339 return -ENODEV; 340 ret = fb_sys_write(info, buf, count, ppos); 341 if (ret >= 0) 342 schedule_delayed_work(&info->deferred_work, 0); 343 return ret; 344 } 345 346 static int picolcd_fb_blank(int blank, struct fb_info *info) 347 { 348 /* We let fb notification do this for us via lcd/backlight device */ 349 return 0; 350 } 351 352 static void picolcd_fb_destroy(struct fb_info *info) 353 { 354 struct picolcd_fb_data *fbdata = info->par; 355 356 /* make sure no work is deferred */ 357 fb_deferred_io_cleanup(info); 358 359 /* No thridparty should ever unregister our framebuffer! */ 360 WARN_ON(fbdata->picolcd != NULL); 361 362 vfree((u8 *)info->fix.smem_start); 363 framebuffer_release(info); 364 } 365 366 static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 367 { 368 __u32 bpp = var->bits_per_pixel; 369 __u32 activate = var->activate; 370 371 /* only allow 1/8 bit depth (8-bit is grayscale) */ 372 *var = picolcdfb_var; 373 var->activate = activate; 374 if (bpp >= 8) { 375 var->bits_per_pixel = 8; 376 var->red.length = 8; 377 var->green.length = 8; 378 var->blue.length = 8; 379 } else { 380 var->bits_per_pixel = 1; 381 var->red.length = 1; 382 var->green.length = 1; 383 var->blue.length = 1; 384 } 385 return 0; 386 } 387 388 static int picolcd_set_par(struct fb_info *info) 389 { 390 struct picolcd_fb_data *fbdata = info->par; 391 u8 *tmp_fb, *o_fb; 392 if (info->var.bits_per_pixel == fbdata->bpp) 393 return 0; 394 /* switch between 1/8 bit depths */ 395 if (info->var.bits_per_pixel != 1 && info->var.bits_per_pixel != 8) 396 return -EINVAL; 397 398 o_fb = fbdata->bitmap; 399 tmp_fb = kmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel, GFP_KERNEL); 400 if (!tmp_fb) 401 return -ENOMEM; 402 403 /* translate FB content to new bits-per-pixel */ 404 if (info->var.bits_per_pixel == 1) { 405 int i, b; 406 for (i = 0; i < PICOLCDFB_SIZE; i++) { 407 u8 p = 0; 408 for (b = 0; b < 8; b++) { 409 p <<= 1; 410 p |= o_fb[i*8+b] ? 0x01 : 0x00; 411 } 412 tmp_fb[i] = p; 413 } 414 memcpy(o_fb, tmp_fb, PICOLCDFB_SIZE); 415 info->fix.visual = FB_VISUAL_MONO01; 416 info->fix.line_length = PICOLCDFB_WIDTH / 8; 417 } else { 418 int i; 419 memcpy(tmp_fb, o_fb, PICOLCDFB_SIZE); 420 for (i = 0; i < PICOLCDFB_SIZE * 8; i++) 421 o_fb[i] = tmp_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00; 422 info->fix.visual = FB_VISUAL_DIRECTCOLOR; 423 info->fix.line_length = PICOLCDFB_WIDTH; 424 } 425 426 kfree(tmp_fb); 427 fbdata->bpp = info->var.bits_per_pixel; 428 return 0; 429 } 430 431 /* Note this can't be const because of struct fb_info definition */ 432 static struct fb_ops picolcdfb_ops = { 433 .owner = THIS_MODULE, 434 .fb_destroy = picolcd_fb_destroy, 435 .fb_read = fb_sys_read, 436 .fb_write = picolcd_fb_write, 437 .fb_blank = picolcd_fb_blank, 438 .fb_fillrect = picolcd_fb_fillrect, 439 .fb_copyarea = picolcd_fb_copyarea, 440 .fb_imageblit = picolcd_fb_imageblit, 441 .fb_check_var = picolcd_fb_check_var, 442 .fb_set_par = picolcd_set_par, 443 }; 444 445 446 /* Callback from deferred IO workqueue */ 447 static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist) 448 { 449 picolcd_fb_update(info); 450 } 451 452 static const struct fb_deferred_io picolcd_fb_defio = { 453 .delay = HZ / PICOLCDFB_UPDATE_RATE_DEFAULT, 454 .deferred_io = picolcd_fb_deferred_io, 455 }; 456 457 458 /* 459 * The "fb_update_rate" sysfs attribute 460 */ 461 static ssize_t picolcd_fb_update_rate_show(struct device *dev, 462 struct device_attribute *attr, char *buf) 463 { 464 struct picolcd_data *data = dev_get_drvdata(dev); 465 struct picolcd_fb_data *fbdata = data->fb_info->par; 466 unsigned i, fb_update_rate = fbdata->update_rate; 467 size_t ret = 0; 468 469 for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++) 470 if (ret >= PAGE_SIZE) 471 break; 472 else if (i == fb_update_rate) 473 ret += snprintf(buf+ret, PAGE_SIZE-ret, "[%u] ", i); 474 else 475 ret += snprintf(buf+ret, PAGE_SIZE-ret, "%u ", i); 476 if (ret > 0) 477 buf[min(ret, (size_t)PAGE_SIZE)-1] = '\n'; 478 return ret; 479 } 480 481 static ssize_t picolcd_fb_update_rate_store(struct device *dev, 482 struct device_attribute *attr, const char *buf, size_t count) 483 { 484 struct picolcd_data *data = dev_get_drvdata(dev); 485 struct picolcd_fb_data *fbdata = data->fb_info->par; 486 int i; 487 unsigned u; 488 489 if (count < 1 || count > 10) 490 return -EINVAL; 491 492 i = sscanf(buf, "%u", &u); 493 if (i != 1) 494 return -EINVAL; 495 496 if (u > PICOLCDFB_UPDATE_RATE_LIMIT) 497 return -ERANGE; 498 else if (u == 0) 499 u = PICOLCDFB_UPDATE_RATE_DEFAULT; 500 501 fbdata->update_rate = u; 502 data->fb_info->fbdefio->delay = HZ / fbdata->update_rate; 503 return count; 504 } 505 506 static DEVICE_ATTR(fb_update_rate, 0666, picolcd_fb_update_rate_show, 507 picolcd_fb_update_rate_store); 508 509 /* initialize Framebuffer device */ 510 int picolcd_init_framebuffer(struct picolcd_data *data) 511 { 512 struct device *dev = &data->hdev->dev; 513 struct fb_info *info = NULL; 514 struct picolcd_fb_data *fbdata = NULL; 515 int i, error = -ENOMEM; 516 u32 *palette; 517 518 /* The extra memory is: 519 * - 256*u32 for pseudo_palette 520 * - struct fb_deferred_io 521 */ 522 info = framebuffer_alloc(256 * sizeof(u32) + 523 sizeof(struct fb_deferred_io) + 524 sizeof(struct picolcd_fb_data) + 525 PICOLCDFB_SIZE, dev); 526 if (info == NULL) { 527 dev_err(dev, "failed to allocate a framebuffer\n"); 528 goto err_nomem; 529 } 530 531 info->fbdefio = info->par; 532 *info->fbdefio = picolcd_fb_defio; 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++) 537 palette[i] = i > 0 && i < 16 ? 0xff : 0; 538 info->pseudo_palette = palette; 539 info->fbops = &picolcdfb_ops; 540 info->var = picolcdfb_var; 541 info->fix = picolcdfb_fix; 542 info->fix.smem_len = PICOLCDFB_SIZE*8; 543 info->flags = FBINFO_FLAG_DEFAULT; 544 545 fbdata = info->par; 546 spin_lock_init(&fbdata->lock); 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 562 error = picolcd_fb_reset(data, 1); 563 if (error) { 564 dev_err(dev, "failed to configure display\n"); 565 goto err_cleanup; 566 } 567 568 error = device_create_file(dev, &dev_attr_fb_update_rate); 569 if (error) { 570 dev_err(dev, "failed to create sysfs attributes\n"); 571 goto err_cleanup; 572 } 573 574 fb_deferred_io_init(info); 575 error = register_framebuffer(info); 576 if (error) { 577 dev_err(dev, "failed to register framebuffer\n"); 578 goto err_sysfs; 579 } 580 return 0; 581 582 err_sysfs: 583 device_remove_file(dev, &dev_attr_fb_update_rate); 584 fb_deferred_io_cleanup(info); 585 err_cleanup: 586 data->fb_info = NULL; 587 588 err_nomem: 589 if (fbdata) 590 vfree(fbdata->bitmap); 591 framebuffer_release(info); 592 return error; 593 } 594 595 void picolcd_exit_framebuffer(struct picolcd_data *data) 596 { 597 struct fb_info *info = data->fb_info; 598 struct picolcd_fb_data *fbdata = info->par; 599 unsigned long flags; 600 601 device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate); 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(&info->deferred_work); 612 613 data->fb_info = NULL; 614 unregister_framebuffer(info); 615 } 616