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