]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/hid/hid-picolcd_fb.c
Merge branches 'for-3.7/upstream-fixes', 'for-3.8/hidraw', 'for-3.8/i2c-hid', 'for...
[karo-tx-linux.git] / drivers / hid / hid-picolcd_fb.c
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 }