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