Blame view

drivers/video/metronomefb.c 18.4 KB
de7c6d15e   Jaya Kumar   fbdev: defio and ...
1
2
3
4
5
6
7
8
9
10
11
12
  /*
   * linux/drivers/video/metronomefb.c -- FB driver for Metronome controller
   *
   * Copyright (C) 2008, Jaya Kumar
   *
   * This file is subject to the terms and conditions of the GNU General Public
   * License. See the file COPYING in the main directory of this archive for
   * more details.
   *
   * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven.
   *
   * This work was made possible by help and equipment support from E-Ink
631dd1a88   Justin P. Mattock   Update broken web...
13
   * Corporation. http://www.eink.com/
de7c6d15e   Jaya Kumar   fbdev: defio and ...
14
15
   *
   * This driver is written to be used with the Metronome display controller.
03c33a4f0   Jaya Kumar   fbdev: platformin...
16
17
18
   * It is intended to be architecture independent. A board specific driver
   * must be used to perform all the physical IO interactions. An example
   * is provided as am200epd.c
de7c6d15e   Jaya Kumar   fbdev: defio and ...
19
   *
de7c6d15e   Jaya Kumar   fbdev: defio and ...
20
21
22
23
24
25
   */
  #include <linux/module.h>
  #include <linux/kernel.h>
  #include <linux/errno.h>
  #include <linux/string.h>
  #include <linux/mm.h>
de7c6d15e   Jaya Kumar   fbdev: defio and ...
26
27
28
29
30
31
32
33
34
35
36
  #include <linux/vmalloc.h>
  #include <linux/delay.h>
  #include <linux/interrupt.h>
  #include <linux/fb.h>
  #include <linux/init.h>
  #include <linux/platform_device.h>
  #include <linux/list.h>
  #include <linux/firmware.h>
  #include <linux/dma-mapping.h>
  #include <linux/uaccess.h>
  #include <linux/irq.h>
03c33a4f0   Jaya Kumar   fbdev: platformin...
37
  #include <video/metronomefb.h>
de7c6d15e   Jaya Kumar   fbdev: defio and ...
38
  #include <asm/unaligned.h>
de7c6d15e   Jaya Kumar   fbdev: defio and ...
39
40
41
  /* Display specific information */
  #define DPY_W 832
  #define DPY_H 622
e93550851   Jaya Kumar   [ARM] 5209/1: met...
42
  static int user_wfm_size;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
43
44
45
46
  /* frame differs from image. frame includes non-visible pixels */
  struct epd_frame {
  	int fw; /* frame width */
  	int fh; /* frame height */
e93550851   Jaya Kumar   [ARM] 5209/1: met...
47
48
  	u16 config[4];
  	int wfm_size;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
49
50
51
52
  };
  
  static struct epd_frame epd_frame_table[] = {
  	{
e93550851   Jaya Kumar   [ARM] 5209/1: met...
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
  		.fw = 832,
  		.fh = 622,
  		.config = {
  			15 /* sdlew */
  			| 2 << 8 /* sdosz */
  			| 0 << 11 /* sdor */
  			| 0 << 12 /* sdces */
  			| 0 << 15, /* sdcer */
  			42 /* gdspl */
  			| 1 << 8 /* gdr1 */
  			| 1 << 9 /* sdshr */
  			| 0 << 15, /* gdspp */
  			18 /* gdspw */
  			| 0 << 15, /* dispc */
  			599 /* vdlc */
  			| 0 << 11 /* dsi */
  			| 0 << 12, /* dsic */
  		},
  		.wfm_size = 47001,
  	},
  	{
  		.fw = 1088,
  		.fh = 791,
  		.config = {
  			0x0104,
  			0x031f,
  			0x0088,
  			0x02ff,
  		},
  		.wfm_size = 46770,
  	},
  	{
  		.fw = 1200,
  		.fh = 842,
  		.config = {
  			0x0101,
  			0x030e,
  			0x0012,
  			0x0280,
  		},
  		.wfm_size = 46770,
de7c6d15e   Jaya Kumar   fbdev: defio and ...
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
  	},
  };
  
  static struct fb_fix_screeninfo metronomefb_fix __devinitdata = {
  	.id =		"metronomefb",
  	.type =		FB_TYPE_PACKED_PIXELS,
  	.visual =	FB_VISUAL_STATIC_PSEUDOCOLOR,
  	.xpanstep =	0,
  	.ypanstep =	0,
  	.ywrapstep =	0,
  	.line_length =	DPY_W,
  	.accel =	FB_ACCEL_NONE,
  };
  
  static struct fb_var_screeninfo metronomefb_var __devinitdata = {
  	.xres		= DPY_W,
  	.yres		= DPY_H,
  	.xres_virtual	= DPY_W,
  	.yres_virtual	= DPY_H,
  	.bits_per_pixel	= 8,
  	.grayscale	= 1,
  	.nonstd		= 1,
  	.red =		{ 4, 3, 0 },
  	.green =	{ 0, 0, 0 },
  	.blue =		{ 0, 0, 0 },
  	.transp =	{ 0, 0, 0 },
  };
