blob: abb6bbf226d5232caa2b21495a0d65a5b204cbcf [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
Justin P. Mattock631dd1a2010-10-18 11:03:14 +020013 * Corporation. http://www.eink.com/
Jaya Kumarde7c6d12008-03-19 17:01:10 -070014 *
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>
Jaya Kumarde7c6d12008-03-19 17:01:10 -070026#include <linux/vmalloc.h>
27#include <linux/delay.h>
28#include <linux/interrupt.h>
29#include <linux/fb.h>
30#include <linux/init.h>
31#include <linux/platform_device.h>
32#include <linux/list.h>
33#include <linux/firmware.h>
34#include <linux/dma-mapping.h>
35#include <linux/uaccess.h>
36#include <linux/irq.h>
37
Jaya Kumar03c33a42008-04-28 02:15:38 -070038#include <video/metronomefb.h>
39
Jaya Kumarde7c6d12008-03-19 17:01:10 -070040#include <asm/unaligned.h>
41
Jaya Kumarde7c6d12008-03-19 17:01:10 -070042/* Display specific information */
43#define DPY_W 832
44#define DPY_H 622
45
Jaya Kumare9355082008-08-19 11:17:55 +010046static int user_wfm_size;
47
Jaya Kumarde7c6d12008-03-19 17:01:10 -070048/* frame differs from image. frame includes non-visible pixels */
49struct epd_frame {
50 int fw; /* frame width */
51 int fh; /* frame height */
Jaya Kumare9355082008-08-19 11:17:55 +010052 u16 config[4];
53 int wfm_size;
Jaya Kumarde7c6d12008-03-19 17:01:10 -070054};
55
56static struct epd_frame epd_frame_table[] = {
57 {
Jaya Kumare9355082008-08-19 11:17:55 +010058 .fw = 832,
59 .fh = 622,
60 .config = {
61 15 /* sdlew */
62 | 2 << 8 /* sdosz */
63 | 0 << 11 /* sdor */
64 | 0 << 12 /* sdces */
65 | 0 << 15, /* sdcer */
66 42 /* gdspl */
67 | 1 << 8 /* gdr1 */
68 | 1 << 9 /* sdshr */
69 | 0 << 15, /* gdspp */
70 18 /* gdspw */
71 | 0 << 15, /* dispc */
72 599 /* vdlc */
73 | 0 << 11 /* dsi */
74 | 0 << 12, /* dsic */
75 },
76 .wfm_size = 47001,
77 },
78 {
79 .fw = 1088,
80 .fh = 791,
81 .config = {
82 0x0104,
83 0x031f,
84 0x0088,
85 0x02ff,
86 },
87 .wfm_size = 46770,
88 },
89 {
90 .fw = 1200,
91 .fh = 842,
92 .config = {
93 0x0101,
94 0x030e,
95 0x0012,
96 0x0280,
97 },
98 .wfm_size = 46770,
Jaya Kumarde7c6d12008-03-19 17:01:10 -070099 },
100};
101
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800102static struct fb_fix_screeninfo metronomefb_fix = {
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700103 .id = "metronomefb",
104 .type = FB_TYPE_PACKED_PIXELS,
105 .visual = FB_VISUAL_STATIC_PSEUDOCOLOR,
106 .xpanstep = 0,
107 .ypanstep = 0,
108 .ywrapstep = 0,
109 .line_length = DPY_W,
110 .accel = FB_ACCEL_NONE,
111};
112
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800113static struct fb_var_screeninfo metronomefb_var = {
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700114 .xres = DPY_W,
115 .yres = DPY_H,
116 .xres_virtual = DPY_W,
117 .yres_virtual = DPY_H,
118 .bits_per_pixel = 8,
119 .grayscale = 1,
120 .nonstd = 1,
121 .red = { 4, 3, 0 },
122 .green = { 0, 0, 0 },
123 .blue = { 0, 0, 0 },
124 .transp = { 0, 0, 0 },
125};
126
Jaya Kumar03c33a42008-04-28 02:15:38 -0700127/* the waveform structure that is coming from userspace firmware */
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700128struct waveform_hdr {
129 u8 stuff[32];
130
131 u8 wmta[3];
132 u8 fvsn;
133
134 u8 luts;
135 u8 mc;
136 u8 trc;
137 u8 stuff3;
138
139 u8 endb;
140 u8 swtb;
141 u8 stuff2a[2];
142
143 u8 stuff2b[3];
144 u8 wfm_cs;
145} __attribute__ ((packed));
146
147/* main metronomefb functions */
148static u8 calc_cksum(int start, int end, u8 *mem)
149{
150 u8 tmp = 0;
151 int i;
152
153 for (i = start; i < end; i++)
154 tmp += mem[i];
155
156 return tmp;
157}
158
159static u16 calc_img_cksum(u16 *start, int length)
160{
161 u16 tmp = 0;
162
163 while (length--)
164 tmp += *start++;
165
166 return tmp;
167}
168
169/* here we decode the incoming waveform file and populate metromem */
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800170static int load_waveform(u8 *mem, size_t size, int m, int t,
171 struct metronomefb_par *par)
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700172{
173 int tta;
174 int wmta;
175 int trn = 0;
176 int i;
177 unsigned char v;
178 u8 cksum;
179 int cksum_idx;
180 int wfm_idx, owfm_idx;
181 int mem_idx = 0;
182 struct waveform_hdr *wfm_hdr;
Jaya Kumar28501332008-08-05 13:52:14 +0100183 u8 *metromem = par->metromem_wfm;
184 struct device *dev = par->info->dev;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700185
Jaya Kumare9355082008-08-19 11:17:55 +0100186 if (user_wfm_size)
187 epd_frame_table[par->dt].wfm_size = user_wfm_size;
188
189 if (size != epd_frame_table[par->dt].wfm_size) {
Alan Cox6d54aaf2008-10-15 10:50:32 +0100190 dev_err(dev, "Error: unexpected size %Zd != %d\n", size,
Jaya Kumare9355082008-08-19 11:17:55 +0100191 epd_frame_table[par->dt].wfm_size);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700192 return -EINVAL;
193 }
194
195 wfm_hdr = (struct waveform_hdr *) mem;
196
197 if (wfm_hdr->fvsn != 1) {
Jaya Kumar28501332008-08-05 13:52:14 +0100198 dev_err(dev, "Error: bad fvsn %x\n", wfm_hdr->fvsn);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700199 return -EINVAL;
200 }
201 if (wfm_hdr->luts != 0) {
Jaya Kumar28501332008-08-05 13:52:14 +0100202 dev_err(dev, "Error: bad luts %x\n", wfm_hdr->luts);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700203 return -EINVAL;
204 }
205 cksum = calc_cksum(32, 47, mem);
206 if (cksum != wfm_hdr->wfm_cs) {
Jaya Kumar28501332008-08-05 13:52:14 +0100207 dev_err(dev, "Error: bad cksum %x != %x\n", cksum,
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700208 wfm_hdr->wfm_cs);
209 return -EINVAL;
210 }
211 wfm_hdr->mc += 1;
212 wfm_hdr->trc += 1;
213 for (i = 0; i < 5; i++) {
214 if (*(wfm_hdr->stuff2a + i) != 0) {
Jaya Kumar28501332008-08-05 13:52:14 +0100215 dev_err(dev, "Error: unexpected value in padding\n");
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700216 return -EINVAL;
217 }
218 }
219
220 /* calculating trn. trn is something used to index into
221 the waveform. presumably selecting the right one for the
222 desired temperature. it works out the offset of the first
223 v that exceeds the specified temperature */
224 if ((sizeof(*wfm_hdr) + wfm_hdr->trc) > size)
225 return -EINVAL;
226
227 for (i = sizeof(*wfm_hdr); i <= sizeof(*wfm_hdr) + wfm_hdr->trc; i++) {
228 if (mem[i] > t) {
229 trn = i - sizeof(*wfm_hdr) - 1;
230 break;
231 }
232 }
233
234 /* check temperature range table checksum */
235 cksum_idx = sizeof(*wfm_hdr) + wfm_hdr->trc + 1;
236 if (cksum_idx > size)
237 return -EINVAL;
238 cksum = calc_cksum(sizeof(*wfm_hdr), cksum_idx, mem);
239 if (cksum != mem[cksum_idx]) {
Jaya Kumar28501332008-08-05 13:52:14 +0100240 dev_err(dev, "Error: bad temperature range table cksum"
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700241 " %x != %x\n", cksum, mem[cksum_idx]);
242 return -EINVAL;
243 }
244
245 /* check waveform mode table address checksum */
Harvey Harrisond15c0a42008-04-29 01:03:41 -0700246 wmta = get_unaligned_le32(wfm_hdr->wmta) & 0x00FFFFFF;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700247 cksum_idx = wmta + m*4 + 3;
248 if (cksum_idx > size)
249 return -EINVAL;
250 cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem);
251 if (cksum != mem[cksum_idx]) {
Jaya Kumar28501332008-08-05 13:52:14 +0100252 dev_err(dev, "Error: bad mode table address cksum"
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700253 " %x != %x\n", cksum, mem[cksum_idx]);
254 return -EINVAL;
255 }
256
257 /* check waveform temperature table address checksum */
Harvey Harrisond15c0a42008-04-29 01:03:41 -0700258 tta = get_unaligned_le32(mem + wmta + m * 4) & 0x00FFFFFF;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700259 cksum_idx = tta + trn*4 + 3;
260 if (cksum_idx > size)
261 return -EINVAL;
262 cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem);
263 if (cksum != mem[cksum_idx]) {
Jaya Kumar28501332008-08-05 13:52:14 +0100264 dev_err(dev, "Error: bad temperature table address cksum"
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700265 " %x != %x\n", cksum, mem[cksum_idx]);
266 return -EINVAL;
267 }
268
269 /* here we do the real work of putting the waveform into the
270 metromem buffer. this does runlength decoding of the waveform */
Harvey Harrisond15c0a42008-04-29 01:03:41 -0700271 wfm_idx = get_unaligned_le32(mem + tta + trn * 4) & 0x00FFFFFF;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700272 owfm_idx = wfm_idx;
273 if (wfm_idx > size)
274 return -EINVAL;
275 while (wfm_idx < size) {
276 unsigned char rl;
277 v = mem[wfm_idx++];
278 if (v == wfm_hdr->swtb) {
279 while (((v = mem[wfm_idx++]) != wfm_hdr->swtb) &&
280 wfm_idx < size)
281 metromem[mem_idx++] = v;
282
283 continue;
284 }
285
286 if (v == wfm_hdr->endb)
287 break;
288
289 rl = mem[wfm_idx++];
290 for (i = 0; i <= rl; i++)
291 metromem[mem_idx++] = v;
292 }
293
294 cksum_idx = wfm_idx;
295 if (cksum_idx > size)
296 return -EINVAL;
297 cksum = calc_cksum(owfm_idx, cksum_idx, mem);
298 if (cksum != mem[cksum_idx]) {
Jaya Kumar28501332008-08-05 13:52:14 +0100299 dev_err(dev, "Error: bad waveform data cksum"
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700300 " %x != %x\n", cksum, mem[cksum_idx]);
301 return -EINVAL;
302 }
Jaya Kumar28501332008-08-05 13:52:14 +0100303 par->frame_count = (mem_idx/64);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700304
305 return 0;
306}
307
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700308static int metronome_display_cmd(struct metronomefb_par *par)
309{
310 int i;
311 u16 cs;
312 u16 opcode;
313 static u8 borderval;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700314
315 /* setup display command
316 we can't immediately set the opcode since the controller
317 will try parse the command before we've set it all up
318 so we just set cs here and set the opcode at the end */
319
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700320 if (par->metromem_cmd->opcode == 0xCC40)
321 opcode = cs = 0xCC41;
322 else
323 opcode = cs = 0xCC40;
324
325 /* set the args ( 2 bytes ) for display */
326 i = 0;
327 par->metromem_cmd->args[i] = 1 << 3 /* border update */
328 | ((borderval++ % 4) & 0x0F) << 4
329 | (par->frame_count - 1) << 8;
330 cs += par->metromem_cmd->args[i++];
331
332 /* the rest are 0 */
333 memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
334
335 par->metromem_cmd->csum = cs;
336 par->metromem_cmd->opcode = opcode; /* display cmd */
337
Jaya Kumar03c33a42008-04-28 02:15:38 -0700338 return par->board->met_wait_event_intr(par);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700339}
340
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800341static int metronome_powerup_cmd(struct metronomefb_par *par)
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700342{
343 int i;
344 u16 cs;
345
346 /* setup power up command */
347 par->metromem_cmd->opcode = 0x1234; /* pwr up pseudo cmd */
348 cs = par->metromem_cmd->opcode;
349
350 /* set pwr1,2,3 to 1024 */
351 for (i = 0; i < 3; i++) {
352 par->metromem_cmd->args[i] = 1024;
353 cs += par->metromem_cmd->args[i];
354 }
355
356 /* the rest are 0 */
Dan Carpenter17f2e8e2016-01-30 17:44:32 +0300357 memset(&par->metromem_cmd->args[i], 0,
358 (ARRAY_SIZE(par->metromem_cmd->args) - i) * 2);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700359
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
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800371static int metronome_config_cmd(struct metronomefb_par *par)
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700372{
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 */
Dan Carpenter17f2e8e2016-01-30 17:44:32 +0300380 memset(&par->metromem_cmd->args[4], 0,
381 (ARRAY_SIZE(par->metromem_cmd->args) - 4) * 2);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700382
Jaya Kumare9355082008-08-19 11:17:55 +0100383 par->metromem_cmd->csum = 0xCC10;
384 par->metromem_cmd->csum += calc_img_cksum(par->metromem_cmd->args, 4);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700385 par->metromem_cmd->opcode = 0xCC10; /* config cmd */
386
Jaya Kumar03c33a42008-04-28 02:15:38 -0700387 return par->board->met_wait_event(par);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700388}
389
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800390static int metronome_init_cmd(struct metronomefb_par *par)
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700391{
392 int i;
393 u16 cs;
394
395 /* setup init command
396 we can't immediately set the opcode since the controller
397 will try parse the command before we've set it all up
398 so we just set cs here and set the opcode at the end */
399
400 cs = 0xCC20;
401
402 /* set the args ( 2 bytes ) for init */
403 i = 0;
404 par->metromem_cmd->args[i] = 0;
405 cs += par->metromem_cmd->args[i++];
406
407 /* the rest are 0 */
408 memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
409
410 par->metromem_cmd->csum = cs;
411 par->metromem_cmd->opcode = 0xCC20; /* init cmd */
412
Jaya Kumar03c33a42008-04-28 02:15:38 -0700413 return par->board->met_wait_event(par);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700414}
415
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800416static int metronome_init_regs(struct metronomefb_par *par)
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700417{
418 int res;
419
Jaya Kumare9355082008-08-19 11:17:55 +0100420 res = par->board->setup_io(par);
421 if (res)
422 return res;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700423
424 res = metronome_powerup_cmd(par);
425 if (res)
426 return res;
427
428 res = metronome_config_cmd(par);
429 if (res)
430 return res;
431
432 res = metronome_init_cmd(par);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700433
434 return res;
435}
436
437static void metronomefb_dpy_update(struct metronomefb_par *par)
438{
Jaya Kumare9355082008-08-19 11:17:55 +0100439 int fbsize;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700440 u16 cksum;
441 unsigned char *buf = (unsigned char __force *)par->info->screen_base;
442
Jaya Kumare9355082008-08-19 11:17:55 +0100443 fbsize = par->info->fix.smem_len;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700444 /* copy from vm to metromem */
Jaya Kumare9355082008-08-19 11:17:55 +0100445 memcpy(par->metromem_img, buf, fbsize);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700446
Jaya Kumare9355082008-08-19 11:17:55 +0100447 cksum = calc_img_cksum((u16 *) par->metromem_img, fbsize/2);
448 *((u16 *)(par->metromem_img) + fbsize/2) = cksum;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700449 metronome_display_cmd(par);
450}
451
452static u16 metronomefb_dpy_update_page(struct metronomefb_par *par, int index)
453{
454 int i;
455 u16 csum = 0;
Jaya Kumar03c33a42008-04-28 02:15:38 -0700456 u16 *buf = (u16 __force *)(par->info->screen_base + index);
457 u16 *img = (u16 *)(par->metromem_img + index);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700458
459 /* swizzle from vm to metromem and recalc cksum at the same time*/
460 for (i = 0; i < PAGE_SIZE/2; i++) {
461 *(img + i) = (buf[i] << 5) & 0xE0E0;
462 csum += *(img + i);
463 }
464 return csum;
465}
466
467/* this is called back from the deferred io workqueue */
468static void metronomefb_dpy_deferred_io(struct fb_info *info,
469 struct list_head *pagelist)
470{
471 u16 cksum;
472 struct page *cur;
473 struct fb_deferred_io *fbdefio = info->fbdefio;
474 struct metronomefb_par *par = info->par;
475
476 /* walk the written page list and swizzle the data */
477 list_for_each_entry(cur, &fbdefio->pagelist, lru) {
478 cksum = metronomefb_dpy_update_page(par,
479 (cur->index << PAGE_SHIFT));
480 par->metromem_img_csum -= par->csum_table[cur->index];
481 par->csum_table[cur->index] = cksum;
482 par->metromem_img_csum += cksum;
483 }
484
485 metronome_display_cmd(par);
486}
487
488static void metronomefb_fillrect(struct fb_info *info,
489 const struct fb_fillrect *rect)
490{
491 struct metronomefb_par *par = info->par;
492
Jaya Kumar555514f2008-04-28 02:15:36 -0700493 sys_fillrect(info, rect);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700494 metronomefb_dpy_update(par);
495}
496
497static void metronomefb_copyarea(struct fb_info *info,
498 const struct fb_copyarea *area)
499{
500 struct metronomefb_par *par = info->par;
501
Jaya Kumar555514f2008-04-28 02:15:36 -0700502 sys_copyarea(info, area);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700503 metronomefb_dpy_update(par);
504}
505
506static void metronomefb_imageblit(struct fb_info *info,
507 const struct fb_image *image)
508{
509 struct metronomefb_par *par = info->par;
510
Jaya Kumar555514f2008-04-28 02:15:36 -0700511 sys_imageblit(info, image);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700512 metronomefb_dpy_update(par);
513}
514
515/*
516 * this is the slow path from userspace. they can seek and write to
517 * the fb. it is based on fb_sys_write
518 */
519static ssize_t metronomefb_write(struct fb_info *info, const char __user *buf,
520 size_t count, loff_t *ppos)
521{
522 struct metronomefb_par *par = info->par;
523 unsigned long p = *ppos;
524 void *dst;
525 int err = 0;
526 unsigned long total_size;
527
528 if (info->state != FBINFO_STATE_RUNNING)
529 return -EPERM;
530
531 total_size = info->fix.smem_len;
532
533 if (p > total_size)
534 return -EFBIG;
535
536 if (count > total_size) {
537 err = -EFBIG;
538 count = total_size;
539 }
540
541 if (count + p > total_size) {
542 if (!err)
543 err = -ENOSPC;
544
545 count = total_size - p;
546 }
547
Jaya Kumar03c33a42008-04-28 02:15:38 -0700548 dst = (void __force *)(info->screen_base + p);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700549
550 if (copy_from_user(dst, buf, count))
551 err = -EFAULT;
552
553 if (!err)
554 *ppos += count;
555
556 metronomefb_dpy_update(par);
557
558 return (err) ? err : count;
559}
560
561static struct fb_ops metronomefb_ops = {
562 .owner = THIS_MODULE,
563 .fb_write = metronomefb_write,
564 .fb_fillrect = metronomefb_fillrect,
565 .fb_copyarea = metronomefb_copyarea,
566 .fb_imageblit = metronomefb_imageblit,
567};
568
569static struct fb_deferred_io metronomefb_defio = {
570 .delay = HZ,
571 .deferred_io = metronomefb_dpy_deferred_io,
572};
573
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800574static int metronomefb_probe(struct platform_device *dev)
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700575{
576 struct fb_info *info;
Jaya Kumar03c33a42008-04-28 02:15:38 -0700577 struct metronome_board *board;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700578 int retval = -ENOMEM;
579 int videomemorysize;
580 unsigned char *videomemory;
581 struct metronomefb_par *par;
582 const struct firmware *fw_entry;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700583 int i;
Jaya Kumare9355082008-08-19 11:17:55 +0100584 int panel_type;
585 int fw, fh;
586 int epd_dt_index;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700587
Jaya Kumar03c33a42008-04-28 02:15:38 -0700588 /* pick up board specific routines */
589 board = dev->dev.platform_data;
590 if (!board)
591 return -EINVAL;
592
593 /* try to count device specific driver, if can't, platform recalls */
594 if (!try_module_get(board->owner))
595 return -ENODEV;
596
Jaya Kumare9355082008-08-19 11:17:55 +0100597 info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev);
598 if (!info)
599 goto err;
600
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700601 /* we have two blocks of memory.
602 info->screen_base which is vm, and is the fb used by apps.
603 par->metromem which is physically contiguous memory and
604 contains the display controller commands, waveform,
605 processed image data and padding. this is the data pulled
Jaya Kumare9355082008-08-19 11:17:55 +0100606 by the device's LCD controller and pushed to Metronome.
607 the metromem memory is allocated by the board driver and
608 is provided to us */
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700609
Jaya Kumare9355082008-08-19 11:17:55 +0100610 panel_type = board->get_panel_type();
611 switch (panel_type) {
612 case 6:
613 epd_dt_index = 0;
614 break;
615 case 8:
616 epd_dt_index = 1;
617 break;
618 case 97:
619 epd_dt_index = 2;
620 break;
621 default:
622 dev_err(&dev->dev, "Unexpected panel type. Defaulting to 6\n");
623 epd_dt_index = 0;
624 break;
625 }
626
627 fw = epd_frame_table[epd_dt_index].fw;
628 fh = epd_frame_table[epd_dt_index].fh;
629
630 /* we need to add a spare page because our csum caching scheme walks
631 * to the end of the page */
632 videomemorysize = PAGE_SIZE + (fw * fh);
Joe Perches1b86d772011-05-28 11:13:33 -0700633 videomemory = vzalloc(videomemorysize);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700634 if (!videomemory)
Jaya Kumare9355082008-08-19 11:17:55 +0100635 goto err_fb_rel;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700636
Jaya Kumar03c33a42008-04-28 02:15:38 -0700637 info->screen_base = (char __force __iomem *)videomemory;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700638 info->fbops = &metronomefb_ops;
639
Jaya Kumare9355082008-08-19 11:17:55 +0100640 metronomefb_fix.line_length = fw;
641 metronomefb_var.xres = fw;
642 metronomefb_var.yres = fh;
643 metronomefb_var.xres_virtual = fw;
644 metronomefb_var.yres_virtual = fh;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700645 info->var = metronomefb_var;
646 info->fix = metronomefb_fix;
647 info->fix.smem_len = videomemorysize;
648 par = info->par;
649 par->info = info;
Jaya Kumar03c33a42008-04-28 02:15:38 -0700650 par->board = board;
Jaya Kumare9355082008-08-19 11:17:55 +0100651 par->dt = epd_dt_index;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700652 init_waitqueue_head(&par->waitq);
653
654 /* this table caches per page csum values. */
655 par->csum_table = vmalloc(videomemorysize/PAGE_SIZE);
656 if (!par->csum_table)
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700657 goto err_vfree;
Jaya Kumare9355082008-08-19 11:17:55 +0100658
659 /* the physical framebuffer that we use is setup by
660 * the platform device driver. It will provide us
661 * with cmd, wfm and image memory in a contiguous area. */
662 retval = board->setup_fb(par);
663 if (retval) {
664 dev_err(&dev->dev, "Failed to setup fb\n");
665 goto err_csum_table;
666 }
667
668 /* after this point we should have a framebuffer */
669 if ((!par->metromem_wfm) || (!par->metromem_img) ||
670 (!par->metromem_dma)) {
671 dev_err(&dev->dev, "fb access failure\n");
672 retval = -EINVAL;
673 goto err_csum_table;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700674 }
675
676 info->fix.smem_start = par->metromem_dma;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700677
Jaya Kumar03c33a42008-04-28 02:15:38 -0700678 /* load the waveform in. assume mode 3, temp 31 for now
679 a) request the waveform file from userspace
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700680 b) process waveform and decode into metromem */
Jaya Kumar03c33a42008-04-28 02:15:38 -0700681 retval = request_firmware(&fw_entry, "metronome.wbf", &dev->dev);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700682 if (retval < 0) {
Jaya Kumar28501332008-08-05 13:52:14 +0100683 dev_err(&dev->dev, "Failed to get waveform\n");
Jaya Kumare9355082008-08-19 11:17:55 +0100684 goto err_csum_table;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700685 }
686
Jaya Kumar28501332008-08-05 13:52:14 +0100687 retval = load_waveform((u8 *) fw_entry->data, fw_entry->size, 3, 31,
688 par);
Sebastian Siewior2422fbb2008-04-28 02:15:39 -0700689 release_firmware(fw_entry);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700690 if (retval < 0) {
Jaya Kumar28501332008-08-05 13:52:14 +0100691 dev_err(&dev->dev, "Failed processing waveform\n");
Jaya Kumare9355082008-08-19 11:17:55 +0100692 goto err_csum_table;
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700693 }
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700694
Wei Yongjune8ddb0c2013-10-12 14:25:23 +0800695 retval = board->setup_irq(info);
696 if (retval)
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
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800747static int metronomefb_remove(struct platform_device *dev)
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700748{
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,
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800769 .remove = metronomefb_remove,
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700770 .driver = {
771 .name = "metronomefb",
772 },
773};
Hanjun Guoae6b6392013-09-29 13:32:03 +0800774module_platform_driver(metronomefb_driver);
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700775
Jaya Kumare9355082008-08-19 11:17:55 +0100776module_param(user_wfm_size, uint, 0);
777MODULE_PARM_DESC(user_wfm_size, "Set custom waveform size");
778
Jaya Kumarde7c6d12008-03-19 17:01:10 -0700779MODULE_DESCRIPTION("fbdev driver for Metronome controller");
780MODULE_AUTHOR("Jaya Kumar");
781MODULE_LICENSE("GPL");