blob: 661bfd20d1943e805dd6aa2865d4c2f361a38eb1 [file] [log] [blame]
Jaya Kumarde7c6d12008-03-19 17:01:10 -07001/*
2 * linux/drivers/video/metronomefb.c -- FB driver for Metronome controller
3 *
4 * Copyright (C) 2008, Jaya Kumar
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file COPYING in the main directory of this archive for
8 * more details.
9 *
10 * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven.
11 *
12 * This work was made possible by help and equipment support from E-Ink
13 * Corporation. http://support.eink.com/community
14 *
15 * This driver is written to be used with the Metronome display controller.
Jaya Kumar03c33a42008-04-28 02:15:38 -070016 * It is intended to be architecture independent. A board specific driver
17 * must be used to perform all the physical IO interactions. An example
18 * is provided as am200epd.c
Jaya Kumarde7c6d12008-03-19 17:01:10 -070019 *
Jaya Kumarde7c6d12008-03-19 17:01:10 -070020 */
21#include <linux/module.h>
22#include <linux/kernel.h>
23#include <linux/errno.h>
24#include <linux/string.h>
25#include <linux/mm.h>
26#include <linux/slab.h>
27#include <linux/vmalloc.h>
28#include <linux/delay.h>
29#include <linux/interrupt.h>
30#include <linux/fb.h>
31#include <linux/init.h>
32#include <linux/platform_device.h>
33#include <linux/list.h>
34#include <linux/firmware.h>
35#include <linux/dma-mapping.h>
36#include <linux/uaccess.h>
37#include <linux/irq.h>
38
Jaya Kumar03c33a42008-04-28 02:15:38 -070039#include <video/metronomefb.h>
40
Jaya Kumarde7c6d12008-03-19 17:01:10 -070041#include <asm/unaligned.h>
42
Jaya Kumarde7c6d12008-03-19 17:01:10 -070043/* Display specific information */
44#define DPY_W 832
45#define DPY_H 622
46
Jaya Kumare9355082008-08-19 11:17:55 +010047static int user_wfm_size;
48
Jaya Kumarde7c6d12008-03-19 17:01:10 -070049/* frame differs from image. frame includes non-visible pixels */
50struct epd_frame {
51 int fw; /* frame width */
52 int fh; /* frame height */
Jaya Kumare9355082008-08-19 11:17:55 +010053 u16 config[4];
54 int wfm_size;
Jaya Kumarde7c6d12008-03-19 17:01:10 -070055};
56
57static struct epd_frame epd_frame_table[] = {
58 {
Jaya Kumare9355082008-08-19 11:17:55 +010059 .fw = 832,
60 .fh = 622,
61 .config = {
62 15 /* sdlew */
63 | 2 << 8 /* sdosz */
64 | 0 << 11 /* sdor */
65 | 0 << 12 /* sdces */
66 | 0 << 15, /* sdcer */
67 42 /* gdspl */
68 | 1 << 8 /* gdr1 */
69 | 1 << 9 /* sdshr */
70 | 0 << 15, /* gdspp */
71 18 /* gdspw */
72 | 0 << 15, /* dispc */
73 599 /* vdlc */
74 | 0 << 11 /* dsi */
75 | 0 << 12, /* dsic */
76 },
77 .wfm_size = 47001,
78 },
79 {
80 .fw = 1088,
81 .fh = 791,
82 .config = {
83 0x0104,
84 0x031f,
85 0x0088,
86 0x02ff,
87 },
88 .wfm_size = 46770,
89 },
90 {
91 .fw = 1200,
92 .fh = 842,
93 .config = {
94 0x0101,
95 0x030e,
96 0x0012,
97 0x0280,
98 },
99 .wfm_size = 46770,
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700100 },
101};
102
103static struct fb_fix_screeninfo metronomefb_fix __devinitdata = {
104 .id = "metronomefb",
105 .type = FB_TYPE_PACKED_PIXELS,
106 .visual = FB_VISUAL_STATIC_PSEUDOCOLOR,
107 .xpanstep = 0,
108 .ypanstep = 0,
109 .ywrapstep = 0,
110 .line_length = DPY_W,
111 .accel = FB_ACCEL_NONE,
112};
113
114static struct fb_var_screeninfo metronomefb_var __devinitdata = {
115 .xres = DPY_W,
116 .yres = DPY_H,
117 .xres_virtual = DPY_W,
118 .yres_virtual = DPY_H,
119 .bits_per_pixel = 8,
120 .grayscale = 1,
121 .nonstd = 1,
122 .red = { 4, 3, 0 },
123 .green = { 0, 0, 0 },
124 .blue = { 0, 0, 0 },
125 .transp = { 0, 0, 0 },
126};
127
Jaya Kumar03c33a42008-04-28 02:15:38 -0700128/* the waveform structure that is coming from userspace firmware */
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700129struct waveform_hdr {
130 u8 stuff[32];
131
132 u8 wmta[3];
133 u8 fvsn;
134
135 u8 luts;
136 u8 mc;
137 u8 trc;
138 u8 stuff3;
139
140 u8 endb;
141 u8 swtb;
142 u8 stuff2a[2];
143
144 u8 stuff2b[3];
145 u8 wfm_cs;
146} __attribute__ ((packed));
147
148/* main metronomefb functions */
149static u8 calc_cksum(int start, int end, u8 *mem)
150{
151 u8 tmp = 0;
152 int i;
153
154 for (i = start; i < end; i++)
155 tmp += mem[i];
156
157 return tmp;
158}
159
160static u16 calc_img_cksum(u16 *start, int length)
161{
162 u16 tmp = 0;
163
164 while (length--)
165 tmp += *start++;
166
167 return tmp;
168}
169
170/* here we decode the incoming waveform file and populate metromem */
Jaya Kumar28501332008-08-05 13:52:14 +0100171static int __devinit load_waveform(u8 *mem, size_t size, int m, int t,
172 struct metronomefb_par *par)
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700173{
174 int tta;
175 int wmta;
176 int trn = 0;
177 int i;
178 unsigned char v;
179 u8 cksum;
180 int cksum_idx;
181 int wfm_idx, owfm_idx;
182 int mem_idx = 0;
183 struct waveform_hdr *wfm_hdr;
Jaya Kumar28501332008-08-05 13:52:14 +0100184 u8 *metromem = par->metromem_wfm;
185 struct device *dev = par->info->dev;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700186
Jaya Kumare9355082008-08-19 11:17:55 +0100187 if (user_wfm_size)
188 epd_frame_table[par->dt].wfm_size = user_wfm_size;
189
190 if (size != epd_frame_table[par->dt].wfm_size) {
Alan Cox6d54aaf2008-10-15 10:50:32 +0100191 dev_err(dev, "Error: unexpected size %Zd != %d\n", size,
Jaya Kumare9355082008-08-19 11:17:55 +0100192 epd_frame_table[par->dt].wfm_size);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700193 return -EINVAL;
194 }
195
196 wfm_hdr = (struct waveform_hdr *) mem;
197
198 if (wfm_hdr->fvsn != 1) {
Jaya Kumar28501332008-08-05 13:52:14 +0100199 dev_err(dev, "Error: bad fvsn %x\n", wfm_hdr->fvsn);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700200 return -EINVAL;
201 }
202 if (wfm_hdr->luts != 0) {
Jaya Kumar28501332008-08-05 13:52:14 +0100203 dev_err(dev, "Error: bad luts %x\n", wfm_hdr->luts);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700204 return -EINVAL;
205 }
206 cksum = calc_cksum(32, 47, mem);
207 if (cksum != wfm_hdr->wfm_cs) {
Jaya Kumar28501332008-08-05 13:52:14 +0100208 dev_err(dev, "Error: bad cksum %x != %x\n", cksum,
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700209 wfm_hdr->wfm_cs);
210 return -EINVAL;
211 }
212 wfm_hdr->mc += 1;
213 wfm_hdr->trc += 1;
214 for (i = 0; i < 5; i++) {
215 if (*(wfm_hdr->stuff2a + i) != 0) {
Jaya Kumar28501332008-08-05 13:52:14 +0100216 dev_err(dev, "Error: unexpected value in padding\n");
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700217 return -EINVAL;
218 }
219 }
220
221 /* calculating trn. trn is something used to index into
222 the waveform. presumably selecting the right one for the
223 desired temperature. it works out the offset of the first
224 v that exceeds the specified temperature */
225 if ((sizeof(*wfm_hdr) + wfm_hdr->trc) > size)
226 return -EINVAL;
227
228 for (i = sizeof(*wfm_hdr); i <= sizeof(*wfm_hdr) + wfm_hdr->trc; i++) {
229 if (mem[i] > t) {
230 trn = i - sizeof(*wfm_hdr) - 1;
231 break;
232 }
233 }
234
235 /* check temperature range table checksum */
236 cksum_idx = sizeof(*wfm_hdr) + wfm_hdr->trc + 1;
237 if (cksum_idx > size)
238 return -EINVAL;
239 cksum = calc_cksum(sizeof(*wfm_hdr), cksum_idx, mem);
240 if (cksum != mem[cksum_idx]) {
Jaya Kumar28501332008-08-05 13:52:14 +0100241 dev_err(dev, "Error: bad temperature range table cksum"
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700242 " %x != %x\n", cksum, mem[cksum_idx]);
243 return -EINVAL;
244 }
245
246 /* check waveform mode table address checksum */
Harvey Harrisond15c0a42008-04-29 01:03:41 -0700247 wmta = get_unaligned_le32(wfm_hdr->wmta) & 0x00FFFFFF;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700248 cksum_idx = wmta + m*4 + 3;
249 if (cksum_idx > size)
250 return -EINVAL;
251 cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem);
252 if (cksum != mem[cksum_idx]) {
Jaya Kumar28501332008-08-05 13:52:14 +0100253 dev_err(dev, "Error: bad mode table address cksum"
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700254 " %x != %x\n", cksum, mem[cksum_idx]);
255 return -EINVAL;
256 }
257
258 /* check waveform temperature table address checksum */
Harvey Harrisond15c0a42008-04-29 01:03:41 -0700259 tta = get_unaligned_le32(mem + wmta + m * 4) & 0x00FFFFFF;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700260 cksum_idx = tta + trn*4 + 3;
261 if (cksum_idx > size)
262 return -EINVAL;
263 cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem);
264 if (cksum != mem[cksum_idx]) {
Jaya Kumar28501332008-08-05 13:52:14 +0100265 dev_err(dev, "Error: bad temperature table address cksum"
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700266 " %x != %x\n", cksum, mem[cksum_idx]);
267 return -EINVAL;
268 }
269
270 /* here we do the real work of putting the waveform into the
271 metromem buffer. this does runlength decoding of the waveform */
Harvey Harrisond15c0a42008-04-29 01:03:41 -0700272 wfm_idx = get_unaligned_le32(mem + tta + trn * 4) & 0x00FFFFFF;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700273 owfm_idx = wfm_idx;
274 if (wfm_idx > size)
275 return -EINVAL;
276 while (wfm_idx < size) {
277 unsigned char rl;
278 v = mem[wfm_idx++];
279 if (v == wfm_hdr->swtb) {
280 while (((v = mem[wfm_idx++]) != wfm_hdr->swtb) &&
281 wfm_idx < size)
282 metromem[mem_idx++] = v;
283
284 continue;
285 }
286
287 if (v == wfm_hdr->endb)
288 break;
289
290 rl = mem[wfm_idx++];
291 for (i = 0; i <= rl; i++)
292 metromem[mem_idx++] = v;
293 }
294
295 cksum_idx = wfm_idx;
296 if (cksum_idx > size)
297 return -EINVAL;
298 cksum = calc_cksum(owfm_idx, cksum_idx, mem);
299 if (cksum != mem[cksum_idx]) {
Jaya Kumar28501332008-08-05 13:52:14 +0100300 dev_err(dev, "Error: bad waveform data cksum"
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700301 " %x != %x\n", cksum, mem[cksum_idx]);
302 return -EINVAL;
303 }
Jaya Kumar28501332008-08-05 13:52:14 +0100304 par->frame_count = (mem_idx/64);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700305
306 return 0;
307}
308
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700309static int metronome_display_cmd(struct metronomefb_par *par)
310{
311 int i;
312 u16 cs;
313 u16 opcode;
314 static u8 borderval;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700315
316 /* setup display command
317 we can't immediately set the opcode since the controller
318 will try parse the command before we've set it all up
319 so we just set cs here and set the opcode at the end */
320
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700321 if (par->metromem_cmd->opcode == 0xCC40)
322 opcode = cs = 0xCC41;
323 else
324 opcode = cs = 0xCC40;
325
326 /* set the args ( 2 bytes ) for display */
327 i = 0;
328 par->metromem_cmd->args[i] = 1 << 3 /* border update */
329 | ((borderval++ % 4) & 0x0F) << 4
330 | (par->frame_count - 1) << 8;
331 cs += par->metromem_cmd->args[i++];
332
333 /* the rest are 0 */
334 memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
335
336 par->metromem_cmd->csum = cs;
337 par->metromem_cmd->opcode = opcode; /* display cmd */
338
Jaya Kumar03c33a42008-04-28 02:15:38 -0700339 return par->board->met_wait_event_intr(par);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700340}
341
342static int __devinit metronome_powerup_cmd(struct metronomefb_par *par)
343{
344 int i;
345 u16 cs;
346
347 /* setup power up command */
348 par->metromem_cmd->opcode = 0x1234; /* pwr up pseudo cmd */
349 cs = par->metromem_cmd->opcode;
350
351 /* set pwr1,2,3 to 1024 */
352 for (i = 0; i < 3; i++) {
353 par->metromem_cmd->args[i] = 1024;
354 cs += par->metromem_cmd->args[i];
355 }
356
357 /* the rest are 0 */
358 memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
359
360 par->metromem_cmd->csum = cs;
361
362 msleep(1);
Jaya Kumar03c33a42008-04-28 02:15:38 -0700363 par->board->set_rst(par, 1);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700364
365 msleep(1);
Jaya Kumar03c33a42008-04-28 02:15:38 -0700366 par->board->set_stdby(par, 1);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700367
Jaya Kumar03c33a42008-04-28 02:15:38 -0700368 return par->board->met_wait_event(par);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700369}
370
371static int __devinit metronome_config_cmd(struct metronomefb_par *par)
372{
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700373 /* setup config command
374 we can't immediately set the opcode since the controller
Jaya Kumare9355082008-08-19 11:17:55 +0100375 will try parse the command before we've set it all up */
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700376
Jaya Kumare9355082008-08-19 11:17:55 +0100377 memcpy(par->metromem_cmd->args, epd_frame_table[par->dt].config,
378 sizeof(epd_frame_table[par->dt].config));
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700379 /* the rest are 0 */
Jaya Kumare9355082008-08-19 11:17:55 +0100380 memset((u8 *) (par->metromem_cmd->args + 4), 0, (32-4)*2);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700381
Jaya Kumare9355082008-08-19 11:17:55 +0100382 par->metromem_cmd->csum = 0xCC10;
383 par->metromem_cmd->csum += calc_img_cksum(par->metromem_cmd->args, 4);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700384 par->metromem_cmd->opcode = 0xCC10; /* config cmd */
385
Jaya Kumar03c33a42008-04-28 02:15:38 -0700386 return par->board->met_wait_event(par);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700387}
388
389static int __devinit metronome_init_cmd(struct metronomefb_par *par)
390{
391 int i;
392 u16 cs;
393
394 /* setup init command
395 we can't immediately set the opcode since the controller
396 will try parse the command before we've set it all up
397 so we just set cs here and set the opcode at the end */
398
399 cs = 0xCC20;
400
401 /* set the args ( 2 bytes ) for init */
402 i = 0;
403 par->metromem_cmd->args[i] = 0;
404 cs += par->metromem_cmd->args[i++];
405
406 /* the rest are 0 */
407 memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
408
409 par->metromem_cmd->csum = cs;
410 par->metromem_cmd->opcode = 0xCC20; /* init cmd */
411
Jaya Kumar03c33a42008-04-28 02:15:38 -0700412 return par->board->met_wait_event(par);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700413}
414
415static int __devinit metronome_init_regs(struct metronomefb_par *par)
416{
417 int res;
418
Jaya Kumare9355082008-08-19 11:17:55 +0100419 res = par->board->setup_io(par);
420 if (res)
421 return res;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700422
423 res = metronome_powerup_cmd(par);
424 if (res)
425 return res;
426
427 res = metronome_config_cmd(par);
428 if (res)
429 return res;
430
431 res = metronome_init_cmd(par);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700432
433 return res;
434}
435
436static void metronomefb_dpy_update(struct metronomefb_par *par)
437{
Jaya Kumare9355082008-08-19 11:17:55 +0100438 int fbsize;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700439 u16 cksum;
440 unsigned char *buf = (unsigned char __force *)par->info->screen_base;
441
Jaya Kumare9355082008-08-19 11:17:55 +0100442 fbsize = par->info->fix.smem_len;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700443 /* copy from vm to metromem */
Jaya Kumare9355082008-08-19 11:17:55 +0100444 memcpy(par->metromem_img, buf, fbsize);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700445
Jaya Kumare9355082008-08-19 11:17:55 +0100446 cksum = calc_img_cksum((u16 *) par->metromem_img, fbsize/2);
447 *((u16 *)(par->metromem_img) + fbsize/2) = cksum;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700448 metronome_display_cmd(par);
449}
450
451static u16 metronomefb_dpy_update_page(struct metronomefb_par *par, int index)
452{
453 int i;
454 u16 csum = 0;
Jaya Kumar03c33a42008-04-28 02:15:38 -0700455 u16 *buf = (u16 __force *)(par->info->screen_base + index);
456 u16 *img = (u16 *)(par->metromem_img + index);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700457
458 /* swizzle from vm to metromem and recalc cksum at the same time*/
459 for (i = 0; i < PAGE_SIZE/2; i++) {
460 *(img + i) = (buf[i] << 5) & 0xE0E0;
461 csum += *(img + i);
462 }
463 return csum;
464}
465
466/* this is called back from the deferred io workqueue */
467static void metronomefb_dpy_deferred_io(struct fb_info *info,
468 struct list_head *pagelist)
469{
470 u16 cksum;
471 struct page *cur;
472 struct fb_deferred_io *fbdefio = info->fbdefio;
473 struct metronomefb_par *par = info->par;
474
475 /* walk the written page list and swizzle the data */
476 list_for_each_entry(cur, &fbdefio->pagelist, lru) {
477 cksum = metronomefb_dpy_update_page(par,
478 (cur->index << PAGE_SHIFT));
479 par->metromem_img_csum -= par->csum_table[cur->index];
480 par->csum_table[cur->index] = cksum;
481 par->metromem_img_csum += cksum;
482 }
483
484 metronome_display_cmd(par);
485}
486
487static void metronomefb_fillrect(struct fb_info *info,
488 const struct fb_fillrect *rect)
489{
490 struct metronomefb_par *par = info->par;
491
Jaya Kumar555514f2008-04-28 02:15:36 -0700492 sys_fillrect(info, rect);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700493 metronomefb_dpy_update(par);
494}
495
496static void metronomefb_copyarea(struct fb_info *info,
497 const struct fb_copyarea *area)
498{
499 struct metronomefb_par *par = info->par;
500
Jaya Kumar555514f2008-04-28 02:15:36 -0700501 sys_copyarea(info, area);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700502 metronomefb_dpy_update(par);
503}
504
505static void metronomefb_imageblit(struct fb_info *info,
506 const struct fb_image *image)
507{
508 struct metronomefb_par *par = info->par;
509
Jaya Kumar555514f2008-04-28 02:15:36 -0700510 sys_imageblit(info, image);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700511 metronomefb_dpy_update(par);
512}
513
514/*
515 * this is the slow path from userspace. they can seek and write to
516 * the fb. it is based on fb_sys_write
517 */
518static ssize_t metronomefb_write(struct fb_info *info, const char __user *buf,
519 size_t count, loff_t *ppos)
520{
521 struct metronomefb_par *par = info->par;
522 unsigned long p = *ppos;
523 void *dst;
524 int err = 0;
525 unsigned long total_size;
526
527 if (info->state != FBINFO_STATE_RUNNING)
528 return -EPERM;
529
530 total_size = info->fix.smem_len;
531
532 if (p > total_size)
533 return -EFBIG;
534
535 if (count > total_size) {
536 err = -EFBIG;
537 count = total_size;
538 }
539
540 if (count + p > total_size) {
541 if (!err)
542 err = -ENOSPC;
543
544 count = total_size - p;
545 }
546
Jaya Kumar03c33a42008-04-28 02:15:38 -0700547 dst = (void __force *)(info->screen_base + p);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700548
549 if (copy_from_user(dst, buf, count))
550 err = -EFAULT;
551
552 if (!err)
553 *ppos += count;
554
555 metronomefb_dpy_update(par);
556
557 return (err) ? err : count;
558}
559
560static struct fb_ops metronomefb_ops = {
561 .owner = THIS_MODULE,
562 .fb_write = metronomefb_write,
563 .fb_fillrect = metronomefb_fillrect,
564 .fb_copyarea = metronomefb_copyarea,
565 .fb_imageblit = metronomefb_imageblit,
566};
567
568static struct fb_deferred_io metronomefb_defio = {
569 .delay = HZ,
570 .deferred_io = metronomefb_dpy_deferred_io,
571};
572
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700573static int __devinit metronomefb_probe(struct platform_device *dev)
574{
575 struct fb_info *info;
Jaya Kumar03c33a42008-04-28 02:15:38 -0700576 struct metronome_board *board;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700577 int retval = -ENOMEM;
578 int videomemorysize;
579 unsigned char *videomemory;
580 struct metronomefb_par *par;
581 const struct firmware *fw_entry;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700582 int i;
Jaya Kumare9355082008-08-19 11:17:55 +0100583 int panel_type;
584 int fw, fh;
585 int epd_dt_index;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700586
Jaya Kumar03c33a42008-04-28 02:15:38 -0700587 /* pick up board specific routines */
588 board = dev->dev.platform_data;
589 if (!board)
590 return -EINVAL;
591
592 /* try to count device specific driver, if can't, platform recalls */
593 if (!try_module_get(board->owner))
594 return -ENODEV;
595
Jaya Kumare9355082008-08-19 11:17:55 +0100596 info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev);
597 if (!info)
598 goto err;
599
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700600 /* we have two blocks of memory.
601 info->screen_base which is vm, and is the fb used by apps.
602 par->metromem which is physically contiguous memory and
603 contains the display controller commands, waveform,
604 processed image data and padding. this is the data pulled
Jaya Kumare9355082008-08-19 11:17:55 +0100605 by the device's LCD controller and pushed to Metronome.
606 the metromem memory is allocated by the board driver and
607 is provided to us */
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700608
Jaya Kumare9355082008-08-19 11:17:55 +0100609 panel_type = board->get_panel_type();
610 switch (panel_type) {
611 case 6:
612 epd_dt_index = 0;
613 break;
614 case 8:
615 epd_dt_index = 1;
616 break;
617 case 97:
618 epd_dt_index = 2;
619 break;
620 default:
621 dev_err(&dev->dev, "Unexpected panel type. Defaulting to 6\n");
622 epd_dt_index = 0;
623 break;
624 }
625
626 fw = epd_frame_table[epd_dt_index].fw;
627 fh = epd_frame_table[epd_dt_index].fh;
628
629 /* we need to add a spare page because our csum caching scheme walks
630 * to the end of the page */
631 videomemorysize = PAGE_SIZE + (fw * fh);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700632 videomemory = vmalloc(videomemorysize);
633 if (!videomemory)
Jaya Kumare9355082008-08-19 11:17:55 +0100634 goto err_fb_rel;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700635
636 memset(videomemory, 0, videomemorysize);
637
Jaya Kumar03c33a42008-04-28 02:15:38 -0700638 info->screen_base = (char __force __iomem *)videomemory;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700639 info->fbops = &metronomefb_ops;
640
Jaya Kumare9355082008-08-19 11:17:55 +0100641 metronomefb_fix.line_length = fw;
642 metronomefb_var.xres = fw;
643 metronomefb_var.yres = fh;
644 metronomefb_var.xres_virtual = fw;
645 metronomefb_var.yres_virtual = fh;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700646 info->var = metronomefb_var;
647 info->fix = metronomefb_fix;
648 info->fix.smem_len = videomemorysize;
649 par = info->par;
650 par->info = info;
Jaya Kumar03c33a42008-04-28 02:15:38 -0700651 par->board = board;
Jaya Kumare9355082008-08-19 11:17:55 +0100652 par->dt = epd_dt_index;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700653 init_waitqueue_head(&par->waitq);
654
655 /* this table caches per page csum values. */
656 par->csum_table = vmalloc(videomemorysize/PAGE_SIZE);
657 if (!par->csum_table)
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700658 goto err_vfree;
Jaya Kumare9355082008-08-19 11:17:55 +0100659
660 /* the physical framebuffer that we use is setup by
661 * the platform device driver. It will provide us
662 * with cmd, wfm and image memory in a contiguous area. */
663 retval = board->setup_fb(par);
664 if (retval) {
665 dev_err(&dev->dev, "Failed to setup fb\n");
666 goto err_csum_table;
667 }
668
669 /* after this point we should have a framebuffer */
670 if ((!par->metromem_wfm) || (!par->metromem_img) ||
671 (!par->metromem_dma)) {
672 dev_err(&dev->dev, "fb access failure\n");
673 retval = -EINVAL;
674 goto err_csum_table;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700675 }
676
677 info->fix.smem_start = par->metromem_dma;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700678
Jaya Kumar03c33a42008-04-28 02:15:38 -0700679 /* load the waveform in. assume mode 3, temp 31 for now
680 a) request the waveform file from userspace
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700681 b) process waveform and decode into metromem */
Jaya Kumar03c33a42008-04-28 02:15:38 -0700682 retval = request_firmware(&fw_entry, "metronome.wbf", &dev->dev);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700683 if (retval < 0) {
Jaya Kumar28501332008-08-05 13:52:14 +0100684 dev_err(&dev->dev, "Failed to get waveform\n");
Jaya Kumare9355082008-08-19 11:17:55 +0100685 goto err_csum_table;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700686 }
687
Jaya Kumar28501332008-08-05 13:52:14 +0100688 retval = load_waveform((u8 *) fw_entry->data, fw_entry->size, 3, 31,
689 par);
Sebastian Siewior2422fbb2008-04-28 02:15:39 -0700690 release_firmware(fw_entry);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700691 if (retval < 0) {
Jaya Kumar28501332008-08-05 13:52:14 +0100692 dev_err(&dev->dev, "Failed processing waveform\n");
Jaya Kumare9355082008-08-19 11:17:55 +0100693 goto err_csum_table;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700694 }
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700695
Jaya Kumar03c33a42008-04-28 02:15:38 -0700696 if (board->setup_irq(info))
Jaya Kumare9355082008-08-19 11:17:55 +0100697 goto err_csum_table;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700698
699 retval = metronome_init_regs(par);
700 if (retval < 0)
701 goto err_free_irq;
702
Konrad Rzeszutek Wilka9b5ff92009-12-03 10:31:58 -0500703 info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700704
705 info->fbdefio = &metronomefb_defio;
706 fb_deferred_io_init(info);
707
708 retval = fb_alloc_cmap(&info->cmap, 8, 0);
709 if (retval < 0) {
Jaya Kumar28501332008-08-05 13:52:14 +0100710 dev_err(&dev->dev, "Failed to allocate colormap\n");
Jaya Kumare9355082008-08-19 11:17:55 +0100711 goto err_free_irq;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700712 }
713
714 /* set cmap */
715 for (i = 0; i < 8; i++)
716 info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/16;
717 memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*8);
718 memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*8);
719
720 retval = register_framebuffer(info);
721 if (retval < 0)
722 goto err_cmap;
723
724 platform_set_drvdata(dev, info);
725
Jaya Kumar28501332008-08-05 13:52:14 +0100726 dev_dbg(&dev->dev,
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700727 "fb%d: Metronome frame buffer device, using %dK of video"
728 " memory\n", info->node, videomemorysize >> 10);
729
730 return 0;
731
732err_cmap:
733 fb_dealloc_cmap(&info->cmap);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700734err_free_irq:
Jaya Kumare9355082008-08-19 11:17:55 +0100735 board->cleanup(par);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700736err_csum_table:
737 vfree(par->csum_table);
738err_vfree:
739 vfree(videomemory);
Jaya Kumare9355082008-08-19 11:17:55 +0100740err_fb_rel:
741 framebuffer_release(info);
742err:
Jaya Kumar03c33a42008-04-28 02:15:38 -0700743 module_put(board->owner);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700744 return retval;
745}
746
747static int __devexit metronomefb_remove(struct platform_device *dev)
748{
749 struct fb_info *info = platform_get_drvdata(dev);
750
751 if (info) {
752 struct metronomefb_par *par = info->par;
Jaya Kumare9355082008-08-19 11:17:55 +0100753
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700754 unregister_framebuffer(info);
Jaya Kumare9355082008-08-19 11:17:55 +0100755 fb_deferred_io_cleanup(info);
756 fb_dealloc_cmap(&info->cmap);
757 par->board->cleanup(par);
758 vfree(par->csum_table);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700759 vfree((void __force *)info->screen_base);
Jaya Kumar03c33a42008-04-28 02:15:38 -0700760 module_put(par->board->owner);
Jaya Kumare9355082008-08-19 11:17:55 +0100761 dev_dbg(&dev->dev, "calling release\n");
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700762 framebuffer_release(info);
763 }
764 return 0;
765}
766
767static struct platform_driver metronomefb_driver = {
768 .probe = metronomefb_probe,
769 .remove = metronomefb_remove,
770 .driver = {
Jaya Kumar03c33a42008-04-28 02:15:38 -0700771 .owner = THIS_MODULE,
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700772 .name = "metronomefb",
773 },
774};
775
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700776static int __init metronomefb_init(void)
777{
Jaya Kumar03c33a42008-04-28 02:15:38 -0700778 return platform_driver_register(&metronomefb_driver);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700779}
780
781static void __exit metronomefb_exit(void)
782{
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700783 platform_driver_unregister(&metronomefb_driver);
784}
785
Jaya Kumare9355082008-08-19 11:17:55 +0100786module_param(user_wfm_size, uint, 0);
787MODULE_PARM_DESC(user_wfm_size, "Set custom waveform size");
788
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700789module_init(metronomefb_init);
790module_exit(metronomefb_exit);
791
792MODULE_DESCRIPTION("fbdev driver for Metronome controller");
793MODULE_AUTHOR("Jaya Kumar");
794MODULE_LICENSE("GPL");