03c33a4f0   Jaya Kumar   fbdev: platformin...
121
  /* the waveform structure that is coming from userspace firmware */
de7c6d15e   Jaya Kumar   fbdev: defio and ...
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
  struct waveform_hdr {
  	u8 stuff[32];
  
  	u8 wmta[3];
  	u8 fvsn;
  
  	u8 luts;
  	u8 mc;
  	u8 trc;
  	u8 stuff3;
  
  	u8 endb;
  	u8 swtb;
  	u8 stuff2a[2];
  
  	u8 stuff2b[3];
  	u8 wfm_cs;
  } __attribute__ ((packed));
  
  /* main metronomefb functions */
  static u8 calc_cksum(int start, int end, u8 *mem)
  {
  	u8 tmp = 0;
  	int i;
  
  	for (i = start; i < end; i++)
  		tmp += mem[i];
  
  	return tmp;
  }
  
  static u16 calc_img_cksum(u16 *start, int length)
  {
  	u16 tmp = 0;
  
  	while (length--)
  		tmp += *start++;
  
  	return tmp;
  }
  
  /* here we decode the incoming waveform file and populate metromem */
28501336f   Jaya Kumar   [ARM] 5186/1: met...
164
165
  static int __devinit load_waveform(u8 *mem, size_t size, int m, int t,
  				struct metronomefb_par *par)
