hid-picolcd_fb.c 15 KB

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