0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/hid.h>
0011 #include <linux/vmalloc.h>
0012
0013 #include <linux/fb.h>
0014 #include <linux/module.h>
0015
0016 #include "hid-picolcd.h"
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038 #define PICOLCDFB_NAME "picolcdfb"
0039 #define PICOLCDFB_WIDTH (256)
0040 #define PICOLCDFB_HEIGHT (64)
0041 #define PICOLCDFB_SIZE (PICOLCDFB_WIDTH * PICOLCDFB_HEIGHT / 8)
0042
0043 #define PICOLCDFB_UPDATE_RATE_LIMIT 10
0044 #define PICOLCDFB_UPDATE_RATE_DEFAULT 2
0045
0046
0047 static const struct fb_fix_screeninfo picolcdfb_fix = {
0048 .id = PICOLCDFB_NAME,
0049 .type = FB_TYPE_PACKED_PIXELS,
0050 .visual = FB_VISUAL_MONO01,
0051 .xpanstep = 0,
0052 .ypanstep = 0,
0053 .ywrapstep = 0,
0054 .line_length = PICOLCDFB_WIDTH / 8,
0055 .accel = FB_ACCEL_NONE,
0056 };
0057
0058 static const struct fb_var_screeninfo picolcdfb_var = {
0059 .xres = PICOLCDFB_WIDTH,
0060 .yres = PICOLCDFB_HEIGHT,
0061 .xres_virtual = PICOLCDFB_WIDTH,
0062 .yres_virtual = PICOLCDFB_HEIGHT,
0063 .width = 103,
0064 .height = 26,
0065 .bits_per_pixel = 1,
0066 .grayscale = 1,
0067 .red = {
0068 .offset = 0,
0069 .length = 1,
0070 .msb_right = 0,
0071 },
0072 .green = {
0073 .offset = 0,
0074 .length = 1,
0075 .msb_right = 0,
0076 },
0077 .blue = {
0078 .offset = 0,
0079 .length = 1,
0080 .msb_right = 0,
0081 },
0082 .transp = {
0083 .offset = 0,
0084 .length = 0,
0085 .msb_right = 0,
0086 },
0087 };
0088
0089
0090 static int picolcd_fb_send_tile(struct picolcd_data *data, u8 *vbitmap,
0091 int chip, int tile)
0092 {
0093 struct hid_report *report1, *report2;
0094 unsigned long flags;
0095 u8 *tdata;
0096 int i;
0097
0098 report1 = picolcd_out_report(REPORT_LCD_CMD_DATA, data->hdev);
0099 if (!report1 || report1->maxfield != 1)
0100 return -ENODEV;
0101 report2 = picolcd_out_report(REPORT_LCD_DATA, data->hdev);
0102 if (!report2 || report2->maxfield != 1)
0103 return -ENODEV;
0104
0105 spin_lock_irqsave(&data->lock, flags);
0106 if ((data->status & PICOLCD_FAILED)) {
0107 spin_unlock_irqrestore(&data->lock, flags);
0108 return -ENODEV;
0109 }
0110 hid_set_field(report1->field[0], 0, chip << 2);
0111 hid_set_field(report1->field[0], 1, 0x02);
0112 hid_set_field(report1->field[0], 2, 0x00);
0113 hid_set_field(report1->field[0], 3, 0x00);
0114 hid_set_field(report1->field[0], 4, 0xb8 | tile);
0115 hid_set_field(report1->field[0], 5, 0x00);
0116 hid_set_field(report1->field[0], 6, 0x00);
0117 hid_set_field(report1->field[0], 7, 0x40);
0118 hid_set_field(report1->field[0], 8, 0x00);
0119 hid_set_field(report1->field[0], 9, 0x00);
0120 hid_set_field(report1->field[0], 10, 32);
0121
0122 hid_set_field(report2->field[0], 0, (chip << 2) | 0x01);
0123 hid_set_field(report2->field[0], 1, 0x00);
0124 hid_set_field(report2->field[0], 2, 0x00);
0125 hid_set_field(report2->field[0], 3, 32);
0126
0127 tdata = vbitmap + (tile * 4 + chip) * 64;
0128 for (i = 0; i < 64; i++)
0129 if (i < 32)
0130 hid_set_field(report1->field[0], 11 + i, tdata[i]);
0131 else
0132 hid_set_field(report2->field[0], 4 + i - 32, tdata[i]);
0133
0134 hid_hw_request(data->hdev, report1, HID_REQ_SET_REPORT);
0135 hid_hw_request(data->hdev, report2, HID_REQ_SET_REPORT);
0136 spin_unlock_irqrestore(&data->lock, flags);
0137 return 0;
0138 }
0139
0140
0141 static int picolcd_fb_update_tile(u8 *vbitmap, const u8 *bitmap, int bpp,
0142 int chip, int tile)
0143 {
0144 int i, b, changed = 0;
0145 u8 tdata[64];
0146 u8 *vdata = vbitmap + (tile * 4 + chip) * 64;
0147
0148 if (bpp == 1) {
0149 for (b = 7; b >= 0; b--) {
0150 const u8 *bdata = bitmap + tile * 256 + chip * 8 + b * 32;
0151 for (i = 0; i < 64; i++) {
0152 tdata[i] <<= 1;
0153 tdata[i] |= (bdata[i/8] >> (i % 8)) & 0x01;
0154 }
0155 }
0156 } else if (bpp == 8) {
0157 for (b = 7; b >= 0; b--) {
0158 const u8 *bdata = bitmap + (tile * 256 + chip * 8 + b * 32) * 8;
0159 for (i = 0; i < 64; i++) {
0160 tdata[i] <<= 1;
0161 tdata[i] |= (bdata[i] & 0x80) ? 0x01 : 0x00;
0162 }
0163 }
0164 } else {
0165
0166 WARN_ON(1);
0167 return 0;
0168 }
0169
0170 for (i = 0; i < 64; i++)
0171 if (tdata[i] != vdata[i]) {
0172 changed = 1;
0173 vdata[i] = tdata[i];
0174 }
0175 return changed;
0176 }
0177
0178 void picolcd_fb_refresh(struct picolcd_data *data)
0179 {
0180 if (data->fb_info)
0181 schedule_delayed_work(&data->fb_info->deferred_work, 0);
0182 }
0183
0184
0185 int picolcd_fb_reset(struct picolcd_data *data, int clear)
0186 {
0187 struct hid_report *report = picolcd_out_report(REPORT_LCD_CMD, data->hdev);
0188 struct picolcd_fb_data *fbdata = data->fb_info->par;
0189 int i, j;
0190 unsigned long flags;
0191 static const u8 mapcmd[8] = { 0x00, 0x02, 0x00, 0x64, 0x3f, 0x00, 0x64, 0xc0 };
0192
0193 if (!report || report->maxfield != 1)
0194 return -ENODEV;
0195
0196 spin_lock_irqsave(&data->lock, flags);
0197 for (i = 0; i < 4; i++) {
0198 for (j = 0; j < report->field[0]->maxusage; j++)
0199 if (j == 0)
0200 hid_set_field(report->field[0], j, i << 2);
0201 else if (j < sizeof(mapcmd))
0202 hid_set_field(report->field[0], j, mapcmd[j]);
0203 else
0204 hid_set_field(report->field[0], j, 0);
0205 hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT);
0206 }
0207 spin_unlock_irqrestore(&data->lock, flags);
0208
0209 if (clear) {
0210 memset(fbdata->vbitmap, 0, PICOLCDFB_SIZE);
0211 memset(fbdata->bitmap, 0, PICOLCDFB_SIZE*fbdata->bpp);
0212 }
0213 fbdata->force = 1;
0214
0215
0216 if (fbdata->ready)
0217 schedule_delayed_work(&data->fb_info->deferred_work, 0);
0218 else
0219 fbdata->ready = 1;
0220
0221 return 0;
0222 }
0223
0224
0225 static void picolcd_fb_update(struct fb_info *info)
0226 {
0227 int chip, tile, n;
0228 unsigned long flags;
0229 struct picolcd_fb_data *fbdata = info->par;
0230 struct picolcd_data *data;
0231
0232 mutex_lock(&info->lock);
0233
0234 spin_lock_irqsave(&fbdata->lock, flags);
0235 if (!fbdata->ready && fbdata->picolcd)
0236 picolcd_fb_reset(fbdata->picolcd, 0);
0237 spin_unlock_irqrestore(&fbdata->lock, flags);
0238
0239
0240
0241
0242
0243
0244
0245
0246 n = 0;
0247 for (chip = 0; chip < 4; chip++)
0248 for (tile = 0; tile < 8; tile++) {
0249 if (!fbdata->force && !picolcd_fb_update_tile(
0250 fbdata->vbitmap, fbdata->bitmap,
0251 fbdata->bpp, chip, tile))
0252 continue;
0253 n += 2;
0254 if (n >= HID_OUTPUT_FIFO_SIZE / 2) {
0255 spin_lock_irqsave(&fbdata->lock, flags);
0256 data = fbdata->picolcd;
0257 spin_unlock_irqrestore(&fbdata->lock, flags);
0258 mutex_unlock(&info->lock);
0259 if (!data)
0260 return;
0261 hid_hw_wait(data->hdev);
0262 mutex_lock(&info->lock);
0263 n = 0;
0264 }
0265 spin_lock_irqsave(&fbdata->lock, flags);
0266 data = fbdata->picolcd;
0267 spin_unlock_irqrestore(&fbdata->lock, flags);
0268 if (!data || picolcd_fb_send_tile(data,
0269 fbdata->vbitmap, chip, tile))
0270 goto out;
0271 }
0272 fbdata->force = false;
0273 if (n) {
0274 spin_lock_irqsave(&fbdata->lock, flags);
0275 data = fbdata->picolcd;
0276 spin_unlock_irqrestore(&fbdata->lock, flags);
0277 mutex_unlock(&info->lock);
0278 if (data)
0279 hid_hw_wait(data->hdev);
0280 return;
0281 }
0282 out:
0283 mutex_unlock(&info->lock);
0284 }
0285
0286
0287 static void picolcd_fb_fillrect(struct fb_info *info,
0288 const struct fb_fillrect *rect)
0289 {
0290 if (!info->par)
0291 return;
0292 sys_fillrect(info, rect);
0293
0294 schedule_delayed_work(&info->deferred_work, 0);
0295 }
0296
0297
0298 static void picolcd_fb_copyarea(struct fb_info *info,
0299 const struct fb_copyarea *area)
0300 {
0301 if (!info->par)
0302 return;
0303 sys_copyarea(info, area);
0304
0305 schedule_delayed_work(&info->deferred_work, 0);
0306 }
0307
0308
0309 static void picolcd_fb_imageblit(struct fb_info *info, const struct fb_image *image)
0310 {
0311 if (!info->par)
0312 return;
0313 sys_imageblit(info, image);
0314
0315 schedule_delayed_work(&info->deferred_work, 0);
0316 }
0317
0318
0319
0320
0321
0322 static ssize_t picolcd_fb_write(struct fb_info *info, const char __user *buf,
0323 size_t count, loff_t *ppos)
0324 {
0325 ssize_t ret;
0326 if (!info->par)
0327 return -ENODEV;
0328 ret = fb_sys_write(info, buf, count, ppos);
0329 if (ret >= 0)
0330 schedule_delayed_work(&info->deferred_work, 0);
0331 return ret;
0332 }
0333
0334 static int picolcd_fb_blank(int blank, struct fb_info *info)
0335 {
0336
0337 return 0;
0338 }
0339
0340 static void picolcd_fb_destroy(struct fb_info *info)
0341 {
0342 struct picolcd_fb_data *fbdata = info->par;
0343
0344
0345 fb_deferred_io_cleanup(info);
0346
0347
0348 WARN_ON(fbdata->picolcd != NULL);
0349
0350 vfree((u8 *)info->fix.smem_start);
0351 framebuffer_release(info);
0352 }
0353
0354 static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
0355 {
0356 __u32 bpp = var->bits_per_pixel;
0357 __u32 activate = var->activate;
0358
0359
0360 *var = picolcdfb_var;
0361 var->activate = activate;
0362 if (bpp >= 8) {
0363 var->bits_per_pixel = 8;
0364 var->red.length = 8;
0365 var->green.length = 8;
0366 var->blue.length = 8;
0367 } else {
0368 var->bits_per_pixel = 1;
0369 var->red.length = 1;
0370 var->green.length = 1;
0371 var->blue.length = 1;
0372 }
0373 return 0;
0374 }
0375
0376 static int picolcd_set_par(struct fb_info *info)
0377 {
0378 struct picolcd_fb_data *fbdata = info->par;
0379 u8 *tmp_fb, *o_fb;
0380 if (info->var.bits_per_pixel == fbdata->bpp)
0381 return 0;
0382
0383 if (info->var.bits_per_pixel != 1 && info->var.bits_per_pixel != 8)
0384 return -EINVAL;
0385
0386 o_fb = fbdata->bitmap;
0387 tmp_fb = kmalloc_array(PICOLCDFB_SIZE, info->var.bits_per_pixel,
0388 GFP_KERNEL);
0389 if (!tmp_fb)
0390 return -ENOMEM;
0391
0392
0393 if (info->var.bits_per_pixel == 1) {
0394 int i, b;
0395 for (i = 0; i < PICOLCDFB_SIZE; i++) {
0396 u8 p = 0;
0397 for (b = 0; b < 8; b++) {
0398 p <<= 1;
0399 p |= o_fb[i*8+b] ? 0x01 : 0x00;
0400 }
0401 tmp_fb[i] = p;
0402 }
0403 memcpy(o_fb, tmp_fb, PICOLCDFB_SIZE);
0404 info->fix.visual = FB_VISUAL_MONO01;
0405 info->fix.line_length = PICOLCDFB_WIDTH / 8;
0406 } else {
0407 int i;
0408 memcpy(tmp_fb, o_fb, PICOLCDFB_SIZE);
0409 for (i = 0; i < PICOLCDFB_SIZE * 8; i++)
0410 o_fb[i] = tmp_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00;
0411 info->fix.visual = FB_VISUAL_DIRECTCOLOR;
0412 info->fix.line_length = PICOLCDFB_WIDTH;
0413 }
0414
0415 kfree(tmp_fb);
0416 fbdata->bpp = info->var.bits_per_pixel;
0417 return 0;
0418 }
0419
0420 static const struct fb_ops picolcdfb_ops = {
0421 .owner = THIS_MODULE,
0422 .fb_destroy = picolcd_fb_destroy,
0423 .fb_read = fb_sys_read,
0424 .fb_write = picolcd_fb_write,
0425 .fb_blank = picolcd_fb_blank,
0426 .fb_fillrect = picolcd_fb_fillrect,
0427 .fb_copyarea = picolcd_fb_copyarea,
0428 .fb_imageblit = picolcd_fb_imageblit,
0429 .fb_check_var = picolcd_fb_check_var,
0430 .fb_set_par = picolcd_set_par,
0431 .fb_mmap = fb_deferred_io_mmap,
0432 };
0433
0434
0435
0436 static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagereflist)
0437 {
0438 picolcd_fb_update(info);
0439 }
0440
0441 static const struct fb_deferred_io picolcd_fb_defio = {
0442 .delay = HZ / PICOLCDFB_UPDATE_RATE_DEFAULT,
0443 .deferred_io = picolcd_fb_deferred_io,
0444 };
0445
0446
0447
0448
0449
0450 static ssize_t picolcd_fb_update_rate_show(struct device *dev,
0451 struct device_attribute *attr, char *buf)
0452 {
0453 struct picolcd_data *data = dev_get_drvdata(dev);
0454 struct picolcd_fb_data *fbdata = data->fb_info->par;
0455 unsigned i, fb_update_rate = fbdata->update_rate;
0456 size_t ret = 0;
0457
0458 for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++)
0459 if (ret >= PAGE_SIZE)
0460 break;
0461 else if (i == fb_update_rate)
0462 ret += scnprintf(buf+ret, PAGE_SIZE-ret, "[%u] ", i);
0463 else
0464 ret += scnprintf(buf+ret, PAGE_SIZE-ret, "%u ", i);
0465 if (ret > 0)
0466 buf[min(ret, (size_t)PAGE_SIZE)-1] = '\n';
0467 return ret;
0468 }
0469
0470 static ssize_t picolcd_fb_update_rate_store(struct device *dev,
0471 struct device_attribute *attr, const char *buf, size_t count)
0472 {
0473 struct picolcd_data *data = dev_get_drvdata(dev);
0474 struct picolcd_fb_data *fbdata = data->fb_info->par;
0475 int i;
0476 unsigned u;
0477
0478 if (count < 1 || count > 10)
0479 return -EINVAL;
0480
0481 i = sscanf(buf, "%u", &u);
0482 if (i != 1)
0483 return -EINVAL;
0484
0485 if (u > PICOLCDFB_UPDATE_RATE_LIMIT)
0486 return -ERANGE;
0487 else if (u == 0)
0488 u = PICOLCDFB_UPDATE_RATE_DEFAULT;
0489
0490 fbdata->update_rate = u;
0491 data->fb_info->fbdefio->delay = HZ / fbdata->update_rate;
0492 return count;
0493 }
0494
0495 static DEVICE_ATTR(fb_update_rate, 0664, picolcd_fb_update_rate_show,
0496 picolcd_fb_update_rate_store);
0497
0498
0499 int picolcd_init_framebuffer(struct picolcd_data *data)
0500 {
0501 struct device *dev = &data->hdev->dev;
0502 struct fb_info *info = NULL;
0503 struct picolcd_fb_data *fbdata = NULL;
0504 int i, error = -ENOMEM;
0505 u32 *palette;
0506
0507
0508
0509
0510
0511 info = framebuffer_alloc(256 * sizeof(u32) +
0512 sizeof(struct fb_deferred_io) +
0513 sizeof(struct picolcd_fb_data) +
0514 PICOLCDFB_SIZE, dev);
0515 if (!info)
0516 goto err_nomem;
0517
0518 info->fbdefio = info->par;
0519 *info->fbdefio = picolcd_fb_defio;
0520 info->par += sizeof(struct fb_deferred_io);
0521 palette = info->par;
0522 info->par += 256 * sizeof(u32);
0523 for (i = 0; i < 256; i++)
0524 palette[i] = i > 0 && i < 16 ? 0xff : 0;
0525 info->pseudo_palette = palette;
0526 info->fbops = &picolcdfb_ops;
0527 info->var = picolcdfb_var;
0528 info->fix = picolcdfb_fix;
0529 info->fix.smem_len = PICOLCDFB_SIZE*8;
0530 info->flags = FBINFO_FLAG_DEFAULT;
0531
0532 fbdata = info->par;
0533 spin_lock_init(&fbdata->lock);
0534 fbdata->picolcd = data;
0535 fbdata->update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT;
0536 fbdata->bpp = picolcdfb_var.bits_per_pixel;
0537 fbdata->force = 1;
0538 fbdata->vbitmap = info->par + sizeof(struct picolcd_fb_data);
0539 fbdata->bitmap = vmalloc(PICOLCDFB_SIZE*8);
0540 if (fbdata->bitmap == NULL) {
0541 dev_err(dev, "can't get a free page for framebuffer\n");
0542 goto err_nomem;
0543 }
0544 info->screen_base = (char __force __iomem *)fbdata->bitmap;
0545 info->fix.smem_start = (unsigned long)fbdata->bitmap;
0546 memset(fbdata->vbitmap, 0xff, PICOLCDFB_SIZE);
0547 data->fb_info = info;
0548
0549 error = picolcd_fb_reset(data, 1);
0550 if (error) {
0551 dev_err(dev, "failed to configure display\n");
0552 goto err_cleanup;
0553 }
0554
0555 error = device_create_file(dev, &dev_attr_fb_update_rate);
0556 if (error) {
0557 dev_err(dev, "failed to create sysfs attributes\n");
0558 goto err_cleanup;
0559 }
0560
0561 fb_deferred_io_init(info);
0562 error = register_framebuffer(info);
0563 if (error) {
0564 dev_err(dev, "failed to register framebuffer\n");
0565 goto err_sysfs;
0566 }
0567 return 0;
0568
0569 err_sysfs:
0570 device_remove_file(dev, &dev_attr_fb_update_rate);
0571 fb_deferred_io_cleanup(info);
0572 err_cleanup:
0573 data->fb_info = NULL;
0574
0575 err_nomem:
0576 if (fbdata)
0577 vfree(fbdata->bitmap);
0578 framebuffer_release(info);
0579 return error;
0580 }
0581
0582 void picolcd_exit_framebuffer(struct picolcd_data *data)
0583 {
0584 struct fb_info *info = data->fb_info;
0585 struct picolcd_fb_data *fbdata;
0586 unsigned long flags;
0587
0588 if (!info)
0589 return;
0590
0591 device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate);
0592 fbdata = info->par;
0593
0594
0595 spin_lock_irqsave(&fbdata->lock, flags);
0596 fbdata->picolcd = NULL;
0597 spin_unlock_irqrestore(&fbdata->lock, flags);
0598
0599
0600
0601
0602 flush_delayed_work(&info->deferred_work);
0603
0604 data->fb_info = NULL;
0605 unregister_framebuffer(info);
0606 }