de7c6d15e   Jaya Kumar   fbdev: defio and ...
166
167
168
169
170
171
172
173
174
175
176
  {
  	int tta;
  	int wmta;
  	int trn = 0;
  	int i;
  	unsigned char v;
  	u8 cksum;
  	int cksum_idx;
  	int wfm_idx, owfm_idx;
  	int mem_idx = 0;
  	struct waveform_hdr *wfm_hdr;
28501336f   Jaya Kumar   [ARM] 5186/1: met...
177
178
  	u8 *metromem = par->metromem_wfm;
  	struct device *dev = par->info->dev;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
179

e93550851   Jaya Kumar   [ARM] 5209/1: met...
180
181
182
183
  	if (user_wfm_size)
  		epd_frame_table[par->dt].wfm_size = user_wfm_size;
  
  	if (size != epd_frame_table[par->dt].wfm_size) {
6d54aaf38   Alan Cox   metronomefb: Fix ...
184
185
  		dev_err(dev, "Error: unexpected size %Zd != %d
  ", size,
e93550851   Jaya Kumar   [ARM] 5209/1: met...
186
  					epd_frame_table[par->dt].wfm_size);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
187
188
189
190
191
192
  		return -EINVAL;
  	}
  
  	wfm_hdr = (struct waveform_hdr *) mem;
  
  	if (wfm_hdr->fvsn != 1) {
28501336f   Jaya Kumar   [ARM] 5186/1: met...
193
194
  		dev_err(dev, "Error: bad fvsn %x
  ", wfm_hdr->fvsn);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
195
196
197
  		return -EINVAL;
  	}
  	if (wfm_hdr->luts != 0) {
28501336f   Jaya Kumar   [ARM] 5186/1: met...
198
199
  		dev_err(dev, "Error: bad luts %x
  ", wfm_hdr->luts);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
200
201
202
203
  		return -EINVAL;
  	}
  	cksum = calc_cksum(32, 47, mem);
  	if (cksum != wfm_hdr->wfm_cs) {
28501336f   Jaya Kumar   [ARM] 5186/1: met...
204
205
  		dev_err(dev, "Error: bad cksum %x != %x
  ", cksum,
de7c6d15e   Jaya Kumar   fbdev: defio and ...
206
207
208
209
210
211
212
  					wfm_hdr->wfm_cs);
  		return -EINVAL;
  	}
  	wfm_hdr->mc += 1;
  	wfm_hdr->trc += 1;
  	for (i = 0; i < 5; i++) {
  		if (*(wfm_hdr->stuff2a + i) != 0) {
28501336f   Jaya Kumar   [ARM] 5186/1: met...
213
214
  			dev_err(dev, "Error: unexpected value in padding
  ");
de7c6d15e   Jaya Kumar   fbdev: defio and ...
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
  			return -EINVAL;
  		}
  	}
  
  	/* calculating trn. trn is something used to index into
  	the waveform. presumably selecting the right one for the
  	desired temperature. it works out the offset of the first
  	v that exceeds the specified temperature */
  	if ((sizeof(*wfm_hdr) + wfm_hdr->trc) > size)
  		return -EINVAL;
  
  	for (i = sizeof(*wfm_hdr); i <= sizeof(*wfm_hdr) + wfm_hdr->trc; i++) {
  		if (mem[i] > t) {
  			trn = i - sizeof(*wfm_hdr) - 1;
  			break;
  		}
  	}
  
  	/* check temperature range table checksum */
  	cksum_idx = sizeof(*wfm_hdr) + wfm_hdr->trc + 1;
  	if (cksum_idx > size)
  		return -EINVAL;
  	cksum = calc_cksum(sizeof(*wfm_hdr), cksum_idx, mem);
  	if (cksum != mem[cksum_idx]) {
28501336f   Jaya Kumar   [ARM] 5186/1: met...
239
  		dev_err(dev, "Error: bad temperature range table cksum"
de7c6d15e   Jaya Kumar   fbdev: defio and ...
240
241
242
243
244
245
  				" %x != %x
  ", cksum, mem[cksum_idx]);
  		return -EINVAL;
  	}
  
  	/* check waveform mode table address checksum */
d15c0a4dc   Harvey Harrison   video: use get/pu...
246
  	wmta = get_unaligned_le32(wfm_hdr->wmta) & 0x00FFFFFF;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
247
248
249
250
251
  	cksum_idx = wmta + m*4 + 3;
  	if (cksum_idx > size)
  		return -EINVAL;
  	cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem);
  	if (cksum != mem[cksum_idx]) {
28501336f   Jaya Kumar   [ARM] 5186/1: met...
252
  		dev_err(dev, "Error: bad mode table address cksum"
de7c6d15e   Jaya Kumar   fbdev: defio and ...
253
254
255
256
257
258
  				" %x != %x
  ", cksum, mem[cksum_idx]);
  		return -EINVAL;
  	}
  
  	/* check waveform temperature table address checksum */
d15c0a4dc   Harvey Harrison   video: use get/pu...
259
  	tta = get_unaligned_le32(mem + wmta + m * 4) & 0x00FFFFFF;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
260
261
262
263
264
  	cksum_idx = tta + trn*4 + 3;
  	if (cksum_idx > size)
  		return -EINVAL;
  	cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem);
  	if (cksum != mem[cksum_idx]) {
28501336f   Jaya Kumar   [ARM] 5186/1: met...
265
  		dev_err(dev, "Error: bad temperature table address cksum"
de7c6d15e   Jaya Kumar   fbdev: defio and ...
266
267
268
269
270
271
272
  			" %x != %x
  ", cksum, mem[cksum_idx]);
  		return -EINVAL;
  	}
  
  	/* here we do the real work of putting the waveform into the
  	metromem buffer. this does runlength decoding of the waveform */
d15c0a4dc   Harvey Harrison   video: use get/pu...
273
  	wfm_idx = get_unaligned_le32(mem + tta + trn * 4) & 0x00FFFFFF;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
  	owfm_idx = wfm_idx;
  	if (wfm_idx > size)
  		return -EINVAL;
  	while (wfm_idx < size) {
  		unsigned char rl;
  		v = mem[wfm_idx++];
  		if (v == wfm_hdr->swtb) {
  			while (((v = mem[wfm_idx++]) != wfm_hdr->swtb) &&
  				wfm_idx < size)
  				metromem[mem_idx++] = v;
  
  			continue;
  		}
  
  		if (v == wfm_hdr->endb)
  			break;
  
  		rl = mem[wfm_idx++];
  		for (i = 0; i <= rl; i++)
  			metromem[mem_idx++] = v;
  	}
  
  	cksum_idx = wfm_idx;
  	if (cksum_idx > size)
  		return -EINVAL;
  	cksum = calc_cksum(owfm_idx, cksum_idx, mem);
  	if (cksum != mem[cksum_idx]) {
28501336f   Jaya Kumar   [ARM] 5186/1: met...
301
  		dev_err(dev, "Error: bad waveform data cksum"
de7c6d15e   Jaya Kumar   fbdev: defio and ...
302
303
304
305
  				" %x != %x
  ", cksum, mem[cksum_idx]);
  		return -EINVAL;
  	}
28501336f   Jaya Kumar   [ARM] 5186/1: met...
306
  	par->frame_count = (mem_idx/64);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
307
308
309
  
  	return 0;
  }
de7c6d15e   Jaya Kumar   fbdev: defio and ...
310
311
312
313
314
315
  static int metronome_display_cmd(struct metronomefb_par *par)
  {
  	int i;
  	u16 cs;
  	u16 opcode;
  	static u8 borderval;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
316
317
318
319
320
  
  	/* setup display command
  	we can't immediately set the opcode since the controller
  	will try parse the command before we've set it all up
  	so we just set cs here and set the opcode at the end */
de7c6d15e   Jaya Kumar   fbdev: defio and ...
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
  	if (par->metromem_cmd->opcode == 0xCC40)
  		opcode = cs = 0xCC41;
  	else
  		opcode = cs = 0xCC40;
  
  	/* set the args ( 2 bytes ) for display */
  	i = 0;
  	par->metromem_cmd->args[i] = 	1 << 3 /* border update */
  					| ((borderval++ % 4) & 0x0F) << 4
  					| (par->frame_count - 1) << 8;
  	cs += par->metromem_cmd->args[i++];
  
  	/* the rest are 0 */
  	memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
  
  	par->metromem_cmd->csum = cs;
  	par->metromem_cmd->opcode = opcode; /* display cmd */
03c33a4f0   Jaya Kumar   fbdev: platformin...
338
  	return par->board->met_wait_event_intr(par);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
  }
  
  static int __devinit metronome_powerup_cmd(struct metronomefb_par *par)
  {
  	int i;
  	u16 cs;
  
  	/* setup power up command */
  	par->metromem_cmd->opcode = 0x1234; /* pwr up pseudo cmd */
  	cs = par->metromem_cmd->opcode;
  
  	/* set pwr1,2,3 to 1024 */
  	for (i = 0; i < 3; i++) {
  		par->metromem_cmd->args[i] = 1024;
  		cs += par->metromem_cmd->args[i];
  	}
  
  	/* the rest are 0 */
  	memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
  
  	par->metromem_cmd->csum = cs;
  
  	msleep(1);
03c33a4f0   Jaya Kumar   fbdev: platformin...
362
  	par->board->set_rst(par, 1);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
363
364
  
  	msleep(1);
03c33a4f0   Jaya Kumar   fbdev: platformin...
365
  	par->board->set_stdby(par, 1);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
366

03c33a4f0   Jaya Kumar   fbdev: platformin...
367
  	return par->board->met_wait_event(par);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
368
369
370
371
  }
  
  static int __devinit metronome_config_cmd(struct metronomefb_par *par)
  {
de7c6d15e   Jaya Kumar   fbdev: defio and ...
372
373
  	/* setup config command
  	we can't immediately set the opcode since the controller
e93550851   Jaya Kumar   [ARM] 5209/1: met...
374
  	will try parse the command before we've set it all up */
de7c6d15e   Jaya Kumar   fbdev: defio and ...
375

e93550851   Jaya Kumar   [ARM] 5209/1: met...
376
377
  	memcpy(par->metromem_cmd->args, epd_frame_table[par->dt].config,
  		sizeof(epd_frame_table[par->dt].config));
de7c6d15e   Jaya Kumar   fbdev: defio and ...
378
  	/* the rest are 0 */
e93550851   Jaya Kumar   [ARM] 5209/1: met...
379
  	memset((u8 *) (par->metromem_cmd->args + 4), 0, (32-4)*2);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
380

e93550851   Jaya Kumar   [ARM] 5209/1: met...
381
382
  	par->metromem_cmd->csum = 0xCC10;
  	par->metromem_cmd->csum += calc_img_cksum(par->metromem_cmd->args, 4);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
383
  	par->metromem_cmd->opcode = 0xCC10; /* config cmd */
03c33a4f0   Jaya Kumar   fbdev: platformin...
384
  	return par->board->met_wait_event(par);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
  }
  
  static int __devinit metronome_init_cmd(struct metronomefb_par *par)
  {
  	int i;
  	u16 cs;
  
  	/* setup init command
  	we can't immediately set the opcode since the controller
  	will try parse the command before we've set it all up
  	so we just set cs here and set the opcode at the end */
  
  	cs = 0xCC20;
  
  	/* set the args ( 2 bytes ) for init */
  	i = 0;
  	par->metromem_cmd->args[i] = 0;
  	cs += par->metromem_cmd->args[i++];
  
  	/* the rest are 0 */
  	memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
  
  	par->metromem_cmd->csum = cs;
  	par->metromem_cmd->opcode = 0xCC20; /* init cmd */
03c33a4f0   Jaya Kumar   fbdev: platformin...
409
  	return par->board->met_wait_event(par);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
410
411
412
413
414
  }
  
  static int __devinit metronome_init_regs(struct metronomefb_par *par)
  {
  	int res;
e93550851   Jaya Kumar   [ARM] 5209/1: met...
415
416
417
  	res = par->board->setup_io(par);
  	if (res)
  		return res;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
418
419
420
421
422
423
424
425
426
427
  
  	res = metronome_powerup_cmd(par);
  	if (res)
  		return res;
  
  	res = metronome_config_cmd(par);
  	if (res)
  		return res;
  
  	res = metronome_init_cmd(par);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
428
429
430
431
432
433
  
  	return res;
  }
  
  static void metronomefb_dpy_update(struct metronomefb_par *par)
  {
e93550851   Jaya Kumar   [ARM] 5209/1: met...
434
  	int fbsize;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
435
436
  	u16 cksum;
  	unsigned char *buf = (unsigned char __force *)par->info->screen_base;
e93550851   Jaya Kumar   [ARM] 5209/1: met...
437
  	fbsize = par->info->fix.smem_len;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
438
  	/* copy from vm to metromem */
e93550851   Jaya Kumar   [ARM] 5209/1: met...
439
  	memcpy(par->metromem_img, buf, fbsize);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
440

e93550851   Jaya Kumar   [ARM] 5209/1: met...
441
442
  	cksum = calc_img_cksum((u16 *) par->metromem_img, fbsize/2);
  	*((u16 *)(par->metromem_img) + fbsize/2) = cksum;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
443
444
445
446
447
448
449
  	metronome_display_cmd(par);
  }
  
  static u16 metronomefb_dpy_update_page(struct metronomefb_par *par, int index)
  {
  	int i;
  	u16 csum = 0;
03c33a4f0   Jaya Kumar   fbdev: platformin...
450
451
  	u16 *buf = (u16 __force *)(par->info->screen_base + index);
  	u16 *img = (u16 *)(par->metromem_img + index);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
  
  	/* swizzle from vm to metromem and recalc cksum at the same time*/
  	for (i = 0; i < PAGE_SIZE/2; i++) {
  		*(img + i) = (buf[i] << 5) & 0xE0E0;
  		csum += *(img + i);
  	}
  	return csum;
  }
  
  /* this is called back from the deferred io workqueue */
  static void metronomefb_dpy_deferred_io(struct fb_info *info,
  				struct list_head *pagelist)
  {
  	u16 cksum;
  	struct page *cur;
  	struct fb_deferred_io *fbdefio = info->fbdefio;
  	struct metronomefb_par *par = info->par;
  
  	/* walk the written page list and swizzle the data */
  	list_for_each_entry(cur, &fbdefio->pagelist, lru) {
  		cksum = metronomefb_dpy_update_page(par,
  					(cur->index << PAGE_SHIFT));
  		par->metromem_img_csum -= par->csum_table[cur->index];
  		par->csum_table[cur->index] = cksum;
  		par->metromem_img_csum += cksum;
  	}
  
  	metronome_display_cmd(par);
  }
  
  static void metronomefb_fillrect(struct fb_info *info,
  				   const struct fb_fillrect *rect)
  {
  	struct metronomefb_par *par = info->par;
555514fab   Jaya Kumar   fbdev: metronomef...
486
  	sys_fillrect(info, rect);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
487
488
489
490
491
492
493
  	metronomefb_dpy_update(par);
  }
  
  static void metronomefb_copyarea(struct fb_info *info,
  				   const struct fb_copyarea *area)
  {
  	struct metronomefb_par *par = info->par;
555514fab   Jaya Kumar   fbdev: metronomef...
494
  	sys_copyarea(info, area);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
495
496
497
498
499
500
501
  	metronomefb_dpy_update(par);
  }
  
  static void metronomefb_imageblit(struct fb_info *info,
  				const struct fb_image *image)
  {
  	struct metronomefb_par *par = info->par;
555514fab   Jaya Kumar   fbdev: metronomef...
502
  	sys_imageblit(info, image);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
  	metronomefb_dpy_update(par);
  }
  
  /*
   * this is the slow path from userspace. they can seek and write to
   * the fb. it is based on fb_sys_write
   */
  static ssize_t metronomefb_write(struct fb_info *info, const char __user *buf,
  				size_t count, loff_t *ppos)
  {
  	struct metronomefb_par *par = info->par;
  	unsigned long p = *ppos;
  	void *dst;
  	int err = 0;
  	unsigned long total_size;
  
  	if (info->state != FBINFO_STATE_RUNNING)
  		return -EPERM;
  
  	total_size = info->fix.smem_len;
  
  	if (p > total_size)
  		return -EFBIG;
  
  	if (count > total_size) {
  		err = -EFBIG;
  		count = total_size;
  	}
  
  	if (count + p > total_size) {
  		if (!err)
  			err = -ENOSPC;
  
  		count = total_size - p;
  	}
03c33a4f0   Jaya Kumar   fbdev: platformin...
538
  	dst = (void __force *)(info->screen_base + p);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
  
  	if (copy_from_user(dst, buf, count))
  		err = -EFAULT;
  
  	if  (!err)
  		*ppos += count;
  
  	metronomefb_dpy_update(par);
  
  	return (err) ? err : count;
  }
  
  static struct fb_ops metronomefb_ops = {
  	.owner		= THIS_MODULE,
  	.fb_write	= metronomefb_write,
  	.fb_fillrect	= metronomefb_fillrect,
  	.fb_copyarea	= metronomefb_copyarea,
  	.fb_imageblit	= metronomefb_imageblit,
  };
  
  static struct fb_deferred_io metronomefb_defio = {
  	.delay		= HZ,
  	.deferred_io	= metronomefb_dpy_deferred_io,
  };
de7c6d15e   Jaya Kumar   fbdev: defio and ...
563
564
565
  static int __devinit metronomefb_probe(struct platform_device *dev)
  {
  	struct fb_info *info;
03c33a4f0   Jaya Kumar   fbdev: platformin...
566
  	struct metronome_board *board;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
567
568
569
570
571
  	int retval = -ENOMEM;
  	int videomemorysize;
  	unsigned char *videomemory;
  	struct metronomefb_par *par;
  	const struct firmware *fw_entry;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
572
  	int i;
e93550851   Jaya Kumar   [ARM] 5209/1: met...
573
574
575
  	int panel_type;
  	int fw, fh;
  	int epd_dt_index;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
576

03c33a4f0   Jaya Kumar   fbdev: platformin...
577
578
579
580
581
582
583
584
  	/* pick up board specific routines */
  	board = dev->dev.platform_data;
  	if (!board)
  		return -EINVAL;
  
  	/* try to count device specific driver, if can't, platform recalls */
  	if (!try_module_get(board->owner))
  		return -ENODEV;
e93550851   Jaya Kumar   [ARM] 5209/1: met...
585
586
587
  	info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev);
  	if (!info)
  		goto err;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
588
589
590
591
592
  	/* we have two blocks of memory.
  	info->screen_base which is vm, and is the fb used by apps.
  	par->metromem which is physically contiguous memory and
  	contains the display controller commands, waveform,
  	processed image data and padding. this is the data pulled
e93550851   Jaya Kumar   [ARM] 5209/1: met...
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
  	by the device's LCD controller and pushed to Metronome.
  	the metromem memory is allocated by the board driver and
  	is provided to us */
  
  	panel_type = board->get_panel_type();
  	switch (panel_type) {
  	case 6:
  		epd_dt_index = 0;
  		break;
  	case 8:
  		epd_dt_index = 1;
  		break;
  	case 97:
  		epd_dt_index = 2;
  		break;
  	default:
  		dev_err(&dev->dev, "Unexpected panel type. Defaulting to 6
  ");
  		epd_dt_index = 0;
  		break;
  	}
de7c6d15e   Jaya Kumar   fbdev: defio and ...
614

e93550851   Jaya Kumar   [ARM] 5209/1: met...
615
616
617
618
619
620
  	fw = epd_frame_table[epd_dt_index].fw;
  	fh = epd_frame_table[epd_dt_index].fh;
  
  	/* we need to add a spare page because our csum caching scheme walks
  	 * to the end of the page */
  	videomemorysize = PAGE_SIZE + (fw * fh);
1b86d775d   Joe Perches   video: Convert vm...
621
  	videomemory = vzalloc(videomemorysize);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
622
  	if (!videomemory)
e93550851   Jaya Kumar   [ARM] 5209/1: met...
623
  		goto err_fb_rel;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
624

03c33a4f0   Jaya Kumar   fbdev: platformin...
625
  	info->screen_base = (char __force __iomem *)videomemory;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
626
  	info->fbops = &metronomefb_ops;
e93550851   Jaya Kumar   [ARM] 5209/1: met...
627
628
629
630
631
  	metronomefb_fix.line_length = fw;
  	metronomefb_var.xres = fw;
  	metronomefb_var.yres = fh;
  	metronomefb_var.xres_virtual = fw;
  	metronomefb_var.yres_virtual = fh;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
632
633
634
635
636
  	info->var = metronomefb_var;
  	info->fix = metronomefb_fix;
  	info->fix.smem_len = videomemorysize;
  	par = info->par;
  	par->info = info;
03c33a4f0   Jaya Kumar   fbdev: platformin...
637
  	par->board = board;
e93550851   Jaya Kumar   [ARM] 5209/1: met...
638
  	par->dt = epd_dt_index;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
639
640
641
642
643
  	init_waitqueue_head(&par->waitq);
  
  	/* this table caches per page csum values. */
  	par->csum_table = vmalloc(videomemorysize/PAGE_SIZE);
  	if (!par->csum_table)
e93550851   Jaya Kumar   [ARM] 5209/1: met...
644
645
646
647
648
649
650
651
652
  		goto err_vfree;
  
  	/* the physical framebuffer that we use is setup by
  	 * the platform device driver. It will provide us
  	 * with cmd, wfm and image memory in a contiguous area. */
  	retval = board->setup_fb(par);
  	if (retval) {
  		dev_err(&dev->dev, "Failed to setup fb
  ");
de7c6d15e   Jaya Kumar   fbdev: defio and ...
653
  		goto err_csum_table;
e93550851   Jaya Kumar   [ARM] 5209/1: met...
654
  	}
de7c6d15e   Jaya Kumar   fbdev: defio and ...
655

e93550851   Jaya Kumar   [ARM] 5209/1: met...
656
657
658
659
660
661
662
  	/* after this point we should have a framebuffer */
  	if ((!par->metromem_wfm) ||  (!par->metromem_img) ||
  		(!par->metromem_dma)) {
  		dev_err(&dev->dev, "fb access failure
  ");
  		retval = -EINVAL;
  		goto err_csum_table;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
663
664
665
  	}
  
  	info->fix.smem_start = par->metromem_dma;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
666

03c33a4f0   Jaya Kumar   fbdev: platformin...
667
668
  	/* load the waveform in. assume mode 3, temp 31 for now
  		a) request the waveform file from userspace
de7c6d15e   Jaya Kumar   fbdev: defio and ...
669
  		b) process waveform and decode into metromem */
03c33a4f0   Jaya Kumar   fbdev: platformin...
670
  	retval = request_firmware(&fw_entry, "metronome.wbf", &dev->dev);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
671
  	if (retval < 0) {
28501336f   Jaya Kumar   [ARM] 5186/1: met...
672
673
  		dev_err(&dev->dev, "Failed to get waveform
  ");
e93550851   Jaya Kumar   [ARM] 5209/1: met...
674
  		goto err_csum_table;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
675
  	}
28501336f   Jaya Kumar   [ARM] 5186/1: met...
676
677
  	retval = load_waveform((u8 *) fw_entry->data, fw_entry->size, 3, 31,
  				par);
2422fbba0   Sebastian Siewior   metronomefb: don'...
678
  	release_firmware(fw_entry);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
679
  	if (retval < 0) {
28501336f   Jaya Kumar   [ARM] 5186/1: met...
680
681
  		dev_err(&dev->dev, "Failed processing waveform
  ");
e93550851   Jaya Kumar   [ARM] 5209/1: met...
682
  		goto err_csum_table;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
683
  	}
de7c6d15e   Jaya Kumar   fbdev: defio and ...
684

03c33a4f0   Jaya Kumar   fbdev: platformin...
685
  	if (board->setup_irq(info))
e93550851   Jaya Kumar   [ARM] 5209/1: met...
686
  		goto err_csum_table;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
687
688
689
690
  
  	retval = metronome_init_regs(par);
  	if (retval < 0)
  		goto err_free_irq;
a9b5ff99c   Konrad Rzeszutek Wilk   fb-defio: Inhibit...
691
  	info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
692
693
694
695
696
697
  
  	info->fbdefio = &metronomefb_defio;
  	fb_deferred_io_init(info);
  
  	retval = fb_alloc_cmap(&info->cmap, 8, 0);
  	if (retval < 0) {
28501336f   Jaya Kumar   [ARM] 5186/1: met...
698
699
  		dev_err(&dev->dev, "Failed to allocate colormap
  ");
e93550851   Jaya Kumar   [ARM] 5209/1: met...
700
  		goto err_free_irq;
de7c6d15e   Jaya Kumar   fbdev: defio and ...
701
702
703
704
705
706
707
708
709
710
711
712
713
  	}
  
  	/* set cmap */
  	for (i = 0; i < 8; i++)
  		info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/16;
  	memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*8);
  	memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*8);
  
  	retval = register_framebuffer(info);
  	if (retval < 0)
  		goto err_cmap;
  
  	platform_set_drvdata(dev, info);
28501336f   Jaya Kumar   [ARM] 5186/1: met...
714
  	dev_dbg(&dev->dev,
de7c6d15e   Jaya Kumar   fbdev: defio and ...
715
716
717
718
719
720
721
722
  		"fb%d: Metronome frame buffer device, using %dK of video"
  		" memory
  ", info->node, videomemorysize >> 10);
  
  	return 0;
  
  err_cmap:
  	fb_dealloc_cmap(&info->cmap);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
723
  err_free_irq:
e93550851   Jaya Kumar   [ARM] 5209/1: met...
724
  	board->cleanup(par);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
725
726
727
728
  err_csum_table:
  	vfree(par->csum_table);
  err_vfree:
  	vfree(videomemory);
e93550851   Jaya Kumar   [ARM] 5209/1: met...
729
730
731
  err_fb_rel:
  	framebuffer_release(info);
  err:
03c33a4f0   Jaya Kumar   fbdev: platformin...
732
  	module_put(board->owner);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
733
734
735
736
737
738
739
740
741
  	return retval;
  }
  
  static int __devexit metronomefb_remove(struct platform_device *dev)
  {
  	struct fb_info *info = platform_get_drvdata(dev);
  
  	if (info) {
  		struct metronomefb_par *par = info->par;
e93550851   Jaya Kumar   [ARM] 5209/1: met...
742
743
  
  		unregister_framebuffer(info);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
744
  		fb_deferred_io_cleanup(info);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
745
  		fb_dealloc_cmap(&info->cmap);
e93550851   Jaya Kumar   [ARM] 5209/1: met...
746
  		par->board->cleanup(par);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
747
  		vfree(par->csum_table);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
748
  		vfree((void __force *)info->screen_base);
03c33a4f0   Jaya Kumar   fbdev: platformin...
749
  		module_put(par->board->owner);
e93550851   Jaya Kumar   [ARM] 5209/1: met...
750
751
  		dev_dbg(&dev->dev, "calling release
  ");
de7c6d15e   Jaya Kumar   fbdev: defio and ...
752
753
754
755
756
757
758
  		framebuffer_release(info);
  	}
  	return 0;
  }
  
  static struct platform_driver metronomefb_driver = {
  	.probe	= metronomefb_probe,
1e93f390e   Axel Lin   video: metronomef...
759
  	.remove = __devexit_p(metronomefb_remove),
de7c6d15e   Jaya Kumar   fbdev: defio and ...
760
  	.driver	= {
03c33a4f0   Jaya Kumar   fbdev: platformin...
761
  		.owner	= THIS_MODULE,
de7c6d15e   Jaya Kumar   fbdev: defio and ...
762
763
764
  		.name	= "metronomefb",
  	},
  };
de7c6d15e   Jaya Kumar   fbdev: defio and ...
765
766
  static int __init metronomefb_init(void)
  {
03c33a4f0   Jaya Kumar   fbdev: platformin...
767
  	return platform_driver_register(&metronomefb_driver);
de7c6d15e   Jaya Kumar   fbdev: defio and ...
768
769
770
771
  }
  
  static void __exit metronomefb_exit(void)
  {
de7c6d15e   Jaya Kumar   fbdev: defio and ...
772
773
  	platform_driver_unregister(&metronomefb_driver);
  }
e93550851   Jaya Kumar   [ARM] 5209/1: met...
774
775
  module_param(user_wfm_size, uint, 0);
  MODULE_PARM_DESC(user_wfm_size, "Set custom waveform size");
de7c6d15e   Jaya Kumar   fbdev: defio and ...
776
777
778
779
780
781
  module_init(metronomefb_init);
  module_exit(metronomefb_exit);
  
  MODULE_DESCRIPTION("fbdev driver for Metronome controller");
  MODULE_AUTHOR("Jaya Kumar");
  MODULE_LICENSE("GPL");