0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021 #include <linux/module.h>
0022 #include <linux/kernel.h>
0023 #include <linux/errno.h>
0024 #include <linux/string.h>
0025 #include <linux/mm.h>
0026 #include <linux/vmalloc.h>
0027 #include <linux/delay.h>
0028 #include <linux/interrupt.h>
0029 #include <linux/fb.h>
0030 #include <linux/init.h>
0031 #include <linux/platform_device.h>
0032 #include <linux/list.h>
0033 #include <linux/firmware.h>
0034 #include <linux/dma-mapping.h>
0035 #include <linux/uaccess.h>
0036 #include <linux/irq.h>
0037
0038 #include <video/metronomefb.h>
0039
0040 #include <asm/unaligned.h>
0041
0042
0043 #define DPY_W 832
0044 #define DPY_H 622
0045
0046 static int user_wfm_size;
0047
0048
0049 struct epd_frame {
0050 int fw;
0051 int fh;
0052 u16 config[4];
0053 int wfm_size;
0054 };
0055
0056 static struct epd_frame epd_frame_table[] = {
0057 {
0058 .fw = 832,
0059 .fh = 622,
0060 .config = {
0061 15
0062 | 2 << 8
0063 | 0 << 11
0064 | 0 << 12
0065 | 0 << 15,
0066 42
0067 | 1 << 8
0068 | 1 << 9
0069 | 0 << 15,
0070 18
0071 | 0 << 15,
0072 599
0073 | 0 << 11
0074 | 0 << 12,
0075 },
0076 .wfm_size = 47001,
0077 },
0078 {
0079 .fw = 1088,
0080 .fh = 791,
0081 .config = {
0082 0x0104,
0083 0x031f,
0084 0x0088,
0085 0x02ff,
0086 },
0087 .wfm_size = 46770,
0088 },
0089 {
0090 .fw = 1200,
0091 .fh = 842,
0092 .config = {
0093 0x0101,
0094 0x030e,
0095 0x0012,
0096 0x0280,
0097 },
0098 .wfm_size = 46770,
0099 },
0100 };
0101
0102 static struct fb_fix_screeninfo metronomefb_fix = {
0103 .id = "metronomefb",
0104 .type = FB_TYPE_PACKED_PIXELS,
0105 .visual = FB_VISUAL_STATIC_PSEUDOCOLOR,
0106 .xpanstep = 0,
0107 .ypanstep = 0,
0108 .ywrapstep = 0,
0109 .line_length = DPY_W,
0110 .accel = FB_ACCEL_NONE,
0111 };
0112
0113 static struct fb_var_screeninfo metronomefb_var = {
0114 .xres = DPY_W,
0115 .yres = DPY_H,
0116 .xres_virtual = DPY_W,
0117 .yres_virtual = DPY_H,
0118 .bits_per_pixel = 8,
0119 .grayscale = 1,
0120 .nonstd = 1,
0121 .red = { 4, 3, 0 },
0122 .green = { 0, 0, 0 },
0123 .blue = { 0, 0, 0 },
0124 .transp = { 0, 0, 0 },
0125 };
0126
0127
0128 struct waveform_hdr {
0129 u8 stuff[32];
0130
0131 u8 wmta[3];
0132 u8 fvsn;
0133
0134 u8 luts;
0135 u8 mc;
0136 u8 trc;
0137 u8 stuff3;
0138
0139 u8 endb;
0140 u8 swtb;
0141 u8 stuff2a[2];
0142
0143 u8 stuff2b[3];
0144 u8 wfm_cs;
0145 } __attribute__ ((packed));
0146
0147
0148 static u8 calc_cksum(int start, int end, u8 *mem)
0149 {
0150 u8 tmp = 0;
0151 int i;
0152
0153 for (i = start; i < end; i++)
0154 tmp += mem[i];
0155
0156 return tmp;
0157 }
0158
0159 static u16 calc_img_cksum(u16 *start, int length)
0160 {
0161 u16 tmp = 0;
0162
0163 while (length--)
0164 tmp += *start++;
0165
0166 return tmp;
0167 }
0168
0169
0170 static int load_waveform(u8 *mem, size_t size, int m, int t,
0171 struct metronomefb_par *par)
0172 {
0173 int tta;
0174 int wmta;
0175 int trn = 0;
0176 int i;
0177 unsigned char v;
0178 u8 cksum;
0179 int cksum_idx;
0180 int wfm_idx, owfm_idx;
0181 int mem_idx = 0;
0182 struct waveform_hdr *wfm_hdr;
0183 u8 *metromem = par->metromem_wfm;
0184 struct device *dev = par->info->dev;
0185
0186 if (user_wfm_size)
0187 epd_frame_table[par->dt].wfm_size = user_wfm_size;
0188
0189 if (size != epd_frame_table[par->dt].wfm_size) {
0190 dev_err(dev, "Error: unexpected size %zd != %d\n", size,
0191 epd_frame_table[par->dt].wfm_size);
0192 return -EINVAL;
0193 }
0194
0195 wfm_hdr = (struct waveform_hdr *) mem;
0196
0197 if (wfm_hdr->fvsn != 1) {
0198 dev_err(dev, "Error: bad fvsn %x\n", wfm_hdr->fvsn);
0199 return -EINVAL;
0200 }
0201 if (wfm_hdr->luts != 0) {
0202 dev_err(dev, "Error: bad luts %x\n", wfm_hdr->luts);
0203 return -EINVAL;
0204 }
0205 cksum = calc_cksum(32, 47, mem);
0206 if (cksum != wfm_hdr->wfm_cs) {
0207 dev_err(dev, "Error: bad cksum %x != %x\n", cksum,
0208 wfm_hdr->wfm_cs);
0209 return -EINVAL;
0210 }
0211 wfm_hdr->mc += 1;
0212 wfm_hdr->trc += 1;
0213 for (i = 0; i < 5; i++) {
0214 if (*(wfm_hdr->stuff2a + i) != 0) {
0215 dev_err(dev, "Error: unexpected value in padding\n");
0216 return -EINVAL;
0217 }
0218 }
0219
0220
0221
0222
0223
0224 if ((sizeof(*wfm_hdr) + wfm_hdr->trc) > size)
0225 return -EINVAL;
0226
0227 for (i = sizeof(*wfm_hdr); i <= sizeof(*wfm_hdr) + wfm_hdr->trc; i++) {
0228 if (mem[i] > t) {
0229 trn = i - sizeof(*wfm_hdr) - 1;
0230 break;
0231 }
0232 }
0233
0234
0235 cksum_idx = sizeof(*wfm_hdr) + wfm_hdr->trc + 1;
0236 if (cksum_idx >= size)
0237 return -EINVAL;
0238 cksum = calc_cksum(sizeof(*wfm_hdr), cksum_idx, mem);
0239 if (cksum != mem[cksum_idx]) {
0240 dev_err(dev, "Error: bad temperature range table cksum"
0241 " %x != %x\n", cksum, mem[cksum_idx]);
0242 return -EINVAL;
0243 }
0244
0245
0246 wmta = get_unaligned_le32(wfm_hdr->wmta) & 0x00FFFFFF;
0247 cksum_idx = wmta + m*4 + 3;
0248 if (cksum_idx >= size)
0249 return -EINVAL;
0250 cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem);
0251 if (cksum != mem[cksum_idx]) {
0252 dev_err(dev, "Error: bad mode table address cksum"
0253 " %x != %x\n", cksum, mem[cksum_idx]);
0254 return -EINVAL;
0255 }
0256
0257
0258 tta = get_unaligned_le32(mem + wmta + m * 4) & 0x00FFFFFF;
0259 cksum_idx = tta + trn*4 + 3;
0260 if (cksum_idx >= size)
0261 return -EINVAL;
0262 cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem);
0263 if (cksum != mem[cksum_idx]) {
0264 dev_err(dev, "Error: bad temperature table address cksum"
0265 " %x != %x\n", cksum, mem[cksum_idx]);
0266 return -EINVAL;
0267 }
0268
0269
0270
0271 wfm_idx = get_unaligned_le32(mem + tta + trn * 4) & 0x00FFFFFF;
0272 owfm_idx = wfm_idx;
0273 if (wfm_idx >= size)
0274 return -EINVAL;
0275 while (wfm_idx < size) {
0276 unsigned char rl;
0277 v = mem[wfm_idx++];
0278 if (v == wfm_hdr->swtb) {
0279 while (((v = mem[wfm_idx++]) != wfm_hdr->swtb) &&
0280 wfm_idx < size)
0281 metromem[mem_idx++] = v;
0282
0283 continue;
0284 }
0285
0286 if (v == wfm_hdr->endb)
0287 break;
0288
0289 rl = mem[wfm_idx++];
0290 for (i = 0; i <= rl; i++)
0291 metromem[mem_idx++] = v;
0292 }
0293
0294 cksum_idx = wfm_idx;
0295 if (cksum_idx >= size)
0296 return -EINVAL;
0297 cksum = calc_cksum(owfm_idx, cksum_idx, mem);
0298 if (cksum != mem[cksum_idx]) {
0299 dev_err(dev, "Error: bad waveform data cksum"
0300 " %x != %x\n", cksum, mem[cksum_idx]);
0301 return -EINVAL;
0302 }
0303 par->frame_count = (mem_idx/64);
0304
0305 return 0;
0306 }
0307
0308 static int metronome_display_cmd(struct metronomefb_par *par)
0309 {
0310 int i;
0311 u16 cs;
0312 u16 opcode;
0313 static u8 borderval;
0314
0315
0316
0317
0318
0319
0320 if (par->metromem_cmd->opcode == 0xCC40)
0321 opcode = cs = 0xCC41;
0322 else
0323 opcode = cs = 0xCC40;
0324
0325
0326 i = 0;
0327 par->metromem_cmd->args[i] = 1 << 3
0328 | ((borderval++ % 4) & 0x0F) << 4
0329 | (par->frame_count - 1) << 8;
0330 cs += par->metromem_cmd->args[i++];
0331
0332
0333 memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
0334
0335 par->metromem_cmd->csum = cs;
0336 par->metromem_cmd->opcode = opcode;
0337
0338 return par->board->met_wait_event_intr(par);
0339 }
0340
0341 static int metronome_powerup_cmd(struct metronomefb_par *par)
0342 {
0343 int i;
0344 u16 cs;
0345
0346
0347 par->metromem_cmd->opcode = 0x1234;
0348 cs = par->metromem_cmd->opcode;
0349
0350
0351 for (i = 0; i < 3; i++) {
0352 par->metromem_cmd->args[i] = 1024;
0353 cs += par->metromem_cmd->args[i];
0354 }
0355
0356
0357 memset(&par->metromem_cmd->args[i], 0,
0358 (ARRAY_SIZE(par->metromem_cmd->args) - i) * 2);
0359
0360 par->metromem_cmd->csum = cs;
0361
0362 msleep(1);
0363 par->board->set_rst(par, 1);
0364
0365 msleep(1);
0366 par->board->set_stdby(par, 1);
0367
0368 return par->board->met_wait_event(par);
0369 }
0370
0371 static int metronome_config_cmd(struct metronomefb_par *par)
0372 {
0373
0374
0375
0376
0377 memcpy(par->metromem_cmd->args, epd_frame_table[par->dt].config,
0378 sizeof(epd_frame_table[par->dt].config));
0379
0380 memset(&par->metromem_cmd->args[4], 0,
0381 (ARRAY_SIZE(par->metromem_cmd->args) - 4) * 2);
0382
0383 par->metromem_cmd->csum = 0xCC10;
0384 par->metromem_cmd->csum += calc_img_cksum(par->metromem_cmd->args, 4);
0385 par->metromem_cmd->opcode = 0xCC10;
0386
0387 return par->board->met_wait_event(par);
0388 }
0389
0390 static int metronome_init_cmd(struct metronomefb_par *par)
0391 {
0392 int i;
0393 u16 cs;
0394
0395
0396
0397
0398
0399
0400 cs = 0xCC20;
0401
0402
0403 i = 0;
0404 par->metromem_cmd->args[i] = 0;
0405 cs += par->metromem_cmd->args[i++];
0406
0407
0408 memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
0409
0410 par->metromem_cmd->csum = cs;
0411 par->metromem_cmd->opcode = 0xCC20;
0412
0413 return par->board->met_wait_event(par);
0414 }
0415
0416 static int metronome_init_regs(struct metronomefb_par *par)
0417 {
0418 int res;
0419
0420 res = par->board->setup_io(par);
0421 if (res)
0422 return res;
0423
0424 res = metronome_powerup_cmd(par);
0425 if (res)
0426 return res;
0427
0428 res = metronome_config_cmd(par);
0429 if (res)
0430 return res;
0431
0432 res = metronome_init_cmd(par);
0433
0434 return res;
0435 }
0436
0437 static void metronomefb_dpy_update(struct metronomefb_par *par)
0438 {
0439 int fbsize;
0440 u16 cksum;
0441 unsigned char *buf = (unsigned char __force *)par->info->screen_base;
0442
0443 fbsize = par->info->fix.smem_len;
0444
0445 memcpy(par->metromem_img, buf, fbsize);
0446
0447 cksum = calc_img_cksum((u16 *) par->metromem_img, fbsize/2);
0448 *((u16 *)(par->metromem_img) + fbsize/2) = cksum;
0449 metronome_display_cmd(par);
0450 }
0451
0452 static u16 metronomefb_dpy_update_page(struct metronomefb_par *par, int index)
0453 {
0454 int i;
0455 u16 csum = 0;
0456 u16 *buf = (u16 __force *)(par->info->screen_base + index);
0457 u16 *img = (u16 *)(par->metromem_img + index);
0458
0459
0460 for (i = 0; i < PAGE_SIZE/2; i++) {
0461 *(img + i) = (buf[i] << 5) & 0xE0E0;
0462 csum += *(img + i);
0463 }
0464 return csum;
0465 }
0466
0467
0468 static void metronomefb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist)
0469 {
0470 u16 cksum;
0471 struct fb_deferred_io_pageref *pageref;
0472 struct metronomefb_par *par = info->par;
0473
0474
0475 list_for_each_entry(pageref, pagereflist, list) {
0476 unsigned long pgoffset = pageref->offset >> PAGE_SHIFT;
0477 cksum = metronomefb_dpy_update_page(par, pageref->offset);
0478 par->metromem_img_csum -= par->csum_table[pgoffset];
0479 par->csum_table[pgoffset] = cksum;
0480 par->metromem_img_csum += cksum;
0481 }
0482
0483 metronome_display_cmd(par);
0484 }
0485
0486 static void metronomefb_fillrect(struct fb_info *info,
0487 const struct fb_fillrect *rect)
0488 {
0489 struct metronomefb_par *par = info->par;
0490
0491 sys_fillrect(info, rect);
0492 metronomefb_dpy_update(par);
0493 }
0494
0495 static void metronomefb_copyarea(struct fb_info *info,
0496 const struct fb_copyarea *area)
0497 {
0498 struct metronomefb_par *par = info->par;
0499
0500 sys_copyarea(info, area);
0501 metronomefb_dpy_update(par);
0502 }
0503
0504 static void metronomefb_imageblit(struct fb_info *info,
0505 const struct fb_image *image)
0506 {
0507 struct metronomefb_par *par = info->par;
0508
0509 sys_imageblit(info, image);
0510 metronomefb_dpy_update(par);
0511 }
0512
0513
0514
0515
0516
0517 static ssize_t metronomefb_write(struct fb_info *info, const char __user *buf,
0518 size_t count, loff_t *ppos)
0519 {
0520 struct metronomefb_par *par = info->par;
0521 unsigned long p = *ppos;
0522 void *dst;
0523 int err = 0;
0524 unsigned long total_size;
0525
0526 if (info->state != FBINFO_STATE_RUNNING)
0527 return -EPERM;
0528
0529 total_size = info->fix.smem_len;
0530
0531 if (p > total_size)
0532 return -EFBIG;
0533
0534 if (count > total_size) {
0535 err = -EFBIG;
0536 count = total_size;
0537 }
0538
0539 if (count + p > total_size) {
0540 if (!err)
0541 err = -ENOSPC;
0542
0543 count = total_size - p;
0544 }
0545
0546 dst = (void __force *)(info->screen_base + p);
0547
0548 if (copy_from_user(dst, buf, count))
0549 err = -EFAULT;
0550
0551 if (!err)
0552 *ppos += count;
0553
0554 metronomefb_dpy_update(par);
0555
0556 return (err) ? err : count;
0557 }
0558
0559 static const struct fb_ops metronomefb_ops = {
0560 .owner = THIS_MODULE,
0561 .fb_write = metronomefb_write,
0562 .fb_fillrect = metronomefb_fillrect,
0563 .fb_copyarea = metronomefb_copyarea,
0564 .fb_imageblit = metronomefb_imageblit,
0565 .fb_mmap = fb_deferred_io_mmap,
0566 };
0567
0568 static struct fb_deferred_io metronomefb_defio = {
0569 .delay = HZ,
0570 .sort_pagereflist = true,
0571 .deferred_io = metronomefb_dpy_deferred_io,
0572 };
0573
0574 static int metronomefb_probe(struct platform_device *dev)
0575 {
0576 struct fb_info *info;
0577 struct metronome_board *board;
0578 int retval = -ENOMEM;
0579 int videomemorysize;
0580 unsigned char *videomemory;
0581 struct metronomefb_par *par;
0582 const struct firmware *fw_entry;
0583 int i;
0584 int panel_type;
0585 int fw, fh;
0586 int epd_dt_index;
0587
0588
0589 board = dev->dev.platform_data;
0590 if (!board)
0591 return -EINVAL;
0592
0593
0594 if (!try_module_get(board->owner))
0595 return -ENODEV;
0596
0597 info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev);
0598 if (!info)
0599 goto err;
0600
0601
0602
0603
0604
0605
0606
0607
0608
0609
0610 panel_type = board->get_panel_type();
0611 switch (panel_type) {
0612 case 6:
0613 epd_dt_index = 0;
0614 break;
0615 case 8:
0616 epd_dt_index = 1;
0617 break;
0618 case 97:
0619 epd_dt_index = 2;
0620 break;
0621 default:
0622 dev_err(&dev->dev, "Unexpected panel type. Defaulting to 6\n");
0623 epd_dt_index = 0;
0624 break;
0625 }
0626
0627 fw = epd_frame_table[epd_dt_index].fw;
0628 fh = epd_frame_table[epd_dt_index].fh;
0629
0630
0631
0632 videomemorysize = PAGE_SIZE + (fw * fh);
0633 videomemory = vzalloc(videomemorysize);
0634 if (!videomemory)
0635 goto err_fb_rel;
0636
0637 info->screen_base = (char __force __iomem *)videomemory;
0638 info->fbops = &metronomefb_ops;
0639
0640 metronomefb_fix.line_length = fw;
0641 metronomefb_var.xres = fw;
0642 metronomefb_var.yres = fh;
0643 metronomefb_var.xres_virtual = fw;
0644 metronomefb_var.yres_virtual = fh;
0645 info->var = metronomefb_var;
0646 info->fix = metronomefb_fix;
0647 info->fix.smem_len = videomemorysize;
0648 par = info->par;
0649 par->info = info;
0650 par->board = board;
0651 par->dt = epd_dt_index;
0652 init_waitqueue_head(&par->waitq);
0653
0654
0655 par->csum_table = vmalloc(videomemorysize/PAGE_SIZE);
0656 if (!par->csum_table)
0657 goto err_vfree;
0658
0659
0660
0661
0662 retval = board->setup_fb(par);
0663 if (retval) {
0664 dev_err(&dev->dev, "Failed to setup fb\n");
0665 goto err_csum_table;
0666 }
0667
0668
0669 if ((!par->metromem_wfm) || (!par->metromem_img) ||
0670 (!par->metromem_dma)) {
0671 dev_err(&dev->dev, "fb access failure\n");
0672 retval = -EINVAL;
0673 goto err_csum_table;
0674 }
0675
0676 info->fix.smem_start = par->metromem_dma;
0677
0678
0679
0680
0681 retval = request_firmware(&fw_entry, "metronome.wbf", &dev->dev);
0682 if (retval < 0) {
0683 dev_err(&dev->dev, "Failed to get waveform\n");
0684 goto err_csum_table;
0685 }
0686
0687 retval = load_waveform((u8 *) fw_entry->data, fw_entry->size, 3, 31,
0688 par);
0689 release_firmware(fw_entry);
0690 if (retval < 0) {
0691 dev_err(&dev->dev, "Failed processing waveform\n");
0692 goto err_csum_table;
0693 }
0694
0695 retval = board->setup_irq(info);
0696 if (retval)
0697 goto err_csum_table;
0698
0699 retval = metronome_init_regs(par);
0700 if (retval < 0)
0701 goto err_free_irq;
0702
0703 info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
0704
0705 info->fbdefio = &metronomefb_defio;
0706 fb_deferred_io_init(info);
0707
0708 retval = fb_alloc_cmap(&info->cmap, 8, 0);
0709 if (retval < 0) {
0710 dev_err(&dev->dev, "Failed to allocate colormap\n");
0711 goto err_free_irq;
0712 }
0713
0714
0715 for (i = 0; i < 8; i++)
0716 info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/16;
0717 memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*8);
0718 memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*8);
0719
0720 retval = register_framebuffer(info);
0721 if (retval < 0)
0722 goto err_cmap;
0723
0724 platform_set_drvdata(dev, info);
0725
0726 dev_dbg(&dev->dev,
0727 "fb%d: Metronome frame buffer device, using %dK of video"
0728 " memory\n", info->node, videomemorysize >> 10);
0729
0730 return 0;
0731
0732 err_cmap:
0733 fb_dealloc_cmap(&info->cmap);
0734 err_free_irq:
0735 board->cleanup(par);
0736 err_csum_table:
0737 vfree(par->csum_table);
0738 err_vfree:
0739 vfree(videomemory);
0740 err_fb_rel:
0741 framebuffer_release(info);
0742 err:
0743 module_put(board->owner);
0744 return retval;
0745 }
0746
0747 static int metronomefb_remove(struct platform_device *dev)
0748 {
0749 struct fb_info *info = platform_get_drvdata(dev);
0750
0751 if (info) {
0752 struct metronomefb_par *par = info->par;
0753
0754 unregister_framebuffer(info);
0755 fb_deferred_io_cleanup(info);
0756 fb_dealloc_cmap(&info->cmap);
0757 par->board->cleanup(par);
0758 vfree(par->csum_table);
0759 vfree((void __force *)info->screen_base);
0760 module_put(par->board->owner);
0761 dev_dbg(&dev->dev, "calling release\n");
0762 framebuffer_release(info);
0763 }
0764 return 0;
0765 }
0766
0767 static struct platform_driver metronomefb_driver = {
0768 .probe = metronomefb_probe,
0769 .remove = metronomefb_remove,
0770 .driver = {
0771 .name = "metronomefb",
0772 },
0773 };
0774 module_platform_driver(metronomefb_driver);
0775
0776 module_param(user_wfm_size, uint, 0);
0777 MODULE_PARM_DESC(user_wfm_size, "Set custom waveform size");
0778
0779 MODULE_DESCRIPTION("fbdev driver for Metronome controller");
0780 MODULE_AUTHOR("Jaya Kumar");
0781 MODULE_LICENSE("GPL");